Skip to content

Commit 6020d89

Browse files
Ishaanj18Ishaan Jain
andauthored
Add filesystem SBOM support for container scans (#42)
* Add filesystem SBOM support for container scans * added trivy removal and correct workdir mapping * fix to address the Ubuntu 22.04 glibc mismatch for the PyInstaller native binary --------- Co-authored-by: Ishaan Jain <ishaanj18@Ishaans-MacBook-Air.local>
1 parent 3e42e7d commit 6020d89

7 files changed

Lines changed: 305 additions & 18 deletions

File tree

.github/workflows/release.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ jobs:
4444
strategy:
4545
matrix:
4646
include:
47-
- os: ubuntu-latest
47+
# Build on the oldest common target to avoid glibc mismatches on customer hosts.
48+
# Customers reported GLIBC_2.38 is required by the binary, but Ubuntu 22.04 ships an older glibc.
49+
- os: ubuntu-22.04
4850
name: linux
4951
suffix: ''
5052
ext: ''
@@ -97,15 +99,15 @@ jobs:
9799
dist/accuknox-aspm-scanner${{ matrix.ext }}
98100
99101
- name: Upload Ubuntu Native Binary Artifact
100-
if: matrix.os == 'ubuntu-latest'
102+
if: matrix.os == 'ubuntu-22.04'
101103
uses: actions/upload-artifact@v4
102104
with:
103105
name: native-binary-linux
104106
retention-days: 1
105107
path: dist/accuknox-aspm-scanner
106108

107109
- name: Flag Ubuntu completion
108-
if: matrix.os == 'ubuntu-latest'
110+
if: matrix.os == 'ubuntu-22.04'
109111
id: set-output-ubuntu
110112
run: echo "ready=true" >> $GITHUB_OUTPUT
111113

aspm_cli/commands/scan_command.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from aspm_cli.utils.config import ConfigValidator
1111
from aspm_cli.utils.common import upload_results, handle_failure, ALLOWED_SCAN_TYPES
1212
from aspm_cli.utils.git_info import GitInfo
13+
from aspm_cli.utils.sbom import derive_sbom_classifier, enrich_sbom_payload, resolve_project_name
1314

1415
class ScanCommand(BaseCommand):
1516
help_text = f"Run a security scan (e.g. {', '.join(ALLOWED_SCAN_TYPES)})"
@@ -48,8 +49,7 @@ def execute(self, args):
4849
"accuknox_label": args.label or os.getenv("ACCUKNOX_LABEL"),
4950
"accuknox_token": args.token or os.getenv("ACCUKNOX_TOKEN"),
5051
"accuknox_tenant": args.tenant or os.getenv("ACCUKNOX_TENANT"),
51-
"accuknox_project_name": args.project_name
52-
or os.getenv("ACCUKNOX_PROJECT"),
52+
"accuknox_project_name": resolve_project_name(args.project_name),
5353
}
5454

5555
# Get the correct scanner strategy from the registry
@@ -85,20 +85,25 @@ def execute(self, args):
8585
# If this is an SBOM upload, enrich the SBOM file with project_name and classifier
8686
if is_sbom_upload:
8787
project_name = accuknox_config.get("accuknox_project_name")
88+
scan_command = getattr(args, "command", "") or ""
89+
project_classifier = derive_sbom_classifier(scan_command)
8890
try:
8991
with open(result_file, "r", encoding="utf-8") as f:
9092
data = json.load(f)
9193

9294
if isinstance(data, dict):
93-
if project_name:
94-
data["project_name"] = project_name
95-
data["project_classifier"] = "container"
95+
enrich_sbom_payload(
96+
data,
97+
scan_command,
98+
project_name,
99+
project_classifier,
100+
)
96101

97102
with open(result_file, "w", encoding="utf-8") as f:
98103
json.dump(data, f, indent=2)
99104
except Exception as e:
100105
Logger.get_logger().debug(
101-
f"Failed to enrich SBOM results.json with project_name/classifier: {e}"
106+
f"Failed to enrich SBOM results.json: {e}"
102107
)
103108

104109
# Upload if not skipping

aspm_cli/scan/container.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import subprocess
22
import json
33
import os
4+
import re
45
import shlex
56
from aspm_cli.tool.manager import ToolManager
67
from aspm_cli.utils.logger import Logger
78
from aspm_cli.utils import docker_pull
89
from aspm_cli.utils import config
10+
from aspm_cli.utils.sbom import normalize_sbom_args_for_docker
911
from colorama import Fore
1012

1113
class ContainerScanner:
@@ -25,17 +27,32 @@ def run(self):
2527
severity_threshold, sanitized_args = self._build_container_scan_args()
2628
scan_cmd = self._build_scan_command(sanitized_args)
2729

28-
Logger.get_logger().debug(f"Scanning container image: {' '.join(scan_cmd)}")
30+
log_msg = (
31+
"Running container SBOM scan"
32+
if self.generate_sbom
33+
else "Scanning container image"
34+
)
35+
Logger.get_logger().debug(f"{log_msg}: {' '.join(scan_cmd)}")
2936
result = subprocess.run(scan_cmd, capture_output=True, text=True)
3037

3138
if result.stdout:
32-
sanitized_stdout = result.stdout.replace("trivy", "[scanner]")
39+
sanitized_stdout = re.sub(
40+
r"trivy|aquasecurity|aqua security",
41+
"[scanner]",
42+
result.stdout,
43+
flags=re.IGNORECASE,
44+
)
3345
Logger.get_logger().debug(sanitized_stdout)
3446
if("--help" in self.command):
3547
Logger.log_with_color('INFO', sanitized_stdout, Fore.WHITE)
3648
return config.PASS_RETURN_CODE, None
3749
if result.stderr:
38-
sanitized_stderr = result.stderr.replace("trivy", "[scanner]")
50+
sanitized_stderr = re.sub(
51+
r"trivy|aquasecurity|aqua security",
52+
"[scanner]",
53+
result.stderr,
54+
flags=re.IGNORECASE,
55+
)
3956
Logger.get_logger().error(sanitized_stderr)
4057

4158
if self.generate_sbom:
@@ -79,6 +96,10 @@ def _build_container_scan_args(self):
7996
i += 1
8097
# Force cyclonedx format and JSON output
8198
sanitized_args.extend(["-f", "cyclonedx", "-o", self.result_file])
99+
if self.container_mode:
100+
sanitized_args = normalize_sbom_args_for_docker(
101+
self.command or "", sanitized_args
102+
)
82103
return None, sanitized_args
83104

84105
# Flags that take a value and should be removed.

aspm_cli/scanners/container_scanner.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,18 @@
44
from aspm_cli.scan.container import ContainerScanner as OriginalContainerScanner
55

66
class ContainerScanner(BaseScanner):
7-
help_text = "Run a container image scan using Trivy"
7+
help_text = "Run a container image or filesystem SBOM scan"
88
data_type_identifier = "TR"
99

1010
def add_arguments(self, parser: argparse.ArgumentParser):
1111
parser.add_argument(
1212
"--command",
1313
type=str,
1414
required=True,
15-
help="Arguments to pass to the container scanner (e.g., 'image nginx:latest')"
15+
help=(
16+
"Scanner arguments (e.g. 'image nginx:latest' for image SBOM/vuln scan; "
17+
"'filesystem .' for repo SBOM with --generate-sbom)"
18+
)
1619
)
1720
parser.add_argument(
1821
"--container-mode",
@@ -26,7 +29,11 @@ def add_arguments(self, parser: argparse.ArgumentParser):
2629
)
2730

2831
def validate_config(self, args: argparse.Namespace, validator: ConfigValidator):
29-
validator.validate_container_scan(args.command, args.container_mode)
32+
validator.validate_container_scan(
33+
args.command,
34+
args.container_mode,
35+
generate_sbom=getattr(args, "generate_sbom", False),
36+
)
3037

3138
def run_scan(self, args: argparse.Namespace) -> tuple[int, str]:
3239
# Instantiate and run the original scanner logic

aspm_cli/utils/config.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from typing import Optional, Literal
44
from aspm_cli.utils.logger import Logger
55
from aspm_cli.utils.common import ALLOWED_SCAN_TYPES
6+
from aspm_cli.utils.sbom import validate_sbom_command
67

78
# Return code constants
89
PASS_RETURN_CODE = 0
@@ -159,19 +160,34 @@ class SecretScanConfig(BaseModel):
159160
Logger.get_logger().debug(f"Secret scan configuration error: {concise_msg}")
160161
raise ValueError(concise_msg)
161162

162-
def validate_container_scan(self, command: str, container_mode: bool):
163+
def validate_container_scan(
164+
self,
165+
command: str,
166+
container_mode: bool,
167+
generate_sbom: bool = False,
168+
):
163169
class ContainerScanConfig(BaseModel):
164170
command: str = Field(..., min_length=1, description="Command arguments for Container scanner")
165171
container_mode: bool
166172

167173
try:
168174
ContainerScanConfig(command=command, container_mode=container_mode)
169-
self._log_validation_success("Container")
170175
except ValidationError as e:
171176
concise_msg = _format_validation_error(e)
172177
Logger.get_logger().debug(f"Container scan configuration error: {concise_msg}")
173178
raise ValueError(concise_msg)
174179

180+
if generate_sbom:
181+
validate_sbom_command(command)
182+
if not self.skip_upload and not self.accuknox_config.accuknox_project_name:
183+
raise ValueError(
184+
"AccuKnox project name is required for SBOM uploads. "
185+
"Provide --project-name or set ACCUKNOX_PROJECT_NAME (or legacy ACCUKNOX_PROJECT), "
186+
"or use --skip-upload if you only need a local SBOM file."
187+
)
188+
189+
self._log_validation_success("Container")
190+
175191
def validate_sast_scan(self, command: str, container_mode: bool, severity: str, repo_url: Optional[str], commit_ref: Optional[str], commit_sha: Optional[str], pipeline_id: Optional[str], job_url: Optional[str]):
176192
class SASTScanConfig(BaseModel):
177193
command: str = Field(..., min_length=1, description="Command arguments for SAST scanner")

0 commit comments

Comments
 (0)