GitHub composite action (and reusable workflow) that runs a multi-scanner security pipeline on every push and pull request: it uploads results to GitHub code scanning as SARIF, can fail the build on a severity threshold, and optionally posts a severity-coloured summary to Discord and runs an AI review of the PR diff.
- Usable as a Marketplace action or a reusable workflow - drop in a single
- uses: w1ck3ds0d4/SecureCheck@v1step, or call the reusable workflow - SARIF + GitHub code scanning - Gitleaks, Semgrep and Trivy findings upload to the Security tab and appear as inline PR annotations
- Severity gate -
fail-onblocks merges at a chosen threshold (low/medium/high/critical); detected secrets always count as critical - Centralised scanner stack - scanners and rules live in one repo; each consumer repo contains a thin caller, so updates propagate without touching every project
- Gitleaks - hardcoded secrets in the working tree and history, on every push and PR
- Semgrep - pattern-based SAST using the curated
autoruleset - Trivy - dependency CVEs, IaC / configuration misconfigurations, plus license findings (vuln / misconfig / license counts surface separately)
- Per-language quality linters (auto-detected) - ESLint (JS/TS, when an eslint config exists), ruff lint + format check (Python),
cargo fmt --check+cargo clippy -D warnings(Rust),dotnet format --verify-no-changes(.NET). Skipped silently when the language isn't present - Complexity hotspots -
lizardflags functions with cyclomatic complexity >= 15 - Duplication detection -
jscpdreports duplicated code blocks across the repo - Performance signals - each scanner stage is timed; the embed surfaces total scan time plus the 3 slowest stages
- Optional Claude review - Claude Sonnet reviews the PR diff for logic issues scanners miss; gated on
ANTHROPIC_API_KEYand skipped silently when the secret is not set - Single Discord embed per run - severity colouring, per-scanner + per-linter + metrics + performance sections, direct links to the workflow run and pull request
- Pull-request heartbeat - an embed is posted for every PR run, including clean ones, so reviewers can see the bot executed
- Silent on clean pushes - no Discord noise for green
maincommits - Archived raw reports - each run uploads per-scanner JSON (security + quality + metrics) as a workflow artifact with 14-day retention
- A GitHub repository you want to scan, with Actions enabled
- A Discord channel with an incoming webhook (channel settings, Integrations, Webhooks, New Webhook, Copy Webhook URL)
- Optional: an Anthropic API key if you want the Claude review step to run on pull requests
Create .github/workflows/security.yml in the consumer repo. Recommended (composite action):
name: Security
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
security-events: write # required to upload SARIF to code scanning
jobs:
securecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # full history for gitleaks + PR diff
- uses: w1ck3ds0d4/SecureCheck@v1 # pin @v1 for the stable line, @main for latest
with:
fail-on: highPin @v1 for the stable line (or @main for latest). The action checks out its own scripts at the same ref you pin, so a pinned caller is fully reproducible.
Prefer to have the whole job managed for you? Use the reusable workflow instead:
permissions:
contents: read
security-events: write
jobs:
scan:
uses: w1ck3ds0d4/SecureCheck/.github/workflows/scan.yml@v1
with:
fail-on: high
secrets:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} # optionalMapping the two secrets explicitly (rather than secrets: inherit) follows least privilege - only these two are ever passed; both are optional.
The reusable workflow accepts these inputs; the composite action's full input list is in Usage below.
| Input | Default | Meaning |
|---|---|---|
node_version |
20 |
Node version for ESLint / jscpd |
python_version |
3.11 |
Python version for ruff |
dotnet_version |
10.0.x |
.NET SDK for dotnet format |
style_check |
false |
Opt-in house-style gate (fails on an introduced em dash); off by default |
| Secret | Required | Used for |
|---|---|---|
DISCORD_WEBHOOK_URL |
no | Posting the run summary embed; omit to skip the Discord post |
ANTHROPIC_API_KEY |
no | The optional Claude PR review; omit to skip that step |
More copy-paste templates live in examples/.
Set the webhook secret on the consumer repo:
gh secret set DISCORD_WEBHOOK_URL --repo <owner>/<repo>Add the Anthropic key to the consumer repo's secrets. The workflow detects the secret at runtime and skips the step when absent, so nothing else needs to change.
gh secret set ANTHROPIC_API_KEY --repo <owner>/<repo>Scans run, findings are tallied, and the Discord notifier stays silent unless at least one scanner reports something. If anything fires, a single embed is posted with per-scanner counts and a link to the workflow run.
The same scan pipeline runs, and the embed is always posted (green when clean, coloured when not) so the reviewer has a clear signal before approving. The optional Claude review runs only on pull requests; pushes skip it to save API calls.
By default SecureCheck reports without failing (fail-on: none). Set fail-on to low, medium, high, or critical to fail the run (and block the merge) when a finding meets or exceeds that severity. Detected secrets always count as critical. A findings-by-severity table is written to the job summary.
Gitleaks, Semgrep, and Trivy results upload to GitHub code scanning, so findings appear in the repo's Security tab and as inline annotations on the pull request. This needs security-events: write in the calling job (already set in the reusable workflow and the examples). Set upload-sarif: false to disable.
| State | Colour |
|---|---|
| All scanners clean | Green |
| Findings, no gitleaks hit | Yellow |
| Many findings (>10 total) | Orange |
| Gitleaks hit | Red |
When the optional Claude step runs and reports critical or high severity issues, the first five are inlined in the Discord embed with file, line, and a one-sentence explanation of the exploit path. Lower-severity findings only appear in the run artifact.
Every run uploads a security-reports artifact containing the raw JSON from each scanner (gitleaks, semgrep, trivy, claude) with 14-day retention. Useful when the embed count is non-zero and you want the full picture without re-running locally.
| Input | Default | Description |
|---|---|---|
fail-on |
none |
Minimum severity that fails the build: none, low, medium, high, critical. Secrets always count as critical. |
upload-sarif |
true |
Upload SARIF to GitHub code scanning (needs security-events: write). |
run-claude |
auto |
Optional Claude PR review: auto (PRs with a key), true, or false. |
node-version |
20 |
Node.js version for JS/TS tooling. |
python-version |
3.11 |
Python version for Semgrep / ruff / lizard. |
dotnet-version |
10.0.x |
.NET SDK version (only used when a .NET project is detected). |
gitleaks-version |
8.24.3 |
Pinned Gitleaks release. |
anthropic-api-key |
'' |
Enables the Claude review when set. Pass ${{ secrets.ANTHROPIC_API_KEY }}. |
discord-webhook |
'' |
Enables Discord notifications when set. Pass ${{ secrets.DISCORD_WEBHOOK_URL }}. |
The reusable-workflow inputs use snake_case (e.g. node_version), while the composite action above uses kebab-case (e.g. node-version). Pass the webhook and API key as mapped secrets rather than with: inputs:
jobs:
scan:
uses: w1ck3ds0d4/SecureCheck/.github/workflows/scan.yml@v1
secrets:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
with:
node_version: '22'
python_version: '3.12'
dotnet_version: '10.0.x'SecureCheck/
action.yml Composite action (primary entry point; Marketplace-ready)
.github/
workflows/
scan.yml Reusable workflow wrapper around the action
dependabot.yml Keeps action + npm versions current
scripts/
embed.mjs Pure embed-building logic (counts, colour, fields) - unit-tested
embed.test.mjs node --test suite for embed.mjs
notify.mjs Thin entry point: reads env + Claude file, builds via embed.mjs, POSTs
claude-review.mjs Sends the PR diff to Claude and emits structured findings
gate.mjs Buckets findings by severity and enforces fail-on
examples/ Copy-paste consumer workflows
package.json @anthropic-ai/sdk dependency for the optional Claude step
CHANGELOG.md
LICENSE AGPL v3
COMMERCIAL.md Commercial license terms
The embed-building logic (scripts/embed.mjs) is pure and unit-tested - no network, no real workflow run:
npm test # node --test over scripts/**/*.test.mjsCI runs the same suite on every push and pull request (.github/workflows/ci.yml).
This project is dual-licensed:
- AGPL v3 - free for open-source use. Derivatives and SaaS deployments must release their source under AGPL.
- Commercial license - for proprietary / closed-source use or hosted services that do not want to comply with AGPL source-disclosure requirements. Contact for terms.