@clear-capabilities/agentic-security-scanner 0.140.0 → 0.141.0
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 +148 -0
- package/dist/113.index.js +79 -3
- package/dist/178.index.js +1 -1
- package/dist/238.index.js +77 -1
- package/dist/384.index.js +1 -1
- package/dist/435.index.js +12 -0
- package/dist/526.index.js +79 -3
- package/dist/637.index.js +1 -1
- package/dist/agentic-security.mjs +14 -14
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/dist/compliance-frameworks/ccpa.json +34 -7
- package/dist/compliance-frameworks/eu-ai-act.json +65 -14
- package/dist/compliance-frameworks/gdpr.json +56 -12
- package/dist/compliance-frameworks/hipaa-security-rule.json +68 -15
- package/dist/compliance-frameworks/nist-ai-600-1.json +57 -12
- package/dist/compliance-frameworks/nist-csf-2.json +78 -16
- package/dist/compliance-frameworks/nist-privacy-1-1.json +3 -0
- package/dist/compliance-frameworks/owasp-asvs-5.json +91 -20
- package/dist/compliance-frameworks/owasp-llm-top-10.json +89 -20
- package/package.json +16 -5
- package/src/dataflow/catalog.js +61 -0
- package/src/engine.js +262 -22
- package/src/mcp/tools.js +12 -0
- package/src/posture/accuracy-scorecard.js +57 -0
- package/src/posture/aibom.js +110 -1
- package/src/posture/auditor-walkthrough.js +56 -17
- package/src/posture/compliance-frameworks/ccpa.json +34 -7
- package/src/posture/compliance-frameworks/eu-ai-act.json +65 -14
- package/src/posture/compliance-frameworks/gdpr.json +56 -12
- package/src/posture/compliance-frameworks/hipaa-security-rule.json +68 -15
- package/src/posture/compliance-frameworks/nist-ai-600-1.json +57 -12
- package/src/posture/compliance-frameworks/nist-csf-2.json +78 -16
- package/src/posture/compliance-frameworks/nist-privacy-1-1.json +3 -0
- package/src/posture/compliance-frameworks/owasp-asvs-5.json +91 -20
- package/src/posture/compliance-frameworks/owasp-llm-top-10.json +89 -20
- package/src/posture/concurrency-checker.js +3 -3
- package/src/posture/coverage-strength.js +182 -0
- package/src/posture/epss.js +17 -1
- package/src/posture/family-registry.js +103 -0
- package/src/posture/family-resolve.js +47 -0
- package/src/posture/fix-coverage.js +113 -0
- package/src/posture/fix-metrics.js +76 -0
- package/src/posture/mcp-rug-pull.js +144 -0
- package/src/posture/poc-inprocess.js +217 -1
- package/src/posture/proof-coverage.js +162 -0
- package/src/posture/reachability-filter.js +44 -0
- package/src/posture/sbom.js +12 -3
- package/src/runScan.js +56 -5
- package/src/sast/CLAUDE.md +2 -2
- package/src/sast/claude-md-prompt-injection.js +47 -3
- package/src/sast/cloud-iam.js +23 -0
- package/src/sast/convention-deviation.js +66 -3
- package/src/sast/crypto-protocol.js +23 -0
- package/src/sast/dapp-frontend.js +20 -0
- package/src/sast/iac-cloud-templates.js +337 -0
- package/src/sast/k8s-admission.js +27 -0
- package/src/sast/ml-supply-chain.js +22 -0
- package/src/sast/ruby.js +132 -0
- package/src/sast/web3-advanced.js +26 -0
- package/src/sca/CLAUDE.md +21 -4
- package/src/sca/container.js +18 -1
- package/src/sca/dep-confusion.js +69 -3
package/src/sca/dep-confusion.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
// 0.9.0 Feat-15: Dependency confusion + typosquat detection (Levenshtein distance against top-1000 npm/PyPI packages).
|
|
2
2
|
//
|
|
3
|
-
// (a) Typosquat: Levenshtein distance
|
|
3
|
+
// (a) Typosquat: a small Damerau-Levenshtein distance from a popular package,
|
|
4
|
+
// where "small" is judged RELATIVE to the name length — see SIMILARITY
|
|
5
|
+
// below for why the absolute 1–2 this originally used produced 166
|
|
6
|
+
// critical/high false positives and zero true positives across 13 real
|
|
7
|
+
// dependency trees.
|
|
4
8
|
// (b) Confusion: internal-scoped names (`@your-org/...`) that also appear on the
|
|
5
9
|
// public registry — declared via .agentic-security/internal-scopes.yml.
|
|
6
10
|
//
|
|
@@ -50,6 +54,64 @@ export function levenshtein(a, b, maxDistance = 2) {
|
|
|
50
54
|
return prev[b.length];
|
|
51
55
|
}
|
|
52
56
|
|
|
57
|
+
// Damerau-Levenshtein (optimal string alignment): a TRANSPOSITION costs 1.
|
|
58
|
+
//
|
|
59
|
+
// Plain Levenshtein charges 2 for a transposition, which is exactly backwards
|
|
60
|
+
// for this problem — swapping two adjacent characters (`lodahs` for `lodash`,
|
|
61
|
+
// `electorn` for `electron`) is the single most common real typo, and it is the
|
|
62
|
+
// one plain distance scores as least similar. `levenshtein` above is kept
|
|
63
|
+
// unchanged and separately tested; this is a second measure for a different
|
|
64
|
+
// question.
|
|
65
|
+
export function damerauLevenshtein(a, b, maxDistance = 2) {
|
|
66
|
+
if (a === b) return 0;
|
|
67
|
+
if (Math.abs(a.length - b.length) > maxDistance) return maxDistance + 1;
|
|
68
|
+
const m = a.length, n = b.length;
|
|
69
|
+
if (!m) return n;
|
|
70
|
+
if (!n) return m;
|
|
71
|
+
const d = Array.from({ length: m + 1 }, (_, i) => {
|
|
72
|
+
const row = new Array(n + 1).fill(0);
|
|
73
|
+
row[0] = i;
|
|
74
|
+
return row;
|
|
75
|
+
});
|
|
76
|
+
for (let j = 0; j <= n; j++) d[0][j] = j;
|
|
77
|
+
for (let i = 1; i <= m; i++) {
|
|
78
|
+
let rowMin = Infinity;
|
|
79
|
+
for (let j = 1; j <= n; j++) {
|
|
80
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
81
|
+
let v = Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);
|
|
82
|
+
if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
|
|
83
|
+
v = Math.min(v, d[i - 2][j - 2] + 1); // transposition
|
|
84
|
+
}
|
|
85
|
+
d[i][j] = v;
|
|
86
|
+
if (v < rowMin) rowMin = v;
|
|
87
|
+
}
|
|
88
|
+
if (rowMin > maxDistance) return maxDistance + 1;
|
|
89
|
+
}
|
|
90
|
+
return d[m][n];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// How much of a name may differ before it is a DIFFERENT name rather than a
|
|
94
|
+
// misspelling of this one.
|
|
95
|
+
//
|
|
96
|
+
// Measured, not chosen by taste. At the previous "distance 1–2, any length"
|
|
97
|
+
// rule, bench/sca-replay produced these across 13 real repositories:
|
|
98
|
+
//
|
|
99
|
+
// ms ~ ws (d1) acorn ~ cors (d2) ajv ~ ava (d2) six ~ tox (d2)
|
|
100
|
+
// abab ~ ava (d2) arg ~ yargs (d2) bail ~ babel (d2) aws4 ~ ws (d2)
|
|
101
|
+
//
|
|
102
|
+
// Every one is a legitimate, popular package, and `ms` is a top-50 npm
|
|
103
|
+
// package. The common factor is length: two edits on a four-character name
|
|
104
|
+
// changes half of it. A quarter of the shorter name is the ceiling — it keeps
|
|
105
|
+
// every genuine shape (one transposition, one dropped char, one doubled char
|
|
106
|
+
// on a name of ordinary length) and rejects all of the above.
|
|
107
|
+
const MAX_DIVERGENCE = 0.25;
|
|
108
|
+
|
|
109
|
+
function _isTyposquatCandidate(name, popular, distance) {
|
|
110
|
+
if (distance <= 0) return false;
|
|
111
|
+
const shorter = Math.min(name.length, popular.length);
|
|
112
|
+
return distance / shorter <= MAX_DIVERGENCE;
|
|
113
|
+
}
|
|
114
|
+
|
|
53
115
|
function _loadInternalScopes(scanRoot) {
|
|
54
116
|
if (!scanRoot) return [];
|
|
55
117
|
for (const name of ['internal-scopes.yml', 'internal-scopes.yaml']) {
|
|
@@ -84,10 +146,14 @@ export function detectDepConfusion(components, scanRoot) {
|
|
|
84
146
|
const lowerName = c.name.toLowerCase();
|
|
85
147
|
// (1) Typosquat — only run if the dep is NOT itself in the popular set
|
|
86
148
|
if (!popularSet.has(lowerName)) {
|
|
149
|
+
// Compare the BARE name. `@scope/react` is not a typosquat of `react`;
|
|
150
|
+
// the scope is the thing that identifies the publisher.
|
|
151
|
+
const bare = lowerName.replace(/^@[^/]+\//, '');
|
|
87
152
|
let bestMatch = null, bestDist = 3;
|
|
88
153
|
for (const popular of popularSet) {
|
|
89
|
-
const d =
|
|
90
|
-
if (
|
|
154
|
+
const d = damerauLevenshtein(bare, popular, 2);
|
|
155
|
+
if (!_isTyposquatCandidate(bare, popular, d)) continue;
|
|
156
|
+
if (d < bestDist) { bestMatch = popular; bestDist = d; }
|
|
91
157
|
}
|
|
92
158
|
if (bestMatch) {
|
|
93
159
|
const id = `dep-confusion:${c.ecosystem}:${c.name}@${c.version}:typosquat`;
|