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.
Files changed (125) hide show
  1. evidentia_core-0.6.0/.gitignore +95 -0
  2. evidentia_core-0.6.0/PKG-INFO +48 -0
  3. evidentia_core-0.6.0/README.md +21 -0
  4. evidentia_core-0.6.0/pyproject.toml +39 -0
  5. evidentia_core-0.6.0/src/evidentia_core/__init__.py +9 -0
  6. evidentia_core-0.6.0/src/evidentia_core/catalogs/__init__.py +17 -0
  7. evidentia_core-0.6.0/src/evidentia_core/catalogs/crosswalk.py +162 -0
  8. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/frameworks.yaml +827 -0
  9. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/international/au-essential-8.json +62 -0
  10. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/international/au-ism.json +176 -0
  11. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/international/canada-itsg-33.json +118 -0
  12. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/international/canada-pipeda.json +105 -0
  13. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/international/eu-ai-act.json +146 -0
  14. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/international/eu-dora.json +116 -0
  15. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/international/eu-gdpr.json +241 -0
  16. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/international/eu-nis2.json +104 -0
  17. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/international/nz-nzism.json +172 -0
  18. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/international/uk-cyber-essentials.json +44 -0
  19. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/international/uk-dpa-2018.json +79 -0
  20. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/international/uk-ncsc-caf-3.2.json +101 -0
  21. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/mappings/fedramp-rev5-moderate_to_cmmc-2-l2.json +265 -0
  22. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/mappings/iso-27001-2022_to_nist-800-53-mod.json +193 -0
  23. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/mappings/nist-800-53-mod_to_hipaa-security.json +169 -0
  24. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/mappings/nist-800-53-rev5_to_soc2-tsc.json +145 -0
  25. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/mappings/nist-csf-2.0_to_nist-800-53-mod.json +297 -0
  26. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/mappings/us-va-vcdpa_to_us-ca-ccpa-cpra.json +113 -0
  27. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-ca-ccpa-cpra.json +116 -0
  28. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-co-cpa.json +107 -0
  29. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-ct-ctdpa.json +107 -0
  30. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-de-dpdpa.json +106 -0
  31. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-fl-fdbr.json +107 -0
  32. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-ia-icdpa.json +103 -0
  33. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-md-mdpa.json +108 -0
  34. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-mn-mncdpa.json +107 -0
  35. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-mt-mcdpa.json +106 -0
  36. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-nh-nhpa.json +107 -0
  37. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-or-ocpa.json +107 -0
  38. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-tn-tipa.json +108 -0
  39. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-tx-tdpsa.json +107 -0
  40. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-ut-ucpa.json +104 -0
  41. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/state-privacy/us-va-vcdpa.json +107 -0
  42. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/cis-benchmark-aws.json +217 -0
  43. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/cis-benchmark-azure.json +217 -0
  44. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/cis-benchmark-gcp.json +217 -0
  45. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/cis-benchmark-kubernetes.json +217 -0
  46. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/cis-benchmark-rhel9.json +217 -0
  47. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/cis-controls-v8.1.json +197 -0
  48. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/cobit-2019.json +421 -0
  49. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/hitrust-csf-v11.json +985 -0
  50. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/iec-62443.json +100 -0
  51. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/iso-22301-2019.json +87 -0
  52. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/iso-27001-2022.json +950 -0
  53. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/iso-27002-2022.json +950 -0
  54. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/iso-27017-2015.json +87 -0
  55. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/iso-27018-2019.json +267 -0
  56. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/iso-27701-2019.json +508 -0
  57. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/iso-42001-2023.json +397 -0
  58. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/pci-dss-4.0.1.json +142 -0
  59. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/scf-2024.json +977 -0
  60. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/soc2-tsc.json +81 -0
  61. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/stubs/swift-cscf-2024.json +353 -0
  62. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/threats/cisa-kev.json +134 -0
  63. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/threats/mitre-attack-enterprise.json +615 -0
  64. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/threats/mitre-capec.json +102 -0
  65. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/threats/mitre-cwe.json +237 -0
  66. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/cisa-cpgs.json +234 -0
  67. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/cjis-v6.json +169 -0
  68. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/cmmc-2-l1.json +121 -0
  69. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/cmmc-2-l2.json +687 -0
  70. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/cmmc-2-l3.json +807 -0
  71. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/cms-ars-5.1.json +146 -0
  72. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/fda-21-cfr-pt11.json +139 -0
  73. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/fedramp-rev5-high.json +2457 -0
  74. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/fedramp-rev5-li-saas.json +933 -0
  75. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/fedramp-rev5-low.json +783 -0
  76. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/fedramp-rev5-moderate.json +2007 -0
  77. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/glba-safeguards.json +129 -0
  78. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/hipaa-breach.json +64 -0
  79. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/hipaa-privacy.json +112 -0
  80. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/hipaa-security.json +396 -0
  81. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/irs-1075.json +150 -0
  82. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nerc-cip-v7.json +103 -0
  83. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-800-171-r2.json +687 -0
  84. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-800-171-r3.json +612 -0
  85. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-800-172.json +227 -0
  86. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-800-53-mod.json +146 -0
  87. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-800-53-rev5-high.json +9547 -0
  88. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-800-53-rev5-low.json +4643 -0
  89. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-800-53-rev5-moderate.json +7806 -0
  90. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-800-53-rev5-privacy.json +2996 -0
  91. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-800-53-rev5.json +254987 -0
  92. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-ai-rmf-1.0.json +449 -0
  93. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-csf-2.0.json +147 -0
  94. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-privacy-framework-1.0.json +594 -0
  95. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/nist-ssdf-800-218.json +269 -0
  96. evidentia_core-0.6.0/src/evidentia_core/catalogs/data/us-federal/ny-dfs-500.json +188 -0
  97. evidentia_core-0.6.0/src/evidentia_core/catalogs/loader.py +306 -0
  98. evidentia_core-0.6.0/src/evidentia_core/catalogs/manifest.py +156 -0
  99. evidentia_core-0.6.0/src/evidentia_core/catalogs/registry.py +142 -0
  100. evidentia_core-0.6.0/src/evidentia_core/catalogs/user_dir.py +145 -0
  101. evidentia_core-0.6.0/src/evidentia_core/config.py +231 -0
  102. evidentia_core-0.6.0/src/evidentia_core/gap_analyzer/__init__.py +17 -0
  103. evidentia_core-0.6.0/src/evidentia_core/gap_analyzer/analyzer.py +547 -0
  104. evidentia_core-0.6.0/src/evidentia_core/gap_analyzer/inventory.py +319 -0
  105. evidentia_core-0.6.0/src/evidentia_core/gap_analyzer/normalizer.py +102 -0
  106. evidentia_core-0.6.0/src/evidentia_core/gap_analyzer/reporter.py +157 -0
  107. evidentia_core-0.6.0/src/evidentia_core/gap_diff.py +327 -0
  108. evidentia_core-0.6.0/src/evidentia_core/gap_store.py +127 -0
  109. evidentia_core-0.6.0/src/evidentia_core/init_wizard.py +424 -0
  110. evidentia_core-0.6.0/src/evidentia_core/models/__init__.py +110 -0
  111. evidentia_core-0.6.0/src/evidentia_core/models/catalog.py +278 -0
  112. evidentia_core-0.6.0/src/evidentia_core/models/common.py +87 -0
  113. evidentia_core-0.6.0/src/evidentia_core/models/control.py +150 -0
  114. evidentia_core-0.6.0/src/evidentia_core/models/evidence.py +223 -0
  115. evidentia_core-0.6.0/src/evidentia_core/models/finding.py +74 -0
  116. evidentia_core-0.6.0/src/evidentia_core/models/gap.py +201 -0
  117. evidentia_core-0.6.0/src/evidentia_core/models/gap_diff.py +149 -0
  118. evidentia_core-0.6.0/src/evidentia_core/models/obligation.py +173 -0
  119. evidentia_core-0.6.0/src/evidentia_core/models/risk.py +188 -0
  120. evidentia_core-0.6.0/src/evidentia_core/models/threat.py +202 -0
  121. evidentia_core-0.6.0/src/evidentia_core/network_guard.py +277 -0
  122. evidentia_core-0.6.0/src/evidentia_core/oscal/__init__.py +20 -0
  123. evidentia_core-0.6.0/src/evidentia_core/oscal/exporter.py +160 -0
  124. evidentia_core-0.6.0/src/evidentia_core/oscal/profile.py +466 -0
  125. 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