fix(test-steps): correct YAML structure handling in bare re-raise check
CI / benchmark-publish (pull_request) Has been skipped
CI / push-validation (pull_request) Successful in 30s
CI / helm (pull_request) Successful in 40s
CI / build (pull_request) Successful in 47s
CI / lint (pull_request) Failing after 1m8s
CI / quality (pull_request) Successful in 1m9s
CI / typecheck (pull_request) Successful in 1m19s
CI / security (pull_request) Successful in 1m41s
CI / integration_tests (pull_request) Successful in 3m20s
CI / e2e_tests (pull_request) Successful in 4m23s
CI / unit_tests (pull_request) Successful in 4m44s
CI / docker (pull_request) Has been skipped
CI / coverage (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 3s

The Semgrep patterns node is a YAML list (not dict), so the previous
implementation's .get() call on patterns would fail with AttributeError.

Fix: properly handle 'patterns' as a list of alternation groups, each
containing pattern-either -> patterns -> pattern-not entries. This
ensures the BDD assertion robustly finds bare raise pattern-nots in
the xml-no-suppressed-exception rule's nested structure.

Issues Closed: #9103
This commit is contained in:
2026-05-07 07:18:32 +00:00
parent 35a35c9f1b
commit 9034786b38
+32 -27
View File
@@ -252,39 +252,44 @@ def step_semgrep_rule_has_reraise_pattern_not(context: Any, rule_id: str) -> Non
rules = config.get("rules", [])
for rule in rules:
if rule.get("id") == rule_id:
# Walk the patterns structure to find pattern-not entries with bare raise
patterns = rule.get("patterns", {})
pattern_either = patterns.get("pattern-either", [])
# Walk the Semgrep patterns structure to find pattern-not entries with bare raise.
# In YAML, 'patterns' is a list of alternative match groups (pattern-either).
patterns = rule.get("patterns", [])
if not isinstance(patterns, list):
raise AssertionError(
f"Rule '{rule_id}' has unexpected 'patterns' type: "
f"{type(patterns).__name__}"
)
found_bare_reraise = False
for option in pattern_either:
sub_patterns = option.get("patterns", [])
for sp in sub_patterns:
pn = sp.get("pattern-not", "")
if isinstance(pn, str):
# A bare re-raise pattern-not should have "raise" on its own
# (not followed by $EXC or from clause)
import re
# Look for a pattern that has raise without arguments
# e.g., "raise$" at end of a line (bare raise)
lines = pn.split("\n")
for line in lines:
stripped = line.strip()
# Bare re-raise: just "raise" with no trailing code
if stripped == "raise":
found_bare_reraise = True
break
for group in patterns:
either_list = group.get("pattern-either", [])
if not isinstance(either_list, list):
continue
for alt in either_list:
sub_pats = alt.get("patterns", [])
if not isinstance(sub_pats, list):
continue
for sp in sub_pats:
pn = sp.get("pattern-not", "")
if isinstance(pn, str):
# A bare re-raise pattern-not must contain "raise" on its own line
# (not raise $EXC or raise ... from ...)
lines = pn.split("\n")
for line in lines:
if line.strip() == "raise":
found_bare_reraise = True
break
# Also check nested pattern-either blocks
nested_either = option.get("pattern-either")
if nested_either:
for nested_opt in nested_either:
pn = nested_opt.get("pattern-not", "")
# Also handle nested pattern-either blocks inside alternation groups
nested_either = alt.get("pattern-either") if isinstance(alt, dict) else None
if isinstance(nested_either, list):
for nested_alt in nested_either:
pn = nested_alt.get("pattern-not", "")
if isinstance(pn, str):
lines = pn.split("\n")
for line in lines:
stripped = line.strip()
if stripped == "raise":
if line.strip() == "raise":
found_bare_reraise = True
break