Skip to content

Commit 524c702

Browse files
authored
Return support for derived and temporary evidence (#123)
1 parent d42fe5b commit 524c702

File tree

6 files changed

+159
-47
lines changed

6 files changed

+159
-47
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ repos:
77
files: "^(compliance|test)"
88
stages: [commit]
99
- repo: https://gitlab.com/pycqa/flake8
10-
rev: 3.9.1
10+
rev: 3.9.2
1111
hooks:
1212
- id: flake8
1313
args: [

CHANGES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# [1.20.0](https://github.com/ComplianceAsCode/auditree-framework/releases/tag/v1.20.0)
2+
3+
- [ADDED] Returned support for `DerivedEvidence`.
4+
- [ADDED] Returned support for `TmpEvidence`.
5+
16
# [1.19.1](https://github.com/ComplianceAsCode/auditree-framework/releases/tag/v1.19.1)
27

38
- [FIXED] Evidence cache loading bug resolved.

compliance/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@
1414
# limitations under the License.
1515
"""Compliance automation package."""
1616

17-
__version__ = '1.19.1'
17+
__version__ = '1.20.0'

compliance/evidence.py

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@
3131
)
3232
from compliance.utils.path import FETCH_PREFIX, substitute_config
3333

34-
from deprecated import deprecated
35-
3634
HOUR = 60 * 60
3735
DAY = HOUR * 24
3836
YEAR = DAY * 365
@@ -236,7 +234,6 @@ def _partition(self, data, key):
236234
return data
237235

238236

239-
@deprecated(reason='Evidence class to be removed, use RawEvidence instead')
240237
class DerivedEvidence(_BaseEvidence):
241238
"""The derived evidence class."""
242239

@@ -255,7 +252,6 @@ def rootdir(self):
255252
return 'reports'
256253

257254

258-
@deprecated(reason='Evidence class to be removed')
259255
class TmpEvidence(_BaseEvidence):
260256
"""The temporary evidence class."""
261257

@@ -279,7 +275,7 @@ def rootdir(self):
279275

280276

281277
class _EvidenceContextManager(object):
282-
"""Base class for raw evidence context managers."""
278+
"""Base class for raw and temporary evidence context managers."""
283279

284280
def __init__(self, locker, evidence_path, evidence_type):
285281
self.locker = locker
@@ -328,7 +324,6 @@ def __init__(self, locker, evidence_path):
328324
super().__init__(locker, evidence_path, 'raw')
329325

330326

331-
@deprecated(reason='Context manager to be removed')
332327
class tmp_evidence(_EvidenceContextManager): # noqa: N801
333328
"""
334329
Helper context manager for a typical ``fetch_`` method implementation.
@@ -357,7 +352,6 @@ def __init__(self, locker, evidence_path):
357352
super().__init__(locker, evidence_path, 'tmp')
358353

359354

360-
@deprecated(reason='Context manager to be removed, use raw_evidence instead')
361355
class derived_evidence(object): # noqa: N801
362356
"""
363357
Helper context manager for a typical ``fetch_`` method implementation.
@@ -573,7 +567,7 @@ def get_evidence_class(evidence_type):
573567
provided. If no match ``None`` is returned.
574568
575569
:param evidence_type: the type of evidence class desired as a string. Valid
576-
values are ``reports``, ``raw``.
570+
values are ``tmp``, ``reports``, ``derived``, ``raw``, ``external``.
577571
578572
:returns: the appropriate evidence class.
579573
"""
@@ -711,7 +705,6 @@ def wrapper(self, *args, **kwargs):
711705
return decorator
712706

713707

714-
@deprecated(reason='Decorator to be removed')
715708
def store_tmp_evidence(evidence_path):
716709
"""
717710
Decorate a typical ``fetcher_`` method fetching temporary evidence.
@@ -735,14 +728,13 @@ def wrapper(self, *args, **kwargs):
735728
return decorator
736729

737730

738-
@deprecated(reason='Decorator to be removed, use store_raw_evidence instead')
739731
def store_derived_evidence(evidences, target):
740732
"""
741733
Decorate a typical ``fetcher_`` method fetching derived evidence.
742734
743735
Use when retrieving derived evidence by a fetcher and the name of the
744736
evidence is static/known. When the evidence name is dynamic use the
745-
tmp_evidence context manager instead.
737+
derived_evidence context manager instead.
746738
747739
The decorator expects that the decorated method returns the content to be
748740
stored in the locker. The storing of the evidence is also handled by this
@@ -813,7 +805,6 @@ def decorator(f):
813805
return decorator
814806

815807

816-
@deprecated(reason='Decorator to be removed, use with_raw_evidences instead')
817808
def with_derived_evidences(*from_evidences):
818809
"""
819810
Decorate a typical ``test_`` check method processing derived evidences.
@@ -833,7 +824,6 @@ def decorator(f):
833824
return decorator
834825

835826

836-
@deprecated(reason='Decorator to be removed')
837827
def with_tmp_evidences(*from_evidences):
838828
"""
839829
Decorate a typical ``test_`` check method processing temporary evidences.

doc-source/design-principles.rst

Lines changed: 149 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ as fetchers and checks are loaded automatically by ``unittest``.
2626
Evidence
2727
~~~~~~~~
2828

29-
Fetchers and checks manage evidence. We have defined three types of
29+
Fetchers and checks manage evidence. We have defined five types of
3030
evidence (see :py:mod:`compliance.evidence`):
3131

3232
* :py:class:`~compliance.evidence.RawEvidence`: Gathered by
@@ -37,6 +37,18 @@ evidence (see :py:mod:`compliance.evidence`):
3737
as binary by setting the ``binary_content=True`` keyword argument key/value
3838
pair when constructing a ``RawEvidence`` object.
3939

40+
* :py:class:`~compliance.evidence.DerivedEvidence`: Gathered/Generated by
41+
fetchers and used by checks as *input*. Derived evidence is useful for
42+
those cases when a fetcher needs other evidence to perform computations
43+
over data collected in order to generate a new evidence. This new
44+
evidence is considered `derived` in the sense that its data is not the
45+
same as the source.
46+
47+
* :py:class:`~compliance.evidence.TmpEvidence`: Gathered by
48+
fetchers and used by checks as *input*. This type of evidence is similar to
49+
RawEvidence but it is never get pushed to the remote git repository. This is
50+
useful for evidence that contains passwords or credentials.
51+
4052
* :py:class:`~compliance.evidence.ExternalEvidence`: Planted in the locker
4153
with `plant <https://github.com/ComplianceAsCode/auditree-plant>`_
4254
and used by checks as *input*. For example, a list of users in GitHub.
@@ -212,7 +224,8 @@ A few examples of what it is allowed:
212224

213225
In any case, any modification of a new raw evidence **must** be
214226
approved and agreed by the reviewers. By default, do **not** modify
215-
the raw data.
227+
the raw data. If you need to, then you should consider using derived
228+
evidence.
216229

217230
This is a list of modifications that are completely forbidden:
218231

@@ -230,11 +243,12 @@ we've provided some helpful decorators and context managers that validate
230243
``ttl`` for you and if necessary write the evidence to the evidence locker for
231244
you after it has been fetched.
232245

233-
* ``store_raw_evidence`` decorator: Use this decorator on your fetcher method
234-
when you know the full path and name of your raw evidence. The decorator
235-
takes as an argument, the path to your raw evidence as a string.
246+
* ``store_raw_evidence`` and ``store_tmp_evidence`` decorators: Use one of
247+
these decorators on your fetcher method when you know the path and name of
248+
your raw or tmp evidence. The decorator takes as an argument, the path to
249+
your raw or tmp evidence as a string.
236250

237-
``@store_raw_evidence`` usage example::
251+
Usage example::
238252

239253
...
240254
from compliance.evidence import store_raw_evidence
@@ -248,15 +262,16 @@ you after it has been fetched.
248262
# The decorator will write it to the evidence locker
249263
return json.dumps(foo_bar_data)
250264

251-
* ``raw_evidence`` context manager: Use this context manager within your
252-
fetcher method when your fetcher retrieves multiple, similar raw evidence
253-
based on a dynamic set of configurable values. In other words the full name
254-
and content of evidence is based on a configuration and not known prior
255-
to execution of the fetcher logic. The context manager takes as arguments, a
256-
locker object and the path to your raw evidence as a string. The context
257-
manager yields the corresponding raw evidence object.
265+
* ``raw_evidence`` and ``tmp_evidence`` context managers: Use one of these
266+
context managers within your fetcher method when your fetcher retrieves
267+
multiple, similar raw or tmp evidence based on a dynamic set of configurable
268+
values. In other words the full name and content of evidence is based on a
269+
configuration and not known prior to execution of the fetcher logic. The
270+
context manager takes as arguments, a locker object and the path to your raw
271+
or tmp evidence as a string. The context manager yields the corresponding
272+
raw or tmp evidence object.
258273

259-
``with raw_evidence`` usage example::
274+
Usage example::
260275

261276
...
262277
from compliance.evidence import raw_evidence
@@ -273,6 +288,109 @@ you after it has been fetched.
273288
# Upon exit it is written to the evidence locker
274289
evidence.set_content(json.dumps(foo_bar_data))
275290

291+
* ``store_derived_evidence`` decorator: Use this decorator on your fetcher
292+
method when you know the paths and names of your source evidences and
293+
the path and name of your target derived evidence. The decorator takes
294+
as arguments, a list of source evidence paths as strings and a target derived
295+
evidence path as a string. It also passes the source evidences to the
296+
decorated method in the form of method arguments.
297+
298+
Usage example::
299+
300+
...
301+
from compliance.evidence import store_derived_evidence
302+
...
303+
@store_derived_evidence(
304+
['raw/foo/evidence_bar.json', 'raw/foo/evidence_baz.json'],
305+
'foo/derived_bar_baz.json'
306+
)
307+
fetch_foo_bar_baz_derived_evidence(self, bar_evidence, baz_evidence):
308+
# Fetcher code only executes if evidence is stale
309+
# Construct your derived evidence
310+
derived_data = self._do_whatever(bar_evidence, baz_evidence)
311+
# Return the content as a string
312+
# The decorator will write it to the evidence locker
313+
return json.dumps(derived_data)
314+
315+
* ``derived_evidence`` context manager: Use this context manager within your
316+
fetcher method when your fetcher generates multiple, similar derived
317+
evidences based on a dynamic set of configurable values. In other words the
318+
name and content of the evidences are based on a configuration and not
319+
known prior to execution of the fetcher logic. The context manager takes as
320+
arguments, a locker object, source evidence paths and a target derived
321+
evidence path as a string. The source evidence paths can be in the form of a
322+
list of paths as strings, a dictionary of key/values pairs as strings where
323+
the key is an evidence short name and the value is the evidence path, or
324+
simply a single evidence path as a string. The context manager yields a
325+
dictionary containing the source and target evidences as the dictionary
326+
values. The source evidence key is its evidence path if a list of source
327+
paths were provided or its evidence short name if a dictionary of paths were
328+
provided or "source" if a single evidence path in the form of a string was
329+
provided. The target derived evidence key is always "derived".
330+
331+
Usage example (source list provided)::
332+
333+
...
334+
from compliance.evidence import derived_evidence
335+
...
336+
fetch_foo_bar_baz_derived_evidence(self):
337+
for system in systems:
338+
sources = ['raw/foo/evidence_bar.json', 'raw/foo/evidence_baz.json']
339+
target = 'foo/derived_bar_baz_{}.json'.format(system)
340+
with derived_evidence(self.locker, sources, target) as evidences:
341+
# None is returned if target evidence is not stale
342+
if evidences:
343+
# Construct your derived evidence
344+
derived_data = self._do_whatever(
345+
evidences['raw/foo/evidence_bar.json'],
346+
evidences['raw/foo/evidence_baz.json']
347+
)
348+
# Set the content as a string
349+
# Upon exit it is written to the evidence locker
350+
evidences['derived'].set_content(json.dumps(derived_data))
351+
352+
Usage example (source dictionary provided)::
353+
354+
...
355+
from compliance.evidence import derived_evidence
356+
...
357+
fetch_foo_bar_baz_derived_evidence(self):
358+
for system in systems:
359+
sources = {
360+
'bar': 'raw/foo/evidence_bar.json',
361+
'baz': 'raw/foo/evidence_baz.json'
362+
}
363+
target = 'foo/derived_bar_baz_{}.json'.format(system)
364+
with derived_evidence(self.locker, sources, target) as evidences:
365+
# None is returned if target evidence is not stale
366+
if evidences:
367+
# Construct your derived evidence
368+
derived_data = self._do_whatever(
369+
evidences['bar'],
370+
evidences['baz']
371+
)
372+
# Set the content as a string
373+
# Upon exit it is written to the evidence locker
374+
evidences['derived'].set_content(json.dumps(derived_data))
375+
376+
Usage example (source string provided)::
377+
378+
...
379+
from compliance.evidence import derived_evidence
380+
...
381+
fetch_foo_bar_derived_evidence(self):
382+
for system in systems:
383+
source = 'raw/foo/evidence_bar.json'
384+
target = 'foo/derived_bar_{}.json'.format(system)
385+
with derived_evidence(self.locker, source, target) as evidences:
386+
# None is returned if target evidence is not stale
387+
if evidences:
388+
# Construct your derived evidence
389+
derived_data = self._do_whatever(evidences['source'])
390+
# Set the content as a string
391+
# Upon exit it is written to the evidence locker
392+
evidences['derived'].set_content(json.dumps(derived_data))
393+
276394
Evidence Dependency Chaining
277395
============================
278396

@@ -355,22 +473,22 @@ we've provided some helpful decorators and context managers that validate
355473
``ttl`` for you and will ``ERROR`` the check if evidence ``ttl`` has expired
356474
prior to executing the check's logic.
357475

358-
* ``with_raw_evidences``, ``with_external_evidences`` decorators: Use these
359-
decorators on your check method when you know the full path and name of
360-
your raw or external evidence. Each decorator takes as arguments, the
361-
paths to your raw or external evidence as strings or as evidence
362-
``LazyLoader`` named tuples. Evidence ``LazyLoader`` has ``path`` and
363-
``ev_class`` (evidence class) as attributes. If the requested evidence pass
364-
TTL validation the evidence is then passed along to the decorated method in
365-
the form of method arguments. Use an evidence ``LazyLoader`` when dealing
366-
with sub-classed ``RawEvidence`` or ``ExternalEvidence``, and you want the
367-
evidence provided to the decorated method to be cast as that sub-classed
476+
* ``with_raw_evidences``, ``with_derived_evidences``, ``with_tmp_evidences``,
477+
and ``with_external_evidences`` decorators: Use these decorators on your
478+
check method when you know the path and name of your raw, derived, tmp or
479+
external evidence. Each decorator takes as arguments, the paths to your evidence as strings or as evidence ``LazyLoader`` named tuples. Evidence
480+
``LazyLoader`` has ``path`` and ``ev_class`` (evidence class) as attributes.
481+
If the requested evidence pass TTL validation the evidence is then passed
482+
along to the decorated method in the form of method arguments. Use an
483+
evidence ``LazyLoader`` when dealing with sub-classed ``RawEvidence``,
484+
``DerivedEvidence``, ``TmpEvidence``, or ``ExternalEvidence``, and you want
485+
the evidence provided to the decorated method to be cast as that sub-classed
368486
evidence otherwise use a string path and the evidence will be provided as
369-
either ``RawEvidence`` or ``ExternalEvidence``. A ``LazyLoader`` named tuple
370-
can be constructed by executing the ``lazy_load`` class method of any evidence
487+
the appropriate base evidence. A ``LazyLoader`` named tuple can be
488+
constructed by executing the ``lazy_load`` class method of any evidence
371489
class such as ``BarEvidence.lazy_load('foo/evidence_bar.json')``.
372490

373-
``@with_*_evidences`` usage example::
491+
Usage example::
374492

375493
...
376494
from compliance.evidence import with_raw_evidences
@@ -413,7 +531,7 @@ prior to executing the check's logic.
413531
the ``lazy_load`` class method of any evidence class such as
414532
``BarEvidence.lazy_load('foo/evidence_bar.json')``.
415533

416-
``with evidences`` (list provided) usage example::
534+
Usage example (list provided)::
417535

418536
...
419537
from compliance.evidence import evidences
@@ -436,7 +554,7 @@ prior to executing the check's logic.
436554
self.add_warnings('bar vs. baz', warnings)
437555
self.add_successes('bar vs. baz', successes)
438556

439-
``with evidences`` (dictionary provided) usage example::
557+
Usage example (dictionary provided)::
440558

441559
...
442560
from compliance.evidence import evidences
@@ -459,7 +577,7 @@ prior to executing the check's logic.
459577
self.add_warnings('bar vs. baz', warnings)
460578
self.add_successes('bar vs. baz', successes)
461579

462-
``with evidences`` (string path provided) usage example::
580+
Usage example (string path provided)::
463581

464582
...
465583
from compliance.evidence import evidences
@@ -475,7 +593,7 @@ prior to executing the check's logic.
475593
self.add_warnings('bar stuff', warnings)
476594
self.add_successes('bar stuff', successes)
477595

478-
``with evidences`` (``LazyLoader`` provided) usage example::
596+
Usage example (``LazyLoader`` provided)::
479597

480598
...
481599
from compliance.evidence import evidences

setup.cfg

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ install_requires =
2525
GitPython>=2.1.3
2626
jinja2>=2.10
2727
ibm_cloud_security_advisor>=2.0.0
28-
deprecated>=1.2.9
2928
ilcli>=0.3.1
3029

3130
[options.packages.find]

0 commit comments

Comments
 (0)