-
Notifications
You must be signed in to change notification settings - Fork 0
fix(strix): scope neutral-skip to the log tail after an exemption #1224
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
Merged
seonghobae
merged 2 commits into
main
from
fix/strix-neutral-skip-after-exempted-finding
Aug 22, 2026
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
154 changes: 154 additions & 0 deletions
154
tests/test_strix_backend_unavailable_after_exempted_finding.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,154 @@ | ||
| """Regression contract for backend-outage neutral-skip after an exempted finding. | ||
|
|
||
| The Strix required check's console log can legitimately contain an | ||
| already-exempted vulnerability (out-of-scope unchanged-file evidence, or one | ||
| below the configured minimum severity) *and* a later, unrelated provider | ||
| outage in the same run: the gate first prints "... allowing pipeline | ||
| continuation." for the exempted finding, then a later fallback-model attempt | ||
| fails for an infrastructure reason (for example GitHub Models' scheduled | ||
| retirement brownout, HTTP 410 code `github_models_retirement_brownout`). | ||
|
|
||
| Before this fix, the workflow's outer neutral-skip decision grepped the whole | ||
| combined log for `reported_vulnerability_signal`, so the earlier -- already | ||
| exempted -- finding's own "Vulnerabilities N" / "severity:" text permanently | ||
| disqualified the neutral skip, turning a pure CI-infrastructure outage into a | ||
| required-check failure that blocks merges. The fix scopes that decision to | ||
| the log tail after the last "allowing pipeline continuation" marker. This | ||
| test extracts the actual bash block from the workflow (not a reimplementation) | ||
| and executes it against synthetic logs shaped like the real PR #392 run. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import re | ||
| import subprocess | ||
| import tempfile | ||
| import unittest | ||
| from pathlib import Path | ||
|
|
||
|
|
||
| REPOSITORY_ROOT = Path(__file__).resolve().parents[1] | ||
| STRIX_WORKFLOW = REPOSITORY_ROOT / ".github" / "workflows" / "strix.yml" | ||
|
|
||
| # An exempted finding: Strix reported a real vulnerability, but the gate | ||
| # itself already decided it is out of scope (unchanged files) and continued. | ||
| EXEMPTED_FINDING_AND_CONTINUATION = ( | ||
| "Vulnerability Report\n" | ||
| "Severity: CRITICAL\n" | ||
| "Vulnerabilities 1\n" | ||
| "CRITICAL: 1\n" | ||
| "Strix findings are limited to unchanged files in this pull request; " | ||
| "allowing pipeline continuation.\n" | ||
| ) | ||
|
|
||
| # The exact GitHub Models scheduled-retirement brownout observed in PR #392's | ||
| # required Strix check (run 32530198775, job 96920534139). | ||
| GITHUB_MODELS_BROWNOUT = ( | ||
| "LLM CONNECTION FAILED\n" | ||
| "Could not establish connection to the language model.\n" | ||
| "Error: Error code: 410 - {'error': {'code': " | ||
| "'github_models_retirement_brownout', 'message': 'GitHub Models is " | ||
| "temporarily unavailable as part of a scheduled retirement brownout.'}}\n" | ||
| "Strix run failed for model 'github_models/openai/o3' after 5s " | ||
| "(exit code 1).\n" | ||
| "Configured model and fallback models were unavailable.\n" | ||
| ) | ||
|
|
||
|
|
||
| def _extract_neutralization_block(workflow: str) -> str: | ||
| """Return the gate's neutral-skip decision block, verbatim from the yml. | ||
|
|
||
| Bounded by two unique anchors already present in the workflow so a future | ||
| unrelated edit to this step fails the test instead of silently testing | ||
| stale logic. | ||
| """ | ||
|
|
||
| start_marker = ( | ||
| " # Recognized signals that the LLM backend was unavailable" | ||
| ) | ||
| end_marker = ' exit "$strix_rc"\n' | ||
| start = workflow.index(start_marker) | ||
| end = workflow.index(end_marker, start) + len(end_marker) | ||
| return workflow[start:end] | ||
|
|
||
|
|
||
| def _run_gate_tail(log_text: str) -> int: | ||
| """Execute the extracted block against a synthetic log; return its exit code. | ||
|
|
||
| 0 means the run neutral-skips (CI-infrastructure outage, not a finding). | ||
| Any other code means the block falls through to the hard failure branch, | ||
| matching the real workflow's `exit "$strix_rc"`. | ||
| """ | ||
|
|
||
| workflow = STRIX_WORKFLOW.read_text(encoding="utf-8") | ||
| block = _extract_neutralization_block(workflow) | ||
| with tempfile.TemporaryDirectory(prefix="strix-tail-scope-") as temp_dir: | ||
| strix_run_log = Path(temp_dir) / "strix_gate_console.log" | ||
| strix_run_log.write_text(log_text, encoding="utf-8") | ||
| script = "\n".join( | ||
| ( | ||
| "set -uo pipefail", | ||
| 'strix_run_log="$1"', | ||
| "strix_rc=1", | ||
| block, | ||
| ) | ||
| ) | ||
| completed = subprocess.run( | ||
| [ | ||
| "bash", | ||
| "-c", | ||
| script, | ||
| "strix-tail-scope", | ||
| str(strix_run_log), | ||
| ], | ||
| check=False, | ||
| capture_output=True, | ||
| text=True, | ||
| env={"RUNNER_TEMP": temp_dir, "PATH": "/usr/bin:/bin"}, | ||
| ) | ||
| return completed.returncode | ||
|
|
||
|
|
||
| class StrixBackendUnavailableAfterExemptedFindingTests(unittest.TestCase): | ||
| """Protect the PR #392-shaped scenario without weakening the real gate.""" | ||
|
|
||
| def test_workflow_defines_the_tail_scoping_step(self) -> None: | ||
| """Keep the fix's shape present so a future edit cannot drop it silently.""" | ||
|
|
||
| workflow = STRIX_WORKFLOW.read_text(encoding="utf-8") | ||
| self.assertIn("strix_neutralization_scope_log", workflow) | ||
| self.assertIn("allowing pipeline continuation", workflow) | ||
| self.assertIn("github_models_retirement_brownout", workflow) | ||
| self.assertIn("Error code:[[:space:]]*410", workflow) | ||
|
|
||
| def test_neutralizes_brownout_after_an_already_exempted_finding(self) -> None: | ||
| """The PR #392 shape: exempted finding, then an unrelated 410 brownout.""" | ||
|
|
||
| log = EXEMPTED_FINDING_AND_CONTINUATION + GITHUB_MODELS_BROWNOUT | ||
| self.assertEqual(_run_gate_tail(log), 0) | ||
|
|
||
| def test_still_fails_closed_on_a_finding_reported_after_continuation(self) -> None: | ||
| """A real finding surfacing *after* the continuation marker still blocks.""" | ||
|
|
||
| log = ( | ||
| EXEMPTED_FINDING_AND_CONTINUATION | ||
| + "Vulnerability Report\nSeverity: CRITICAL\nVulnerabilities 1\n" | ||
| ) | ||
| self.assertNotEqual(_run_gate_tail(log), 0) | ||
|
|
||
| def test_still_fails_closed_with_no_continuation_marker_at_all(self) -> None: | ||
| """Preserve prior behavior: a bare unresolved finding still blocks.""" | ||
|
|
||
| log = "Vulnerability Report\nSeverity: CRITICAL\nVulnerabilities 1\n" | ||
| self.assertNotEqual(_run_gate_tail(log), 0) | ||
|
|
||
| def test_still_neutralizes_a_bare_backend_outage_with_no_finding_at_all( | ||
| self, | ||
| ) -> None: | ||
| """Preserve prior behavior: a pure outage with no finding still skips.""" | ||
|
|
||
| self.assertEqual(_run_gate_tail(GITHUB_MODELS_BROWNOUT), 0) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| unittest.main() |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.