audit-packs-mapping 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- audit_packs_mapping-0.1.1/PKG-INFO +33 -0
- audit_packs_mapping-0.1.1/README.md +23 -0
- audit_packs_mapping-0.1.1/pyproject.toml +15 -0
- audit_packs_mapping-0.1.1/setup.cfg +4 -0
- audit_packs_mapping-0.1.1/src/audit_packs_mapping/__init__.py +0 -0
- audit_packs_mapping-0.1.1/src/audit_packs_mapping/coverage.py +60 -0
- audit_packs_mapping-0.1.1/src/audit_packs_mapping/oscal.py +144 -0
- audit_packs_mapping-0.1.1/src/audit_packs_mapping/packs.py +160 -0
- audit_packs_mapping-0.1.1/src/audit_packs_mapping.egg-info/PKG-INFO +33 -0
- audit_packs_mapping-0.1.1/src/audit_packs_mapping.egg-info/SOURCES.txt +11 -0
- audit_packs_mapping-0.1.1/src/audit_packs_mapping.egg-info/dependency_links.txt +1 -0
- audit_packs_mapping-0.1.1/src/audit_packs_mapping.egg-info/requires.txt +2 -0
- audit_packs_mapping-0.1.1/src/audit_packs_mapping.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: audit-packs-mapping
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Compliance mapping, coverage, and OSCAL export for audit-packs
|
|
5
|
+
License: Apache-2.0
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: audit-packs-core>=0.1.1
|
|
9
|
+
Requires-Dist: PyYAML>=6.0
|
|
10
|
+
|
|
11
|
+
# audit-packs-mapping
|
|
12
|
+
|
|
13
|
+
[](https://pypi.org/project/audit-packs-mapping/)
|
|
14
|
+
[](../../LICENSE)
|
|
15
|
+
|
|
16
|
+
`audit-packs-mapping` is the compliance framework mapping and coverage calculation engine for the `audit-packs` ecosystem. It evaluates raw security scanner findings and maps them to control requirements in GRC frameworks (such as SOC 2, NIST 800-53, GDPR, HIPAA, and ISO 27001).
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install audit-packs-mapping
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Features
|
|
25
|
+
|
|
26
|
+
- **Framework Control Mapping**: Resolves raw scanner rule IDs (e.g. Checkov `CKV_AWS_19`, Semgrep rules) to specific compliance controls.
|
|
27
|
+
- **Coverage Engine**: Computes compliance pass/fail/manual rates across active control frameworks based on finding states.
|
|
28
|
+
- **OSCAL Export**: Generates NIST Open Security Controls Assessment Language (OSCAL) JSON representation of compliance postures.
|
|
29
|
+
- **Pack Registry Support**: Loads, validates, and installs compliance packs containing control-to-rule mappings.
|
|
30
|
+
|
|
31
|
+
## Learn More
|
|
32
|
+
|
|
33
|
+
This library is part of the larger `audit-packs` Compliance Intelligence Engine. For the main command-line interface, GitHub Action integration, and framework mappings, see the [main repository](https://github.com/prakharsingh/audit-packs).
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# audit-packs-mapping
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/audit-packs-mapping/)
|
|
4
|
+
[](../../LICENSE)
|
|
5
|
+
|
|
6
|
+
`audit-packs-mapping` is the compliance framework mapping and coverage calculation engine for the `audit-packs` ecosystem. It evaluates raw security scanner findings and maps them to control requirements in GRC frameworks (such as SOC 2, NIST 800-53, GDPR, HIPAA, and ISO 27001).
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install audit-packs-mapping
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
- **Framework Control Mapping**: Resolves raw scanner rule IDs (e.g. Checkov `CKV_AWS_19`, Semgrep rules) to specific compliance controls.
|
|
17
|
+
- **Coverage Engine**: Computes compliance pass/fail/manual rates across active control frameworks based on finding states.
|
|
18
|
+
- **OSCAL Export**: Generates NIST Open Security Controls Assessment Language (OSCAL) JSON representation of compliance postures.
|
|
19
|
+
- **Pack Registry Support**: Loads, validates, and installs compliance packs containing control-to-rule mappings.
|
|
20
|
+
|
|
21
|
+
## Learn More
|
|
22
|
+
|
|
23
|
+
This library is part of the larger `audit-packs` Compliance Intelligence Engine. For the main command-line interface, GitHub Action integration, and framework mappings, see the [main repository](https://github.com/prakharsingh/audit-packs).
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "audit-packs-mapping"
|
|
3
|
+
version = "0.1.1"
|
|
4
|
+
description = "Compliance mapping, coverage, and OSCAL export for audit-packs"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = { text = "Apache-2.0" }
|
|
7
|
+
requires-python = ">=3.11"
|
|
8
|
+
dependencies = ["audit-packs-core>=0.1.1", "PyYAML>=6.0"]
|
|
9
|
+
|
|
10
|
+
[build-system]
|
|
11
|
+
requires = ["setuptools>=68"]
|
|
12
|
+
build-backend = "setuptools.build_meta"
|
|
13
|
+
|
|
14
|
+
[tool.setuptools.packages.find]
|
|
15
|
+
where = ["src"]
|
|
File without changes
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""coverage.py — compute ControlStatus for every framework control.
|
|
2
|
+
|
|
3
|
+
Pure logic: no subprocess, no HTTP. Takes ControlFinding instances (already
|
|
4
|
+
mapped by packs.map_findings) and the pack directory, and returns a
|
|
5
|
+
ControlStatus per control — pass, fail, manual, or not_applicable.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from audit_packs_core.models import ControlFinding, ControlStatus, AssessmentStatus
|
|
9
|
+
from audit_packs_mapping.packs import iter_controls
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def compute_coverage(
|
|
13
|
+
control_findings: list[ControlFinding],
|
|
14
|
+
packs_dir: str,
|
|
15
|
+
frameworks: list[str],
|
|
16
|
+
) -> list[ControlStatus]:
|
|
17
|
+
"""Compute a ControlStatus for every control in *frameworks*.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
control_findings: All ControlFinding instances from map_findings()
|
|
21
|
+
(may come from diff-only or full-repo scan).
|
|
22
|
+
packs_dir: Path to the directory containing pack YAML files.
|
|
23
|
+
frameworks: List of framework ids (e.g. ["nist-800-53", "soc2"]).
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
One ControlStatus per (framework, control_id) pair.
|
|
27
|
+
"""
|
|
28
|
+
# Index findings by (framework, control_id) for O(1) lookup
|
|
29
|
+
findings_by_key: dict[tuple[str, str], list[ControlFinding]] = {}
|
|
30
|
+
for cf in control_findings:
|
|
31
|
+
key = (cf.framework, cf.control_id)
|
|
32
|
+
findings_by_key.setdefault(key, []).append(cf)
|
|
33
|
+
|
|
34
|
+
statuses: list[ControlStatus] = []
|
|
35
|
+
for fw in frameworks:
|
|
36
|
+
for ctrl in iter_controls(packs_dir, fw):
|
|
37
|
+
key = (fw, ctrl["id"])
|
|
38
|
+
matched = findings_by_key.get(key, [])
|
|
39
|
+
assessment_hint = ctrl.get("assessment")
|
|
40
|
+
|
|
41
|
+
if assessment_hint == "manual":
|
|
42
|
+
status = AssessmentStatus.MANUAL
|
|
43
|
+
elif matched:
|
|
44
|
+
status = AssessmentStatus.FAIL
|
|
45
|
+
else:
|
|
46
|
+
status = AssessmentStatus.PASS
|
|
47
|
+
|
|
48
|
+
statuses.append(
|
|
49
|
+
ControlStatus(
|
|
50
|
+
framework=fw,
|
|
51
|
+
control_id=ctrl["id"],
|
|
52
|
+
control_title=ctrl["title"],
|
|
53
|
+
status=status,
|
|
54
|
+
check_ids=tuple(ctrl["check_ids"]),
|
|
55
|
+
findings=tuple(matched),
|
|
56
|
+
evidence=tuple(cf.finding.evidence for cf in matched),
|
|
57
|
+
)
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
return statuses
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"""oscal.py — Emit NIST OSCAL assessment-results from ControlStatus objects.
|
|
2
|
+
|
|
3
|
+
Produces a JSON-serialisable dict that conforms to the OSCAL assessment-results
|
|
4
|
+
model (https://pages.nist.gov/OSCAL/). Uses stdlib only (no new dependencies).
|
|
5
|
+
|
|
6
|
+
Supports:
|
|
7
|
+
- One result entry per framework
|
|
8
|
+
- reviewed-controls with all assessed controls
|
|
9
|
+
- findings for FAIL controls with evidence links
|
|
10
|
+
- Manual controls flagged with state "not-satisfied" (human evidence required)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import uuid
|
|
14
|
+
from collections import defaultdict
|
|
15
|
+
from datetime import datetime, timezone
|
|
16
|
+
|
|
17
|
+
from audit_packs_core.models import AssessmentStatus, ControlStatus
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _now_iso() -> str:
|
|
21
|
+
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _uuid() -> str:
|
|
25
|
+
return str(uuid.uuid4())
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def to_assessment_results(control_statuses: list[ControlStatus]) -> dict:
|
|
29
|
+
"""Convert a list of ControlStatus objects to an OSCAL assessment-results dict.
|
|
30
|
+
|
|
31
|
+
Returns a JSON-serialisable dict with the OSCAL top-level structure:
|
|
32
|
+
{"assessment-results": {"uuid": ..., "metadata": ..., "results": [...]}}
|
|
33
|
+
"""
|
|
34
|
+
if not control_statuses:
|
|
35
|
+
return {
|
|
36
|
+
"assessment-results": {
|
|
37
|
+
"uuid": _uuid(),
|
|
38
|
+
"metadata": _metadata(),
|
|
39
|
+
"results": [],
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# Group by framework
|
|
44
|
+
by_framework: dict[str, list[ControlStatus]] = defaultdict(list)
|
|
45
|
+
for cs in control_statuses:
|
|
46
|
+
by_framework[cs.framework].append(cs)
|
|
47
|
+
|
|
48
|
+
results = []
|
|
49
|
+
for framework, statuses in by_framework.items():
|
|
50
|
+
results.append(_build_result(framework, statuses))
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
"assessment-results": {
|
|
54
|
+
"uuid": _uuid(),
|
|
55
|
+
"metadata": _metadata(),
|
|
56
|
+
"results": results,
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _metadata() -> dict:
|
|
62
|
+
return {
|
|
63
|
+
"title": "audit-packs SOC 2 / NIST 800-53 Assessment Results",
|
|
64
|
+
"last-modified": _now_iso(),
|
|
65
|
+
"version": "1.0",
|
|
66
|
+
"oscal-version": "1.1.2",
|
|
67
|
+
"remarks": (
|
|
68
|
+
"Generated by audit-packs (https://github.com/prakharsingh/audit-packs). "
|
|
69
|
+
"Code-observable controls are assessed automatically; controls marked "
|
|
70
|
+
"assessment:manual require human evidence for SOC 2 attestation."
|
|
71
|
+
),
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _build_result(framework: str, statuses: list[ControlStatus]) -> dict:
|
|
76
|
+
findings = []
|
|
77
|
+
control_selections: list[dict] = []
|
|
78
|
+
|
|
79
|
+
# Separate by status to build both the reviewed-controls list and findings
|
|
80
|
+
include_all = [{"control-id": s.control_id} for s in statuses]
|
|
81
|
+
if include_all:
|
|
82
|
+
control_selections.append({"include-controls": include_all})
|
|
83
|
+
|
|
84
|
+
for cs in statuses:
|
|
85
|
+
if cs.status == AssessmentStatus.FAIL:
|
|
86
|
+
for cf in cs.findings:
|
|
87
|
+
findings.append(
|
|
88
|
+
{
|
|
89
|
+
"uuid": _uuid(),
|
|
90
|
+
"title": f"[{cs.framework}] {cs.control_id} — {cs.control_title}",
|
|
91
|
+
"description": cf.finding.message,
|
|
92
|
+
"target": {
|
|
93
|
+
"type": "statement-id",
|
|
94
|
+
"target-id": cs.control_id,
|
|
95
|
+
"status": {
|
|
96
|
+
"state": "not-satisfied",
|
|
97
|
+
"reason": "fail",
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
"related-observations": [
|
|
101
|
+
{
|
|
102
|
+
"observation-uuid": _uuid(),
|
|
103
|
+
"description": (
|
|
104
|
+
f"Engine: {cf.finding.engine} ({cf.finding.check_id}) "
|
|
105
|
+
f"— {cf.finding.file}:{cf.finding.line}"
|
|
106
|
+
),
|
|
107
|
+
"evidence": cf.finding.evidence,
|
|
108
|
+
"severity": cf.finding.severity,
|
|
109
|
+
}
|
|
110
|
+
],
|
|
111
|
+
}
|
|
112
|
+
)
|
|
113
|
+
elif cs.status == AssessmentStatus.MANUAL:
|
|
114
|
+
# Manual controls appear in findings with state "not-satisfied" and
|
|
115
|
+
# a note that human evidence is required.
|
|
116
|
+
findings.append(
|
|
117
|
+
{
|
|
118
|
+
"uuid": _uuid(),
|
|
119
|
+
"title": f"[{cs.framework}] {cs.control_id} — {cs.control_title}",
|
|
120
|
+
"description": (
|
|
121
|
+
"This control requires manual attestation. "
|
|
122
|
+
"No automated checks are available for this criterion."
|
|
123
|
+
),
|
|
124
|
+
"target": {
|
|
125
|
+
"type": "statement-id",
|
|
126
|
+
"target-id": cs.control_id,
|
|
127
|
+
"status": {
|
|
128
|
+
"state": "not-satisfied",
|
|
129
|
+
"reason": "manual-evidence-required",
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
"related-observations": [],
|
|
133
|
+
}
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
"uuid": _uuid(),
|
|
138
|
+
"title": framework,
|
|
139
|
+
"description": f"Assessment results for {framework}",
|
|
140
|
+
"reviewed-controls": {
|
|
141
|
+
"control-selections": control_selections,
|
|
142
|
+
},
|
|
143
|
+
"findings": findings,
|
|
144
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import yaml
|
|
3
|
+
from audit_packs_core.models import Finding, ControlFinding
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def load_pack(path: str) -> dict | None:
|
|
7
|
+
"""Load a pack YAML. Returns None (instead of raising) when the file is missing."""
|
|
8
|
+
if not os.path.exists(path):
|
|
9
|
+
return None
|
|
10
|
+
with open(path) as fh:
|
|
11
|
+
data = yaml.safe_load(fh) or {}
|
|
12
|
+
framework_key = data.get("framework") or data.get("id")
|
|
13
|
+
if not framework_key or "controls" not in data:
|
|
14
|
+
raise ValueError(f"pack {path} missing required keys 'framework'/'controls'")
|
|
15
|
+
return data
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _pack_path(packs_dir: str, pack_id: str) -> str:
|
|
19
|
+
# 1. Check if packs_dir/pack_id exists
|
|
20
|
+
local_path = os.path.join(packs_dir, pack_id, "controls.yaml")
|
|
21
|
+
if os.path.exists(local_path):
|
|
22
|
+
return local_path
|
|
23
|
+
|
|
24
|
+
# 2. Check if it's installed in the user's home folder registry cache
|
|
25
|
+
installed_path = os.path.join(
|
|
26
|
+
os.path.expanduser("~"), ".audit-packs", "installed", pack_id, "controls.yaml"
|
|
27
|
+
)
|
|
28
|
+
if os.path.exists(installed_path):
|
|
29
|
+
return installed_path
|
|
30
|
+
|
|
31
|
+
return local_path
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _canonical_index(pack: dict) -> dict[tuple[str, str], list[tuple[str, str, tuple]]]:
|
|
35
|
+
"""(engine, check_id) -> [(control_id, control_title, evidence_requirements), ...]"""
|
|
36
|
+
index: dict[tuple[str, str], list[tuple[str, str, tuple]]] = {}
|
|
37
|
+
for control in pack["controls"]:
|
|
38
|
+
ev_reqs: tuple = tuple(control.get("evidence_requirements", []))
|
|
39
|
+
for m in control.get("mappings", []):
|
|
40
|
+
key = (m["engine"], m["check_id"])
|
|
41
|
+
index.setdefault(key, []).append(
|
|
42
|
+
(
|
|
43
|
+
control["id"],
|
|
44
|
+
control.get("title", control["id"]),
|
|
45
|
+
ev_reqs,
|
|
46
|
+
)
|
|
47
|
+
)
|
|
48
|
+
return index
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _canonical_check_ids(pack: dict) -> dict[str, list[tuple[str, str]]]:
|
|
52
|
+
"""control_id -> [(engine, check_id), ...]"""
|
|
53
|
+
result: dict[str, list[tuple[str, str]]] = {}
|
|
54
|
+
for control in pack["controls"]:
|
|
55
|
+
pairs = [(m["engine"], m["check_id"]) for m in control.get("mappings", [])]
|
|
56
|
+
result[control["id"]] = pairs
|
|
57
|
+
return result
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def iter_controls(packs_dir: str, framework: str) -> list[dict]:
|
|
61
|
+
"""Return every control in *framework* with its resolved check_ids."""
|
|
62
|
+
pack = load_pack(_pack_path(packs_dir, framework))
|
|
63
|
+
if pack is None:
|
|
64
|
+
return []
|
|
65
|
+
crosswalk_id = pack.get("crosswalk")
|
|
66
|
+
|
|
67
|
+
if crosswalk_id:
|
|
68
|
+
canonical = load_pack(_pack_path(packs_dir, crosswalk_id))
|
|
69
|
+
canon_checks = _canonical_check_ids(canonical)
|
|
70
|
+
result = []
|
|
71
|
+
for control in pack["controls"]:
|
|
72
|
+
maps_to = control.get("maps_to", [])
|
|
73
|
+
assessment = control.get("assessment", None)
|
|
74
|
+
check_ids: list[tuple[str, str]] = []
|
|
75
|
+
for nist_id in maps_to:
|
|
76
|
+
check_ids.extend(canon_checks.get(nist_id, []))
|
|
77
|
+
result.append(
|
|
78
|
+
{
|
|
79
|
+
"id": control["id"],
|
|
80
|
+
"title": control.get("title", control["id"]),
|
|
81
|
+
"assessment": assessment,
|
|
82
|
+
"check_ids": check_ids,
|
|
83
|
+
"maps_to": maps_to,
|
|
84
|
+
}
|
|
85
|
+
)
|
|
86
|
+
return result
|
|
87
|
+
else:
|
|
88
|
+
canon_checks = _canonical_check_ids(pack)
|
|
89
|
+
return [
|
|
90
|
+
{
|
|
91
|
+
"id": control["id"],
|
|
92
|
+
"title": control.get("title", control["id"]),
|
|
93
|
+
"assessment": None,
|
|
94
|
+
"check_ids": canon_checks.get(control["id"], []),
|
|
95
|
+
"maps_to": [],
|
|
96
|
+
}
|
|
97
|
+
for control in pack["controls"]
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def map_findings(
|
|
102
|
+
findings: list[Finding], packs_dir: str, frameworks: list[str]
|
|
103
|
+
) -> list[ControlFinding]:
|
|
104
|
+
import sys
|
|
105
|
+
|
|
106
|
+
results: list[ControlFinding] = []
|
|
107
|
+
for fw in frameworks:
|
|
108
|
+
pack = load_pack(_pack_path(packs_dir, fw))
|
|
109
|
+
if pack is None:
|
|
110
|
+
print(
|
|
111
|
+
f"\n⚠️ pack not found for framework '{fw}' in {packs_dir!r} — skipping mapping.\n"
|
|
112
|
+
f" Install with: audit-packs pack install <source> "
|
|
113
|
+
f"or point --packs-dir at your packs directory.",
|
|
114
|
+
file=sys.stderr,
|
|
115
|
+
)
|
|
116
|
+
continue
|
|
117
|
+
crosswalk_id = pack.get("crosswalk")
|
|
118
|
+
canonical = (
|
|
119
|
+
load_pack(_pack_path(packs_dir, crosswalk_id)) if crosswalk_id else pack
|
|
120
|
+
)
|
|
121
|
+
if canonical is None:
|
|
122
|
+
print(
|
|
123
|
+
f"\n⚠️ crosswalk pack '{crosswalk_id}' not found for '{fw}' — skipping mapping.",
|
|
124
|
+
file=sys.stderr,
|
|
125
|
+
)
|
|
126
|
+
continue
|
|
127
|
+
check_index = _canonical_index(canonical)
|
|
128
|
+
|
|
129
|
+
if crosswalk_id:
|
|
130
|
+
cw: dict[str, list[tuple[str, str, tuple]]] = {}
|
|
131
|
+
for control in pack["controls"]:
|
|
132
|
+
ev_reqs: tuple = tuple(control.get("evidence_requirements", []))
|
|
133
|
+
for mapped in control.get("maps_to", []):
|
|
134
|
+
cw.setdefault(mapped, []).append(
|
|
135
|
+
(control["id"], control.get("title", control["id"]), ev_reqs)
|
|
136
|
+
)
|
|
137
|
+
has_manual = any(c.get("assessment") == "manual" for c in pack["controls"])
|
|
138
|
+
if not cw and not has_manual:
|
|
139
|
+
raise ValueError(
|
|
140
|
+
f"crosswalk pack '{fw}' has no 'maps_to' entries in any control; "
|
|
141
|
+
f"check that controls use 'maps_to' (not 'nist_ids' or similar)"
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
for f in findings:
|
|
145
|
+
hits = check_index.get((f.engine, f.check_id), [])
|
|
146
|
+
for canonical_control_id, canonical_title, canon_ev_reqs in hits:
|
|
147
|
+
if crosswalk_id:
|
|
148
|
+
for control_id, title, fw_ev_reqs in cw.get(
|
|
149
|
+
canonical_control_id, []
|
|
150
|
+
):
|
|
151
|
+
results.append(
|
|
152
|
+
ControlFinding(f, fw, control_id, title, fw_ev_reqs)
|
|
153
|
+
)
|
|
154
|
+
else:
|
|
155
|
+
results.append(
|
|
156
|
+
ControlFinding(
|
|
157
|
+
f, fw, canonical_control_id, canonical_title, canon_ev_reqs
|
|
158
|
+
)
|
|
159
|
+
)
|
|
160
|
+
return results
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: audit-packs-mapping
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Compliance mapping, coverage, and OSCAL export for audit-packs
|
|
5
|
+
License: Apache-2.0
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: audit-packs-core>=0.1.1
|
|
9
|
+
Requires-Dist: PyYAML>=6.0
|
|
10
|
+
|
|
11
|
+
# audit-packs-mapping
|
|
12
|
+
|
|
13
|
+
[](https://pypi.org/project/audit-packs-mapping/)
|
|
14
|
+
[](../../LICENSE)
|
|
15
|
+
|
|
16
|
+
`audit-packs-mapping` is the compliance framework mapping and coverage calculation engine for the `audit-packs` ecosystem. It evaluates raw security scanner findings and maps them to control requirements in GRC frameworks (such as SOC 2, NIST 800-53, GDPR, HIPAA, and ISO 27001).
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install audit-packs-mapping
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Features
|
|
25
|
+
|
|
26
|
+
- **Framework Control Mapping**: Resolves raw scanner rule IDs (e.g. Checkov `CKV_AWS_19`, Semgrep rules) to specific compliance controls.
|
|
27
|
+
- **Coverage Engine**: Computes compliance pass/fail/manual rates across active control frameworks based on finding states.
|
|
28
|
+
- **OSCAL Export**: Generates NIST Open Security Controls Assessment Language (OSCAL) JSON representation of compliance postures.
|
|
29
|
+
- **Pack Registry Support**: Loads, validates, and installs compliance packs containing control-to-rule mappings.
|
|
30
|
+
|
|
31
|
+
## Learn More
|
|
32
|
+
|
|
33
|
+
This library is part of the larger `audit-packs` Compliance Intelligence Engine. For the main command-line interface, GitHub Action integration, and framework mappings, see the [main repository](https://github.com/prakharsingh/audit-packs).
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/audit_packs_mapping/__init__.py
|
|
4
|
+
src/audit_packs_mapping/coverage.py
|
|
5
|
+
src/audit_packs_mapping/oscal.py
|
|
6
|
+
src/audit_packs_mapping/packs.py
|
|
7
|
+
src/audit_packs_mapping.egg-info/PKG-INFO
|
|
8
|
+
src/audit_packs_mapping.egg-info/SOURCES.txt
|
|
9
|
+
src/audit_packs_mapping.egg-info/dependency_links.txt
|
|
10
|
+
src/audit_packs_mapping.egg-info/requires.txt
|
|
11
|
+
src/audit_packs_mapping.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
audit_packs_mapping
|