forked from ChelseaKR/oscal-validate
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvalidator.py
More file actions
63 lines (47 loc) · 2.18 KB
/
Copy pathvalidator.py
File metadata and controls
63 lines (47 loc) · 2.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
"""Orchestration: run every check over a document and whatever came with it."""
from __future__ import annotations
from pathlib import Path
from .checks import ALL_CHECKS
from .corpus import build_corpus
from .findings import Finding, finalize
from .metaschema import load_metaschema
from .schema import load_schema
from .session import Session
def build_session(document: Path, resolve: list[Path] | None = None) -> Session:
schema = load_schema()
return Session(
corpus=build_corpus(document, list(resolve or []), schema),
schema=schema,
metaschema=load_metaschema(),
)
def validate(session: Session) -> list[Finding]:
"""Run every check. Findings come back in a deterministic order."""
findings: list[Finding] = []
for check in ALL_CHECKS:
findings.extend(check(session))
return finalize(_deduplicate(findings))
def validate_file(document: Path, resolve: list[Path] | None = None) -> list[Finding]:
return validate(build_session(document, resolve))
def _deduplicate(findings: list[Finding]) -> list[Finding]:
"""One report per location and value, from the check with the strongest rule.
NIST's constraint layer and the prose rule in check 4 overlap on a few
identifier references. Reporting both would double-count the same defect,
so the constraint-layer finding wins: a rule NIST states formally is a
better citation than the same rule stated in a documentation page.
"""
kept: list[Finding] = []
references: dict[tuple[str, str], Finding] = {}
for finding in findings:
if not finding.code.startswith("REFERENCE_"):
kept.append(finding)
continue
key = (finding.location, finding.value)
references[key] = _prefer(references.get(key), finding)
return kept + list(references.values())
#: A constraint-layer citation names a NIST constraint id. Prefer it over the
#: same defect reported against a documentation page.
CONSTRAINT_CITATION = "NIST OSCAL constraint"
def _prefer(existing: Finding | None, candidate: Finding) -> Finding:
if existing is None:
return candidate
return existing if CONSTRAINT_CITATION in existing.rule.citation else candidate