Skip to content

Commit e8555d7

Browse files
committed
feat: scan stackablectl SBOM
1 parent 22fbe94 commit e8555d7

File tree

1 file changed

+31
-36
lines changed

1 file changed

+31
-36
lines changed

stack_scanner/main.py

Lines changed: 31 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
163169
def 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

197204
def _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

Comments
 (0)