@@ -160,38 +160,45 @@ def get_latest_github_release(owner: str, repo: str) -> str | None:
160160 return None
161161
162162
163+ _STACKABLECTL_SBOMS = [
164+ "stackablectl-x86_64-unknown-linux-gnu.cdx.xml" ,
165+ "stackablectl-aarch64-unknown-linux-gnu.cdx.xml" ,
166+ ]
167+
168+
163169def scan_stackablectl (secobserve_api_token : str ) -> None :
164- """Download and scan the latest stackablectl binary from GitHub releases.
170+ """Download and scan the latest stackablectl SBOMs from GitHub releases.
165171
166- Uses rootfs mode for both Trivy and Grype, which supports scanning standalone
167- binaries for embedded dependency information. Once the project publishes a
168- CycloneDX SBOM, this should be replaced with SBOM-based scanning .
172+ The stackable-cockpit project publishes CycloneDX SBOMs alongside each
173+ binary. We download the SBOM files and scan them with Trivy and Grype in
174+ SBOM mode .
169175 """
170176 version = get_latest_github_release ("stackabletech" , "stackable-cockpit" )
171177 if version is None :
172178 print ("WARNING: Could not determine latest stackablectl version, skipping." )
173179 return
174180
175181 print (f"Scanning stackablectl { version } " )
176- binary_name = "stackablectl-x86_64-unknown-linux-gnu"
177- download_url = (
178- f"https://github.com/stackabletech/stackable-cockpit/releases/download"
179- f"/{ version } /{ binary_name } "
180- )
181- binary_path = f"/tmp/stackable/{ binary_name } "
182182
183- request = urllib .request .Request (download_url )
184- request .add_header ("User-Agent" , "stack-scanner" )
185- try :
186- with urllib .request .urlopen (request ) as response :
187- with open (binary_path , "wb" ) as f :
188- f .write (response .read ())
189- print (f"Downloaded stackablectl binary to { binary_path } " )
190- except urllib .error .URLError as error :
191- print (f"Failed to download stackablectl binary: { error } " )
192- return
183+ for sbom_name in _STACKABLECTL_SBOMS :
184+ download_url = (
185+ f"https://github.com/stackabletech/stackable-cockpit/releases/download"
186+ f"/{ version } /{ sbom_name } "
187+ )
188+ sbom_path = f"/tmp/stackable/{ sbom_name } "
189+
190+ request = urllib .request .Request (download_url )
191+ request .add_header ("User-Agent" , "stack-scanner" )
192+ try :
193+ with urllib .request .urlopen (request ) as response :
194+ with open (sbom_path , "wb" ) as f :
195+ f .write (response .read ())
196+ print (f"Downloaded SBOM to { sbom_path } " )
197+ except urllib .error .URLError as error :
198+ print (f"Failed to download SBOM { sbom_name } : { error } " )
199+ continue
193200
194- scan_binary (secobserve_api_token , binary_name , "stackablectl" , version )
201+ scan_sbom (secobserve_api_token , sbom_name , "stackablectl" , version )
195202
196203
197204def _build_base_env (secobserve_api_token : str , product_name : str , branch_name : str ) -> dict :
@@ -220,42 +227,30 @@ def _build_scanner_cmd(entrypoint: str, env: dict) -> list[str]:
220227 return cmd
221228
222229
223- def scan_binary (
230+ def scan_sbom (
224231 secobserve_api_token : str ,
225232 file_name : str ,
226233 product_name : str ,
227234 branch_name : str ,
228235) -> None :
229- """Scan a local binary file using Trivy (filesystem) and Grype (sbom) .
236+ """Scan a local SBOM file using Trivy and Grype in SBOM mode .
230237
231238 The file must reside under /tmp/stackable/ so it is accessible inside the
232239 scanner container (which mounts that directory to /tmp).
233-
234- Trivy runs in filesystem mode against the binary and writes a CycloneDX
235- report to /tmp/trivy.json. Grype then uses that report as SBOM input,
236- since there is no Grype filesystem entrypoint.
237240 """
238- # Run Trivy in filesystem mode. RUN_DIRECTORY is required by the entrypoint
239- # (it cd's there before running trivy) and also becomes the implicit WORKSPACE
240- # when neither GITHUB_WORKSPACE nor CI_PROJECT_DIR is set, so the report ends
241- # up at /tmp/trivy.json inside the container (= /tmp/stackable/trivy.json on host).
242241 trivy_env = _build_base_env (secobserve_api_token , product_name , branch_name )
243242 trivy_env ["TARGET" ] = f"/tmp/{ file_name } "
244- trivy_env ["RUN_DIRECTORY" ] = "/tmp"
245243
246- cmd = _build_scanner_cmd ("/entrypoints/entrypoint_trivy_filesystem .sh" , trivy_env )
244+ cmd = _build_scanner_cmd ("/entrypoints/entrypoint_trivy_sbom .sh" , trivy_env )
247245 print (" " .join (cmd ))
248246 subprocess .run (cmd )
249247
250- # Run Grype in sbom mode, using Trivy's CycloneDX output as input.
251248 grype_env = {
252249 ** trivy_env ,
253- "TARGET" : "/tmp/trivy.json" ,
254250 "FURTHER_PARAMETERS" : "--by-cve" ,
255251 "GRYPE_DB_CACHE_DIR" : "/tmp/grype_db_cache" ,
256252 "REPORT_NAME" : "grype.json" ,
257253 }
258- del grype_env ["RUN_DIRECTORY" ]
259254 cmd = _build_scanner_cmd ("/entrypoints/entrypoint_grype_sbom.sh" , grype_env )
260255 subprocess .run (cmd )
261256
0 commit comments