Skip to content

Commit

Permalink
Refactor and Enhance Docker Scout Integration (#322)
Browse files Browse the repository at this point in the history
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Ryan Mast <[email protected]>
  • Loading branch information
3 people authored Jan 15, 2025
1 parent 2cc7586 commit e25c99c
Showing 1 changed file with 48 additions and 28 deletions.
76 changes: 48 additions & 28 deletions surfactant/infoextractors/docker_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,59 +6,79 @@
import json
import subprocess
import tempfile
from typing import Optional

from loguru import logger

import surfactant.plugin
from surfactant.sbomtypes import SBOM, Software


def is_docker_scout_installed():
# Check that Docker Scout can be run
try:
result = subprocess.run(["docker", "scout"], capture_output=True, check=False)
if result.returncode != 0:
class DockerScoutManager:
def __init__(self) -> None:
self.disable_docker_scout = True
self.docker_scout_installed = False

def check_docker_scout_installed(self) -> None:
"""Check if Docker Scout is installed and update the state accordingly."""
try:
result = subprocess.run(["docker", "scout"], capture_output=True, check=False)
self.docker_scout_installed = result.returncode == 0
except FileNotFoundError:
self.docker_scout_installed = False

self.disable_docker_scout = not self.docker_scout_installed
if not self.docker_scout_installed:
logger.warning("Install Docker Scout to scan containers for additional information")
return False
return True
except FileNotFoundError:
return False

def run_docker_scout(self, filename: str) -> object:
"""Run Docker Scout on the given file and return the results."""
try:
result = subprocess.run(
["docker", "scout", "sbom", "--format", "spdx", f"fs://{filename}"],
capture_output=True,
check=False,
)
if result.returncode != 0:
logger.warning(f"Running Docker Scout on {filename} failed")
return {}
spdx_out = json.loads(result.stdout)
return {"dockerSPDX": spdx_out}
except json.JSONDecodeError as e:
logger.error(f"Failed to parse Docker Scout output for {filename}: {e}")
return {}


# Check if Docker Scout is installed when this Python module gets loaded
disable_docker_scout = not is_docker_scout_installed()
# Initialize DockerScoutManager to check installation status
dsManager = DockerScoutManager()


def supports_file(filetype: str) -> bool:
"""Check if the file type is supported."""
return filetype in ("DOCKER_TAR", "DOCKER_GZIP")


@surfactant.plugin.hookimpl
def extract_file_info(sbom: SBOM, software: Software, filename: str, filetype: str) -> object:
if disable_docker_scout or not supports_file(filetype):
"""Extract file information using Docker Scout if supported."""
if dsManager.disable_docker_scout or not supports_file(filetype):
return None
return extract_docker_info(filetype, filename)


def extract_docker_info(filetype: str, filename: str) -> object:
"""Extract Docker information based on file type."""
if filetype == "DOCKER_GZIP":
with open(filename, "rb") as gzip_in:
gzip_data = gzip_in.read()
with tempfile.NamedTemporaryFile() as gzip_out:
gzip_out.write(gzip.decompress(gzip_data))
return run_docker_scout(gzip_out.name)
return run_docker_scout(filename)


# Function that extract_docker_info delegates to to actually run Docker scout
def run_docker_scout(filename: str) -> object:
result = subprocess.run(
["docker", "scout", "sbom", "--format", "spdx", f"fs://{filename}"],
capture_output=True,
check=False,
)
if result.returncode != 0:
logger.warning(f"Running Docker Scout on {filename} failed")
return {}
spdx_out = json.loads(result.stdout)
return {"dockerSPDX": spdx_out}
gzip_out.flush() # Ensure data is written before reading
return dsManager.run_docker_scout(gzip_out.name)
return dsManager.run_docker_scout(filename)


@surfactant.plugin.hookimpl
def init_hook(command_name: Optional[str] = None) -> None:
if command_name != "update-db":
dsManager.check_docker_scout_installed()

0 comments on commit e25c99c

Please sign in to comment.