cbomscanner 0.2.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 (39) hide show
  1. cbomscanner-0.2.0/.gitignore +16 -0
  2. cbomscanner-0.2.0/PKG-INFO +54 -0
  3. cbomscanner-0.2.0/README.md +12 -0
  4. cbomscanner-0.2.0/pyproject.toml +66 -0
  5. cbomscanner-0.2.0/src/cbomscanner/__init__.py +3 -0
  6. cbomscanner-0.2.0/src/cbomscanner/ai/__init__.py +0 -0
  7. cbomscanner-0.2.0/src/cbomscanner/ai/roadmap_agent.py +118 -0
  8. cbomscanner-0.2.0/src/cbomscanner/classifier/__init__.py +0 -0
  9. cbomscanner-0.2.0/src/cbomscanner/classifier/risk_classifier.py +58 -0
  10. cbomscanner-0.2.0/src/cbomscanner/classifier/rules.py +121 -0
  11. cbomscanner-0.2.0/src/cbomscanner/cli.py +259 -0
  12. cbomscanner-0.2.0/src/cbomscanner/formatters/__init__.py +0 -0
  13. cbomscanner-0.2.0/src/cbomscanner/formatters/html_formatter.py +89 -0
  14. cbomscanner-0.2.0/src/cbomscanner/formatters/json_formatter.py +8 -0
  15. cbomscanner-0.2.0/src/cbomscanner/formatters/sarif_formatter.py +57 -0
  16. cbomscanner-0.2.0/src/cbomscanner/formatters/summary_formatter.py +64 -0
  17. cbomscanner-0.2.0/src/cbomscanner/generator/__init__.py +0 -0
  18. cbomscanner-0.2.0/src/cbomscanner/generator/cbom_generator.py +103 -0
  19. cbomscanner-0.2.0/src/cbomscanner/models/__init__.py +0 -0
  20. cbomscanner-0.2.0/src/cbomscanner/models/cyclonedx.py +98 -0
  21. cbomscanner-0.2.0/src/cbomscanner/models/finding.py +40 -0
  22. cbomscanner-0.2.0/src/cbomscanner/scanners/__init__.py +11 -0
  23. cbomscanner-0.2.0/src/cbomscanner/scanners/base.py +51 -0
  24. cbomscanner-0.2.0/src/cbomscanner/scanners/go_scanner.py +64 -0
  25. cbomscanner-0.2.0/src/cbomscanner/scanners/java_scanner.py +62 -0
  26. cbomscanner-0.2.0/src/cbomscanner/scanners/javascript_scanner.py +165 -0
  27. cbomscanner-0.2.0/src/cbomscanner/scanners/manifest_scanner.py +100 -0
  28. cbomscanner-0.2.0/src/cbomscanner/scanners/python_scanner.py +121 -0
  29. cbomscanner-0.2.0/tests/__init__.py +0 -0
  30. cbomscanner-0.2.0/tests/fixtures/javascript_ecdsa.js +8 -0
  31. cbomscanner-0.2.0/tests/fixtures/package.json +11 -0
  32. cbomscanner-0.2.0/tests/fixtures/python_rsa.py +9 -0
  33. cbomscanner-0.2.0/tests/integration/__init__.py +0 -0
  34. cbomscanner-0.2.0/tests/integration/test_cli.py +84 -0
  35. cbomscanner-0.2.0/tests/unit/__init__.py +0 -0
  36. cbomscanner-0.2.0/tests/unit/test_cbom_generator.py +66 -0
  37. cbomscanner-0.2.0/tests/unit/test_cyclonedx_models.py +72 -0
  38. cbomscanner-0.2.0/tests/unit/test_python_scanner.py +51 -0
  39. cbomscanner-0.2.0/tests/unit/test_risk_classifier.py +56 -0
@@ -0,0 +1,16 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ .coverage
5
+ .pytest_cache/
6
+ .mypy_cache/
7
+ .ruff_cache/
8
+ dist/
9
+ build/
10
+ *.egg-info/
11
+ .venv/
12
+ venv/
13
+ htmlcov/
14
+ *.log
15
+ cbom.json
16
+ report.html
@@ -0,0 +1,54 @@
1
+ Metadata-Version: 2.4
2
+ Name: cbomscanner
3
+ Version: 0.2.0
4
+ Summary: CycloneDX 1.7 Cryptography Bill of Materials (CBOM) scanner for post-quantum compliance
5
+ Project-URL: Homepage, https://cbomscanner.com
6
+ Project-URL: Issues, https://github.com/Ganeshmancity/cbomscanner/issues
7
+ Author-email: Ganesh Netha <ganeshchilupuri99@gmail.com>
8
+ License: Apache-2.0
9
+ Keywords: cbom,compliance,cryptography,cyclonedx,nist,post-quantum,pqc,security
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: Apache Software License
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Security
18
+ Classifier: Topic :: Software Development :: Quality Assurance
19
+ Requires-Python: >=3.11
20
+ Requires-Dist: click>=8.1
21
+ Requires-Dist: pydantic>=2.7
22
+ Provides-Extra: ai
23
+ Requires-Dist: anthropic>=0.40; extra == 'ai'
24
+ Provides-Extra: dev
25
+ Requires-Dist: anthropic>=0.40; extra == 'dev'
26
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
27
+ Requires-Dist: pytest>=8.0; extra == 'dev'
28
+ Requires-Dist: ruff>=0.4; extra == 'dev'
29
+ Requires-Dist: tree-sitter-javascript>=0.23; extra == 'dev'
30
+ Requires-Dist: tree-sitter-typescript>=0.23; extra == 'dev'
31
+ Requires-Dist: tree-sitter>=0.23; extra == 'dev'
32
+ Provides-Extra: full
33
+ Requires-Dist: anthropic>=0.40; extra == 'full'
34
+ Requires-Dist: tree-sitter-javascript>=0.23; extra == 'full'
35
+ Requires-Dist: tree-sitter-typescript>=0.23; extra == 'full'
36
+ Requires-Dist: tree-sitter>=0.23; extra == 'full'
37
+ Provides-Extra: js
38
+ Requires-Dist: tree-sitter-javascript>=0.23; extra == 'js'
39
+ Requires-Dist: tree-sitter-typescript>=0.23; extra == 'js'
40
+ Requires-Dist: tree-sitter>=0.23; extra == 'js'
41
+ Description-Content-Type: text/markdown
42
+
43
+ # cbomscanner
44
+
45
+ Generate a CycloneDX 1.7 Cryptography Bill of Materials (CBOM) for post-quantum compliance.
46
+
47
+ ```bash
48
+ pip install cbomscanner
49
+ cbomscanner scan ./src
50
+ cbomscanner scan ./src --format summary
51
+ cbomscanner scan ./src --ai-roadmap --key $ANTHROPIC_API_KEY
52
+ ```
53
+
54
+ Detects quantum-vulnerable cryptography (RSA, ECDSA, DH, DSA, MD5, SHA-1) across Python, JavaScript, TypeScript, Java, Go, and dependency manifests. Outputs valid CycloneDX 1.7 CBOM JSON — the compliance artifact required by EU CRA, DORA, and NIST guidance.
@@ -0,0 +1,12 @@
1
+ # cbomscanner
2
+
3
+ Generate a CycloneDX 1.7 Cryptography Bill of Materials (CBOM) for post-quantum compliance.
4
+
5
+ ```bash
6
+ pip install cbomscanner
7
+ cbomscanner scan ./src
8
+ cbomscanner scan ./src --format summary
9
+ cbomscanner scan ./src --ai-roadmap --key $ANTHROPIC_API_KEY
10
+ ```
11
+
12
+ Detects quantum-vulnerable cryptography (RSA, ECDSA, DH, DSA, MD5, SHA-1) across Python, JavaScript, TypeScript, Java, Go, and dependency manifests. Outputs valid CycloneDX 1.7 CBOM JSON — the compliance artifact required by EU CRA, DORA, and NIST guidance.
@@ -0,0 +1,66 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "cbomscanner"
7
+ version = "0.2.0"
8
+ description = "CycloneDX 1.7 Cryptography Bill of Materials (CBOM) scanner for post-quantum compliance"
9
+ readme = "README.md"
10
+ license = { text = "Apache-2.0" }
11
+ requires-python = ">=3.11"
12
+ authors = [{ name = "Ganesh Netha", email = "ganeshchilupuri99@gmail.com" }]
13
+ keywords = ["security", "cryptography", "post-quantum", "cyclonedx", "cbom", "nist", "pqc", "compliance"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Environment :: Console",
17
+ "Intended Audience :: Developers",
18
+ "Topic :: Security",
19
+ "Topic :: Software Development :: Quality Assurance",
20
+ "License :: OSI Approved :: Apache Software License",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ ]
25
+ dependencies = [
26
+ "click>=8.1",
27
+ "pydantic>=2.7",
28
+ ]
29
+
30
+ [project.optional-dependencies]
31
+ js = [
32
+ "tree-sitter>=0.23",
33
+ "tree-sitter-javascript>=0.23",
34
+ "tree-sitter-typescript>=0.23",
35
+ ]
36
+ ai = [
37
+ "anthropic>=0.40",
38
+ ]
39
+ full = ["cbomscanner[js,ai]"]
40
+ dev = [
41
+ "cbomscanner[full]",
42
+ "pytest>=8.0",
43
+ "pytest-cov>=5.0",
44
+ "ruff>=0.4",
45
+ ]
46
+
47
+ [project.scripts]
48
+ cbomscanner = "cbomscanner.cli:cli"
49
+
50
+ [project.urls]
51
+ Homepage = "https://cbomscanner.com"
52
+ Issues = "https://github.com/Ganeshmancity/cbomscanner/issues"
53
+
54
+ [tool.hatch.build.targets.wheel]
55
+ packages = ["src/cbomscanner"]
56
+
57
+ [tool.ruff]
58
+ target-version = "py311"
59
+ line-length = 100
60
+
61
+ [tool.ruff.lint]
62
+ select = ["E", "F", "I", "UP", "B"]
63
+
64
+ [tool.pytest.ini_options]
65
+ testpaths = ["tests"]
66
+ addopts = "--cov=cbomscanner --cov-report=term-missing -q"
@@ -0,0 +1,3 @@
1
+ """cbom-scan — CycloneDX 1.7 CBOM generator for post-quantum compliance."""
2
+
3
+ __version__ = "0.1.0"
File without changes
@@ -0,0 +1,118 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import logging
5
+ import re
6
+ from dataclasses import dataclass
7
+
8
+ from cbomscanner.models.cyclonedx import CycloneDXBom
9
+
10
+ _log = logging.getLogger("cbomscanner.roadmap")
11
+
12
+ _SYSTEM = """You are a post-quantum cryptography migration expert.
13
+ Given a CycloneDX 1.7 CBOM (Cryptography Bill of Materials) from a real codebase,
14
+ produce a prioritized migration roadmap.
15
+
16
+ Return ONLY valid JSON (no markdown):
17
+ {
18
+ "executive_summary": "2-sentence CISO-level summary",
19
+ "tasks": [
20
+ {
21
+ "rank": 1,
22
+ "algorithm": "RSA",
23
+ "location": "src/auth/jwt.py:34",
24
+ "why_first": "one sentence — why this is the highest priority",
25
+ "effort_days": 3.0,
26
+ "migration_diff": "# BEFORE\\nfrom Crypto.PublicKey import RSA\\nkey = RSA.generate(2048)\\n\\n# AFTER\\nfrom oqs import KeyEncapsulation\\nkem = KeyEncapsulation('ML-KEM-768')\\npub = kem.generate_keypair()",
27
+ "nist_replacement": "ML-KEM-768 (FIPS 203)"
28
+ }
29
+ ],
30
+ "total_effort_days": 5.5
31
+ }
32
+
33
+ Rules:
34
+ - Rank by impact: signing keys first (break auth), then encryption, then hashing
35
+ - Show top 5 tasks only
36
+ - migration_diff must be actual code for the detected language (infer from file extension)
37
+ - Be specific: use the exact file path and line number from the CBOM evidence"""
38
+
39
+
40
+ @dataclass
41
+ class MigrationTask:
42
+ rank: int
43
+ algorithm: str
44
+ location: str
45
+ why_first: str
46
+ effort_days: float
47
+ migration_diff: str
48
+ nist_replacement: str
49
+
50
+
51
+ @dataclass
52
+ class RoadmapResult:
53
+ executive_summary: str
54
+ tasks: list[MigrationTask]
55
+ total_effort_days: float
56
+
57
+
58
+ class RoadmapAgent:
59
+ """
60
+ Calls Claude with the CBOM JSON and returns a prioritized migration roadmap.
61
+ Returns None gracefully when api_key is missing or Claude call fails.
62
+ """
63
+
64
+ def __init__(self, api_key: str | None = None) -> None:
65
+ self._api_key = api_key
66
+
67
+ @property
68
+ def available(self) -> bool:
69
+ return bool(self._api_key)
70
+
71
+ def generate(self, bom: CycloneDXBom) -> RoadmapResult | None:
72
+ if not self.available:
73
+ return None
74
+ try:
75
+ import anthropic
76
+ except ImportError:
77
+ _log.warning("anthropic package not installed. Run: pip install cbom-scan[ai]")
78
+ return None
79
+
80
+ cbom_json = bom.to_json(indent=2)
81
+ prompt = (
82
+ f"Here is the CBOM for a software project:\n\n```json\n{cbom_json}\n```\n\n"
83
+ "Produce the migration roadmap JSON."
84
+ )
85
+
86
+ try:
87
+ client = anthropic.Anthropic(api_key=self._api_key)
88
+ message = client.messages.create(
89
+ model = "claude-sonnet-4-6",
90
+ max_tokens = 1500,
91
+ system = _SYSTEM,
92
+ messages = [{"role": "user", "content": prompt}],
93
+ )
94
+ raw = message.content[0].text
95
+ m = re.search(r"\{.*\}", raw, re.DOTALL)
96
+ if not m:
97
+ return None
98
+ data = json.loads(m.group(0))
99
+ tasks = [
100
+ MigrationTask(
101
+ rank = t.get("rank", i + 1),
102
+ algorithm = t.get("algorithm", ""),
103
+ location = t.get("location", ""),
104
+ why_first = t.get("why_first", ""),
105
+ effort_days = float(t.get("effort_days", 1.0)),
106
+ migration_diff = t.get("migration_diff", ""),
107
+ nist_replacement = t.get("nist_replacement", ""),
108
+ )
109
+ for i, t in enumerate(data.get("tasks", [])[:5])
110
+ ]
111
+ return RoadmapResult(
112
+ executive_summary = data.get("executive_summary", ""),
113
+ tasks = tasks,
114
+ total_effort_days = float(data.get("total_effort_days", 0.0)),
115
+ )
116
+ except Exception as exc:
117
+ _log.warning(f"RoadmapAgent failed: {exc}")
118
+ return None
@@ -0,0 +1,58 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from cbomscanner.classifier.rules import NIST_RULES, RuleEntry
6
+ from cbomscanner.models.finding import CryptoFinding
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class RiskProfile:
11
+ risk_label: str
12
+ nist_quantum_security_level: int
13
+ primitive: str
14
+ nist_replacement: str
15
+ effort_days: float
16
+ crypto_functions: list[str]
17
+ certification_level: list[str]
18
+ parameter_set_identifier: str
19
+
20
+
21
+ _UNKNOWN_RULE: RuleEntry = {
22
+ "primitive": "unknown",
23
+ "nist_quantum_security_level": 0,
24
+ "nist_replacement": "Review manually",
25
+ "effort_days": 1.0,
26
+ "risk_label": "HIGH",
27
+ "crypto_functions": [],
28
+ "certification_level": ["none"],
29
+ "parameter_set_identifier": "unknown",
30
+ }
31
+
32
+
33
+ class RiskClassifier:
34
+ """
35
+ Pure function classifier. No I/O, no state.
36
+ Maps a CryptoFinding to a RiskProfile using the NIST_RULES table.
37
+ """
38
+
39
+ def classify(self, finding: CryptoFinding) -> RiskProfile:
40
+ rule = NIST_RULES.get(finding.algorithm.upper(), _UNKNOWN_RULE)
41
+ return RiskProfile(
42
+ risk_label = rule["risk_label"],
43
+ nist_quantum_security_level = rule["nist_quantum_security_level"],
44
+ primitive = rule["primitive"],
45
+ nist_replacement = rule["nist_replacement"],
46
+ effort_days = rule["effort_days"],
47
+ crypto_functions = list(rule["crypto_functions"]),
48
+ certification_level = list(rule["certification_level"]),
49
+ parameter_set_identifier = rule["parameter_set_identifier"],
50
+ )
51
+
52
+ def classify_all(self, findings: list[CryptoFinding]) -> dict[str, RiskProfile]:
53
+ seen: dict[str, RiskProfile] = {}
54
+ for f in findings:
55
+ algo = f.algorithm.upper()
56
+ if algo not in seen:
57
+ seen[algo] = self.classify(f)
58
+ return seen
@@ -0,0 +1,121 @@
1
+ """
2
+ NIST PQC status database.
3
+ Source: NIST IR 8547 (2024), FIPS 203/204/205, NSA CNSA 2.0.
4
+
5
+ nistQuantumSecurityLevel meanings:
6
+ 0 = broken by quantum (Shor's algorithm) — RSA, ECC, DH, DSA
7
+ 0 = classically weak — MD5, SHA-1
8
+ 1 = acceptable short-term (classical only)
9
+ 3 = 128-bit post-quantum security (Grover halves symmetric key strength)
10
+ 5 = 192-bit post-quantum security
11
+ 6 = 256-bit post-quantum security (max level)
12
+ """
13
+
14
+ from __future__ import annotations
15
+ from typing import TypedDict
16
+
17
+
18
+ class RuleEntry(TypedDict):
19
+ primitive: str
20
+ nist_quantum_security_level: int
21
+ nist_replacement: str
22
+ effort_days: float
23
+ risk_label: str # "CRITICAL" | "HIGH" | "MEDIUM" | "LOW"
24
+ crypto_functions: list[str]
25
+ certification_level: list[str]
26
+ parameter_set_identifier: str
27
+
28
+
29
+ NIST_RULES: dict[str, RuleEntry] = {
30
+ "RSA": {
31
+ "primitive": "pke",
32
+ "nist_quantum_security_level": 0,
33
+ "nist_replacement": "ML-KEM-768 (FIPS 203) for KEM, ML-DSA-65 (FIPS 204) for signing",
34
+ "effort_days": 3.0,
35
+ "risk_label": "CRITICAL",
36
+ "crypto_functions": ["keygen", "encrypt", "decrypt", "sign", "verify"],
37
+ "certification_level": ["none"],
38
+ "parameter_set_identifier": "2048",
39
+ },
40
+ "ECDSA": {
41
+ "primitive": "signature",
42
+ "nist_quantum_security_level": 0,
43
+ "nist_replacement": "ML-DSA-65 (FIPS 204) for signing, ML-KEM-768 (FIPS 203) for KEM",
44
+ "effort_days": 2.5,
45
+ "risk_label": "CRITICAL",
46
+ "crypto_functions": ["keygen", "sign", "verify"],
47
+ "certification_level": ["none"],
48
+ "parameter_set_identifier": "P-256",
49
+ },
50
+ "DH": {
51
+ "primitive": "pke",
52
+ "nist_quantum_security_level": 0,
53
+ "nist_replacement": "ML-KEM-768 (FIPS 203)",
54
+ "effort_days": 2.0,
55
+ "risk_label": "CRITICAL",
56
+ "crypto_functions": ["keygen", "keyagreement"],
57
+ "certification_level": ["none"],
58
+ "parameter_set_identifier": "2048",
59
+ },
60
+ "DSA": {
61
+ "primitive": "signature",
62
+ "nist_quantum_security_level": 0,
63
+ "nist_replacement": "ML-DSA-65 (FIPS 204)",
64
+ "effort_days": 2.0,
65
+ "risk_label": "CRITICAL",
66
+ "crypto_functions": ["keygen", "sign", "verify"],
67
+ "certification_level": ["none"],
68
+ "parameter_set_identifier": "2048",
69
+ },
70
+ "MD5": {
71
+ "primitive": "hash",
72
+ "nist_quantum_security_level": 0,
73
+ "nist_replacement": "SHA-256 or SHA3-256",
74
+ "effort_days": 0.5,
75
+ "risk_label": "HIGH",
76
+ "crypto_functions": ["digest"],
77
+ "certification_level": ["none"],
78
+ "parameter_set_identifier": "128",
79
+ },
80
+ "SHA1": {
81
+ "primitive": "hash",
82
+ "nist_quantum_security_level": 0,
83
+ "nist_replacement": "SHA-256 or SHA3-256",
84
+ "effort_days": 0.5,
85
+ "risk_label": "HIGH",
86
+ "crypto_functions": ["digest"],
87
+ "certification_level": ["none"],
88
+ "parameter_set_identifier": "160",
89
+ },
90
+ # Quantum-safe references (for completeness / future detection)
91
+ "AES-128": {
92
+ "primitive": "ae",
93
+ "nist_quantum_security_level": 1,
94
+ "nist_replacement": "AES-256 recommended (Grover reduces to 64-bit security)",
95
+ "effort_days": 1.0,
96
+ "risk_label": "MEDIUM",
97
+ "crypto_functions": ["encrypt", "decrypt"],
98
+ "certification_level": ["fips140-2"],
99
+ "parameter_set_identifier": "128",
100
+ },
101
+ "AES-256": {
102
+ "primitive": "ae",
103
+ "nist_quantum_security_level": 3,
104
+ "nist_replacement": "Retain — 128-bit post-quantum security",
105
+ "effort_days": 0.0,
106
+ "risk_label": "LOW",
107
+ "crypto_functions": ["encrypt", "decrypt"],
108
+ "certification_level": ["fips140-2"],
109
+ "parameter_set_identifier": "256",
110
+ },
111
+ "SHA-256": {
112
+ "primitive": "hash",
113
+ "nist_quantum_security_level": 3,
114
+ "nist_replacement": "Retain — 128-bit post-quantum security",
115
+ "effort_days": 0.0,
116
+ "risk_label": "LOW",
117
+ "crypto_functions": ["digest"],
118
+ "certification_level": ["fips180-4"],
119
+ "parameter_set_identifier": "256",
120
+ },
121
+ }