Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
209 changes: 209 additions & 0 deletions .github/workflows/snyk-scan.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
name: Snyk Security Scan

on:
workflow_call:
inputs:
fail-on-issues:
required: false
type: boolean
default: true
description: "Whether to fail the build if vulnerabilities are found"

severity-threshold:
required: false
type: string
default: "high"
description: "Minimum severity to fail on (low|medium|high|critical)"

target-file:
required: false
type: string
description: "Specific file to scan (e.g., package.json, go.mod, pom.xml)"

command:
required: false
type: string
default: "test"
description: "Snyk command to run (test|monitor|iac test|container test)"

args:
required: false
type: string
default: ""
description: "Additional arguments to pass to Snyk CLI"

snyk-org:
required: false
type: string
description: "Snyk organization slug or ID"

debug:
required: false
type: boolean
default: false
description: "Enable debug mode (-d flag) for verbose output"

upload-sarif:
required: false
type: boolean
default: true
description: "Whether to upload SARIF results to GitHub Security"

sarif-file-output:
required: false
type: string
default: "snyk.sarif"
description: "Path for SARIF output file"

json-file-output:
required: false
type: string
default: "snyk.json"
description: "Path for JSON output file"

outputs:
vulnerabilities-found:
description: "Whether vulnerabilities were found (true/false)"
value: ${{ jobs.snyk-scan.outputs.vulnerabilities-found }}

jobs:
snyk-scan:
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
actions: read
outputs:
vulnerabilities-found: ${{ steps.snyk-scan.outputs.vulnerabilities-found }}

steps:
- name: Checkout repository
uses: actions/checkout@v5

- name: Setup Snyk
uses: snyk/actions/setup@master

- name: Run Snyk Security Scan
id: snyk-scan
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
run: |
set +e # Don't exit on error immediately

# Verify token is set
if [ -z "$SNYK_TOKEN" ]; then
echo "❌ ERROR: SNYK_TOKEN is not set"
exit 1
fi

echo "✅ SNYK_TOKEN is set (length: ${#SNYK_TOKEN})"

# Build the Snyk command
SNYK_CMD="snyk ${{ inputs.command }}"

# Add debug flag if enabled
if [ "${{ inputs.debug }}" = "true" ]; then
SNYK_CMD="$SNYK_CMD -d"
echo "🐛 Debug mode enabled"
fi

# Add organization if specified
if [ -n "${{ inputs.snyk-org }}" ]; then
SNYK_CMD="$SNYK_CMD --org=${{ inputs.snyk-org }}"
fi

# Add severity threshold
SNYK_CMD="$SNYK_CMD --severity-threshold=${{ inputs.severity-threshold }}"

# Add SARIF output if upload is enabled
if [ "${{ inputs.upload-sarif }}" = "true" ]; then
SNYK_CMD="$SNYK_CMD --sarif-file-output=${{ inputs.sarif-file-output }}"
fi

# Add JSON output for detailed reporting
SNYK_CMD="$SNYK_CMD --json-file-output=${{ inputs.json-file-output }}"

# Add target file if specified
if [ -n "${{ inputs.target-file }}" ]; then
SNYK_CMD="$SNYK_CMD --file=${{ inputs.target-file }}"
fi

# Add additional args if specified
if [ -n "${{ inputs.args }}" ]; then
SNYK_CMD="$SNYK_CMD ${{ inputs.args }}"
fi

echo "Running: $SNYK_CMD"

# Run Snyk scan
eval "$SNYK_CMD"
SNYK_EXIT_CODE=$?

# Set output based on exit code
if [ "$SNYK_EXIT_CODE" -eq 0 ]; then
echo "vulnerabilities-found=false" >> "$GITHUB_OUTPUT"
echo "✅ No vulnerabilities found"
else
echo "vulnerabilities-found=true" >> "$GITHUB_OUTPUT"
echo "⚠️ Vulnerabilities found or scan error occurred"
fi

# Handle fail-on-issues flag
if [ "${{ inputs.fail-on-issues }}" = "true" ]; then
echo "fail-on-issues is enabled, preserving exit code"
exit "$SNYK_EXIT_CODE"
else
echo "fail-on-issues is disabled, reporting only"
exit 0
fi

- name: Parse Snyk results and create summary
if: always() && hashFiles(inputs.json-file-output) != ''
run: |
echo "## 🔍 Snyk Security Scan Results" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"

if [ -f "${{ inputs.json-file-output }}" ]; then
# Extract vulnerability counts by severity
CRITICAL=$(jq '[.vulnerabilities[]? | select(.severity=="critical")] | length' "${{ inputs.json-file-output }}" 2>/dev/null || echo "0")
HIGH=$(jq '[.vulnerabilities[]? | select(.severity=="high")] | length' "${{ inputs.json-file-output }}" 2>/dev/null || echo "0")
MEDIUM=$(jq '[.vulnerabilities[]? | select(.severity=="medium")] | length' "${{ inputs.json-file-output }}" 2>/dev/null || echo "0")
LOW=$(jq '[.vulnerabilities[]? | select(.severity=="low")] | length' "${{ inputs.json-file-output }}" 2>/dev/null || echo "0")

{
echo "| Severity | Count |"
echo "|----------|-------|"
echo "| 🔴 Critical | $CRITICAL |"
echo "| 🟠 High | $HIGH |"
echo "| 🟡 Medium | $MEDIUM |"
echo "| 🟢 Low | $LOW |"
echo ""
} >> "$GITHUB_STEP_SUMMARY"

TOTAL=$((CRITICAL + HIGH + MEDIUM + LOW))
if [ "$TOTAL" -gt 0 ]; then
echo "⚠️ **Total vulnerabilities found: $TOTAL**" >> "$GITHUB_STEP_SUMMARY"
else
echo "✅ **No vulnerabilities found**" >> "$GITHUB_STEP_SUMMARY"
fi
else
echo "ℹ️ No JSON output file found" >> "$GITHUB_STEP_SUMMARY"
fi

- name: Upload Snyk SARIF results to GitHub Security
if: always() && inputs.upload-sarif == true && hashFiles(inputs.sarif-file-output) != ''
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: ${{ inputs.sarif-file-output }}
category: snyk-${{ inputs.command }}

- name: Upload Snyk results as artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: snyk-results-${{ github.run_id }}
path: |
${{ inputs.sarif-file-output }}
${{ inputs.json-file-output }}
retention-days: 30
if-no-files-found: ignore
53 changes: 49 additions & 4 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,54 @@
# Reusable GitHub Actions

This directory contains documentation for the reusable GitHub actions that are
available.
available for use across the organization.

## Actions
## Available Actions

- [Publish Docker Images to Artifact Registry](./publish-docker/)
- [Publish Kustomize Bundle to GHCR](./publish-kustomize-bundle/)
### Security & Compliance

- [**Snyk Security Scan**](./snyk-scan/) - Comprehensive security vulnerability scanning for dependencies, IaC, containers, and code using Snyk

### Publishing & Distribution

- [**Publish Docker Images**](./publish-docker/) - Build and push Docker images to GitHub Container Registry
- [**Publish Kustomize Bundle**](./publish-kustomize-bundle/) - Build and push Kustomize bundles to GitHub Container Registry

### Validation & Linting

- [**Lint GitHub Actions Workflows**](./lint-workflows/) - Validate workflow files using actionlint to catch errors and best practice violations
- [**Validate Kustomize Configurations**](./validate-kustomize/) - Validate all Kustomize configurations by building them and checking for errors

## Usage

All workflows are designed to be used as reusable workflows. Reference them in your repository workflows using:

```yaml
jobs:
job-name:
uses: datum-cloud/actions/.github/workflows/<workflow-name>.yaml@v1
with:
# inputs here
secrets: inherit
```

## Best Practices

- **Use tagged versions**: Always reference workflows with a specific version tag (e.g., `@v1`) to prevent unexpected breaking changes
- **Inherit secrets**: Use `secrets: inherit` to pass repository secrets to reusable workflows
- **Set permissions**: Explicitly define required permissions in your workflow
- **Review documentation**: Check individual action documentation for specific requirements and examples

## Contributing

When adding new reusable workflows:

1. Create the workflow file in `.github/workflows/`
2. Add comprehensive documentation in `docs/<workflow-name>/README.md`
3. Follow the established documentation structure
4. Update this README to include the new action
5. Create a release with appropriate version tags

## Support

For issues or questions about these actions, please open an issue in the repository.
Loading