diff --git a/.github/workflows/aeon.yml b/.github/workflows/aeon.yml index a1400e8c06b..baf2a44addd 100644 --- a/.github/workflows/aeon.yml +++ b/.github/workflows/aeon.yml @@ -1059,6 +1059,39 @@ jobs: 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: | + 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 + 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 + # 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 475d735bcb6..b67e135c254 100755 --- a/scripts/stage-vuln-scanner.sh +++ b/scripts/stage-vuln-scanner.sh @@ -41,6 +41,24 @@ 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 + rm -f "$wrapper" # drop any dangling symlink so the heredoc writes a plain file into $BIN + 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 +135,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"