From 6659d495e9c533ff0e00bc3e23d2d48fc700adfd Mon Sep 17 00:00:00 2001 From: svector-anu Date: Wed, 26 Aug 2026 06:43:19 +0100 Subject: [PATCH 1/3] add scanner execution evidence --- .github/workflows/aeon.yml | 14 ++++++++++++++ scripts/stage-vuln-scanner.sh | 24 ++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/.github/workflows/aeon.yml b/.github/workflows/aeon.yml index a1400e8c06b..a3150ca78e7 100644 --- a/.github/workflows/aeon.yml +++ b/.github/workflows/aeon.yml @@ -1051,6 +1051,20 @@ jobs: # Egress hardening P2: publish the warn-mode audit log. always() so it lands # even if the run failed; the host list is the allowlist oracle P3 builds from. + - name: Verify vuln-scanner execution evidence + if: always() && steps.work.outputs.mode != '' && steps.skill.outputs.name == 'vuln-scanner' + run: | + set -u + echo '--- staged scanner manifest ---' + if [ -f /tmp/vuln-scan/prefetch.txt ]; then cat /tmp/vuln-scan/prefetch.txt; else echo 'manifest=missing'; fi + echo '--- actual scanner invocations ---' + if [ -s /tmp/vuln-scan/executions.log ]; then + cat /tmp/vuln-scan/executions.log + else + echo 'executions=none' + echo '::warning::vuln-scanner produced no machine-readable scanner invocation evidence' + fi + - name: Upload egress audit log if: always() && vars.EGRESS_AUDIT == '1' uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 diff --git a/scripts/stage-vuln-scanner.sh b/scripts/stage-vuln-scanner.sh index 475d735bcb6..66c14c266c8 100755 --- a/scripts/stage-vuln-scanner.sh +++ b/scripts/stage-vuln-scanner.sh @@ -41,6 +41,23 @@ MANIFEST=/tmp/vuln-scan/prefetch.txt log() { echo "stage-vuln-scanner: $*"; } record() { echo "$1=$2" >> "$MANIFEST"; } # tool=installed|fail|skipped +# Keep machine-readable evidence that the agent actually invoked a scanner. The +# post-run workflow check reads this file; model prose is not execution evidence. +EXEC_LOG=/tmp/vuln-scan/executions.log +: > "$EXEC_LOG" + +instrument() { # command name, real executable + local name="$1" real="$2" wrapper="$BIN/$1" + [ -x "$real" ] || return 0 + mv "$real" "$BIN/.${name}.real" 2>/dev/null || return 0 + cat > "$wrapper" <> '$EXEC_LOG' +exec '$BIN/.${name}.real' "\$@" +EOF + chmod +x "$wrapper" +} + pip_install() { # package pip install --quiet "$1" 2>/dev/null \ || pip3 install --quiet "$1" 2>/dev/null \ @@ -117,5 +134,12 @@ else record cargo-fuzz skipped fi +# Wrap staged binaries after installation so every actual invocation is recorded. +instrument semgrep "$(command -v semgrep 2>/dev/null || true)" +instrument trufflehog "$BIN/trufflehog" +instrument osv-scanner "$BIN/osv-scanner" +instrument slither "$(command -v slither 2>/dev/null || true)" +instrument cargo-fuzz "$(command -v cargo-fuzz 2>/dev/null || true)" + log "manifest (/tmp/vuln-scan/prefetch.txt):" cat "$MANIFEST" From 08899890ef05ac1fcd60725f96e23baa4deb607f Mon Sep 17 00:00:00 2001 From: svector-anu Date: Wed, 26 Aug 2026 08:00:43 +0100 Subject: [PATCH 2/3] report each scanner execution separately --- .github/workflows/aeon.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/aeon.yml b/.github/workflows/aeon.yml index a3150ca78e7..58c5644e725 100644 --- a/.github/workflows/aeon.yml +++ b/.github/workflows/aeon.yml @@ -1064,6 +1064,22 @@ jobs: echo 'executions=none' echo '::warning::vuln-scanner produced no machine-readable scanner invocation evidence' fi + echo '--- per-tool execution status ---' + for tool in semgrep trufflehog osv-scanner; do + if [ -s /tmp/vuln-scan/executions.log ] && grep -q "^${tool} " /tmp/vuln-scan/executions.log; then + echo "${tool}=observed" + else + echo "${tool}=not-observed" + echo "::warning::${tool} was staged but no invocation was recorded" + fi + done + for tool in slither cargo-fuzz; do + if [ -s /tmp/vuln-scan/executions.log ] && grep -q "^${tool} " /tmp/vuln-scan/executions.log; then + echo "${tool}=observed" + else + echo "${tool}=not-observed-or-not-applicable" + fi + done - name: Upload egress audit log if: always() && vars.EGRESS_AUDIT == '1' From 4965fe055b6b2ea919d08d241ac564c97b3106d2 Mon Sep 17 00:00:00 2001 From: aaronjmars <61592645+aaronjmars@users.noreply.github.com> Date: Wed, 26 Aug 2026 09:11:25 -0400 Subject: [PATCH 3/3] fix: harden vuln-scanner evidence wrapper + reorder egress step - rm -f the wrapper path before the heredoc so a pip-installed scanner's symlink (e.g. semgrep/slither) can't be followed and recreated in the global bin dir; always writes a plain file into /tmp/bin. - Move "Verify vuln-scanner execution evidence" below "Upload egress audit log" so the "Egress hardening P2" comment documents the step it precedes. Co-Authored-By: Svector-anu --- .github/workflows/aeon.yml | 19 +++++++++++-------- scripts/stage-vuln-scanner.sh | 1 + 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.github/workflows/aeon.yml b/.github/workflows/aeon.yml index 58c5644e725..baf2a44addd 100644 --- a/.github/workflows/aeon.yml +++ b/.github/workflows/aeon.yml @@ -1051,6 +1051,17 @@ jobs: # Egress hardening P2: publish the warn-mode audit log. always() so it lands # even if the run failed; the host list is the allowlist oracle P3 builds from. + - name: Upload egress audit log + if: always() && vars.EGRESS_AUDIT == '1' + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: egress-${{ steps.skill.outputs.name }}-${{ github.run_id }} + path: ${{ runner.temp }}/iron/audit.jsonl + if-no-files-found: ignore + + # Prove the vuln-scanner actually invoked a scanner: the staged wrappers log + # each call to executions.log; model prose is not execution evidence. Best-effort, + # warnings only, never gates the run. - name: Verify vuln-scanner execution evidence if: always() && steps.work.outputs.mode != '' && steps.skill.outputs.name == 'vuln-scanner' run: | @@ -1081,14 +1092,6 @@ jobs: fi done - - name: Upload egress audit log - if: always() && vars.EGRESS_AUDIT == '1' - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: egress-${{ steps.skill.outputs.name }}-${{ github.run_id }} - path: ${{ runner.temp }}/iron/audit.jsonl - if-no-files-found: ignore - # Undo "Single-source standing instructions" so the working tree matches HEAD # before any capture/guard/commit step runs. always(): the file must come back # even if Run failed or timed out. The move is a plain rename back — it touches diff --git a/scripts/stage-vuln-scanner.sh b/scripts/stage-vuln-scanner.sh index 66c14c266c8..b67e135c254 100755 --- a/scripts/stage-vuln-scanner.sh +++ b/scripts/stage-vuln-scanner.sh @@ -50,6 +50,7 @@ instrument() { # command name, real executable local name="$1" real="$2" wrapper="$BIN/$1" [ -x "$real" ] || return 0 mv "$real" "$BIN/.${name}.real" 2>/dev/null || return 0 + rm -f "$wrapper" # drop any dangling symlink so the heredoc writes a plain file into $BIN cat > "$wrapper" <> '$EXEC_LOG'