Reusable GitHub Actions workflow that runs a multi-scanner security pipeline on every push and pull request and posts a single, severity-coloured summary to Discord.
- 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:
name: Security Scan
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
jobs:
scan:
uses: w1ck3ds0d4/SecureCheck/.github/workflows/scan.yml@v1
secrets:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} # optionalPin @v1 for the stable line (or @main for latest). The workflow checks out its own scripts at the same ref you pin, so a pinned caller is fully reproducible. Mapping the two secrets explicitly (rather than secrets: inherit) follows least privilege - only these two are ever passed; both are optional.
| 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 |
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.
| 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.
The reusable workflow accepts three optional 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/
.github/
workflows/
scan.yml Reusable workflow; checkout, scanners, notify, upload
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
package.json @anthropic-ai/sdk dependency for the optional Claude step
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.