Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
38 changes: 24 additions & 14 deletions src/macaron/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def __init__(self, *args: Any, verbose: bool = False, **kwargs: Any) -> None:
self.setLevel(logging.DEBUG)
self.command = ""
self.logs: list[str] = []
self.error_logs: list[str] = []
self.description_table = Table(show_header=False, box=None)
self.description_table_content: dict[str, str | Status] = {
"Package URL:": Status("[green]Processing[/]"),
Expand Down Expand Up @@ -118,6 +119,7 @@ def emit(self, record: logging.LogRecord) -> None:

if record.levelno >= logging.ERROR:
self.logs.append(f"[red][ERROR][/red] {log_time} {msg}")
self.error_logs.append(f"[red][ERROR][/red] {log_time} {msg}")
elif record.levelno >= logging.WARNING:
self.logs.append(f"[yellow][WARNING][/yellow] {log_time} {msg}")
else:
Expand Down Expand Up @@ -386,10 +388,17 @@ def make_layout(self) -> Group:
A rich Group object containing the layout for the live console display.
"""
layout: list[RenderableType] = []
if self.error_logs:
error_log_panel = Panel(
"\n".join(self.error_logs),
title="Error Logs",
title_align="left",
border_style="red",
)
layout = layout + [error_log_panel]
if self.command == "analyze":
layout = layout + [Rule(" DESCRIPTION", align="left")]
if self.description_table.row_count > 0:
layout = layout + ["", self.description_table]
layout = layout + [Rule(" DESCRIPTION", align="left"), "", self.description_table]
if self.progress_table.row_count > 0:
layout = layout + ["", self.progress, "", self.progress_table]
if self.failed_checks_table.row_count > 0:
Expand Down Expand Up @@ -418,25 +427,25 @@ def make_layout(self) -> Group:
]
elif self.command == "verify-policy":
if self.policy_summary_table.row_count > 0:
if self.components_violates_table.row_count > 0:
if self.components_satisfy_table.row_count > 0:
layout = layout + [
"[bold red] Components Violate Policy[/]",
self.components_violates_table,
"[bold green] Components Satisfy Policy[/]",
self.components_satisfy_table,
]
else:
layout = layout + [
"[bold red] Components Violate Policy[/] [white not italic]None[/]",
"[bold green] Components Satisfy Policy[/] [white not italic]None[/]",
]
if self.components_satisfy_table.row_count > 0:
if self.components_violates_table.row_count > 0:
layout = layout + [
"",
"[bold green] Components Satisfy Policy[/]",
self.components_satisfy_table,
"[bold red] Components Violate Policy[/]",
self.components_violates_table,
]
else:
layout = layout + [
"",
"[bold green] Components Satisfy Policy[/] [white not italic]None[/]",
"[bold red] Components Violate Policy[/] [white not italic]None[/]",
]
layout = layout + ["", self.policy_summary_table]
if self.verification_summary_attestation:
Expand All @@ -448,10 +457,11 @@ def make_layout(self) -> Group:
"[bold blue]Verification Summary Attestation[/]",
self.verification_summary_attestation,
)
vsa_table.add_row(
"[bold blue]Decode and Inspect the Content[/]",
f"cat {self.verification_summary_attestation} | jq -r [white]'.payload'[/] | base64 -d | jq",
)
if self.verification_summary_attestation != "No VSA generated.":
vsa_table.add_row(
"[bold blue]Decode and Inspect the Content[/]",
f"cat {self.verification_summary_attestation} | jq -r [white]'.payload'[/] | base64 -d | jq",
)

layout = layout + [vsa_table]
elif self.command == "find-source":
Expand Down
24 changes: 12 additions & 12 deletions src/macaron/dependency_analyzer/cyclonedx.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ def add_latest_version(
):
latest_deps[key] = item
except ValueError as error:
logger.error("Could not parse dependency version number: %s", error)
logger.debug("Could not parse dependency version number: %s", error)

@staticmethod
def to_configs(resolved_deps: dict[str, DependencyInfo]) -> list[Configuration]:
Expand Down Expand Up @@ -344,7 +344,7 @@ def resolve_dependencies(main_ctx: Any, sbom_path: str, recursive: bool = False)
# We allow dependency analysis if SBOM is provided but no repository is found.
dep_analyzer = build_tool.get_dep_analyzer()
except DependencyAnalyzerError as error:
logger.error("Unable to find a dependency analyzer for %s: %s", build_tool.name, error)
logger.debug("Unable to find a dependency analyzer for %s: %s", build_tool.name, error)
return {}

if isinstance(dep_analyzer, NoneDependencyAnalyzer):
Expand Down Expand Up @@ -381,11 +381,11 @@ def resolve_dependencies(main_ctx: Any, sbom_path: str, recursive: bool = False)
log_file.write(analyzer_output.stdout.decode("utf-8"))

except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as error:
logger.error(error)
logger.debug(error)
with open(log_path, mode="a", encoding="utf-8") as log_file:
log_file.write(error.output.decode("utf-8"))
except FileNotFoundError as error:
logger.error(error)
logger.debug(error)

# We collect the generated SBOM as a best effort, even if the build exits with errors.
# TODO: add improvements to help the SBOM build succeed as much as possible.
Expand Down Expand Up @@ -437,12 +437,12 @@ def get_root_component(self, root_bom_path: Path) -> CDXComponent | None:
try:
root_bom = deserialize_bom_json(root_bom_path)
except CycloneDXParserError as error:
logger.error(error)
logger.debug(error)
return None
try:
return root_bom.metadata.component
except AttributeError as error:
logger.error(error)
logger.debug(error)

return None

Expand Down Expand Up @@ -482,7 +482,7 @@ def _is_target_cmp(cmp: CDXComponent | None) -> bool:
if _is_target_cmp(root_bom.metadata.component):
return root_bom.metadata.component
if root_bom.metadata.component:
logger.error(
logger.debug(
(
"The analysis target %s and the metadata component %s in the BOM file do not match."
" Please fix the PURL input and try again."
Expand All @@ -494,7 +494,7 @@ def _is_target_cmp(cmp: CDXComponent | None) -> bool:
)
return None

logger.error(
logger.debug(
"Unable to find the analysis target %s in the BOM file. Please fix the PURL input and try again.",
target_component.purl,
)
Expand Down Expand Up @@ -528,11 +528,11 @@ def get_dep_components(
try:
root_bom = deserialize_bom_json(root_bom_path)
except CycloneDXParserError as error:
logger.error(error)
logger.debug(error)
return

if root_bom.components is None:
logger.error("The BOM file at %s misses components.", str(root_bom_path))
logger.debug("The BOM file at %s misses components.", str(root_bom_path))
return

dependencies: list[CDXDependency] = []
Expand All @@ -559,7 +559,7 @@ def get_dep_components(
try:
child_bom_objects.append(deserialize_bom_json(child_path))
except CycloneDXParserError as error:
logger.error(error)
logger.debug(error)
continue

for bom in child_bom_objects:
Expand Down Expand Up @@ -663,7 +663,7 @@ def convert_components_to_artifacts(
with open(os.path.join(global_config.output_path, "sbom_debug.json"), "w", encoding="utf8") as debug_file:
debug_file.write(json.dumps(all_versions, indent=4))
except OSError as error:
logger.error(error)
logger.debug(error)

return latest_deps

Expand Down
10 changes: 5 additions & 5 deletions src/macaron/parsers/yaml/loader.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2022 - 2022, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2022 - 2025, 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 loader for YAML files."""
Expand Down Expand Up @@ -77,10 +77,10 @@ def validate_yaml_data(cls, schema: Schema, data: list) -> bool:
yamale.validate(schema, data)
return True
except yamale.YamaleError as error:
logger.error("Yaml data validation failed.")
logger.debug("Yaml data validation failed.")
for result in error.results:
for err_str in result.errors:
logger.error("\t%s", err_str)
logger.debug("\t%s", err_str)
return False

@classmethod
Expand All @@ -105,11 +105,11 @@ def load(cls, path: os.PathLike | str, schema: Schema = None) -> Any:
logger.info("Loading yaml content for %s", path)
loaded_data = YamlLoader._load_yaml_content(path=path)
if not loaded_data:
logger.error("Error while loading the config yaml file %s.", path)
logger.debug("Error while loading the config yaml file %s.", path)
return None

if schema and not YamlLoader.validate_yaml_data(schema, loaded_data):
logger.error("The yaml content in %s is invalid according to the schema.", path)
logger.debug("The yaml content in %s is invalid according to the schema.", path)
return None

result = None
Expand Down
10 changes: 5 additions & 5 deletions src/macaron/provenance/provenance_finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,15 +189,15 @@ def find_npm_provenance(purl: PackageURL, registry: NPMRegistry) -> list[Provena
# Load the provenance file (provenance attestation).
provenance_payload = load_provenance_payload(download_path)
except LoadIntotoAttestationError as error:
logger.error("Error while loading provenance attestation: %s", error)
logger.debug("Error while loading provenance attestation: %s", error)
return []

signed_download_path = f"{download_path}.signed"
try:
# Load the other npm provenance file (publish attestation).
publish_payload = load_provenance_payload(signed_download_path)
except LoadIntotoAttestationError as error:
logger.error("Error while loading publish attestation: %s", error)
logger.debug("Error while loading publish attestation: %s", error)
return [ProvenanceAsset(provenance_payload, npm_provenance_asset.name, npm_provenance_asset.url)]

return [
Expand All @@ -206,7 +206,7 @@ def find_npm_provenance(purl: PackageURL, registry: NPMRegistry) -> list[Provena
]

except OSError as error:
logger.error("Error while storing provenance in the temporary directory: %s", error)
logger.debug("Error while storing provenance in the temporary directory: %s", error)
return []


Expand Down Expand Up @@ -331,7 +331,7 @@ def find_pypi_provenance(purl: PackageURL) -> list[ProvenanceAsset]:
payload.verified = verified
return [ProvenanceAsset(payload, purl.name, url)]
except LoadIntotoAttestationError as load_error:
logger.error("Error while loading provenance: %s", load_error)
logger.debug("Error while loading provenance: %s", load_error)
return []


Expand Down Expand Up @@ -484,7 +484,7 @@ def download_provenances_from_ci_service(ci_info: CIInfo, download_path: str) ->
try:
payload = load_provenance_payload(provenance_filepath)
except LoadIntotoAttestationError as error:
logger.error("Error logging provenance: %s", error)
logger.debug("Error logging provenance: %s", error)
continue

# Add the provenance file.
Expand Down
10 changes: 5 additions & 5 deletions src/macaron/repo_finder/repo_finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,11 +372,11 @@ def get_latest_purl_if_different(purl: PackageURL) -> PackageURL | None:

latest_version_purl, _ = DepsDevRepoFinder.get_latest_version(no_version_purl)
if not latest_version_purl:
logger.error("Latest version PURL could not be found.")
logger.debug("Latest version PURL could not be found.")
return None

if latest_version_purl == purl:
logger.error("Latest version PURL is the same as the current.")
logger.debug("Latest version PURL is the same as the current.")
return None

logger.debug("Found new version of PURL: %s", latest_version_purl)
Expand All @@ -400,11 +400,11 @@ def get_latest_repo_if_different(latest_version_purl: PackageURL, original_repo:
"""
latest_repo, _ = find_repo(latest_version_purl, False)
if not latest_repo:
logger.error("Could not find repository from latest PURL: %s", latest_version_purl)
logger.debug("Could not find repository from latest PURL: %s", latest_version_purl)
return ""

if check_repo_urls_are_equivalent(original_repo, latest_repo):
logger.error(
logger.debug(
"Repository from latest PURL is equivalent to original repository: %s ~= %s",
latest_repo,
original_repo,
Expand Down Expand Up @@ -470,7 +470,7 @@ def prepare_repo(
logger.info("The path to repo %s is a remote path.", repo_path)
resolved_remote_path = get_remote_vcs_url(repo_path)
if not resolved_remote_path:
logger.error("The provided path to repo %s is not a valid remote path.", repo_path)
logger.debug("The provided path to repo %s is not a valid remote path.", repo_path)
return None, commit_finder_outcome

git_service = get_git_service(resolved_remote_path)
Expand Down
2 changes: 1 addition & 1 deletion src/macaron/slsa_analyzer/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -673,7 +673,7 @@ def add_repository(self, branch_name: str | None, git_obj: Git) -> Repository |
# We only allow complete_name's length to be 2 or 3 because we need to construct PURL
# strings using the complete_name, i.e., type/namespace/name@commitsha
if (parts_len := len(Path(complete_name).parts)) < 2 or parts_len > 3:
logger.error("The repository path %s is not valid.", complete_name)
logger.debug("The repository path %s is not valid.", complete_name)
return None

repository = Repository(
Expand Down
4 changes: 2 additions & 2 deletions src/macaron/slsa_analyzer/build_tool/poetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,11 @@ def is_detected(self, repo_path: str) -> bool:
if ("tool" in data) and ("poetry" in data["tool"]):
return True
except tomllib.TOMLDecodeError:
logger.error("Failed to read the %s file: invalid toml file.", conf)
logger.debug("Failed to read the %s file: invalid toml file.", conf)
return False
return False
except FileNotFoundError:
logger.error("Failed to read the %s file.", conf)
logger.debug("Failed to read the %s file.", conf)
return False

return False
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ def run_check(self, ctx: AnalyzeContext) -> CheckResultData:
try:
build_def = ProvenancePredicate.find_build_def(prov_payload.statement)
except ProvenanceError as error:
logger.error(error)
logger.debug(error)
return CheckResultData(result_tables=[], result_type=CheckResultType.FAILED)
prov_workflow, prov_trigger_run = build_def.get_build_invocation(prov_payload.statement)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ def build_call_graph_from_path(root: BaseNode, workflow_path: str, repo_path: st
try:
parsed_obj: Workflow = parse_action(workflow_path)
except ParseError as error:
logger.error("Unable to parse GitHub Actions at the target %s: %s", repo_path, error)
logger.debug("Unable to parse GitHub Actions at the target %s: %s", repo_path, error)
raise ParseError from error

# Add internal workflows.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2022 - 2024, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2022 - 2025, 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 analyzes GitHub Actions CI."""
Expand Down Expand Up @@ -186,13 +186,13 @@ def has_latest_run_passed(

workflow_data = self.api_client.get_repo_workflow_data(repo_full_name, workflow)
if not workflow_data:
logger.error("Cannot find data of workflow %s.", workflow)
logger.debug("Cannot find data of workflow %s.", workflow)
return ""

try:
workflow_id = workflow_data["id"]
except KeyError:
logger.error("Cannot get unique ID of workflow %s.", workflow)
logger.debug("Cannot get unique ID of workflow %s.", workflow)
return ""

logger.info("The unique ID of workflow %s is %s", workflow, workflow_id)
Expand Down Expand Up @@ -540,7 +540,7 @@ def search_for_workflow_run(
full_name, branch_name=branch_name, created_after=created_after, page=query_page
)
except KeyError:
logger.error("Error while reading run data. Skipping ...")
logger.debug("Error while reading run data. Skipping ...")
continue

return {}
Expand Down
2 changes: 1 addition & 1 deletion src/macaron/slsa_analyzer/git_url.py
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,7 @@ def get_remote_origin_of_local_repo(git_obj: Git) -> str:
try:
url_parse_result = urllib.parse.urlparse(remote_origin_path)
except ValueError:
logger.error("Error occurs while processing the remote URL of repo %s.", git_obj.project_name)
logger.debug("Error occurs while processing the remote URL of repo %s.", git_obj.project_name)
return ""

_, _, hostname = url_parse_result.netloc.rpartition("@")
Expand Down
2 changes: 1 addition & 1 deletion src/macaron/slsa_analyzer/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def _add_relationship_entry(self, check_id: str, relationship: tuple[str, CheckR
else:
existed_label = parent.get(check_id)
if existed_label:
logger.error(
logger.debug(
"The relationship between %s and parent %s has been defined with label %s.",
check_id,
parent_id,
Expand Down
Loading
Loading