You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/source/pages/developers_guide/index.rst
+93-40Lines changed: 93 additions & 40 deletions
Original file line number
Diff line number
Diff line change
@@ -32,68 +32,91 @@ triggered in the CI.
32
32
33
33
Another important aspect of our design is that all the check results are automatically mapped and stored in a local database.
34
34
By performing this mapping, we make it possible to enforce use case-specific policies on the results of the checks. While storing
35
-
the check results to the database happens automatically in Macaron's backend, the developer needs to add a brief specification
35
+
the check results in the database happens automatically in Macaron's backend, the developer needs to add a brief specification
36
36
to make that possible as we will see later.
37
37
38
+
Once you get familiar with writing a basic check, you can explore the check dependency feature in Macaron. The checks
39
+
in our framework can be customized to only run if another check has run and returned a specific
40
+
:class:`result type <macaron.slsa_analyzer.checks.check_result.CheckResultType>`. This feature can be used when some checks
41
+
can be ordered and have a parent-child relationship, i.e., one check implements a weaker or stronger version of a
42
+
security property in a parent check. Therefore, it might make sense to skip running the check and report a
43
+
:class:`result type <macaron.slsa_analyzer.checks.check_result.CheckResultType>` based on the result of the parent check.
44
+
38
45
+++++++++++++++++++
39
46
The Check Interface
40
47
+++++++++++++++++++
41
48
42
49
Each check needs to be implemented as a Python class in a Python module under ``src/macaron/slsa_analyzer/checks``.
43
50
A check class should subclass the :class:`BaseCheck <macaron.slsa_analyzer.checks.base_check.BaseCheck>` class.
44
51
45
-
You need to set the name, description, and other details of your new check in the ``__init__`` method. After implementing
46
-
the initializer, you need to implement the ``run_check`` abstract method. This method provides the context object
47
-
:class:`AnalyzeContext <macaron.slsa_analyzer.analyze_context.AnalyzeContext>`, which contains various
48
-
intermediate representations and models. The ``dynamic_data`` property would be particularly useful as it contains
49
-
data about the CI service, artifact registry, and build tool used for building the software component.
50
-
51
-
``component`` is another useful attribute in the :class:`AnalyzeContext <macaron.slsa_analyzer.analyze_context.AnalyzeContext>` object
52
-
that you should know about. This attribute contains the information about a software component, such
53
-
as it's corresponding ``repository`` and ``dependencies``. Note that ``component`` will also be stored into the database and its attributes
54
-
such as ``repository`` are established as database relationships. You can see the existing tables and their
55
-
relationships in our :mod:`data model <macaron.database.table_definitions>`.
56
-
57
-
Once you implement the logic of your check in the ``run_check`` method, you need to add a class to help
58
-
Macaron handle your check's output:
59
-
60
-
* Add a class that subclasses ``CheckFacts`` to map your outputs to a table in the database. The class name should follow the ``<MyCheck>Facts`` pattern.
61
-
* Specify the table name in the ``__tablename__ = "_my_check"`` class variable. Note that the table name should start with ``_`` and it should not have been used by other checks.
62
-
* Add the ``id`` column as the primary key where the foreign key is ``_check_facts.id``.
63
-
* Add columns for the check outputs that you would like to store into the database. If a column needs to appear as a justification in the HTML/JSON report, pass ``info={"justification": JustificationType.<TEXT or HREF>}`` to the column mapper.
64
-
* Add ``__mapper_args__`` class variable and set ``"polymorphic_identity"`` key to the table name.
52
+
The main logic of a check should be implemented in the :func:`run_check <macaron.slsa_analyzer.checks.base_check.BaseCheck.run_check>` abstract method. It is important to understand the input
53
+
parameters and output objects computed by this method.
65
54
66
-
Next, you need to create a ``result_tables`` list and append check facts as part of the ``run_check`` implementation.
67
-
You should also specify a :class:`Confidence <macaron.slsa_analyzer.checks.check_result.Confidence>`
68
-
score choosing one of the ``Confidence`` enum values, e.g., ``Confidence.HIGH`` and pass it via keyword
69
-
argument ``confidence``. You should choose a suitable confidence score based on the accuracy
The :func:`run_check <macaron.slsa_analyzer.checks.base_check.BaseCheck.run_check>` method is a callback called by our checker framework. The framework pre-computes a context object,
63
+
:class:`ctx: AnalyzeContext <macaron.slsa_analyzer.analyze_context.AnalyzeContext>` and makes it available as the input
64
+
parameter to the function. The ``ctx`` object contains various intermediate representations and models as the input parameter.
65
+
Most likely, you will need to use the following properties:
75
66
76
-
This list as well as the check result status should be stored in a :class:`CheckResultData <macaron.slsa_analyzer.checks.check_result.CheckResultData>`
Note that :attr:`component <macaron.slsa_analyzer.analyze_context.AnalyzeContext.component>` will also be stored
75
+
in the database and its attributes, such as :attr:`repository <macaron.database.table_definitions.Component.repository>`
76
+
are established as database relationships. You can see the existing tables and their relationships
77
+
in our :mod:`data model <macaron.database.table_definitions>`.
80
78
81
-
.. code-block:: python
79
+
The :attr:`dynamic_data <macaron.slsa_analyzer.analyze_context.AnalyzeContext.dynamic_data>` property would be particularly useful as it contains
80
+
data about the CI service, artifact registry, and build tool used for building the software component.
81
+
Note that this object is a shared state among checks. If a check runs before another check, it can
82
+
make changes to this object, which will be accessible to the checks run subsequently.
82
83
83
-
registry.register(MyCheck())
84
+
''''''
85
+
Output
86
+
''''''
84
87
85
-
And of course, make sure to add tests for your check by adding a module under ``tests/slsa_analyzer/checks/``.
88
+
The :func:`run_check <macaron.slsa_analyzer.checks.base_check.BaseCheck.run_check>` method returns a :class:`CheckResultData <macaron.slsa_analyzer.checks.check_result.CheckResultData>` object.
89
+
This object consists of :attr:`result_tables <macaron.slsa_analyzer.checks.check_result.CheckResultData.result_tables>` and
The :attr:`result_tables <macaron.slsa_analyzer.checks.check_result.CheckResultData.result_tables>` object is the list of facts generated from the check. The :attr:`result_type <macaron.slsa_analyzer.checks.check_result.CheckResultData.result_type>`
92
+
value shows the final result type of the check.
86
93
87
94
+++++++
88
95
Example
89
96
+++++++
90
97
91
-
In this example, we show how to add a check determine if a software component has a source-code repository.
98
+
In this example, we show how to add a check to determine if a software component has a source-code repository.
99
+
Note that this is a simple example to just demonstrate how to add a check from scratch.
92
100
Feel free to explore other existing checks under ``src/macaron/slsa_analyzer/checks`` for more examples.
93
101
94
-
1. First create a module called ``repo_check.py`` under ``src/macaron/slsa_analyzer/checks``.
102
+
As discussed earlier, each check needs to be implemented as a Python class in a Python module under ``src/macaron/slsa_analyzer/checks``.
103
+
A check class should subclass the :class:`BaseCheck <macaron.slsa_analyzer.checks.base_check.BaseCheck>` class.
104
+
105
+
'''''''''''''''
106
+
Create a module
107
+
'''''''''''''''
108
+
First create a module called ``repo_check.py`` under ``src/macaron/slsa_analyzer/checks``.
95
109
96
-
2. Add a class and specify the columns that you want to store for the check outputs to the database.
110
+
111
+
''''''''''''''''''''''''''''
112
+
Add a class for the database
113
+
''''''''''''''''''''''''''''
114
+
115
+
* Add a class that subclasses :class:`CheckFacts <macaron.database.table_definitions.CheckFacts>` to map your outputs to a table in the database. The class name should follow the ``<MyCheck>Facts`` pattern.
116
+
* Specify the table name in the ``__tablename__`` class variable. Note that the table name should start with ``_`` and it should not have been used by other checks.
117
+
* Add the ``id`` column as the primary key where the foreign key is ``_check_facts.id``.
118
+
* Add columns for the check outputs that you would like to store in the database. If a column needs to appear as a justification in the HTML/JSON report, pass ``info={"justification": JustificationType.<TEXT or HREF>}`` to the column mapper.
119
+
* Add ``__mapper_args__`` class variable and set ``"polymorphic_identity"`` key to the table name.
97
120
98
121
.. code-block:: python
99
122
@@ -113,10 +136,25 @@ Feel free to explore other existing checks under ``src/macaron/slsa_analyzer/che
A ``check_id`` should meet the following requirements:
151
+
152
+
- The general format: ``mcn_<name>_<digits>``
153
+
- In ``name``, only lowercase alphabetical letters are allowed. If ``name`` contains multiple \
154
+
words, they must be separated by underscores.
155
+
156
+
157
+
You can set the ``depends_on`` attribute in the initializer method to declare such dependencies. In this example, we leave this list empty.
120
158
121
159
.. code-block:: python
122
160
@@ -156,13 +194,28 @@ Feel free to explore other existing checks under ``src/macaron/slsa_analyzer/che
156
194
result_type=CheckResultType.PASSED,
157
195
)
158
196
159
-
4. Register your check.
197
+
As you can see, the result of the check is returned via the :class:`CheckResultData <macaron.slsa_analyzer.checks.check_result.CheckResultData>` object.
198
+
You should specify a :class:`Confidence <macaron.slsa_analyzer.checks.check_result.Confidence>`
199
+
score choosing one of the :class:`Confidence <macaron.slsa_analyzer.checks.check_result.Confidence>` enum values,
200
+
e.g., :class:`Confidence.HIGH <macaron.slsa_analyzer.checks.check_result.Confidence.HIGH>` and pass it via keyword
201
+
argument :attr:`confidence <macaron.database.table_definitions.CheckFacts.confidence>`. You should choose a suitable
202
+
confidence score based on the accuracy of your check analysis.
203
+
204
+
'''''''''''''''''''
205
+
Register your check
206
+
'''''''''''''''''''
207
+
208
+
Finally, you need to register your check by adding it to the :mod:`registry module <macaron.slsa_analyzer.registry>` at the end of your check module:
160
209
161
210
.. code-block:: python
162
211
163
212
registry.register(RepoCheck())
164
213
165
214
215
+
'''''''''''''''
216
+
Test your check
217
+
'''''''''''''''
218
+
166
219
Finally, you can add tests for you check by adding ``tests/slsa_analyzer/checks/test_repo_check.py`` module. Macaron
167
220
uses `pytest <https://docs.pytest.org>`_ and `hypothesis <https://hypothesis.readthedocs.io>`_ for testing. Take a look
0 commit comments