Reusable workflow for comprehensive security scanning on pull requests. Supports single app repositories and monorepos with automatic detection of changed components.
- Secret scanning: Trivy filesystem scan for exposed secrets (scans only changed component folder)
- Vulnerability scanning: Docker image vulnerability detection (optional)
- CodeQL static analysis: GitHub CodeQL for semantic code analysis (opt-in via
enable_codeql) - Pre-release version gate: Blocks dependencies pinned to
-betaor-rcversions (enabled by default) - CLI/Non-Docker support: Skip Docker scanning for projects without Dockerfile via
enable_docker_scan: false - Monorepo support: Automatic detection of changed components
- Component-scoped scanning: Only scans the specific component folder that changed, not entire repo
- Multiple architectures: Type 1 and Type 2 monorepo patterns
- SARIF output: Security results in standard format
- Fail-fast on secrets: Workflow fails if secrets are detected
- Docker Hub login: Avoid rate limits during scans
- Slack notifications: Automatic success/failure notifications
Scans entire repository when filter_paths is not provided.
Components in separate folders with individual Dockerfiles:
project/
├── components/
│ ├── onboarding/
│ │ └── Dockerfile
│ ├── transaction/
│ │ └── Dockerfile
│ └── console/
│ └── Dockerfile
Backend in root with frontend in separate folder:
project/
├── Dockerfile # Backend
├── api/
├── cmd/
├── internal/
└── frontend/
└── Dockerfile # Frontend
name: PR Security Scan
on:
pull_request:
branches: [develop, release-candidate, main]
jobs:
security-scan:
uses: LerianStudio/github-actions-shared-workflows/.github/workflows/pr-security-scan.yml@v1.0.0
with:
runner_type: "blacksmith-4vcpu-ubuntu-2404"
dockerhub_org: "lerianstudio"
secrets: inheritname: PR Security Scan
on:
pull_request:
branches: [develop, release-candidate, main]
jobs:
security-scan:
uses: LerianStudio/github-actions-shared-workflows/.github/workflows/pr-security-scan.yml@v1.0.0
with:
runner_type: "blacksmith-4vcpu-ubuntu-2404"
filter_paths: |-
components/onboarding
components/transaction
components/console
path_level: "2"
dockerhub_org: "lerianstudio"
secrets: inheritname: PR Security Scan
on:
pull_request:
branches: [develop, release-candidate, main]
jobs:
security-scan:
uses: LerianStudio/github-actions-shared-workflows/.github/workflows/pr-security-scan.yml@v1.0.0
with:
runner_type: "blacksmith-4vcpu-ubuntu-2404"
filter_paths: |-
frontend
cmd
internal
api
pkg
.
path_level: "1"
monorepo_type: "type2"
frontend_folder: "frontend"
dockerhub_org: "lerianstudio"
secrets: inheritFor projects without a Dockerfile (e.g., CLI tools), disable Docker scanning to only run filesystem secret scanning:
name: PR Security Scan
on:
pull_request:
branches: [develop, release-candidate, main]
jobs:
security-scan:
uses: LerianStudio/github-actions-shared-workflows/.github/workflows/pr-security-scan.yml@v1.0.0
with:
runner_type: "blacksmith-4vcpu-ubuntu-2404"
enable_docker_scan: false
secrets: inheritThis will:
- ✅ Run Trivy filesystem secret scanning
- ❌ Skip Docker image build
- ❌ Skip Docker vulnerability scanning
- ❌ Skip Docker Scout analysis
Enable CodeQL for semantic static analysis on top of the standard security scans:
name: PR Security Scan
on:
pull_request:
branches: [develop, release-candidate, main]
jobs:
security-scan:
uses: LerianStudio/github-actions-shared-workflows/.github/workflows/pr-security-scan.yml@v1.0.0
with:
runner_type: "blacksmith-4vcpu-ubuntu-2404"
enable_codeql: true
codeql_languages: 'go'
secrets: inheritThis will run all standard scans plus CodeQL analysis scoped to changed paths. Results are posted as a PR comment. To also upload SARIF to the GitHub Security tab, set codeql_upload_sarif: true (requires Code Security / GHAS enabled on the repo).
Supported languages: go, javascript-typescript, actions, python, java-kotlin, csharp, ruby, swift, cpp
Pre-release checks are enabled by default. To disable:
jobs:
security-scan:
uses: LerianStudio/github-actions-shared-workflows/.github/workflows/pr-security-scan.yml@v1.0.0
with:
enable_prerelease_check: false
secrets: inheritWhen enabled, the workflow scans go.mod, package.json, and Dockerfile for unstable version pins (-alpha, -beta, -rc, -dev, etc.). On branches listed in prerelease_block_branches (default: release-candidate,main) the PR is blocked. On other branches (e.g., develop) findings are reported as warnings only.
| Input | Type | Default | Description |
|---|---|---|---|
runner_type |
string | blacksmith-4vcpu-ubuntu-2404 |
GitHub runner type |
filter_paths |
string | - | Paths to monitor (newline separated). If empty, treats as single app |
path_level |
string | 2 |
Directory depth level to extract app name (monorepo only) |
monorepo_type |
string | type1 |
Monorepo type: type1 or type2 |
frontend_folder |
string | frontend |
Frontend folder name for type2 monorepos |
dockerhub_org |
string | lerianstudio |
DockerHub organization name |
docker_registry |
string | docker.io |
Docker registry URL |
dockerfile_name |
string | Dockerfile |
Name of the Dockerfile |
enable_docker_scan |
boolean | true |
Enable Docker image build and vulnerability scanning. Set to false for projects without Dockerfile (e.g., CLI tools) |
enable_health_score |
boolean | true |
Enable Docker Hub Health Score compliance checks (non-root user, CVEs, licenses) |
enable_codeql |
boolean | false |
Enable CodeQL static analysis. Requires codeql_languages to be set |
codeql_languages |
string | '' |
Languages to analyze with CodeQL (comma-separated, e.g., go, javascript-typescript, actions) |
codeql_fail_on_findings |
boolean | true |
Fail the workflow when CodeQL detects security issues |
codeql_upload_sarif |
boolean | false |
Upload CodeQL SARIF results to the GitHub Security tab. Requires Code Security (GHAS) enabled on the repo |
enable_prerelease_check |
boolean | true |
Block dependencies pinned to pre-release versions (-beta, -rc) |
prerelease_block_branches |
string | release-candidate,main |
Comma-separated PR target branches where pre-release versions cause a hard failure. On other branches, findings are reported as warnings only |
| Secret | Description |
|---|---|
docker_username |
Docker registry username |
docker_password |
Docker registry password |
| Secret | Description | Required When |
|---|---|---|
manage_token |
GitHub token for private repositories | Building images that need private repo access |
permissions:
id-token: write # Required for OIDC authentication
contents: read # Required to checkout the repository
pull-requests: write # Allows commenting on PRs
security-events: write # Required for security scanning- Docker Login: Authenticate to avoid rate limits
- Get Changed Paths: Detect which components changed (monorepo only)
- Set Matrix: Build matrix of components to scan
For each component in the matrix:
- Docker Login: Authenticate to registry (avoids rate limits)
- Checkout Repository: Clone the code
- Setup Docker Buildx: Enable multi-platform builds (skipped if
enable_docker_scan: false) - Trivy Filesystem Scan: Scan filesystem for secrets and vulnerabilities
- Build Docker Image: Build image for vulnerability scanning (skipped if
enable_docker_scan: false) - Trivy Image Scan: Scan image for vulnerabilities and licenses (skipped if
enable_docker_scan: false) - Dockerfile Compliance Checks: Non-root user and health score checks (skipped unless
enable_health_score: trueANDenable_docker_scan: true) - Pre-release Version Check: Scan for
-beta/-rcversion pins (skipped ifenable_prerelease_check: false) - Post Security Scan Results: PR comment with consolidated findings
Note: When
enable_docker_scan: false, only filesystem scanning and pre-release checks run.
Runs when enable_codeql: true and codeql_languages is set:
- Checkout Repository: Clone the code
- Extract Changed Paths: Derive scoped paths from the component matrix
- Generate CodeQL Config: Scope analysis to changed paths
- Initialize CodeQL: Set up CodeQL with configured languages and query suite
- Autobuild: Automatically build the project for compiled languages
- Perform CodeQL Analysis: Run semantic analysis and upload SARIF
- Post CodeQL Results: PR comment with findings table and security gate
What it does: Scans repository filesystem for exposed secrets (API keys, tokens, passwords)
Severity: CRITICAL - workflow fails if secrets are found
Skipped directories:
.gitnode_modulesdistbuild.nextcoveragevendor
Exit behavior: exit-code: 1 (fails workflow)
What it does: Scans Docker image for known vulnerabilities
Severity levels: CRITICAL, HIGH, MEDIUM, LOW, UNKNOWN
Vulnerability types:
- OS packages
- Application libraries
Exit behavior: exit-code: 0 (informative only, doesn't fail workflow)
What it does: Runs GitHub CodeQL semantic analysis for security vulnerabilities and code quality issues
Scope: Automatically scoped to changed paths in the PR (via codeql-config composite)
Query suite: security-extended (default) — covers OWASP Top 10, CWE Top 25, and more
Exit behavior: Configurable via codeql_fail_on_findings (default: fails on findings)
What it does: Scans go.mod, package.json, and Dockerfile for unstable version pins
Pattern matched: X.Y.Z-<letter...> for Go/npm (any pre-release suffix starting with a letter). For Docker, only known pre-release prefixes: -alpha, -beta, -rc, -dev, -preview, -canary, -snapshot, -nightly. Stable Docker variants like -slim, -alpine, -bookworm are allowed.
Exit behavior: exit-code: 1 on branches listed in prerelease_block_branches (default: release-candidate,main). On other branches (e.g., develop), findings are reported as warnings only.
Any changes to backend folders (api, cmd, internal, pkg, .) are consolidated to root:
{
"name": "repository-name",
"working_dir": "."
}Frontend changes are kept separate:
{
"name": "repository-name-frontend",
"working_dir": "frontend"
}Changes to .github and .githooks are ignored in Type 2 monorepos.
on:
pull_request:
branches: [develop, release-candidate, main]with:
runner_type: "blacksmith-4vcpu-ubuntu-2404"secrets: inheritThis passes all repository secrets to the workflow automatically.
Type 1 Monorepo (components in folders):
with:
path_level: "2" # components/onboarding → onboardingType 2 Monorepo (backend in root):
with:
path_level: "1" # api → api, frontend → frontendInclude all directories that should trigger scans:
with:
filter_paths: |-
api
cmd
internal
pkg
frontend
.Issue: Security scan doesn't run on PR
Solutions:
- Check if changed paths match
filter_paths - Verify PR targets correct branch
- Check workflow permissions
Issue: Cannot build Docker image
Solutions:
- Provide
manage_tokenfor private dependencies - Check Dockerfile path
- Verify Docker registry credentials
Issue: Docker Hub rate limit (429 errors)
Solution: Workflow includes automatic Docker login to avoid rate limits. Ensure docker_username and docker_password secrets are set.
Issue: Scan detects test secrets or examples
Solutions:
- Move test secrets to
.env.examplefiles - Use placeholder values in documentation
- Add comments to indicate test data
Issue: All components scanned instead of only changed ones
Solution: Verify filter_paths configuration matches your repository structure.
security-scan:
uses: LerianStudio/github-actions-shared-workflows/.github/workflows/pr-security-scan.yml@v1.0.0
with:
dockerhub_org: "mycompany"
docker_registry: "ghcr.io"
secrets: inheritNote: For GitHub Container Registry (ghcr.io), ensure
DOCKER_USERNAMEandDOCKER_PASSWORDsecrets are configured appropriately.
security-scan:
uses: LerianStudio/github-actions-shared-workflows/.github/workflows/pr-security-scan.yml@v1.0.0
with:
runner_type: "blacksmith-4vcpu-ubuntu-2404"
filter_paths: |-
services/auth
services/payment
services/notification
services/user
path_level: "2"
monorepo_type: "type1"
secrets: inheritsecurity-scan:
uses: LerianStudio/github-actions-shared-workflows/.github/workflows/pr-security-scan.yml@v1.0.0
with:
runner_type: "blacksmith-4vcpu-ubuntu-2404"
filter_paths: |-
web
api
cmd
internal
.
path_level: "1"
monorepo_type: "type2"
frontend_folder: "web"
secrets: inheritname: Pull Request Checks
on:
pull_request:
branches: [develop, release-candidate, main]
permissions:
id-token: write
contents: read
pull-requests: write
security-events: write
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run linters
run: make lint
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tests
run: make test
security-scan:
uses: LerianStudio/github-actions-shared-workflows/.github/workflows/pr-security-scan.yml@v1.0.0
with:
runner_type: "blacksmith-4vcpu-ubuntu-2404"
filter_paths: |-
components/onboarding
components/transaction
path_level: "2"
secrets: inheritDisplayed in workflow logs for quick review:
┌─────────────────────────────────────────────────────────────┐
│ Trivy Secret Scan Results │
├─────────────────────────────────────────────────────────────┤
│ No secrets found │
└─────────────────────────────────────────────────────────────┘
Generated for each scan type:
trivy-secret-scan-repo-{app-name}.sariftrivy-vulnerability-scan-docker-{app-name}.sarif
Uploaded to GitHub Security tab via CodeQL when enable_codeql is enabled.
- GitOps Update - Update deployments after security checks pass
- API Dog E2E Tests - Run E2E tests after security validation
- Release Workflow - Create releases after all checks pass