Skip to content

Commit 1fc44b3

Browse files
07souravkundaclaude
andcommitted
Pin Semgrep CI image by digest and drop security-events from the container job
The Semgrep workflow runs a third-party container on a daily cron with `security-events: write` and the repository checked out. Two hardening changes (CWE-829, Inclusion of Functionality from Untrusted Control Sphere): 1. Pin `returntocorp/semgrep` by immutable digest instead of the `1.166.0` tag. A version tag on Docker Hub is still mutable — it can be re-pointed at new content upstream, and the next scheduled run would execute unreviewed code with no PR gate. The digest is the same image that tag resolves to today (multi-arch index, pushed 2026-06-11), so this is a no-op for behaviour. 2. Move the SARIF upload into its own job. `security-events: write` is now held only by a job that runs no third-party code; the container job keeps `contents: read`. If the image is ever compromised it can no longer forge entries in the code-scanning dashboard. The SARIF crosses between jobs as a workflow artifact, uploaded with `if: always()` because `semgrep ci` exits non-zero when it has blocking findings. Also adds a Dependabot config for `github-actions` so the action SHA pins do not go stale. Dependabot cannot bump a workflow `container:` digest (dependabot/dependabot-core#5819), so the refresh command for the image pin is recorded next to it. Ref: https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 55b79c4 commit 1fc44b3

2 files changed

Lines changed: 57 additions & 6 deletions

File tree

.github/dependabot.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Keeps the digest/SHA pins in .github/workflows/ from going stale.
2+
#
3+
# NOTE: Dependabot cannot bump the `container: image:` digest in a workflow file — its
4+
# `docker` ecosystem only parses Dockerfiles, Kubernetes manifests and Helm values
5+
# (dependabot/dependabot-core#5819), and `github-actions` only covers `uses:` refs.
6+
# The Semgrep image digest in Semgrep.yml must therefore be refreshed manually (the
7+
# command is in a comment next to the pin), or by adopting Renovate, which does support
8+
# workflow container digests.
9+
version: 2
10+
updates:
11+
- package-ecosystem: "github-actions"
12+
directory: "/"
13+
schedule:
14+
interval: "weekly"
15+
open-pull-requests-limit: 5

.github/workflows/Semgrep.yml

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,25 @@ permissions:
1818
jobs:
1919
semgrep:
2020
# User definable name of this GitHub Actions job.
21+
# This job runs third-party code (the Semgrep container), so it is granted
22+
# `contents: read` only. SARIF upload — which needs `security-events: write` — is
23+
# deliberately isolated in the `upload-sarif` job below, so a compromised image
24+
# cannot write to the repository's code-scanning dashboard.
2125
permissions:
2226
contents: read # for actions/checkout to fetch code
23-
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
24-
name: semgrep/ci
25-
# If you are self-hosting, change the following `runs-on` value:
27+
name: semgrep/ci
28+
# If you are self-hosting, change the following `runs-on` value:
2629
runs-on: ubuntu-latest
2730

2831
container:
2932
# A Docker image with Semgrep installed. Do not change this.
30-
image: returntocorp/semgrep:1.166.0
33+
# Pinned by immutable digest, not by tag: a tag (even a version tag) can be
34+
# re-pointed at new content upstream, which would silently execute unreviewed
35+
# third-party code in this runner on the next scheduled run.
36+
# Digest below == returntocorp/semgrep:1.166.0 (multi-arch index, pushed 2026-06-11).
37+
# To refresh the pin (and update this comment):
38+
# docker manifest inspect returntocorp/semgrep:<version> -v | grep -m1 Digest
39+
image: returntocorp/semgrep@sha256:c180f0c93a17b420c0af5006214a29d3c747c5459c732b740191adf657dd0068
3140
# Skip any PR created by dependabot to avoid permission issues:
3241
if: (github.actor != 'dependabot[bot]')
3342

@@ -37,11 +46,38 @@ jobs:
3746
# Run the "semgrep ci" command on the command line of the docker image.
3847
- run: semgrep ci --sarif --output=semgrep.sarif
3948
env:
40-
# Add the rules that Semgrep uses by setting the SEMGREP_RULES environment variable.
49+
# Add the rules that Semgrep uses by setting the SEMGREP_RULES environment variable.
4150
SEMGREP_RULES: p/default # more at semgrep.dev/explore
4251

52+
# Hand the SARIF to the upload job as an artifact. `semgrep ci` exits non-zero when
53+
# it has blocking findings, so this must run even on failure.
54+
- name: Upload SARIF as a workflow artifact
55+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
56+
with:
57+
name: semgrep-sarif
58+
path: semgrep.sarif
59+
if-no-files-found: error
60+
if: always()
61+
62+
# Separate job so that `security-events: write` is never held by the job running the
63+
# third-party Semgrep image. This job runs no third-party code beyond first-party
64+
# GitHub actions, all digest-pinned.
65+
upload-sarif:
66+
name: Upload SARIF to GitHub Advanced Security Dashboard
67+
needs: semgrep
68+
if: always() && (github.actor != 'dependabot[bot]')
69+
runs-on: ubuntu-latest
70+
permissions:
71+
contents: read
72+
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
73+
74+
steps:
75+
- name: Download SARIF artifact
76+
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
77+
with:
78+
name: semgrep-sarif
79+
4380
- name: Upload SARIF file for GitHub Advanced Security Dashboard
4481
uses: github/codeql-action/upload-sarif@6c089f53dd51dc3fc7e599c3cb5356453a52ca9e # v2.20.0
4582
with:
4683
sarif_file: semgrep.sarif
47-
if: always()

0 commit comments

Comments
 (0)