muaddib-scanner 2.4.4 → 2.4.5
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 +20 -20
- package/iocs/builtin.yaml +131 -131
- package/iocs/hashes.yaml +214 -214
- package/iocs/packages.yaml +276 -276
- package/package.json +2 -3
- package/src/canary-tokens.js +184 -184
- package/src/ioc/bootstrap.js +181 -181
- package/src/ioc/yaml-loader.js +223 -223
- package/src/maintainer-change.js +224 -224
- package/src/output-formatter.js +192 -192
- package/src/publish-anomaly.js +206 -206
- package/src/report.js +230 -230
- package/src/sarif.js +96 -96
- package/src/scanner/ai-config.js +183 -183
- package/src/scanner/ast-detectors.js +40 -17
- package/src/scanner/ast.js +1 -0
- package/src/scanner/dataflow.js +14 -2
- package/src/scanner/dependencies.js +223 -223
- package/src/scanner/entropy.js +7 -0
- package/src/scanner/hash.js +118 -118
- package/src/scanner/npm-registry.js +128 -128
- package/src/scanner/python.js +442 -442
- package/src/scoring.js +3 -1
- package/src/shared/analyze-helper.js +49 -49
- package/src/temporal-analysis.js +260 -260
- package/src/temporal-runner.js +139 -139
- package/src/utils.js +327 -327
- package/src/watch.js +55 -55
package/src/ioc/yaml-loader.js
CHANGED
|
@@ -1,223 +1,223 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const yaml = require('js-yaml');
|
|
4
|
-
|
|
5
|
-
const IOCS_DIR = path.join(__dirname, '../../iocs');
|
|
6
|
-
|
|
7
|
-
function loadYAMLIOCs() {
|
|
8
|
-
const iocs = {
|
|
9
|
-
packages: [],
|
|
10
|
-
hashes: [],
|
|
11
|
-
markers: [],
|
|
12
|
-
files: []
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
// Dedup sets for O(1) lookup during loading
|
|
16
|
-
const seenPkgs = new Set();
|
|
17
|
-
const seenHashes = new Set();
|
|
18
|
-
const seenMarkers = new Set();
|
|
19
|
-
const seenFiles = new Set();
|
|
20
|
-
|
|
21
|
-
// Charger packages.yaml
|
|
22
|
-
loadPackagesYAML(path.join(IOCS_DIR, 'packages.yaml'), iocs, seenPkgs);
|
|
23
|
-
|
|
24
|
-
// Charger builtin.yaml (fallback)
|
|
25
|
-
loadBuiltinYAML(path.join(IOCS_DIR, 'builtin.yaml'), iocs, seenPkgs, seenHashes, seenMarkers, seenFiles);
|
|
26
|
-
|
|
27
|
-
// Charger hashes.yaml
|
|
28
|
-
loadHashesYAML(path.join(IOCS_DIR, 'hashes.yaml'), iocs, seenHashes, seenMarkers, seenFiles);
|
|
29
|
-
|
|
30
|
-
return iocs;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function loadPackagesYAML(filePath, iocs, seenPkgs) {
|
|
34
|
-
if (!fs.existsSync(filePath)) return;
|
|
35
|
-
|
|
36
|
-
try {
|
|
37
|
-
const data = yaml.load(fs.readFileSync(filePath, 'utf8'), { schema: yaml.JSON_SCHEMA });
|
|
38
|
-
if (data && data.packages) {
|
|
39
|
-
for (const p of data.packages) {
|
|
40
|
-
if (!p.name || typeof p.name !== 'string') continue;
|
|
41
|
-
const key = p.name + '@' + p.version;
|
|
42
|
-
if (!seenPkgs.has(key)) {
|
|
43
|
-
seenPkgs.add(key);
|
|
44
|
-
iocs.packages.push({
|
|
45
|
-
id: p.id,
|
|
46
|
-
name: p.name,
|
|
47
|
-
version: p.version,
|
|
48
|
-
severity: p.severity || 'critical',
|
|
49
|
-
confidence: p.confidence || 'high',
|
|
50
|
-
source: p.source,
|
|
51
|
-
description: p.description,
|
|
52
|
-
references: p.references || [],
|
|
53
|
-
mitre: p.mitre || 'T1195.002'
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
} catch (e) {
|
|
59
|
-
console.error('[WARN] Erreur parsing packages.yaml:', e.message);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function loadBuiltinYAML(filePath, iocs, seenPkgs, seenHashes, seenMarkers, seenFiles) {
|
|
64
|
-
if (!fs.existsSync(filePath)) return;
|
|
65
|
-
|
|
66
|
-
try {
|
|
67
|
-
const data = yaml.load(fs.readFileSync(filePath, 'utf8'), { schema: yaml.JSON_SCHEMA });
|
|
68
|
-
|
|
69
|
-
// Packages
|
|
70
|
-
if (data && data.packages) {
|
|
71
|
-
for (const p of data.packages) {
|
|
72
|
-
if (!p.name || typeof p.name !== 'string') continue;
|
|
73
|
-
const key = p.name + '@' + p.version;
|
|
74
|
-
if (!seenPkgs.has(key)) {
|
|
75
|
-
seenPkgs.add(key);
|
|
76
|
-
iocs.packages.push({
|
|
77
|
-
id: `BUILTIN-${p.name}`,
|
|
78
|
-
name: p.name,
|
|
79
|
-
version: p.version,
|
|
80
|
-
severity: p.severity || 'critical',
|
|
81
|
-
confidence: p.confidence || 'high',
|
|
82
|
-
source: p.source,
|
|
83
|
-
description: p.description || `Malicious package: ${p.name}`,
|
|
84
|
-
references: p.references || [],
|
|
85
|
-
mitre: p.mitre || 'T1195.002'
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Files
|
|
92
|
-
if (data && data.files) {
|
|
93
|
-
for (const f of data.files) {
|
|
94
|
-
const fileName = typeof f === 'string' ? f : f.name;
|
|
95
|
-
if (!seenFiles.has(fileName)) {
|
|
96
|
-
seenFiles.add(fileName);
|
|
97
|
-
iocs.files.push({
|
|
98
|
-
id: `BUILTIN-FILE-${fileName}`,
|
|
99
|
-
name: fileName,
|
|
100
|
-
severity: 'critical',
|
|
101
|
-
confidence: 'high',
|
|
102
|
-
source: 'builtin',
|
|
103
|
-
description: `Suspicious file: ${fileName}`
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Hashes
|
|
110
|
-
if (data && data.hashes) {
|
|
111
|
-
for (const h of data.hashes) {
|
|
112
|
-
const hash = typeof h === 'string' ? h : h.sha256;
|
|
113
|
-
if (!seenHashes.has(hash)) {
|
|
114
|
-
seenHashes.add(hash);
|
|
115
|
-
iocs.hashes.push({
|
|
116
|
-
id: `BUILTIN-HASH-${hash.slice(0, 12)}`,
|
|
117
|
-
sha256: hash,
|
|
118
|
-
severity: 'critical',
|
|
119
|
-
confidence: 'high',
|
|
120
|
-
source: 'builtin',
|
|
121
|
-
description: 'Known malicious hash'
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Markers
|
|
128
|
-
if (data && data.markers) {
|
|
129
|
-
for (const m of data.markers) {
|
|
130
|
-
const pattern = typeof m === 'string' ? m : m.pattern;
|
|
131
|
-
if (!seenMarkers.has(pattern)) {
|
|
132
|
-
seenMarkers.add(pattern);
|
|
133
|
-
iocs.markers.push({
|
|
134
|
-
id: `BUILTIN-MARKER-${pattern.slice(0, 10)}`,
|
|
135
|
-
pattern: pattern,
|
|
136
|
-
severity: 'critical',
|
|
137
|
-
confidence: 'high',
|
|
138
|
-
source: 'builtin',
|
|
139
|
-
description: `Malware marker: ${pattern}`
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
} catch (e) {
|
|
145
|
-
console.error('[WARN] Erreur parsing builtin.yaml:', e.message);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function loadHashesYAML(filePath, iocs, seenHashes, seenMarkers, seenFiles) {
|
|
150
|
-
if (!fs.existsSync(filePath)) return;
|
|
151
|
-
|
|
152
|
-
try {
|
|
153
|
-
const data = yaml.load(fs.readFileSync(filePath, 'utf8'), { schema: yaml.JSON_SCHEMA });
|
|
154
|
-
|
|
155
|
-
if (data && data.hashes) {
|
|
156
|
-
for (const h of data.hashes) {
|
|
157
|
-
if (!seenHashes.has(h.sha256)) {
|
|
158
|
-
seenHashes.add(h.sha256);
|
|
159
|
-
iocs.hashes.push({
|
|
160
|
-
id: h.id,
|
|
161
|
-
sha256: h.sha256,
|
|
162
|
-
file: h.file,
|
|
163
|
-
severity: h.severity || 'critical',
|
|
164
|
-
confidence: h.confidence || 'high',
|
|
165
|
-
source: h.source,
|
|
166
|
-
description: h.description,
|
|
167
|
-
references: h.references || []
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (data && data.markers) {
|
|
174
|
-
for (const m of data.markers) {
|
|
175
|
-
if (!seenMarkers.has(m.pattern)) {
|
|
176
|
-
seenMarkers.add(m.pattern);
|
|
177
|
-
iocs.markers.push({
|
|
178
|
-
id: m.id,
|
|
179
|
-
pattern: m.pattern,
|
|
180
|
-
severity: m.severity || 'critical',
|
|
181
|
-
confidence: m.confidence || 'high',
|
|
182
|
-
source: m.source,
|
|
183
|
-
description: m.description
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
if (data && data.files) {
|
|
190
|
-
for (const f of data.files) {
|
|
191
|
-
if (!seenFiles.has(f.name)) {
|
|
192
|
-
seenFiles.add(f.name);
|
|
193
|
-
iocs.files.push({
|
|
194
|
-
id: f.id,
|
|
195
|
-
name: f.name,
|
|
196
|
-
severity: f.severity || 'critical',
|
|
197
|
-
confidence: f.confidence || 'high',
|
|
198
|
-
source: f.source,
|
|
199
|
-
description: f.description
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
} catch (e) {
|
|
205
|
-
console.error('[WARN] Erreur parsing hashes.yaml:', e.message);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
let _cachedIOCStats = null;
|
|
210
|
-
|
|
211
|
-
function getIOCStats() {
|
|
212
|
-
if (_cachedIOCStats) return _cachedIOCStats;
|
|
213
|
-
const iocs = loadYAMLIOCs();
|
|
214
|
-
_cachedIOCStats = {
|
|
215
|
-
packages: iocs.packages.length,
|
|
216
|
-
hashes: iocs.hashes.length,
|
|
217
|
-
markers: iocs.markers.length,
|
|
218
|
-
files: iocs.files.length
|
|
219
|
-
};
|
|
220
|
-
return _cachedIOCStats;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
module.exports = { loadYAMLIOCs, getIOCStats };
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const yaml = require('js-yaml');
|
|
4
|
+
|
|
5
|
+
const IOCS_DIR = path.join(__dirname, '../../iocs');
|
|
6
|
+
|
|
7
|
+
function loadYAMLIOCs() {
|
|
8
|
+
const iocs = {
|
|
9
|
+
packages: [],
|
|
10
|
+
hashes: [],
|
|
11
|
+
markers: [],
|
|
12
|
+
files: []
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// Dedup sets for O(1) lookup during loading
|
|
16
|
+
const seenPkgs = new Set();
|
|
17
|
+
const seenHashes = new Set();
|
|
18
|
+
const seenMarkers = new Set();
|
|
19
|
+
const seenFiles = new Set();
|
|
20
|
+
|
|
21
|
+
// Charger packages.yaml
|
|
22
|
+
loadPackagesYAML(path.join(IOCS_DIR, 'packages.yaml'), iocs, seenPkgs);
|
|
23
|
+
|
|
24
|
+
// Charger builtin.yaml (fallback)
|
|
25
|
+
loadBuiltinYAML(path.join(IOCS_DIR, 'builtin.yaml'), iocs, seenPkgs, seenHashes, seenMarkers, seenFiles);
|
|
26
|
+
|
|
27
|
+
// Charger hashes.yaml
|
|
28
|
+
loadHashesYAML(path.join(IOCS_DIR, 'hashes.yaml'), iocs, seenHashes, seenMarkers, seenFiles);
|
|
29
|
+
|
|
30
|
+
return iocs;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function loadPackagesYAML(filePath, iocs, seenPkgs) {
|
|
34
|
+
if (!fs.existsSync(filePath)) return;
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const data = yaml.load(fs.readFileSync(filePath, 'utf8'), { schema: yaml.JSON_SCHEMA });
|
|
38
|
+
if (data && data.packages) {
|
|
39
|
+
for (const p of data.packages) {
|
|
40
|
+
if (!p.name || typeof p.name !== 'string') continue;
|
|
41
|
+
const key = p.name + '@' + p.version;
|
|
42
|
+
if (!seenPkgs.has(key)) {
|
|
43
|
+
seenPkgs.add(key);
|
|
44
|
+
iocs.packages.push({
|
|
45
|
+
id: p.id,
|
|
46
|
+
name: p.name,
|
|
47
|
+
version: p.version,
|
|
48
|
+
severity: p.severity || 'critical',
|
|
49
|
+
confidence: p.confidence || 'high',
|
|
50
|
+
source: p.source,
|
|
51
|
+
description: p.description,
|
|
52
|
+
references: p.references || [],
|
|
53
|
+
mitre: p.mitre || 'T1195.002'
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
} catch (e) {
|
|
59
|
+
console.error('[WARN] Erreur parsing packages.yaml:', e.message);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function loadBuiltinYAML(filePath, iocs, seenPkgs, seenHashes, seenMarkers, seenFiles) {
|
|
64
|
+
if (!fs.existsSync(filePath)) return;
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const data = yaml.load(fs.readFileSync(filePath, 'utf8'), { schema: yaml.JSON_SCHEMA });
|
|
68
|
+
|
|
69
|
+
// Packages
|
|
70
|
+
if (data && data.packages) {
|
|
71
|
+
for (const p of data.packages) {
|
|
72
|
+
if (!p.name || typeof p.name !== 'string') continue;
|
|
73
|
+
const key = p.name + '@' + p.version;
|
|
74
|
+
if (!seenPkgs.has(key)) {
|
|
75
|
+
seenPkgs.add(key);
|
|
76
|
+
iocs.packages.push({
|
|
77
|
+
id: `BUILTIN-${p.name}`,
|
|
78
|
+
name: p.name,
|
|
79
|
+
version: p.version,
|
|
80
|
+
severity: p.severity || 'critical',
|
|
81
|
+
confidence: p.confidence || 'high',
|
|
82
|
+
source: p.source,
|
|
83
|
+
description: p.description || `Malicious package: ${p.name}`,
|
|
84
|
+
references: p.references || [],
|
|
85
|
+
mitre: p.mitre || 'T1195.002'
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Files
|
|
92
|
+
if (data && data.files) {
|
|
93
|
+
for (const f of data.files) {
|
|
94
|
+
const fileName = typeof f === 'string' ? f : f.name;
|
|
95
|
+
if (!seenFiles.has(fileName)) {
|
|
96
|
+
seenFiles.add(fileName);
|
|
97
|
+
iocs.files.push({
|
|
98
|
+
id: `BUILTIN-FILE-${fileName}`,
|
|
99
|
+
name: fileName,
|
|
100
|
+
severity: 'critical',
|
|
101
|
+
confidence: 'high',
|
|
102
|
+
source: 'builtin',
|
|
103
|
+
description: `Suspicious file: ${fileName}`
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Hashes
|
|
110
|
+
if (data && data.hashes) {
|
|
111
|
+
for (const h of data.hashes) {
|
|
112
|
+
const hash = typeof h === 'string' ? h : h.sha256;
|
|
113
|
+
if (!seenHashes.has(hash)) {
|
|
114
|
+
seenHashes.add(hash);
|
|
115
|
+
iocs.hashes.push({
|
|
116
|
+
id: `BUILTIN-HASH-${hash.slice(0, 12)}`,
|
|
117
|
+
sha256: hash,
|
|
118
|
+
severity: 'critical',
|
|
119
|
+
confidence: 'high',
|
|
120
|
+
source: 'builtin',
|
|
121
|
+
description: 'Known malicious hash'
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Markers
|
|
128
|
+
if (data && data.markers) {
|
|
129
|
+
for (const m of data.markers) {
|
|
130
|
+
const pattern = typeof m === 'string' ? m : m.pattern;
|
|
131
|
+
if (!seenMarkers.has(pattern)) {
|
|
132
|
+
seenMarkers.add(pattern);
|
|
133
|
+
iocs.markers.push({
|
|
134
|
+
id: `BUILTIN-MARKER-${pattern.slice(0, 10)}`,
|
|
135
|
+
pattern: pattern,
|
|
136
|
+
severity: 'critical',
|
|
137
|
+
confidence: 'high',
|
|
138
|
+
source: 'builtin',
|
|
139
|
+
description: `Malware marker: ${pattern}`
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
} catch (e) {
|
|
145
|
+
console.error('[WARN] Erreur parsing builtin.yaml:', e.message);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function loadHashesYAML(filePath, iocs, seenHashes, seenMarkers, seenFiles) {
|
|
150
|
+
if (!fs.existsSync(filePath)) return;
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const data = yaml.load(fs.readFileSync(filePath, 'utf8'), { schema: yaml.JSON_SCHEMA });
|
|
154
|
+
|
|
155
|
+
if (data && data.hashes) {
|
|
156
|
+
for (const h of data.hashes) {
|
|
157
|
+
if (!seenHashes.has(h.sha256)) {
|
|
158
|
+
seenHashes.add(h.sha256);
|
|
159
|
+
iocs.hashes.push({
|
|
160
|
+
id: h.id,
|
|
161
|
+
sha256: h.sha256,
|
|
162
|
+
file: h.file,
|
|
163
|
+
severity: h.severity || 'critical',
|
|
164
|
+
confidence: h.confidence || 'high',
|
|
165
|
+
source: h.source,
|
|
166
|
+
description: h.description,
|
|
167
|
+
references: h.references || []
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (data && data.markers) {
|
|
174
|
+
for (const m of data.markers) {
|
|
175
|
+
if (!seenMarkers.has(m.pattern)) {
|
|
176
|
+
seenMarkers.add(m.pattern);
|
|
177
|
+
iocs.markers.push({
|
|
178
|
+
id: m.id,
|
|
179
|
+
pattern: m.pattern,
|
|
180
|
+
severity: m.severity || 'critical',
|
|
181
|
+
confidence: m.confidence || 'high',
|
|
182
|
+
source: m.source,
|
|
183
|
+
description: m.description
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (data && data.files) {
|
|
190
|
+
for (const f of data.files) {
|
|
191
|
+
if (!seenFiles.has(f.name)) {
|
|
192
|
+
seenFiles.add(f.name);
|
|
193
|
+
iocs.files.push({
|
|
194
|
+
id: f.id,
|
|
195
|
+
name: f.name,
|
|
196
|
+
severity: f.severity || 'critical',
|
|
197
|
+
confidence: f.confidence || 'high',
|
|
198
|
+
source: f.source,
|
|
199
|
+
description: f.description
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
} catch (e) {
|
|
205
|
+
console.error('[WARN] Erreur parsing hashes.yaml:', e.message);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
let _cachedIOCStats = null;
|
|
210
|
+
|
|
211
|
+
function getIOCStats() {
|
|
212
|
+
if (_cachedIOCStats) return _cachedIOCStats;
|
|
213
|
+
const iocs = loadYAMLIOCs();
|
|
214
|
+
_cachedIOCStats = {
|
|
215
|
+
packages: iocs.packages.length,
|
|
216
|
+
hashes: iocs.hashes.length,
|
|
217
|
+
markers: iocs.markers.length,
|
|
218
|
+
files: iocs.files.length
|
|
219
|
+
};
|
|
220
|
+
return _cachedIOCStats;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
module.exports = { loadYAMLIOCs, getIOCStats };
|