PolicyEnforcerFuzzer.java
// Copyright 2023 the cncf-fuzzing authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
///////////////////////////////////////////////////////////////////////////
import com.code_intelligence.jazzer.api.BugDetectors;
import com.code_intelligence.jazzer.api.FuzzedDataProvider;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.EnumSet;
import java.util.Map;
import java.util.function.BiPredicate;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import org.keycloak.adapters.authorization.PolicyEnforcer;
import org.keycloak.adapters.authorization.TokenPrincipal;
import org.keycloak.adapters.authorization.integration.elytron.ServletHttpRequest;
import org.keycloak.adapters.authorization.integration.elytron.ServletHttpResponse;
import org.keycloak.adapters.authorization.spi.HttpRequest;
import org.keycloak.adapters.authorization.spi.HttpResponse;
import org.keycloak.protocol.oidc.client.authentication.ClientCredentialsProvider;
import org.keycloak.protocol.oidc.client.authentication.ClientCredentialsProviderUtils;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
import org.mockito.Mockito;
/**
* This fuzzer creates configuration objects and mock
* HttpRequest and HttpResponse to fuzz the enforce
* method of the PolicyEnforcer class of the authz package.
*/
public class PolicyEnforcerFuzzer {
private static HttpServletRequest servletRequest;
private static HttpServletResponse servletResponse;
private static HttpRequest request;
private static HttpResponse response;
private static MockWebServer server;
private static MockResponse mockResponse;
private static PolicyEnforcerConfig enforcerConfig;
private static ClientCredentialsProvider provider;
public static void fuzzerInitialize() {
try {
// Start the mock web server
server = new MockWebServer();
server.start();
// Retrieve host name, port and url of the mock web server
String serverHost = server.getHostName();
Integer serverPort = server.getPort();
String serverUrl = "http://" + serverHost + ":" + serverPort;
// Mock HttpServletRequest, HttpServletResponse and TokenPrincipal
servletRequest = Mockito.mock(HttpServletRequest.class);
servletResponse = Mockito.mock(HttpServletResponse.class);
TokenPrincipal principal = Mockito.mock(TokenPrincipal.class);
// Mock key method of the TokenPrincipal instance
Mockito.when(principal.getToken()).thenReturn(new AccessToken());
// Prepare HttpRequest and HttpResponse instance with the mocked object
request = new ServletHttpRequest(servletRequest, principal);
response = new ServletHttpResponse(servletResponse);
// Prepare Mock Response instance
mockResponse = new MockResponse();
mockResponse.addHeader("Content-Type", "application/json");
// Initialize enforcer config instance with random set of request and response data
enforcerConfig = new PolicyEnforcerConfig();
// Set the redirect string
enforcerConfig.setOnDenyRedirectTo(serverUrl);
// Randomly config the string data for the config
enforcerConfig.setAuthServerUrl(serverUrl);
enforcerConfig.setRealm("");
enforcerConfig.setResource("");
// Prepare client credentials provider
provider = ClientCredentialsProviderUtils.bootstrapClientAuthenticator(new AdapterConfig());
// Enable the fuzzer to connect only to the mock web server, deny any other connections
BugDetectors.allowNetworkConnections(
(host, port) -> host.equals(serverHost) && port.equals(serverPort));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void fuzzerTearDown() {
// Shutdown the mock web server
try {
if (server != null) {
server.shutdown();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void fuzzerTestOneInput(FuzzedDataProvider data) {
try {
// Create a random mock response for the mock web server
// Then enqueue to the server to serve possible request
mockResponse.setBody(data.consumeString(data.remainingBytes() / 2));
server.enqueue(mockResponse);
// Randomly choose the enforcement mode
enforcerConfig.setEnforcementMode(
data.pickValue(EnumSet.allOf(PolicyEnforcerConfig.EnforcementMode.class)));
// Randomly turn on and off for using http method as scope
enforcerConfig.setHttpMethodAsScope(data.consumeBoolean());
// Prepare credential map with random data
Map<String, Object> map = enforcerConfig.getCredentials();
map.put(data.consumeString(data.remainingBytes() / 2),
data.consumeString(data.remainingBytes() / 2));
enforcerConfig.setCredentials(map);
// Build the policy enforcer with random data and the config and provider object initialised
// above
PolicyEnforcer enforcer = PolicyEnforcer.builder()
.clientId(data.consumeString(data.remainingBytes() / 2))
.bearerOnly(data.consumeBoolean())
.enforcerConfig(enforcerConfig)
.credentialProvider(provider)
.build();
// Mock key method of the HttpServletRequest and HttpServletResponse with random data
// Use the mock method to deny real HTTP request and simulate the response with random data
Mockito.when(servletRequest.getParameter(data.consumeString(data.remainingBytes() / 2)))
.thenReturn(data.consumeString(data.remainingBytes() / 2));
Mockito.when(servletResponse.getWriter())
.thenReturn(new PrintWriter(data.consumeRemainingAsString()));
// Fuzz the enforce method with the mocked object and random data
enforcer.enforce(request, response);
} catch (IOException | RuntimeException e) {
// Known exception thrown directly from method above.
} finally {
// Suggest the java garbage collector to clean up unused memory
System.gc();
}
}
}