From bdc03d0a7f6979b31a1663e24f4b8f1174ff9c18 Mon Sep 17 00:00:00 2001 From: Dan Guido Date: Sun, 8 Feb 2026 11:59:30 -0500 Subject: [PATCH] ci: add semgrep SAST scanning to CI and pre-commit (#14964) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ci: add semgrep SAST scanning to CI and pre-commit Add semgrep static analysis to the security workflow and pre-commit hooks. Uses `--config auto` for comprehensive coverage across Python, Dockerfile, and GitHub Actions. The intentional `USER root` in the Dockerfile (required for bind mount compatibility) is excluded via `--exclude-rule`. Co-Authored-By: Claude Opus 4.6 * ci: pin semgrep container image to digest for zizmor Pin semgrep/semgrep to sha256 digest (v1.151.0) to satisfy zizmor's unpinned-images audit. Co-Authored-By: Claude Opus 4.6 * ci: add zizmor pre-commit hook for Actions security audit Matches the existing actionlint pattern — gracefully skips if zizmor is not installed. Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Claude Opus 4.6 --- .github/workflows/security.yml | 16 ++++++++++++++++ .pre-commit-config.yaml | 19 +++++++++++++++++++ CLAUDE.md | 3 ++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 5ef925fc..701e13f6 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -10,6 +10,22 @@ permissions: contents: read jobs: + semgrep: + name: Semgrep SAST + runs-on: ubuntu-22.04 + container: + image: semgrep/semgrep@sha256:d3d1be3a3770514d16a6a57b9761575d7536d70f45a5220274f4ec7d55c442b9 # v1.151.0 + steps: + - uses: actions/checkout@0c366fd6a839edf440554fa01a7085ccba70ac98 # v5.0.1 + with: + persist-credentials: false + + - name: Run semgrep + run: > + semgrep --config auto + --exclude-rule dockerfile.security.last-user-is-root.last-user-is-root + --error --quiet . + pip-audit: name: Python dependency audit runs-on: ubuntu-22.04 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b70d9f16..37e34f53 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -76,6 +76,18 @@ repos: files: 'main\.yml|server\.yml|users\.yml' pass_filenames: false + - id: semgrep + name: Semgrep security scan + entry: > + bash -c ' + command -v semgrep >/dev/null && + semgrep --config auto + --exclude-rule dockerfile.security.last-user-is-root.last-user-is-root + --error --quiet --skip-unknown-extensions . + || echo "semgrep not installed - skipping"' + language: system + pass_filenames: false + - id: actionlint name: GitHub Actions lint entry: bash -c 'command -v actionlint >/dev/null && actionlint .github/workflows/ || echo "actionlint not installed - skipping"' @@ -83,6 +95,13 @@ repos: files: '^\.github/workflows/.*\.yml$' pass_filenames: false + - id: zizmor + name: GitHub Actions security audit + entry: bash -c 'command -v zizmor >/dev/null && zizmor .github/workflows/ || echo "zizmor not installed - skipping"' + language: system + files: '^\.github/workflows/.*\.yml$' + pass_filenames: false + # Configuration for prek # Files to exclude globally diff --git a/CLAUDE.md b/CLAUDE.md index 201c0ce7..ca6232f2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -54,7 +54,7 @@ algo/ ```bash # Run the full lint suite (same as CI) -ansible-lint . && yamllint . && ruff check . && shellcheck scripts/*.sh +ansible-lint . && yamllint . && ruff check . && shellcheck scripts/*.sh && semgrep --config auto --exclude-rule dockerfile.security.last-user-is-root.last-user-is-root --error --quiet . ansible-playbook main.yml --syntax-check ansible-playbook users.yml --syntax-check pytest tests/unit/ -q @@ -101,6 +101,7 @@ When adding or modifying features, verify these before requesting review: | `yamllint` | All YAML | Document start `---`, quote `'on':` in workflows | | `ruff` | Python | Line length 120, target Python 3.11 | | `shellcheck` | Shell scripts | Quote variables, use `set -euo pipefail` | +| `semgrep` | All code | SAST scanner, `--config auto`, suppress with `# nosemgrep: rule-id` | ### Git Workflow