Skip to content

Commit a9b9425

Browse files
committed
feat: add support for JFrog Maven package registry and witness provenance
Signed-off-by: Nathan Nguyen <[email protected]>
1 parent 2062e7e commit a9b9425

21 files changed

+1102
-58
lines changed

src/macaron/__main__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from macaron.policy_engine.policy_engine import run_policy_engine, show_prelude
2323
from macaron.slsa_analyzer.analyzer import Analyzer
2424
from macaron.slsa_analyzer.git_service import GIT_SERVICES
25+
from macaron.slsa_analyzer.package_registry import PACKAGE_REGISTRIES
2526

2627
logger: logging.Logger = logging.getLogger(__name__)
2728

@@ -142,6 +143,8 @@ def perform_action(action_args: argparse.Namespace) -> None:
142143
try:
143144
for git_service in GIT_SERVICES:
144145
git_service.load_defaults()
146+
for package_registry in PACKAGE_REGISTRIES:
147+
package_registry.load_defaults()
145148
except ConfigurationError as error:
146149
logger.error(error)
147150
sys.exit(os.EX_USAGE)

src/macaron/config/defaults.ini

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,3 +337,15 @@ provenance_extensions =
337337
max_download_size = 70000000
338338
# This is the timeout (in seconds) to run the SLSA verifier.
339339
timeout = 120
340+
341+
# Witness provenance.
342+
# [provenance.witness]
343+
# # The allowed values of the `predicateType` field (data type: list).
344+
# predicate_types =
345+
# https://witness.testifysec.com/attestation-collection/v0.1
346+
347+
# Package registries.
348+
# [package_registry.jfrog.maven]
349+
# In this example, the Maven package registry can be accessed at https://internal.registry.org/maven-repo.
350+
# repo_url = https://internal.registry.org
351+
# repo_key = maven-repo

src/macaron/errors.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,11 @@ class ConfigurationError(MacaronError):
3030

3131
class CloneError(MacaronError):
3232
"""Happens when cannot clone a git repository."""
33+
34+
35+
class ProvenanceDownloadError(MacaronError):
36+
"""Happens when there is an issue downloading a provenance asset."""
37+
38+
39+
class ProvenanceLoadError(MacaronError):
40+
"""Happens when there is an issue decoding and loading a provenance from a provenance asset."""

src/macaron/slsa_analyzer/analyze_context.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from macaron.slsa_analyzer.slsa_req import ReqName, SLSAReq, get_requirements_dict
2222
from macaron.slsa_analyzer.specs.build_spec import BuildSpec
2323
from macaron.slsa_analyzer.specs.ci_spec import CIInfo
24+
from macaron.slsa_analyzer.specs.package_registry_data import PackageRegistryData
2425

2526
logger: logging.Logger = logging.getLogger(__name__)
2627

@@ -38,6 +39,8 @@ class ChecksOutputs(TypedDict):
3839
"""True if we cannot find the provenance and Macaron need to infer the provenance."""
3940
expectation: Expectation | None
4041
"""The expectation to verify the provenance for this repository."""
42+
package_registries: list[PackageRegistryData]
43+
"""The package registries for this repository."""
4144

4245

4346
class AnalyzeContext:
@@ -118,6 +121,7 @@ def __init__(
118121
git_service=NoneGitService(),
119122
build_spec=BuildSpec(tools=[]),
120123
ci_services=[],
124+
package_registries=[],
121125
is_inferred_prov=True,
122126
expectation=None,
123127
)
@@ -134,7 +138,7 @@ def provenances(self) -> dict:
134138
"""
135139
try:
136140
ci_services = self.dynamic_data["ci_services"]
137-
result = {}
141+
result = {} # key: ci service's name; values: list of provenances (in Json)
138142
for ci_info in ci_services:
139143
result[ci_info["service"].name] = ci_info["provenances"]
140144
return result

src/macaron/slsa_analyzer/analyzer.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,12 @@
4141
from macaron.slsa_analyzer.database_store import store_analysis_to_db, store_analyze_context_to_db
4242
from macaron.slsa_analyzer.git_service import GIT_SERVICES, BaseGitService
4343
from macaron.slsa_analyzer.git_service.base_git_service import NoneGitService
44+
from macaron.slsa_analyzer.package_registry import PACKAGE_REGISTRIES
4445
from macaron.slsa_analyzer.provenance.expectations.expectation_registry import ExpectationRegistry
4546
from macaron.slsa_analyzer.registry import registry
4647
from macaron.slsa_analyzer.specs.ci_spec import CIInfo
4748
from macaron.slsa_analyzer.specs.inferred_provenance import Provenance
49+
from macaron.slsa_analyzer.specs.package_registry_data import PackageRegistryData
4850

4951
logger: logging.Logger = logging.getLogger(__name__)
5052

@@ -664,7 +666,7 @@ def perform_checks(self, analyze_ctx: AnalyzeContext) -> dict[str, CheckResult]:
664666
ci_service.load_defaults()
665667
ci_service.set_api_client()
666668

667-
if ci_service.is_detected(analyze_ctx.git_obj.path):
669+
if ci_service.is_detected(analyze_ctx.git_obj.path, analyze_ctx.dynamic_data["git_service"]):
668670
logger.info("The repo uses %s CI service.", ci_service.name)
669671

670672
# Parse configuration files and generate IRs.
@@ -684,6 +686,19 @@ def perform_checks(self, analyze_ctx: AnalyzeContext) -> dict[str, CheckResult]:
684686
)
685687
)
686688

689+
# Determine the package registries.
690+
# We match the repo against package registries through build tools.
691+
build_tools = analyze_ctx.dynamic_data["build_spec"]["tools"]
692+
for package_registry in PACKAGE_REGISTRIES:
693+
for build_tool in build_tools:
694+
if package_registry.is_detected(build_tool):
695+
analyze_ctx.dynamic_data["package_registries"].append(
696+
PackageRegistryData(
697+
build_tool=build_tool,
698+
package_registry=package_registry,
699+
)
700+
)
701+
687702
# TODO: Get the list of skipped checks from user configuration
688703
skipped_checks: list[SkippedInfo] = []
689704

src/macaron/slsa_analyzer/build_tool/gradle.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import logging
1010
import os
11+
import subprocess # nosec B404
1112

1213
from macaron.config.defaults import defaults
1314
from macaron.config.global_config import global_config
@@ -135,3 +136,31 @@ def get_dep_analyzer(self, repo_path: str) -> CycloneDxGradle:
135136
)
136137

137138
raise DependencyAnalyzerError(f"Unsupported SBOM generator for Gradle: {tool_name}.")
139+
140+
def get_group_id(self, project_path: str) -> str | None:
141+
"""Get the group id of a Java repository.
142+
143+
Parameters
144+
----------
145+
project_path : str
146+
Path to the repository.
147+
"""
148+
try:
149+
result = subprocess.run( # nosec B603
150+
["./gradlew", "properties"],
151+
capture_output=True,
152+
cwd=project_path,
153+
check=False,
154+
)
155+
except (subprocess.CalledProcessError, OSError) as error:
156+
logger.info("Could not capture the group id of the repo at %s", project_path)
157+
logger.debug("Error: %s", error)
158+
return None
159+
160+
lines = result.stdout.decode().split("\n")
161+
for line in lines:
162+
if line.startswith("group: "):
163+
return line.replace("group: ", "")
164+
165+
logger.debug("Could not capture the group id of the repo at %s", project_path)
166+
return None

0 commit comments

Comments
 (0)