Complete guide to integrating Socket Basics into your GitHub Actions workflows for automated security scanning.
- Quick Start
- Performance and Caching (maintainers: see releasing.md)
- Basic Configuration
- Enterprise Features
- Advanced Workflows
- Configuration Reference
- Troubleshooting
Add Socket Basics to your workflow in 3 steps:
- Create workflow file at
.github/workflows/security-scan.yml - Add required secrets to your repository
- Configure scanning options
name: Security Scan
on:
pull_request:
types: [opened, synchronize, reopened]
permissions:
contents: read
jobs:
security-scan:
permissions:
issues: write
contents: read
pull-requests: write
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Run Socket Basics
uses: SocketDev/socket-basics@v2.0.0
env:
GITHUB_PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }}
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
socket_security_api_key: ${{ secrets.SOCKET_SECURITY_API_KEY }}With just your SOCKET_SECURITY_API_KEY, all scanning configurations are managed through the Socket Dashboard — no workflow changes needed.
When you reference uses: SocketDev/socket-basics@v2.0.0, GitHub Actions builds the
Dockerfile from source at the start of every workflow run. As of 1.1.3 the
Dockerfile uses a multi-stage build with BuildKit cache mounts, which provides
two categories of improvement:
| Improvement | Benefit |
|---|---|
Multi-stage stages (trivy, trufflehog, etc.) |
GitHub's runner cache can reuse unchanged tool layers across runs |
python:3.12-slim base |
~850 MB smaller final image → faster layer pulls on cold runners |
--mount=type=cache for apt / uv / npm |
Faster repeated builds locally and on self-hosted runners with a persistent cache |
On standard GitHub-hosted runners (ephemeral, no persistent Docker cache between jobs), the multi-stage improvement is most visible when the same runner picks up a cached layer — typically within a workflow run or when GitHub's runner image itself includes the base layers. Cold runs still download and run all tool-install steps.
Starting with v2, the action pulls a pre-built image from GHCR rather than
building from source on every run. Pinning to a specific version tag (e.g. @v2.0.0)
means the action starts in seconds — the image is built, integration-tested, and
published before the release tag is ever created.
Maintainers: see releasing.md for the publish-before-tag release process and the PR checklist.
If you run socket-basics in other CI systems (Jenkins, GitLab, CircleCI, etc.) or
as a standalone docker run, pull the pre-built image directly:
docker pull ghcr.io/socketdev/socket-basics:1.1.3See Local Docker Installation for usage examples.
Socket Basics is a security tool. Its own supply-chain integrity matters — if the action itself is compromised or ships a bad release, every repo running it is immediately affected. We've seen this happen across the ecosystem:
- Floating tags (
@v2,:latest) auto-update on every new release. A single bad push silently reaches all users with no review gate. This is structurally identical todocker pull :latest— the anti-pattern we explicitly warn against in our Docker docs. - Version tags (
@v2.0.0) are better, but tags are mutable by default. A tag can be deleted and recreated pointing at a different commit. There are documented cases of this happening — maliciously and accidentally. - Commit SHAs are the only truly immutable reference. A SHA cannot be reassigned. Combined with Dependabot, you get automated upgrades with a human review gate at zero ongoing maintenance cost.
We don't publish a floating major tag (v2). We do publish immutable version
tags (v2.0.0) protected by tag protection rules in GitHub — but SHA pinning
is still the recommendation for defence in depth.
Two supported approaches, both managed by Dependabot:
Strategy 1 — Commit SHA pin + Dependabot (recommended)
The only truly immutable reference. Dependabot keeps it current automatically.
- name: Run Socket Basics
# Dependabot keeps this SHA up to date — see .github/dependabot.yml setup below.
uses: SocketDev/socket-basics@<sha> # v2.0.0
with:
socket_security_api_key: ${{ secrets.SOCKET_SECURITY_API_KEY }}Get the SHA for any release:
git ls-remote https://github.com/SocketDev/socket-basics refs/tags/v2.0.0Strategy 2 — Version tag pin + Dependabot
Acceptable if you trust that tags are immutable (they are — socket-basics enforces tag protection rules). SHA pinning is still preferable for defence in depth.
- uses: SocketDev/socket-basics@v2.0.0
with:
socket_security_api_key: ${{ secrets.SOCKET_SECURITY_API_KEY }}Dependabot setup (works for both strategies)
Add or extend .github/dependabot.yml in your repo:
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"Dependabot opens a PR for each new release, updating the SHA or version tag
and keeping the # v2.0.0 comment in sync. You review, approve, and merge
on your own schedule — automated upgrades with a human gate.
Comparison
| Strategy | Immutable? | Auto-updates | Review gate |
|---|---|---|---|
@v2 floating tag |
❌ (not published) | — | — |
@v2.0.0 + Dependabot |
✅ (tag protection enforced) | Yes (weekly PR) | Yes |
@<sha> + Dependabot |
✅ always | Yes (weekly PR) | Yes |
Socket Basics requires the following permissions to post PR comments and create issues:
permissions:
issues: write # Create and update issues for findings
contents: read # Read repository contents
pull-requests: write # Post comments on pull requestsInclude these in your workflow's jobs.<job_id>.permissions section.
github_token (required)
- GitHub token for posting PR comments and API access
- Use
${{ secrets.GITHUB_TOKEN }}(automatically provided)
SAST (Static Analysis):
- uses: SocketDev/socket-basics@v2.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
# Enable SAST for specific languages
python_sast_enabled: 'true'
javascript_sast_enabled: 'true'
go_sast_enabled: 'true'
java_sast_enabled: 'true'
# Or enable all languages
all_languages_enabled: 'true'Secret Scanning:
- uses: SocketDev/socket-basics@v2.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
secret_scanning_enabled: 'true'
# Optional: exclude directories
trufflehog_exclude_dir: 'node_modules,vendor,dist'
# Optional: show unverified secrets
trufflehog_show_unverified: 'true'Container Scanning:
- uses: SocketDev/socket-basics@v2.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
# Scan Docker images (auto-enables container scanning)
container_images: 'myorg/myapp:latest,redis:7'
# Scan Dockerfiles (auto-enables Dockerfile scanning)
dockerfiles: 'Dockerfile,docker/Dockerfile.prod'Socket Tier 1 Reachability:
- uses: SocketDev/socket-basics@v2.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
socket_tier_1_enabled: 'true'- uses: SocketDev/socket-basics@v2.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
python_sast_enabled: 'true'
# Enable tabular console output
console_tabular_enabled: 'true'
# Or enable JSON output
console_json_enabled: 'true'
# Enable verbose logging for debugging
verbose: 'true'Socket Basics automatically posts enhanced PR comments with smart defaults that work out of the box — clickable file links, collapsible sections, syntax highlighting, CVE links, CVSS scores, and auto-labels are all enabled by default.
📖 PR Comment Guide → — Complete customization options, configuration examples, and reference table
Socket Basics Enterprise features require a Socket Enterprise subscription.
Configure Socket Basics centrally from the Socket Dashboard:
Setup:
- Log in to Socket Dashboard
- Navigate to Settings → Socket Basics
- Configure scanning policies, notification channels, and rule sets
- Save your configuration
Enable in workflow:
- uses: SocketDev/socket-basics@v2.0.0
env:
GITHUB_PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }}
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
# Dashboard configuration (Enterprise required)
socket_org: 'your-org-slug'
socket_security_api_key: ${{ secrets.SOCKET_SECURITY_API_KEY }}Note: You can also pass credentials using environment variables instead of the
with:section:- uses: SocketDev/socket-basics@v2.0.0 env: SOCKET_SECURITY_API_KEY: ${{ secrets.SOCKET_SECURITY_API_KEY }} with: github_token: ${{ secrets.GITHUB_TOKEN }}Both approaches work identically. Use whichever fits your workflow style.
Your workflow will automatically use the settings configured in the dashboard.
All notification integrations require Socket Enterprise.
Slack Notifications:
- uses: SocketDev/socket-basics@v2.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
socket_org: ${{ secrets.SOCKET_ORG }}
socket_security_api_key: ${{ secrets.SOCKET_SECURITY_API_KEY }}
python_sast_enabled: 'true'
# Slack webhook (Enterprise required)
slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }}Jira Issue Creation:
- uses: SocketDev/socket-basics@v2.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
socket_org: ${{ secrets.SOCKET_ORG }}
socket_security_api_key: ${{ secrets.SOCKET_SECURITY_API_KEY }}
python_sast_enabled: 'true'
# Jira integration (Enterprise required)
jira_url: 'https://your-org.atlassian.net'
jira_email: ${{ secrets.JIRA_EMAIL }}
jira_api_token: ${{ secrets.JIRA_API_TOKEN }}
jira_project: 'SEC'Microsoft Teams:
- uses: SocketDev/socket-basics@v2.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
socket_org: ${{ secrets.SOCKET_ORG }}
socket_security_api_key: ${{ secrets.SOCKET_SECURITY_API_KEY }}
python_sast_enabled: 'true'
# MS Teams webhook (Enterprise required)
msteams_webhook_url: ${{ secrets.MSTEAMS_WEBHOOK_URL }}Generic Webhook:
- uses: SocketDev/socket-basics@v2.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
socket_org: ${{ secrets.SOCKET_ORG }}
socket_security_api_key: ${{ secrets.SOCKET_SECURITY_API_KEY }}
python_sast_enabled: 'true'
# Generic webhook (Enterprise required)
webhook_url: ${{ secrets.WEBHOOK_URL }}SIEM Integration:
- uses: SocketDev/socket-basics@v2.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
socket_org: ${{ secrets.SOCKET_ORG }}
socket_security_api_key: ${{ secrets.SOCKET_SECURITY_API_KEY }}
python_sast_enabled: 'true'
# Microsoft Sentinel (Enterprise required)
ms_sentinel_workspace_id: ${{ secrets.MS_SENTINEL_WORKSPACE_ID }}
ms_sentinel_shared_key: ${{ secrets.MS_SENTINEL_SHARED_KEY }}
# Sumo Logic (Enterprise required)
sumologic_endpoint: ${{ secrets.SUMOLOGIC_ENDPOINT }}name: Comprehensive Security Scan
on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches: [main, develop]
jobs:
security-scan:
permissions:
issues: write
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Run Socket Basics
uses: SocketDev/socket-basics@v2.0.0
env:
GITHUB_PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }}
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
socket_org: ${{ secrets.SOCKET_ORG }}
socket_security_api_key: ${{ secrets.SOCKET_SECURITY_API_KEY }}
# Enable multiple languages
python_sast_enabled: 'true'
javascript_sast_enabled: 'true'
typescript_sast_enabled: 'true'
go_sast_enabled: 'true'
# Security scans
secret_scanning_enabled: 'true'
socket_tier_1_enabled: 'true'
# Container scanning
dockerfiles: 'Dockerfile'
# Notifications (Enterprise)
slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }}name: Weekly Security Audit
on:
schedule:
# Run every Monday at 9 AM UTC
- cron: '0 9 * * 1'
workflow_dispatch: # Allow manual trigger
jobs:
security-audit:
permissions:
issues: write
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Run Full Security Scan
uses: SocketDev/socket-basics@v2.0.0
env:
GITHUB_PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }}
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
socket_org: ${{ secrets.SOCKET_ORG }}
socket_security_api_key: ${{ secrets.SOCKET_SECURITY_API_KEY }}
# Scan all supported languages
all_languages_enabled: 'true'
# Enable all security features
secret_scanning_enabled: 'true'
socket_tier_1_enabled: 'true'
# Verbose output for audit trail
verbose: 'true'
console_tabular_enabled: 'true'
# Send to multiple channels (Enterprise)
slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }}
jira_url: ${{ secrets.JIRA_URL }}
jira_email: ${{ secrets.JIRA_EMAIL }}
jira_api_token: ${{ secrets.JIRA_API_TOKEN }}
jira_project: 'SEC'name: Container Security
on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches: [main]
paths:
- 'Dockerfile*'
- 'docker/**'
jobs:
container-scan:
permissions:
issues: write
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Build Docker Image
run: docker build -t myapp:${{ github.sha }} .
- name: Scan Container
uses: SocketDev/socket-basics@v2.0.0
env:
GITHUB_PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }}
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
# Scan built image and Dockerfile
container_images: 'myapp:${{ github.sha }}'
dockerfiles: 'Dockerfile'
# Additional Trivy options
trivy_vuln_enabled: 'true'For repositories with multiple Dockerfiles across different directories, you can automatically discover them instead of manually listing each path.
name: Security Scan with Dockerfile Auto-Discovery
on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches: [main]
jobs:
discover-dockerfiles:
runs-on: ubuntu-latest
outputs:
dockerfiles: ${{ steps.discover.outputs.dockerfiles }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Discover Dockerfiles
id: discover
run: |
DOCKERFILES=$(find . -type d \( \
-name node_modules -o -name vendor -o -name .git -o \
-name test -o -name tests -o -name testing -o -name __tests__ -o \
-name fixture -o -name fixtures -o -name testdata -o \
-name example -o -name examples -o -name sample -o -name samples -o \
-name dist -o -name build -o -name out -o -name target -o \
-name venv -o -name .venv -o -name .cache \
\) -prune -o \
-type f \( -name 'Dockerfile' -o -name 'Dockerfile.*' -o -name '*.dockerfile' \) \
-print | sed 's|^./||' | paste -sd ',' -)
echo "Discovered Dockerfiles: $DOCKERFILES"
echo "dockerfiles=$DOCKERFILES" >> $GITHUB_OUTPUT
security-scan:
needs: discover-dockerfiles
if: needs.discover-dockerfiles.outputs.dockerfiles != ''
permissions:
issues: write
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Run Socket Basics
uses: SocketDev/socket-basics@v2.0.0
env:
GITHUB_PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }}
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
dockerfiles: ${{ needs.discover-dockerfiles.outputs.dockerfiles }}
trivy_vuln_enabled: 'true'How it works:
-
Discovery job uses
findto locate Dockerfiles matching common patterns:Dockerfile(exact match)Dockerfile.*(e.g.,Dockerfile.prod,Dockerfile.dev)*.dockerfile(e.g.,backend.dockerfile)
-
Excluded directories prevent scanning test fixtures and build artifacts:
- Package managers:
node_modules,vendor,venv - Test directories:
test,tests,__tests__,fixtures - Build outputs:
dist,build,out,target
- Package managers:
-
Scan job receives discovered paths via job output and skips if none found
Customizing discovery patterns:
# Only scan production Dockerfiles
-type f -name 'Dockerfile.prod' -print
# Add custom exclusions
-name custom_test_dir -o -name legacy -o \name: Security Scan with Custom Rules
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
security-scan:
permissions:
issues: write
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Run Socket Basics
uses: SocketDev/socket-basics@v2.0.0
env:
GITHUB_PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }}
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
# Enable Python SAST
python_sast_enabled: 'true'
# Enable specific Python rules
python_enabled_rules: 'sql-injection,xss,hardcoded-credentials'
# Disable noisy rules
python_disabled_rules: 'unused-import,line-too-long'
# JavaScript with custom rules
javascript_sast_enabled: 'true'
javascript_enabled_rules: 'eval-usage,prototype-pollution'See action.yml for the complete list of inputs.
Core Configuration:
socket_org— Socket organization slug (Enterprise)socket_security_api_key— Socket Security API key (Enterprise)github_token— GitHub token (required)verbose— Enable verbose loggingconsole_tabular_enabled— Tabular console outputconsole_json_enabled— JSON console output
SAST Languages:
all_languages_enabled— Enable all languagespython_sast_enabled,javascript_sast_enabled,typescript_sast_enabledgo_sast_enabled,golang_sast_enabledjava_sast_enabled,php_sast_enabled,ruby_sast_enabledcsharp_sast_enabled,dotnet_sast_enabledc_sast_enabled,cpp_sast_enabledkotlin_sast_enabled,scala_sast_enabled,swift_sast_enabledrust_sast_enabled,elixir_sast_enabled
Rule Configuration (per language):
<language>_enabled_rules— Comma-separated rules to enable<language>_disabled_rules— Comma-separated rules to disable
Security Scanning:
secret_scanning_enabled— Enable secret scanningtrufflehog_exclude_dir— Directories to excludetrufflehog_show_unverified— Show unverified secretssocket_tier_1_enabled— Socket Tier 1 reachability
Container Scanning:
container_images— Comma-separated images to scandockerfiles— Comma-separated Dockerfiles to scantrivy_disabled_rules— Trivy rules to disabletrivy_vuln_enabled— Enable vulnerability scanning
Notifications (Enterprise Required):
slack_webhook_url— Slack webhookjira_url,jira_email,jira_api_token,jira_project— Jira configmsteams_webhook_url— MS Teams webhookwebhook_url— Generic webhookms_sentinel_workspace_id,ms_sentinel_shared_key— MS Sentinelsumologic_endpoint— Sumo Logic
Storage:
s3_enabled,s3_bucket,s3_access_key,s3_secret_key— S3 upload
All inputs support both standard and INPUT_ prefixed environment variables:
env:
INPUT_PYTHON_SAST_ENABLED: 'true'
INPUT_SECRET_SCANNING_ENABLED: 'true'
SOCKET_ORG: ${{ secrets.SOCKET_ORG }}
SOCKET_SECURITY_API_KEY: ${{ secrets.SOCKET_SECURITY_API_KEY }}Problem: Scanner reports no files found.
Solution: Ensure actions/checkout runs before Socket Basics:
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - Must be first
- uses: SocketDev/socket-basics@v2.0.0Problem: Security findings don't appear as PR comments.
Solutions:
- Verify
github_tokenis provided - Check workflow permissions:
permissions:
contents: read
pull-requests: writeProblem: Container image scanning fails.
Solutions:
- Ensure Docker is available in runner
- For private images, add authentication:
- name: Login to Registry
run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdinProblem: Dashboard configuration or notifications not working.
Solutions:
- Verify Socket Enterprise subscription is active
- Check that
socket_organdsocket_security_api_keyare set correctly - Confirm API key has required permissions in Socket Dashboard
Problem: Action runs out of memory.
Solutions:
- Exclude large directories:
trufflehog_exclude_dir: 'node_modules,vendor,dist,.git'- Scan specific languages instead of
all_languages_enabled - Use self-hosted runner with more resources
Problem: GitHub API rate limit exceeded.
Solution: Use a personal access token with higher limits:
with:
github_token: ${{ secrets.GITHUB_PAT }}Next Steps:
- Pre-Commit Hook Setup — Catch issues before commit
- Local Installation — Run scans from your terminal
- Configuration Guide — Detailed configuration options


