Skip to content

Commit

Permalink
fix: sundrio path traversal issue
Browse files Browse the repository at this point in the history
When compiled in Java 8, there's an error caused by sundrio
not being able to traverse the full dependency graph of
OkHttpClient.

Most likely OkHttp was compiled with JDK11+ and has
references that are not available for local compilation.

Sundrio faces this issue since when traversing the OkHttp AST
for the return type it ends up analyzing types that are not
available in the current JDK.
  • Loading branch information
manusa committed Sep 22, 2021
1 parent bb58484 commit aafd503
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@


public abstract class BaseClient implements Client, HttpClientAware {

public static final String APIS = "/apis";

protected OkHttpClient httpClient;
Expand All @@ -58,9 +58,9 @@ public BaseClient(final Config config) {

public BaseClient(final OkHttpClient httpClient, Config config) {
try {
this.httpClient = config.adaptClient(httpClient);
this.namespace = config.getNamespace();
this.configuration = config;
this.httpClient = adaptOkHttpClient(httpClient);
this.namespace = config.getNamespace();
this.apiVersion = config.getApiVersion();
if (config.getMasterUrl() == null) {
throw new KubernetesClientException("Unknown Kubernetes master URL - " +
Expand Down Expand Up @@ -161,19 +161,23 @@ public boolean supportsApiPath(String apiPath) {
}
return false;
}

@Override
public APIGroupList getApiGroups() {
return newBaseOperation(httpClient, configuration).restCall(APIGroupList.class, APIS);
}

@Override
public APIGroup getApiGroup(String name) {
return newBaseOperation(httpClient, configuration).restCall(APIGroup.class, APIS, name);
}

@Override
public APIResourceList getApiResources(String groupVersion) {
return newBaseOperation(httpClient, configuration).restCall(APIResourceList.class, APIS, groupVersion);
}

protected OkHttpClient adaptOkHttpClient(OkHttpClient okHttpClient) {
return okHttpClient;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@
* It is thread safe.
*/
public abstract class BaseKubernetesClient<C extends Client> extends BaseClient implements GenericKubernetesClient<C> {

static {
Handlers.register(Pod.class, PodOperationsImpl::new);
Handlers.register(Job.class, JobOperationsImpl::new);
Expand Down Expand Up @@ -181,7 +181,7 @@ public ParameterNamespaceListVisitFromServerGetDeleteRecreateWaitApplicable<HasM
public NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable<HasMetadata> resourceList(KubernetesResourceList item) {
return resourceListFor(item);
}

public NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl resourceListFor(Object item) {
return new NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl(httpClient, getConfiguration(), item);
}
Expand Down Expand Up @@ -412,7 +412,7 @@ public <T extends CustomResource> MixedOperation<T, KubernetesResourceList<T>, R
public <T extends CustomResource, L extends KubernetesResourceList<T>> MixedOperation<T, L, Resource<T>> customResources(Class<T> resourceType, Class<L> listClass) {
return customResources(CustomResourceDefinitionContext.fromCustomResourceType(resourceType), resourceType, listClass);
}

@Override
public <T extends HasMetadata, L extends KubernetesResourceList<T>> HasMetadataOperation<T, L, Resource<T>> resources(
Class<T> resourceType, Class<L> listClass) {
Expand Down Expand Up @@ -591,5 +591,5 @@ public RunOperations run() {
public NonNamespaceOperation<RuntimeClass, RuntimeClassList, Resource<RuntimeClass>> runtimeClasses() {
return Handlers.getOperation(RuntimeClass.class, RuntimeClassList.class, httpClient, getConfiguration());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,10 @@

package io.fabric8.kubernetes.client;

import static okhttp3.TlsVersion.TLS_1_2;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

import io.fabric8.kubernetes.api.model.AuthInfo;
import io.fabric8.kubernetes.api.model.AuthProviderConfig;
import io.fabric8.kubernetes.api.model.Cluster;
Expand All @@ -56,8 +36,25 @@
import io.fabric8.kubernetes.client.utils.Serialization;
import io.fabric8.kubernetes.client.utils.Utils;
import io.sundr.builder.annotations.Buildable;
import okhttp3.OkHttpClient;
import okhttp3.TlsVersion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import static okhttp3.TlsVersion.TLS_1_2;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true, allowGetters = true, allowSetters = true)
Expand Down Expand Up @@ -527,7 +524,7 @@ private static String absolutify(File relativeTo, String filename) {
return new File(relativeTo.getParentFile(), filename).getAbsolutePath();
}

public static Config fromKubeconfig(String kubeconfigContents) throws IOException {
public static Config fromKubeconfig(String kubeconfigContents) {
return fromKubeconfig(null, kubeconfigContents, null);
}

Expand Down Expand Up @@ -1357,7 +1354,4 @@ public AuthProviderConfig getAuthProvider() {
return authProvider;
}

public OkHttpClient adaptClient(OkHttpClient httpClient) {
return httpClient;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ public class ApiGroupResourceListsIT {
Session session;

@Test
public void testApiGroups() throws InterruptedException {
public void testApiGroups() {
APIGroupList list = client.getApiGroups();

assertTrue(list.getGroups().stream().anyMatch(g -> "apps".equals(g.getName())));
}

@Test
public void testApiGroup() throws InterruptedException {
public void testApiGroup() {
APIGroup group = client.getApiGroup("apps");

assertNotNull(group);
Expand All @@ -60,7 +60,7 @@ public void testApiGroup() throws InterruptedException {
}

@Test
public void testApiResources() throws InterruptedException {
public void testApiResources() {
APIResourceList list = client.getApiResources("apps/v1");

assertTrue(list.getResources().stream().anyMatch(r -> "deployments".equals(r.getName())));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -675,4 +675,9 @@ public boolean supportsOpenShiftAPIGroup(String apiGroup) {
return false;
}

@Override
protected OkHttpClient adaptOkHttpClient(OkHttpClient okHttpClient) {
return OpenshiftAdapterSupport.adaptOkHttpClient(okHttpClient, getConfiguration());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,24 @@
*/
package io.fabric8.openshift.client;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Map;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.OAuthTokenProvider;
import io.fabric8.kubernetes.client.internal.readiness.Readiness;
import io.fabric8.kubernetes.client.utils.BackwardsCompatibilityInterceptor;
import io.fabric8.kubernetes.client.utils.ImpersonatorInterceptor;
import io.fabric8.kubernetes.client.utils.URLUtils;
import io.fabric8.kubernetes.client.utils.Utils;
import io.fabric8.openshift.client.internal.OpenShiftOAuthInterceptor;
import io.fabric8.openshift.client.internal.readiness.OpenShiftReadiness;
import io.sundr.builder.annotations.Buildable;
import io.sundr.builder.annotations.BuildableReference;
import okhttp3.Authenticator;
import okhttp3.OkHttpClient;
import okhttp3.TlsVersion;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Map;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true, allowGetters = true, allowSetters = true)
public class OpenShiftConfig extends Config {
Expand Down Expand Up @@ -202,17 +197,4 @@ public Readiness getReadiness() {
return OpenShiftReadiness.getInstance();
}

@Override
public OkHttpClient adaptClient(OkHttpClient httpClient) {
OkHttpClient.Builder builder = httpClient != null ?
httpClient.newBuilder().authenticator(Authenticator.NONE) :
new OkHttpClient.Builder().authenticator(Authenticator.NONE);

builder.interceptors().clear();
return builder.addInterceptor(new OpenShiftOAuthInterceptor(httpClient, this))
.addInterceptor(new ImpersonatorInterceptor(this))
.addInterceptor(new BackwardsCompatibilityInterceptor())
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
import io.fabric8.kubernetes.client.BaseClient;
import io.fabric8.kubernetes.client.Client;
import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.kubernetes.client.utils.BackwardsCompatibilityInterceptor;
import io.fabric8.kubernetes.client.utils.ImpersonatorInterceptor;
import io.fabric8.openshift.client.internal.OpenShiftOAuthInterceptor;
import okhttp3.Authenticator;
import okhttp3.OkHttpClient;

import java.net.URI;
Expand All @@ -44,7 +48,7 @@ public DefaultOpenShiftClient adapt(Client client) {
* @return True if oapi is found in the root paths.
*/
public static boolean isOpenShift(OkHttpClient client, OpenShiftConfig config) {
return new BaseClient(client, config) {}.getApiGroups()
return new BaseClient(adaptOkHttpClient(client, config), config) {}.getApiGroups()
.getGroups().stream().anyMatch(g -> g.getName().endsWith("openshift.io"));
}

Expand All @@ -62,4 +66,22 @@ static boolean hasCustomOpenShiftUrl(OpenShiftConfig config) {
throw KubernetesClientException.launderThrowable(e);
}
}

/**
* Creates a new OkHttpClient from the provided one with OpenShift specific interceptors and configurations.
*
* @param okHttpClient the client to adapt.
* @param config the OpenShift configuration.
* @return an adapted OkHttpClient instance
*/
public static OkHttpClient adaptOkHttpClient(OkHttpClient okHttpClient, OpenShiftConfig config) {
OkHttpClient.Builder builder = okHttpClient != null ?
okHttpClient.newBuilder().authenticator(Authenticator.NONE) :
new OkHttpClient.Builder().authenticator(Authenticator.NONE);
builder.interceptors().clear();
return builder.addInterceptor(new OpenShiftOAuthInterceptor(okHttpClient, config))
.addInterceptor(new ImpersonatorInterceptor(config))
.addInterceptor(new BackwardsCompatibilityInterceptor())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@
import io.fabric8.kubernetes.client.ConfigBuilder;
import io.fabric8.kubernetes.client.utils.Serialization;

import org.junit.Assert;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.net.MalformedURLException;
import org.mockito.MockedStatic;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mockStatic;

class OpenShiftConfigTest {

Expand Down Expand Up @@ -81,15 +81,18 @@ void testNoOpenshiftURL() {
}

@Test
void shouldInstantiateClientUsingSerializeDeserialize() throws MalformedURLException {
DefaultOpenShiftClient original = new DefaultOpenShiftClient();
String json = Serialization.asJson(original.getConfiguration());
DefaultOpenShiftClient copy = DefaultOpenShiftClient.fromConfig(json);

Assert.assertEquals(original.getConfiguration().getMasterUrl(), copy.getConfiguration().getMasterUrl());
Assert.assertEquals(original.getConfiguration().getOauthToken(), copy.getConfiguration().getOauthToken());
Assert.assertEquals(original.getConfiguration().getNamespace(), copy.getConfiguration().getNamespace());
Assert.assertEquals(original.getConfiguration().getUsername(), copy.getConfiguration().getUsername());
Assert.assertEquals(original.getConfiguration().getPassword(), copy.getConfiguration().getPassword());
void shouldInstantiateClientUsingSerializeDeserialize() {
try (MockedStatic<OpenshiftAdapterSupport> ms = mockStatic(OpenshiftAdapterSupport.class)) {
ms.when(() -> OpenshiftAdapterSupport.isOpenShift(any(), any())).thenReturn(true);
DefaultOpenShiftClient original = new DefaultOpenShiftClient();
String json = Serialization.asJson(original.getConfiguration());
DefaultOpenShiftClient copy = DefaultOpenShiftClient.fromConfig(json);

assertEquals(original.getConfiguration().getMasterUrl(), copy.getConfiguration().getMasterUrl());
assertEquals(original.getConfiguration().getOauthToken(), copy.getConfiguration().getOauthToken());
assertEquals(original.getConfiguration().getNamespace(), copy.getConfiguration().getNamespace());
assertEquals(original.getConfiguration().getUsername(), copy.getConfiguration().getUsername());
assertEquals(original.getConfiguration().getPassword(), copy.getConfiguration().getPassword());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,37 @@

import io.fabric8.openshift.client.DefaultOpenShiftClient;
import io.fabric8.openshift.client.OpenShiftClient;
import io.fabric8.openshift.client.OpenShiftConfig;
import io.fabric8.openshift.client.OpenshiftAdapterSupport;
import okhttp3.OkHttpClient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mockStatic;

class AdaptTest {

private MockedStatic<OpenshiftAdapterSupport> openshiftAdapterSupportMockedStatic;

@BeforeEach
void setUp() {
openshiftAdapterSupportMockedStatic = mockStatic(OpenshiftAdapterSupport.class);
openshiftAdapterSupportMockedStatic
.when(() -> OpenshiftAdapterSupport.isOpenShift(any(), any(OpenShiftConfig.class))).thenReturn(true);
openshiftAdapterSupportMockedStatic
.when(() -> OpenshiftAdapterSupport.adaptOkHttpClient(any(), any())).thenAnswer(i -> i.getArgument(0));
}

@AfterEach
void tearDown() {
openshiftAdapterSupportMockedStatic.close();
}

@Test
void testAdaptToHttpClient() {
// Given
Expand Down

0 comments on commit aafd503

Please sign in to comment.