Skip to content

Add sigstore + gha-extras + pulumi-iac + go-canon Semgrep rule packs#10

Merged
Cre-eD merged 1 commit into
mainfrom
feat/semgrep-rules-sigstore-pulumi-go-canon
May 16, 2026
Merged

Add sigstore + gha-extras + pulumi-iac + go-canon Semgrep rule packs#10
Cre-eD merged 1 commit into
mainfrom
feat/semgrep-rules-sigstore-pulumi-go-canon

Conversation

@Cre-eD
Copy link
Copy Markdown
Contributor

@Cre-eD Cre-eD commented May 16, 2026

Summary

Promotes four custom Semgrep rule packs (22 rules total) from the API repo's local .semgrep/rules/ into the shared semgrep-scan ruleset so every consumer of the action gets the coverage by default. Baked for ~3 weeks on simple-container-com/api (PR #261) with 0 findings on a clean branch.

Pack # rules Coverage
sigstore.yml 6 cosign verify identity/issuer flags, --insecure-ignore-*, gh attestation verify --owner/--repo, attest-build-provenance mutable tags, OIDC id-token: write on pull_request_target / workflow_run
gha-extras.yml 4 workflow-level secrets.* in env:, upload-artifact of .env/*.pem/*.key/secrets/**, pull_request_target + checkout without workflow_run-pinned ref, GITHUB_TOKEN leak via $GITHUB_ENV
pulumi-iac.yml 5 K8s Privileged / HostNetwork / HostPID / hostPath, GCP bucket public ACL or UBLA off, GCE default SA + cloud-platform scope, AWS SG 0.0.0.0/0 ingress, IAM Action/Resource: "*"
go-canon.yml 7 gosec G-series + dgryski/semgrep-go misses CodeQL security-extended doesn't cover — bytes.Equal on MAC, deterministic AEAD nonce, JWT ParseUnverified / None, &http.Client{} without Timeout, tls.Config{MinVersion: <1.2}, exec.Command without cmd.Env

Why now

These were drafted in the API repo first (per the standard ruleset-promotion flow: bake locally → consumer-rules → 0 findings → promote to shared) and have been clean on api for ~3 weeks. Moving them here means the next consumer that opts into semgrep-scan gets them for free instead of copy-pasting .semgrep/rules/ into every repo.

Implementation notes

  • YAML rules use languages: [generic] — Semgrep's YAML block-scalar metavariables ($BODY of a run: | mapping) don't bind to the block content in practice; switched to file-level pattern-regex + pattern-not-regex pairs. Trade-off: a workflow that mixes safe and unsafe usages on the same rule may not fire. Acceptable for CI hardening, where one workflow exercises one consistent pattern.
  • go-canon-exec-command ships a path-exclude list for pkg/security/{sbom,scan,tools}/** plus a trusted-CLI metavariable filter (git/stty/gh/cosign/syft/trivy/grype/kubectl/docker/pulumi/sc/welder) so legitimate scanner / git invocations don't FP.
  • pulumi-aws-sg-open-ingress is regex-only (no AST alternative) — the AST shape ec2.SecurityGroupArgs{Ingress: ...} matched any ingress including restricted CIDRs, so it was dropped in favor of the precise CidrBlocks: ... "0.0.0.0/0" regex.

Test plan

semgrep-scan/run-tests.sh extended with four new rule/fixture pairings:

  • sigstore.ymltests/.github/workflows/sigstore-cases.yml (9 cases)
  • gha-extras.ymltests/.github/workflows/gha-extras-cases.yml (7 cases)
  • pulumi-iac.ymltests/pulumi/cases.go (18 cases)
  • go-canon.ymltests/canon/cases.go (26 cases)

Go fixtures live in their own subdirectories with isolated package names so per-file Semgrep scans don't collide with the existing tests/go_cases.go declarations (pulumi, rds, sdk, lo, …).

35/35 cases passed for go_cases.go
18/18 cases passed for cases.go (pulumi-iac)
26/26 cases passed for cases.go (go-canon)
9/9 cases passed for sigstore-cases.yml
7/7 cases passed for gha-extras-cases.yml
38/38 cases passed for shell.bash
56/56 cases passed for cases.yml
Full-repo scan: 0 findings (0 blocking)

Follow-up

After this merges, the consumer side (simple-container-com/api PR #261) drops its local .semgrep/rules/ and the consumer-rules input — the shared ruleset covers it.

… packs

Promote the four custom Semgrep rule packs that have been baking on
simple-container-com/api (PR #261, 0 findings clean) into the shared
semgrep-scan ruleset so every consumer of the action picks them up.

Rule files (4 packs, 22 rules):
- sigstore.yml (6): cosign verify/verify-blob/verify-attestation must
  set --certificate-identity + --certificate-oidc-issuer; reject
  --insecure-ignore-{tlog,sct}; gh attestation verify requires
  --owner/--repo scope; attest-build-provenance must pin subject-digest
  on :latest / :staging tags; id-token: write disallowed on
  pull_request_target / workflow_run triggers.
- gha-extras.yml (4): workflow-level env: must not reference
  secrets.*; upload-artifact paths must not match .env / *.pem /
  *.key / secrets/**; pull_request_target + checkout without
  workflow_run-pinned ref is the canonical PPE vector; echo of any
  TOKEN-named var into $GITHUB_ENV leaks across steps.
- pulumi-iac.yml (5): K8s pod with Privileged / HostNetwork / HostPID
  / hostPath; GCP bucket public ACL (allUsers / allAuthenticatedUsers)
  or UBLA disabled; GCE VM with default SA + cloud-platform scope; AWS
  SG ingress 0.0.0.0/0; IAM policy with Action / Resource = "*".
- go-canon.yml (7): bytes.Equal on MAC / signature variables (timing
  side-channel); AEAD Seal with deterministic / zero nonce; JWT
  ParseUnverified / SigningMethodNone / "alg":"none"; &http.Client{}
  without Timeout; tls.Config MinVersion below 1.2; exec.Command +
  Run/Output without setting cmd.Env (path file-excludes + trusted-CLI
  metavariable filter so syft/trivy/cosign/git/kubectl etc. skip).

Scoping note: Semgrep YAML block-scalar metavariables (`$BODY` of a
`run: |` mapping) don't bind to block content, so the sigstore /
gha-extras rules use file-level pattern-regex + pattern-not-regex
pairs (with `languages: [generic]`) — a workflow that mixes safe and
unsafe usages on the same rule may not fire, which is acceptable for
CI hardening where one workflow exercises one consistent pattern.

Test harness:
- 4 new fixture files under semgrep-scan/tests/{.github/workflows,
  pulumi,canon}, 60 new positive/negative test cases.
- run-tests.sh pairs table extended; full-repo scan still produces 0
  findings on this repo.
- Go fixtures live in their own subdirectories with isolated package
  names so per-file Semgrep scans don't collide with the existing
  tests/go_cases.go declarations (pulumi.Bool, rds, etc.).

Signed-off-by: Dmitrii Creed <creeed22@gmail.com>
@github-actions
Copy link
Copy Markdown

Security Scan Results

Repository: actions | Commit: 042c3e6

Check Status Details
✅ Secret Scan Pass No secrets detected
⏩ Dependencies Skipped -

Scanned at 2026-05-16 15:39 UTC

@github-actions
Copy link
Copy Markdown

Semgrep Scan Results

Repository: actions | Commit: 042c3e6

Check Status Details
✅ Semgrep Pass 0 total findings (no error/warning)

Scanned at 2026-05-16 15:39 UTC

@Cre-eD Cre-eD merged commit 3287e39 into main May 16, 2026
11 checks passed
Cre-eD added a commit to simple-container-com/api that referenced this pull request May 16, 2026
The 22 custom Semgrep rules (sigstore, gha-extras, pulumi-iac, go-canon)
that lived in .semgrep/rules/ have been promoted to the shared ruleset
at simple-container-com/actions/semgrep-scan/rules/ via
simple-container-com/actions#10. Once that lands on the actions repo's
`main` branch, the rules are picked up here automatically via the
`@main` ref on the reusable workflow — no `consumer-rules:` input
needed.

Short coverage gap until actions#10 merges: the registry packs
(p/ci, p/golang, p/gosec) keep running; only the SC-specific rules
pause. Re-enable on the actions side as soon as #10 is merged.

Signed-off-by: Dmitrii Creed <creeed22@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant