evidentia-core 0.6.0__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.
- evidentia_core-0.6.0/.gitignore +95 -0
- evidentia_core-0.6.0/PKG-INFO +48 -0
- evidentia_core-0.6.0/README.md +21 -0
- evidentia_core-0.6.0/pyproject.toml +39 -0
- evidentia_core-0.6.0/src/evidentia_core/__init__.py +9 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/__init__.py +17 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/crosswalk.py +162 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/frameworks.yaml +827 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/international/au-essential-8.json +62 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/international/au-ism.json +176 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/international/canada-itsg-33.json +118 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/international/canada-pipeda.json +105 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/international/eu-ai-act.json +146 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/international/eu-dora.json +116 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/international/eu-gdpr.json +241 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/international/eu-nis2.json +104 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/international/nz-nzism.json +172 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/international/uk-cyber-essentials.json +44 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/international/uk-dpa-2018.json +79 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/international/uk-ncsc-caf-3.2.json +101 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/mappings/fedramp-rev5-moderate_to_cmmc-2-l2.json +265 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/mappings/iso-27001-2022_to_nist-800-53-mod.json +193 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/mappings/nist-800-53-mod_to_hipaa-security.json +169 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/mappings/nist-800-53-rev5_to_soc2-tsc.json +145 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/mappings/nist-csf-2.0_to_nist-800-53-mod.json +297 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/mappings/us-va-vcdpa_to_us-ca-ccpa-cpra.json +113 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-ca-ccpa-cpra.json +116 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-co-cpa.json +107 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-ct-ctdpa.json +107 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-de-dpdpa.json +106 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-fl-fdbr.json +107 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-ia-icdpa.json +103 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-md-mdpa.json +108 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-mn-mncdpa.json +107 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-mt-mcdpa.json +106 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-nh-nhpa.json +107 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-or-ocpa.json +107 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-tn-tipa.json +108 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-tx-tdpsa.json +107 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-ut-ucpa.json +104 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-va-vcdpa.json +107 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/cis-benchmark-aws.json +217 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/cis-benchmark-azure.json +217 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/cis-benchmark-gcp.json +217 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/cis-benchmark-kubernetes.json +217 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/cis-benchmark-rhel9.json +217 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/cis-controls-v8.1.json +197 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/cobit-2019.json +421 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/hitrust-csf-v11.json +985 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/iec-62443.json +100 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/iso-22301-2019.json +87 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/iso-27001-2022.json +950 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/iso-27002-2022.json +950 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/iso-27017-2015.json +87 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/iso-27018-2019.json +267 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/iso-27701-2019.json +508 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/iso-42001-2023.json +397 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/pci-dss-4.0.1.json +142 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/scf-2024.json +977 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/soc2-tsc.json +81 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/swift-cscf-2024.json +353 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/threats/cisa-kev.json +134 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/threats/mitre-attack-enterprise.json +615 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/threats/mitre-capec.json +102 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/threats/mitre-cwe.json +237 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/cisa-cpgs.json +234 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/cjis-v6.json +169 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/cmmc-2-l1.json +121 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/cmmc-2-l2.json +687 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/cmmc-2-l3.json +807 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/cms-ars-5.1.json +146 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/fda-21-cfr-pt11.json +139 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/fedramp-rev5-high.json +2457 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/fedramp-rev5-li-saas.json +933 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/fedramp-rev5-low.json +783 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/fedramp-rev5-moderate.json +2007 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/glba-safeguards.json +129 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/hipaa-breach.json +64 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/hipaa-privacy.json +112 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/hipaa-security.json +396 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/irs-1075.json +150 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nerc-cip-v7.json +103 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-800-171-r2.json +687 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-800-171-r3.json +612 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-800-172.json +227 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-800-53-mod.json +146 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-800-53-rev5-high.json +9547 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-800-53-rev5-low.json +4643 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-800-53-rev5-moderate.json +7806 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-800-53-rev5-privacy.json +2996 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-800-53-rev5.json +254987 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-ai-rmf-1.0.json +449 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-csf-2.0.json +147 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-privacy-framework-1.0.json +594 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-ssdf-800-218.json +269 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/ny-dfs-500.json +188 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/loader.py +306 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/manifest.py +156 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/registry.py +142 -0
- evidentia_core-0.6.0/src/evidentia_core/catalogs/user_dir.py +145 -0
- evidentia_core-0.6.0/src/evidentia_core/config.py +231 -0
- evidentia_core-0.6.0/src/evidentia_core/gap_analyzer/__init__.py +17 -0
- evidentia_core-0.6.0/src/evidentia_core/gap_analyzer/analyzer.py +547 -0
- evidentia_core-0.6.0/src/evidentia_core/gap_analyzer/inventory.py +319 -0
- evidentia_core-0.6.0/src/evidentia_core/gap_analyzer/normalizer.py +102 -0
- evidentia_core-0.6.0/src/evidentia_core/gap_analyzer/reporter.py +157 -0
- evidentia_core-0.6.0/src/evidentia_core/gap_diff.py +327 -0
- evidentia_core-0.6.0/src/evidentia_core/gap_store.py +127 -0
- evidentia_core-0.6.0/src/evidentia_core/init_wizard.py +424 -0
- evidentia_core-0.6.0/src/evidentia_core/models/__init__.py +110 -0
- evidentia_core-0.6.0/src/evidentia_core/models/catalog.py +278 -0
- evidentia_core-0.6.0/src/evidentia_core/models/common.py +87 -0
- evidentia_core-0.6.0/src/evidentia_core/models/control.py +150 -0
- evidentia_core-0.6.0/src/evidentia_core/models/evidence.py +223 -0
- evidentia_core-0.6.0/src/evidentia_core/models/finding.py +74 -0
- evidentia_core-0.6.0/src/evidentia_core/models/gap.py +201 -0
- evidentia_core-0.6.0/src/evidentia_core/models/gap_diff.py +149 -0
- evidentia_core-0.6.0/src/evidentia_core/models/obligation.py +173 -0
- evidentia_core-0.6.0/src/evidentia_core/models/risk.py +188 -0
- evidentia_core-0.6.0/src/evidentia_core/models/threat.py +202 -0
- evidentia_core-0.6.0/src/evidentia_core/network_guard.py +277 -0
- evidentia_core-0.6.0/src/evidentia_core/oscal/__init__.py +20 -0
- evidentia_core-0.6.0/src/evidentia_core/oscal/exporter.py +160 -0
- evidentia_core-0.6.0/src/evidentia_core/oscal/profile.py +466 -0
- evidentia_core-0.6.0/src/evidentia_core/py.typed +0 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# v0.4.0 — frontend build output lands in the Python package's static
|
|
2
|
+
# directory at wheel-assembly time via the hatchling build hook. The
|
|
3
|
+
# .gitkeep file in static/ is tracked; everything else is regenerated.
|
|
4
|
+
packages/evidentia-api/src/evidentia_api/static/assets/
|
|
5
|
+
packages/evidentia-api/src/evidentia_api/static/index.html
|
|
6
|
+
packages/evidentia-api/src/evidentia_api/static/*.js
|
|
7
|
+
packages/evidentia-api/src/evidentia_api/static/*.css
|
|
8
|
+
|
|
9
|
+
# Python
|
|
10
|
+
__pycache__/
|
|
11
|
+
*.py[cod]
|
|
12
|
+
*$py.class
|
|
13
|
+
*.so
|
|
14
|
+
.Python
|
|
15
|
+
build/
|
|
16
|
+
develop-eggs/
|
|
17
|
+
dist/
|
|
18
|
+
downloads/
|
|
19
|
+
eggs/
|
|
20
|
+
.eggs/
|
|
21
|
+
lib/
|
|
22
|
+
lib64/
|
|
23
|
+
parts/
|
|
24
|
+
sdist/
|
|
25
|
+
var/
|
|
26
|
+
wheels/
|
|
27
|
+
# NB: `lib/` and `lib64/` above would otherwise also match
|
|
28
|
+
# packages/evidentia-ui/src/lib/ (TypeScript utils). Scope to top-level
|
|
29
|
+
# only — there's no real Python-venv lib/ we'd fail to ignore because
|
|
30
|
+
# .venv/ and venv/ below cover that case.
|
|
31
|
+
!packages/evidentia-ui/src/lib/
|
|
32
|
+
*.egg-info/
|
|
33
|
+
.installed.cfg
|
|
34
|
+
*.egg
|
|
35
|
+
MANIFEST
|
|
36
|
+
|
|
37
|
+
# Virtual environments
|
|
38
|
+
.venv/
|
|
39
|
+
venv/
|
|
40
|
+
ENV/
|
|
41
|
+
env/
|
|
42
|
+
|
|
43
|
+
# uv
|
|
44
|
+
# NOTE: uv.lock is committed for reproducible builds.
|
|
45
|
+
# https://docs.astral.sh/uv/concepts/projects/sync/#locking-dependencies
|
|
46
|
+
|
|
47
|
+
# Testing
|
|
48
|
+
.pytest_cache/
|
|
49
|
+
.coverage
|
|
50
|
+
.coverage.*
|
|
51
|
+
htmlcov/
|
|
52
|
+
.tox/
|
|
53
|
+
.cache
|
|
54
|
+
coverage.xml
|
|
55
|
+
*.cover
|
|
56
|
+
.hypothesis/
|
|
57
|
+
|
|
58
|
+
# mypy
|
|
59
|
+
.mypy_cache/
|
|
60
|
+
.dmypy.json
|
|
61
|
+
dmypy.json
|
|
62
|
+
|
|
63
|
+
# Ruff
|
|
64
|
+
.ruff_cache/
|
|
65
|
+
|
|
66
|
+
# IDE
|
|
67
|
+
.vscode/
|
|
68
|
+
.idea/
|
|
69
|
+
*.swp
|
|
70
|
+
*.swo
|
|
71
|
+
*~
|
|
72
|
+
.DS_Store
|
|
73
|
+
|
|
74
|
+
# Claude Code local state
|
|
75
|
+
.claude/
|
|
76
|
+
|
|
77
|
+
# Evidentia runtime — user project state (NOT bundled examples).
|
|
78
|
+
# `.controlbridge/` and `/controlbridge.yaml` are kept ignored for the
|
|
79
|
+
# lifetime of the shim (through v0.7.0) so legacy project workspaces
|
|
80
|
+
# authored against v0.1.0 – v0.5.0 don't start leaking into git.
|
|
81
|
+
.evidentia/
|
|
82
|
+
.controlbridge/
|
|
83
|
+
/evidentia.yaml
|
|
84
|
+
/controlbridge.yaml
|
|
85
|
+
*.local.yaml
|
|
86
|
+
evidence/
|
|
87
|
+
reports/
|
|
88
|
+
risks/
|
|
89
|
+
|
|
90
|
+
# Generated reports from examples (keep source files, ignore generated ones)
|
|
91
|
+
examples/**/report.json
|
|
92
|
+
examples/**/report.csv
|
|
93
|
+
examples/**/report.md
|
|
94
|
+
examples/**/report.oscal.json
|
|
95
|
+
examples/**/risks.json
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: evidentia-core
|
|
3
|
+
Version: 0.6.0
|
|
4
|
+
Summary: Core data models, catalog loading, and gap analysis engine for Evidentia GRC tool
|
|
5
|
+
Project-URL: Homepage, https://github.com/allenfbyrd/evidentia
|
|
6
|
+
Project-URL: Repository, https://github.com/allenfbyrd/evidentia
|
|
7
|
+
Project-URL: Issues, https://github.com/allenfbyrd/evidentia/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/allenfbyrd/evidentia/blob/main/CHANGELOG.md
|
|
9
|
+
Author-email: Allen Byrd <allen@allenfbyrd.com>
|
|
10
|
+
License-Expression: Apache-2.0
|
|
11
|
+
Keywords: compliance,gap-analysis,grc,nist,oscal,pydantic,risk,soc2
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Information Technology
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.12
|
|
20
|
+
Requires-Dist: platformdirs>=4.3
|
|
21
|
+
Requires-Dist: pydantic-settings>=2.6
|
|
22
|
+
Requires-Dist: pydantic>=2.9
|
|
23
|
+
Requires-Dist: python-dateutil>=2.9
|
|
24
|
+
Requires-Dist: pyyaml>=6.0
|
|
25
|
+
Requires-Dist: thefuzz[speedup]>=0.22
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# evidentia-core
|
|
29
|
+
|
|
30
|
+
Core data models, OSCAL catalog loaders, and the gap analysis engine for [Evidentia](https://github.com/allenfbyrd/evidentia).
|
|
31
|
+
|
|
32
|
+
This package has no AI dependencies and can be installed standalone for environments that need only the gap analysis functionality.
|
|
33
|
+
|
|
34
|
+
## Provides
|
|
35
|
+
|
|
36
|
+
- **Pydantic v2 data models** for controls, evidence, risks, gaps, findings, catalogs, and system context
|
|
37
|
+
- **OSCAL catalog loader** for NIST 800-53, NIST CSF, and other OSCAL-formatted frameworks
|
|
38
|
+
- **Crosswalk engine** for cross-framework control mappings
|
|
39
|
+
- **Gap analyzer** that compares a control inventory against framework catalogs
|
|
40
|
+
- **Report formatters** for JSON, CSV, Markdown, and OSCAL Assessment Results
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install evidentia-core
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
License: Apache 2.0
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# evidentia-core
|
|
2
|
+
|
|
3
|
+
Core data models, OSCAL catalog loaders, and the gap analysis engine for [Evidentia](https://github.com/allenfbyrd/evidentia).
|
|
4
|
+
|
|
5
|
+
This package has no AI dependencies and can be installed standalone for environments that need only the gap analysis functionality.
|
|
6
|
+
|
|
7
|
+
## Provides
|
|
8
|
+
|
|
9
|
+
- **Pydantic v2 data models** for controls, evidence, risks, gaps, findings, catalogs, and system context
|
|
10
|
+
- **OSCAL catalog loader** for NIST 800-53, NIST CSF, and other OSCAL-formatted frameworks
|
|
11
|
+
- **Crosswalk engine** for cross-framework control mappings
|
|
12
|
+
- **Gap analyzer** that compares a control inventory against framework catalogs
|
|
13
|
+
- **Report formatters** for JSON, CSV, Markdown, and OSCAL Assessment Results
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install evidentia-core
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
License: Apache 2.0
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "evidentia-core"
|
|
3
|
+
version = "0.6.0"
|
|
4
|
+
description = "Core data models, catalog loading, and gap analysis engine for Evidentia GRC tool"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [{name = "Allen Byrd", email = "allen@allenfbyrd.com"}]
|
|
7
|
+
license = "Apache-2.0"
|
|
8
|
+
requires-python = ">=3.12"
|
|
9
|
+
keywords = ["grc", "compliance", "oscal", "nist", "soc2", "risk", "gap-analysis", "pydantic"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 3 - Alpha",
|
|
12
|
+
"Intended Audience :: Information Technology",
|
|
13
|
+
"License :: OSI Approved :: Apache Software License",
|
|
14
|
+
"Operating System :: OS Independent",
|
|
15
|
+
"Programming Language :: Python :: 3.12",
|
|
16
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
17
|
+
"Typing :: Typed",
|
|
18
|
+
]
|
|
19
|
+
dependencies = [
|
|
20
|
+
"pydantic>=2.9",
|
|
21
|
+
"pydantic-settings>=2.6",
|
|
22
|
+
"pyyaml>=6.0",
|
|
23
|
+
"python-dateutil>=2.9",
|
|
24
|
+
"thefuzz[speedup]>=0.22",
|
|
25
|
+
"platformdirs>=4.3",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://github.com/allenfbyrd/evidentia"
|
|
30
|
+
Repository = "https://github.com/allenfbyrd/evidentia"
|
|
31
|
+
Issues = "https://github.com/allenfbyrd/evidentia/issues"
|
|
32
|
+
Changelog = "https://github.com/allenfbyrd/evidentia/blob/main/CHANGELOG.md"
|
|
33
|
+
|
|
34
|
+
[build-system]
|
|
35
|
+
requires = ["hatchling"]
|
|
36
|
+
build-backend = "hatchling.build"
|
|
37
|
+
|
|
38
|
+
[tool.hatch.build.targets.wheel]
|
|
39
|
+
packages = ["src/evidentia_core"]
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Evidentia core: data models, OSCAL catalog loaders, and gap analysis engine."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import PackageNotFoundError
|
|
4
|
+
from importlib.metadata import version as _pkg_version
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
__version__ = _pkg_version("evidentia-core")
|
|
8
|
+
except PackageNotFoundError: # pragma: no cover — only hit in editable repos without install
|
|
9
|
+
__version__ = "0.0.0+unknown"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Framework catalog loading, crosswalks, and registry."""
|
|
2
|
+
|
|
3
|
+
from evidentia_core.catalogs.crosswalk import CrosswalkEngine
|
|
4
|
+
from evidentia_core.catalogs.loader import (
|
|
5
|
+
load_catalog,
|
|
6
|
+
load_evidentia_catalog,
|
|
7
|
+
load_oscal_catalog,
|
|
8
|
+
)
|
|
9
|
+
from evidentia_core.catalogs.registry import FrameworkRegistry
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"CrosswalkEngine",
|
|
13
|
+
"FrameworkRegistry",
|
|
14
|
+
"load_catalog",
|
|
15
|
+
"load_evidentia_catalog",
|
|
16
|
+
"load_oscal_catalog",
|
|
17
|
+
]
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"""Cross-framework mapping engine.
|
|
2
|
+
|
|
3
|
+
Loads crosswalk definitions and provides bidirectional mapping between
|
|
4
|
+
framework controls. The mapping graph is built at startup and cached
|
|
5
|
+
for fast lookups during gap analysis.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import logging
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from evidentia_core.models.catalog import (
|
|
15
|
+
CrosswalkDefinition,
|
|
16
|
+
FrameworkMapping,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
MAPPINGS_DIR = Path(__file__).parent / "data" / "mappings"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class CrosswalkEngine:
|
|
25
|
+
"""Bidirectional cross-framework control mapping engine.
|
|
26
|
+
|
|
27
|
+
Loads all available crosswalk definitions and builds an in-memory
|
|
28
|
+
mapping graph for fast lookups.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(self, mappings_dir: Path | None = None) -> None:
|
|
32
|
+
self._dir = mappings_dir or MAPPINGS_DIR
|
|
33
|
+
# Forward index: (source_fw, source_ctl, target_fw) → [FrameworkMapping]
|
|
34
|
+
self._forward: dict[tuple[str, str, str], list[FrameworkMapping]] = {}
|
|
35
|
+
# Reverse index built from each forward entry
|
|
36
|
+
self._reverse: dict[tuple[str, str, str], list[FrameworkMapping]] = {}
|
|
37
|
+
self._crosswalks: list[CrosswalkDefinition] = []
|
|
38
|
+
|
|
39
|
+
def load_all(self) -> None:
|
|
40
|
+
"""Load all crosswalk JSON files from the mappings directory."""
|
|
41
|
+
if not self._dir.exists():
|
|
42
|
+
logger.warning("Mappings directory not found: %s", self._dir)
|
|
43
|
+
return
|
|
44
|
+
|
|
45
|
+
for json_file in sorted(self._dir.glob("*.json")):
|
|
46
|
+
self.load_crosswalk(json_file)
|
|
47
|
+
|
|
48
|
+
logger.info(
|
|
49
|
+
"Loaded %d crosswalks with %d total mappings",
|
|
50
|
+
len(self._crosswalks),
|
|
51
|
+
sum(len(c.mappings) for c in self._crosswalks),
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
def load_crosswalk(self, path: Path) -> CrosswalkDefinition:
|
|
55
|
+
"""Load a single crosswalk definition and index it."""
|
|
56
|
+
with open(path, encoding="utf-8") as f:
|
|
57
|
+
data = json.load(f)
|
|
58
|
+
|
|
59
|
+
crosswalk = CrosswalkDefinition(**data)
|
|
60
|
+
self._crosswalks.append(crosswalk)
|
|
61
|
+
|
|
62
|
+
for mapping in crosswalk.mappings:
|
|
63
|
+
src_key = (
|
|
64
|
+
crosswalk.source_framework,
|
|
65
|
+
mapping.source_control_id.upper(),
|
|
66
|
+
crosswalk.target_framework,
|
|
67
|
+
)
|
|
68
|
+
self._forward.setdefault(src_key, []).append(mapping)
|
|
69
|
+
|
|
70
|
+
# Reverse mapping (swap source/target)
|
|
71
|
+
rev_mapping = FrameworkMapping(
|
|
72
|
+
source_control_id=mapping.target_control_id,
|
|
73
|
+
source_control_title=mapping.target_control_title,
|
|
74
|
+
target_control_id=mapping.source_control_id,
|
|
75
|
+
target_control_title=mapping.source_control_title,
|
|
76
|
+
relationship=mapping.relationship,
|
|
77
|
+
notes=mapping.notes,
|
|
78
|
+
)
|
|
79
|
+
rev_key = (
|
|
80
|
+
crosswalk.target_framework,
|
|
81
|
+
mapping.target_control_id.upper(),
|
|
82
|
+
crosswalk.source_framework,
|
|
83
|
+
)
|
|
84
|
+
self._reverse.setdefault(rev_key, []).append(rev_mapping)
|
|
85
|
+
|
|
86
|
+
return crosswalk
|
|
87
|
+
|
|
88
|
+
def get_mapped_controls(
|
|
89
|
+
self,
|
|
90
|
+
source_framework: str,
|
|
91
|
+
source_control_id: str,
|
|
92
|
+
target_framework: str,
|
|
93
|
+
) -> list[FrameworkMapping]:
|
|
94
|
+
"""Get controls in target_framework that map from source_control_id.
|
|
95
|
+
|
|
96
|
+
Checks both forward and reverse indexes.
|
|
97
|
+
"""
|
|
98
|
+
ctl = source_control_id.strip().upper()
|
|
99
|
+
|
|
100
|
+
forward_key = (source_framework, ctl, target_framework)
|
|
101
|
+
forward_results = self._forward.get(forward_key, [])
|
|
102
|
+
|
|
103
|
+
reverse_key = (source_framework, ctl, target_framework)
|
|
104
|
+
reverse_results = self._reverse.get(reverse_key, [])
|
|
105
|
+
|
|
106
|
+
# Deduplicate by target_control_id
|
|
107
|
+
seen: set[str] = set()
|
|
108
|
+
results: list[FrameworkMapping] = []
|
|
109
|
+
for m in forward_results + reverse_results:
|
|
110
|
+
if m.target_control_id.upper() not in seen:
|
|
111
|
+
seen.add(m.target_control_id.upper())
|
|
112
|
+
results.append(m)
|
|
113
|
+
|
|
114
|
+
return results
|
|
115
|
+
|
|
116
|
+
def get_all_mapped_controls(
|
|
117
|
+
self,
|
|
118
|
+
framework: str,
|
|
119
|
+
control_id: str,
|
|
120
|
+
) -> dict[str, list[FrameworkMapping]]:
|
|
121
|
+
"""Get all controls across ALL frameworks that map to/from this control.
|
|
122
|
+
|
|
123
|
+
Returns a dict keyed by target framework ID.
|
|
124
|
+
"""
|
|
125
|
+
ctl = control_id.strip().upper()
|
|
126
|
+
results: dict[str, list[FrameworkMapping]] = {}
|
|
127
|
+
|
|
128
|
+
for (src_fw, src_ctl, tgt_fw), mappings in self._forward.items():
|
|
129
|
+
if src_fw == framework and src_ctl == ctl:
|
|
130
|
+
results.setdefault(tgt_fw, []).extend(mappings)
|
|
131
|
+
|
|
132
|
+
for (src_fw, src_ctl, tgt_fw), mappings in self._reverse.items():
|
|
133
|
+
if src_fw == framework and src_ctl == ctl:
|
|
134
|
+
results.setdefault(tgt_fw, []).extend(mappings)
|
|
135
|
+
|
|
136
|
+
return results
|
|
137
|
+
|
|
138
|
+
def get_cross_framework_value(
|
|
139
|
+
self,
|
|
140
|
+
framework: str,
|
|
141
|
+
control_id: str,
|
|
142
|
+
) -> list[str]:
|
|
143
|
+
"""Get a flat list of 'framework:control_id' pairs that this control maps to.
|
|
144
|
+
|
|
145
|
+
Used for gap prioritization — controls that satisfy more frameworks
|
|
146
|
+
are higher value to implement.
|
|
147
|
+
"""
|
|
148
|
+
all_mappings = self.get_all_mapped_controls(framework, control_id)
|
|
149
|
+
result: list[str] = []
|
|
150
|
+
for target_fw, mappings in all_mappings.items():
|
|
151
|
+
for m in mappings:
|
|
152
|
+
result.append(f"{target_fw}:{m.target_control_id}")
|
|
153
|
+
return result
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def available_frameworks(self) -> set[str]:
|
|
157
|
+
"""All framework IDs that appear in loaded crosswalks."""
|
|
158
|
+
frameworks: set[str] = set()
|
|
159
|
+
for crosswalk in self._crosswalks:
|
|
160
|
+
frameworks.add(crosswalk.source_framework)
|
|
161
|
+
frameworks.add(crosswalk.target_framework)
|
|
162
|
+
return frameworks
|