@lateos/npm-scan 0.15.5 → 0.16.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/README.de.md +11 -2
- package/README.fr.md +11 -2
- package/README.ja.md +11 -2
- package/README.md +28 -2
- package/README.zh.md +11 -2
- package/backend/detectors/cve-2026-48710-badhost/codePattern.js +99 -0
- package/backend/detectors/cve-2026-48710-badhost/findings.js +105 -0
- package/backend/detectors/cve-2026-48710-badhost/index.js +15 -0
- package/backend/detectors/cve-2026-48710-badhost/manifest.js +305 -0
- package/backend/detectors/cve-2026-48710-badhost/transitive.js +189 -0
- package/backend/detectors/index.js +6 -0
- package/backend/detectors/node-ipc-compromise/d1-version-blocklist.js +24 -0
- package/backend/detectors/node-ipc-compromise/d10-unauthorized-publisher.js +19 -0
- package/backend/detectors/node-ipc-compromise/d11-blast-radius.js +40 -0
- package/backend/detectors/node-ipc-compromise/d2-tarball-hash.js +31 -0
- package/backend/detectors/node-ipc-compromise/d3-cjs-payload-injection.js +73 -0
- package/backend/detectors/node-ipc-compromise/d4-injected-payload-hash.js +37 -0
- package/backend/detectors/node-ipc-compromise/d5-dns-c2-pattern.js +49 -0
- package/backend/detectors/node-ipc-compromise/d6-bootstrap-resolver.js +40 -0
- package/backend/detectors/node-ipc-compromise/d7-dns-txt-exfil.js +42 -0
- package/backend/detectors/node-ipc-compromise/d8-runtime-trigger.js +27 -0
- package/backend/detectors/node-ipc-compromise/d9-temp-artifact.js +20 -0
- package/backend/detectors/node-ipc-compromise/index.js +93 -0
- package/backend/detectors/node-ipc-compromise/iocs.json +59 -0
- package/backend/detectors/trapdoor/d1-campaign-marker.js +20 -0
- package/backend/detectors/trapdoor/d2-payload-fingerprint.js +22 -0
- package/backend/detectors/trapdoor/d3-publisher-blocklist.js +10 -0
- package/backend/detectors/trapdoor/d4-gists-exfil.js +34 -0
- package/backend/detectors/trapdoor/d5-ai-poisoning.js +35 -0
- package/backend/detectors/trapdoor/d6-lure-name.js +42 -0
- package/backend/detectors/trapdoor/d7-crypto-primitives.js +22 -0
- package/backend/detectors/trapdoor/d8-xor-key.js +15 -0
- package/backend/detectors/trapdoor/d9-cred-validation.js +32 -0
- package/backend/detectors/trapdoor/index.js +77 -0
- package/backend/detectors/trapdoor/iocs.json +51 -0
- package/package.json +1 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const CRED_VALIDATION_PATTERNS = [
|
|
2
|
+
/sts\.amazonaws\.com/i,
|
|
3
|
+
/api\.github\.com\/user/i,
|
|
4
|
+
];
|
|
5
|
+
|
|
6
|
+
export function scanCredValidation(allFiles, pkgJson) {
|
|
7
|
+
const matches = [];
|
|
8
|
+
|
|
9
|
+
const scripts = pkgJson?.scripts || {};
|
|
10
|
+
for (const [hook, content] of Object.entries(scripts)) {
|
|
11
|
+
if (/preinstall|install|postinstall|prepare/.test(hook)) {
|
|
12
|
+
for (const pattern of CRED_VALIDATION_PATTERNS) {
|
|
13
|
+
if (pattern.test(content)) {
|
|
14
|
+
matches.push({ file: `script:${hook}`, pattern: pattern.source });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
for (const file of allFiles) {
|
|
21
|
+
const path = file.path || '';
|
|
22
|
+
if (!path.endsWith('.js') && !path.endsWith('.mjs') && !path.endsWith('.cjs')) continue;
|
|
23
|
+
const content = file.content || '';
|
|
24
|
+
for (const pattern of CRED_VALIDATION_PATTERNS) {
|
|
25
|
+
if (pattern.test(content)) {
|
|
26
|
+
matches.push({ file: path, pattern: pattern.source });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return { triggered: matches.length > 0, matches };
|
|
32
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { scanCampaignMarker } from './d1-campaign-marker.js';
|
|
2
|
+
import { scanPayloadFingerprint } from './d2-payload-fingerprint.js';
|
|
3
|
+
import { scanPublisherBlocklist } from './d3-publisher-blocklist.js';
|
|
4
|
+
import { scanGistsExfil } from './d4-gists-exfil.js';
|
|
5
|
+
import { scanAIPoisoning } from './d5-ai-poisoning.js';
|
|
6
|
+
import { scanLureName } from './d6-lure-name.js';
|
|
7
|
+
import { scanCryptoPrimitives } from './d7-crypto-primitives.js';
|
|
8
|
+
import { scanXorKey } from './d8-xor-key.js';
|
|
9
|
+
import { scanCredValidation } from './d9-cred-validation.js';
|
|
10
|
+
|
|
11
|
+
const RULE_SEVERITY = {
|
|
12
|
+
D1: 'critical',
|
|
13
|
+
D2: 'critical',
|
|
14
|
+
D3: 'critical',
|
|
15
|
+
D4: 'critical',
|
|
16
|
+
D5: 'high',
|
|
17
|
+
D6: 'medium',
|
|
18
|
+
D7: 'high',
|
|
19
|
+
D8: 'high',
|
|
20
|
+
D9: 'critical',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const SEVERITY_ORDER = ['critical', 'high', 'medium', 'low', 'info', 'none'];
|
|
24
|
+
|
|
25
|
+
function highestSeverity(severities) {
|
|
26
|
+
for (const s of SEVERITY_ORDER) {
|
|
27
|
+
if (severities.includes(s)) return s;
|
|
28
|
+
}
|
|
29
|
+
return 'none';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function scan(pkgJson, files = [], registryMeta = null, allFiles = null) {
|
|
33
|
+
const fileList = allFiles || files || [];
|
|
34
|
+
|
|
35
|
+
const results = {
|
|
36
|
+
D1: scanCampaignMarker(fileList),
|
|
37
|
+
D2: scanPayloadFingerprint(fileList),
|
|
38
|
+
D3: scanPublisherBlocklist(pkgJson, registryMeta),
|
|
39
|
+
D4: scanGistsExfil(fileList, pkgJson),
|
|
40
|
+
D5: scanAIPoisoning(fileList),
|
|
41
|
+
D6: scanLureName(pkgJson, registryMeta),
|
|
42
|
+
D7: scanCryptoPrimitives(fileList, pkgJson),
|
|
43
|
+
D8: scanXorKey(fileList),
|
|
44
|
+
D9: scanCredValidation(fileList, pkgJson),
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const triggered = Object.entries(results)
|
|
48
|
+
.filter(([_, r]) => r.triggered)
|
|
49
|
+
.map(([id]) => id);
|
|
50
|
+
|
|
51
|
+
if (triggered.length === 0) return [];
|
|
52
|
+
|
|
53
|
+
const severity = highestSeverity(triggered.map(id => RULE_SEVERITY[id]));
|
|
54
|
+
|
|
55
|
+
const evidence = {
|
|
56
|
+
campaign: 'TRAPDOOR',
|
|
57
|
+
triggeredRules: triggered,
|
|
58
|
+
details: Object.fromEntries(
|
|
59
|
+
Object.entries(results).filter(([_, r]) => r.triggered)
|
|
60
|
+
),
|
|
61
|
+
iocSummary: {
|
|
62
|
+
publisher: 'asdxzxc',
|
|
63
|
+
c2Domain: 'ddjidd564.github.io',
|
|
64
|
+
campaignMarker: 'P-2024-001',
|
|
65
|
+
payloadFile: 'trap-core.js',
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return [{
|
|
70
|
+
id: 'TRAPDOOR',
|
|
71
|
+
severity,
|
|
72
|
+
title: 'TrapDoor cross-ecosystem supply chain attack campaign',
|
|
73
|
+
description: `${triggered.length} signal(s): ${triggered.join(', ')}`,
|
|
74
|
+
evidence: JSON.stringify(evidence),
|
|
75
|
+
mitigation: 'Block install immediately. Revoke any npm tokens associated with this package. Rotate CI/CD secrets. Audit for postinstall scripts accessing credentials. Check for AI config poisoning (.cursorrules/CLAUDE.md). Verify all package versions from publisher asdxzxc. If confirmed compromise, follow incident response procedures per SECURITY.md.',
|
|
76
|
+
}];
|
|
77
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lastUpdated": "2026-05-28T00:00:00.000Z",
|
|
3
|
+
"campaign": {
|
|
4
|
+
"id": "trapdoor",
|
|
5
|
+
"description": "TrapDoor cross-ecosystem supply chain attack (npm, PyPI, Crates.io) targeting crypto, DeFi, Solana, and AI developer communities.",
|
|
6
|
+
"firstObserved": "2026-05-22T00:00:00.000Z",
|
|
7
|
+
"attribution": {
|
|
8
|
+
"npmPublisher": "asdxzxc",
|
|
9
|
+
"githubAccount": "ddjidd564",
|
|
10
|
+
"githubPages": "ddjidd564.github.io"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"iocs": [
|
|
14
|
+
{
|
|
15
|
+
"type": "publisherAccount",
|
|
16
|
+
"value": "asdxzxc",
|
|
17
|
+
"ecosystem": "npm",
|
|
18
|
+
"notes": "TrapDoor campaign — known malicious npm publisher account."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"type": "campaignMarker",
|
|
22
|
+
"value": "P-2024-001",
|
|
23
|
+
"notes": "Hardcoded campaign marker found in package files (README.md, .cursorrules, CLAUDE.md)."
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"type": "payloadFilename",
|
|
27
|
+
"value": "trap-core.js",
|
|
28
|
+
"notes": "Shared payload filename in TrapDoor packages."
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"type": "payloadSize",
|
|
32
|
+
"value": 48485,
|
|
33
|
+
"notes": "Exact byte size of trap-core.js payload."
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"type": "xorKey",
|
|
37
|
+
"value": "cargo-build-helper-2026",
|
|
38
|
+
"notes": "XOR key string found in Crates.io / cross-ecosystem packages."
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"type": "c2Domain",
|
|
42
|
+
"value": "ddjidd564.github.io",
|
|
43
|
+
"notes": "GitHub Pages C2 infrastructure."
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"type": "gistDomain",
|
|
47
|
+
"value": "gist.github.com",
|
|
48
|
+
"notes": "GitHub Gist used for exfiltration."
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lateos/npm-scan",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Modern npm supply chain security scanner — detects obfuscated payloads, credential stealers, conditional triggers, sandbox evasion, and worm-like propagation. 11 attack types, SBOM, NIST/EU CRA compliance reporting.",
|
|
5
5
|
"main": "backend/index.js",
|
|
6
6
|
"bin": {
|