diff --git a/scripts/post_alert_action.py b/scripts/post_alert_action.py index d412461..c694e88 100644 --- a/scripts/post_alert_action.py +++ b/scripts/post_alert_action.py @@ -42,6 +42,7 @@ BLENDER_NAME = "BLEnder" DISMISS_BLOCKED_SEVERITIES = {"critical", "high"} +DISMISS_UNKNOWN_SEVERITIES = {"", "unknown"} VERDICT_FILE = ".blender-alert-verdict.json" REQUIRED_KEYS = { "affected", @@ -470,6 +471,14 @@ def main() -> None: print(f" Existing PR #{existing_pr} covers this package.") comment_on_pr(repo, existing_pr, reason, dry_run) action = "existing_pr" + elif ( + dismiss_enabled + and severity.lower() not in DISMISS_BLOCKED_SEVERITIES + and severity.lower() not in DISMISS_UNKNOWN_SEVERITIES + ): + print(" Unaffected + dismiss enabled (low/medium). Dismissing alert.") + dismiss_alert(repo, alert_number, reason, dry_run) + action = "dismissed" elif recommended == "bump_pr": if ecosystem == "npm" and patched_version: print(" npm ecosystem — deferring to npm_bump workflow step.") @@ -511,10 +520,6 @@ def main() -> None: action = "noop" else: action = "noop" - elif dismiss_enabled and severity.lower() not in DISMISS_BLOCKED_SEVERITIES: - print(" Unaffected + dismiss enabled. Dismissing alert.") - dismiss_alert(repo, alert_number, reason, dry_run) - action = "dismissed" elif dismiss_enabled: print( f" Unaffected but severity is {severity}." diff --git a/tests/scripts/test_post_alert_action.py b/tests/scripts/test_post_alert_action.py index cb7f576..4e32bca 100644 --- a/tests/scripts/test_post_alert_action.py +++ b/tests/scripts/test_post_alert_action.py @@ -332,6 +332,23 @@ def test_dismiss_skips_high_severity( mock_repo._requester.requestJsonAndCheck.assert_not_called() + def test_dismiss_skips_unknown_severity( + self, verdict_file, tmp_path, monkeypatch + ): + """Empty/unknown severity is not auto-dismissed (could be high/critical).""" + verdict = {**SAMPLE_VERDICT, "recommended_action": "none"} + verdict_file(verdict) + mock_repo = MagicMock() + mock_repo.full_name = "owner/repo" + mock_repo.get_pulls.return_value = [] + + self._run_main( + verdict_file, tmp_path, monkeypatch, mock_repo, + DISMISS_UNAFFECTED="true", ALERT_SEVERITY="", + ) + + mock_repo._requester.requestJsonAndCheck.assert_not_called() + def test_npm_bump_outputs_action( self, verdict_file, tmp_path, monkeypatch ): @@ -363,6 +380,29 @@ def test_npm_bump_outputs_action( mock_repo.get_contents.assert_not_called() mock_repo.create_pull.assert_not_called() + def test_dismiss_precedes_npm_bump_for_unaffected( + self, verdict_file, tmp_path, monkeypatch + ): + """Unaffected low/medium alert is dismissed, not routed to npm_bump (#112).""" + verdict_file(SAMPLE_VERDICT) # affected=False, recommended_action=bump_pr + mock_repo = MagicMock() + mock_repo.full_name = "owner/repo" + mock_repo.get_pulls.return_value = [] # no existing PR + + output_file = str(tmp_path / "github_output") + open(output_file, "w").close() + + self._run_main( + verdict_file, tmp_path, monkeypatch, mock_repo, + ALERT_ECOSYSTEM="npm", ALERT_SEVERITY="medium", + DISMISS_UNAFFECTED="true", DRY_RUN="true", + GITHUB_OUTPUT=output_file, + ) + + outputs = open(output_file).read() + assert "action=dismissed" in outputs + assert "action=npm_bump" not in outputs + def test_npm_bump_no_patched_version( self, verdict_file, tmp_path, monkeypatch ):