Stack (DEP-91)
Split from #1662 for reviewability, then rescoped: main has since landed #1681 (dependency/toolchain CVEs) and #1693 (hoophq/hoopagent), which superseded two PRs of the original stack.
-
#1683 — Go dependency + toolchain CVE bumps— closed, superseded by #1681 -
#1685 — opt-in— closed, superseded by #1693hoophq/hoopdev-minimal -
#1684 — 1. agent-tools: drop kernel-header CVEs, modernize clean train
-
#1686 — 2. Vulnerability gates for hoopagent + govulncheck ← you are here
-
#1687 — 3. SBOM + bundled-tool manifest on releases
-
#1688 — 4. Weekly base-OS rebuild of hoopagent
-
#1689 — 5. Image release integrity hardening
Merge in order, bottom-up. Each PR targets the one below it.
The remaining work is what main still lacks: the fat image's kernel-header CVEs, and any vulnerability gate, SBOM, or scheduled OS patching for hoophq/hoopagent.
What
Adds the vulnerability gating hoophq/hoopagent currently has none of:
- Build smoke check for
Dockerfile.agent, both flavours, both platforms. It was previously only built after a tag push, so a broken Dockerfile could reachmainand fail post-merge. - Enforcing Trivy gate on fixable CRITICAL/HIGH for both flavours.
- Unprivileged-user assertion — resolves
USERto a real uid, handlingdistroless(10001:10001, numeric) andminimal(hoop, by name) correctly. - Report-only Trivy on
agent-tools, which bundles upstream CLIs whose CVEs cannot be rebuilt away. - govulncheck (pinned v1.6.0) on every PR for the shipped Go binary.
Why
hoophq/hoopagent is the low-CVE image, but nothing enforced that. main runs Trivy with --scanners license only — there is no vulnerability gate anywhere, and no source-level Go gate.
Risk
Adds blocking checks. The fat image stays report-only, so existing releases are not newly gated.
Note on the placeholder binary
The release tarballs do not exist at PR time, so the smoke build stages placeholder tars named hoop_*_Linux_${TARGETARCH}.tar.gz — matching Dockerfile.agent's glob, which uses GOARCH values, not uname -m. This gates Dockerfile correctness and the base layer, not a functional agent.
How to test
Open any PR touching Dockerfile.agent: Build Slim Agent Image runs for both flavours and must pass. Go Vulnerability Gate runs on every PR.
Part of DEP-91.
Automated by MisterMal
Activity
8Code Review by Qodo
🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0) 🔗 Cross-repo conflicts (2) 📜 Skill insights (0)
1. Trivy report can still fail 🐞 Bug ☼ Reliability
Description
In agent-tools-build.yml, the new “non-blocking” Trivy report step ignores Trivy failures ("|| true") but then runs an unguarded jq ... | head pipeline; if Trivy produces empty/invalid JSON, jq can fail and block the workflow anyway. This can prevent later steps (including publish) from running even though the step is intended to be report-only.
Code
.github/workflows/agent-tools-build.yml[R197-200]+ echo "Strict gate is on hoophq/hoopdev-minimal. Top packages:" + echo '```' + jq -r '[.Results[]?.Vulnerabilities // []]|add|group_by(.PkgName)|map({p:.[0].PkgName,c:length})|sort_by(-.c)|.[]|"\(.c)\t\(.p)"' trivy-vuln.json 2>/dev/null | head -20 + echo '```'
Evidence
The step intentionally ignores Trivy failures when writing trivy-vuln.json, but then immediately runs a jq pipeline (without any fallback) to render the "Top packages" list; this can still fail if the JSON file is empty/invalid. The same pattern is duplicated in the arm64 job.
.github/workflows/agent-tools-build.yml[179-201].github/workflows/agent-tools-build.yml[300-318]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution ## Issue description The Trivy step is explicitly labeled/report-only and uses `|| true` to avoid failing when Trivy errors, but the subsequent `jq ... | head` command is not guarded. If `trivy-vuln.json` is empty or invalid (e.g., Trivy fails before producing valid JSON), `jq` may exit non-zero and the step can still fail. ## Issue Context This occurs in both amd64 and arm64 jobs in `agent-tools-build.yml`. ## Fix Focus Areas - .github/workflows/agent-tools-build.yml[185-201] - .github/workflows/agent-tools-build.yml[302-318] ## Suggested fix approach - Make the "Top packages" rendering resilient, e.g.: - Wrap it in a conditional JSON validity check (`jq -e . trivy-vuln.json >/dev/null 2>&1`) and print a fallback message when invalid; and/or - Append `|| true` to the `jq ... | head` pipeline. - Also consider making the count query robust to empty `.Results` by using `add // []` (or similar) so it produces `0` instead of `null` when appropriate.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
2. Gate needs libhoop token 🔗 Cross-repo conflict ☼ Reliability
Description
The new blocking govulncheck job requires checking out hoophq/libhoop using secrets.GH_TOKEN; if that secret is not available in a given PR context, the checkout/build will fail and block merges. This adds/strengthens a cross-repo access contract between this repo’s PR CI and the libhoop repo’s availability/permissions.
Code
.github/workflows/pullrequest.yml[R328-331]+ repository: hoophq/libhoop + path: "./libhoop" + token: ${{ secrets.GH_TOKEN }} + ref: ${{ needs.resolve-libhoop.outputs.sha }}
Relevance
●●● StrongThey’ve accepted adding explicit checks around required secrets to avoid late workflow failures.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
The newly-added govulncheck job checks out hoophq/libhoop using secrets.GH_TOKEN, and the workspace includes ./libhoop, meaning the job’s success depends on successfully fetching that separate repository/module. This is an explicit cross-repo auth/availability dependency introduced into the PR merge gate.
.github/workflows/pullrequest.yml[3-4].github/workflows/pullrequest.yml[27-41].github/workflows/pullrequest.yml[309-331]go.work[1-16]External repo: hoophq/libhoop, go.mod [1-4]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution ### Issue description `govulncheck` is now a blocking PR job and it checks out `hoophq/libhoop` using `secrets.GH_TOKEN`. If `GH_TOKEN` is missing/unavailable for a PR run (common in fork PR scenarios), the gate will fail before analysis. ### Issue Context The workflow uses `on: pull_request`, and both `resolve-libhoop` and `govulncheck` rely on `secrets.GH_TOKEN` to access libhoop. ### Fix Focus Areas - .github/workflows/pullrequest.yml[3-4] - .github/workflows/pullrequest.yml[27-41] - .github/workflows/pullrequest.yml[309-331] - go.work[1-16] ### Suggested change Implement one of the following patterns: 1) **Conditional gate**: only run `govulncheck` (or only the libhoop checkout step) when `secrets.GH_TOKEN` is present, and otherwise skip the job with a clear message. 2) **OSS stub fallback**: when libhoop checkout cannot run, create `./libhoop` by symlinking to `./_libhoop` (or running the existing mapping target) so govulncheck can still compile against the OSS stub. 3) If `hoophq/libhoop` is intended to be publicly readable for CI, remove the hard dependency on `secrets.GH_TOKEN` for checkout and use a standard token/unauthenticated checkout.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
3. Gate tracks libhoop head 🔗 Cross-repo conflict ☼ Reliability
Description
The new blocking govulncheck job compiles against the libhoop revision resolved at workflow runtime (default-branch HEAD when no paired branch), so re-running the same Hoop PR commit can start failing due to new changes/vulns in hoophq/libhoop. This increases cross-repo coupling and reduces CI reproducibility across reruns.
Code
.github/workflows/pullrequest.yml[R328-331]+ repository: hoophq/libhoop + path: "./libhoop" + token: ${{ secrets.GH_TOKEN }} + ref: ${{ needs.resolve-libhoop.outputs.sha }}
Relevance
●● ModerateReproducibility vs tracking latest libhoop is a policy trade-off; no close pinning precedent found.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
The PR workflow’s resolve-libhoop job explicitly falls back to the current default-branch checkout’s HEAD when there is no paired branch, and the newly-added govulncheck job checks out libhoop at that resolved SHA. The workspace expects a local ./libhoop module, and libhoop is an independent Go module in a separate repository, so its moving default-branch state can change the dependency graph analyzed by govulncheck across reruns.
.github/workflows/pullrequest.yml[27-77].github/workflows/pullrequest.yml[309-348]go.work[1-16]External repo: hoophq/libhoop, go.mod [1-4]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution ### Issue description The new `govulncheck` merge gate checks out and compiles against `hoophq/libhoop` using a SHA that can resolve to the libhoop default branch HEAD when no paired branch exists. This makes Hoop PR results dependent on a moving external repo state and can cause the same PR commit to pass/fail depending on when the workflow is run. ### Issue Context - `resolve-libhoop` falls back to the default-branch checkout’s current `HEAD` when a same-named paired branch does not exist. - `govulncheck` then checks out that resolved SHA and compiles the workspace (which includes `./libhoop`). ### Fix Focus Areas - .github/workflows/pullrequest.yml[27-77] - .github/workflows/pullrequest.yml[309-348] - go.work[1-16] ### Suggested change - Introduce an immutable, Hoop-repo-controlled fallback libhoop revision for the “no paired branch” case (e.g., a committed `LIBHOOP_SHA` file or workflow variable), and have `resolve-libhoop` use that pinned SHA instead of “whatever HEAD is today”. - Keep the paired-branch behavior as-is for coordinated cross-repo changes.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Context
39 rules✅ Cross-repo context
repo: hoophq/libhoop (sha: 84d16e41)hoophq/infrahoophq/documentationhoophq/changelogTip of the day
💡 Did you know, you can enable the Remediation agent and Qodo fixes findings in a dedicated fix PRMore tips ↗ | Customize Qodo ↗ | Qodo docs ↗
Migration Safety Analysis
No database migrations were changed in this PR. Safe to deploy to sandbox.
📋 API Changelog
API Changelog unknown vs. unknown
API Changes
GET /access-requests/rules
- :warning: added the new
jit_commandenum value to thedata/items/access_typeresponse property for the response status200 - added the optional property
data/items/skip_review_groupsto the response with the200status
POST /access-requests/rules
- :warning: added the new
jit_commandenum value to theaccess_typeresponse property for the response status201 - added the new optional request property
skip_review_groups - added the new
jit_commandenum value to the request propertyaccess_type - added the optional property
skip_review_groupsto the response with the201status
GET /access-requests/rules/{name}
- :warning: added the new
jit_commandenum value to theaccess_typeresponse property for the response status200 - added the optional property
skip_review_groupsto the response with the200status
PUT /access-requests/rules/{name}
- :warning: added the new
jit_commandenum value to theaccess_typeresponse property for the response status200 - added the new optional request property
skip_review_groups - added the new
jit_commandenum value to the request propertyaccess_type - added the optional property
skip_review_groupsto the response with the200status
GET /connection-credentials
- endpoint added
✅ Build Completed with Success, Version=1686.0.0-g27d00d4
PR Summary by Qodo
DEP-91: add Trivy gates for minimal image and govulncheck PR gate
✨ Enhancement⚙️ Configuration changes🕐 40+ MinutesAI Description
Diagram
High-Level Assessment
1. Use official GitHub Actions for scanners
2. Publish SARIF/Artifacts instead of only Step Summary
Files changed (4)
+110 / -1Other (4)
+110 / -1agent-tools-build.yml
Add report-only Trivy scan summary for agent-tools images (amd64/arm64)+44/-0minimal-image-build.yml
Enforce Trivy HIGH/CRITICAL gate on minimal PR-smoke image+15/-0pullrequest.yml
Add govulncheck job to gate reachable Go vulnerabilities on PRs+42/-1Dockerfile.minimal
Apply security upgrades during minimal image build via apt-get upgrade+9/-0