diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..0c85b4a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,17 @@ +version: 2 +updates: + # Keep the actions referenced by the workflow and composite action current. + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + commit-message: + prefix: ci + + # Keep the Node script dependencies (Anthropic SDK) current. + - package-ecosystem: npm + directory: / + schedule: + interval: weekly + commit-message: + prefix: deps diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..ca7d13a --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,33 @@ +# Changelog + +All notable changes to SecureCheck are documented here. The format is based on +[Keep a Changelog](https://keepachangelog.com/), and the project aims to follow +semantic versioning. + +## [Unreleased] + +### Added +- Composite action (`action.yml`) so SecureCheck can be used as + `- uses: w1ck3ds0d4/SecureCheck@` and listed on the GitHub Marketplace. +- SARIF output from Gitleaks, Semgrep and Trivy, uploaded to GitHub code + scanning (Security tab plus inline PR annotations). Requires the calling job + to grant `security-events: write`. +- Configurable severity gate via the `fail-on` input + (`none` | `low` | `medium` | `high` | `critical`). Detected secrets are always + treated as critical. +- Caching for pip, npm and the Trivy database, and a `concurrency` group (in the + reusable workflow) to cancel superseded runs. +- `gitleaks-version` input to pin the Gitleaks release. +- Findings-by-severity summary written to the job summary. +- Example consumer workflows under `examples/`. +- Dependabot configuration to keep pinned action versions current. + +### Changed +- The reusable workflow (`.github/workflows/scan.yml`) is now a thin wrapper that + calls the composite action, so the scan logic lives in a single place. +- `notify.mjs` reads reports from the run's reports directory instead of a + hardcoded `/tmp` path. + +### Removed +- The em-dash (U+2014) style check that previously failed builds. It was a + personal style rule, not a security control. diff --git a/README.md b/README.md index 7878e4a..9f64a91 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,14 @@ # SecureCheck -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. +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. --- ## Features +- **Usable as a Marketplace action or a reusable workflow** - drop in a single `- uses: w1ck3ds0d4/SecureCheck@v1` step, 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-on` blocks 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 `auto` ruleset @@ -30,12 +33,12 @@ Reusable GitHub Actions workflow that runs a multi-scanner security pipeline on - 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 -### Add the caller workflow +### Add the workflow -Create `.github/workflows/security.yml` in the consumer repo: +Create `.github/workflows/security.yml` in the consumer repo. Recommended (composite action): ```yaml -name: Security Scan +name: Security on: push: @@ -44,13 +47,37 @@ on: permissions: contents: read + security-events: write # required to upload SARIF to code scanning jobs: - scan: + securecheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # full history for gitleaks + PR diff + - uses: w1ck3ds0d4/SecureCheck@main # pin to a release tag for production + with: + fail-on: high +``` + +Prefer to have the whole job managed for you? Use the reusable workflow instead: + +```yaml +permissions: + contents: read + security-events: write + +jobs: + securecheck: uses: w1ck3ds0d4/SecureCheck/.github/workflows/scan.yml@main + with: + fail-on: high secrets: inherit ``` +More copy-paste templates live in [`examples/`](examples/). + ### Configure secrets Set the webhook secret on the consumer repo: @@ -79,6 +106,14 @@ Scans run, findings are tallied, and the Discord notifier stays silent unless at 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. +### Failure gate + +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. + +### Code scanning (SARIF) + +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. + ### Severity colour | State | Colour | @@ -96,20 +131,21 @@ When the optional Claude step runs and reports critical or high severity issues, 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. -### Overriding defaults +### Inputs -The reusable workflow accepts three optional inputs: +| 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 }}`. | -```yaml -jobs: - scan: - uses: w1ck3ds0d4/SecureCheck/.github/workflows/scan.yml@main - secrets: inherit - with: - node_version: '22' - python_version: '3.12' - dotnet_version: '10.0.x' -``` +For the **reusable workflow**, pass the API key and webhook as repository secrets (`ANTHROPIC_API_KEY`, `DISCORD_WEBHOOK_URL`) together with `secrets: inherit`, rather than as `with:` inputs. --- @@ -117,15 +153,20 @@ jobs: ``` SecureCheck/ + action.yml Composite action (primary entry point; Marketplace-ready) .github/ workflows/ - scan.yml Reusable workflow; checkout, scanners, notify, upload + scan.yml Reusable workflow wrapper around the action + dependabot.yml Keeps action + npm versions current scripts/ - notify.mjs Builds the Discord embed from scanner counts and posts it - 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 + notify.mjs Builds the Discord embed from scanner counts and posts it + 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 ``` --- diff --git a/examples/security-reusable.yml b/examples/security-reusable.yml new file mode 100644 index 0000000..f537425 --- /dev/null +++ b/examples/security-reusable.yml @@ -0,0 +1,20 @@ +# SecureCheck via the reusable workflow (handles checkout/permissions/concurrency +# for you). Prefer the composite action (see security.yml) unless you specifically +# want the whole job managed. +name: Security + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + security-events: write + +jobs: + securecheck: + uses: w1ck3ds0d4/SecureCheck/.github/workflows/scan.yml@main # pin to a tag for production + with: + fail-on: high + secrets: inherit diff --git a/examples/security-with-claude.yml b/examples/security-with-claude.yml new file mode 100644 index 0000000..26eddb2 --- /dev/null +++ b/examples/security-with-claude.yml @@ -0,0 +1,27 @@ +# SecureCheck with the optional Claude PR review and Discord notifications. +# Set ANTHROPIC_API_KEY and DISCORD_WEBHOOK_URL as repository secrets first. +name: Security + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + security-events: write + +jobs: + securecheck: + name: SecureCheck + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: w1ck3ds0d4/SecureCheck@main # pin to a release tag for production + with: + fail-on: high + anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }} + discord-webhook: ${{ secrets.DISCORD_WEBHOOK_URL }} diff --git a/examples/security.yml b/examples/security.yml new file mode 100644 index 0000000..ee94ca4 --- /dev/null +++ b/examples/security.yml @@ -0,0 +1,25 @@ +# Minimal SecureCheck setup using the composite action (recommended). +# Copy to .github/workflows/security.yml in your repo. +name: Security + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + security-events: write # required so findings upload to the Security tab + +jobs: + securecheck: + name: SecureCheck + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # full history for gitleaks + PR diff + + - uses: w1ck3ds0d4/SecureCheck@main # pin to a release tag for production + with: + fail-on: high # block on high/critical; use 'none' to report only