@@ -103,6 +103,64 @@ def default(self, obj):
103103)
104104
105105
106+ def resolve_dependencies (
107+ repo_path : Path , repo_branch : str
108+ ) -> tuple [dict [str , Dependency ], list [str ]]:
109+ """Return the dependencies that can be parsed from the checked-out repo.
110+
111+ Known branches still use their curated dependency list. Unknown branches fall
112+ back to probing every tracked dependency definition and keeping only the ones
113+ whose version parser succeeds against the checkout.
114+ """
115+
116+ configured_names = dependencies_per_branch .get (repo_branch )
117+ if configured_names is None :
118+ candidate_dependencies = dependencies_info
119+ print (
120+ f"Info: '{ repo_branch } ' is not explicitly configured; scanning all known dependency definitions"
121+ )
122+ else :
123+ candidate_dependencies = {
124+ name : dep
125+ for name , dep in dependencies_info .items ()
126+ if name in configured_names
127+ }
128+
129+ available_dependencies : dict [str , Dependency ] = {}
130+ skipped_dependencies : list [str ] = []
131+
132+ for name , dep in candidate_dependencies .items ():
133+ try :
134+ dep .version_parser (repo_path )
135+ except (
136+ FileNotFoundError ,
137+ RuntimeError ,
138+ ValueError ,
139+ KeyError ,
140+ IndexError ,
141+ AttributeError ,
142+ TypeError ,
143+ OSError ,
144+ ) as exc :
145+ skipped_dependencies .append (f"{ name } : { exc } " )
146+ continue
147+ available_dependencies [name ] = dep
148+
149+ if not available_dependencies :
150+ raise RuntimeError (
151+ f"No supported dependencies could be resolved from '{ repo_branch } '"
152+ )
153+
154+ if skipped_dependencies :
155+ print (
156+ f"Info: Skipping { len (skipped_dependencies )} dependencies that are not present or not parseable in '{ repo_branch } '"
157+ )
158+ for skipped in skipped_dependencies :
159+ print (f" - { skipped } " )
160+
161+ return available_dependencies , skipped_dependencies
162+
163+
106164def query_ghad (
107165 dependencies : dict [str , Dependency ], gh_token : str , repo_path : Path
108166) -> list [Vulnerability ]:
@@ -237,7 +295,10 @@ def main() -> int:
237295 parser .add_argument (
238296 "node_repo_branch" ,
239297 metavar = "NODE_REPO_BRANCH" ,
240- help = f"the current branch of the Node repository (supports { supported_branches } )" ,
298+ help = (
299+ "the current branch of the Node/N|Solid repository; known branches use "
300+ f"curated dependency lists, other branches are scanned by probing available dependencies ({ supported_branches } )"
301+ ),
241302 )
242303 parser .add_argument (
243304 "--gh-token" ,
@@ -281,10 +342,6 @@ def main() -> int:
281342 raise RuntimeError (
282343 "Invalid argument: '{repo_path}' is not a valid Node git repository"
283344 )
284- if repo_branch not in dependencies_per_branch :
285- raise RuntimeError (
286- f"Invalid argument: '{ repo_branch } ' is not a supported branch. Please use one of: { supported_branches } "
287- )
288345 if gh_token is None :
289346 print (
290347 "Warning: GitHub authentication token not provided, skipping GitHub Advisory Database queries" ,
@@ -296,15 +353,17 @@ def main() -> int:
296353 file = sys .stderr ,
297354 )
298355
299- dependencies = {
300- name : dep
301- for name , dep in dependencies_info .items ()
302- if name in dependencies_per_branch [repo_branch ]
303- }
356+ dependencies , skipped_dependencies = resolve_dependencies (repo_path , repo_branch )
304357
305358 # Track whether every vulnerability source completed successfully. A partial scan must not
306359 # cause the reconciler to close issues for vulns that simply weren't queried this run.
307360 scan_complete = True
361+ if repo_branch in dependencies_per_branch and skipped_dependencies :
362+ scan_complete = False
363+ print (
364+ f"Warning: { len (skipped_dependencies )} curated dependencies could not be resolved for '{ repo_branch } '" ,
365+ file = sys .stderr ,
366+ )
308367
309368 ghad_vulnerabilities : list [Vulnerability ] = []
310369 if gh_token is not None :
0 commit comments