Skip to content

Commit 4a4771f

Browse files
authored
chore: add provenance as an input CLI option (#654)
Signed-off-by: Trong Nhan Mai <[email protected]>
1 parent 1058567 commit 4a4771f

File tree

5 files changed

+66
-10
lines changed

5 files changed

+66
-10
lines changed

docs/source/pages/cli_usage/command_analyze.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ Options
5858

5959
The path to provenance expectation file or directory.
6060

61+
.. option:: -pf PROVENANCE_FILE, --provenance-file PROVENANCE_FILE
62+
63+
The path to the provenance file in in-toto format.
64+
6165
.. option:: -c CONFIG_PATH, --config-path CONFIG_PATH
6266

6367
The path to the user configuration.

scripts/dev_scripts/integration_tests.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,17 @@ then
635635
log_fail
636636
fi
637637

638+
echo -e "\n----------------------------------------------------------------------------------"
639+
echo "Test providing an invalid provenance file as input."
640+
echo -e "----------------------------------------------------------------------------------\n"
641+
$RUN_MACARON analyze -rp https://github.com/apache/maven --provenance-file $WORKSPACE/golang/internal/cue_validator/resources/invalid_provenance.json --skip-deps
642+
643+
if [ $? -eq 0 ];
644+
then
645+
echo -e "Expect non-zero status code but got $?."
646+
log_fail
647+
fi
648+
638649
# Testing the CUE provenance expectation verifier.
639650
echo -e "\n----------------------------------------------------------------------------------"
640651
echo "Test verifying CUE provenance expectation for ossf/scorecard"

src/macaron/__main__.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
from macaron.slsa_analyzer.analyzer import Analyzer
2424
from macaron.slsa_analyzer.git_service import GIT_SERVICES
2525
from macaron.slsa_analyzer.package_registry import PACKAGE_REGISTRIES
26+
from macaron.slsa_analyzer.provenance.intoto.errors import LoadIntotoAttestationError
27+
from macaron.slsa_analyzer.provenance.loader import load_provenance_payload
2628
from macaron.vsa.vsa import generate_vsa
2729

2830
logger: logging.Logger = logging.getLogger(__name__)
@@ -78,7 +80,6 @@ def analyze_slsa_levels_single(analyzer_single_args: argparse.Namespace) -> None
7880
analyzer.reporters.append(JSONReporter())
7981

8082
run_config = {}
81-
8283
if analyzer_single_args.config_path:
8384
# Get user config from yaml file
8485
loaded_config = YamlLoader.load(analyzer_single_args.config_path)
@@ -129,7 +130,20 @@ def analyze_slsa_levels_single(analyzer_single_args: argparse.Namespace) -> None
129130
"dependencies": [],
130131
}
131132

132-
status_code = analyzer.run(run_config, analyzer_single_args.sbom_path, analyzer_single_args.skip_deps)
133+
prov_payload = None
134+
if analyzer_single_args.provenance_file:
135+
try:
136+
prov_payload = load_provenance_payload(analyzer_single_args.provenance_file)
137+
except LoadIntotoAttestationError as error:
138+
logger.error("Error while loading the input provenance file: %s", error)
139+
sys.exit(os.EX_DATAERR)
140+
141+
status_code = analyzer.run(
142+
run_config,
143+
analyzer_single_args.sbom_path,
144+
analyzer_single_args.skip_deps,
145+
prov_payload=prov_payload,
146+
)
133147
sys.exit(status_code)
134148

135149

@@ -365,6 +379,13 @@ def main(argv: list[str] | None = None) -> None:
365379
help=("The path to provenance expectation file or directory."),
366380
)
367381

382+
single_analyze_parser.add_argument(
383+
"-pf",
384+
"--provenance-file",
385+
required=False,
386+
help=("The path to the provenance file in in-toto format."),
387+
)
388+
368389
group.add_argument(
369390
"-c",
370391
"--config-path",

src/macaron/slsa_analyzer/analyze_context.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from macaron.slsa_analyzer.git_service.base_git_service import NoneGitService
1818
from macaron.slsa_analyzer.levels import SLSALevels
1919
from macaron.slsa_analyzer.provenance.expectations.expectation import Expectation
20+
from macaron.slsa_analyzer.provenance.intoto import InTotoPayload
2021
from macaron.slsa_analyzer.provenance.intoto.v01 import InTotoV01Statement
2122
from macaron.slsa_analyzer.provenance.intoto.v1 import InTotoV1Statement
2223
from macaron.slsa_analyzer.slsa_req import ReqName, SLSAReqStatus, create_requirement_status_dict
@@ -31,17 +32,19 @@ class ChecksOutputs(TypedDict):
3132
"""Data computed at runtime by checks."""
3233

3334
git_service: BaseGitService
34-
"""The git service information for this repository."""
35+
"""The git service information for the target software component."""
3536
build_spec: BuildSpec
36-
"""The build spec inferred for this repository."""
37+
"""The build spec inferred for the target software component."""
3738
ci_services: list[CIInfo]
38-
"""The CI services information for this repository."""
39+
"""The CI services information for the target software component."""
3940
is_inferred_prov: bool
4041
"""True if we cannot find the provenance and Macaron need to infer the provenance."""
4142
expectation: Expectation | None
42-
"""The expectation to verify the provenance for this repository."""
43+
"""The expectation to verify the provenance for the target software component."""
4344
package_registries: list[PackageRegistryInfo]
44-
"""The package registries for this repository."""
45+
"""The package registries for the target software component."""
46+
provenance: InTotoPayload | None
47+
"""The provenance payload for the target software component."""
4548

4649

4750
class AnalyzeContext:
@@ -92,6 +95,7 @@ def __init__(
9295
package_registries=[],
9396
is_inferred_prov=True,
9497
expectation=None,
98+
provenance=None,
9599
)
96100

97101
@property

src/macaron/slsa_analyzer/analyzer.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
from macaron.slsa_analyzer.git_service.base_git_service import NoneGitService
4141
from macaron.slsa_analyzer.package_registry import PACKAGE_REGISTRIES
4242
from macaron.slsa_analyzer.provenance.expectations.expectation_registry import ExpectationRegistry
43-
from macaron.slsa_analyzer.provenance.intoto import InTotoV01Payload
43+
from macaron.slsa_analyzer.provenance.intoto import InTotoPayload, InTotoV01Payload
4444
from macaron.slsa_analyzer.provenance.slsa import SLSAProvenanceData
4545
from macaron.slsa_analyzer.registry import registry
4646
from macaron.slsa_analyzer.specs.ci_spec import CIInfo
@@ -111,7 +111,13 @@ def __init__(self, output_path: str, build_log_path: str) -> None:
111111
# Create database tables: all checks have been registered so all tables should be mapped now
112112
self.db_man.create_tables()
113113

114-
def run(self, user_config: dict, sbom_path: str = "", skip_deps: bool = False) -> int:
114+
def run(
115+
self,
116+
user_config: dict,
117+
sbom_path: str = "",
118+
skip_deps: bool = False,
119+
prov_payload: InTotoPayload | None = None,
120+
) -> int:
115121
"""Run the analysis and write results to the output path.
116122
117123
This method handles the configuration file and writes the result html reports including dependencies.
@@ -125,6 +131,8 @@ def run(self, user_config: dict, sbom_path: str = "", skip_deps: bool = False) -
125131
The path to the SBOM.
126132
skip_deps : bool
127133
Flag to skip dependency resolution.
134+
prov_payload : InToToPayload | None
135+
The provenance intoto payload for the main software component.
128136
129137
Returns
130138
-------
@@ -154,7 +162,11 @@ def run(self, user_config: dict, sbom_path: str = "", skip_deps: bool = False) -
154162
)
155163

156164
# Analyze the main target.
157-
main_record = self.run_single(main_config, analysis)
165+
main_record = self.run_single(
166+
main_config,
167+
analysis,
168+
prov_payload=prov_payload,
169+
)
158170

159171
if main_record.status != SCMStatus.AVAILABLE or not main_record.context:
160172
logger.info("Analysis has failed.")
@@ -255,6 +267,7 @@ def run_single(
255267
config: Configuration,
256268
analysis: Analysis,
257269
existing_records: dict[str, Record] | None = None,
270+
prov_payload: InTotoPayload | None = None,
258271
) -> Record:
259272
"""Run the checks for a single repository target.
260273
@@ -269,6 +282,8 @@ def run_single(
269282
The current analysis instance.
270283
existing_records : dict[str, Record] | None
271284
The mapping of existing records that the analysis has run successfully.
285+
prov_payload : InToToPayload | None
286+
The provenance intoto payload for the analyzed software component.
272287
273288
Returns
274289
-------
@@ -306,6 +321,7 @@ def run_single(
306321
analyze_ctx.dynamic_data["expectation"] = self.expectations.get_expectation_for_target(
307322
analyze_ctx.component.purl.split("@")[0]
308323
)
324+
analyze_ctx.dynamic_data["provenance"] = prov_payload
309325
analyze_ctx.check_results = self.perform_checks(analyze_ctx)
310326

311327
return Record(

0 commit comments

Comments
 (0)