@clear-capabilities/agentic-security-scanner 0.86.0 → 0.118.2
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/CHANGELOG.md +404 -0
- package/dist/178.index.js +1 -1
- package/dist/384.index.js +1 -1
- package/dist/637.index.js +1 -1
- package/dist/838.index.js +1 -1
- package/dist/agentic-security.mjs +84 -84
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/package.json +12 -6
- package/src/.agentic-security/dpia.md +26 -0
- package/src/.agentic-security/pqc-migration-plan.json +80 -0
- package/src/.agentic-security/pqc-migration-plan.md +32 -0
- package/src/.agentic-security/sbom-history/0775d96ba4cf814520d02760cfb3010760c7684f.json +6 -0
- package/src/.agentic-security/sbom-history/29300441d3460832137a8e51a92b74068cad2f15.json +6 -0
- package/src/.agentic-security/sbom-history/3852ad2aa0e50951bed0c930c29d1861aa13d048.json +6 -0
- package/src/.agentic-security/sbom-history/3d96e2daec97e3226c9679bac6e64a89ca10bfe9.json +6 -0
- package/src/.agentic-security/sbom-history/5f0b136622fce09b469a1a6137c04d5b18652ea4.json +6 -0
- package/src/.agentic-security/sbom-history/64e30595d447b62344d0d9200760269df2095332.json +6 -0
- package/src/.agentic-security/sbom-history/71f3241902c9e7437764e3caa046ba8a3bcdd32a.json +6 -0
- package/src/.agentic-security/sbom-history/99c372cdc33ecc9dde92960b4ffadf7775860e61.json +6 -0
- package/src/.agentic-security/sbom-history/9c93b15e8cf3ff2ddb8cdae92371b82e178b463c.json +6 -0
- package/src/.agentic-security/sbom-history/a3544abf1863d71776b61302ca74d9ead2b26a9f.json +6 -0
- package/src/.agentic-security/sbom-history/dca15abdec43c508aa342db86dc5db8d7f01fbbe.json +6 -0
- package/src/.agentic-security/threat-model.json +3702 -0
- package/src/.agentic-security/threat-model.md +118 -0
- package/src/dataflow/.agentic-security/dpia.md +26 -0
- package/src/dataflow/.agentic-security/sbom-history/15d7c4d20f9d968ebec29445c9e9b3fc6f74e6a1.json +6 -0
- package/src/dataflow/.agentic-security/sbom-history/3852ad2aa0e50951bed0c930c29d1861aa13d048.json +6 -0
- package/src/dataflow/.agentic-security/sbom-history/73166872888d9ddcbe98645af818ecc7d8917b07.json +6 -0
- package/src/dataflow/.agentic-security/sbom-history/8869fe64035bec0152d92ece961e9369d8637076.json +6 -0
- package/src/dataflow/.agentic-security/sbom-history/9c93b15e8cf3ff2ddb8cdae92371b82e178b463c.json +6 -0
- package/src/dataflow/.agentic-security/sbom-history/b7ac2dada4a9216ccfe8969fc57f988c83f46975.json +6 -0
- package/src/dataflow/.agentic-security/threat-model.json +161 -0
- package/src/dataflow/.agentic-security/threat-model.md +43 -0
- package/src/dataflow/CLAUDE.md +17 -5
- package/src/dataflow/catalog.js +9 -0
- package/src/dataflow/engine.js +30 -4
- package/src/dataflow/index.js +8 -0
- package/src/dataflow/proof-gate.js +95 -0
- package/src/dataflow/summaries.js +44 -5
- package/src/engine.js +199 -5
- package/src/ir/.agentic-security/dpia.md +26 -0
- package/src/ir/.agentic-security/exploit-bundles.json +77 -0
- package/src/ir/.agentic-security/sbom-history/73166872888d9ddcbe98645af818ecc7d8917b07.json +6 -0
- package/src/ir/.agentic-security/sbom-history/b5f468a158f6a145aedd8680841c106b9809f16b.json +6 -0
- package/src/ir/.agentic-security/threat-model.json +143 -0
- package/src/ir/.agentic-security/threat-model.md +39 -0
- package/src/ir/CLAUDE.md +1 -0
- package/src/ir/callgraph.js +12 -1
- package/src/ir/tree-sitter-loader.js +108 -0
- package/src/posture/.agentic-security/dpia.md +1 -1
- package/src/posture/.agentic-security/pqc-migration-plan.json +1 -1
- package/src/posture/.agentic-security/pqc-migration-plan.md +1 -1
- package/src/posture/.agentic-security/sbom-history/0724c133d2943c87555695b5b2adcfcfc3373a2d.json +6 -0
- package/src/posture/.agentic-security/sbom-history/99c372cdc33ecc9dde92960b4ffadf7775860e61.json +6 -0
- package/src/posture/.agentic-security/sbom-history/b7ac2dada4a9216ccfe8969fc57f988c83f46975.json +6 -0
- package/src/posture/.agentic-security/threat-model.json +3 -3
- package/src/posture/.agentic-security/threat-model.md +1 -1
- package/src/posture/corpus-status.js +74 -0
- package/src/posture/coverage-report.js +113 -0
- package/src/posture/holdout-eval.js +57 -0
- package/src/posture/router.js +33 -3
- package/src/report/.agentic-security/dpia.md +26 -0
- package/src/report/.agentic-security/sbom-history/f94be839aa6a1011f53da016416a561698b8f338.json +6 -0
- package/src/report/.agentic-security/threat-model.md +1 -1
- package/src/report/index.js +17 -0
- package/src/sast/.agentic-security/dpia.md +26 -0
- package/src/sast/.agentic-security/pqc-migration-plan.json +65 -0
- package/src/sast/.agentic-security/pqc-migration-plan.md +30 -0
- package/src/sast/.agentic-security/sbom-history/043a7b401c68f5a298c83d4c42e94f118ff2a89a.json +6 -0
- package/src/sast/.agentic-security/sbom-history/0775d96ba4cf814520d02760cfb3010760c7684f.json +6 -0
- package/src/sast/.agentic-security/sbom-history/20d77ad7363a82777d506dedbbb1ecfed52f094a.json +6 -0
- package/src/sast/.agentic-security/sbom-history/29300441d3460832137a8e51a92b74068cad2f15.json +6 -0
- package/src/sast/.agentic-security/sbom-history/3852ad2aa0e50951bed0c930c29d1861aa13d048.json +6 -0
- package/src/sast/.agentic-security/sbom-history/3c6b3205f04e26f7654943d465281d00e0fa395e.json +6 -0
- package/src/sast/.agentic-security/sbom-history/3d96e2daec97e3226c9679bac6e64a89ca10bfe9.json +6 -0
- package/src/sast/.agentic-security/sbom-history/5282502e090013219eb006f7e02d8c983bc29c8e.json +6 -0
- package/src/sast/.agentic-security/sbom-history/5f0b136622fce09b469a1a6137c04d5b18652ea4.json +6 -0
- package/src/sast/.agentic-security/sbom-history/64e30595d447b62344d0d9200760269df2095332.json +6 -0
- package/src/sast/.agentic-security/sbom-history/71f3241902c9e7437764e3caa046ba8a3bcdd32a.json +6 -0
- package/src/sast/.agentic-security/sbom-history/73514b485d4770f3c5ee57d87338bccd1c61d8b7.json +6 -0
- package/src/sast/.agentic-security/sbom-history/96fa584fc3134512237d58aac63d6f3778b0cbcb.json +6 -0
- package/src/sast/.agentic-security/sbom-history/99c372cdc33ecc9dde92960b4ffadf7775860e61.json +6 -0
- package/src/sast/.agentic-security/sbom-history/a3544abf1863d71776b61302ca74d9ead2b26a9f.json +6 -0
- package/src/sast/.agentic-security/sbom-history/a4a34ab8b3d758de2adc8c3dc3e42d4cb0db00a3.json +6 -0
- package/src/sast/.agentic-security/sbom-history/ac236ec3602cfc8fb133f389ec3687e409084673.json +6 -0
- package/src/sast/.agentic-security/sbom-history/b57b3e1292f1f9f0de6c5a34a436e330c78008a3.json +6 -0
- package/src/sast/.agentic-security/sbom-history/dca15abdec43c508aa342db86dc5db8d7f01fbbe.json +6 -0
- package/src/sast/.agentic-security/sbom-history/dcbfd4935922dd165093c08a1003c338eed937c5.json +6 -0
- package/src/sast/.agentic-security/sbom-history/fa017f725615b0293814344c8ef7dfeb06775ca0.json +6 -0
- package/src/sast/.agentic-security/threat-model.json +816 -0
- package/src/sast/.agentic-security/threat-model.md +91 -0
- package/src/sast/CLAUDE.md +9 -3
- package/src/sast/code-injection-multilang.js +117 -0
- package/src/sast/crypto-protocol.js +43 -1
- package/src/sast/csharp-structural.js +65 -0
- package/src/sast/csharp.js +3 -0
- package/src/sast/csrf.js +24 -3
- package/src/sast/csv-injection.js +84 -0
- package/src/sast/frontend-hygiene.js +106 -0
- package/src/sast/go-structural.js +60 -0
- package/src/sast/java-structural.js +81 -0
- package/src/sast/js-framework-structural.js +115 -0
- package/src/sast/kotlin.js +72 -0
- package/src/sast/ldap-injection.js +73 -17
- package/src/sast/open-redirect.js +47 -5
- package/src/sast/php.js +35 -0
- package/src/sast/python-structural.js +83 -0
- package/src/sast/redos-nfa.js +75 -0
- package/src/sast/response-splitting.js +48 -19
- package/src/sast/ruby.js +38 -0
- package/src/sast/secret-concat.js +60 -0
- package/src/sast/ssrf-cloud-metadata.js +13 -0
- package/src/sast/stored-taint.js +88 -0
- package/src/sast/tree-sitter-sinks.js +62 -0
- package/src/sast/weak-password-hash.js +28 -0
- package/src/sast/weak-randomness.js +46 -5
- package/src/sast/wrong-context-sanitizer.js +94 -0
- package/src/sast/xpath-injection.js +30 -6
- package/src/sast/xss-reflected-multilang.js +116 -0
- package/src/sast/xxe.js +67 -0
|
@@ -23,6 +23,25 @@ import { brierScore, computeBrierOnHeldOut, wilsonInterval } from './calibration
|
|
|
23
23
|
|
|
24
24
|
const ECE_BINS_DEFAULT = 10;
|
|
25
25
|
|
|
26
|
+
// Map a labeled sample to a source language. Prefer an explicit `language`
|
|
27
|
+
// field; otherwise derive it from the `file` extension. Keeps per-language
|
|
28
|
+
// metrics meaningful even for older corpora that only carry a file path.
|
|
29
|
+
const EXT_TO_LANG = {
|
|
30
|
+
js: 'js', jsx: 'js', mjs: 'js', cjs: 'js', ts: 'ts', tsx: 'ts',
|
|
31
|
+
py: 'py', java: 'java', go: 'go', rb: 'rb', php: 'php',
|
|
32
|
+
cs: 'cs', kt: 'kt', kts: 'kt', swift: 'swift', rs: 'rs',
|
|
33
|
+
c: 'c', h: 'c', cpp: 'cpp', cc: 'cpp', cxx: 'cpp', hpp: 'cpp',
|
|
34
|
+
sol: 'sol', dart: 'dart',
|
|
35
|
+
};
|
|
36
|
+
export function languageOf(o) {
|
|
37
|
+
if (o && typeof o.language === 'string' && o.language) return o.language;
|
|
38
|
+
if (o && typeof o.file === 'string') {
|
|
39
|
+
const ext = o.file.split('.').pop().toLowerCase();
|
|
40
|
+
if (EXT_TO_LANG[ext]) return EXT_TO_LANG[ext];
|
|
41
|
+
}
|
|
42
|
+
return 'unknown';
|
|
43
|
+
}
|
|
44
|
+
|
|
26
45
|
export function parseLabeledJsonl(text) {
|
|
27
46
|
if (typeof text !== 'string' || !text.length) return [];
|
|
28
47
|
const out = [];
|
|
@@ -39,6 +58,7 @@ export function parseLabeledJsonl(text) {
|
|
|
39
58
|
if (p === null || a === null) continue;
|
|
40
59
|
out.push({
|
|
41
60
|
family: typeof o.family === 'string' ? o.family : 'unknown',
|
|
61
|
+
language: languageOf(o),
|
|
42
62
|
stableId: typeof o.stableId === 'string' ? o.stableId : null,
|
|
43
63
|
predicted: Math.max(0, Math.min(1, p)),
|
|
44
64
|
actual: a,
|
|
@@ -98,6 +118,27 @@ export function perFamily(samples) {
|
|
|
98
118
|
return fams;
|
|
99
119
|
}
|
|
100
120
|
|
|
121
|
+
// Per-language precision/recall breakdown (roadmap #9). The "perfect
|
|
122
|
+
// multi-language SAST" claim is only defensible with per-language ground
|
|
123
|
+
// truth — a corpus that's 90% JS can hide poor Ruby precision behind a
|
|
124
|
+
// healthy aggregate. Mirrors perFamily, keyed by sample.language, and adds
|
|
125
|
+
// a per-language precision so a regression in one language is visible.
|
|
126
|
+
export function perLanguage(samples) {
|
|
127
|
+
const langs = {};
|
|
128
|
+
for (const s of samples) {
|
|
129
|
+
const l = s.language || 'unknown';
|
|
130
|
+
if (!langs[l]) langs[l] = { tp: 0, fp: 0, n: 0, precision: 0 };
|
|
131
|
+
langs[l].n++;
|
|
132
|
+
if (s.actual === 1) langs[l].tp++;
|
|
133
|
+
else if (s.actual === 0) langs[l].fp++;
|
|
134
|
+
}
|
|
135
|
+
for (const l of Object.keys(langs)) {
|
|
136
|
+
const { tp, fp } = langs[l];
|
|
137
|
+
langs[l].precision = (tp + fp) > 0 ? Number((tp / (tp + fp)).toFixed(4)) : 0;
|
|
138
|
+
}
|
|
139
|
+
return langs;
|
|
140
|
+
}
|
|
141
|
+
|
|
101
142
|
// One-shot evaluation: Brier + ECE + per-family TP/FP + overall precision.
|
|
102
143
|
// Returns null only when there's truly no data; never returns a tautological
|
|
103
144
|
// zero.
|
|
@@ -124,14 +165,30 @@ export function evaluateHeldOut(samples) {
|
|
|
124
165
|
precision,
|
|
125
166
|
precisionCi95: ci,
|
|
126
167
|
perFamily: fams,
|
|
168
|
+
perLanguage: perLanguage(samples),
|
|
127
169
|
notes: [
|
|
128
170
|
...(samples.length < 100 ? ['n<100: Brier and ECE have wide confidence; treat as directional, not decision-grade.'] : []),
|
|
129
171
|
...(brierR.brier !== null && brierR.brier > 0.10 ? [`brier=${brierR.brier.toFixed(3)} exceeds PRD target 0.10`] : []),
|
|
130
172
|
...(ece && ece.ece > 0.05 ? [`ece=${ece.ece.toFixed(3)} exceeds 0.05 calibration target`] : []),
|
|
173
|
+
// Per-language regression surfacing: a language with enough samples
|
|
174
|
+
// whose precision trails the aggregate by >0.15 is the failure the
|
|
175
|
+
// aggregate would otherwise mask.
|
|
176
|
+
...Object.entries(perLanguage(samples))
|
|
177
|
+
.filter(([, m]) => m.n >= 20 && precision - m.precision > 0.15)
|
|
178
|
+
.map(([lang, m]) => `language '${lang}' precision=${m.precision.toFixed(3)} (n=${m.n}) trails aggregate ${precision.toFixed(3)}`),
|
|
131
179
|
],
|
|
132
180
|
};
|
|
133
181
|
}
|
|
134
182
|
|
|
183
|
+
// Per-language dashboard line: one row per language, sorted by sample count.
|
|
184
|
+
export function summarizePerLanguage(result) {
|
|
185
|
+
if (!result || !result.ok || !result.perLanguage) return 'per-language: no data';
|
|
186
|
+
return Object.entries(result.perLanguage)
|
|
187
|
+
.sort((a, b) => b[1].n - a[1].n)
|
|
188
|
+
.map(([lang, m]) => `${lang}: precision=${m.precision.toFixed(3)} (tp=${m.tp} fp=${m.fp} n=${m.n})`)
|
|
189
|
+
.join('\n');
|
|
190
|
+
}
|
|
191
|
+
|
|
135
192
|
// CLI-friendly summary line.
|
|
136
193
|
export function summarize(result) {
|
|
137
194
|
if (!result || !result.ok) return `held-out: ${result?.reason || 'unknown error'}`;
|
package/src/posture/router.js
CHANGED
|
@@ -27,7 +27,34 @@ function ageHours(fp) {
|
|
|
27
27
|
return (Date.now() - fs.statSync(fp).mtimeMs) / 3_600_000;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
// Compare the two most-recent scan-history entries to tell the user whether
|
|
31
|
+
// they're trending safer or more exposed. Returns {} when there isn't enough
|
|
32
|
+
// history to say anything honest. (premortem: never invent a trend from one
|
|
33
|
+
// data point.)
|
|
34
|
+
export function computeScanTrend(scanRoot) {
|
|
35
|
+
const hist = readJson(path.join(scanRoot, '.agentic-security', 'scan-history.json'));
|
|
36
|
+
if (!Array.isArray(hist) || hist.length < 2) return {};
|
|
37
|
+
const cur = hist[hist.length - 1];
|
|
38
|
+
const prev = hist[hist.length - 2];
|
|
39
|
+
const sevOf = (e) => (e?.critical || 0) + (e?.high || 0);
|
|
40
|
+
const delta = sevOf(cur) - sevOf(prev);
|
|
41
|
+
let trend = 'flat';
|
|
42
|
+
if (delta < 0) trend = 'improving';
|
|
43
|
+
else if (delta > 0) trend = 'regressing';
|
|
44
|
+
const n = Math.abs(delta);
|
|
45
|
+
const whatChanged =
|
|
46
|
+
trend === 'improving' ? `${n} fewer critical+high than last scan — keep going.`
|
|
47
|
+
: trend === 'regressing' ? `${n} more critical+high than last scan — new risk crept in.`
|
|
48
|
+
: 'No change in critical+high since last scan.';
|
|
49
|
+
return { trend, whatChanged };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function decide(opts) {
|
|
53
|
+
const base = baseDecision(opts);
|
|
54
|
+
return { ...base, ...computeScanTrend(opts.scanRoot) };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function baseDecision({ scanRoot, intent }) {
|
|
31
58
|
const stateDir = path.join(scanRoot, '.agentic-security');
|
|
32
59
|
const lastScan = readJson(path.join(stateDir, 'last-scan.json'));
|
|
33
60
|
const scanAge = ageHours(path.join(stateDir, 'last-scan.json'));
|
|
@@ -102,8 +129,11 @@ export function explain(decision) {
|
|
|
102
129
|
``,
|
|
103
130
|
` Action: ${decision.action}`,
|
|
104
131
|
` Why: ${decision.reason}`,
|
|
105
|
-
` Run: ${decision.command}`,
|
|
106
|
-
``,
|
|
107
132
|
];
|
|
133
|
+
if (decision.trend) {
|
|
134
|
+
const arrow = decision.trend === 'improving' ? '↓' : decision.trend === 'regressing' ? '↑' : '→';
|
|
135
|
+
lines.push(` Trend: ${arrow} ${decision.whatChanged}`);
|
|
136
|
+
}
|
|
137
|
+
lines.push(` Run: ${decision.command}`, ``);
|
|
108
138
|
return lines.join('\n');
|
|
109
139
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Data Protection Impact Assessment (DPIA)
|
|
2
|
+
|
|
3
|
+
Generated by agentic-security scanner on 2026-05-31.
|
|
4
|
+
|
|
5
|
+
This is an automated DPIA scaffold derived from static analysis.
|
|
6
|
+
It must be reviewed and completed by a privacy officer before use.
|
|
7
|
+
|
|
8
|
+
## Data classes identified
|
|
9
|
+
|
|
10
|
+
## Privacy-related findings
|
|
11
|
+
|
|
12
|
+
| Severity | File:Line | Class → Sink | Description |
|
|
13
|
+
|---|---|---|---|
|
|
14
|
+
|
|
15
|
+
## Regulatory framework mapping
|
|
16
|
+
|
|
17
|
+
- **GDPR Art. 35** — DPIA required when processing is likely to result in high risk to data subjects.
|
|
18
|
+
- **CCPA §1798.130** — Notice + access rights for collected personal information.
|
|
19
|
+
|
|
20
|
+
## Reviewer checklist
|
|
21
|
+
|
|
22
|
+
- [ ] Confirm each PII field's collection has a documented lawful basis
|
|
23
|
+
- [ ] Confirm retention period for each class is documented
|
|
24
|
+
- [ ] Confirm DSAR (data subject access request) workflow exists
|
|
25
|
+
- [ ] Confirm encryption at rest + in transit for each class
|
|
26
|
+
- [ ] Confirm logging of PII access for audit (where applicable)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Threat Model (auto-generated)
|
|
2
2
|
|
|
3
|
-
Generated by agentic-security on 2026-05-
|
|
3
|
+
Generated by agentic-security on 2026-05-31.
|
|
4
4
|
|
|
5
5
|
This threat model is derived from static analysis of the current codebase and is regenerated on every scan. It is intended as a working artifact, not a finished compliance document.
|
|
6
6
|
|
package/src/report/index.js
CHANGED
|
@@ -1001,6 +1001,23 @@ export function toShipVerdict(scan, options = {}) {
|
|
|
1001
1001
|
} else if (advisoryCount > 0) {
|
|
1002
1002
|
lines.push(c(` ${advisoryCount} advisory item${advisoryCount === 1 ? '' : 's'} — run /security-scan-all --firehose to see them.`, DIM));
|
|
1003
1003
|
}
|
|
1004
|
+
// Coverage-honesty line (#5/#6): show the scan's blind spots — which
|
|
1005
|
+
// languages got flow analysis vs pattern-only, what was skipped, and how
|
|
1006
|
+
// many dangerous-looking calls had no finding. One concise line, not bloat.
|
|
1007
|
+
const _covMeta = scan._scanMeta;
|
|
1008
|
+
if (_covMeta && _covMeta.analysisTier) {
|
|
1009
|
+
const t = _covMeta.analysisTier;
|
|
1010
|
+
const pat = Object.keys(t.patternOnly || {});
|
|
1011
|
+
const segs = [`${_covMeta.filesScanned ?? '?'} files`];
|
|
1012
|
+
const ir = Object.keys(t.irTaint || {});
|
|
1013
|
+
if (ir.length) segs.push(`flow=[${ir.join(',')}]`);
|
|
1014
|
+
if (pat.length) segs.push(`pattern-only=[${pat.join(',')}]`);
|
|
1015
|
+
const skipped = (_covMeta.filesSkipped || 0) + (_covMeta.filesDenseSkipped || 0);
|
|
1016
|
+
if (skipped) segs.push(`${skipped} skipped`);
|
|
1017
|
+
if (_covMeta.unmodeledSinkCandidates?.count) segs.push(`${_covMeta.unmodeledSinkCandidates.count} unmodeled-sink candidate${_covMeta.unmodeledSinkCandidates.count === 1 ? '' : 's'}`);
|
|
1018
|
+
lines.push('');
|
|
1019
|
+
lines.push(c(' Coverage: ' + segs.join(' · '), DIM));
|
|
1020
|
+
}
|
|
1004
1021
|
lines.push('');
|
|
1005
1022
|
lines.push(c(' 🛡 agentic-security · created by ClearCapabilities.Com', DIM));
|
|
1006
1023
|
return lines.join('\n');
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Data Protection Impact Assessment (DPIA)
|
|
2
|
+
|
|
3
|
+
Generated by agentic-security scanner on 2026-06-01.
|
|
4
|
+
|
|
5
|
+
This is an automated DPIA scaffold derived from static analysis.
|
|
6
|
+
It must be reviewed and completed by a privacy officer before use.
|
|
7
|
+
|
|
8
|
+
## Data classes identified
|
|
9
|
+
|
|
10
|
+
## Privacy-related findings
|
|
11
|
+
|
|
12
|
+
| Severity | File:Line | Class → Sink | Description |
|
|
13
|
+
|---|---|---|---|
|
|
14
|
+
|
|
15
|
+
## Regulatory framework mapping
|
|
16
|
+
|
|
17
|
+
- **GDPR Art. 35** — DPIA required when processing is likely to result in high risk to data subjects.
|
|
18
|
+
- **CCPA §1798.130** — Notice + access rights for collected personal information.
|
|
19
|
+
|
|
20
|
+
## Reviewer checklist
|
|
21
|
+
|
|
22
|
+
- [ ] Confirm each PII field's collection has a documented lawful basis
|
|
23
|
+
- [ ] Confirm retention period for each class is documented
|
|
24
|
+
- [ ] Confirm DSAR (data subject access request) workflow exists
|
|
25
|
+
- [ ] Confirm encryption at rest + in transit for each class
|
|
26
|
+
- [ ] Confirm logging of PII access for audit (where applicable)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"generatedAt": "2026-06-01T03:15:31.819Z",
|
|
3
|
+
"summary": {
|
|
4
|
+
"total": 1,
|
|
5
|
+
"hndlCritical": 1,
|
|
6
|
+
"standard": 0,
|
|
7
|
+
"filesAffected": 1,
|
|
8
|
+
"primitivesNeeded": [
|
|
9
|
+
"ML-KEM-768"
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
"milestones": [
|
|
13
|
+
{
|
|
14
|
+
"id": "M1",
|
|
15
|
+
"title": "Inventory & policy",
|
|
16
|
+
"target": "90 days",
|
|
17
|
+
"owner": "security",
|
|
18
|
+
"items": [
|
|
19
|
+
"Confirm scanner findings against design docs",
|
|
20
|
+
"Adopt PQC migration policy (CNSA 2.0 / NIST IR 8547 alignment)",
|
|
21
|
+
"Establish KMS support for hybrid keys"
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": "M2",
|
|
26
|
+
"title": "HNDL-critical paths to PQ-hybrid",
|
|
27
|
+
"target": "180 days",
|
|
28
|
+
"owner": "platform",
|
|
29
|
+
"items": [
|
|
30
|
+
{
|
|
31
|
+
"finding": "pqc-rsa-encrypt:post-quantum-crypto.js:136",
|
|
32
|
+
"file": "post-quantum-crypto.js",
|
|
33
|
+
"line": 136,
|
|
34
|
+
"replacement": "X25519+ML-KEM-768 (CNSA 2.0 transitional)"
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "M3",
|
|
40
|
+
"title": "Standard signing/KEX migration",
|
|
41
|
+
"target": "12 months",
|
|
42
|
+
"owner": "platform",
|
|
43
|
+
"items": []
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"id": "M4",
|
|
47
|
+
"title": "Deprecate classical primitives",
|
|
48
|
+
"target": "24 months",
|
|
49
|
+
"owner": "security",
|
|
50
|
+
"items": [
|
|
51
|
+
"Remove dual-stack libraries once peers are PQ-capable",
|
|
52
|
+
"Rotate root CA / long-lived signing keys to ML-DSA"
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
"perFile": {
|
|
57
|
+
"post-quantum-crypto.js": {
|
|
58
|
+
"count": 1,
|
|
59
|
+
"subfamilies": [
|
|
60
|
+
"pqc-rsa-encrypt"
|
|
61
|
+
],
|
|
62
|
+
"hndlCritical": true
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Post-quantum cryptography migration plan
|
|
2
|
+
|
|
3
|
+
Generated 2026-06-01.
|
|
4
|
+
|
|
5
|
+
**1** pre-quantum primitive sites across **1** files.
|
|
6
|
+
HNDL-critical: **1** | Standard: **0**
|
|
7
|
+
|
|
8
|
+
## Recommended PQ primitives
|
|
9
|
+
- ML-KEM-768
|
|
10
|
+
|
|
11
|
+
## M1 — Inventory & policy (target 90 days, owner security)
|
|
12
|
+
- Confirm scanner findings against design docs
|
|
13
|
+
- Adopt PQC migration policy (CNSA 2.0 / NIST IR 8547 alignment)
|
|
14
|
+
- Establish KMS support for hybrid keys
|
|
15
|
+
|
|
16
|
+
## M2 — HNDL-critical paths to PQ-hybrid (target 180 days, owner platform)
|
|
17
|
+
- `post-quantum-crypto.js:136` → X25519+ML-KEM-768 (CNSA 2.0 transitional)
|
|
18
|
+
|
|
19
|
+
## M3 — Standard signing/KEX migration (target 12 months, owner platform)
|
|
20
|
+
|
|
21
|
+
## M4 — Deprecate classical primitives (target 24 months, owner security)
|
|
22
|
+
- Remove dual-stack libraries once peers are PQ-capable
|
|
23
|
+
- Rotate root CA / long-lived signing keys to ML-DSA
|
|
24
|
+
|
|
25
|
+
## References
|
|
26
|
+
- NIST FIPS 203 (ML-KEM), FIPS 204 (ML-DSA), FIPS 205 (SLH-DSA)
|
|
27
|
+
- NIST IR 8547 — Transition to Post-Quantum Cryptographic Standards
|
|
28
|
+
- CNSA 2.0 — Commercial National Security Algorithm Suite, Sept 2022
|
|
29
|
+
- RFC 9794 — X25519MLKEM768 hybrid key exchange for TLS 1.3
|
|
30
|
+
- Open Quantum Safe project (liboqs, oqs-provider for OpenSSL 3)
|