muaddib-scanner 2.4.3 → 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/src/report.js CHANGED
@@ -1,230 +1,230 @@
1
- const fs = require('fs');
2
- const { escapeHtml } = require('./utils.js');
3
-
4
- function generateHTML(results) {
5
- const { target, timestamp, threats, summary } = results;
6
-
7
- const threatRows = threats.map(t => `
8
- <tr class="${escapeHtml(t.severity).toLowerCase()}">
9
- <td>${escapeHtml(t.severity)}</td>
10
- <td>${escapeHtml(t.type)}</td>
11
- <td>${escapeHtml(t.message)}</td>
12
- <td>${escapeHtml(t.file)}</td>
13
- <td>${escapeHtml(t.playbook)}</td>
14
- </tr>
15
- `).join('');
16
-
17
- return `<!DOCTYPE html>
18
- <html lang="en">
19
- <head>
20
- <meta charset="UTF-8">
21
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
22
- <title>MUAD'DIB - Scan Report</title>
23
- <style>
24
- body {
25
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
26
- background: #1a1a2e;
27
- color: #eee;
28
- margin: 0;
29
- padding: 20px;
30
- }
31
- .container {
32
- max-width: 1200px;
33
- margin: 0 auto;
34
- }
35
- h1 {
36
- color: #e94560;
37
- border-bottom: 2px solid #e94560;
38
- padding-bottom: 10px;
39
- }
40
- .summary {
41
- display: flex;
42
- gap: 20px;
43
- margin: 20px 0;
44
- }
45
- .summary-card {
46
- background: #16213e;
47
- padding: 20px;
48
- border-radius: 8px;
49
- flex: 1;
50
- }
51
- .summary-card h3 {
52
- margin: 0 0 10px 0;
53
- color: #888;
54
- font-size: 14px;
55
- }
56
- .summary-card .value {
57
- font-size: 32px;
58
- font-weight: bold;
59
- }
60
- .critical .value { color: #e94560; }
61
- .high .value { color: #ff6b35; }
62
- .medium .value { color: #f9c74f; }
63
- .total .value { color: #4ecdc4; }
64
- table {
65
- width: 100%;
66
- border-collapse: collapse;
67
- margin-top: 20px;
68
- }
69
- th, td {
70
- padding: 12px;
71
- text-align: left;
72
- border-bottom: 1px solid #333;
73
- }
74
- th {
75
- background: #16213e;
76
- color: #e94560;
77
- }
78
- tr.critical { background: rgba(233, 69, 96, 0.2); }
79
- tr.high { background: rgba(255, 107, 53, 0.2); }
80
- tr.medium { background: rgba(249, 199, 79, 0.1); }
81
- .meta {
82
- color: #666;
83
- font-size: 12px;
84
- margin-top: 40px;
85
- }
86
- .ok {
87
- background: #16213e;
88
- padding: 40px;
89
- border-radius: 8px;
90
- text-align: center;
91
- color: #4ecdc4;
92
- font-size: 24px;
93
- }
94
- .sandbox-section {
95
- background: #16213e;
96
- padding: 20px;
97
- border-radius: 8px;
98
- margin-top: 30px;
99
- border-left: 4px solid #9b59b6;
100
- }
101
- .sandbox-section h2 {
102
- color: #9b59b6;
103
- margin-top: 0;
104
- }
105
- .sandbox-meta {
106
- display: flex;
107
- gap: 30px;
108
- margin-bottom: 15px;
109
- }
110
- .sandbox-meta span {
111
- color: #aaa;
112
- }
113
- .sandbox-finding {
114
- padding: 8px 12px;
115
- margin: 4px 0;
116
- border-radius: 4px;
117
- background: rgba(155, 89, 182, 0.1);
118
- }
119
- </style>
120
- </head>
121
- <body>
122
- <div class="container">
123
- <h1>MUAD'DIB - Scan Report</h1>
124
-
125
- <div class="summary">
126
- <div class="summary-card total">
127
- <h3>TOTAL</h3>
128
- <div class="value">${escapeHtml(summary.total)}</div>
129
- </div>
130
- <div class="summary-card critical">
131
- <h3>CRITICAL</h3>
132
- <div class="value">${escapeHtml(summary.critical)}</div>
133
- </div>
134
- <div class="summary-card high">
135
- <h3>HIGH</h3>
136
- <div class="value">${escapeHtml(summary.high)}</div>
137
- </div>
138
- <div class="summary-card medium">
139
- <h3>MEDIUM</h3>
140
- <div class="value">${escapeHtml(summary.medium)}</div>
141
- </div>
142
- </div>
143
-
144
- ${threats.length > 0 ? `
145
- <table>
146
- <thead>
147
- <tr>
148
- <th>Severity</th>
149
- <th>Type</th>
150
- <th>Message</th>
151
- <th>File</th>
152
- <th>Recommendation</th>
153
- </tr>
154
- </thead>
155
- <tbody>
156
- ${threatRows}
157
- </tbody>
158
- </table>
159
- ` : '<div class="ok">No threats detected</div>'}
160
-
161
- ${results.sandbox ? `
162
- <div class="sandbox-section">
163
- <h2>[SANDBOX] Dynamic Analysis</h2>
164
- <div class="sandbox-meta">
165
- <span>Package: <strong>${escapeHtml(results.sandbox.package)}</strong></span>
166
- <span>Score: <strong>${escapeHtml(String(results.sandbox.score))}/100</strong></span>
167
- <span>Severity: <strong>${escapeHtml(results.sandbox.severity)}</strong></span>
168
- </div>
169
- ${results.sandbox.findings.length === 0
170
- ? '<p style="color: #4ecdc4;">No suspicious behavior detected.</p>'
171
- : results.sandbox.findings.map(f => `
172
- <div class="sandbox-finding">
173
- <strong>[${escapeHtml(f.severity)}]</strong> ${escapeHtml(f.type)}: ${escapeHtml(f.detail)}
174
- </div>
175
- `).join('')}
176
-
177
- ${results.sandbox.network ? `
178
- <h3 style="color: #9b59b6; margin-top: 20px;">Network Activity</h3>
179
- ${(results.sandbox.network.dns_resolutions || []).length > 0 ? `
180
- <h4 style="color: #aaa;">DNS Resolutions</h4>
181
- ${results.sandbox.network.dns_resolutions.map(r => `
182
- <div class="sandbox-finding">${escapeHtml(r.domain)} &rarr; ${escapeHtml(r.ip)}</div>
183
- `).join('')}
184
- ` : ''}
185
- ${(results.sandbox.network.http_requests || []).length > 0 ? `
186
- <h4 style="color: #aaa;">HTTP Requests</h4>
187
- ${results.sandbox.network.http_requests.map(r => `
188
- <div class="sandbox-finding">${escapeHtml(r.method)} ${escapeHtml(r.host)}${escapeHtml(r.path)}</div>
189
- `).join('')}
190
- ` : ''}
191
- ${(results.sandbox.network.tls_connections || []).length > 0 ? `
192
- <h4 style="color: #aaa;">TLS Connections</h4>
193
- ${results.sandbox.network.tls_connections.map(t => `
194
- <div class="sandbox-finding">${escapeHtml(t.domain)} (${escapeHtml(t.ip)}:${escapeHtml(String(t.port))})</div>
195
- `).join('')}
196
- ` : ''}
197
- ${(results.sandbox.network.blocked_connections || []).length > 0 ? `
198
- <h4 style="color: #e94560;">Blocked Connections</h4>
199
- ${results.sandbox.network.blocked_connections.map(b => `
200
- <div class="sandbox-finding" style="background: rgba(233,69,96,0.2);">[BLOCKED] ${escapeHtml(b.ip)}:${escapeHtml(String(b.port))}</div>
201
- `).join('')}
202
- ` : ''}
203
- ` : ''}
204
- </div>
205
- ` : ''}
206
-
207
- <div class="meta">
208
- <p>Target: ${escapeHtml(target)}</p>
209
- <p>Date: ${escapeHtml(timestamp)}</p>
210
- <p>Generated by MUAD'DIB</p>
211
- </div>
212
- </div>
213
- </body>
214
- </html>`;
215
- }
216
-
217
- function saveReport(results, outputPath) {
218
- if (!outputPath || typeof outputPath !== 'string') {
219
- throw new Error('Invalid output path for HTML report');
220
- }
221
- const html = generateHTML(results);
222
- try {
223
- fs.writeFileSync(outputPath, html);
224
- } catch (e) {
225
- throw new Error(`Failed to write HTML report to ${outputPath}: ${e.message}`);
226
- }
227
- return outputPath;
228
- }
229
-
230
- module.exports = { generateHTML, saveReport };
1
+ const fs = require('fs');
2
+ const { escapeHtml } = require('./utils.js');
3
+
4
+ function generateHTML(results) {
5
+ const { target, timestamp, threats, summary } = results;
6
+
7
+ const threatRows = threats.map(t => `
8
+ <tr class="${escapeHtml(t.severity).toLowerCase()}">
9
+ <td>${escapeHtml(t.severity)}</td>
10
+ <td>${escapeHtml(t.type)}</td>
11
+ <td>${escapeHtml(t.message)}</td>
12
+ <td>${escapeHtml(t.file)}</td>
13
+ <td>${escapeHtml(t.playbook)}</td>
14
+ </tr>
15
+ `).join('');
16
+
17
+ return `<!DOCTYPE html>
18
+ <html lang="en">
19
+ <head>
20
+ <meta charset="UTF-8">
21
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
22
+ <title>MUAD'DIB - Scan Report</title>
23
+ <style>
24
+ body {
25
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
26
+ background: #1a1a2e;
27
+ color: #eee;
28
+ margin: 0;
29
+ padding: 20px;
30
+ }
31
+ .container {
32
+ max-width: 1200px;
33
+ margin: 0 auto;
34
+ }
35
+ h1 {
36
+ color: #e94560;
37
+ border-bottom: 2px solid #e94560;
38
+ padding-bottom: 10px;
39
+ }
40
+ .summary {
41
+ display: flex;
42
+ gap: 20px;
43
+ margin: 20px 0;
44
+ }
45
+ .summary-card {
46
+ background: #16213e;
47
+ padding: 20px;
48
+ border-radius: 8px;
49
+ flex: 1;
50
+ }
51
+ .summary-card h3 {
52
+ margin: 0 0 10px 0;
53
+ color: #888;
54
+ font-size: 14px;
55
+ }
56
+ .summary-card .value {
57
+ font-size: 32px;
58
+ font-weight: bold;
59
+ }
60
+ .critical .value { color: #e94560; }
61
+ .high .value { color: #ff6b35; }
62
+ .medium .value { color: #f9c74f; }
63
+ .total .value { color: #4ecdc4; }
64
+ table {
65
+ width: 100%;
66
+ border-collapse: collapse;
67
+ margin-top: 20px;
68
+ }
69
+ th, td {
70
+ padding: 12px;
71
+ text-align: left;
72
+ border-bottom: 1px solid #333;
73
+ }
74
+ th {
75
+ background: #16213e;
76
+ color: #e94560;
77
+ }
78
+ tr.critical { background: rgba(233, 69, 96, 0.2); }
79
+ tr.high { background: rgba(255, 107, 53, 0.2); }
80
+ tr.medium { background: rgba(249, 199, 79, 0.1); }
81
+ .meta {
82
+ color: #666;
83
+ font-size: 12px;
84
+ margin-top: 40px;
85
+ }
86
+ .ok {
87
+ background: #16213e;
88
+ padding: 40px;
89
+ border-radius: 8px;
90
+ text-align: center;
91
+ color: #4ecdc4;
92
+ font-size: 24px;
93
+ }
94
+ .sandbox-section {
95
+ background: #16213e;
96
+ padding: 20px;
97
+ border-radius: 8px;
98
+ margin-top: 30px;
99
+ border-left: 4px solid #9b59b6;
100
+ }
101
+ .sandbox-section h2 {
102
+ color: #9b59b6;
103
+ margin-top: 0;
104
+ }
105
+ .sandbox-meta {
106
+ display: flex;
107
+ gap: 30px;
108
+ margin-bottom: 15px;
109
+ }
110
+ .sandbox-meta span {
111
+ color: #aaa;
112
+ }
113
+ .sandbox-finding {
114
+ padding: 8px 12px;
115
+ margin: 4px 0;
116
+ border-radius: 4px;
117
+ background: rgba(155, 89, 182, 0.1);
118
+ }
119
+ </style>
120
+ </head>
121
+ <body>
122
+ <div class="container">
123
+ <h1>MUAD'DIB - Scan Report</h1>
124
+
125
+ <div class="summary">
126
+ <div class="summary-card total">
127
+ <h3>TOTAL</h3>
128
+ <div class="value">${escapeHtml(summary.total)}</div>
129
+ </div>
130
+ <div class="summary-card critical">
131
+ <h3>CRITICAL</h3>
132
+ <div class="value">${escapeHtml(summary.critical)}</div>
133
+ </div>
134
+ <div class="summary-card high">
135
+ <h3>HIGH</h3>
136
+ <div class="value">${escapeHtml(summary.high)}</div>
137
+ </div>
138
+ <div class="summary-card medium">
139
+ <h3>MEDIUM</h3>
140
+ <div class="value">${escapeHtml(summary.medium)}</div>
141
+ </div>
142
+ </div>
143
+
144
+ ${threats.length > 0 ? `
145
+ <table>
146
+ <thead>
147
+ <tr>
148
+ <th>Severity</th>
149
+ <th>Type</th>
150
+ <th>Message</th>
151
+ <th>File</th>
152
+ <th>Recommendation</th>
153
+ </tr>
154
+ </thead>
155
+ <tbody>
156
+ ${threatRows}
157
+ </tbody>
158
+ </table>
159
+ ` : '<div class="ok">No threats detected</div>'}
160
+
161
+ ${results.sandbox ? `
162
+ <div class="sandbox-section">
163
+ <h2>[SANDBOX] Dynamic Analysis</h2>
164
+ <div class="sandbox-meta">
165
+ <span>Package: <strong>${escapeHtml(results.sandbox.package)}</strong></span>
166
+ <span>Score: <strong>${escapeHtml(String(results.sandbox.score))}/100</strong></span>
167
+ <span>Severity: <strong>${escapeHtml(results.sandbox.severity)}</strong></span>
168
+ </div>
169
+ ${results.sandbox.findings.length === 0
170
+ ? '<p style="color: #4ecdc4;">No suspicious behavior detected.</p>'
171
+ : results.sandbox.findings.map(f => `
172
+ <div class="sandbox-finding">
173
+ <strong>[${escapeHtml(f.severity)}]</strong> ${escapeHtml(f.type)}: ${escapeHtml(f.detail)}
174
+ </div>
175
+ `).join('')}
176
+
177
+ ${results.sandbox.network ? `
178
+ <h3 style="color: #9b59b6; margin-top: 20px;">Network Activity</h3>
179
+ ${(results.sandbox.network.dns_resolutions || []).length > 0 ? `
180
+ <h4 style="color: #aaa;">DNS Resolutions</h4>
181
+ ${results.sandbox.network.dns_resolutions.map(r => `
182
+ <div class="sandbox-finding">${escapeHtml(r.domain)} &rarr; ${escapeHtml(r.ip)}</div>
183
+ `).join('')}
184
+ ` : ''}
185
+ ${(results.sandbox.network.http_requests || []).length > 0 ? `
186
+ <h4 style="color: #aaa;">HTTP Requests</h4>
187
+ ${results.sandbox.network.http_requests.map(r => `
188
+ <div class="sandbox-finding">${escapeHtml(r.method)} ${escapeHtml(r.host)}${escapeHtml(r.path)}</div>
189
+ `).join('')}
190
+ ` : ''}
191
+ ${(results.sandbox.network.tls_connections || []).length > 0 ? `
192
+ <h4 style="color: #aaa;">TLS Connections</h4>
193
+ ${results.sandbox.network.tls_connections.map(t => `
194
+ <div class="sandbox-finding">${escapeHtml(t.domain)} (${escapeHtml(t.ip)}:${escapeHtml(String(t.port))})</div>
195
+ `).join('')}
196
+ ` : ''}
197
+ ${(results.sandbox.network.blocked_connections || []).length > 0 ? `
198
+ <h4 style="color: #e94560;">Blocked Connections</h4>
199
+ ${results.sandbox.network.blocked_connections.map(b => `
200
+ <div class="sandbox-finding" style="background: rgba(233,69,96,0.2);">[BLOCKED] ${escapeHtml(b.ip)}:${escapeHtml(String(b.port))}</div>
201
+ `).join('')}
202
+ ` : ''}
203
+ ` : ''}
204
+ </div>
205
+ ` : ''}
206
+
207
+ <div class="meta">
208
+ <p>Target: ${escapeHtml(target)}</p>
209
+ <p>Date: ${escapeHtml(timestamp)}</p>
210
+ <p>Generated by MUAD'DIB</p>
211
+ </div>
212
+ </div>
213
+ </body>
214
+ </html>`;
215
+ }
216
+
217
+ function saveReport(results, outputPath) {
218
+ if (!outputPath || typeof outputPath !== 'string') {
219
+ throw new Error('Invalid output path for HTML report');
220
+ }
221
+ const html = generateHTML(results);
222
+ try {
223
+ fs.writeFileSync(outputPath, html);
224
+ } catch (e) {
225
+ throw new Error(`Failed to write HTML report to ${outputPath}: ${e.message}`);
226
+ }
227
+ return outputPath;
228
+ }
229
+
230
+ module.exports = { generateHTML, saveReport };
package/src/sarif.js CHANGED
@@ -1,97 +1,97 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const { RULES } = require('./rules/index.js');
4
-
5
- const pkgVersion = (() => {
6
- try {
7
- return JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')).version;
8
- } catch {
9
- return '0.0.0';
10
- }
11
- })();
12
-
13
- function generateSARIF(results) {
14
- const sarif = {
15
- $schema: 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json',
16
- version: '2.1.0',
17
- runs: [
18
- {
19
- tool: {
20
- driver: {
21
- name: 'MUADDIB',
22
- version: pkgVersion,
23
- informationUri: 'https://github.com/DNSZLSK/muad-dib',
24
- rules: Object.values(RULES).map(rule => ({
25
- id: rule.id,
26
- name: rule.name,
27
- shortDescription: { text: rule.description },
28
- fullDescription: { text: rule.description },
29
- helpUri: rule.references[0] || '',
30
- properties: {
31
- severity: rule.severity,
32
- confidence: rule.confidence,
33
- mitre: rule.mitre
34
- }
35
- }))
36
- }
37
- },
38
- properties: results.sandbox ? {
39
- sandbox: {
40
- score: results.sandbox.score,
41
- severity: results.sandbox.severity,
42
- network: results.sandbox.network || {}
43
- }
44
- } : {},
45
- results: (results.threats || []).map(threat => ({
46
- ruleId: threat.rule_id,
47
- level: sarifLevel(threat.severity),
48
- message: { text: threat.message },
49
- locations: [
50
- {
51
- physicalLocation: {
52
- artifactLocation: {
53
- uri: encodeURI(threat.file || ''),
54
- uriBaseId: '%SRCROOT%'
55
- },
56
- region: {
57
- startLine: threat.line || 1
58
- }
59
- }
60
- }
61
- ],
62
- properties: {
63
- confidence: threat.confidence,
64
- mitre: threat.mitre
65
- }
66
- }))
67
- }
68
- ]
69
- };
70
-
71
- return sarif;
72
- }
73
-
74
- function sarifLevel(severity) {
75
- switch (severity) {
76
- case 'CRITICAL': return 'error';
77
- case 'HIGH': return 'error';
78
- case 'MEDIUM': return 'warning';
79
- case 'LOW': return 'note';
80
- default: return 'note';
81
- }
82
- }
83
-
84
- function saveSARIF(results, outputPath) {
85
- if (!outputPath || typeof outputPath !== 'string') {
86
- throw new Error('Invalid output path for SARIF report');
87
- }
88
- const sarif = generateSARIF(results);
89
- try {
90
- fs.writeFileSync(outputPath, JSON.stringify(sarif, null, 2));
91
- } catch (e) {
92
- throw new Error(`Failed to write SARIF report to ${outputPath}: ${e.message}`);
93
- }
94
- return outputPath;
95
- }
96
-
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { RULES } = require('./rules/index.js');
4
+
5
+ const pkgVersion = (() => {
6
+ try {
7
+ return JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')).version;
8
+ } catch {
9
+ return '0.0.0';
10
+ }
11
+ })();
12
+
13
+ function generateSARIF(results) {
14
+ const sarif = {
15
+ $schema: 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json',
16
+ version: '2.1.0',
17
+ runs: [
18
+ {
19
+ tool: {
20
+ driver: {
21
+ name: 'MUADDIB',
22
+ version: pkgVersion,
23
+ informationUri: 'https://github.com/DNSZLSK/muad-dib',
24
+ rules: Object.values(RULES).map(rule => ({
25
+ id: rule.id,
26
+ name: rule.name,
27
+ shortDescription: { text: rule.description },
28
+ fullDescription: { text: rule.description },
29
+ helpUri: rule.references[0] || '',
30
+ properties: {
31
+ severity: rule.severity,
32
+ confidence: rule.confidence,
33
+ mitre: rule.mitre
34
+ }
35
+ }))
36
+ }
37
+ },
38
+ properties: results.sandbox ? {
39
+ sandbox: {
40
+ score: results.sandbox.score,
41
+ severity: results.sandbox.severity,
42
+ network: results.sandbox.network || {}
43
+ }
44
+ } : {},
45
+ results: (results.threats || []).map(threat => ({
46
+ ruleId: threat.rule_id,
47
+ level: sarifLevel(threat.severity),
48
+ message: { text: threat.message },
49
+ locations: [
50
+ {
51
+ physicalLocation: {
52
+ artifactLocation: {
53
+ uri: encodeURI(threat.file || ''),
54
+ uriBaseId: '%SRCROOT%'
55
+ },
56
+ region: {
57
+ startLine: threat.line || 1
58
+ }
59
+ }
60
+ }
61
+ ],
62
+ properties: {
63
+ confidence: threat.confidence,
64
+ mitre: threat.mitre
65
+ }
66
+ }))
67
+ }
68
+ ]
69
+ };
70
+
71
+ return sarif;
72
+ }
73
+
74
+ function sarifLevel(severity) {
75
+ switch (severity) {
76
+ case 'CRITICAL': return 'error';
77
+ case 'HIGH': return 'error';
78
+ case 'MEDIUM': return 'warning';
79
+ case 'LOW': return 'note';
80
+ default: return 'note';
81
+ }
82
+ }
83
+
84
+ function saveSARIF(results, outputPath) {
85
+ if (!outputPath || typeof outputPath !== 'string') {
86
+ throw new Error('Invalid output path for SARIF report');
87
+ }
88
+ const sarif = generateSARIF(results);
89
+ try {
90
+ fs.writeFileSync(outputPath, JSON.stringify(sarif, null, 2));
91
+ } catch (e) {
92
+ throw new Error(`Failed to write SARIF report to ${outputPath}: ${e.message}`);
93
+ }
94
+ return outputPath;
95
+ }
96
+
97
97
  module.exports = { generateSARIF, saveSARIF };