Skip to content
Open
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
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ repos:
language: python
files: ^src/macaron/|^tests/
types: [text, python]
exclude: ^tests/malware_analyzer/pypi/resources/sourcecode_samples.*
pass_filenames: false
args: [--show-traceback, --config-file, pyproject.toml]

# Enable a whole bunch of useful helper hooks, too.
Expand Down
18 changes: 15 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ actions = [
]
dev = [
"flit >=3.2.0,<4.0.0",
"mypy >=1.19.1,<1.20",
"mypy >=2.0.0,<3.0.0",
"types-pyyaml >=6.0.4,<7.0.0",
"types-requests >=2.25.6,<3.0.0",
"types-jsonschema >=4.22.0,<5.0.0",
Expand Down Expand Up @@ -169,6 +169,18 @@ exclude = [

# https://mypy.readthedocs.io/en/stable/config_file.html#using-a-pyproject-toml
[tool.mypy]
files = [
"src/macaron/",
"tests/",
]
exclude = [
"tests/malware_analyzer/pypi/resources/sourcecode_samples/",
]
mypy_path = [
"src/",
]
num_workers = 4
explicit_package_bases = true
show_error_codes = true
show_column_numbers = true
check_untyped_defs = true
Expand All @@ -187,13 +199,13 @@ disallow_untyped_decorators = true

[[tool.mypy.overrides]]
module = [
"pytest.*", # https://github.com/pytest-dev/pytest/issues/7469
"pydriller.*",
"gitdb.*",
"yamale.*",
"problog.*",
]
ignore_missing_imports = true
# ignore_missing_imports = true
follow_untyped_imports = true


# https://pylint.pycqa.org/en/latest/user_guide/configuration/index.html
Expand Down
4 changes: 2 additions & 2 deletions src/macaron/parsers/actionparser.py
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 - 2026, 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 parser for GitHub Actions Workflow files."""
Expand Down Expand Up @@ -37,7 +37,7 @@ def parse(workflow_path: str) -> Workflow:
When parsing fails with errors.
"""
try:
parse_result = yamale.make_data(workflow_path, parser="ruamel")
parse_result = yamale.make_data(workflow_path, parser="ruamel") # type: ignore[no-untyped-call]
except OSError as error:
raise ParseError("Cannot parse GitHub Workflow: " + workflow_path) from error

Expand Down
8 changes: 4 additions & 4 deletions src/macaron/parsers/yaml/loader.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2022 - 2025, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2022 - 2026, 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 @@ -38,7 +38,7 @@ def _load_yaml_content(path: os.PathLike | str) -> list:
"""
try:
logger.debug("Loading yaml from file %s", path)
return list(yamale.make_data(path))
return list(yamale.make_data(path)) # type: ignore[no-untyped-call]
except YAMLError as error:
abs_path = os.path.abspath(path)

Expand Down Expand Up @@ -74,7 +74,7 @@ def validate_yaml_data(cls, schema: Schema, data: list) -> bool:
"""
try:
logger.debug("Validate data %s with schema %s.", str(data), str(schema.dict))
yamale.validate(schema, data)
yamale.validate(schema, data) # type: ignore[no-untyped-call]
return True
except yamale.YamaleError as error:
logger.debug("Yaml data validation failed.")
Expand All @@ -84,7 +84,7 @@ def validate_yaml_data(cls, schema: Schema, data: list) -> bool:
return False

@classmethod
def load(cls, path: os.PathLike | str, schema: Schema = None) -> Any:
def load(cls, path: os.PathLike | str, schema: Schema | None = None) -> Any:
"""Load and return a Python object from a yaml file.

If ``schema`` is provided. This method will validate the loaded content against the
Expand Down
4 changes: 2 additions & 2 deletions src/macaron/repo_finder/repo_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,8 @@ def get_repo_tags(git_obj: Git) -> dict[str, str]:

# Retrieve tags using a Git subprocess.
repository_path = git_obj.repo.working_tree_dir
if not os.path.isdir(repository_path):
logger.debug("")
if not repository_path or not os.path.isdir(repository_path):
logger.debug("Invalid repository path: %s", repository_path)
return {}
try:
result = subprocess.run(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,10 +190,10 @@ def evaluate_heuristic_results(
problog_code = f"{facts}\n\n{self.malware_rules_problog_model}"
logger.debug("Problog model used for evaluation:\n %s", problog_code)

problog_model = PrologString(problog_code)
problog_results: dict[Term, float] = get_evaluatable().create_from(problog_model).evaluate()
problog_model = PrologString(problog_code) # type: ignore[no-untyped-call]
problog_results: dict[Term, float] = get_evaluatable().create_from(problog_model).evaluate() # type: ignore[no-untyped-call]

confidence = problog_results.pop(Term(self.problog_result_access), 0.0)
confidence = problog_results.pop(Term(self.problog_result_access), 0.0) # type: ignore[no-untyped-call]
if confidence > 0: # a rule was triggered
for term, conf in problog_results.items():
if term.args:
Expand Down
2 changes: 1 addition & 1 deletion src/macaron/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ def check_rate_limit(response: Response) -> None:
remains = int(response.headers["X-RateLimit-Remaining"]) if "X-RateLimit-Remaining" in response.headers else 2

if remains <= 1:
rate_limit_reset = response.headers.get("X-RateLimit-Reset", default="")
rate_limit_reset = response.headers.get("X-RateLimit-Reset", "")

if not rate_limit_reset:
return
Expand Down
8 changes: 4 additions & 4 deletions tests/parsers/yaml/test_yaml_loader.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2022 - 2025, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2022 - 2026, 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 test the yaml loader functions."""
Expand Down Expand Up @@ -40,21 +40,21 @@ def test_validate_yaml_data(self) -> None:
"""Test the validate yaml data method."""
# We are not testing the behavior of yamale methods
# so the schema and data can be empty.
mock_schema = Schema({})
mock_schema = Schema({}) # type: ignore[no-untyped-call]
mock_data: list = []

# No errors
with patch("yamale.validate", return_value=[]):
assert YamlLoader.validate_yaml_data(mock_schema, mock_data)

# Errors exist
with patch("yamale.validate", side_effect=YamaleError(results=[])):
with patch("yamale.validate", side_effect=YamaleError(results=[])): # type: ignore[no-untyped-call]
assert not YamlLoader.validate_yaml_data(mock_schema, mock_data)

def test_load(self) -> None:
"""Test the load method of YamlLoader."""
schema_file = os.path.join(self.RESOURCES_DIR, "schema.yaml")
schema: Schema = yamale.make_schema(schema_file)
schema: Schema = yamale.make_schema(schema_file) # type: ignore[no-untyped-call]

assert YamlLoader.load(os.path.join(self.RESOURCES_DIR, "invalid.yaml")) == {None: None}
assert not YamlLoader.load(os.path.join(self.RESOURCES_DIR, "invalid.yaml"), schema)
Expand Down
19 changes: 9 additions & 10 deletions tests/repo_finder/test_commit_finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@
import os
import re
import shutil
from typing import Any

import hypothesis
import pytest
from hypothesis import given, settings
from hypothesis.strategies import DataObject, data, text
from packageurl import PackageURL
from pydriller.git import Git
from pydriller.git import Git, GitCommit

from macaron.repo_finder import commit_finder
from macaron.repo_finder.commit_finder import AbstractPurlType, determine_optional_suffix_index
Expand Down Expand Up @@ -117,24 +116,24 @@ def mocked_repo_() -> Git:


@pytest.fixture(name="mocked_repo_commit")
def mocked_repo_commit_(mocked_repo: Git) -> Any:
def mocked_repo_commit_(mocked_repo: Git) -> GitCommit:
"""Add a commit to the mocked repository."""
return mocked_repo.repo.index.commit(message="Commit_0")


@pytest.fixture(name="mocked_repo_empty_commit")
def mocked_repo_empty_commit_(mocked_repo: Git) -> Any:
def mocked_repo_empty_commit_(mocked_repo: Git) -> GitCommit:
"""Add an empty commit to the mocked repository."""
return mocked_repo.repo.index.commit(message="Empty_Commit")


@pytest.fixture(name="mocked_repo_expanded")
def mocked_repo_expanded_(mocked_repo: Git, mocked_repo_commit: Any, mocked_repo_empty_commit: Any) -> Any:
def mocked_repo_expanded_(mocked_repo: Git, mocked_repo_commit: GitCommit, mocked_repo_empty_commit: GitCommit) -> Git:
"""Add tags to the mocked repository."""
mocked_repo.repo.create_tag("4.5", mocked_repo_commit.hexsha)

# Create a tag from a tree.
mocked_repo.repo.create_tag("1.0", ref=mocked_repo.repo.heads.master.commit.tree)
mocked_repo.repo.create_tag("1.0", mocked_repo.repo.heads.master.commit.tree.hexsha)

# Add a tag with unicode version.
mocked_repo.repo.create_tag(UNICODE_VERSION, mocked_repo_commit.hexsha)
Expand Down Expand Up @@ -200,7 +199,7 @@ def test_commit_finder_tag_failure(
)
def test_commit_finder_success_commit(
mocked_repo_expanded: Git,
mocked_repo_commit: Any,
mocked_repo_commit: GitCommit,
purl_string: str,
) -> None:
"""Test Commit Finder on mocked repository that should match valid PURLs."""
Expand All @@ -223,15 +222,15 @@ def test_commit_finder_success_commit(
],
)
def test_commit_finder_success_empty_commit(
mocked_repo_expanded: Git, mocked_repo_empty_commit: Any, purl_string: str
mocked_repo_expanded: Git, mocked_repo_empty_commit: GitCommit, purl_string: str
) -> None:
"""Test Commit Finder on mocked repository that should match value PURLs."""
match, outcome = commit_finder.find_commit(mocked_repo_expanded, PackageURL.from_string(purl_string))
assert match == mocked_repo_empty_commit.hexsha
assert outcome == CommitFinderInfo.MATCHED


def test_commit_finder_repo_purl_success(mocked_repo_expanded: Git, mocked_repo_commit: Any) -> None:
def test_commit_finder_repo_purl_success(mocked_repo_expanded: Git, mocked_repo_commit: GitCommit) -> None:
"""Test Commit Finder on mocked repository using a repo type PURL."""
match, outcome = commit_finder.find_commit(
mocked_repo_expanded, PackageURL.from_string(f"pkg:github/apache/maven@{mocked_repo_commit.hexsha}")
Expand All @@ -254,7 +253,7 @@ def test_commit_finder_optional_suffixes(version: str, parts: list, expected: in
assert determine_optional_suffix_index(version, parts) == expected


def test_get_repo_tags(mocked_repo_empty_commit: Any) -> None:
def test_get_repo_tags(mocked_repo_empty_commit: GitCommit) -> None:
"""Test the get repo tags utils function."""
# Create the repository object.
repo = Git(os.path.join(REPO_DIR))
Expand Down
7 changes: 4 additions & 3 deletions tests/slsa_analyzer/git_service/test_gitlab.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Copyright (c) 2023 - 2024, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2023 - 2026, 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/.

"""Tests for the GitLab git service."""

import collections.abc
import os
from pathlib import Path
from unittest import mock
Expand Down Expand Up @@ -117,7 +118,7 @@ def test_self_hosted_gitlab_without_env_set(tmp_path: Path) -> None:


@pytest.fixture(name="self_hosted_gitlab")
def self_hosted_gitlab_repo_fixture(request: pytest.FixtureRequest) -> Git:
def self_hosted_gitlab_repo_fixture(request: pytest.FixtureRequest) -> collections.abc.Iterable[Git]:
"""Create a mock GitLab self_hosted repo.

This fixture expects ONE parameter of type STR which will be the origin remote url initialized for the self_hosted
Expand Down Expand Up @@ -153,7 +154,7 @@ def self_hosted_gitlab_repo_fixture(request: pytest.FixtureRequest) -> Git:

yield gitlab_repo

gitlab_repo.clear()
gitlab_repo.clear() # type: ignore[no-untyped-call]


# The indirect parameter is used to note that ``self_hosted_gitlab`` should be passed to the ``self_hosted_gitlab``
Expand Down
4 changes: 2 additions & 2 deletions tests/slsa_analyzer/mock_git_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ def initiate_repo(repo_path: str | os.PathLike, git_init_options: dict | None =
os.makedirs(repo_path)

try:
git_wrapper = Git(repo_path)
git_wrapper = Git(str(repo_path))
return git_wrapper
except GitError:
# No git repo at repo_path.
git.Repo.init(repo_path, **git_init_options)
return Git(repo_path)
return Git(str(repo_path))


def commit_files(git_wrapper: Git, file_names: list) -> bool:
Expand Down