From d8e6020cade92dd28469cab2f2283be906b6de8d Mon Sep 17 00:00:00 2001 From: Demolus13 Date: Sun, 28 Sep 2025 19:28:52 +0530 Subject: [PATCH 1/6] feat: add --policy flag in verify-policy command for example policies Signed-off-by: Demolus13 --- src/macaron/__main__.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/macaron/__main__.py b/src/macaron/__main__.py index c887a721e..7e1caf7df 100644 --- a/src/macaron/__main__.py +++ b/src/macaron/__main__.py @@ -204,6 +204,7 @@ def verify_policy(verify_policy_args: argparse.Namespace) -> int: show_prelude(verify_policy_args.database) return os.EX_OK + policy_content = None if verify_policy_args.file: if not os.path.isfile(verify_policy_args.file): logger.critical('The policy file "%s" does not exist.', verify_policy_args.file) @@ -211,7 +212,21 @@ def verify_policy(verify_policy_args: argparse.Namespace) -> int: with open(verify_policy_args.file, encoding="utf-8") as file: policy_content = file.read() + elif verify_policy_args.policy: + policy_dir = os.path.join(macaron.MACARON_PATH, "resources/policies/datalog") + available_policies = [policy[:-3] for policy in os.listdir(policy_dir) if policy.endswith(".dl")] + if verify_policy_args.policy not in available_policies: + logger.error( + "The policy %s is not available. Available policies are: %s", + verify_policy_args.policy, + available_policies, + ) + return os.EX_USAGE + policy_path = os.path.join(policy_dir, f"{verify_policy_args.policy}.dl") + with open(policy_path, encoding="utf-8") as file: + policy_content = file.read() + if policy_content: result = run_policy_engine(verify_policy_args.database, policy_content) vsa = generate_vsa(policy_content=policy_content, policy_result=result) # Retrieve the console handler previously configured via the access_handler. @@ -574,6 +589,7 @@ def main(argv: list[str] | None = None) -> None: vp_parser.add_argument("-d", "--database", required=True, type=str, help="Path to the database.") vp_group.add_argument("-f", "--file", type=str, help="Path to the Datalog policy.") + vp_group.add_argument("-p", "--policy", help="Example policy to run.") vp_group.add_argument("-s", "--show-prelude", action="store_true", help="Show policy prelude.") # Find the repo and commit of a passed PURL, or the commit of a passed PURL and repo. From cf533817dfd95130193e445affd46f4161a6db8e Mon Sep 17 00:00:00 2001 From: Demolus13 Date: Mon, 29 Sep 2025 21:26:08 +0530 Subject: [PATCH 2/6] feat: add policy template modification for standardization Signed-off-by: Demolus13 --- src/macaron/__main__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/macaron/__main__.py b/src/macaron/__main__.py index 7e1caf7df..4e02d0a9f 100644 --- a/src/macaron/__main__.py +++ b/src/macaron/__main__.py @@ -214,7 +214,7 @@ def verify_policy(verify_policy_args: argparse.Namespace) -> int: policy_content = file.read() elif verify_policy_args.policy: policy_dir = os.path.join(macaron.MACARON_PATH, "resources/policies/datalog") - available_policies = [policy[:-3] for policy in os.listdir(policy_dir) if policy.endswith(".dl")] + available_policies = [policy[:-12] for policy in os.listdir(policy_dir) if policy.endswith(".dl.template")] if verify_policy_args.policy not in available_policies: logger.error( "The policy %s is not available. Available policies are: %s", @@ -222,9 +222,11 @@ def verify_policy(verify_policy_args: argparse.Namespace) -> int: available_policies, ) return os.EX_USAGE - policy_path = os.path.join(policy_dir, f"{verify_policy_args.policy}.dl") + policy_path = os.path.join(policy_dir, f"{verify_policy_args.policy}.dl.template") with open(policy_path, encoding="utf-8") as file: policy_content = file.read() + if verify_policy_args.package_url: + policy_content = policy_content.replace("", verify_policy_args.package_url) if policy_content: result = run_policy_engine(verify_policy_args.database, policy_content) @@ -588,6 +590,7 @@ def main(argv: list[str] | None = None) -> None: vp_group = vp_parser.add_mutually_exclusive_group(required=True) vp_parser.add_argument("-d", "--database", required=True, type=str, help="Path to the database.") + vp_parser.add_argument("-purl", "--package-url", help="PackageURL for policy template.") vp_group.add_argument("-f", "--file", type=str, help="Path to the Datalog policy.") vp_group.add_argument("-p", "--policy", help="Example policy to run.") vp_group.add_argument("-s", "--show-prelude", action="store_true", help="Show policy prelude.") From 75e5c426f3d429b5105ad05cd580137ea36c72e3 Mon Sep 17 00:00:00 2001 From: Demolus13 Date: Tue, 7 Oct 2025 11:49:08 +0530 Subject: [PATCH 3/6] feat: add sample policies Signed-off-by: Demolus13 --- .../policies/datalog/check-github-actions.dl.template | 8 ++++++++ .../datalog/malware-detection-dependencies.dl.template | 10 ++++++++++ .../policies/datalog/malware-detection.dl.template | 9 +++++++++ 3 files changed, 27 insertions(+) create mode 100644 src/macaron/resources/policies/datalog/check-github-actions.dl.template create mode 100644 src/macaron/resources/policies/datalog/malware-detection-dependencies.dl.template create mode 100644 src/macaron/resources/policies/datalog/malware-detection.dl.template diff --git a/src/macaron/resources/policies/datalog/check-github-actions.dl.template b/src/macaron/resources/policies/datalog/check-github-actions.dl.template new file mode 100644 index 000000000..bfd0b04d3 --- /dev/null +++ b/src/macaron/resources/policies/datalog/check-github-actions.dl.template @@ -0,0 +1,8 @@ +#include "prelude.dl" + +Policy("github_actions_vulns", component_id, "GitHub Actions Vulnerability Detection") :- + check_passed(component_id, "mcn_githubactions_vulnerabilities_1"). + +apply_policy_to("github_actions_vulns", component_id) :- + is_component(component_id, purl), + match("@.*", purl). diff --git a/src/macaron/resources/policies/datalog/malware-detection-dependencies.dl.template b/src/macaron/resources/policies/datalog/malware-detection-dependencies.dl.template new file mode 100644 index 000000000..55c2adca1 --- /dev/null +++ b/src/macaron/resources/policies/datalog/malware-detection-dependencies.dl.template @@ -0,0 +1,10 @@ +#include "prelude.dl" + +Policy("check-dependencies", component_id, "Check the dependencies of component.") :- + transitive_dependency(component_id, dependency), + check_passed(component_id, "mcn_detect_malicious_metadata_1"), + check_passed(dependency, "mcn_detect_malicious_metadata_1"). + +apply_policy_to("check-dependencies", component_id) :- + is_component(component_id, purl), + match("@.*", purl). diff --git a/src/macaron/resources/policies/datalog/malware-detection.dl.template b/src/macaron/resources/policies/datalog/malware-detection.dl.template new file mode 100644 index 000000000..38bff2c4b --- /dev/null +++ b/src/macaron/resources/policies/datalog/malware-detection.dl.template @@ -0,0 +1,9 @@ +#include "prelude.dl" + +Policy("check-component", component_id, "Check component artifacts.") :- + check_passed(component_id, "mcn_detect_malicious_metadata_1"). + + +apply_policy_to("check-component", component_id) :- + is_component(component_id, purl), + match("@.*", purl). From a55e050426ee54d3cd315e5973af1a369db37b88 Mon Sep 17 00:00:00 2001 From: Demolus13 Date: Fri, 31 Oct 2025 12:19:31 +0530 Subject: [PATCH 4/6] chore: improve handling of policy templates and verify packageurl Signed-off-by: Demolus13 --- src/macaron/__main__.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/macaron/__main__.py b/src/macaron/__main__.py index 4e02d0a9f..21aeb45e1 100644 --- a/src/macaron/__main__.py +++ b/src/macaron/__main__.py @@ -213,8 +213,14 @@ def verify_policy(verify_policy_args: argparse.Namespace) -> int: with open(verify_policy_args.file, encoding="utf-8") as file: policy_content = file.read() elif verify_policy_args.policy: - policy_dir = os.path.join(macaron.MACARON_PATH, "resources/policies/datalog") - available_policies = [policy[:-12] for policy in os.listdir(policy_dir) if policy.endswith(".dl.template")] + policy_dir = os.path.join(macaron.MACARON_PATH, "resources", "policies", "datalog") + policy_suffix = ".dl" + template_suffix = f"{policy_suffix}.template" + available_policies = [ + os.path.splitext(policy)[0].replace(policy_suffix, "") + for policy in os.listdir(policy_dir) + if policy.endswith(template_suffix) + ] if verify_policy_args.policy not in available_policies: logger.error( "The policy %s is not available. Available policies are: %s", @@ -222,11 +228,15 @@ def verify_policy(verify_policy_args: argparse.Namespace) -> int: available_policies, ) return os.EX_USAGE - policy_path = os.path.join(policy_dir, f"{verify_policy_args.policy}.dl.template") + policy_path = os.path.join(policy_dir, f"{verify_policy_args.policy}{template_suffix}") with open(policy_path, encoding="utf-8") as file: policy_content = file.read() - if verify_policy_args.package_url: + try: + PackageURL.from_string(verify_policy_args.package_url) policy_content = policy_content.replace("", verify_policy_args.package_url) + except ValueError as err: + logger.error("The package url %s is not valid. Error: %s", verify_policy_args.package_url, err) + return os.EX_USAGE if policy_content: result = run_policy_engine(verify_policy_args.database, policy_content) From ec11626758167b5c9afc3b3071686c3d862cc982 Mon Sep 17 00:00:00 2001 From: Demolus13 Date: Sat, 1 Nov 2025 09:54:49 +0530 Subject: [PATCH 5/6] refactor: change -p (--policy) to -e (--existing-policy) for verify-policy command Signed-off-by: Demolus13 --- src/macaron/__main__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/macaron/__main__.py b/src/macaron/__main__.py index 21aeb45e1..a0901b07a 100644 --- a/src/macaron/__main__.py +++ b/src/macaron/__main__.py @@ -212,7 +212,7 @@ def verify_policy(verify_policy_args: argparse.Namespace) -> int: with open(verify_policy_args.file, encoding="utf-8") as file: policy_content = file.read() - elif verify_policy_args.policy: + elif verify_policy_args.existing_policy: policy_dir = os.path.join(macaron.MACARON_PATH, "resources", "policies", "datalog") policy_suffix = ".dl" template_suffix = f"{policy_suffix}.template" @@ -221,14 +221,14 @@ def verify_policy(verify_policy_args: argparse.Namespace) -> int: for policy in os.listdir(policy_dir) if policy.endswith(template_suffix) ] - if verify_policy_args.policy not in available_policies: + if verify_policy_args.existing_policy not in available_policies: logger.error( "The policy %s is not available. Available policies are: %s", - verify_policy_args.policy, + verify_policy_args.existing_policy, available_policies, ) return os.EX_USAGE - policy_path = os.path.join(policy_dir, f"{verify_policy_args.policy}{template_suffix}") + policy_path = os.path.join(policy_dir, f"{verify_policy_args.existing_policy}{template_suffix}") with open(policy_path, encoding="utf-8") as file: policy_content = file.read() try: @@ -602,7 +602,7 @@ def main(argv: list[str] | None = None) -> None: vp_parser.add_argument("-d", "--database", required=True, type=str, help="Path to the database.") vp_parser.add_argument("-purl", "--package-url", help="PackageURL for policy template.") vp_group.add_argument("-f", "--file", type=str, help="Path to the Datalog policy.") - vp_group.add_argument("-p", "--policy", help="Example policy to run.") + vp_group.add_argument("-e", "--existing-policy", help="Name of the existing policy to run.") vp_group.add_argument("-s", "--show-prelude", action="store_true", help="Show policy prelude.") # Find the repo and commit of a passed PURL, or the commit of a passed PURL and repo. From ff9fff73bbd75ba92fe33c8edfbde94aea7f7977 Mon Sep 17 00:00:00 2001 From: Demolus13 Date: Sun, 2 Nov 2025 22:39:05 +0530 Subject: [PATCH 6/6] chore: add simple tests for existing-policy flag Signed-off-by: Demolus13 --- tests/policy_engine/test_existing_policy.py | 61 +++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 tests/policy_engine/test_existing_policy.py diff --git a/tests/policy_engine/test_existing_policy.py b/tests/policy_engine/test_existing_policy.py new file mode 100644 index 000000000..359c07f1d --- /dev/null +++ b/tests/policy_engine/test_existing_policy.py @@ -0,0 +1,61 @@ +# Copyright (c) 2025 - 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 tests the existing-policy flag supported by the policy engine.""" + +import argparse +import os +from pathlib import Path +from unittest.mock import MagicMock, patch + +from macaron.__main__ import verify_policy + + +def test_verify_existing_policy_success(tmp_path: Path) -> None: + """When an existing policy is provided and package-url is valid, verify_policy returns EX_OK.""" + db_file = tmp_path / "macaron.db" + db_file.write_text("") + + # Use a MagicMock for the handler. + mock_handler = MagicMock() + + # Fake run_policy_engine and generate_vsa that returns a fixed result. + fake_run = MagicMock(return_value={"passed_policies": [["check-component"]], "failed_policies": []}) + fake_generate_vsa = MagicMock(return_value=None) + + # Fake PolicyReporter class: when called, returns an instance with generate method. + fake_policy_reporter_cls = MagicMock() + fake_policy_reporter_inst = MagicMock() + fake_policy_reporter_inst.generate.return_value = None + fake_policy_reporter_cls.return_value = fake_policy_reporter_inst + + with ( + patch("macaron.__main__.run_policy_engine", fake_run), + patch("macaron.__main__.generate_vsa", fake_generate_vsa), + patch("macaron.__main__.access_handler.get_handler", return_value=mock_handler), + patch("macaron.__main__.PolicyReporter", fake_policy_reporter_cls), + ): + policy_args = argparse.Namespace( + database=str(db_file), + show_prelude=False, + file=None, + existing_policy="malware-detection", + package_url="pkg:pypi/django", + ) + result = verify_policy(policy_args) + assert result == os.EX_OK + + +def test_verify_existing_policy_not_found(tmp_path: Path) -> None: + """Requesting a non-existent policy returns usage error.""" + db_file = tmp_path / "macaron.db" + db_file.write_text("") + policy_args = argparse.Namespace( + database=str(db_file), + show_prelude=False, + file=None, + existing_policy="no-such-policy", + package_url="pkg:pypi/django", + ) + result = verify_policy(policy_args) + assert result == os.EX_USAGE