Repository navigation
fix: the Hypatia gate could never fire — the defects that made it unconditionally vacuous #73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -53,14 +53,28 @@ jobs: | |
| if: steps.install.outputs.installed == 'true' | ||
| run: | | ||
| set +e | ||
| panic-attack assail --format json . > panic-attack-findings.json 2>&1 | ||
| panic-attack assail --format json . > panic-attack-findings.json | ||
| PA_EXIT=$? | ||
| set -e | ||
|
|
||
| # Same defect class as the Hypatia job below: `2>&1` folded the | ||
| # scanner's stderr into the JSON payload, so every jq parse failed, | ||
| # every count silently became 0 via `|| echo 0`, and "Fail on critical | ||
| # findings" could never fire on any input. Keep stderr on the log. | ||
| if [ ! -s panic-attack-findings.json ]; then | ||
| echo "[]" > panic-attack-findings.json | ||
| fi | ||
|
|
||
| # Deliberately a WARNING, not a failure. panic-attack is a downloaded | ||
| # release binary whose exit-code and output contract are not verified | ||
| # here, and it has no confirmed --exit-zero equivalent, so we surface a | ||
| # malformed payload in the log rather than block on an unverified tool. | ||
| # Promote to `exit 1` (as the Hypatia job does) once that contract is | ||
| # confirmed -- see the follow-up issue linked from this PR. | ||
| if ! jq -e 'type == "array"' panic-attack-findings.json >/dev/null 2>&1; then | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- workflow ---'
sed -n '1,150p' .github/workflows/static-analysis-gate.yml
printf '%s\n' '--- documented panic-attack output ---'
rg -n -C 5 'weak_points|panic-attack|assail|findings' docs/governance/MAINTENANCE-CHECKLIST.adoc .github/workflows/static-analysis-gate.yml
printf '%s\n' '--- repository references ---'
rg -n -C 4 'panic-attack-findings|panic-attack|weak_points' --glob '!node_modules' --glob '!dist' --glob '!build' .Repository: hyperpolymath/llm-grace Length of output: 50381 🌐 Web query:
💡 Result: In the context of the Hyperpolymath software ecosystem, panic-attack is a multi-language security analysis and stress-testing tool [1][2][3]. The assail command is a specific subcommand of the panic-attack CLI designed for static analysis [1][2]. When executed with the --format json flag, it outputs a JSON object containing a list of detected weak_points [4][5][6]. The following structure represents the typical JSON output schema for the weak_points identified by the assail command [4][5][6][7]: { "weak_points": [ { "severity": "string", "location": "string", "description": "string", "test_context": "string" } ] } Key details regarding this output include: - The root of the JSON object is an array named weak_points [4][5]. - Each entry in the array typically includes fields for severity (e.g., Critical, High, Medium, Low), the file location, a description of the finding, and optionally the test_context (e.g., Production, TestOnly, Doc) [5][7]. - If no weak points are found, the tool returns an empty list: {"weak_points": []} [4][6]. - The tool identifies 25 canonical weak-point categories, such as UnsafeCode, PanicPath, CommandInjection, and HardcodedSecret [1][7]. - Users can audit findings and record false positives in a separate registry, allowing for persistent suppression of specific weak points while maintaining a clean, verifiable codebase [8][9][7]. Citations:
Parse the
🤖 Prompt for AI Agents |
||
| echo "::warning::panic-attack output is not a JSON array (exit ${PA_EXIT}); counts below are unreliable" | ||
| fi | ||
|
|
||
| # Parse finding counts | ||
| TOTAL=$(jq '. | length' panic-attack-findings.json 2>/dev/null || echo 0) | ||
| CRITICAL=$(jq '[.[] | select(.severity == "critical")] | length' panic-attack-findings.json 2>/dev/null || echo 0) | ||
|
|
@@ -79,13 +93,19 @@ jobs: | |
| if: steps.install.outputs.installed == 'true' | ||
| run: | | ||
| # Convert JSON findings into GitHub Actions annotations | ||
| jq -r '.[] | select(.file != null) | | ||
| # Findings carry no `.message` (keys: action,file,line,reason,rule_module, | ||
| # severity,type), so every annotation read "null". `.file` is an absolute | ||
| # runner path, which GitHub cannot anchor to the diff, so it is made | ||
| # workspace-relative here. | ||
| jq -r --arg ws "$GITHUB_WORKSPACE" '.[] | select(.file != null) | | ||
| (.file | ltrimstr($ws + "/")) as $f | | ||
| (.reason // .message // .type // "finding") as $m | | ||
| if .severity == "critical" then | ||
| "::error file=\(.file),line=\(.line // 1)::[panic-attack] \(.message)" | ||
| "::error file=\($f),line=\(.line // 1)::[panic-attack] \($m)" | ||
| elif .severity == "high" then | ||
| "::error file=\(.file),line=\(.line // 1)::[panic-attack] \(.message)" | ||
| "::error file=\($f),line=\(.line // 1)::[panic-attack] \($m)" | ||
| else | ||
| "::warning file=\(.file),line=\(.line // 1)::[panic-attack] \(.message)" | ||
| "::warning file=\($f),line=\(.line // 1)::[panic-attack] \($m)" | ||
| end | ||
| ' panic-attack-findings.json || true | ||
|
|
||
|
|
@@ -169,12 +189,28 @@ jobs: | |
| if: steps.build.outputs.ready == 'true' | ||
| run: | | ||
| set +e | ||
| HYPATIA_FORMAT=json "$HOME/hypatia/hypatia-cli.sh" scan . > hypatia-findings.json 2>&1 | ||
| HYPATIA_FORMAT=json "$HOME/hypatia/hypatia-cli.sh" scan . --exit-zero > hypatia-findings.json | ||
| HYP_EXIT=$? | ||
| set -e | ||
|
|
||
| if [ ! -s hypatia-findings.json ] || ! jq empty hypatia-findings.json 2>/dev/null; then | ||
| echo "[]" > hypatia-findings.json | ||
| # --exit-zero is Hypatia's own documented CI recipe (lib/hypatia/cli.ex), | ||
| # for exactly this case: "use in CI when a downstream step gates on | ||
| # severity counts". Findings go to stdout, the one-line summary to | ||
| # stderr, and the process exits 0 unless the SCANNER itself failed. | ||
| # | ||
| # Do NOT redirect stderr into the payload with `2>&1`: that folds the | ||
| # summary line into the JSON, so every parse fails, the old `[]` | ||
| # fallback substituted a clean result, CRITICAL was always 0, and the | ||
| # gate below could never fire on any input. Keep stderr on the log. | ||
| if [ "$HYP_EXIT" -ne 0 ]; then | ||
| echo "::error::Hypatia scanner execution failed with exit ${HYP_EXIT}" | ||
| exit "$HYP_EXIT" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- workflow context ---'
sed -n '150,235p' .github/workflows/static-analysis-gate.yml
printf '%s\n' '--- workflow references ---'
rg -n -C 4 'hypatia-findings|deposit-findings|HYP_EXIT|upload-artifact|download-artifact|panic-attack' .github/workflows/static-analysis-gate.yml
printf '%s\n' '--- documented panic-attack output ---'
rg -n -C 5 'panic-attack|weak_points' docs/governance/MAINTENANCE-CHECKLIST.adocRepository: hyperpolymath/llm-grace Length of output: 23176 🌐 Web query:
💡 Result: In Citations:
Preserve the findings artifact after a Hypatia failure. If the scan exits at line 207, the later upload step is skipped. 🤖 Prompt for AI Agents |
||
| fi | ||
| # `jq empty` is NOT sufficient -- it succeeds on any valid JSON, | ||
| # including a bare string, object or null. Assert the array. | ||
| if [ ! -s hypatia-findings.json ] || ! jq -e 'type == "array"' hypatia-findings.json >/dev/null; then | ||
| echo "::error::Hypatia did not produce a valid JSON findings array" | ||
| exit 1 | ||
| fi | ||
|
|
||
| TOTAL=$(jq '. | length' hypatia-findings.json 2>/dev/null || echo 0) | ||
|
|
@@ -192,13 +228,19 @@ jobs: | |
| - name: Emit check annotations | ||
| if: steps.build.outputs.ready == 'true' | ||
| run: | | ||
| jq -r '.[] | select(.file != null) | | ||
| # Findings carry no `.message` (keys: action,file,line,reason,rule_module, | ||
| # severity,type), so every annotation read "null". `.file` is an absolute | ||
| # runner path, which GitHub cannot anchor to the diff, so it is made | ||
| # workspace-relative here. | ||
| jq -r --arg ws "$GITHUB_WORKSPACE" '.[] | select(.file != null) | | ||
| (.file | ltrimstr($ws + "/")) as $f | | ||
| (.reason // .message // .type // "finding") as $m | | ||
| if .severity == "critical" then | ||
| "::error file=\(.file),line=\(.line // 1)::[hypatia] \(.message)" | ||
| "::error file=\($f),line=\(.line // 1)::[hypatia] \($m)" | ||
| elif .severity == "high" then | ||
| "::error file=\(.file),line=\(.line // 1)::[hypatia] \(.message)" | ||
| "::error file=\($f),line=\(.line // 1)::[hypatia] \($m)" | ||
| else | ||
| "::warning file=\(.file),line=\(.line // 1)::[hypatia] \(.message)" | ||
| "::warning file=\($f),line=\(.line // 1)::[hypatia] \($m)" | ||
| end | ||
| ' hypatia-findings.json || true | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: hyperpolymath/llm-grace
Length of output: 34056
Warn before writing the empty-array fallback.
If
panic-attackproduces no stdout, line 65 writes[]before line 74 validates the output. The workflow then records zero findings without warning about the failed scan. Check and warn on the original output before writing the fallback.🤖 Prompt for AI Agents