1+ """
2+ Validate OpenDoor GitHub Actions E2E reports against v5.15.2 report shape.
3+ """
4+
5+ import json
6+ import sys
7+ from pathlib import Path
8+
9+
10+ TARGET = "127.0.0.1"
11+ REPORTS_DIR = Path ("./reports" ) / TARGET
12+
13+ EXPECTED_SUCCESS_PATHS = {
14+ "/admin" ,
15+ "/backup" ,
16+ "/health" ,
17+ "/uploads" ,
18+ "/login" ,
19+ }
20+
21+ EXPECTED_IGNORED_404_PATHS = {
22+ "/nonexistent" ,
23+ "/ghost" ,
24+ "/random-miss" ,
25+ "/doesnotexist" ,
26+ }
27+
28+ passed = 0
29+ failed = 0
30+
31+
32+ def assert_true (condition : bool , message : str ) -> None :
33+ global passed , failed
34+
35+ if condition :
36+ print (f"✅ { message } " )
37+ passed += 1
38+ return
39+
40+ print (f"❌ { message } " , file = sys .stderr )
41+ failed += 1
42+
43+
44+ def load_json (path : Path ) -> dict :
45+ with path .open ("r" , encoding = "utf-8" ) as handler :
46+ return json .load (handler )
47+
48+
49+ def item_urls (report : dict , bucket : str ) -> list [str ]:
50+ details = report .get ("report_items" , {}).get (bucket )
51+
52+ if isinstance (details , list ):
53+ return [
54+ str (item .get ("url" , "" ))
55+ for item in details
56+ if isinstance (item , dict )
57+ ]
58+
59+ return [
60+ str (item )
61+ for item in report .get ("items" , {}).get (bucket , [])
62+ ]
63+
64+
65+ def item_details (report : dict , bucket : str ) -> list [dict ]:
66+ details = report .get ("report_items" , {}).get (bucket )
67+
68+ if isinstance (details , list ):
69+ return [
70+ item
71+ for item in details
72+ if isinstance (item , dict )
73+ ]
74+
75+ return [
76+ {"url" : str (item ), "code" : "-" }
77+ for item in report .get ("items" , {}).get (bucket , [])
78+ ]
79+
80+
81+ def has_path (urls : list [str ], path : str ) -> bool :
82+ return any (url .endswith (path ) or path in url for url in urls )
83+
84+
85+ def validate_json_report () -> None :
86+ report = load_json (REPORTS_DIR / f"{ TARGET } .json" )
87+ total = report .get ("total" , {})
88+
89+ assert_true (total .get ("success" ) == 5 , "JSON: success bucket has exactly 5 hits" )
90+ assert_true (total .get ("forbidden" ) == 1 , "JSON: forbidden bucket has exactly 1 hit" )
91+ assert_true (total .get ("auth" ) == 1 , "JSON: auth bucket has exactly 1 hit" )
92+ assert_true (total .get ("redirect" ) == 1 , "JSON: redirect bucket has exactly 1 hit" )
93+ assert_true (total .get ("ignored" ) == 4 , "JSON: ignored bucket has exactly 4 filtered misses" )
94+
95+ success_urls = item_urls (report , "success" )
96+
97+ for path in sorted (EXPECTED_SUCCESS_PATHS ):
98+ assert_true (has_path (success_urls , path ), f"JSON: { path } is in success bucket" )
99+
100+ assert_true (
101+ has_path (item_urls (report , "forbidden" ), "/forbidden" ),
102+ "JSON: /forbidden is in forbidden bucket" ,
103+ )
104+
105+ assert_true (
106+ has_path (item_urls (report , "auth" ), "/auth-required" ),
107+ "JSON: /auth-required is in auth bucket" ,
108+ )
109+
110+ ignored_items = item_details (report , "ignored" )
111+
112+ for path in sorted (EXPECTED_IGNORED_404_PATHS ):
113+ assert_true (
114+ any (
115+ has_path ([str (item .get ("url" , "" ))], path )
116+ and str (item .get ("code" )) == "404"
117+ for item in ignored_items
118+ ),
119+ f"JSON: { path } is preserved as ignored 404" ,
120+ )
121+
122+ active_buckets = ("success" , "forbidden" , "auth" , "redirect" )
123+ active_urls = [
124+ url
125+ for bucket in active_buckets
126+ for url in item_urls (report , bucket )
127+ ]
128+
129+ for path in sorted (EXPECTED_IGNORED_404_PATHS ):
130+ assert_true (
131+ not has_path (active_urls , path ),
132+ f"JSON: { path } is not in active finding buckets" ,
133+ )
134+
135+
136+ def validate_sarif_report () -> None :
137+ sarif = load_json (REPORTS_DIR / f"{ TARGET } .sarif" )
138+
139+ runs = sarif .get ("runs" , [])
140+ run = runs [0 ] if runs else {}
141+ results = run .get ("results" , [])
142+
143+ assert_true (sarif .get ("version" ) == "2.1.0" , "SARIF: version is 2.1.0" )
144+ assert_true (
145+ run .get ("tool" , {}).get ("driver" , {}).get ("name" ) == "OpenDoor" ,
146+ "SARIF: tool is OpenDoor" ,
147+ )
148+
149+ def result_matches (rule_id : str , path : str | None = None , code : int | None = None ) -> bool :
150+ for result in results :
151+ if result .get ("ruleId" ) != rule_id :
152+ continue
153+
154+ props = result .get ("properties" , {})
155+ uri = (
156+ result .get ("locations" , [{}])[0 ]
157+ .get ("physicalLocation" , {})
158+ .get ("artifactLocation" , {})
159+ .get ("uri" , "" )
160+ )
161+
162+ if path is not None and path not in str (uri ) and path not in str (props .get ("url" , "" )):
163+ continue
164+
165+ if code is not None and props .get ("statusCode" ) != code :
166+ continue
167+
168+ return True
169+
170+ return False
171+
172+ for path in sorted (EXPECTED_SUCCESS_PATHS ):
173+ assert_true (
174+ result_matches ("opendoor.finding.success" , path , 200 ),
175+ f"SARIF: { path } is success/200" ,
176+ )
177+
178+ assert_true (
179+ result_matches ("opendoor.finding.forbidden" , "/forbidden" , 403 ),
180+ "SARIF: /forbidden is forbidden/403" ,
181+ )
182+
183+ assert_true (
184+ result_matches ("opendoor.finding.auth" , "/auth-required" , 401 ),
185+ "SARIF: /auth-required is auth/401" ,
186+ )
187+
188+ assert_true (
189+ result_matches ("opendoor.finding.redirect" , None , 301 ),
190+ "SARIF: redirect bucket has status 301" ,
191+ )
192+
193+ for path in sorted (EXPECTED_IGNORED_404_PATHS ):
194+ assert_true (
195+ result_matches ("opendoor.finding.ignored" , path , 404 ),
196+ f"SARIF: { path } is ignored/404" ,
197+ )
198+
199+
200+ def main () -> int :
201+ try :
202+ validate_json_report ()
203+ validate_sarif_report ()
204+ except Exception as error :
205+ print (f"💥 Validation error: { error } " , file = sys .stderr )
206+ return 1
207+
208+ print (f"\n Results: { passed } passed, { failed } failed" )
209+ return 1 if failed else 0
210+
211+
212+ if __name__ == "__main__" :
213+ raise SystemExit (main ())
0 commit comments