@@ -1165,37 +1165,66 @@ def _text(value: Any, limit: int | None = 170) -> str:
11651165 # Model text stays literal; it cannot create Markdown links or images.
11661166 return "" .join ("\\ " + char if char in "\\ `*_{}[]()#+-.!|~$" else char for char in value )
11671167
1168- def show_table (title : str , rows : Sequence [Mapping [str , Any ]], columns : Sequence [str ]):
1168+ def show_table (title : str , rows : Sequence [Mapping [str , Any ]], columns : Sequence [str ], * ,
1169+ scrollable : bool = False ):
11691170 rows = list (rows )
11701171 lines = [f"**{ _text (title )} **" , "" ,
11711172 "| " + " | " .join (map (_text , columns )) + " |" ,
11721173 "| " + " | " .join ("---" for _ in columns ) + " |" ]
11731174 body = ["| " + " | " .join (_text (row .get (column , "" )) for column in columns )
11741175 + " |" for row in rows ]
1175- display (Markdown ("\n " .join ((* lines , * body ))))
1176-
1177- def show_review_details (reviews : Mapping [str , Any ]):
1178- pending = [
1179- (target , finding )
1180- for target , bundle in reviews .items ()
1181- for finding in (bundle .provenance .findings if bundle .provenance else ())
1182- if finding .disposition == "needs_review"
1183- ]
1184- if not pending :
1185- return
1186- lines = ["**Findings that need more evidence**" , "" ]
1187- for target , finding in pending :
1188- location = f"{ finding .source_path } :{ finding .line } "
1189- lines .extend ([
1190- f"**{ _text (target )} — { _text (location , None )} **" , "" ,
1191- f"Candidate: { _text (finding .candidate_id , None )} " , "" ,
1192- _text (finding .reason , None ), "" ,
1193- * (f"- Proof gap: { _text (gap , None )} " for gap in finding .proof_gaps ), "" ,
1194- ])
1176+ if scrollable :
1177+ label = html .escape (f"{ title } review results" , quote = True )
1178+ opening = (f'<div role="region" aria-label="{ label } " tabindex="0" '
1179+ 'style="overflow-x: auto;">' )
1180+ lines = [* lines [:2 ], opening , "" , * lines [2 :], * body , "" , "</div>" ]
1181+ else :
1182+ lines .extend (body )
11951183 display (Markdown ("\n " .join (lines )))
11961184
1197- def result_rows (target : str , bundle : Any ):
1198- label = f"{ target } / { ', ' .join (bundle .selected_scanners ) or 'none' } "
1185+ def _finding_label (finding : Any ) -> str :
1186+ words = finding .category .split ("_" )
1187+ category = " " .join (word .upper () if word in {"sql" , "jwt" , "tls" } else word for word in words )
1188+ return f"{ finding .priority } / { category } "
1189+
1190+ def _source_link (snapshot : SourceSnapshot , finding : Any ) -> Markdown :
1191+ path = _safe_path (finding .source_path )
1192+ source = snapshot .file (path )
1193+ repository = snapshot .source_url .rstrip ("/" )
1194+ revision = snapshot .source_revision
1195+ if (not re .fullmatch (r"https://github\.com/[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+" , repository )
1196+ or not re .fullmatch (r"[0-9a-f]{40}" , revision )
1197+ or finding .source_revision != revision or finding .source_sha256 != source .sha256
1198+ or type (finding .line ) is not int or finding .line < 1 ):
1199+ raise EvidenceError ("Display link does not match a pinned GitHub source record." )
1200+ file = PurePosixPath (path )
1201+ label = "/" .join (file .parts [- 2 :])
1202+ if len (label ) > 28 :
1203+ label = file .name
1204+ if len (label ) > 28 :
1205+ label = f"{ file .stem [:27 - len (file .suffix )]} …{ file .suffix } "
1206+ url = f"{ repository } /blob/{ revision } /{ quote (path , safe = '/' )} #L{ finding .line } "
1207+ return Markdown (f"[{ _text (label , None )} :{ finding .line } ]({ url } )" )
1208+
1209+ def show_review_details (reviews : Mapping [str , Any ]):
1210+ lines = ["**Decision notes**" , "" ]
1211+ for target , bundle in reviews .items ():
1212+ repository = bundle .snapshot .source_url .rstrip ("/" ).rsplit ("/" , 1 )[- 1 ] if bundle .snapshot else target
1213+ for finding in (bundle .provenance .findings if bundle .provenance else ()):
1214+ location = _source_link (bundle .snapshot , finding ).data
1215+ lines .extend ([
1216+ f"**{ _text (repository )} / { _text (_finding_label (finding ))} — { location } **" , "" ,
1217+ f"Candidate: { _text (finding .candidate_id , None )} · { _text (finding .disposition )} " , "" ,
1218+ _text (finding .reason , None ), "" ,
1219+ * (f"- Proof gap: { _text (gap , None )} " for gap in finding .proof_gaps ), "" ,
1220+ ])
1221+ if not bundle .provenance and bundle .error :
1222+ lines .extend ([f"**{ _text (target )} — { _text (bundle .status )} **" , "" ,
1223+ _text (bundle .error , None ), "" ])
1224+ if len (lines ) > 2 :
1225+ display (Markdown ("\n " .join (lines )))
1226+
1227+ def result_rows (bundle : Any ):
11991228 role_by_candidate = {
12001229 assessment .candidate_id : review .role
12011230 for review in bundle .specialist_reviews
@@ -1209,20 +1238,15 @@ def result_rows(target: str, bundle: Any):
12091238 for candidate in bundle .candidates :
12101239 model_finding = proposed .get (candidate .candidate_id )
12111240 final_finding = final .get (candidate .candidate_id )
1212- reason = getattr (final_finding , "reason" , None ) or getattr (model_finding , "reason" , "not reviewed" )
1213- gaps = getattr (final_finding , "proof_gaps" , ()) or getattr (model_finding , "proof_gaps" , ())
12141241 rows .append ({
1215- "target / scanners" : label ,
1216- "signal" : f"{ candidate .priority } / { candidate .category } " ,
1217- "source" : f"{ candidate .source_path } :{ candidate .line } " ,
1242+ "finding" : _finding_label (candidate ),
1243+ "source" : _source_link (bundle .snapshot , candidate ),
12181244 "specialist" : role_by_candidate .get (candidate .candidate_id , "not run" ),
12191245 "validator" : getattr (model_finding , "disposition" , "not run" ),
12201246 "final" : getattr (final_finding , "disposition" , "not adjudicated" ),
1221- "reason or gap" : f"{ reason } Proof gap: { ', ' .join (gaps )} " if gaps else reason ,
12221247 })
12231248 if not rows :
1224- rows .append ({"target / scanners" : label , "signal " : bundle .status ,
1249+ rows .append ({"finding " : bundle .status ,
12251250 "source" : "none" , "specialist" : "not run" , "validator" : "not run" ,
1226- "final" : "not adjudicated" ,
1227- "reason or gap" : bundle .error or "No candidates were reviewed." })
1251+ "final" : "not adjudicated" })
12281252 return rows
0 commit comments