CVE-2023-1065 (TypeScript, published 2023-02-28T19:15Z)
Description: value: This vulnerability in the Snyk Kubernetes Monitor can result in irrelevant data being posted to a Snyk Organization, which could in turn obfuscate other, relevant, security issues. It does not expose the user of the integration to any direct security risk and no user data can be leaked. To exploit the vulnerability the attacker does not need to be authenticated to Snyk but does need to know the target's Integration ID (which may or may not be the same as the Organization ID, although this is an unpredictable UUID in either case).
Severity: nan (CVSS v3 base score: 5.3)
Weakness: CWE-287 \u2014 Improper Authentication
When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.
Repository: https://github.com/snyk/kubernetes-monitor
Files changed:
.circleci/config.yml
.circleci/config/jobs/operator_upgrade_tests.yml
README.md
config.default.json
scripts/circleci-jobs/deploy_to_dev.sh
scripts/local-testing.sh
snyk-monitor-deployment.yaml
snyk-operator-certified/README.md
snyk-operator-certified/bundle/manifests/snyk-monitor.clusterserviceversion.yaml
snyk-operator-certified/config/manifests/bases/snyk-monitor.clusterserviceversion.yaml
snyk-operator-certified/helm-charts/snyk-monitor/templates/deployment.yaml
snyk-operator/certified-operator/README.md
snyk-operator/deploy/olm-catalog/snyk-operator/0.0.0/snyk-operator.v0.0.0.clusterserviceversion.yaml
src/common/config.ts
src/common/types.ts
src/transmitter/index.ts
test/common/config.spec.ts
test/fixtures/operator/custom-resource-k8s.yaml
test/fixtures/operator/custom-resource.yaml
test/setup/deployers/helm-with-proxy.ts
test/setup/deployers/helm.ts
test/setup/deployers/yaml.ts
test/setup/index.ts
test/unit/data-scraper/scrape-data.spec.ts
Commit message: feat: call authenticated snyk API endpoints with token

BREAKING CHANGE: use a service account API token to perform
authenticated requests to snyk API.


Vulnerable code:
                    kubectl create secret generic snyk-monitor -n snyk-monitor --from-literal=integrationId=${INTEGRATION_ID} --from-literal=dockercfg.json={}
        kubectl create secret generic snyk-monitor -n snyk-monitor --from-literal=integrationId=${INTEGRATION_ID} --from-literal=dockercfg.json={}
* External internet access from the Kubernetes cluster to `kubernetes-upstream.snyk.io`.
  "DEFAULT_KUBERNETES_UPSTREAM_URL": "https://kubernetes-upstream.snyk.io",
integrationApi: https://kubernetes-upstream.dev.snyk.io
kubectl create secret generic snyk-monitor -n snyk-monitor --from-literal=dockercfg.json={} --from-literal=integrationId=${INTEGRATION_ID}
  integrationApi: https://kubernetes-upstream.dev.snyk.io
    To start using the Operator and the Snyk controller that it manages, you will need to create a secret containing your Snyk integrationId and Docker config file:
    kubectl create secret generic snyk-monitor -n snyk-monitor --from-literal=dockercfg.json={} --from-literal=integrationId=${SNYK_INTEGRATION_ID}
    To start using the Operator and the Snyk controller that it manages, you will need to create a secret containing your Snyk integrationId and Docker config file:
    kubectl create secret generic snyk-monitor -n snyk-monitor --from-literal=dockercfg.json={} --from-literal=integrationId=${SNYK_INTEGRATION_ID}
  integrationApi: https://kubernetes-upstream.dev.snyk.io
    To start using the Operator and the Snyk controller that it manages, you will need to create a secret containing your Snyk integrationId and Docker config file:
    kubectl create secret generic snyk-monitor -n snyk-monitor --from-literal=dockercfg.json={} --from-literal=integrationId=${SNYK_INTEGRATION_ID}
  return await retryRequest(req.method, req.url, req.payload);
        url: `${upstreamUrl}/api/v1/dependency-graph`,
        url: `${upstreamUrl}/api/v1/scan-results`,
      url: `${upstreamUrl}/api/v1/workload`,
      `${upstreamUrl}/api/v1/policy`,
    const query = `userLocator=${userLocator}&cluster=${cluster}&namespace=${namespace}&type=${type}&name=${name}&agentId=${agentId}`;
      url: `${upstreamUrl}/api/v1/workload?${query}`,
      url: `${upstreamUrl}/api/v1/cluster`,
      url: `${upstreamUrl}/api/v1/runtime-results`,
    expect(config.WORKERS_COUNT).toEqual(10);
  integrationApi: https://kubernetes-upstream.dev.snyk.io
  integrationApi: https://kubernetes-upstream.dev.snyk.io
      '--set integrationApi=https://kubernetes-upstream.dev.snyk.io ' +
      '--set integrationApi=https://kubernetes-upstream.dev.snyk.io ' +
  envVar.value = 'https://kubernetes-upstream.dev.snyk.io';
    await predeploy(integrationId, namespace);
    nock('https://kubernetes-upstream.snyk.io')
      .post('/api/v1/runtime-results')
      .post('/api/v1/runtime-results')

Fixed code:
                    SERVICE_ACCOUNT_API_TOKEN=$(uuidgen)
                    kubectl create secret generic snyk-monitor -n snyk-monitor --from-literal=integrationId=${INTEGRATION_ID} --from-literal=serviceAccountApiToken=${SERVICE_ACCOUNT_API_TOKEN} --from-literal=dockercfg.json={}
        SERVICE_ACCOUNT_API_TOKEN=$(uuidgen)
        kubectl create secret generic snyk-monitor -n snyk-monitor --from-literal=integrationId=${INTEGRATION_ID} --from-literal=serviceAccountApiToken=${SERVICE_ACCOUNT_API_TOKEN} --from-literal=dockercfg.json={}
* External internet access from the Kubernetes cluster to `api.snyk.io`.
  "SERVICE_ACCOUNT_API_TOKEN": "",
  "DEFAULT_KUBERNETES_UPSTREAM_URL": "https://api.snyk.io/v2/kubernetes-upstream",
integrationApi: "https://api.dev.snyk.io/v2/kubernetes-upstream"
if [ "$SERVICE_ACCOUNT_API_TOKEN" == "" ]; then
    echo "Enter your service account API token: "
    read SERVICE_ACCOUNT_API_TOKEN
fi

kubectl create secret generic snyk-monitor -n snyk-monitor --from-literal=dockercfg.json={} --from-literal=integrationId=${INTEGRATION_ID} --from-literal=serviceAccountApiToken=${SERVICE_ACCOUNT_API_TOKEN}
          - name: SNYK_SERVICE_ACCOUNT_API_TOKEN
            valueFrom:
              secretKeyRef:
                name: snyk-monitor
                key: serviceAccountApiToken
  integrationApi: https://api.dev.snyk.io/v2/kubernetes-upstream
    To start using the Operator and the Snyk controller that it manages, you will need to create a secret containing your Snyk integrationId, serviceAccountApiToken and Docker config file:
    kubectl create secret generic snyk-monitor -n snyk-monitor --from-literal=dockercfg.json={} --from-literal=integrationId=${SNYK_INTEGRATION_ID} --from-literal=serviceAccountApiToken=${SNYK_SERVICE_ACCOUNT_API_TOKEN}
    To start using the Operator and the Snyk controller that it manages, you will need to create a secret containing your Snyk integrationId, serviceAccountApiToken and Docker config file:
    kubectl create secret generic snyk-monitor -n snyk-monitor --from-literal=dockercfg.json={} --from-literal=integrationId=${SNYK_INTEGRATION_ID} --from-literal=serviceAccountApiToken=${SNYK_SERVICE_ACCOUNT_API_TOKEN}
          - name: SNYK_SERVICE_ACCOUNT_API_TOKEN
            valueFrom:
              secretKeyRef:
                name: snyk-monitor
                key: serviceAccountApiToken
  integrationApi: https://api.dev.snyk.io/v2/kubernetes-upstream
    To start using the Operator and the Snyk controller that it manages, you will need to create a secret containing your Snyk integrationId, serviceAccountApiToken and Docker config file:
    kubectl create secret generic snyk-monitor -n snyk-monitor --from-literal=dockercfg.json={} --from-literal=integrationId=${SNYK_INTEGRATION_ID} --from-literal=serviceAccountApiToken=${SNYK_SERVICE_ACCOUNT_API_TOKEN}
config.SERVICE_ACCOUNT_API_TOKEN = config.SERVICE_ACCOUNT_API_TOKEN.trim();
  SERVICE_ACCOUNT_API_TOKEN: string;
import path from 'path';
  options: NeedleOptions;
const upstreamRequestVersion = '2023-02-10';

  return await retryRequest(req.method, req.url, req.payload, req.options);
const upstreamRequestOptions = {
  headers: {
    Authorization: `token ${config.SERVICE_ACCOUNT_API_TOKEN}`,
  },
};

function constructUpstreamRequestUrl(
  requestPath: string,
  queryParams?: Record<string, string>,
): string {
  const requestUrl = new URL(upstreamUrl);
  requestUrl.pathname = path.join(requestUrl.pathname, requestPath);
  requestUrl.searchParams.set('version', upstreamRequestVersion);
  for (const key in queryParams) {
    requestUrl.searchParams.set(key, queryParams[key]);
  }
  return requestUrl.toString();
}

        url: constructUpstreamRequestUrl('/api/v1/dependency-graph'),
        options: upstreamRequestOptions,
        url: constructUpstreamRequestUrl('/api/v1/scan-results'),
        options: upstreamRequestOptions,
      url: constructUpstreamRequestUrl('/api/v1/workload'),
      options: upstreamRequestOptions,
      constructUpstreamRequestUrl('/api/v1/policy'),
      upstreamRequestOptions,
    const queryParams: Record<string, string> = {
      userLocator,
      cluster,
      namespace,
      type,
      name,
      agentId,
    };
      url: constructUpstreamRequestUrl('api/v1/workload', queryParams),
      options: upstreamRequestOptions,
      url: constructUpstreamRequestUrl('/api/v1/cluster'),
      options: upstreamRequestOptions,
      url: constructUpstreamRequestUrl('/api/v1/runtime-results'),
      options: upstreamRequestOptions,
  const apiToken = '46766a0a-ed0b-4e91-84c8-ea1c827f2a73';
    process.env.SNYK_SERVICE_ACCOUNT_API_TOKEN = apiToken;
    expect(config.SERVICE_ACCOUNT_API_TOKEN).toEqual(apiToken);
    expect(config.WORKERS_COUNT).toEqual(5);
  integrationApi: https://api.dev.snyk.io/v2/kubernetes-upstream
  integrationApi: https://api.dev.snyk.io/v2/kubernetes-upstream
      '--set integrationApi=https://api.dev.snyk.io/v2/kubernetes-upstream ' +
      '--set integrationApi=https://api.dev.snyk.io/v2/kubernetes-upstream ' +
  envVar.value = 'https://api.dev.snyk.io/v2/kubernetes-upstream';
function getServiceAccountApiToken(): string {
  const serviceAccountApiToken = randomUUID();
  console.log(
    `Generated new service account API token ${serviceAccountApiToken}`,
  );
  return serviceAccountApiToken;
}

  serviceAccountApiToken: string,
      serviceAccountApiToken,
    const serviceAccountApiToken = getServiceAccountApiToken();
    await predeploy(integrationId, serviceAccountApiToken, namespace);
    nock('https://api.snyk.io')
      .post('/v2/kubernetes-upstream/api/v1/runtime-results?version=2023-02-10')
      .post('/v2/kubernetes-upstream/api/v1/runtime-results?version=2023-02-10')

    try {
      expect(nock.isDone()).toBeTruthy();
    } catch (err) {
      console.error(`nock pending mocks: ${nock.pendingMocks()}`);
      throw err;
    }