mirror of
https://github.com/boshu2/agentops.git
synced 2026-09-14 15:08:13 +08:00
feat(security): govulncheck standing lane + pin every CI scanner (age-govulncheck-standing-lane-eg2n, age-pin-ci-scanners-7u00)
Sweep 2026-07-09-claude M-2 + M-3, the arc that turns the GO-2026-4970 catch into a standing gate: eg2n: run_govulncheck in scripts/toolchain-validate.sh (mirrors run_gosec: gate-scope skip, per-module run, exit 0/3 = ran, anything else = loud tool error). Every reachable finding counts SECURITY HIGH so the gate BLOCKs - govulncheck's callgraph mode means a finding is a known CVE on a live path. Pinned govulncheck@v1.6.0 installed in validate.yml security job + nightly scanner suite; nightly is the standing lane that catches CVEs published AFTER code lands. Real-data measurement: go1.26.3 scanned 2 hits (sweep); landed go1.26.5 scans clean (No vulnerabilities found, rc 0). 7u00: every scanner the gate trusts is now pinned - gosec@v2.27.1, gitleaks@v8.30.1, semgrep==1.169.0, ruff==0.15.21, radon==6.0.1, pytest==9.1.1, hadolint v2.14.0, yq v4.53.3 (2 sites), and validate.yml's trivy no longer pipes the MUTABLE main-branch install script (pinned v0.72.0 download-then-execute, mirroring nightly). release.yml gosec/ gitleaks/semgrep pinned too. Zero @latest / releases/latest remain in .github/workflows/. Claude-Session: https://claude.ai/code/session_01BkgdP88SiccCSyYdi8GHNa
This commit is contained in:
@@ -124,14 +124,20 @@ jobs:
|
||||
- name: Install scanner tools
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install semgrep ruff radon pytest
|
||||
python -m pip install semgrep==1.169.0 ruff==0.15.21 radon==6.0.1 pytest==9.1.1
|
||||
|
||||
GOBIN=/usr/local/bin go install github.com/securego/gosec/v2/cmd/gosec@latest
|
||||
GOBIN=/usr/local/bin go install github.com/zricethezav/gitleaks/v8@latest
|
||||
GOBIN=/usr/local/bin go install github.com/securego/gosec/v2/cmd/gosec@v2.27.1
|
||||
GOBIN=/usr/local/bin go install github.com/zricethezav/gitleaks/v8@v8.30.1
|
||||
|
||||
# golangci-lint
|
||||
GOBIN=/usr/local/bin go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.11.4
|
||||
|
||||
# govulncheck — known-CVE reachability over the module graph + stdlib
|
||||
# (sweep 2026-07-09 M-2: the blind spot that let GO-2026-4970 sit a week).
|
||||
# The nightly run is the STANDING lane: it catches CVEs published after
|
||||
# the code landed, which no diff-triggered gate can.
|
||||
GOBIN=/usr/local/bin go install golang.org/x/vuln/cmd/govulncheck@v1.6.0
|
||||
|
||||
# trivy — pinned tag, download-then-execute. Piping a mutable-branch
|
||||
# install script into sh was the semgrep gha-curl-pipe-shell CRITICAL
|
||||
# that blocked the v3.2.0 publisher (supply-chain: a hijacked script
|
||||
@@ -140,7 +146,7 @@ jobs:
|
||||
sh /tmp/trivy-install.sh -b /usr/local/bin v0.72.0
|
||||
|
||||
# hadolint
|
||||
curl -sSfL -o /usr/local/bin/hadolint "https://github.com/hadolint/hadolint/releases/latest/download/hadolint-Linux-x86_64"
|
||||
curl -sSfL -o /usr/local/bin/hadolint "https://github.com/hadolint/hadolint/releases/download/v2.14.0/hadolint-Linux-x86_64"
|
||||
chmod +x /usr/local/bin/hadolint
|
||||
|
||||
- name: Run security gate (full)
|
||||
|
||||
@@ -55,9 +55,9 @@ jobs:
|
||||
- name: Install scanner tools
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install semgrep
|
||||
GOBIN=/usr/local/bin go install github.com/securego/gosec/v2/cmd/gosec@latest
|
||||
GOBIN=/usr/local/bin go install github.com/zricethezav/gitleaks/v8@latest
|
||||
python -m pip install semgrep==1.169.0
|
||||
GOBIN=/usr/local/bin go install github.com/securego/gosec/v2/cmd/gosec@v2.27.1
|
||||
GOBIN=/usr/local/bin go install github.com/zricethezav/gitleaks/v8@v8.30.1
|
||||
|
||||
- name: Generate pre-publish release evidence
|
||||
run: |
|
||||
|
||||
@@ -206,7 +206,7 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y jq shellcheck
|
||||
sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
|
||||
sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/download/v4.53.3/yq_linux_amd64
|
||||
sudo chmod +x /usr/local/bin/yq
|
||||
pip install jsonschema pyyaml
|
||||
|
||||
@@ -297,7 +297,7 @@ jobs:
|
||||
- name: Install yq (skill-schema)
|
||||
if: needs.changes.outputs.skills == 'true' || needs.changes.outputs.ci == 'true'
|
||||
run: |
|
||||
sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
|
||||
sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/download/v4.53.3/yq_linux_amd64
|
||||
sudo chmod +x /usr/local/bin/yq
|
||||
|
||||
- name: Install jq
|
||||
@@ -810,12 +810,15 @@ jobs:
|
||||
done
|
||||
}
|
||||
retry python -m pip install --upgrade pip
|
||||
retry python -m pip install semgrep ruff radon
|
||||
retry env GOBIN=/usr/local/bin go install github.com/securego/gosec/v2/cmd/gosec@latest
|
||||
retry env GOBIN=/usr/local/bin go install github.com/zricethezav/gitleaks/v8@latest
|
||||
retry python -m pip install semgrep==1.169.0 ruff==0.15.21 radon==6.0.1
|
||||
retry env GOBIN=/usr/local/bin go install github.com/securego/gosec/v2/cmd/gosec@v2.27.1
|
||||
retry env GOBIN=/usr/local/bin go install github.com/zricethezav/gitleaks/v8@v8.30.1
|
||||
retry env GOBIN=/usr/local/bin go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.11.4
|
||||
retry bash -c 'curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin'
|
||||
retry bash -c 'curl -sL https://github.com/hadolint/hadolint/releases/latest/download/hadolint-Linux-x86_64 -o /tmp/hadolint && chmod +x /tmp/hadolint && sudo mv /tmp/hadolint /usr/local/bin/hadolint'
|
||||
retry env GOBIN=/usr/local/bin go install golang.org/x/vuln/cmd/govulncheck@v1.6.0
|
||||
# trivy — pinned tag, download-then-execute (mirrors nightly.yml; piping a
|
||||
# mutable-branch script into sh is the gha-curl-pipe-shell supply-chain class).
|
||||
retry bash -c 'curl -sSfL -o /tmp/trivy-install.sh https://raw.githubusercontent.com/aquasecurity/trivy/v0.72.0/contrib/install.sh && sh /tmp/trivy-install.sh -b /usr/local/bin v0.72.0'
|
||||
retry bash -c 'curl -sL https://github.com/hadolint/hadolint/releases/download/v2.14.0/hadolint-Linux-x86_64 -o /tmp/hadolint && chmod +x /tmp/hadolint && sudo mv /tmp/hadolint /usr/local/bin/hadolint'
|
||||
|
||||
- name: Run security toolchain gate
|
||||
if: needs.changes.outputs.go == 'true' || needs.changes.outputs.shell == 'true' || needs.changes.outputs.ci == 'true'
|
||||
|
||||
@@ -830,6 +830,91 @@ run_gosec() {
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# TOOL: govulncheck (known-CVE reachability: module graph + Go stdlib)
|
||||
# ============================================================================
|
||||
# Sweep 2026-07-09-claude M-2 (age-govulncheck-standing-lane-eg2n): gosec/
|
||||
# semgrep/gitleaks cover own-code classes; NOTHING checked the module graph or
|
||||
# the pinned toolchain's stdlib against the vulnerability database — exactly
|
||||
# why GO-2026-4970 (an os.Root escape, this repo's containment primitive) sat
|
||||
# undetected for a week after a clean scan. Every reachable finding counts as
|
||||
# SECURITY HIGH: govulncheck's default callgraph mode only reports vulns whose
|
||||
# code paths are actually reachable, so a finding here is a known CVE on a live
|
||||
# path — the gate must BLOCK, not warn.
|
||||
run_govulncheck() {
|
||||
local output_file="$OUTPUT_DIR/govulncheck.txt"
|
||||
local stderr_file="$OUTPUT_DIR/govulncheck.stderr.txt"
|
||||
|
||||
if [[ "$GATE" == "true" ]] && ! target_has_any_ext go mod sum; then
|
||||
echo "NO_GO_CHANGES_IN_TARGET" > "$output_file"
|
||||
TOOL_STATUS["govulncheck"]="skipped"
|
||||
TOOLS_SKIPPED=$((TOOLS_SKIPPED + 1))
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! run_tool "govulncheck" govulncheck; then return 0; fi
|
||||
|
||||
local modules
|
||||
modules="$(discover_go_modules)"
|
||||
if [[ -z "$modules" ]]; then
|
||||
echo "NO_GO_MODULES" > "$output_file"
|
||||
TOOL_STATUS["govulncheck"]="skipped"
|
||||
TOOLS_SKIPPED=$((TOOLS_SKIPPED + 1))
|
||||
return 0
|
||||
fi
|
||||
|
||||
: > "$stderr_file"
|
||||
: > "$output_file"
|
||||
|
||||
local vuln_count=0
|
||||
while IFS= read -r module_dir; do
|
||||
[[ -z "$module_dir" ]] && continue
|
||||
|
||||
local module_out module_stderr module_rc
|
||||
module_out="$(mktemp)"
|
||||
module_stderr="$(mktemp)"
|
||||
|
||||
echo "== govulncheck: $module_dir ==" >> "$output_file"
|
||||
module_rc=0
|
||||
(cd "$module_dir" && govulncheck ./... > "$module_out" 2> "$module_stderr") || module_rc=$?
|
||||
|
||||
cat "$module_out" >> "$output_file"
|
||||
echo "" >> "$output_file"
|
||||
|
||||
# Exit 0 = clean, 3 = vulns found (both are successful RUNS); anything
|
||||
# else (network/vulndb/toolchain failure) is a tool error — fail loud,
|
||||
# never silently green.
|
||||
if [[ "$module_rc" -ne 0 && "$module_rc" -ne 3 ]]; then
|
||||
{
|
||||
echo "ERROR: govulncheck exited $module_rc in $module_dir"
|
||||
[[ -s "$module_stderr" ]] && cat "$module_stderr"
|
||||
} >> "$stderr_file"
|
||||
else
|
||||
local module_vulns
|
||||
module_vulns=$(grep -c '^Vulnerability #' "$module_out" 2>/dev/null || true)
|
||||
module_vulns=${module_vulns:-0}
|
||||
module_vulns=$(echo "$module_vulns" | tr -d '[:space:]')
|
||||
vuln_count=$((vuln_count + module_vulns))
|
||||
fi
|
||||
|
||||
rm -f "$module_out" "$module_stderr"
|
||||
done <<< "$modules"
|
||||
|
||||
if [[ -s "$stderr_file" ]]; then
|
||||
TOOL_STATUS["govulncheck"]="error"
|
||||
cat "$stderr_file" >> "$output_file"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ "$vuln_count" -gt 0 ]]; then
|
||||
HIGH_COUNT=$((HIGH_COUNT + vuln_count))
|
||||
SECURITY_HIGH_COUNT=$((SECURITY_HIGH_COUNT + vuln_count))
|
||||
TOOL_STATUS["govulncheck"]="findings"
|
||||
else
|
||||
TOOL_STATUS["govulncheck"]="pass"
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# TOOL: hadolint (Dockerfile)
|
||||
# ============================================================================
|
||||
@@ -889,6 +974,7 @@ run_radon
|
||||
run_semgrep
|
||||
run_trivy
|
||||
run_gosec
|
||||
run_govulncheck
|
||||
run_hadolint
|
||||
run_pytest
|
||||
run_gotest
|
||||
@@ -909,7 +995,7 @@ fi
|
||||
# Build tools JSON object
|
||||
TOOLS_JSON="{"
|
||||
first=true
|
||||
for tool in ruff golangci-lint gitleaks shellcheck radon semgrep trivy gosec hadolint pytest go-test; do
|
||||
for tool in ruff golangci-lint gitleaks shellcheck radon semgrep trivy gosec govulncheck hadolint pytest go-test; do
|
||||
status="${TOOL_STATUS[$tool]:-not_run}"
|
||||
if [[ "$first" == "true" ]]; then
|
||||
first=false
|
||||
|
||||
Reference in New Issue
Block a user