Skip to content

Commit 118cad3

Browse files
authored
refactor: refactor interface of base check (#513)
Refactor BaseCheck.run_check to return all result info as part of return value, rather than passing in and mutating a CheckResult object containing other data irrelevant to run_check. Refactor SLSA requirement status information in AnalyzeContext to avoid unnecessary replication of generic info about a SLSA requirement, which can be looked up by ReqName when needed instead. Signed-off-by: Nicholas Allen <nicholas.allen@oracle.com>
1 parent 542056a commit 118cad3

32 files changed

+665
-571
lines changed

src/macaron/output_reporter/results.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from macaron.slsa_analyzer.checks.check_result import CheckResultType
1515
from macaron.slsa_analyzer.levels import SLSALevels
1616
from macaron.slsa_analyzer.registry import registry
17-
from macaron.slsa_analyzer.slsa_req import ReqName
17+
from macaron.slsa_analyzer.slsa_req import BUILD_REQ_DESC, ReqName
1818

1919

2020
class DepSummary(TypedDict):
@@ -119,7 +119,7 @@ def get_dict(self) -> dict:
119119
has_passing_check = False
120120
if self.context:
121121
for res in self.context.check_results.values():
122-
if res["result_type"] == CheckResultType.PASSED:
122+
if res.result.result_type == CheckResultType.PASSED:
123123
has_passing_check = True
124124
break
125125

@@ -156,9 +156,9 @@ def get_dep_summary(self) -> DepSummary:
156156
result["unique_dep_repos"] += 1
157157
if dep_record.context:
158158
for check_result in dep_record.context.check_results.values():
159-
if check_result["result_type"] == CheckResultType.PASSED:
159+
if check_result.result.result_type == CheckResultType.PASSED:
160160
for entry in result["checks_summary"]:
161-
if entry["check_id"] == check_result["check_id"]:
161+
if entry["check_id"] == check_result.check.check_id:
162162
entry["num_deps_pass"] += 1
163163
case SCMStatus.DUPLICATED_SCM:
164164
result["analyzed_deps"] += 1
@@ -291,9 +291,10 @@ def __str__(self) -> str:
291291
)
292292

293293
slsa_req_mesg: dict[SLSALevels, list[str]] = {level: [] for level in SLSALevels if level != SLSALevels.LEVEL0}
294-
for req in main_ctx.ctx_data.values():
295-
if req.min_level_required != SLSALevels.LEVEL0 and req.is_addressed:
296-
message = f"{req.name.capitalize()}: " + ("PASSED" if req.is_pass else "FAILED")
294+
for req_name, req_status in main_ctx.ctx_data.items():
295+
req = BUILD_REQ_DESC[req_name]
296+
if req.min_level_required != SLSALevels.LEVEL0 and req_status.is_addressed:
297+
message = f"{req.name.capitalize()}: " + ("PASSED" if req_status.is_pass else "FAILED")
297298

298299
if ctx_list:
299300
# Get the fail count for dependencies.

src/macaron/slsa_analyzer/analyze_context.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from macaron.slsa_analyzer.git_service.base_git_service import NoneGitService
1717
from macaron.slsa_analyzer.levels import SLSALevels
1818
from macaron.slsa_analyzer.provenance.expectations.expectation import Expectation
19-
from macaron.slsa_analyzer.slsa_req import ReqName, SLSAReq, get_requirements_dict
19+
from macaron.slsa_analyzer.slsa_req import ReqName, SLSAReqStatus, create_requirement_status_dict
2020
from macaron.slsa_analyzer.specs.build_spec import BuildSpec
2121
from macaron.slsa_analyzer.specs.ci_spec import CIInfo
2222
from macaron.slsa_analyzer.specs.package_registry_spec import PackageRegistryInfo
@@ -64,7 +64,7 @@ def __init__(
6464
The output dir.
6565
"""
6666
self.component = component
67-
self.ctx_data: dict[ReqName, SLSAReq] = get_requirements_dict()
67+
self.ctx_data: dict[ReqName, SLSAReqStatus] = create_requirement_status_dict()
6868

6969
self.slsa_level = SLSALevels.LEVEL0
7070
# Indicate whether this repo fully reach a level or
@@ -176,14 +176,14 @@ def get_slsa_level_table(self) -> SLSALevel:
176176

177177
def get_dict(self) -> dict:
178178
"""Return the dictionary representation of the AnalyzeContext instance."""
179-
_sorted_on_id = sorted(self.check_results.values(), key=lambda item: item["check_id"])
179+
_sorted_on_id = sorted(self.check_results.values(), key=lambda item: item.check.check_id)
180180
# Remove result_tables since we don't have a good json representation for them.
181181
sorted_on_id = []
182182
for res in _sorted_on_id:
183-
# res is CheckResult(TypedDict)
184-
res_copy: dict = dict(res.copy())
185-
res_copy.pop("result_tables")
186-
sorted_on_id.append(res_copy)
183+
# res is CheckResult
184+
res_dict: dict = dict(res.get_summary())
185+
res_dict.pop("result_tables")
186+
sorted_on_id.append(res_dict)
187187
sorted_results = sorted(sorted_on_id, key=lambda item: item["result_type"], reverse=True)
188188
check_summary = {
189189
result_type.value: len(result_list) for result_type, result_list in self.get_check_summary().items()
@@ -219,7 +219,7 @@ def get_check_summary(self) -> dict[CheckResultType, list[CheckResult]]:
219219
result: dict[CheckResultType, list[CheckResult]] = {result_type: [] for result_type in CheckResultType}
220220

221221
for check_result in self.check_results.values():
222-
match check_result["result_type"]:
222+
match check_result.result.result_type:
223223
case CheckResultType.PASSED:
224224
result[CheckResultType.PASSED].append(check_result)
225225
case CheckResultType.SKIPPED:
@@ -243,8 +243,8 @@ def __str__(self) -> str:
243243
output = "".join(
244244
[
245245
output,
246-
f"Check {check_result['check_id']}: {check_result['check_description']}\n",
247-
f"\t{check_result['result_type'].value}\n",
246+
f"Check {check_result.check.check_id}: {check_result.check.check_description}\n",
247+
f"\t{check_result.result.result_type.value}\n",
248248
]
249249
)
250250

src/macaron/slsa_analyzer/checks/base_check.py

Lines changed: 49 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,22 @@
77
from abc import abstractmethod
88

99
from macaron.slsa_analyzer.analyze_context import AnalyzeContext
10-
from macaron.slsa_analyzer.checks.check_result import CheckResult, CheckResultType, SkippedInfo, get_result_as_bool
11-
from macaron.slsa_analyzer.slsa_req import ReqName, get_requirements_dict
10+
from macaron.slsa_analyzer.checks.check_result import (
11+
CheckInfo,
12+
CheckResult,
13+
CheckResultData,
14+
CheckResultType,
15+
SkippedInfo,
16+
get_result_as_bool,
17+
)
18+
from macaron.slsa_analyzer.slsa_req import ReqName
1219

1320
logger: logging.Logger = logging.getLogger(__name__)
1421

1522

1623
class BaseCheck:
1724
"""This abstract class is used to implement Checks in Macaron."""
1825

19-
# The dictionary that contains the data of all SLSA requirements.
20-
SLSA_REQ_DATA = get_requirements_dict()
21-
2226
def __init__(
2327
self,
2428
check_id: str = "",
@@ -44,20 +48,34 @@ def __init__(
4448
result_on_skip : CheckResultType
4549
The status for this check when it's skipped based on another check's result.
4650
"""
47-
self.check_id = check_id
48-
self.description = description
51+
self._check_info = CheckInfo(
52+
check_id=check_id, check_description=description, eval_reqs=eval_reqs if eval_reqs else []
53+
)
4954

5055
if not depends_on:
51-
self.depends_on = []
56+
self._depends_on = []
5257
else:
53-
self.depends_on = depends_on
58+
self._depends_on = depends_on
5459

55-
if not eval_reqs:
56-
self.eval_reqs = []
57-
else:
58-
self.eval_reqs = eval_reqs
60+
self._result_on_skip = result_on_skip
61+
62+
@property
63+
def check_info(self) -> CheckInfo:
64+
"""Get the information identifying/describing this check."""
65+
return self._check_info
66+
67+
@property
68+
def depends_on(self) -> list[tuple[str, CheckResultType]]:
69+
"""Get the list of parent checks that this check depends on.
5970
60-
self.result_on_skip = result_on_skip
71+
Each member of the list is a tuple of the parent's id and the status of that parent check.
72+
"""
73+
return self._depends_on
74+
75+
@property
76+
def result_on_skip(self) -> CheckResultType:
77+
"""Get the status for this check when it's skipped based on another check's result."""
78+
return self._result_on_skip
6179

6280
def run(self, target: AnalyzeContext, skipped_info: SkippedInfo | None = None) -> CheckResult:
6381
"""Run the check and return the results.
@@ -75,66 +93,58 @@ def run(self, target: AnalyzeContext, skipped_info: SkippedInfo | None = None) -
7593
The result of the check.
7694
"""
7795
logger.info("----------------------------------")
78-
logger.info("BEGIN CHECK: %s", self.check_id)
96+
logger.info("BEGIN CHECK: %s", self.check_info.check_id)
7997
logger.info("----------------------------------")
8098

81-
check_result = CheckResult(
82-
check_id=self.check_id,
83-
check_description=self.description,
84-
slsa_requirements=[str(self.SLSA_REQ_DATA.get(req)) for req in self.eval_reqs],
85-
justification=[],
86-
result_type=CheckResultType.SKIPPED,
87-
result_tables=[],
88-
)
99+
check_result_data: CheckResultData
89100

90101
if skipped_info:
91-
check_result["result_type"] = self.result_on_skip
92-
check_result["justification"].append(skipped_info["suppress_comment"])
102+
check_result_data = CheckResultData(
103+
justification=[skipped_info["suppress_comment"]], result_tables=[], result_type=self.result_on_skip
104+
)
93105
logger.info(
94106
"Check %s is skipped on target %s, comment: %s",
95-
self.check_id,
107+
self.check_info.check_id,
96108
target.component.purl,
97109
skipped_info["suppress_comment"],
98110
)
99111
else:
100-
check_result["result_type"] = self.run_check(target, check_result)
112+
check_result_data = self.run_check(target)
101113
logger.info(
102114
"Check %s run %s on target %s, result: %s",
103-
self.check_id,
104-
check_result["result_type"].value,
115+
self.check_info.check_id,
116+
check_result_data.result_type.value,
105117
target.component.purl,
106-
check_result["justification"],
118+
check_result_data.justification,
107119
)
108120

109121
justification_str = ""
110-
for ele in check_result["justification"]:
122+
for ele in check_result_data.justification:
111123
if isinstance(ele, dict):
112124
for key, val in ele.items():
113125
justification_str += f"{key}: {val}. "
114126
justification_str += f"{str(ele)}. "
115127

116128
target.bulk_update_req_status(
117-
self.eval_reqs,
118-
get_result_as_bool(check_result["result_type"]),
129+
self.check_info.eval_reqs,
130+
get_result_as_bool(check_result_data.result_type),
119131
justification_str,
120132
)
121133

122-
return check_result
134+
return CheckResult(check=self.check_info, result=check_result_data)
123135

124136
@abstractmethod
125-
def run_check(self, ctx: AnalyzeContext, check_result: CheckResult) -> CheckResultType:
137+
def run_check(self, ctx: AnalyzeContext) -> CheckResultData:
126138
"""Implement the check in this method.
127139
128140
Parameters
129141
----------
130142
ctx : AnalyzeContext
131143
The object containing processed data for the target repo.
132-
check_result : CheckResult
133-
The object containing result data of a check.
134144
135145
Returns
136146
-------
137-
CheckResultType
138-
The result type of the check (e.g. PASSED).
147+
CheckResultData
148+
The result of the check.
139149
"""
140150
raise NotImplementedError

0 commit comments

Comments
 (0)