Skip to content

fix(cli): JSON output reports warn as medium for pre-bd9313a6 callers (D260) - #895

Merged
hyperpolymath merged 3 commits into
mainfrom
fix/json-warn-severity-map
Oct 1, 2026
Merged

hyperpolymath merged 3 commits into
mainfrom
fix/json-warn-severity-map

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Why

Since a63c432 (#763), the ResearchExtensions rules emit severity "warn". Callers pinned to copies of standards' hypatia-scan-reusable.yml older than bd9313a6 validate the JSON findings against critical/high/medium/low/info/informational. A single "warn" makes them reject the whole array, and they fail with:

Hypatia did not produce one valid findings array

The estate health census (2026-09-30) found hypatia-scan.yml failing on 354 repos. In a 100-pin sample, about 65 were on rejecting copies: 8f2ee508, 81dbf2dd, 571cc734, 84355587 and 092deda.

The reusable runs git clone --depth 1 of hypatia HEAD, so a fix in the JSON sink clears every such caller at once. This is the owner ruling D260 ("Both") on standards#787.

What

  • lib/hypatia/cli.ex: output(findings, "json") maps severity: "warn" → "medium".
    • warn already ranks with medium in @severity_order, so no information is lost.
    • SARIF output (level_for("warn") → "warning") is unchanged.
    • GitHub-format output is unchanged.
  • test/research_extensions_wiring_test.exs: a new test.
    • It runs CLI.main(["scan", …, "--format", "json"]) on the RE tripwire repo.
    • It asserts that "medium" is present, that "warn" is absent, and that every severity is in the accepted five.
  • The bash fallback (hypatia-cli-bash.sh) never emits "warn", so the escript is the only path that needed the fix.

Evidence

  • mix test test/research_extensions_wiring_test.exs: 11 tests, 0 failures.
  • cli_test.exs together with the wiring test: 18 tests, 0 failures.
  • Mutant: with the shim passing warn through unchanged, the suite gives 11 tests and 1 failure (the new test). The test bites.
  • mix format --check-formatted is clean on both files.

Removal criterion

This shim is temporary. Callers get repinned to standards ≥ bd9313a6 in the actions.lock regen sweep (E1a). A follow-up PR removes the mapping once an enumerated census of hypatia-scan callers (per-repo workflow listing, not gh search) shows none pinned to a rejecting copy.

Post-merge verification

hyperpolymath/laniakea is the known-answer repo: it is pinned to 8f2ee508, and its hypatia-scan must turn green after this merges.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YJ6PbZUYBcjJv7FTRfyogo

… (D260)

Since a63c432 (#763) the ResearchExtensions rules emit severity "warn".
Callers pinned to standards' hypatia-scan-reusable.yml before bd9313a6
(8f2ee508, 81dbf2dd, 571cc734, 84355587, 092deda) validate the JSON
findings against critical/high/medium/low/info and reject the whole
array on a single "warn", failing with "Hypatia did not produce one
valid findings array" on ~350 repos.

The reusable clones hypatia HEAD, so mapping warn -> medium in the JSON
sink clears every such caller at once. warn already ranks with medium in
@severity_order, so no information is lost. SARIF ("warning") and GitHub
output are unchanged.

Removal criterion (owner ruling D260, standards#787): drop the shim once
an enumerated census of hypatia-scan callers shows none pinned to a
rejecting copy of the reusable.

Test: CLI JSON output on the RE tripwire repo contains "medium", never
"warn", and only the five accepted severities. Mutant (shim passes warn
through) turns it red: 11 tests, 1 failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJ6PbZUYBcjJv7FTRfyogo
@coderabbitai

coderabbitai Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: a4de0dea-db0e-4a21-974e-99f5cef9d12a

📥 Commits

Reviewing files that changed from the base of the PR and between b383356 and 251ce2b.

📒 Files selected for processing (2)
  • lib/hypatia/cli.ex
  • test/research_extensions_wiring_test.exs

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Recent review details
⏰ Context from checks skipped due to timeout. (34)
  • GitHub Check: Cargo check + clippy + fmt
  • GitHub Check: Format
  • GitHub Check: Check
  • GitHub Check: Clippy
  • GitHub Check: Test
  • GitHub Check: governance / Actions lockfile verify
  • GitHub Check: governance / Check Workflow Staleness
  • GitHub Check: governance / Debt ratchet
  • GitHub Check: governance / Guix packaging policy (Nix retired)
  • GitHub Check: governance / Well-Known (RFC 9116 + RSR)
  • GitHub Check: governance / Workflow security linter
  • GitHub Check: governance / Allowlist Preflight
  • GitHub Check: governance / Code quality + docs
  • GitHub Check: governance / Language / package anti-pattern policy
  • GitHub Check: governance / Live Actions policy (credentialed advisory)
  • GitHub Check: governance / Trusted-base reduction policy
  • GitHub Check: governance / Exemption ratchet
  • GitHub Check: governance / Licence consistency
  • GitHub Check: scan / gitleaks
  • GitHub Check: scan / shell-secrets
  • GitHub Check: governance / Security policy checks
  • GitHub Check: scan / rust-secrets
  • GitHub Check: hypatia / Hypatia Neurosymbolic Analysis
  • GitHub Check: zig build test (FFI + wire contract)
  • GitHub Check: Startup probe
  • GitHub Check: Language Policy
  • GitHub Check: abi-codegen-drift
  • GitHub Check: Empty-linter (invisible characters)
  • GitHub Check: Validate DEED manifests
  • GitHub Check: Validate K9 contracts
  • GitHub Check: Groove manifest check
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: Startup probe
  • GitHub Check: Language Policy
🔇 Additional comments (3)
lib/hypatia/cli.ex (2)

1205-1205: LGTM!


1260-1260: 🗄️ Data Integrity & Integration

ImplementationInsideCanon.scan/1 cannot bypass this conversion with :warn. Its finding/4 function stores to_string(rule.severity || :medium), so an atom-valued :warn becomes the string "warn" before json_compat_severity/1 receives the finding.

test/research_extensions_wiring_test.exs (1)

164-197: LGTM!


📝 Summary

Summary by CodeRabbit

  • Bug Fixes
    • JSON output now reports findings with warn severity as medium. Other severity levels remain unchanged.

Walkthrough

The JSON output handler converts findings with severity "warn" to "medium" before encoding. A CLI test checks the conversion and confirms that output uses only the allowed severity values.

Changes

JSON severity compatibility

Layer / File(s) Summary
JSON mapping and validation
lib/hypatia/cli.ex, test/research_extensions_wiring_test.exs
The JSON handler converts "warn" findings to "medium" and leaves other findings unchanged. The CLI test checks the conversion and allowed severity values.

Priority: ⬆️ High

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to 251ce

Warning findings now emit the medium value accepted by older validators, and the CLI test checks that output. No actionable merge-blocking risk remains.

Security Architecture Review

Security architecture risk: ⚪ Minimal · up to 251ce

The change translates warning-tier findings to medium only when producing JSON. It preserves findings, other severity levels, scan thresholds, and exit behavior. No material security risk was identified in this narrowly scoped change.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The direct changed exposure is the severity label delivered to JSON consumers of warning-producing scans. The inspected transformation adds no repository-reading capability, credential access, execution sink, or persistent state transition; its output remains JSON written to stdout.

Security Findings and Attack Paths

  • inferred — Repository content that triggers a warning still produces a finding. The conversion neither removes findings nor downgrades high or critical severity, and warn already shares medium's threshold rank. The inspected path therefore provides no new finding-suppression or CLI gate-bypass mechanism.

Trust Boundaries and Controls

  • observed — The conversion is restricted to JSON serialization. SARIF still receives the original findings, and GitHub annotations still map warn to warning. CLI threshold filtering and nonzero exit behavior remain separate from the compatibility helper.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarises the main change: mapping JSON severity "warn" to "medium" for compatibility with older callers.
Description check ✅ Passed The description directly explains the compatibility problem, the implementation, the tests, the temporary removal criterion, and post-merge verification.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

A rabbit checks the JSON stream,
And sees "warn" become "medium" with care.
The other findings keep their names,
While allowed severities fill the output.
The tests confirm the mapping holds,
Then hop away through fields of data.

Comment @coderabbitai help to get the list of available commands.

The D260 shim helpers sat between the output/2 clauses, which Elixir
warns about ("clauses with the same name and arity should be grouped
together"). escript-soundness.yml compiles with --warnings-as-errors,
so the job went red. Move json_compat_severity/1 below the last
output/2 clause; behaviour is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJ6PbZUYBcjJv7FTRfyogo
@hyperpolymath
hyperpolymath enabled auto-merge (squash) October 1, 2026 19:43
@hyperpolymath
hyperpolymath disabled auto-merge October 1, 2026 19:43
@hyperpolymath
hyperpolymath merged commit 51ab649 into main Oct 1, 2026
42 of 46 checks passed
@hyperpolymath
hyperpolymath deleted the fix/json-warn-severity-map branch October 1, 2026 21:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant