Skip to content

Commit 033a991

Browse files
author
Trong Nhan Mai
committed
chore: add --deps-depth
1 parent 42ac7f6 commit 033a991

File tree

17 files changed

+109
-54
lines changed

17 files changed

+109
-54
lines changed

src/macaron/__main__.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,24 @@ def analyze_slsa_levels_single(analyzer_single_args: argparse.Namespace) -> None
153153
+ "Dependency resolution is off by default. This flag will be removed soon."
154154
)
155155

156+
deps_depth = None
157+
if analyzer_single_args.deps_depth == "inf":
158+
deps_depth = -1
159+
else:
160+
try:
161+
deps_depth = int(analyzer_single_args.deps_depth)
162+
except ValueError:
163+
logger.error("Please provide '1', '0' or 'inf' to `--deps-depth`")
164+
sys.exit(os.EX_USAGE)
165+
166+
if deps_depth not in [-1, 0, 1]:
167+
logger.error("Please provide '1', '0' or 'inf' to `--deps-depth`")
168+
sys.exit(os.EX_USAGE)
169+
156170
status_code = analyzer.run(
157171
run_config,
158172
analyzer_single_args.sbom_path,
159-
analyzer_single_args.deps_resolution,
173+
deps_depth,
160174
provenance_payload=prov_payload,
161175
)
162176
sys.exit(status_code)
@@ -422,11 +436,13 @@ def main(argv: list[str] | None = None) -> None:
422436
)
423437

424438
single_analyze_parser.add_argument(
425-
"--deps-resolution",
439+
"--deps-depth",
426440
required=False,
427-
action="store_true",
428-
default=False,
429-
help=("Perform dependency resolution and analysis."),
441+
default="0",
442+
help=(
443+
"The depth of the dependency resolution. 0: disable, 1: direct dependencies, "
444+
+ "inf: all transitive dependencies. (Default: 0)"
445+
),
430446
)
431447

432448
single_analyze_parser.add_argument(

src/macaron/config/defaults.ini

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ dep_tool_maven = cyclonedx-maven:2.6.2
3939
dep_tool_gradle = cyclonedx-gradle:1.7.4
4040
# This is the timeout (in seconds) to run the dependency resolver.
4141
timeout = 2400
42-
recursive = False
4342
# Determines whether the CycloneDX BOM file should be validated or not.
4443
validate = True
4544
# The CycloneDX schema version used for validation.

src/macaron/dependency_analyzer/cyclonedx.py

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,9 @@ def __init__(self, resources_path: str, file_name: str, tool_name: str, tool_ver
138138
self.visited_deps: set = set()
139139

140140
@abstractmethod
141-
def collect_dependencies(self, dir_path: str, target_component: Component) -> dict[str, DependencyInfo]:
141+
def collect_dependencies(
142+
self, dir_path: str, target_component: Component, recursive: bool = False
143+
) -> dict[str, DependencyInfo]:
142144
"""Process the dependency JSON files and collect direct dependencies.
143145
144146
Parameters
@@ -147,6 +149,8 @@ def collect_dependencies(self, dir_path: str, target_component: Component) -> di
147149
Local path to the target repo.
148150
target_component: Component
149151
The analyzed target software component.
152+
recursive: bool
153+
Set to False to get the direct dependencies only (default).
150154
151155
Returns
152156
-------
@@ -349,14 +353,16 @@ def tool_valid(tool: str) -> bool:
349353
return True
350354

351355
@staticmethod
352-
def resolve_dependencies(main_ctx: Any, sbom_path: str) -> dict[str, DependencyInfo]:
356+
def resolve_dependencies(main_ctx: Any, sbom_path: str, recursive: bool = False) -> dict[str, DependencyInfo]:
353357
"""Resolve the dependencies of the main target repo.
354358
355359
Parameters
356360
----------
357361
main_ctx : Any (AnalyzeContext)
358362
The context of object of the target repository.
359-
sbom_path: str
363+
recursive : bool
364+
If True, perform transitive dependency resolution. Default: False.
365+
sbom_path : str
360366
The path to the SBOM.
361367
362368
Returns
@@ -397,7 +403,11 @@ def resolve_dependencies(main_ctx: Any, sbom_path: str) -> dict[str, DependencyI
397403
if sbom_path:
398404
logger.info("Getting the dependencies from the SBOM defined at %s.", sbom_path)
399405

400-
deps_resolved = dep_analyzer.get_deps_from_sbom(sbom_path, main_ctx.component)
406+
deps_resolved = dep_analyzer.get_deps_from_sbom(
407+
sbom_path,
408+
main_ctx.component,
409+
recursive=recursive,
410+
)
401411

402412
# Use repo finder to find more repositories to analyze.
403413
if defaults.getboolean("repofinder", "find_repos"):
@@ -456,7 +466,11 @@ def resolve_dependencies(main_ctx: Any, sbom_path: str) -> dict[str, DependencyI
456466

457467
# We collect the generated SBOM as a best effort, even if the build exits with errors.
458468
# TODO: add improvements to help the SBOM build succeed as much as possible.
459-
deps_resolved |= dep_analyzer.collect_dependencies(str(working_dir), main_ctx.component)
469+
deps_resolved |= dep_analyzer.collect_dependencies(
470+
str(working_dir),
471+
main_ctx.component,
472+
recursive=recursive,
473+
)
460474

461475
logger.info("Stored dependency resolver log for %s to %s.", dep_analyzer.tool_name, log_path)
462476

@@ -719,7 +733,9 @@ def convert_components_to_artifacts(
719733

720734
return latest_deps
721735

722-
def get_deps_from_sbom(self, sbom_path: str | Path, target_component: Component) -> dict[str, DependencyInfo]:
736+
def get_deps_from_sbom(
737+
self, sbom_path: str | Path, target_component: Component, recursive: bool = False
738+
) -> dict[str, DependencyInfo]:
723739
"""Get the dependencies from a provided SBOM.
724740
725741
Parameters
@@ -728,6 +744,8 @@ def get_deps_from_sbom(self, sbom_path: str | Path, target_component: Component)
728744
The path to the SBOM file.
729745
target_component: Component
730746
The analyzed target software component.
747+
recursive: bool
748+
Set to False to get the direct dependencies only (default).
731749
732750
Returns
733751
-------
@@ -737,11 +755,7 @@ def get_deps_from_sbom(self, sbom_path: str | Path, target_component: Component)
737755
self.get_dep_components(
738756
target_component=target_component,
739757
root_bom_path=Path(sbom_path),
740-
recursive=defaults.getboolean(
741-
"dependency.resolver",
742-
"recursive",
743-
fallback=False,
744-
),
758+
recursive=recursive,
745759
)
746760
)
747761

@@ -753,7 +767,9 @@ def __init__(self) -> None:
753767
"""Initialize the dependency analyzer instance."""
754768
super().__init__(resources_path="", file_name="", tool_name="", tool_version="")
755769

756-
def collect_dependencies(self, dir_path: str, target_component: Component) -> dict[str, DependencyInfo]:
770+
def collect_dependencies(
771+
self, dir_path: str, target_component: Component, recursive: bool = False
772+
) -> dict[str, DependencyInfo]:
757773
"""Process the dependency JSON files and collect direct dependencies.
758774
759775
Parameters
@@ -762,6 +778,8 @@ def collect_dependencies(self, dir_path: str, target_component: Component) -> di
762778
Local path to the target repo.
763779
target_component: Component
764780
The analyzed target software component.
781+
recursive: bool
782+
Set to False to get the direct dependencies only (default).
765783
766784
Returns
767785
-------

src/macaron/dependency_analyzer/cyclonedx_gradle.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
from cyclonedx.model.component import Component as CDXComponent
1616
from packageurl import PackageURL
1717

18-
from macaron.config.defaults import defaults
1918
from macaron.database.table_definitions import Component
2019
from macaron.dependency_analyzer.cyclonedx import DependencyAnalyzer, DependencyInfo
2120

@@ -47,7 +46,12 @@ def get_cmd(self) -> list:
4746
"cyclonedxBom",
4847
]
4948

50-
def collect_dependencies(self, dir_path: str, target_component: Component) -> dict[str, DependencyInfo]:
49+
def collect_dependencies(
50+
self,
51+
dir_path: str,
52+
target_component: Component,
53+
recursive: bool = False,
54+
) -> dict[str, DependencyInfo]:
5155
"""Process the dependency JSON files and collect direct dependencies.
5256
5357
Parameters
@@ -56,6 +60,8 @@ def collect_dependencies(self, dir_path: str, target_component: Component) -> di
5660
Local path to the target repo.
5761
target_component: Component
5862
The analyzed target software component.
63+
recursive: bool
64+
Set to False to get the direct dependencies only (default).
5965
6066
Returns
6167
-------
@@ -82,11 +88,7 @@ def collect_dependencies(self, dir_path: str, target_component: Component) -> di
8288
target_component,
8389
top_path,
8490
child_paths,
85-
recursive=defaults.getboolean(
86-
"dependency.resolver",
87-
"recursive",
88-
fallback=False,
89-
),
91+
recursive=recursive,
9092
)
9193
return self.convert_components_to_artifacts(components, root_component)
9294

src/macaron/dependency_analyzer/cyclonedx_mvn.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
from cyclonedx.model.component import Component as CDXComponent
1616
from packageurl import PackageURL
1717

18-
from macaron.config.defaults import defaults
1918
from macaron.database.table_definitions import Component
2019
from macaron.dependency_analyzer.cyclonedx import DependencyAnalyzer, DependencyInfo
2120

@@ -47,7 +46,12 @@ def get_cmd(self) -> list:
4746
"includeTestScope=true",
4847
]
4948

50-
def collect_dependencies(self, dir_path: str, target_component: Component) -> dict[str, DependencyInfo]:
49+
def collect_dependencies(
50+
self,
51+
dir_path: str,
52+
target_component: Component,
53+
recursive: bool = False,
54+
) -> dict[str, DependencyInfo]:
5155
"""Process the dependency JSON files and collect direct dependencies.
5256
5357
We allow the dependency JSON files to be accepted as long as there is only one JSON file in the target
@@ -63,6 +67,8 @@ def collect_dependencies(self, dir_path: str, target_component: Component) -> di
6367
Local path to the target repo.
6468
target_component: Component
6569
The analyzed target software component.
70+
recursive: bool
71+
Set to False to get the direct dependencies only (default).
6672
6773
Returns
6874
-------
@@ -111,11 +117,7 @@ def collect_dependencies(self, dir_path: str, target_component: Component) -> di
111117
target_component,
112118
top_path,
113119
child_paths,
114-
recursive=defaults.getboolean(
115-
"dependency.resolver",
116-
"recursive",
117-
fallback=False,
118-
),
120+
recursive=recursive,
119121
)
120122
return self.convert_components_to_artifacts(components, root_component)
121123

src/macaron/dependency_analyzer/cyclonedx_python.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
from cyclonedx.model.component import Component as CDXComponent
1515
from packageurl import PackageURL
1616

17-
from macaron.config.defaults import defaults
1817
from macaron.config.global_config import global_config
1918
from macaron.database.table_definitions import Component
2019
from macaron.dependency_analyzer.cyclonedx import DependencyAnalyzer, DependencyInfo
@@ -50,7 +49,12 @@ def get_cmd(self) -> list:
5049
os.path.join(global_config.output_path, self.file_name),
5150
]
5251

53-
def collect_dependencies(self, dir_path: str, target_component: Component) -> dict[str, DependencyInfo]:
52+
def collect_dependencies(
53+
self,
54+
dir_path: str,
55+
target_component: Component,
56+
recursive: bool = False,
57+
) -> dict[str, DependencyInfo]:
5458
"""Process the dependency JSON files and collect dependencies.
5559
5660
Parameters
@@ -59,6 +63,8 @@ def collect_dependencies(self, dir_path: str, target_component: Component) -> di
5963
Local path to the target repo.
6064
target_component: Component
6165
The analyzed target software component.
66+
recursive: bool
67+
Set to False to get the direct dependencies only (default).
6268
6369
Returns
6470
-------
@@ -69,11 +75,7 @@ def collect_dependencies(self, dir_path: str, target_component: Component) -> di
6975
self.get_dep_components(
7076
target_component=target_component,
7177
root_bom_path=Path(global_config.output_path, self.file_name),
72-
recursive=defaults.getboolean(
73-
"dependency.resolver",
74-
"recursive",
75-
fallback=False,
76-
),
78+
recursive=recursive,
7779
)
7880
)
7981

src/macaron/slsa_analyzer/analyzer.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ def run(
130130
self,
131131
user_config: dict,
132132
sbom_path: str = "",
133-
dependencies: bool = False,
133+
deps_depth: int = 0,
134134
provenance_payload: InTotoPayload | None = None,
135135
) -> int:
136136
"""Run the analysis and write results to the output path.
@@ -144,8 +144,8 @@ def run(
144144
The dictionary that contains the user config parsed from the yaml file.
145145
sbom_path : str
146146
The path to the SBOM.
147-
dependencies : bool
148-
Flag to enable dependency resolution. False by default.
147+
deps_depth : int
148+
The depth of dependency resolution. Default: 0.
149149
provenance_payload : InToToPayload | None
150150
The provenance intoto payload for the main software component.
151151
@@ -187,11 +187,26 @@ def run(
187187
logger.info("Analysis has failed.")
188188
return os.EX_DATAERR
189189

190-
# Run the chosen dependency analyzer plugin.
191-
if not dependencies:
190+
if deps_depth == 0:
192191
logger.info("Skipping automatic dependency analysis...")
192+
elif deps_depth == 1:
193+
# Run the chosen dependency analyzer plugin on direct dependencies.
194+
deps_resolved = DependencyAnalyzer.resolve_dependencies(
195+
main_record.context,
196+
sbom_path,
197+
recursive=False,
198+
)
199+
elif deps_depth == -1:
200+
# Run the chosen dependency analyzer plugin on transitive dependencies.
201+
deps_resolved = DependencyAnalyzer.resolve_dependencies(
202+
main_record.context,
203+
sbom_path,
204+
recursive=True,
205+
)
193206
else:
194-
deps_resolved = DependencyAnalyzer.resolve_dependencies(main_record.context, sbom_path)
207+
# Can't reach here.
208+
logger.critical("Expecting deps depth to be '0', '1' or '-1', got %s", deps_depth)
209+
return os.EX_USAGE
195210

196211
# Merge the automatically resolved dependencies with the manual configuration.
197212
deps_config = DependencyAnalyzer.merge_configs(deps_config, deps_resolved)

tests/integration/cases/apache_maven_cyclonedx_sbom_tutorial/test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ steps:
1515
command_args:
1616
- -purl
1717
- pkg:maven/org.apache.maven/[email protected]?type=pom
18-
- --deps-resolution
18+
- --deps-depth=1
1919
sbom: sbom.json
2020
- name: Compare dependencies report.
2121
kind: compare

tests/integration/cases/apache_maven_local_path_with_branch_name_digest_deps_cyclonedx_maven/test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ steps:
2727
- master
2828
- -d
2929
- 3fc399318edef0d5ba593723a24fff64291d6f9b
30-
- --deps-resolution
30+
- --deps-depth=1
3131
- name: Compare deps report
3232
kind: compare
3333
options:

tests/integration/cases/apache_maven_repo_path_branch_digest_with_deps_cyclonedx_maven/test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ steps:
2020
- master
2121
- -d
2222
- 3fc399318edef0d5ba593723a24fff64291d6f9b
23-
- --deps-resolution
23+
- --deps-depth=1
2424
- name: Compare deps report
2525
kind: compare
2626
options:

0 commit comments

Comments
 (0)