Skip to content

Commit af92903

Browse files
committed
feat: scan stackablectl
1 parent 68f6fcc commit af92903

1 file changed

Lines changed: 115 additions & 0 deletions

File tree

stack_scanner/main.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,118 @@ def get_harbor_recent_tags(project: str, repository: str) -> list[str] | None:
108108
return tags
109109

110110

111+
def get_latest_github_release(owner: str, repo: str) -> str | None:
112+
"""Fetch the tag name of the latest GitHub release for a repository."""
113+
url = f"https://api.github.com/repos/{owner}/{repo}/releases/latest"
114+
request = urllib.request.Request(url)
115+
request.add_header("Accept", "application/vnd.github+json")
116+
request.add_header("User-Agent", "stack-scanner")
117+
118+
try:
119+
with urllib.request.urlopen(request) as response:
120+
data = json.loads(response.read().decode())
121+
return data["tag_name"]
122+
except (urllib.error.URLError, json.JSONDecodeError, KeyError) as error:
123+
print(f"Failed to fetch latest {owner}/{repo} release: {error}")
124+
return None
125+
126+
127+
def scan_stackablectl(secobserve_api_token: str) -> None:
128+
"""Download and scan the latest stackablectl binary from GitHub releases.
129+
130+
Uses rootfs mode for both Trivy and Grype, which supports scanning standalone
131+
binaries for embedded dependency information. Once the project publishes a
132+
CycloneDX SBOM, this should be replaced with SBOM-based scanning.
133+
"""
134+
version = get_latest_github_release("stackabletech", "stackable-cockpit")
135+
if version is None:
136+
print("WARNING: Could not determine latest stackablectl version, skipping.")
137+
return
138+
139+
print(f"Scanning stackablectl {version}")
140+
binary_name = "stackablectl-x86_64-unknown-linux-gnu"
141+
download_url = (
142+
f"https://github.com/stackabletech/stackable-cockpit/releases/download"
143+
f"/{version}/{binary_name}"
144+
)
145+
binary_path = f"/tmp/stackable/{binary_name}"
146+
147+
request = urllib.request.Request(download_url)
148+
request.add_header("User-Agent", "stack-scanner")
149+
try:
150+
with urllib.request.urlopen(request) as response:
151+
with open(binary_path, "wb") as f:
152+
f.write(response.read())
153+
print(f"Downloaded stackablectl binary to {binary_path}")
154+
except urllib.error.URLError as error:
155+
print(f"Failed to download stackablectl binary: {error}")
156+
return
157+
158+
scan_binary(secobserve_api_token, binary_name, "stackablectl", version)
159+
160+
161+
def scan_binary(
162+
secobserve_api_token: str,
163+
file_name: str,
164+
product_name: str,
165+
branch_name: str,
166+
) -> None:
167+
"""Scan a local binary file using Trivy and Grype in rootfs mode.
168+
169+
The file must reside under /tmp/stackable/ so it is accessible inside the
170+
scanner container (which mounts that directory to /tmp).
171+
"""
172+
# Run Trivy
173+
env = {}
174+
env["TARGET"] = f"/tmp/{file_name}"
175+
env["SO_UPLOAD"] = "true"
176+
env["SO_PRODUCT_NAME"] = product_name
177+
env["SO_API_BASE_URL"] = "https://secobserve-backend.stackable.tech"
178+
env["SO_API_TOKEN"] = secobserve_api_token
179+
env["SO_BRANCH_NAME"] = branch_name
180+
env["TMPDIR"] = "/tmp/trivy_tmp"
181+
env["TRIVY_CACHE_DIR"] = "/tmp/trivy_cache"
182+
env["REPORT_NAME"] = "trivy.json"
183+
184+
cmd = [
185+
"docker",
186+
"run",
187+
"--entrypoint",
188+
"/entrypoints/entrypoint_trivy_rootfs.sh",
189+
"-v",
190+
"/tmp/stackable:/tmp",
191+
"-v",
192+
"/var/run/docker.sock:/var/run/docker.sock",
193+
]
194+
for key, value in env.items():
195+
cmd.extend(["-e", f"{key}={value}"])
196+
cmd.append("oci.stackable.tech/sandbox/secobserve-scanners:latest")
197+
198+
print(" ".join(cmd))
199+
subprocess.run(cmd)
200+
201+
# Run Grype
202+
env["FURTHER_PARAMETERS"] = "--by-cve"
203+
env["GRYPE_DB_CACHE_DIR"] = "/tmp/grype_db_cache"
204+
env["REPORT_NAME"] = "grype.json"
205+
206+
cmd = [
207+
"docker",
208+
"run",
209+
"--entrypoint",
210+
"/entrypoints/entrypoint_grype_rootfs.sh",
211+
"-v",
212+
"/tmp/stackable:/tmp",
213+
"-v",
214+
"/var/run/docker.sock:/var/run/docker.sock",
215+
]
216+
for key, value in env.items():
217+
cmd.extend(["-e", f"{key}={value}"])
218+
cmd.append("oci.stackable.tech/sandbox/secobserve-scanners:latest")
219+
220+
subprocess.run(cmd)
221+
222+
111223
def scan_additional_images(secobserve_api_token: str) -> None:
112224
"""Scan additional images that are not part of the regular versioned Stackable release.
113225
@@ -260,6 +372,9 @@ def main():
260372
# already or are arch-agnostic manifests.
261373
scan_additional_images(secobserve_api_token)
262374

375+
# Scan the latest stackablectl binary from GitHub releases.
376+
scan_stackablectl(secobserve_api_token)
377+
263378

264379
def scan_image(
265380
secobserve_api_token: str,

0 commit comments

Comments
 (0)