@@ -83,7 +83,6 @@ def main(argv: Optional[list[str]] = None) -> int:
8383
8484 all_reports : list [dict ] = []
8585 any_denied = False
86- review = 0
8786
8887 for target in targets :
8988 if not target .is_file ():
@@ -102,8 +101,6 @@ def main(argv: Optional[list[str]] = None) -> int:
102101
103102 if report .decision .value == "deny" :
104103 any_denied = True
105- if report .decision .value == "needs_human_review" :
106- review += 1
107104 if args .verbose or report .decision .value != "allow" :
108105 print (f"[{ report .decision .value .upper ():>20} ] { target .name } "
109106 f"(risk={ report .risk_level .value } , rules={ report .rule_ids } )" )
@@ -125,24 +122,27 @@ def main(argv: Optional[list[str]] = None) -> int:
125122 print (f"\n Summary: { len (all_reports )} scanned | "
126123 f"{ allowed } allow | { denied } deny | { review_count } needs_review" )
127124
125+ manifest_failed = False
128126 if args .manifest :
129- _check_manifest (Path (args .manifest ), all_reports )
127+ manifest_failed = not _check_manifest (Path (args .manifest ), all_reports )
130128
131- if any_denied :
129+ # Manifest mismatch is a CI failure even when no deny was produced.
130+ if manifest_failed or any_denied :
132131 return 1
133132 if review_count > 0 :
134133 return 2
135134 return 0
136135
137136
138- def _check_manifest (manifest_path : Path , reports : list [dict ]) -> None :
137+ def _check_manifest (manifest_path : Path , reports : list [dict ]) -> bool :
138+ """Validate reports against manifest. Returns True when all cases match."""
139139 import yaml
140140
141141 data = yaml .safe_load (manifest_path .read_text (encoding = "utf-8" )) or {}
142142 cases = data .get ("cases" ) or data .get ("samples" ) or data
143143 if not isinstance (cases , list ):
144144 print ("manifest: unexpected format" , file = sys .stderr )
145- return
145+ return False
146146 by_name = {Path (r ["script_path" ]).name : r for r in reports }
147147 ok = 0
148148 fail = 0
@@ -156,12 +156,19 @@ def _check_manifest(manifest_path: Path, reports: list[dict]) -> None:
156156 print (f"manifest MISS { name } " )
157157 fail += 1
158158 continue
159- if rec ["decision" ] == expect :
159+ got = rec ["decision" ]
160+ # needs_human_review expectations also accept deny (stricter is fine).
161+ if expect == "needs_human_review" :
162+ matched = got in ("needs_human_review" , "deny" )
163+ else :
164+ matched = got == expect
165+ if matched :
160166 ok += 1
161167 else :
162- print (f"manifest FAIL { name } : got { rec [ 'decision' ] } , expect { expect } " )
168+ print (f"manifest FAIL { name } : got { got } , expect { expect } " )
163169 fail += 1
164170 print (f"Manifest: { ok } ok | { fail } fail" )
171+ return fail == 0
165172
166173
167174def _infer_language (path : Path ) -> str :
0 commit comments