Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Go, NPM and Yarn detection #451

Merged
merged 18 commits into from
Oct 3, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: go detection
Signed-off-by: Tim Yarkov <[email protected]>
timyarkov committed Sep 30, 2023

Verified

This commit was signed with the committer’s verified signature.
timyarkov Tim Yarkov
commit 0cdc38d4d25f252388f391beedc14165899a0e55
12 changes: 11 additions & 1 deletion scripts/dev_scripts/integration_tests.sh
Original file line number Diff line number Diff line change
@@ -105,11 +105,21 @@ echo "uiv-lib/uiv: Analysing the repo path, the branch name and the commit diges
echo "skipping dependency resolution."
echo -e "----------------------------------------------------------------------------------\n"
JSON_EXPECTED=$WORKSPACE/tests/e2e/expected_results/urllib3/urllib3.json
JSON_RESULT=$WORKSPACE/output/reports/github_com/urllib3/urllib3/urllib3.json
JSON_RESULT=$WORKSPACE/output/reports/github_com/uiv-lib/urllib3/urllib3.json
$RUN_MACARON analyze -rp https://github.com/uiv-lib/uiv -b dev -d 057b25b4db0913edab4cf728c306085e6fc20d49 --skip-deps || log_fail

python $COMPARE_JSON_OUT $JSON_RESULT $JSON_EXPECTED || log_fail

echo -e "\n----------------------------------------------------------------------------------"
echo "sagikazarmark/modern-go-application: Analysing the repo path, the branch name and the"
echo "commit digest for a Go project, skipping dependency resolution."
echo -e "----------------------------------------------------------------------------------\n"
JSON_EXPECTED=$WORKSPACE/tests/e2e/expected_results/modern-go-application.json
JSON_RESULT=$WORKSPACE/output/reports/github_com/sagikazarmark/modern-go-application/modern-go-application.json
$RUN_MACARON analyze -rp https://github.com/sagikazarmark/modern-go-application -b main -d 468a20bc42f96ffebae693a51b006b263b4a497e --skip-deps || log_fail

python $COMPARE_JSON_OUT $JSON_RESULT $JSON_EXPECTED || log_fail

echo -e "\n----------------------------------------------------------------------------------"
echo "apache/maven: Analyzing with PURL and repository path without dependency resolution."
echo -e "----------------------------------------------------------------------------------\n"
6 changes: 5 additions & 1 deletion src/macaron/config/defaults.ini
Original file line number Diff line number Diff line change
@@ -335,7 +335,11 @@ builder = go
build_arg =
build
install
deploy_arg = ??????
# Given documentation recommends git, so we can use it as the publisher https://go.dev/doc/modules/publishing
publisher = git
deploy_arg = push
# TODO: Figure out what should count for CI deployment, maybe something like
# https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go#packaging-workflow-data-as-artifacts

# This is the spec for GitHub Actions CI.
[ci.github_actions]
3 changes: 2 additions & 1 deletion src/macaron/slsa_analyzer/build_tool/__init__.py
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@

"""The build_tool package contains the supported build tools for Macaron."""

from macaron.slsa_analyzer.build_tool.go import Go
from macaron.slsa_analyzer.build_tool.npm import NPM

from .base_build_tool import BaseBuildTool
@@ -14,4 +15,4 @@

# The list of supported build tools. The order of the list determine the order
# in which each build tool is checked against the target repository.
BUILD_TOOLS: list[BaseBuildTool] = [Gradle(), Maven(), Poetry(), Pip(), Docker(), NPM()]
BUILD_TOOLS: list[BaseBuildTool] = [Gradle(), Maven(), Poetry(), Pip(), Docker(), NPM(), Go()]
86 changes: 86 additions & 0 deletions src/macaron/slsa_analyzer/build_tool/go.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# 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/.

"""This module contains the Go class which inherits BaseBuildTool.
This module is used to work with repositories that use Go as its build tool.
tromai marked this conversation as resolved.
Show resolved Hide resolved
"""

from macaron.config.defaults import defaults
from macaron.dependency_analyzer.dependency_resolver import DependencyAnalyzer, NoneDependencyAnalyzer
from macaron.slsa_analyzer.build_tool.base_build_tool import BaseBuildTool, file_exists


class Go(BaseBuildTool):
"""This class contains the information of the Go build tool."""

def __init__(self) -> None:
super().__init__(name="go")

def load_defaults(self) -> None:
"""Load the default values from defaults.ini."""
if "builder.go" in defaults:
for item in defaults["builder.go"]:
if hasattr(self, item):
setattr(self, item, defaults.get_list("builder.go", item))

# TODO: When suitable ci deployment pipeline is found
# if "builder.go.ci.deploy" in defaults:
# for item in defaults["builder.go.ci.deploy"]:
# if item in self.ci_deploy_kws:
# self.ci_deploy_kws[item] = defaults.get_list("builder.go.ci.deploy", item)

def is_detected(self, repo_path: str) -> bool:
"""Return True if this build tool is used in the target repo.
Parameters
----------
repo_path : str
The path to the target repo.
Returns
-------
bool
True if this build tool is detected, else False.
"""
go_config_files = self.build_configs + self.entry_conf
for file in go_config_files:
if file_exists(repo_path, file):
return True

return False

def prepare_config_files(self, wrapper_path: str, build_dir: str) -> bool:
"""Prepare the necessary wrapper files for running the build.
Go doesn't require preparation, so return true.
Parameters
----------
wrapper_path : str
The path where all necessary wrapper files are located.
build_dir : str
The path of the build dir. This is where all files are copied to.
Returns
-------
bool
True if succeed else False.
"""
return True

def get_dep_analyzer(self, repo_path: str) -> DependencyAnalyzer:
"""Create a DependencyAnalyzer for the build tool.
Parameters
----------
repo_path: str
The path to the target repo.
Returns
-------
DependencyAnalyzer
The DependencyAnalyzer object.
"""
# TODO: Implement this method.
return NoneDependencyAnalyzer()
20 changes: 20 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@
from macaron.database.table_definitions import Analysis, Component, Repository
from macaron.slsa_analyzer.analyze_context import AnalyzeContext
from macaron.slsa_analyzer.build_tool.docker import Docker
from macaron.slsa_analyzer.build_tool.go import Go
from macaron.slsa_analyzer.build_tool.gradle import Gradle
from macaron.slsa_analyzer.build_tool.maven import Maven
from macaron.slsa_analyzer.build_tool.npm import NPM
@@ -187,6 +188,25 @@ def npm_tool(setup_test) -> NPM: # type: ignore # pylint: disable=unused-argume
return npm


@pytest.fixture(autouse=True)
def go_tool(setup_test) -> Go: # type: ignore # pylint: disable=unused-argument
"""Create a Go tool instance.
Parameters
----------
setup_test
Depends on setup_test fixture.
Returns
-------
Go
The Go instance.
"""
go = Go() # pylint: disable=invalid-name
go.load_defaults()
return go


class MockGitHubActions(GitHubActions):
"""Mock the GitHubActions class."""

Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
{
"metadata": {
"timestamps": "2023-08-30 17:11:39"
},
"target": {
"info": {
"full_name": "pkg:github.com/sagikazarmark/modern-go-application@468a20bc42f96ffebae693a51b006b263b4a497e",
"local_cloned_path": "git_repos/github_com/sagikazarmark/modern-go-application",
"remote_path": "https://github.com/sagikazarmark/modern-go-application",
"branch": "main",
"commit_hash": "468a20bc42f96ffebae693a51b006b263b4a497e",
"commit_date": "2023-04-06T10:17:28+02:00"
},
"provenances": {
"is_inferred": true,
"content": {
"github_actions": [
{
"_type": "https://in-toto.io/Statement/v0.1",
"subject": [],
"predicateType": "https://slsa.dev/provenance/v0.2",
"predicate": {
"builder": {
"id": "<URI>"
},
"buildType": "<URI>",
"invocation": {
"configSource": {
"uri": "<URI>",
"digest": {
"sha1": "<STING>"
},
"entryPoint": "<STRING>"
},
"parameters": {},
"environment": {}
},
"buildConfig": {},
"metadata": {
"buildInvocationId": "<STRING>",
"buildStartedOn": "<TIMESTAMP>",
"buildFinishedOn": "<TIMESTAMP>",
"completeness": {
"parameters": "false",
"environment": "false",
"materials": "false"
},
"reproducible": "false"
},
"materials": [
{
"uri": "<URI>",
"digest": {}
}
]
}
}
]
}
},
"checks": {
"summary": {
"DISABLED": 0,
"FAILED": 7,
"PASSED": 2,
"SKIPPED": 0,
"UNKNOWN": 0
},
"results": [
{
"check_id": "mcn_build_script_1",
"check_description": "Check if the target repo has a valid build script.",
"slsa_requirements": [
"Scripted Build - SLSA Level 1"
],
"justification": [
"The target repository uses build tool go."
],
"result_type": "PASSED"
},
{
"check_id": "mcn_version_control_system_1",
"check_description": "Check whether the target repo uses a version control system.",
"slsa_requirements": [
"Version controlled - SLSA Level 2"
],
"justification": [
{
"This is a Git repository": "https://github.com/sagikazarmark/modern-go-application"
}
],
"result_type": "PASSED"
},
{
"check_id": "mcn_build_as_code_1",
"check_description": "The build definition and configuration executed by the build service is verifiably derived from text file definitions stored in a version control system.",
"slsa_requirements": [
"Build as code - SLSA Level 3"
],
"justification": [
"The target repository does not use go to deploy."
],
"result_type": "FAILED"
},
{
"check_id": "mcn_build_service_1",
"check_description": "Check if the target repo has a valid build service.",
"slsa_requirements": [
"Build service - SLSA Level 2"
],
"justification": [
"The target repository does not have a build service for go.",
"The target repository does not have a build service for at least one build tool."
],
"result_type": "FAILED"
},
{
"check_id": "mcn_provenance_available_1",
"check_description": "Check whether the target has intoto provenance.",
"slsa_requirements": [
"Provenance - Available - SLSA Level 1",
"Provenance content - Identifies build instructions - SLSA Level 1",
"Provenance content - Identifies artifacts - SLSA Level 1",
"Provenance content - Identifies builder - SLSA Level 1"
],
"justification": [
"Could not find any SLSA provenances."
],
"result_type": "FAILED"
},
{
"check_id": "mcn_provenance_expectation_1",
"check_description": "Check whether the SLSA provenance for the produced artifact conforms to the expected value.",
"slsa_requirements": [
"Provenance conforms with expectations - SLSA Level 3"
],
"justification": [
"Check mcn_provenance_expectation_1 is set to FAILED because mcn_provenance_available_1 FAILED."
],
"result_type": "FAILED"
},
{
"check_id": "mcn_provenance_level_three_1",
"check_description": "Check whether the target has SLSA provenance level 3.",
"slsa_requirements": [
"Provenance - Non falsifiable - SLSA Level 3",
"Provenance content - Includes all build parameters - SLSA Level 3",
"Provenance content - Identifies entry point - SLSA Level 3",
"Provenance content - Identifies source code - SLSA Level 2"
],
"justification": [
"Check mcn_provenance_level_three_1 is set to FAILED because mcn_provenance_available_1 FAILED."
],
"result_type": "FAILED"
},
{
"check_id": "mcn_provenance_witness_level_one_1",
"check_description": "Check whether the target has a level-1 witness provenance.",
"slsa_requirements": [
"Provenance - Available - SLSA Level 1",
"Provenance content - Identifies build instructions - SLSA Level 1",
"Provenance content - Identifies artifacts - SLSA Level 1",
"Provenance content - Identifies builder - SLSA Level 1"
],
"justification": [
"Check mcn_provenance_witness_level_one_1 is set to FAILED because mcn_provenance_available_1 FAILED."
],
"result_type": "FAILED"
},
{
"check_id": "mcn_trusted_builder_level_three_1",
"check_description": "Check whether the target uses a trusted SLSA level 3 builder.",
"slsa_requirements": [
"Hermetic - SLSA Level 4",
"Isolated - SLSA Level 3",
"Parameterless - SLSA Level 4",
"Ephemeral environment - SLSA Level 3"
],
"justification": [
"Could not find a trusted level 3 builder as a GitHub Actions workflow."
],
"result_type": "FAILED"
}
]
}
},
"dependencies": {
"analyzed_deps": 0,
"unique_dep_repos": 0,
"checks_summary": [
{
"check_id": "mcn_build_script_1",
"num_deps_pass": 0
},
{
"check_id": "mcn_version_control_system_1",
"num_deps_pass": 0
},
{
"check_id": "mcn_build_service_1",
"num_deps_pass": 0
},
{
"check_id": "mcn_trusted_builder_level_three_1",
"num_deps_pass": 0
},
{
"check_id": "mcn_provenance_witness_level_one_1",
"num_deps_pass": 0
},
{
"check_id": "mcn_provenance_available_1",
"num_deps_pass": 0
},
{
"check_id": "mcn_build_as_code_1",
"num_deps_pass": 0
},
{
"check_id": "mcn_provenance_level_three_1",
"num_deps_pass": 0
},
{
"check_id": "mcn_provenance_expectation_1",
"num_deps_pass": 0
}
],
"dep_status": []
}
}
5 changes: 5 additions & 0 deletions tests/slsa_analyzer/build_tool/__snapshots__/test_go.ambr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# serializer version: 1
# name: test_get_build_dirs[mock_repo0]
list([
])
# ---
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module test/oracle/macaron

go 1.18
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello
39 changes: 39 additions & 0 deletions tests/slsa_analyzer/build_tool/test_go.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# 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/.

"""This module tests the Go build functions."""

from pathlib import Path

import pytest

from macaron.slsa_analyzer.build_tool.go import Go
from tests.slsa_analyzer.mock_git_utils import prepare_repo_for_testing


@pytest.mark.parametrize(
"mock_repo",
[
Path(__file__).parent.joinpath("mock_repos", "go_repos", "no_go_mod"),
# TODO: Having an extra go.mod breaks the pre-commit hooks; investigate a fix
# Path(__file__).parent.joinpath("mock_repos", "go_repos", "go_mod"),
],
)
def test_get_build_dirs(snapshot: list, go_tool: Go, mock_repo: Path) -> None:
"""Test discovering build directories."""
assert list(go_tool.get_build_dirs(str(mock_repo))) == snapshot


@pytest.mark.parametrize(
("mock_repo", "expected_value"),
[
(Path(__file__).parent.joinpath("mock_repos", "go_repos", "no_go_mod"), False),
# TODO: Having an extra go.mod breaks the pre-commit hooks; investigate a fix
# (Path(__file__).parent.joinpath("mock_repos", "go_repos", "go_mod"), True),
],
)
def test_go_build_tool(go_tool: Go, macaron_path: str, mock_repo: str, expected_value: bool) -> None:
"""Test the Go build tool."""
base_dir = Path(__file__).parent
ctx = prepare_repo_for_testing(mock_repo, macaron_path, base_dir)
assert go_tool.is_detected(ctx.component.repository.fs_path) == expected_value
2 changes: 1 addition & 1 deletion tests/slsa_analyzer/build_tool/test_npm.py
Original file line number Diff line number Diff line change
@@ -35,7 +35,7 @@ def test_get_build_dirs(snapshot: list, npm_tool: NPM, mock_repo: Path) -> None:
],
)
def test_npm_build_tool(npm_tool: NPM, macaron_path: str, mock_repo: str, expected_value: bool) -> None:
"""Test the Maven build tool."""
"""Test the NPM build tool."""
base_dir = Path(__file__).parent
ctx = prepare_repo_for_testing(mock_repo, macaron_path, base_dir)
assert npm_tool.is_detected(ctx.component.repository.fs_path) == expected_value