Skip to content

Commit

Permalink
feat: add a new check to map artifacts to pipelines (#471)
Browse files Browse the repository at this point in the history
This PR adds a new check, `mcn_infer_artifact_pipeline_1` to detect a 
potential pipeline from which an artifact is published.

When a verifiable provenance is found for an artifact, the result of this 
check can be discarded. Otherwise, we check whether a CI workflow 
run has automatically published the artifact.

This check supports Maven artifacts built using Gradle or Maven and 
published on Maven Central only. Support for other registries and ecosystems will be added in the future.

Signed-off-by: behnazh-w <[email protected]>
  • Loading branch information
behnazh-w authored Sep 13, 2023
1 parent 5815139 commit 13206e5
Show file tree
Hide file tree
Showing 44 changed files with 2,191 additions and 752 deletions.
3 changes: 3 additions & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ the requirements that are currently supported by Macaron.
* - 3
- **Build as code** - If a trusted builder is not present, this requirement determines that the build definition and configuration executed by the build service is verifiably derived from text file definitions stored in a version control system.
- Identify and validate the CI service(s) used to build and deploy/publish an artifact.
* - 3
- **Infer artifact publish pipeline** - When a provenance is not available, checks whether a CI workflow run has automatically published the artifact.
- Identify a workflow run that has triggered the deploy step determined by the ``Build as code`` check.
* - 3
- **Provenance Level three** - Check whether the target has SLSA provenance level 3.
- Use the `slsa-verifier <https://github.com/slsa-framework/slsa-verifier>`_ to attest to the subjects in the SLSA provenance that accompanies an artifact.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ macaron.slsa\_analyzer.checks.check\_result module
:undoc-members:
:show-inheritance:

macaron.slsa\_analyzer.checks.infer\_artifact\_pipeline\_check module
---------------------------------------------------------------------

.. automodule:: macaron.slsa_analyzer.checks.infer_artifact_pipeline_check
:members:
:undoc-members:
:show-inheritance:

macaron.slsa\_analyzer.checks.provenance\_available\_check module
-----------------------------------------------------------------

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ macaron.slsa\_analyzer.package\_registry.jfrog\_maven\_registry module
:undoc-members:
:show-inheritance:

macaron.slsa\_analyzer.package\_registry.maven\_central\_registry module
------------------------------------------------------------------------

.. automodule:: macaron.slsa_analyzer.package_registry.maven_central_registry
:members:
:undoc-members:
:show-inheritance:

macaron.slsa\_analyzer.package\_registry.package\_registry module
-----------------------------------------------------------------

Expand Down
4 changes: 4 additions & 0 deletions docs/source/pages/supported_technologies/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ Package Registries
* - `JFrog Artifactory <https://jfrog.com/artifactory>`_
- Only projects built with Gradle and publishing to a JFrog Artifactory repo following `Maven layout <https://maven.apache.org/repository/layout.html>`_
- :doc:`page </pages/supported_technologies/jfrog>`
* - `Maven Central Artifactory <https://central.sonatype.com>`_
- Only projects built with Gradle or Maven and published to the Maven Central Artifactory.
- :doc:`page </pages/supported_technologies/maven_central>`

-----------
Provenances
Expand Down Expand Up @@ -70,3 +73,4 @@ See also

jfrog
witness
maven_central
6 changes: 6 additions & 0 deletions docs/source/pages/supported_technologies/maven_central.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.. Copyright (c) 2023 - 2023, Oracle and/or its affiliates. All rights reserved.
.. Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/.
=============
Maven Central
=============
10 changes: 10 additions & 0 deletions scripts/dev_scripts/integration_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,16 @@ python $COMPARE_JSON_OUT $JSON_RESULT $JSON_EXPECTED || log_fail

# python $COMPARE_DEPS $DEP_RESULT $DEP_EXPECTED || log_fail

echo -e "\n----------------------------------------------------------------------------------"
echo "google/guava: Analyzing with PURL and repository path without dependency resolution."
echo -e "----------------------------------------------------------------------------------\n"
JSON_EXPECTED=$WORKSPACE/tests/e2e/expected_results/purl/com_google_guava/guava/guava.json
JSON_RESULT=$WORKSPACE/output/reports/maven/com_google_guava/guava/guava.json
$RUN_MACARON analyze -purl pkg:maven/com.google.guava/[email protected]?type=jar -rp https://github.com/google/guava -b master -d d8633ac8539dae52c8361f79c7a0dbd9ad6dd2c4 --skip-deps || log_fail

python $COMPARE_JSON_OUT $JSON_RESULT $JSON_EXPECTED || log_fail


# Running Macaron using local paths.
echo -e "\n=================================================================================="
echo "Run integration tests with local paths for apache/maven..."
Expand Down
12 changes: 11 additions & 1 deletion src/macaron/config/defaults.ini
Original file line number Diff line number Diff line change
Expand Up @@ -375,10 +375,20 @@ predicate_types =
https://witness.testifysec.com/attestation-collection/v0.1
artifact_extensions =
jar

# Package registries.
[package_registry]
# The allowed time range (in seconds) from a deploy workflow run start time to publish time.
publish_time_range = 3600

# [package_registry.jfrog.maven]
# In this example, the Maven repo can be accessed at `https://internal.registry.org/repo-name`.
# hostname = internal.registry.org
# repo = repo-name
# download_timeout = 120

[package_registry.maven_central]
# Maven Central host name.
hostname = search.maven.org
# The search REST API. See https://central.sonatype.org/search/rest-api-guide/
search_endpoint = solrsearch/select
request_timeout = 20
1 change: 1 addition & 0 deletions src/macaron/dependency_analyzer/cyclonedx.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ def convert_components_to_artifacts(
version=component.get("version") or "",
group=component.get("group") or "",
name=component.get("name") or "",
purl=component.get("purl") or "",
url="",
note="",
available=SCMStatus.AVAILABLE,
Expand Down
2 changes: 2 additions & 0 deletions src/macaron/dependency_analyzer/dependency_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class DependencyInfo(TypedDict):
version: str
group: str
name: str
purl: str
url: str
note: str
available: SCMStatus
Expand Down Expand Up @@ -262,6 +263,7 @@ def merge_configs(
Configuration(
{
"id": key,
"purl": value.get("purl"),
"path": value.get("url"),
"branch": "",
"digest": "",
Expand Down
4 changes: 4 additions & 0 deletions src/macaron/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,7 @@ class InvalidPURLError(MacaronError):

class DuplicateError(MacaronError):
"""The class for errors for duplicated data."""


class InvalidHTTPResponseError(MacaronError):
"""Happens when the HTTP response is invalid or unexpected."""
21 changes: 20 additions & 1 deletion src/macaron/parsers/bashparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ class BashCommands(TypedDict):
"""CI service type."""
commands: list[list[str]]
"""Parsed bash commands."""
job_name: str
"""The name of the job where commands were called."""
step_name: str
"""The name of the step where commands were called."""


def parse_file(file_path: str, macaron_path: str = "") -> dict:
Expand Down Expand Up @@ -119,6 +123,8 @@ def extract_bash_from_ci(
recursive: bool = False,
repo_path: str = "",
working_dir: str = "",
job_name: str = "",
step_name: str = "",
) -> Iterable[BashCommands]:
"""Parse the bash scripts triggered from CI.
Expand All @@ -140,6 +146,10 @@ def extract_bash_from_ci(
working_dir : str
The working directory from which the bash script has run.
Empty value is considered as the root of the repo.
job_name: str
The name of the job where commands were called.
step_name: str
The name of the step where commands were called.
Yields
------
Expand All @@ -152,7 +162,14 @@ def extract_bash_from_ci(
parsed_parent = parse(bash_content)
caller_commands = parsed_parent.get("commands", [])
if caller_commands:
yield BashCommands(caller_path=ci_file, CI_path=ci_file, CI_type=ci_type, commands=caller_commands)
yield BashCommands(
caller_path=ci_file,
CI_path=ci_file,
CI_type=ci_type,
commands=caller_commands,
job_name=job_name,
step_name=step_name,
)

# Parse the bash script files called from the current script.
if recursive and repo_path:
Expand All @@ -170,4 +187,6 @@ def extract_bash_from_ci(
CI_path=ci_file,
CI_type=ci_type,
commands=callee_commands,
job_name=job_name,
step_name=step_name,
)
6 changes: 4 additions & 2 deletions src/macaron/slsa_analyzer/checks/build_as_code_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ def _check_build_tool(
ctx.component.repository.branch_name,
ctx.component.repository.commit_sha,
ctx.component.repository.commit_date,
os.path.basename(callee.caller_path),
callee.caller_path,
)

# TODO: include in the justification multiple cases of external action usage
Expand Down Expand Up @@ -253,7 +253,7 @@ def _check_build_tool(
ctx.component.repository.branch_name,
ctx.component.repository.commit_sha,
ctx.component.repository.commit_date,
os.path.basename(bash_cmd["CI_path"]),
bash_cmd["CI_path"],
)

justification_cmd: list[str | dict[str, str]] = [
Expand Down Expand Up @@ -283,6 +283,8 @@ def _check_build_tool(
"sha1"
] = ctx.component.repository.commit_sha
predicate["invocation"]["configSource"]["entryPoint"] = trigger_link
predicate["buildConfig"]["jobID"] = bash_cmd["job_name"]
predicate["buildConfig"]["stepID"] = bash_cmd["step_name"]
predicate["metadata"]["buildInvocationId"] = html_url
check_result["result_tables"].append(
BuildAsCodeFacts(
Expand Down
Loading

0 comments on commit 13206e5

Please sign in to comment.