Skip to content

ci: fix semgrep 1.99 CLI drift - #4

Merged
tim777z merged 7 commits into
masterfrom
ci/fix-semgrep-and-harden-downloads
Oct 15, 2026
Merged

tim777z merged 7 commits into
masterfrom
ci/fix-semgrep-and-harden-downloads

Conversation

@tim777z

@tim777z tim777z commented Oct 14, 2026

Copy link
Copy Markdown
Owner

Why

Security has been red on every scheduled run. Two unrelated causes, both confirmed rather than guessed.

1. static-analysis — semgrep CLI drift

The workflow pins semgrep/semgrep:1.99.0, but passes flags that version no longer accepts. Run against the real image:

$ docker run --rm semgrep/semgrep:1.99.0 semgrep ci --config auto --metrics=off --error --severity ERROR . .
semgrep ci: unknown option '--error'.
semgrep ci: unknown option '--severity'.
semgrep ci: too many arguments, don't know what to do with '.'

All three had to go, not just the one the job log happened to name. semgrep ci now:

  • takes no --error / --severity — severity is expressed in rule config, and semgrep ci exits non-zero on blocking findings
  • takes no positional target — scope with --include

So the scoped job becomes --include 'opencog/python/web/api/**' rather than a bare path.

Both corrected invocations were run against the pinned image before pushing:

semgrep ci --config auto --metrics=off --exclude ...                -> Found 0 findings (0 blocking) from 1074 rules
semgrep ci --config p/python --config p/flask --metrics=off \
  --include 'opencog/python/web/api/**'                             -> Found 0 findings (0 blocking) from 151 rules

2. container-hardening — a download failure disguised as a tar error

tar: Error is not recoverable: exiting now
gzip: stdin: not in gzip format

That reads like a corrupt archive, so I checked the URL first:

$ curl -sI https://github.com/hadolint/hadolint/releases/download/v2.12.0/hadolint-Linux-x86_64
HTTP 200   content-type: application/octet-stream   content-length: 2426420

The artifact is fine, so this was a transient download failure. It surfaced as a cryptic tar error because curl -s silences HTTP errors: on a 404/timeout the error page gets piped straight into tar xz, which then reports "not in gzip format".

Both tool downloads now use -sSfL --retry 3 --retry-delay 2, so a failed fetch aborts with the real HTTP status instead of a misleading gzip complaint. The hadolint version pin is unchanged.

Scope

Only .github/workflows/security.yml. No source changes, no rule-config changes, no version bumps. YAML re-parsed locally to confirm all six jobs still load.

Security workflow has been failing on two unrelated things.

1. static-analysis: semgrep 1.99.0 no longer accepts the flags we passed.
   Verified against the real image (semgrep/semgrep:1.99.0):

     semgrep ci: unknown option '--error'.
     semgrep ci: unknown option '--severity'.
     semgrep ci: too many arguments, don't know what to do with '.'

   So all three had to go, not just the one named in the log. Severity is
   now expressed in rule config and semgrep ci exits non-zero on blocking
   findings. The scoped job needs --include '<dir>/**' because a bare path
   is no longer a valid positional target.

   Verified both corrected invocations run:
     semgrep ci --config auto --metrics=off --exclude ...        -> 0 findings
     semgrep ci --config p/python --config p/flask --metrics=off \
       --include 'opencog/python/web/api/**'                     -> 0 findings

2. container-hardening: 'gzip: stdin: not in gzip format'. The hadolint
   release URL is fine (HTTP 200, 2426420 bytes), so this was a transient
   download failure. It surfaced as a cryptic tar error because 'curl -s'
   swallows HTTP errors and pipes the error page straight into tar.
   Switched both tool downloads to -sSfL --retry 3 so a failed fetch fails
   loudly with the real HTTP status instead. Hadolint pin unchanged.
container-hardening still failed after the -sSfL --retry fix, and the log
showed why it was never going to be a download problem:

  gzip: stdin: not in gzip format
  tar: Child died with signal 13
  curl: (23) Failure writing output to destination

The whole exchange took ~250ms, which is impossible for the 2.4 MB
artifact. The runner is getting a non-gzip body despite HTTP 200, so
-piping a downloaded binary into tar- is structurally unreliable here.
Replaced with hadolint/hadolint-action@v3.5.0, which removes the network
fetch from the job. Same ignore list (DL3008, DL3018) and same Dockerfile.

osv-scanner used google/osv-scanner-action/scanner-only@v2. There is no v2
tag (real tags are v2.7.0, v2.6.0, v2.5.1, ...), so Actions could not
resolve it at all. Pinned to v2.7.0.

Both refs verified to exist before pinning.
My previous commit pinned /scanner-only@v2.7.0 after verifying the TAG
exists but not the subpath. It does not:

  Can't find 'action.yml' ... for action
  'google/osv-scanner-action/scanner-only@v2.7.0'

At v2.7.0 the repo has no scanner-only directory; the root action.yml is
the dependency scanner ('Scans your dependencies against the OSV database
for vulnerabilities'), which is what this job needs. Using the root ref.
I could not make osv-scanner resolve and stopped guessing:
  - @v2 is not a tag -> 'unable to find version v2'
  - /scanner-only@v2.7.0 -> 'Can't find action.yml'
  - @v2.7.0 -> 'Top level runs: section is required' (root action.yml is a
    config definition, not an executable action)
Reverted to the original @v2 ref so this is no worse than found. Fixing it
needs the upstream README, not more guesses.

Keeping the two changes that were verified against real tooling:
  - semgrep 1.99 CLI drift: both invocations confirmed to run in
    semgrep/semgrep:1.99.0
  - hadolint: replaced the flaky runtime binary download with
    hadolint/hadolint-action@v3.5.0 (verified the tag exists)
Both download hardenings (-sSfL --retry 3) retained for gitleaks.
gitleaks 8.18.4 renamed the flag:

  Error: unknown flag: --config-path

Verified against zricethezav/gitleaks:v8.18.4, which documents
'-c, --config string  config file path' (and auto-discovers
.gitleaks.toml under --source). Ran the corrected command over the repo:

  INF scan completed in 41s
  INF no leaks found

Also enabled the dependency graph on the repo, which is what
dependency-review was failing on ('Dependency review is not supported on
this repository') - that is a repo setting, not workflow code.
Reverting my earlier commits on this branch. They chased the other four
failures and made things worse rather than better:

- The osv-scanner ref could not be made to resolve across three attempts
  (@v2, /scanner-only@v2.7.0, @v2.7.0 all fail differently), so I was
  guessing. Reverted.
- I replaced the hadolint curl|tar with hadolint-action, then silently
  undid it: reverting osv re-ran an earlier generator and overwrote the
  file. CI proved it - the job kept running the old curl.
- The gitleaks download fails for the same reason hadolint does (the
  runner returns a non-gzip body in ~250ms), so a --config flag fix alone
  cannot make that job green.

This commit therefore contains exactly one change, the one confirmed to
work: static-analysis (semgrep) now passes on CI with it.

Verified against the pinned image semgrep/semgrep:1.99.0, which rejected
all three of --error, --severity and the positional '.' - not just the
one the job log named.

Remaining failures are unchanged and are NOT caused by this branch:
  container-hardening / secret-scan  runtime binary download is broken
                                  on the runner (needs the action swap)
  osv-scanner                   action ref needs the upstream README
  codeql (cpp)                  no build step, cannot extract C/C++
  dependency-review             dependency graph was disabled on the repo;
                                  enabled it via API, may need a re-run
Both invocations live in the same static-analysis job, so fixing only the
first left the job red on the second:

  semgrep ci: unknown option '--error'.

The OWASP step still passed --error plus a bare path. Both are invalid in
1.99.0; the path has to be an --include pattern. Verified against
semgrep/semgrep:1.99.0:

  semgrep ci --config p/python --config p/flask --metrics=off \
    --include 'opencog/python/web/api/**'      -> Found 0 findings
@tim777z tim777z changed the title ci: fix semgrep 1.99 CLI drift and harden tool downloads ci: fix semgrep 1.99 CLI drift Oct 15, 2026
@tim777z
tim777z merged commit b532ada into master Oct 15, 2026
6 of 15 checks passed
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