generated from oracle/template-repo
-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: verify whether the reported repository can be linked back to th…
…e artifact (#873) Signed-off-by: Mohammad Abdollahpour <[email protected]>
- Loading branch information
1 parent
9ef9d50
commit 3854a85
Showing
17 changed files
with
946 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Copyright (c) 2024 - 2024, 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 package contains classes for repository verification.""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
# Copyright (c) 2024 - 2024, 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 code to verify whether a reported repository can be linked back to the artifact.""" | ||
import logging | ||
|
||
from macaron.repo_verifier.repo_verifier_base import ( | ||
RepositoryVerificationResult, | ||
RepositoryVerificationStatus, | ||
RepoVerifierBase, | ||
) | ||
from macaron.repo_verifier.repo_verifier_gradle import RepoVerifierGradle | ||
from macaron.repo_verifier.repo_verifier_maven import RepoVerifierMaven | ||
from macaron.slsa_analyzer.build_tool import BaseBuildTool, Gradle, Maven | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
def verify_repo( | ||
namespace: str | None, | ||
name: str, | ||
version: str, | ||
reported_repo_url: str, | ||
reported_repo_fs: str, | ||
build_tool: BaseBuildTool, | ||
) -> RepositoryVerificationResult: | ||
"""Verify whether the repository links back to the artifact. | ||
Parameters | ||
---------- | ||
namespace : str | None | ||
The namespace of the artifact. | ||
name : str | ||
The name of the artifact. | ||
version : str | ||
The version of the artifact. | ||
reported_repo_url : str | ||
The reported repository URL. | ||
reported_repo_fs : str | ||
The reported repository filesystem path. | ||
build_tool : BaseBuildTool | ||
The build tool used to build the package. | ||
Returns | ||
------- | ||
RepositoryVerificationResult | ||
The result of the repository verification | ||
""" | ||
# TODO: Add support for other build tools. | ||
verifier_map: dict[type[BaseBuildTool], type[RepoVerifierBase]] = { | ||
Maven: RepoVerifierMaven, | ||
Gradle: RepoVerifierGradle, | ||
# Poetry(): RepoVerifierPoetry, | ||
# Pip(): RepoVerifierPip, | ||
# Docker(): RepoVerifierDocker, | ||
# NPM(): RepoVerifierNPM, | ||
# Yarn(): RepoVerifierYarn, | ||
# Go(): RepoVerifierGo, | ||
} | ||
|
||
verifier_cls = verifier_map.get(type(build_tool)) | ||
if not verifier_cls: | ||
return RepositoryVerificationResult( | ||
status=RepositoryVerificationStatus.UNKNOWN, reason="unsupported_type", build_tool=build_tool | ||
) | ||
|
||
verifier = verifier_cls( | ||
namespace=namespace, | ||
name=name, | ||
version=version, | ||
reported_repo_url=reported_repo_url, | ||
reported_repo_fs=reported_repo_fs, | ||
) | ||
|
||
return verifier.verify_repo() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
# Copyright (c) 2024 - 2024, 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 base class and core data models for repository verification.""" | ||
import abc | ||
import logging | ||
import os | ||
from collections import deque | ||
from dataclasses import dataclass | ||
from enum import Enum | ||
from pathlib import Path | ||
|
||
from macaron.slsa_analyzer.build_tool import BaseBuildTool | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
def find_file_in_repo(root_dir: Path, filename: str) -> Path | None: | ||
"""Find the highest level file with a given name in a local repository. | ||
This function ignores certain paths that are not under the main source code directories. | ||
Parameters | ||
---------- | ||
root_dir : Path | ||
The root directory of the repository. | ||
filename : str | ||
The name of the file to search for. | ||
Returns | ||
------- | ||
Path | None | ||
The path to the file if it exists, otherwise | ||
""" | ||
# TODO: Consider using BaseBuildTool.get_build_dirs. | ||
# + Refactor 'get_build_dirs' to skip certain directories | ||
# that are most likely not part of the main codebase (e.g., sample). | ||
# + Need to find a way to look for other | ||
# files (e.g., gradle.properties) for the purpose of repo verification | ||
# without breaking the current logic of finding build directories. | ||
# + Add the capability to return the content/path of the file. | ||
if not os.path.isdir(root_dir): | ||
return None | ||
|
||
queue: deque[Path] = deque() | ||
queue.append(Path(root_dir)) | ||
while queue: | ||
current_dir = queue.popleft() | ||
|
||
# Don't look through non-main directories. | ||
if any( | ||
keyword in current_dir.name.lower() | ||
for keyword in ["test", "example", "sample", "doc", "demo", "spec", "mock"] | ||
): | ||
continue | ||
|
||
if Path(current_dir, filename).exists(): | ||
return Path(current_dir, filename) | ||
|
||
# Ignore symlinks to prevent potential infinite loop. | ||
sub_dirs = [Path(it) for it in current_dir.iterdir() if it.is_dir() and not it.is_symlink()] | ||
queue.extend(sub_dirs) | ||
|
||
return None | ||
|
||
|
||
class RepositoryVerificationStatus(str, Enum): | ||
"""A class to store the status of the repo verification.""" | ||
|
||
#: We found evidence to prove that the repository can be linked back to the publisher of the artifact. | ||
PASSED = "passed" | ||
|
||
#: We found evidence showing that the repository is not the publisher of the artifact. | ||
FAILED = "failed" | ||
|
||
#: We could not find any evidence to prove or disprove that the repository can be linked back to the artifact. | ||
UNKNOWN = "unknown" | ||
|
||
|
||
@dataclass(frozen=True) | ||
class RepositoryVerificationResult: | ||
"""A class to store the information about repository verification.""" | ||
|
||
#: The status of the repository verification. | ||
status: RepositoryVerificationStatus | ||
|
||
#: The reason for the verification result. | ||
reason: str | ||
|
||
#: The build tool used to build the package. | ||
build_tool: BaseBuildTool | ||
|
||
|
||
class RepoVerifierBase(abc.ABC): | ||
"""The base class to verify whether a reported repository links back to the artifact.""" | ||
|
||
@property | ||
@abc.abstractmethod | ||
def build_tool(self) -> BaseBuildTool: | ||
"""Define the build tool used to build the package.""" | ||
|
||
def __init__( | ||
self, | ||
namespace: str | None, | ||
name: str, | ||
version: str, | ||
reported_repo_url: str, | ||
reported_repo_fs: str, | ||
): | ||
"""Instantiate the class. | ||
Parameters | ||
---------- | ||
namespace : str | ||
The namespace of the artifact. | ||
name : str | ||
The name of the artifact. | ||
version : str | ||
The version of the artifact. | ||
reported_repo_url : str | ||
The URL of the repository reported by the publisher. | ||
reported_repo_fs : str | ||
The file system path of the reported repository. | ||
""" | ||
self.namespace = namespace | ||
self.name = name | ||
self.version = version | ||
self.reported_repo_url = reported_repo_url | ||
self.reported_repo_fs = reported_repo_fs | ||
|
||
@abc.abstractmethod | ||
def verify_repo(self) -> RepositoryVerificationResult: | ||
"""Verify whether the repository links back to the artifact. | ||
Returns | ||
------- | ||
RepositoryVerificationResult | ||
The result of the repository verification | ||
""" |
Oops, something went wrong.