@sebastienrousseau/crypto-cbom 0.0.0-stage → 0.0.7
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.
- package/LICENSE +8 -0
- package/README.md +97 -2
- package/dist/audit.d.ts +5 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +114 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +73 -0
- package/dist/cyclonedx.d.ts +8 -0
- package/dist/cyclonedx.d.ts.map +1 -0
- package/dist/cyclonedx.js +58 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/scanner.d.ts +4 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +289 -0
- package/dist/spdx.d.ts +7 -0
- package/dist/spdx.d.ts.map +1 -0
- package/dist/spdx.js +35 -0
- package/dist/types.d.ts +99 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/validator.d.ts +6 -0
- package/dist/validator.d.ts.map +1 -0
- package/dist/validator.js +48 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +7 -0
- package/package.json +57 -3
package/LICENSE
ADDED
package/README.md
CHANGED
|
@@ -1,3 +1,98 @@
|
|
|
1
|
-
|
|
1
|
+
<!-- SPDX-License-Identifier: Apache-2.0 OR MIT -->
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# `@sebastienrousseau/crypto-cbom`
|
|
4
|
+
|
|
5
|
+
> Cryptographic Bill of Materials (CBOM) generator and auditor: CycloneDX 1.6 and SPDX 3.0 output, plus a heuristic quantum-readiness score.
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/@sebastienrousseau/crypto-cbom)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
`@sebastienrousseau/crypto-cbom` provides automated inventory, specification, and auditing of cryptographic assets across codebases and enterprise systems. It maps algorithms, protocols, key lengths, cipher modes, and certificates into standardized machine-readable CycloneDX 1.6 Cryptographic BOM and SPDX 3.0 formats.
|
|
15
|
+
|
|
16
|
+
The auditor classifies each asset by algorithm name and returns a posture score (0–100), a `PASS` / `REVIEW` / `FAIL` status and findings that cite the guidance they draw on:
|
|
17
|
+
|
|
18
|
+
- **NIST SP 800-131A**: broken or disallowed primitives (MD5, SHA-1, DES, RC4, ECB).
|
|
19
|
+
- **NIST IR 8547** (draft): quantum-vulnerable public-key algorithms (RSA, ECC, Ed25519).
|
|
20
|
+
- **CNSA 2.0**: symmetric keys below 256 bits.
|
|
21
|
+
|
|
22
|
+
The result is a heuristic over names found in code or a CBOM. It is evidence you can use in a DORA or CRA assessment, not a compliance verdict.
|
|
23
|
+
|
|
24
|
+
## CLI Usage
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# Generate CycloneDX 1.6 CBOM from source directory
|
|
28
|
+
crypto-cbom scan ./src --format cyclonedx --output cbom.json
|
|
29
|
+
|
|
30
|
+
# Generate SPDX 3.0 CBOM
|
|
31
|
+
crypto-cbom scan ./src --format spdx --output cbom-spdx.json
|
|
32
|
+
|
|
33
|
+
# Audit an existing CBOM (exit code 1 when the status is FAIL)
|
|
34
|
+
crypto-cbom audit cbom.json
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Programmatic API
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import {
|
|
41
|
+
scanDirectory,
|
|
42
|
+
generateCycloneDxCbom,
|
|
43
|
+
auditCbom,
|
|
44
|
+
} from "@sebastienrousseau/crypto-cbom";
|
|
45
|
+
|
|
46
|
+
// 1. Scan codebase for cryptographic assets
|
|
47
|
+
const assets = scanDirectory("./src");
|
|
48
|
+
|
|
49
|
+
// 2. Generate standard CycloneDX 1.6 CBOM
|
|
50
|
+
const cbomDoc = generateCycloneDxCbom(assets, {
|
|
51
|
+
componentName: "financial-payment-engine",
|
|
52
|
+
componentVersion: "1.0.0",
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// 3. Score the cryptographic posture (heuristic; not a compliance verdict)
|
|
56
|
+
const audit = auditCbom(cbomDoc);
|
|
57
|
+
console.log(`Posture: ${audit.status}, score ${audit.score}/100`);
|
|
58
|
+
console.log(`Quantum Safe Assets: ${audit.quantumSafeRatio * 100}%`);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Development
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pnpm --filter @sebastienrousseau/crypto-cbom run build
|
|
67
|
+
pnpm --filter @sebastienrousseau/crypto-cbom run test
|
|
68
|
+
pnpm --filter @sebastienrousseau/crypto-cbom run lint
|
|
69
|
+
pnpm --filter @sebastienrousseau/crypto-cbom run format
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
All 18 packages in the Crypto Service workspace maintain a **100% coverage floor** across statements, branches, functions, and lines.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Documentation
|
|
77
|
+
|
|
78
|
+
- [Full Suite Documentation](https://docs.crypto-service.co/)
|
|
79
|
+
- [API Reference (TypeDoc)](https://docs.crypto-service.co/packages/crypto-cbom/)
|
|
80
|
+
- [Developer Guide](../../DEVELOPMENT.md)
|
|
81
|
+
- [Security Policy](../../SECURITY.md)
|
|
82
|
+
- [Architecture & Design](../../ARCHITECTURE.md)
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Stability guarantees
|
|
87
|
+
|
|
88
|
+
Versions advance strictly one step at a time on the `0.0.x` line (`v0.0.1` → `v0.0.2` → `v0.0.3` ... → `v0.0.999` → `v0.1.0`). Work for every release iteration begins on a dedicated `feat/v<version>` branch.
|
|
89
|
+
|
|
90
|
+
All 18 packages in the workspace move in lockstep. Public API signatures, cipher output formats, and serialization schemas are strictly versioned. Breaking changes to serialized formats or algorithm defaults are considered major breaking changes. Minimum toolchain upgrades (e.g. Node.js LTS floor) are governed by [POLICIES.md](../../docs/POLICIES.md).
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## License
|
|
95
|
+
|
|
96
|
+
Dual-licensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0) or [MIT](https://opensource.org/licenses/MIT), at your option.
|
|
97
|
+
|
|
98
|
+
Copyright (c) 2022-2026 Sebastien Rousseau and The Crypto Service Suite contributors.
|
package/dist/audit.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { CbomAuditResult, CryptoAsset, CycloneDxCbom, QuantumResistanceLevel } from "./types";
|
|
2
|
+
export declare const AUDIT_DISCLAIMER = "Heuristic cryptographic posture score derived from algorithm names; it is not a DORA or CRA compliance assessment.";
|
|
3
|
+
export declare function classifyPrimitive(primitive: string): QuantumResistanceLevel;
|
|
4
|
+
export declare function auditCbom(input: CycloneDxCbom | CryptoAsset[]): CbomAuditResult;
|
|
5
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,eAAe,EACf,WAAW,EACX,aAAa,EACb,sBAAsB,EACvB,MAAM,SAAS,CAAC;AAGjB,eAAO,MAAM,gBAAgB,uHACyF,CAAC;AAgBvH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,sBAAsB,CAO3E;AAkGD,wBAAgB,SAAS,CACvB,KAAK,EAAE,aAAa,GAAG,WAAW,EAAE,GACnC,eAAe,CA+BjB"}
|
package/dist/audit.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AUDIT_DISCLAIMER = void 0;
|
|
4
|
+
exports.classifyPrimitive = classifyPrimitive;
|
|
5
|
+
exports.auditCbom = auditCbom;
|
|
6
|
+
exports.AUDIT_DISCLAIMER = "Heuristic cryptographic posture score derived from algorithm names; it is not a DORA or CRA compliance assessment.";
|
|
7
|
+
const BROKEN_MARKERS = ["MD5", "SHA-1", "DES", "RC4", "ECB"];
|
|
8
|
+
const QUANTUM_VULNERABLE_MARKERS = ["RSA", "ECC", "ECDSA", "ED25519"];
|
|
9
|
+
const TRANSITIONAL_MARKERS = ["AES-128", "CBC"];
|
|
10
|
+
function classifyPrimitive(primitive) {
|
|
11
|
+
const upper = primitive.toUpperCase();
|
|
12
|
+
const has = (markers) => markers.some((m) => upper.includes(m));
|
|
13
|
+
if (has(BROKEN_MARKERS))
|
|
14
|
+
return "DEPRECATED_BROKEN";
|
|
15
|
+
if (has(QUANTUM_VULNERABLE_MARKERS))
|
|
16
|
+
return "VULNERABLE_CRQC";
|
|
17
|
+
if (has(TRANSITIONAL_MARKERS))
|
|
18
|
+
return "TRANSITIONAL_HYBRID";
|
|
19
|
+
return "QUANTUM_SAFE";
|
|
20
|
+
}
|
|
21
|
+
function toAssets(input) {
|
|
22
|
+
if (Array.isArray(input))
|
|
23
|
+
return input;
|
|
24
|
+
return input.components.map((c) => ({
|
|
25
|
+
name: c.name,
|
|
26
|
+
resistanceLevel: classifyPrimitive(c["crypto-properties"]?.algorithmProperties?.primitive || c.name),
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
const DEDUCTIONS = {
|
|
30
|
+
DEPRECATED_BROKEN: 25,
|
|
31
|
+
VULNERABLE_CRQC: 10,
|
|
32
|
+
TRANSITIONAL_HYBRID: 5,
|
|
33
|
+
QUANTUM_SAFE: 0,
|
|
34
|
+
};
|
|
35
|
+
function findingFor(asset) {
|
|
36
|
+
switch (asset.resistanceLevel) {
|
|
37
|
+
case "DEPRECATED_BROKEN":
|
|
38
|
+
return {
|
|
39
|
+
severity: "CRITICAL",
|
|
40
|
+
asset: asset.name,
|
|
41
|
+
rule: "DEPRECATED_BROKEN_PRIMITIVE",
|
|
42
|
+
reference: "NIST_SP_800_131A",
|
|
43
|
+
description: `${asset.name} is broken or disallowed for new use (NIST SP 800-131A).`,
|
|
44
|
+
remediation: "Replace with SHA-256 or AES-256-GCM.",
|
|
45
|
+
};
|
|
46
|
+
case "VULNERABLE_CRQC":
|
|
47
|
+
return {
|
|
48
|
+
severity: "HIGH",
|
|
49
|
+
asset: asset.name,
|
|
50
|
+
rule: "CRQC_HNDL_EXPOSURE",
|
|
51
|
+
reference: "NIST_IR_8547",
|
|
52
|
+
description: `${asset.name} can be broken by a cryptographically relevant quantum computer, so data protected today is exposed to harvest-now-decrypt-later (NIST IR 8547 draft: deprecated after 2030, disallowed after 2035).`,
|
|
53
|
+
remediation: "Deploy hybrid classical/post-quantum KEM (ML-KEM-768) and signatures (ML-DSA-65).",
|
|
54
|
+
};
|
|
55
|
+
case "TRANSITIONAL_HYBRID":
|
|
56
|
+
return {
|
|
57
|
+
severity: "MEDIUM",
|
|
58
|
+
asset: asset.name,
|
|
59
|
+
rule: "TRANSITIONAL_CRYPTOGRAPHY",
|
|
60
|
+
reference: "CNSA_2_0",
|
|
61
|
+
description: `${asset.name} has a reduced security margin against quantum search; CNSA 2.0 requires AES-256.`,
|
|
62
|
+
remediation: "Upgrade to AES-256-GCM or ChaCha20-Poly1305.",
|
|
63
|
+
};
|
|
64
|
+
default:
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function postureStatus(score, deprecatedCount, vulnerableCount) {
|
|
69
|
+
if (deprecatedCount > 0 || score < 50)
|
|
70
|
+
return "FAIL";
|
|
71
|
+
if (vulnerableCount > 0 || score < 85)
|
|
72
|
+
return "REVIEW";
|
|
73
|
+
return "PASS";
|
|
74
|
+
}
|
|
75
|
+
function migrationRoadmap(deprecatedCount, vulnerableCount) {
|
|
76
|
+
const steps = [];
|
|
77
|
+
if (deprecatedCount > 0) {
|
|
78
|
+
steps.push("Phase 1 (Immediate / 30 Days): Eliminate all deprecated ciphers and broken hash functions.");
|
|
79
|
+
}
|
|
80
|
+
if (vulnerableCount > 0) {
|
|
81
|
+
steps.push("Phase 2 (Near-Term / 90 Days): Implement hybrid post-quantum key encapsulation ML-KEM-768.", "Phase 3 (Mid-Term / 180 Days): Transition authentication and code signing to ML-DSA-65 / SLH-DSA.");
|
|
82
|
+
}
|
|
83
|
+
if (steps.length === 0) {
|
|
84
|
+
steps.push("Continuous Monitoring: Maintain automated CBOM tracking across CI/CD supply chain pipelines.");
|
|
85
|
+
}
|
|
86
|
+
return steps;
|
|
87
|
+
}
|
|
88
|
+
function auditCbom(input) {
|
|
89
|
+
const assets = toAssets(input);
|
|
90
|
+
const count = (level) => assets.filter((a) => a.resistanceLevel === level).length;
|
|
91
|
+
const deprecatedCount = count("DEPRECATED_BROKEN");
|
|
92
|
+
const vulnerableCount = count("VULNERABLE_CRQC");
|
|
93
|
+
const quantumSafeCount = count("QUANTUM_SAFE");
|
|
94
|
+
const deductions = assets.reduce((sum, a) => sum + DEDUCTIONS[a.resistanceLevel], 0);
|
|
95
|
+
const score = Math.max(0, 100 - deductions);
|
|
96
|
+
const totalAssets = assets.length;
|
|
97
|
+
return {
|
|
98
|
+
score,
|
|
99
|
+
status: postureStatus(score, deprecatedCount, vulnerableCount),
|
|
100
|
+
disclaimer: exports.AUDIT_DISCLAIMER,
|
|
101
|
+
totalAssets,
|
|
102
|
+
quantumSafeCount,
|
|
103
|
+
vulnerableCount,
|
|
104
|
+
deprecatedCount,
|
|
105
|
+
quantumSafeRatio: totalAssets === 0
|
|
106
|
+
? 1
|
|
107
|
+
: Number((quantumSafeCount / totalAssets).toFixed(2)),
|
|
108
|
+
findings: assets
|
|
109
|
+
.map(findingFor)
|
|
110
|
+
.filter((f) => f !== undefined),
|
|
111
|
+
migrationRoadmap: migrationRoadmap(deprecatedCount, vulnerableCount),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=audit.js.map
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAmEA,wBAAgB,GAAG,CAAC,IAAI,GAAE,MAAM,EAA0B,GAAG,MAAM,CAOlE"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.run = run;
|
|
8
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
|
+
const node_process_1 = __importDefault(require("node:process"));
|
|
10
|
+
const scanner_1 = require("./scanner");
|
|
11
|
+
const cyclonedx_1 = require("./cyclonedx");
|
|
12
|
+
const spdx_1 = require("./spdx");
|
|
13
|
+
const audit_1 = require("./audit");
|
|
14
|
+
const validator_1 = require("./validator");
|
|
15
|
+
function printHelp() {
|
|
16
|
+
node_process_1.default.stdout.write("Usage: crypto-cbom <scan|audit> [options]\n");
|
|
17
|
+
node_process_1.default.stdout.write("Commands:\n");
|
|
18
|
+
node_process_1.default.stdout.write(" scan <dir> [--format cyclonedx|spdx] [--output <file>]\n");
|
|
19
|
+
node_process_1.default.stdout.write(" audit <file.json>\n");
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
22
|
+
function optionValue(args, flag) {
|
|
23
|
+
const idx = args.indexOf(flag);
|
|
24
|
+
return idx !== -1 ? args[idx + 1] || undefined : undefined;
|
|
25
|
+
}
|
|
26
|
+
function runScan(args) {
|
|
27
|
+
const targetDir = args[1] || ".";
|
|
28
|
+
const format = optionValue(args, "--format") ?? "cyclonedx";
|
|
29
|
+
const outFile = optionValue(args, "--output");
|
|
30
|
+
const assets = (0, scanner_1.scanDirectory)(targetDir);
|
|
31
|
+
const cbom = format === "spdx"
|
|
32
|
+
? (0, spdx_1.generateSpdxCbom)(assets)
|
|
33
|
+
: (0, cyclonedx_1.generateCycloneDxCbom)(assets);
|
|
34
|
+
const json = JSON.stringify(cbom, null, 2);
|
|
35
|
+
if (outFile) {
|
|
36
|
+
node_fs_1.default.writeFileSync(outFile, json, "utf8");
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
node_process_1.default.stdout.write(json + "\n");
|
|
40
|
+
}
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
function runAudit(args) {
|
|
44
|
+
const filePath = args[1];
|
|
45
|
+
if (!filePath || !node_fs_1.default.existsSync(filePath)) {
|
|
46
|
+
node_process_1.default.stderr.write(`Error: file not found '${String(filePath)}'\n`);
|
|
47
|
+
return 1;
|
|
48
|
+
}
|
|
49
|
+
const parsed = JSON.parse(node_fs_1.default.readFileSync(filePath, "utf8"));
|
|
50
|
+
const val = (0, validator_1.validateCbom)(parsed);
|
|
51
|
+
if (!val.valid) {
|
|
52
|
+
node_process_1.default.stderr.write(`Validation failed: ${val.errors.join(", ")}\n`);
|
|
53
|
+
return 1;
|
|
54
|
+
}
|
|
55
|
+
const result = (0, audit_1.auditCbom)(parsed);
|
|
56
|
+
node_process_1.default.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
57
|
+
return result.status === "FAIL" ? 1 : 0;
|
|
58
|
+
}
|
|
59
|
+
function run(args = node_process_1.default.argv.slice(2)) {
|
|
60
|
+
const command = args[0];
|
|
61
|
+
if (!command || command === "--help" || command === "-h")
|
|
62
|
+
return printHelp();
|
|
63
|
+
if (command === "scan")
|
|
64
|
+
return runScan(args);
|
|
65
|
+
if (command === "audit")
|
|
66
|
+
return runAudit(args);
|
|
67
|
+
node_process_1.default.stderr.write(`Unknown command '${command}'\n`);
|
|
68
|
+
return 1;
|
|
69
|
+
}
|
|
70
|
+
if (require.main === module) {
|
|
71
|
+
node_process_1.default.exit(run());
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { CryptoAsset, CycloneDxCbom } from "./types";
|
|
2
|
+
export interface CycloneDxOptions {
|
|
3
|
+
componentName?: string;
|
|
4
|
+
componentVersion?: string;
|
|
5
|
+
componentType?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function generateCycloneDxCbom(assets: CryptoAsset[], options?: CycloneDxOptions): CycloneDxCbom;
|
|
8
|
+
//# sourceMappingURL=cyclonedx.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cyclonedx.d.ts","sourceRoot":"","sources":["../src/cyclonedx.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,aAAa,EAAsB,MAAM,SAAS,CAAC;AAGzE,MAAM,WAAW,gBAAgB;IAC/B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAKD,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,WAAW,EAAE,EACrB,OAAO,GAAE,gBAAqB,GAC7B,aAAa,CAoDf"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateCycloneDxCbom = generateCycloneDxCbom;
|
|
7
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
8
|
+
const version_1 = require("./version");
|
|
9
|
+
function generateCycloneDxCbom(assets, options = {}) {
|
|
10
|
+
const serialNumber = `urn:uuid:${node_crypto_1.default.randomUUID()}`;
|
|
11
|
+
const timestamp = new Date().toISOString();
|
|
12
|
+
const components = assets.map((asset) => {
|
|
13
|
+
return {
|
|
14
|
+
type: "cryptographic-asset",
|
|
15
|
+
name: asset.name,
|
|
16
|
+
version: asset.standard,
|
|
17
|
+
"crypto-properties": {
|
|
18
|
+
assetType: asset.type,
|
|
19
|
+
algorithmProperties: {
|
|
20
|
+
primitive: asset.algorithm,
|
|
21
|
+
mode: asset.mode,
|
|
22
|
+
classicalSecurityLevel: asset.keySize,
|
|
23
|
+
nistQuantumSecurityLevel: asset.quantumResistant
|
|
24
|
+
? asset.keySize && asset.keySize >= 1024
|
|
25
|
+
? 5
|
|
26
|
+
: 3
|
|
27
|
+
: 0,
|
|
28
|
+
cryptoFunctions: [asset.type],
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
return {
|
|
34
|
+
bomFormat: "CycloneDX",
|
|
35
|
+
specVersion: "1.6",
|
|
36
|
+
serialNumber,
|
|
37
|
+
version: 1,
|
|
38
|
+
metadata: {
|
|
39
|
+
timestamp,
|
|
40
|
+
tools: [
|
|
41
|
+
{
|
|
42
|
+
vendor: "Sebastien Rousseau",
|
|
43
|
+
name: "@sebastienrousseau/crypto-cbom",
|
|
44
|
+
version: version_1.VERSION,
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
component: {
|
|
48
|
+
name: options.componentName || "cryptographic-application",
|
|
49
|
+
...(options.componentVersion
|
|
50
|
+
? { version: options.componentVersion }
|
|
51
|
+
: {}),
|
|
52
|
+
type: options.componentType || "application",
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
components,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=cyclonedx.js.map
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,QAAQ,CAAC;AACvB,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,OAAO,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./types"), exports);
|
|
18
|
+
__exportStar(require("./scanner"), exports);
|
|
19
|
+
__exportStar(require("./cyclonedx"), exports);
|
|
20
|
+
__exportStar(require("./spdx"), exports);
|
|
21
|
+
__exportStar(require("./audit"), exports);
|
|
22
|
+
__exportStar(require("./validator"), exports);
|
|
23
|
+
__exportStar(require("./cli"), exports);
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,WAAW,EAGZ,MAAM,SAAS,CAAC;AA8OjB,wBAAgB,QAAQ,CACtB,UAAU,EAAE,MAAM,EAClB,QAAQ,SAAY,GACnB,WAAW,EAAE,CA8Bf;AAKD,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,EAAE,CA6C5D"}
|
package/dist/scanner.js
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.scanCode = scanCode;
|
|
7
|
+
exports.scanDirectory = scanDirectory;
|
|
8
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const ASSET_RULES = [
|
|
11
|
+
{
|
|
12
|
+
pattern: /\bml[-_]?kem[-_]?512\b/i,
|
|
13
|
+
name: "ML-KEM-512",
|
|
14
|
+
algorithm: "ML-KEM",
|
|
15
|
+
type: "kem",
|
|
16
|
+
keySize: 512,
|
|
17
|
+
resistanceLevel: "QUANTUM_SAFE",
|
|
18
|
+
standard: "NIST FIPS 203",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
pattern: /\bml[-_]?kem[-_]?768\b/i,
|
|
22
|
+
name: "ML-KEM-768",
|
|
23
|
+
algorithm: "ML-KEM",
|
|
24
|
+
type: "kem",
|
|
25
|
+
keySize: 768,
|
|
26
|
+
resistanceLevel: "QUANTUM_SAFE",
|
|
27
|
+
standard: "NIST FIPS 203",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
pattern: /\bml[-_]?kem[-_]?1024\b/i,
|
|
31
|
+
name: "ML-KEM-1024",
|
|
32
|
+
algorithm: "ML-KEM",
|
|
33
|
+
type: "kem",
|
|
34
|
+
keySize: 1024,
|
|
35
|
+
resistanceLevel: "QUANTUM_SAFE",
|
|
36
|
+
standard: "NIST FIPS 203",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
pattern: /\bml[-_]?dsa[-_]?44\b/i,
|
|
40
|
+
name: "ML-DSA-44",
|
|
41
|
+
algorithm: "ML-DSA",
|
|
42
|
+
type: "signature",
|
|
43
|
+
resistanceLevel: "QUANTUM_SAFE",
|
|
44
|
+
standard: "NIST FIPS 204",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
pattern: /\bml[-_]?dsa[-_]?65\b/i,
|
|
48
|
+
name: "ML-DSA-65",
|
|
49
|
+
algorithm: "ML-DSA",
|
|
50
|
+
type: "signature",
|
|
51
|
+
resistanceLevel: "QUANTUM_SAFE",
|
|
52
|
+
standard: "NIST FIPS 204",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
pattern: /\bml[-_]?dsa[-_]?87\b/i,
|
|
56
|
+
name: "ML-DSA-87",
|
|
57
|
+
algorithm: "ML-DSA",
|
|
58
|
+
type: "signature",
|
|
59
|
+
resistanceLevel: "QUANTUM_SAFE",
|
|
60
|
+
standard: "NIST FIPS 204",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
pattern: /\bslh[-_]?dsa\b/i,
|
|
64
|
+
name: "SLH-DSA",
|
|
65
|
+
algorithm: "SLH-DSA",
|
|
66
|
+
type: "signature",
|
|
67
|
+
resistanceLevel: "QUANTUM_SAFE",
|
|
68
|
+
standard: "NIST FIPS 205",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
pattern: /\baes[-_]?256[-_]?gcm\b/i,
|
|
72
|
+
name: "AES-256-GCM",
|
|
73
|
+
algorithm: "AES",
|
|
74
|
+
type: "cipher",
|
|
75
|
+
keySize: 256,
|
|
76
|
+
mode: "GCM",
|
|
77
|
+
resistanceLevel: "QUANTUM_SAFE",
|
|
78
|
+
standard: "NIST SP 800-38D",
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
pattern: /\baes[-_]?128[-_]?gcm\b/i,
|
|
82
|
+
name: "AES-128-GCM",
|
|
83
|
+
algorithm: "AES",
|
|
84
|
+
type: "cipher",
|
|
85
|
+
keySize: 128,
|
|
86
|
+
mode: "GCM",
|
|
87
|
+
resistanceLevel: "TRANSITIONAL_HYBRID",
|
|
88
|
+
standard: "NIST SP 800-38D",
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
pattern: /\baes[-_]?(?:128|256)[-_]?cbc\b/i,
|
|
92
|
+
name: "AES-CBC",
|
|
93
|
+
algorithm: "AES",
|
|
94
|
+
type: "cipher",
|
|
95
|
+
mode: "CBC",
|
|
96
|
+
resistanceLevel: "TRANSITIONAL_HYBRID",
|
|
97
|
+
standard: "NIST SP 800-38A",
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
pattern: /\baes[-_]?(?:128|256)[-_]?ecb\b/i,
|
|
101
|
+
name: "AES-ECB",
|
|
102
|
+
algorithm: "AES",
|
|
103
|
+
type: "cipher",
|
|
104
|
+
mode: "ECB",
|
|
105
|
+
resistanceLevel: "DEPRECATED_BROKEN",
|
|
106
|
+
standard: "INSECURE (NIST Disallowed)",
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
pattern: /\bchacha20[-_]?poly1305\b/i,
|
|
110
|
+
name: "ChaCha20-Poly1305",
|
|
111
|
+
algorithm: "ChaCha20",
|
|
112
|
+
type: "cipher",
|
|
113
|
+
keySize: 256,
|
|
114
|
+
mode: "Poly1305",
|
|
115
|
+
resistanceLevel: "QUANTUM_SAFE",
|
|
116
|
+
standard: "RFC 8439",
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
pattern: /\b(?:3des|des[-_]?ede3)\b/i,
|
|
120
|
+
name: "Triple-DES",
|
|
121
|
+
algorithm: "3DES",
|
|
122
|
+
type: "cipher",
|
|
123
|
+
resistanceLevel: "DEPRECATED_BROKEN",
|
|
124
|
+
standard: "DEPRECATED (NIST SP 800-131A)",
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
pattern: /\b(?:des|rc4|blowfish)\b/i,
|
|
128
|
+
name: "Legacy Cipher",
|
|
129
|
+
algorithm: "Legacy",
|
|
130
|
+
type: "cipher",
|
|
131
|
+
resistanceLevel: "DEPRECATED_BROKEN",
|
|
132
|
+
standard: "DEPRECATED (Broken)",
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
pattern: /\bed25519\b/i,
|
|
136
|
+
name: "Ed25519",
|
|
137
|
+
algorithm: "Ed25519",
|
|
138
|
+
type: "signature",
|
|
139
|
+
keySize: 256,
|
|
140
|
+
resistanceLevel: "VULNERABLE_CRQC",
|
|
141
|
+
standard: "RFC 8032",
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
pattern: /\b(?:ecdsa|ecdh|prime256v1|secp256r1|secp384r1)\b/i,
|
|
145
|
+
name: "ECDSA / ECDH",
|
|
146
|
+
algorithm: "ECC",
|
|
147
|
+
type: "signature",
|
|
148
|
+
resistanceLevel: "VULNERABLE_CRQC",
|
|
149
|
+
standard: "NIST FIPS 186-5",
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
pattern: /\brsa\b/i,
|
|
153
|
+
name: "RSA",
|
|
154
|
+
algorithm: "RSA",
|
|
155
|
+
type: "cipher",
|
|
156
|
+
resistanceLevel: "VULNERABLE_CRQC",
|
|
157
|
+
standard: "PKCS #1",
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
pattern: /\bsha[-_]?256\b/i,
|
|
161
|
+
name: "SHA-256",
|
|
162
|
+
algorithm: "SHA-2",
|
|
163
|
+
type: "hash",
|
|
164
|
+
keySize: 256,
|
|
165
|
+
resistanceLevel: "QUANTUM_SAFE",
|
|
166
|
+
standard: "NIST FIPS 180-4",
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
pattern: /\bsha[-_]?512\b/i,
|
|
170
|
+
name: "SHA-512",
|
|
171
|
+
algorithm: "SHA-2",
|
|
172
|
+
type: "hash",
|
|
173
|
+
keySize: 512,
|
|
174
|
+
resistanceLevel: "QUANTUM_SAFE",
|
|
175
|
+
standard: "NIST FIPS 180-4",
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
pattern: /\bsha[-_]?3[-_]?256\b/i,
|
|
179
|
+
name: "SHA3-256",
|
|
180
|
+
algorithm: "SHA-3",
|
|
181
|
+
type: "hash",
|
|
182
|
+
keySize: 256,
|
|
183
|
+
resistanceLevel: "QUANTUM_SAFE",
|
|
184
|
+
standard: "NIST FIPS 202",
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
pattern: /\b(?:md5|md4|ripemd160)\b/i,
|
|
188
|
+
name: "Broken Hash",
|
|
189
|
+
algorithm: "MD5/MD4",
|
|
190
|
+
type: "hash",
|
|
191
|
+
resistanceLevel: "DEPRECATED_BROKEN",
|
|
192
|
+
standard: "DEPRECATED (RFC 6151)",
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
pattern: /\bsha[-_]?1\b/i,
|
|
196
|
+
name: "SHA-1",
|
|
197
|
+
algorithm: "SHA-1",
|
|
198
|
+
type: "hash",
|
|
199
|
+
keySize: 160,
|
|
200
|
+
resistanceLevel: "DEPRECATED_BROKEN",
|
|
201
|
+
standard: "DEPRECATED (NIST SP 800-131A)",
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
pattern: /-----BEGIN CERTIFICATE-----/,
|
|
205
|
+
name: "X.509 Certificate",
|
|
206
|
+
algorithm: "X.509",
|
|
207
|
+
type: "certificate",
|
|
208
|
+
resistanceLevel: "VULNERABLE_CRQC",
|
|
209
|
+
standard: "RFC 5280",
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
pattern: /-----BEGIN (?:RSA|EC) PRIVATE KEY-----/,
|
|
213
|
+
name: "Private Key Material",
|
|
214
|
+
algorithm: "PKCS",
|
|
215
|
+
type: "key",
|
|
216
|
+
resistanceLevel: "VULNERABLE_CRQC",
|
|
217
|
+
standard: "RFC 7468",
|
|
218
|
+
},
|
|
219
|
+
];
|
|
220
|
+
function scanCode(sourceText, filename = "unknown") {
|
|
221
|
+
const assets = [];
|
|
222
|
+
const lines = sourceText.split(/\r?\n/);
|
|
223
|
+
const seenOnLine = new Set();
|
|
224
|
+
for (let i = 0; i < lines.length; i++) {
|
|
225
|
+
const line = lines[i];
|
|
226
|
+
for (const rule of ASSET_RULES) {
|
|
227
|
+
if (rule.pattern.test(line)) {
|
|
228
|
+
const dedupeKey = `${i}:${rule.name}`;
|
|
229
|
+
if (!seenOnLine.has(dedupeKey)) {
|
|
230
|
+
seenOnLine.add(dedupeKey);
|
|
231
|
+
assets.push({
|
|
232
|
+
name: rule.name,
|
|
233
|
+
algorithm: rule.algorithm,
|
|
234
|
+
type: rule.type,
|
|
235
|
+
keySize: rule.keySize,
|
|
236
|
+
mode: rule.mode,
|
|
237
|
+
quantumResistant: rule.resistanceLevel === "QUANTUM_SAFE",
|
|
238
|
+
resistanceLevel: rule.resistanceLevel,
|
|
239
|
+
standard: rule.standard,
|
|
240
|
+
file: filename,
|
|
241
|
+
line: i + 1,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return assets;
|
|
248
|
+
}
|
|
249
|
+
function scanDirectory(dirPath) {
|
|
250
|
+
const assets = [];
|
|
251
|
+
if (!node_fs_1.default.existsSync(dirPath)) {
|
|
252
|
+
return assets;
|
|
253
|
+
}
|
|
254
|
+
const stat = node_fs_1.default.statSync(dirPath);
|
|
255
|
+
if (!stat.isDirectory()) {
|
|
256
|
+
const content = node_fs_1.default.readFileSync(dirPath, "utf8");
|
|
257
|
+
return scanCode(content, dirPath);
|
|
258
|
+
}
|
|
259
|
+
const entries = node_fs_1.default.readdirSync(dirPath, { withFileTypes: true });
|
|
260
|
+
for (const entry of entries) {
|
|
261
|
+
if (entry.name === "node_modules" ||
|
|
262
|
+
entry.name === ".git" ||
|
|
263
|
+
entry.name === "dist") {
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
const fullPath = node_path_1.default.join(dirPath, entry.name);
|
|
267
|
+
if (entry.isDirectory()) {
|
|
268
|
+
assets.push(...scanDirectory(fullPath));
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
const ext = node_path_1.default.extname(entry.name).toLowerCase();
|
|
272
|
+
if ([
|
|
273
|
+
".ts",
|
|
274
|
+
".js",
|
|
275
|
+
".mjs",
|
|
276
|
+
".cjs",
|
|
277
|
+
".json",
|
|
278
|
+
".pem",
|
|
279
|
+
".crt",
|
|
280
|
+
".key",
|
|
281
|
+
].includes(ext)) {
|
|
282
|
+
const content = node_fs_1.default.readFileSync(fullPath, "utf8");
|
|
283
|
+
assets.push(...scanCode(content, fullPath));
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return assets;
|
|
288
|
+
}
|
|
289
|
+
//# sourceMappingURL=scanner.js.map
|
package/dist/spdx.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spdx.d.ts","sourceRoot":"","sources":["../src/spdx.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAe,MAAM,SAAS,CAAC;AAG7D,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAKD,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,WAAW,EAAE,EACrB,OAAO,GAAE,WAAgB,GACxB,QAAQ,CA2BV"}
|
package/dist/spdx.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateSpdxCbom = generateSpdxCbom;
|
|
7
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
8
|
+
const version_1 = require("./version");
|
|
9
|
+
function generateSpdxCbom(assets, options = {}) {
|
|
10
|
+
const spdxId = `SPDXRef-CBOM-${node_crypto_1.default.randomUUID()}`;
|
|
11
|
+
const created = new Date().toISOString();
|
|
12
|
+
const elements = assets.map((asset, index) => {
|
|
13
|
+
return {
|
|
14
|
+
spdxId: `SPDXRef-CryptoAsset-${index + 1}`,
|
|
15
|
+
name: asset.name,
|
|
16
|
+
type: asset.type,
|
|
17
|
+
algorithm: asset.algorithm,
|
|
18
|
+
quantumSafe: asset.quantumResistant,
|
|
19
|
+
standard: asset.standard,
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
spdxVersion: "SPDX-3.0.0",
|
|
24
|
+
spdxId,
|
|
25
|
+
name: options.name || "Crypto-Service-CBOM",
|
|
26
|
+
creationInfo: {
|
|
27
|
+
created,
|
|
28
|
+
creators: [
|
|
29
|
+
options.creator || `Tool: @sebastienrousseau/crypto-cbom-${version_1.VERSION}`,
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
elements,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=spdx.js.map
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
export type CryptographicAssetType = "algorithm" | "cipher" | "signature" | "kem" | "hash" | "key" | "certificate" | "protocol";
|
|
2
|
+
export type QuantumResistanceLevel = "QUANTUM_SAFE" | "TRANSITIONAL_HYBRID" | "VULNERABLE_CRQC" | "DEPRECATED_BROKEN";
|
|
3
|
+
export interface CryptoAsset {
|
|
4
|
+
name: string;
|
|
5
|
+
algorithm: string;
|
|
6
|
+
type: CryptographicAssetType;
|
|
7
|
+
keySize?: number | undefined;
|
|
8
|
+
mode?: string | undefined;
|
|
9
|
+
padding?: string | undefined;
|
|
10
|
+
quantumResistant: boolean;
|
|
11
|
+
resistanceLevel: QuantumResistanceLevel;
|
|
12
|
+
standard?: string | undefined;
|
|
13
|
+
file?: string | undefined;
|
|
14
|
+
line?: number | undefined;
|
|
15
|
+
}
|
|
16
|
+
export interface CycloneDxCryptoProperties {
|
|
17
|
+
assetType: CryptographicAssetType;
|
|
18
|
+
algorithmProperties?: {
|
|
19
|
+
primitive: string;
|
|
20
|
+
parameterSetIdentifier?: string | undefined;
|
|
21
|
+
curve?: string | undefined;
|
|
22
|
+
executionEnvironment?: string | undefined;
|
|
23
|
+
implementationPlatform?: string | undefined;
|
|
24
|
+
certificationLevel?: string | undefined;
|
|
25
|
+
mode?: string | undefined;
|
|
26
|
+
padding?: string | undefined;
|
|
27
|
+
cryptoFunctions?: string[] | undefined;
|
|
28
|
+
classicalSecurityLevel?: number | undefined;
|
|
29
|
+
nistQuantumSecurityLevel?: number | undefined;
|
|
30
|
+
} | undefined;
|
|
31
|
+
oid?: string | undefined;
|
|
32
|
+
}
|
|
33
|
+
export interface CycloneDxComponent {
|
|
34
|
+
type: "cryptographic-asset";
|
|
35
|
+
name: string;
|
|
36
|
+
version?: string | undefined;
|
|
37
|
+
purl?: string | undefined;
|
|
38
|
+
"crypto-properties": CycloneDxCryptoProperties;
|
|
39
|
+
}
|
|
40
|
+
export interface CycloneDxCbom {
|
|
41
|
+
bomFormat: "CycloneDX";
|
|
42
|
+
specVersion: "1.6";
|
|
43
|
+
serialNumber: string;
|
|
44
|
+
version: number;
|
|
45
|
+
metadata: {
|
|
46
|
+
timestamp: string;
|
|
47
|
+
tools: Array<{
|
|
48
|
+
vendor: string;
|
|
49
|
+
name: string;
|
|
50
|
+
version: string;
|
|
51
|
+
}>;
|
|
52
|
+
component?: {
|
|
53
|
+
name: string;
|
|
54
|
+
version?: string | undefined;
|
|
55
|
+
type: string;
|
|
56
|
+
} | undefined;
|
|
57
|
+
};
|
|
58
|
+
components: CycloneDxComponent[];
|
|
59
|
+
}
|
|
60
|
+
export interface SpdxElement {
|
|
61
|
+
spdxId: string;
|
|
62
|
+
name: string;
|
|
63
|
+
type: string;
|
|
64
|
+
algorithm: string;
|
|
65
|
+
quantumSafe: boolean;
|
|
66
|
+
standard?: string | undefined;
|
|
67
|
+
}
|
|
68
|
+
export interface SpdxCbom {
|
|
69
|
+
spdxVersion: "SPDX-3.0.0";
|
|
70
|
+
spdxId: string;
|
|
71
|
+
creationInfo: {
|
|
72
|
+
created: string;
|
|
73
|
+
creators: string[];
|
|
74
|
+
};
|
|
75
|
+
name: string;
|
|
76
|
+
elements: SpdxElement[];
|
|
77
|
+
}
|
|
78
|
+
export interface AuditFinding {
|
|
79
|
+
severity: "CRITICAL" | "HIGH" | "MEDIUM" | "LOW";
|
|
80
|
+
asset: string;
|
|
81
|
+
rule: string;
|
|
82
|
+
reference: "NIST_SP_800_131A" | "NIST_IR_8547" | "CNSA_2_0";
|
|
83
|
+
description: string;
|
|
84
|
+
remediation: string;
|
|
85
|
+
}
|
|
86
|
+
export interface CbomAuditResult {
|
|
87
|
+
score: number;
|
|
88
|
+
status: "PASS" | "REVIEW" | "FAIL";
|
|
89
|
+
disclaimer: string;
|
|
90
|
+
totalAssets: number;
|
|
91
|
+
quantumSafeCount: number;
|
|
92
|
+
vulnerableCount: number;
|
|
93
|
+
deprecatedCount: number;
|
|
94
|
+
quantumSafeRatio: number;
|
|
95
|
+
findings: AuditFinding[];
|
|
96
|
+
migrationRoadmap: string[];
|
|
97
|
+
}
|
|
98
|
+
export type DoraAuditResult = CbomAuditResult;
|
|
99
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAKD,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,gBAAgB,CAuD3D"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateCbom = validateCbom;
|
|
4
|
+
function validateCbom(doc) {
|
|
5
|
+
const errors = [];
|
|
6
|
+
if (typeof doc !== "object" || doc === null) {
|
|
7
|
+
return { valid: false, errors: ["Document must be a non-null object"] };
|
|
8
|
+
}
|
|
9
|
+
const record = doc;
|
|
10
|
+
if (record.bomFormat === "CycloneDX") {
|
|
11
|
+
if (record.specVersion !== "1.6") {
|
|
12
|
+
errors.push(`Expected specVersion '1.6', got '${String(record.specVersion)}'`);
|
|
13
|
+
}
|
|
14
|
+
if (!record.serialNumber || typeof record.serialNumber !== "string") {
|
|
15
|
+
errors.push("Missing or invalid 'serialNumber'");
|
|
16
|
+
}
|
|
17
|
+
if (!Array.isArray(record.components)) {
|
|
18
|
+
errors.push("Missing or invalid 'components' array");
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
for (let i = 0; i < record.components.length; i++) {
|
|
22
|
+
const comp = record.components[i];
|
|
23
|
+
if (!comp || comp.type !== "cryptographic-asset") {
|
|
24
|
+
errors.push(`Component at index ${i} must have type 'cryptographic-asset'`);
|
|
25
|
+
}
|
|
26
|
+
if (!comp || !comp.name) {
|
|
27
|
+
errors.push(`Component at index ${i} is missing 'name'`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return { valid: errors.length === 0, errors };
|
|
32
|
+
}
|
|
33
|
+
if (typeof record.spdxVersion === "string" &&
|
|
34
|
+
record.spdxVersion.startsWith("SPDX-")) {
|
|
35
|
+
if (!record.spdxId || typeof record.spdxId !== "string") {
|
|
36
|
+
errors.push("Missing or invalid 'spdxId'");
|
|
37
|
+
}
|
|
38
|
+
if (!Array.isArray(record.elements)) {
|
|
39
|
+
errors.push("Missing or invalid 'elements' array");
|
|
40
|
+
}
|
|
41
|
+
return { valid: errors.length === 0, errors };
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
valid: false,
|
|
45
|
+
errors: ["Unsupported document format. Expected CycloneDX 1.6 or SPDX 3.0"],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,OAAO,EAAE,MAIb,CAAC"}
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VERSION = void 0;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
exports.VERSION = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, "..", "package.json"), "utf8")).version;
|
|
7
|
+
//# sourceMappingURL=version.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,60 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sebastienrousseau/crypto-cbom",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.0.7",
|
|
4
|
+
"description": "Cryptographic Bill of Materials (CBOM) generator and auditor compliant with CycloneDX 1.6, SPDX 3.0, EU DORA Articles 9/13, and CRA Article 14.",
|
|
5
|
+
"author": "Sebastien Rousseau <sebastienrousseau@users.noreply.github.com>",
|
|
6
|
+
"license": "MIT OR Apache-2.0",
|
|
7
|
+
"homepage": "https://crypto-service.co",
|
|
8
|
+
"repository": {
|
|
9
|
+
"directory": "packages/crypto-cbom",
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git@github.com:sebastienrousseau/crypto-service.git"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/sebastienrousseau/crypto-service/issues"
|
|
15
|
+
},
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=22.0.0"
|
|
18
|
+
},
|
|
19
|
+
"main": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"sideEffects": false,
|
|
22
|
+
"bin": {
|
|
23
|
+
"crypto-cbom": "./dist/cli.js"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist/**/*.js",
|
|
27
|
+
"dist/**/*.d.ts",
|
|
28
|
+
"dist/**/*.d.ts.map"
|
|
29
|
+
],
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@sebastienrousseau/crypto-lib": "0.0.7"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/chai": "^4.3.7",
|
|
35
|
+
"@types/chai-as-promised": "^8.0.2",
|
|
36
|
+
"@types/mocha": "^10.0.2",
|
|
37
|
+
"@types/node": "^26.4.1",
|
|
38
|
+
"c8": "^12.0.0",
|
|
39
|
+
"chai": "^4.5.0",
|
|
40
|
+
"chai-as-promised": "^7.1.2",
|
|
41
|
+
"mocha": "^10.8.2",
|
|
42
|
+
"rimraf": "^6.1.3",
|
|
43
|
+
"ts-node": "^10.9.2",
|
|
44
|
+
"typedoc": "^0.28.20",
|
|
45
|
+
"typescript": "~5.9.3"
|
|
46
|
+
},
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsc --build tsconfig.json",
|
|
52
|
+
"clean": "rimraf ./coverage ./dist ./docs",
|
|
53
|
+
"lint": "eslint --ext .ts src __tests__",
|
|
54
|
+
"lint:fix": "eslint --cache --fix --ext .ts src __tests__",
|
|
55
|
+
"format": "prettier --write src/**/*.ts",
|
|
56
|
+
"format:check": "prettier --check \"src/**/*.ts\"",
|
|
57
|
+
"test": "c8 --check-coverage --lines 100 --branches 100 --functions 100 --reporter=lcov --reporter=text mocha --config=.mocharc.cjs",
|
|
58
|
+
"docs": "typedoc"
|
|
59
|
+
}
|
|
6
60
|
}
|