
Secure Github Actions
- 59 installs
- 9 repo stars
- Updated June 11, 2026
- vechain/vechain-ai-skills
Helps with security tasks.
About
secure-github-actions is a Claude Code skill for security. It helps solo builders move faster with AI-assisted coding.
- secure-github-actions
- Security
- AI-coding skill
Secure Github Actions by the numbers
- 59 all-time installs (skills.sh)
- +2 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #1,237 of 2,203 Security skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/vechain/vechain-ai-skills --skill secure-github-actionsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 59 |
|---|---|
| repo stars | ★ 9 |
| Last updated | June 11, 2026 |
| Repository | vechain/vechain-ai-skills ↗ |
What it does
Helps with security tasks.
Files
Secure GitHub Actions
Create, review, and audit GitHub Actions workflows with supply-chain-safe defaults.
CRITICAL RULES
1. Read the relevant reference files first. When the user's request involves any topic in the reference table below, read those files before doing anything else. Briefly mention which files you are reading so the user can confirm the skill is active. 2. Pin every non-local `uses:` reference to a full 40-character commit SHA. Treat @v*, @main, @master, branch names, and short SHAs as security debt. 3. Never invent SHAs. Resolve them from GitHub or ask the user; if you cannot verify the right SHA, say so explicitly instead of fabricating one. 4. Do not introduce `pull_request_target` unless the user explicitly requires it and the workflow never executes untrusted code with secrets or write permissions. 5. Never splice untrusted context directly into shell. Move ${{ github.* }}, ${{ inputs.* }}, and similar values into env: and quote the shell variable. 6. Set explicit least-privilege `permissions:`. Default to read-only and grant write scopes only to the specific job that needs them. 7. Always run the full audit checklist when asked to "audit", "harden", or "security scan" a repository. 8. Never silently skip a check. If a tool is missing (gitleaks, trufflehog, zizmor), report it and suggest installation. 9. After compaction or context loss, re-read this SKILL and the reference files before continuing.
Operating procedure
For writing or editing workflows
1. Classify the task: new workflow, workflow edit, reusable workflow, or security review. 2. Read references/workflows.md and references/secure-patterns.md. 3. Audit every uses: reference:
- Local actions like
./.github/actions/fooare fine. - Step-level actions and job-level reusable workflows must use full SHAs.
- Preserve the human release label in a comment (e.g.,
# v4.3.1).
4. Audit the trust boundary:
- Prefer
pull_requestoverpull_request_target. - Assume forked PR data is untrusted.
- Avoid exposing secrets or write tokens to untrusted code paths.
5. Audit every run: step:
- Pass dynamic values through
env:. - Quote shell variables.
- Prefer simple shell over adding a new third-party action when either works.
6. Add or update maintenance guardrails:
- Ensure Dependabot updates the
github-actionsecosystem. - Call out transitive risk: pinned actions can still reference mutable actions internally.
For full security audits
When asked to "audit", "harden", or "security scan" a repository:
1. Read references/audit-checklist.md. 2. Execute all checks, using subagents to parallelize where possible. 3. Present findings grouped by severity: CRITICAL, HIGH, MEDIUM, LOW. 4. End with a summary table and prioritized action list.
Reference files
| Topic | File | Read when... |
|---|---|---|
| Workflow hardening patterns | references/workflows.md | Creating, editing, or reviewing workflows |
| Secure workflow templates | references/secure-patterns.md | Writing new workflows from scratch |
| Full audit procedure | references/audit-checklist.md | Running a security audit on a repository |
| SHA pinning automation | references/sha-pinning.md | Pinning actions to commit SHAs |
Tools
The audit checks for these tools and reports missing ones:
| Tool | Purpose | Install |
|---|---|---|
gitleaks | Scan git history for secrets | brew install gitleaks |
trufflehog | Deep secrets scanning with verification | brew install trufflehog |
zizmor | Static analysis for GH Actions | brew install woodruffw/tap/zizmor |
gh | GitHub CLI for API calls | brew install gh |
Security Audit Checklist
Run every check when performing a full security audit. Group findings by severity.
Phase 1: Secrets Scanning
1.1 Gitleaks
gitleaks detect --source . -v --report-path /tmp/gitleaks-report.jsonInstall if missing: brew install gitleaks
1.2 TruffleHog
trufflehog git file://. --json > /tmp/trufflehog-report.jsonInstall if missing: brew install trufflehog
TruffleHog attempts to verify secrets. Findings with "verified": true are live, exploitable credentials -- treat as CRITICAL.
1.3 Classify findings
- Verified live secret (trufflehog verified=true): CRITICAL
- Real-looking unverified secret (API key, token, password): HIGH
- Example/placeholder in .env.example: FALSE POSITIVE (
NEXT_PUBLIC_*keys are client-side) - Local dev credential (localhost passwords, test mnemonics): LOW
- Package integrity hash (yarn.lock SHA-1): FALSE POSITIVE
1.4 Manual pattern search (current HEAD)
grep -rn "AKIA" --include="*.ts" --include="*.js" --include="*.json" --include="*.yml" --include="*.env*" . | grep -v node_modules
grep -rn "ghp_\|gho_\|github_pat_" --include="*.ts" --include="*.js" --include="*.json" . | grep -v node_modules
find . -name "*.pem" -o -name "*.key" -o -name "*.p12" | grep -v node_modules | grep -v .git
git ls-files | grep -i "\.env" | grep -v example | grep -v templatePhase 2: Sensitive Files
2.1 Tracked files that should not be
git ls-files | grep -E "\.tfstate|terraform\.tfvars"
find . -name "*.sqlite" -o -name "*.db" | grep -v node_modules | grep -v .git2.2 .gitignore completeness
Verify these patterns exist: .env, .env.*, !.env.example, node_modules/, *.tfstate, *.tfvars, .terraform/, coverage/, dist/, build/
2.3 Large binary files
find . -not -path "*/.git/*" -not -path "*/node_modules/*" -size +1M -type fPhase 3: GitHub Actions Security
3.1 Zizmor scan
High-severity, high-confidence findings (recommended for audits):
zizmor --min-severity high --min-confidence high --persona auditor .github/workflows/*Broader scan including medium findings:
zizmor --min-severity medium --min-confidence medium --persona auditor .github/workflows/*Auto-fix safe issues:
zizmor --min-severity medium --min-confidence high --persona auditor .github/workflows/* --fix safeInstall if missing: brew install woodruffw/tap/zizmor
3.2 Permissions audit
for f in .github/workflows/*.yml .github/workflows/*.yaml; do
[ -f "$f" ] || continue
name=$(basename "$f")
if grep -q "permissions" "$f"; then
echo "OK: $name"
else
echo "MISSING: $name -- inherits repo default (likely write-all)"
fi
done3.3 Dangerous trigger patterns
grep -rn "pull_request_target" .github/workflows/
grep -rn "workflow_run" .github/workflows/
grep -rn '\${{.*github\.event\.\(issue\|pull_request\|comment\|review\)' .github/workflows/3.4 Secrets inheritance
grep -rn "secrets: inherit" .github/workflows/3.5 Self-hosted runners
grep -rn "self-hosted\|runs-on.*self" .github/workflows/Self-hosted runners in public repos = CRITICAL risk.
3.6 Third-party action inventory
grep -rh "uses:" .github/workflows/ | grep -v "#" | grep -v "\./" | sed 's/.*uses: //' | sed 's/@.*//' | sort -u3.7 Concurrency guards
Verify deploy and infrastructure workflows have concurrency: settings.
Phase 4: Repository Configuration
4.1 Required files for public repos
| File | Purpose |
|---|---|
LICENSE | Legal framework |
SECURITY.md | Vulnerability reporting |
CODEOWNERS | Mandatory reviewers for sensitive paths |
.github/dependabot.yml | Automated dependency updates |
4.2 Internal references
grep -rn "slack\.com\|confluence\|jira\.\|notion\.so" --include="*.ts" --include="*.js" --include="*.md" . | grep -v node_modules4.3 Infrastructure exposure
grep -rn "arn:aws" --include="*.tf" --include="*.yaml" . | grep -v node_modules
grep -rn "secrets\." .github/workflows/ | sed 's/.*secrets\./secrets./' | sort -uNOTE: AWS account IDs and VPC IDs are not secrets. Flag for awareness, not as CRITICAL.
Phase 5: Report
Present findings using this format:
## Security Audit Report -- [repo-name]
**Date:** YYYY-MM-DD | **Tools:** gitleaks, trufflehog, zizmor
| Severity | Count |
|----------|-------|
| CRITICAL | X |
| HIGH | X |
| MEDIUM | X |
| LOW | X |
### Findings
#### CRITICAL
1. [Title] -- [file:line] -- [description] -- [remediation]
### Action Items (Priority Order)
1. ...Secure Workflow Patterns
Apply these patterns when writing new workflows or fixing existing ones.
Permission reference table
| Workflow type | Permissions needed |
|---|---|
| Lint / test / build | contents: read |
| Deploy via OIDC | contents: read, id-token: write |
| Create release | contents: write |
| Comment on PR | contents: read, pull-requests: write |
| Push to GHCR | contents: read, packages: write |
| Upload SARIF | contents: read, security-events: write |
| Label PRs | contents: read, pull-requests: write |
Explicit secrets (no secrets: inherit)
When calling reusable workflows, pass only the secrets needed:
# BAD -- passes ALL repo secrets
secrets: inherit
# GOOD -- explicit, auditable
secrets:
AWS_ROLE_ARN: ${{ secrets.AWS_ROLE_ARN }}Concurrency guards
Deploy workflows must prevent parallel runs:
concurrency:
group: deploy-${{ github.ref }}
cancel-in-progress: false # Don't cancel in-progress deploysFor CI (tests, lint), cancel previous runs:
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: trueCODEOWNERS for workflow protection
# .github/CODEOWNERS
.github/workflows/ @org/security-team @org/devops-team
.github/dependabot.yml @org/security-teamCombined with branch protection rules requiring CODEOWNERS approval, this prevents unauthorized workflow changes.
Complete secure workflow template
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@<FULL_40_CHAR_SHA> # v4.x.x
- uses: actions/setup-node@<FULL_40_CHAR_SHA> # v4.x.x
with:
node-version-file: .nvmrc
cache: "npm"
- run: npm ci
- run: npm testSHA Pinning Reference
Why pin to SHA
Git tags are mutable. A malicious actor who compromises an action's repository can move the v4 tag to a different commit containing backdoored code. SHA pins are immutable and prevent supply-chain attacks.
Manual SHA resolution
# Find latest specific version for a major tag
gh api /repos/actions/checkout/git/refs/tags/v4. --jq '.[].ref' | sort -V | tail -1
# Get SHA for that tag
gh api /repos/actions/checkout/git/refs/tags/v4.3.1 --jq '.object.sha'
# For annotated tags, dereference
SHA=$(gh api /repos/OWNER/REPO/git/refs/tags/TAG --jq '.object.sha')
TYPE=$(gh api /repos/OWNER/REPO/git/refs/tags/TAG --jq '.object.type')
if [ "$TYPE" = "tag" ]; then
SHA=$(gh api /repos/OWNER/REPO/git/tags/$SHA --jq '.object.sha')
fi
echo $SHADependabot and SHA pins
Dependabot reads the version comment (# v4.3.1) to track pinned versions. When a new release appears, Dependabot opens a PR updating both the SHA and the comment.
Without the version comment, Dependabot cannot determine the current version and may not propose updates.
Required .github/dependabot.yml:
version: 2
updates:
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: weeklySpecific version comments
Always use the most specific version in comments, not just the major tag:
# GOOD -- Dependabot can track and update incrementally
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
# BAD -- Dependabot cannot determine the exact version
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4To find the specific version a major tag points to, list all tags under that prefix:
gh api /repos/actions/checkout/git/refs/tags/v4. --jq '.[].ref' | sort -V | tail -1
# Output: refs/tags/v4.3.1Workflow Hardening Reference
Quick checklist
- Pin every external
uses:reference to a full 40-character SHA. - Do not guess SHAs.
- Prefer
pull_requestoverpull_request_target. - Declare explicit
permissions:. - Pass
github.*,inputs.*, and other dynamic values intorun:viaenv:. - Prefer fewer third-party actions.
- Add Dependabot updates for
github-actions. - Remember that SHA pinning is necessary but not sufficient: pinned actions can still depend on mutable actions internally.
Pinning rules
Pin both step-level actions and job-level reusable workflows:
- uses: actions/checkout@<FULL_40_CHAR_SHA> # v4.3.1
- uses: aquasecurity/trivy-action@<FULL_40_CHAR_SHA> # v0.28.0
jobs:
ci:
uses: owner/repo/.github/workflows/reusable.yml@<FULL_40_CHAR_SHA> # v1.2.0Do not leave mutable refs in place:
# BAD
- uses: actions/checkout@v4
- uses: aquasecurity/trivy-action@main
- uses: owner/repo/.github/workflows/reusable.yml@v1- Local actions like
./.github/actions/foodo not need pinning. - Short SHAs are not enough. Use all 40 characters.
- If you cannot verify the exact SHA in the current environment, stop and tell the user.
Prefer fewer dependencies
- Before adding a third-party action, ask whether plain
run:or a GitHub-maintained action is enough. - Treat scanners, deploy or publish actions, artifact or comment actions, and credential-handling actions as especially sensitive.
Events and permissions
- Prefer
pull_requestfor untrusted fork contributions. - Avoid
pull_request_targetunless the workflow only performs safe, non-executing metadata tasks. - Set explicit
permissions:. Common safe default:
permissions:
contents: read- Grant write scopes only per job when required.
Shell injection safety
Bad:
- run: echo "${{ github.event.pull_request.title }}"Good:
- env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: echo "$PR_TITLE"Apply the same pattern to github.head_ref, github.ref_name, inputs.*, matrix.*, and any value that can contain spaces or shell metacharacters. Quote variables in shell and prefer small scripts over dense one-liners.
Dependabot
Use .github/dependabot.yml to keep pinned SHAs current:
version: 2
updates:
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: weeklyReview updates before merging. Keep the human version hint in comments so reviewers can map a SHA back to a release.
Review prompts
When auditing workflows, actively search for:
@v,@main,@master, or short SHAs in anyuses:linepull_request_target- Missing or overly broad
permissions: - Direct
${{ ... }}interpolation insiderun: secrets: inheritinstead of explicit secrets- Unnecessary third-party actions