agent-master-devsecops 0.0.0-stage → 5.1.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.md +204 -2
- package/agents/AGENT_DEVSECOPS.md +32 -0
- package/agents/AGENT_INFRA.md +31 -0
- package/agents/AGENT_ORCHESTRATOR.md +39 -0
- package/agents/AGENT_QA.md +58 -0
- package/agents/AGENT_SCRUM.md +23 -0
- package/agents/AGENT_SRE.md +22 -0
- package/agents/AGENT_UIUX.md +26 -0
- package/docs/ARCHITECTURE.md +152 -0
- package/docs/CALCULATIONS.md +17 -0
- package/docs/CHANGELOG.md +112 -0
- package/docs/ENGINEERING_RULES.md +56 -0
- package/docs/IDEAS.md +17 -0
- package/docs/LOCAL_BACKLOG.md +20 -0
- package/docs/STATE.md +111 -0
- package/docs/playbooks/PLAYBOOK_CIRCUIT_BREAKER_LLM.md +58 -0
- package/docs/playbooks/PLAYBOOK_OPENTELEMETRY_TRACING.md +58 -0
- package/package.json +57 -4
- package/scripts/auditors/a11y_auditor.js +462 -0
- package/scripts/auditors/code_quality_auditor.js +444 -0
- package/scripts/auditors/contract_test_validator.js +396 -0
- package/scripts/auditors/eval_harness.js +213 -0
- package/scripts/auditors/git_hygiene_linter.js +391 -0
- package/scripts/auditors/pr_reviewer.js +627 -0
- package/scripts/auditors/sast_scanner.js +442 -0
- package/scripts/auditors/sca_dependency_auditor.js +673 -0
- package/scripts/bin/bootstrap_cli.js +597 -0
- package/scripts/bin/domain_idea_miner.js +658 -0
- package/scripts/bin/smart_onboarding.js +905 -0
- package/scripts/bin/web_inspector.js +578 -0
- package/scripts/core/architecture_advisor.js +378 -0
- package/scripts/core/ast_skeletonizer.js +413 -0
- package/scripts/core/autonomous_remediation_engine.js +305 -0
- package/scripts/core/circuit_breaker.js +546 -0
- package/scripts/core/context_compressor.js +237 -0
- package/scripts/core/fast_feature_scaffold.js +840 -0
- package/scripts/core/hot_state_cache.js +238 -0
- package/scripts/core/knowledge_gateway.js +888 -0
- package/scripts/core/magic_prompt_generator.js +250 -0
- package/scripts/core/multi_agent_orchestrator.js +421 -0
- package/scripts/core/otel_tracer.js +335 -0
- package/scripts/core/perf_benchmark.js +212 -0
- package/scripts/core/sarif_engine.js +299 -0
- package/scripts/core/sre_remediation_engine.js +236 -0
- package/scripts/core/universal_packager.js +357 -0
- package/scripts/integrations/atlassian_client.js +600 -0
- package/scripts/integrations/atlassian_sync_action.js +576 -0
- package/scripts/integrations/confluence_sync.js +684 -0
- package/scripts/integrations/credentials_guard.js +500 -0
- package/scripts/integrations/jira_manager.js +722 -0
- package/scripts/mcp/handlers/atlassian_handlers.js +300 -0
- package/scripts/mcp/handlers/core_handlers.js +419 -0
- package/scripts/mcp/handlers/qa_handlers.js +502 -0
- package/scripts/mcp/handlers/rules_handlers.js +379 -0
- package/scripts/mcp/handlers/scaffold_handlers.js +334 -0
- package/scripts/mcp/handlers/sre_handlers.js +443 -0
- package/scripts/mcp/resources_registry.js +453 -0
- package/scripts/mcp/server.js +408 -0
- package/scripts/mcp/tool_handlers.js +444 -0
- package/scripts/mcp/tools_registry.js +1123 -0
- package/scripts/mcp_server.js +236 -0
|
@@ -0,0 +1,673 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scripts/auditors/sca_dependency_auditor.js
|
|
3
|
+
* Auditor Industrial de Composição de Software (SCA) & Conformidade de Licenças (v5.1 Enterprise)
|
|
4
|
+
*
|
|
5
|
+
* Integração multi-tier e de primeira classe com ferramentas oficiais da indústria:
|
|
6
|
+
* - npm audit nativo (banco de vulnerabilidades GitHub Advisory / CVE em tempo real)
|
|
7
|
+
* - Trivy CLI (scanner universal de vulnerabilidades em sistemas de arquivos e dependências)
|
|
8
|
+
* - OSV.dev Live API via KnowledgeGateway (Tier 1 de Grounding com Hot Cache SQLite WAL)
|
|
9
|
+
* - Fallback determinístico para catálogo de contingência e checagem de licenças copyleft tóxicas
|
|
10
|
+
* - Emissão de relatórios oficiais em padrão OASIS SARIF v2.1.0 (auditToSarif)
|
|
11
|
+
*/
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const fs = require('node:fs');
|
|
15
|
+
const path = require('node:path');
|
|
16
|
+
const { spawnSync } = require('node:child_process');
|
|
17
|
+
const { getKnowledgeGateway } = require('../core/knowledge_gateway.js');
|
|
18
|
+
const { createSarifReport } = require('../core/sarif_engine.js');
|
|
19
|
+
|
|
20
|
+
// Catálogo imutável de licenças com restrições copyleft estritas (risco comercial e IP)
|
|
21
|
+
const RESTRICTIVE_LICENSES = Object.freeze([
|
|
22
|
+
'GPL-2.0',
|
|
23
|
+
'GPL-3.0',
|
|
24
|
+
'AGPL-3.0',
|
|
25
|
+
'SSPL',
|
|
26
|
+
'LGPL-3.0',
|
|
27
|
+
'GPL',
|
|
28
|
+
'AGPL'
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
// Base de conhecimento offline de vulnerabilidades conhecidas (Fallback determinístico)
|
|
32
|
+
const KNOWN_VULNERABLE_PACKAGES = Object.freeze([
|
|
33
|
+
Object.freeze({
|
|
34
|
+
name: 'lodash',
|
|
35
|
+
affectedVersionRegex: /^([0-3]\.|4\.(0|1[0-6]\.|17\.[0-9]\b|17\.1[0-9]\b|17\.20\b))/,
|
|
36
|
+
cve: 'CVE-2020-8203 / CWE-1321',
|
|
37
|
+
severity: 'ALTA',
|
|
38
|
+
description: 'Poluição de Protótipo (Prototype Pollution) via zipObjectDeep e defaultsDeep.',
|
|
39
|
+
recommendation: 'Atualize para lodash >= 4.17.21'
|
|
40
|
+
}),
|
|
41
|
+
Object.freeze({
|
|
42
|
+
name: 'axios',
|
|
43
|
+
affectedVersionRegex: /^(0\.(1[0-9]\.|2[0-1]\.[0-1]\b))/,
|
|
44
|
+
cve: 'CVE-2020-28168 / CWE-918',
|
|
45
|
+
severity: 'ALTA',
|
|
46
|
+
description: 'Server-Side Request Forgery (SSRF) e vazamento de cabeçalho de autorização em redirects.',
|
|
47
|
+
recommendation: 'Atualize para axios >= 0.21.2 ou >= 1.x'
|
|
48
|
+
}),
|
|
49
|
+
Object.freeze({
|
|
50
|
+
name: 'tar',
|
|
51
|
+
affectedVersionRegex: /^([0-5]\.|6\.(0\.|1\.[0-8]\b))/,
|
|
52
|
+
cve: 'CVE-2021-37712 / CWE-22',
|
|
53
|
+
severity: 'ALTA',
|
|
54
|
+
description: 'Path Traversal e sobrescrita arbitrária de arquivos durante extração de tarballs.',
|
|
55
|
+
recommendation: 'Atualize para tar >= 6.1.9'
|
|
56
|
+
}),
|
|
57
|
+
Object.freeze({
|
|
58
|
+
name: 'minimist',
|
|
59
|
+
affectedVersionRegex: /^([0-1]\.(0\.|1\.|2\.[0-5]\b))/,
|
|
60
|
+
cve: 'CVE-2020-7598 / CWE-1321',
|
|
61
|
+
severity: 'MÉDIA',
|
|
62
|
+
description: 'Prototype Pollution em argumento de linha de comando.',
|
|
63
|
+
recommendation: 'Atualize para minimist >= 1.2.6'
|
|
64
|
+
}),
|
|
65
|
+
Object.freeze({
|
|
66
|
+
name: 'shell-quote',
|
|
67
|
+
affectedVersionRegex: /^(1\.[0-6]\.|1\.7\.[0-2]\b)/,
|
|
68
|
+
cve: 'CVE-2021-42740 / CWE-78',
|
|
69
|
+
severity: 'CRÍTICA',
|
|
70
|
+
description: 'Command Injection via caracteres de escape mal formatados.',
|
|
71
|
+
recommendation: 'Atualize para shell-quote >= 1.7.3'
|
|
72
|
+
})
|
|
73
|
+
]);
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Executa comandos CLI com timeout seguro, isolamento de buffer e proteção windowsHide.
|
|
77
|
+
*/
|
|
78
|
+
function runCliSafely(bin, args = [], options = {}) {
|
|
79
|
+
try {
|
|
80
|
+
const timeout = options.timeout || 30000;
|
|
81
|
+
const maxBuffer = options.maxBuffer || 15 * 1024 * 1024;
|
|
82
|
+
const cwd = options.cwd || process.cwd();
|
|
83
|
+
|
|
84
|
+
if (process.platform === 'win32') {
|
|
85
|
+
return spawnSync(process.env.ComSpec || 'cmd.exe', ['/d', '/s', '/c', bin, ...args], {
|
|
86
|
+
encoding: 'utf8',
|
|
87
|
+
timeout,
|
|
88
|
+
maxBuffer,
|
|
89
|
+
windowsHide: true,
|
|
90
|
+
cwd
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return spawnSync(bin, args, {
|
|
94
|
+
encoding: 'utf8',
|
|
95
|
+
timeout,
|
|
96
|
+
maxBuffer,
|
|
97
|
+
windowsHide: true,
|
|
98
|
+
cwd
|
|
99
|
+
});
|
|
100
|
+
} catch (err) {
|
|
101
|
+
return { status: -1, error: err };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Verifica se o comando npm audit está operacional no ambiente do host.
|
|
107
|
+
* @returns {boolean}
|
|
108
|
+
*/
|
|
109
|
+
function isNpmAuditAvailable() {
|
|
110
|
+
try {
|
|
111
|
+
const res = runCliSafely('npm', ['--version'], { timeout: 10000 });
|
|
112
|
+
return res.status === 0;
|
|
113
|
+
} catch (_) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Verifica se a ferramenta Trivy CLI está disponível no PATH do host.
|
|
120
|
+
* @returns {boolean}
|
|
121
|
+
*/
|
|
122
|
+
function isTrivyAvailable() {
|
|
123
|
+
try {
|
|
124
|
+
const res = runCliSafely('trivy', ['--version'], { timeout: 10000 });
|
|
125
|
+
return res.status === 0;
|
|
126
|
+
} catch (_) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Executa auditoria nativa real com npm audit.
|
|
133
|
+
* @param {string} targetDir
|
|
134
|
+
* @returns {object|null}
|
|
135
|
+
*/
|
|
136
|
+
function auditWithNpmAudit(targetDir) {
|
|
137
|
+
try {
|
|
138
|
+
const res = runCliSafely('npm', ['audit', '--json'], { cwd: targetDir, timeout: 60000 });
|
|
139
|
+
if (!res.stdout) return null;
|
|
140
|
+
|
|
141
|
+
let auditData;
|
|
142
|
+
try {
|
|
143
|
+
auditData = JSON.parse(res.stdout);
|
|
144
|
+
} catch (_) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (auditData.error) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const vulnerabilities = [];
|
|
153
|
+
const rawVulns = auditData.vulnerabilities || {};
|
|
154
|
+
|
|
155
|
+
for (const [pkgName, detail] of Object.entries(rawVulns)) {
|
|
156
|
+
const severityMap = {
|
|
157
|
+
critical: 'CRÍTICA',
|
|
158
|
+
high: 'ALTA',
|
|
159
|
+
moderate: 'MÉDIA',
|
|
160
|
+
low: 'BAIXA',
|
|
161
|
+
info: 'INFO'
|
|
162
|
+
};
|
|
163
|
+
const severity = severityMap[detail.severity?.toLowerCase()] || 'ALTA';
|
|
164
|
+
const cves = Array.isArray(detail.via)
|
|
165
|
+
? detail.via.map(v => typeof v === 'object' ? `${v.title} (${v.url || v.name})` : v).join('; ')
|
|
166
|
+
: (typeof detail.via === 'string' ? detail.via : 'Advisory');
|
|
167
|
+
|
|
168
|
+
vulnerabilities.push({
|
|
169
|
+
package: pkgName,
|
|
170
|
+
installedVersion: detail.range || 'N/A',
|
|
171
|
+
severity,
|
|
172
|
+
cve: cves,
|
|
173
|
+
description: `Detectado via npm audit: ${cves}`,
|
|
174
|
+
recommendation: detail.fixAvailable ? `npm audit fix (disponível: ${JSON.stringify(detail.fixAvailable)})` : 'Revisão manual da árvore de dependências'
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const criticalOrHighVulns = vulnerabilities.filter(v => v.severity === 'CRÍTICA' || v.severity === 'ALTA');
|
|
179
|
+
const totalDeps = (auditData.metadata?.dependencies?.total) || Object.keys(rawVulns).length;
|
|
180
|
+
|
|
181
|
+
// Checagem de licença do manifesto
|
|
182
|
+
const pkgPath = path.join(targetDir, 'package.json');
|
|
183
|
+
const licenseIssues = [];
|
|
184
|
+
if (fs.existsSync(pkgPath)) {
|
|
185
|
+
try {
|
|
186
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
187
|
+
const lic = pkg.license || 'UNLICENSED';
|
|
188
|
+
if (RESTRICTIVE_LICENSES.some(r => String(lic).toUpperCase().includes(r))) {
|
|
189
|
+
licenseIssues.push({
|
|
190
|
+
package: pkg.name || 'root-project',
|
|
191
|
+
license: lic,
|
|
192
|
+
severity: 'ALTA',
|
|
193
|
+
message: `Licença restritiva detectada: ${lic}. Pode exigir abertura compulsória do código-fonte.`
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
} catch (err) {
|
|
197
|
+
void err;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const approved = criticalOrHighVulns.length === 0 && licenseIssues.length === 0;
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
engine: 'npm_audit_industrial',
|
|
205
|
+
source: 'npm_audit_cli',
|
|
206
|
+
approved,
|
|
207
|
+
totalDependencies: totalDeps,
|
|
208
|
+
vulnerabilitiesCount: vulnerabilities.length,
|
|
209
|
+
criticalVulnerabilities: criticalOrHighVulns.length,
|
|
210
|
+
vulnerabilities,
|
|
211
|
+
licenseIssues,
|
|
212
|
+
wildcardIssues: [],
|
|
213
|
+
verdict: approved ? 'CONFORME' : 'VETADO',
|
|
214
|
+
auditSummary: auditData.metadata?.vulnerabilities || null
|
|
215
|
+
};
|
|
216
|
+
} catch (_) {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Executa auditoria nativa real com Trivy CLI.
|
|
223
|
+
* @param {string} targetDir
|
|
224
|
+
* @returns {object|null}
|
|
225
|
+
*/
|
|
226
|
+
function auditWithTrivy(targetDir) {
|
|
227
|
+
try {
|
|
228
|
+
const res = runCliSafely('trivy', ['fs', '--format', 'json', '--quiet', targetDir], { timeout: 60000 });
|
|
229
|
+
if (!res.stdout || res.status !== 0) return null;
|
|
230
|
+
|
|
231
|
+
const data = JSON.parse(res.stdout);
|
|
232
|
+
const results = data.Results || [];
|
|
233
|
+
const vulnerabilities = [];
|
|
234
|
+
|
|
235
|
+
for (const r of results) {
|
|
236
|
+
const vulns = r.Vulnerabilities || [];
|
|
237
|
+
for (const v of vulns) {
|
|
238
|
+
const sevMap = {
|
|
239
|
+
CRITICAL: 'CRÍTICA',
|
|
240
|
+
HIGH: 'ALTA',
|
|
241
|
+
MEDIUM: 'MÉDIA',
|
|
242
|
+
LOW: 'BAIXA'
|
|
243
|
+
};
|
|
244
|
+
vulnerabilities.push({
|
|
245
|
+
package: v.PkgName,
|
|
246
|
+
installedVersion: v.InstalledVersion,
|
|
247
|
+
severity: sevMap[v.Severity] || 'MÉDIA',
|
|
248
|
+
cve: v.VulnerabilityID,
|
|
249
|
+
description: v.Title || v.Description || 'Trivy finding',
|
|
250
|
+
recommendation: v.FixedVersion ? `Atualizar para versão >= ${v.FixedVersion}` : 'Consultar fornecedor'
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const criticalOrHighVulns = vulnerabilities.filter(v => v.severity === 'CRÍTICA' || v.severity === 'ALTA');
|
|
256
|
+
const approved = criticalOrHighVulns.length === 0;
|
|
257
|
+
|
|
258
|
+
return {
|
|
259
|
+
engine: 'trivy_cli_industrial',
|
|
260
|
+
source: 'trivy_cli',
|
|
261
|
+
approved,
|
|
262
|
+
totalDependencies: vulnerabilities.length,
|
|
263
|
+
vulnerabilitiesCount: vulnerabilities.length,
|
|
264
|
+
criticalVulnerabilities: criticalOrHighVulns.length,
|
|
265
|
+
vulnerabilities,
|
|
266
|
+
licenseIssues: [],
|
|
267
|
+
wildcardIssues: [],
|
|
268
|
+
verdict: approved ? 'CONFORME' : 'VETADO'
|
|
269
|
+
};
|
|
270
|
+
} catch (_) {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Normaliza e parseia manifesto package.json com tratamento defensivo.
|
|
277
|
+
* @param {string|object} input
|
|
278
|
+
* @returns {object}
|
|
279
|
+
*/
|
|
280
|
+
function resolveManifest(input) {
|
|
281
|
+
if (typeof input === 'object' && input !== null) {
|
|
282
|
+
return input;
|
|
283
|
+
}
|
|
284
|
+
if (typeof input === 'string') {
|
|
285
|
+
if (fs.existsSync(input)) {
|
|
286
|
+
const stats = fs.statSync(input);
|
|
287
|
+
if (stats.isDirectory()) {
|
|
288
|
+
const pkgPath = path.join(input, 'package.json');
|
|
289
|
+
if (fs.existsSync(pkgPath)) {
|
|
290
|
+
return JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return JSON.parse(fs.readFileSync(input, 'utf8'));
|
|
294
|
+
}
|
|
295
|
+
try {
|
|
296
|
+
return JSON.parse(input);
|
|
297
|
+
} catch (_) {
|
|
298
|
+
throw new Error(`Entrada de manifesto inválida: ${input.substring(0, 50)}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (!input) {
|
|
302
|
+
return { name: 'unspecified-project', dependencies: {}, devDependencies: {}, license: 'UNLICENSED' };
|
|
303
|
+
}
|
|
304
|
+
throw new Error('Entrada inválida para manifesto package.json.');
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Audita um manifesto ou diretório para SCA e conformidade de licenças.
|
|
309
|
+
* @param {string|object} manifestInput
|
|
310
|
+
* @param {object} [options={}]
|
|
311
|
+
* @returns {object}
|
|
312
|
+
*/
|
|
313
|
+
function auditDependenciesSCA(manifestInput, options = {}) {
|
|
314
|
+
const preferLiveTool = options.preferLiveTool !== false;
|
|
315
|
+
|
|
316
|
+
// 1. Se for um caminho de arquivo ou diretório existente, tenta ferramentas industriais
|
|
317
|
+
if (preferLiveTool && typeof manifestInput === 'string' && fs.existsSync(manifestInput)) {
|
|
318
|
+
const stats = fs.statSync(manifestInput);
|
|
319
|
+
const targetDir = stats.isDirectory() ? manifestInput : path.dirname(manifestInput);
|
|
320
|
+
|
|
321
|
+
if (isTrivyAvailable()) {
|
|
322
|
+
const trivyRes = auditWithTrivy(targetDir);
|
|
323
|
+
if (trivyRes) return trivyRes;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const lockPath = path.join(targetDir, 'package-lock.json');
|
|
327
|
+
const shrinkwrapPath = path.join(targetDir, 'npm-shrinkwrap.json');
|
|
328
|
+
if ((fs.existsSync(lockPath) || fs.existsSync(shrinkwrapPath)) && isNpmAuditAvailable()) {
|
|
329
|
+
const npmRes = auditWithNpmAudit(targetDir);
|
|
330
|
+
if (npmRes) return npmRes;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// 2. Fallback determinístico para catálogo estático e análise de manifesto
|
|
335
|
+
let manifest;
|
|
336
|
+
try {
|
|
337
|
+
manifest = resolveManifest(manifestInput);
|
|
338
|
+
} catch (err) {
|
|
339
|
+
return {
|
|
340
|
+
approved: false,
|
|
341
|
+
error: `Falha ao carregar manifesto: ${err.message}`,
|
|
342
|
+
vulnerabilities: [],
|
|
343
|
+
licenseIssues: []
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const prodDeps = manifest.dependencies || {};
|
|
348
|
+
const devDeps = manifest.devDependencies || {};
|
|
349
|
+
const allDeps = { ...prodDeps, ...devDeps };
|
|
350
|
+
|
|
351
|
+
const vulnerabilities = [];
|
|
352
|
+
const licenseIssues = [];
|
|
353
|
+
const wildcardIssues = [];
|
|
354
|
+
|
|
355
|
+
// Auditoria de Licença do Próprio Pacote
|
|
356
|
+
const projectLicense = manifest.license || 'UNLICENSED';
|
|
357
|
+
if (RESTRICTIVE_LICENSES.some(lic => String(projectLicense).toUpperCase().includes(lic))) {
|
|
358
|
+
licenseIssues.push({
|
|
359
|
+
package: manifest.name || 'root-project',
|
|
360
|
+
license: projectLicense,
|
|
361
|
+
severity: 'ALTA',
|
|
362
|
+
message: `Licença restritiva detectada: ${projectLicense}. Pode exigir abertura compulsória do código-fonte.`
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Auditoria de Versões e Vulnerabilidades Conhecidas
|
|
367
|
+
for (const [pkgName, versionSpec] of Object.entries(allDeps)) {
|
|
368
|
+
const rawVersion = String(versionSpec).replace(/^[\^~>=<]+/, '').trim();
|
|
369
|
+
|
|
370
|
+
// Checagem de Wildcard (reprodutibilidade)
|
|
371
|
+
if (versionSpec === '*' || versionSpec === 'latest') {
|
|
372
|
+
wildcardIssues.push({
|
|
373
|
+
package: pkgName,
|
|
374
|
+
version: versionSpec,
|
|
375
|
+
severity: 'MÉDIA',
|
|
376
|
+
message: `Uso de versão não determinística ("${versionSpec}"). Fere a Regra 1 de reprodutibilidade.`
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Checagem de CVEs conhecidas
|
|
381
|
+
for (const vuln of KNOWN_VULNERABLE_PACKAGES) {
|
|
382
|
+
if (vuln.name === pkgName && vuln.affectedVersionRegex.test(rawVersion)) {
|
|
383
|
+
vulnerabilities.push({
|
|
384
|
+
package: pkgName,
|
|
385
|
+
installedVersion: versionSpec,
|
|
386
|
+
severity: vuln.severity,
|
|
387
|
+
cve: vuln.cve,
|
|
388
|
+
description: vuln.description,
|
|
389
|
+
recommendation: vuln.recommendation
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const criticalOrHighVulns = vulnerabilities.filter(v => v.severity === 'CRÍTICA' || v.severity === 'ALTA');
|
|
396
|
+
const approved = criticalOrHighVulns.length === 0 && licenseIssues.length === 0;
|
|
397
|
+
|
|
398
|
+
return {
|
|
399
|
+
engine: 'offline_catalog_fallback',
|
|
400
|
+
source: 'embedded_cve_catalog',
|
|
401
|
+
advisoryNotice: 'Auditoria executada via catálogo determinístico de contingência. Para análise em tempo real com bancos CVE oficiais (NVD/OSV), forneça o package-lock.json e utilize npm audit ou Trivy CLI.',
|
|
402
|
+
approved,
|
|
403
|
+
totalDependencies: Object.keys(allDeps).length,
|
|
404
|
+
vulnerabilitiesCount: vulnerabilities.length,
|
|
405
|
+
criticalVulnerabilities: criticalOrHighVulns.length,
|
|
406
|
+
vulnerabilities,
|
|
407
|
+
licenseIssues,
|
|
408
|
+
wildcardIssues,
|
|
409
|
+
verdict: approved ? 'CONFORME' : 'VETADO'
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Executa auditoria de dependências consumindo a base oficial do OSV.dev via KnowledgeGateway.
|
|
415
|
+
* @param {string|object} manifestInput
|
|
416
|
+
* @param {object} [options={}]
|
|
417
|
+
* @returns {Promise<object>}
|
|
418
|
+
*/
|
|
419
|
+
async function auditWithOsvApi(manifestInput, options = {}) {
|
|
420
|
+
let manifest;
|
|
421
|
+
try {
|
|
422
|
+
manifest = resolveManifest(manifestInput);
|
|
423
|
+
} catch (err) {
|
|
424
|
+
return {
|
|
425
|
+
approved: false,
|
|
426
|
+
error: `Falha ao carregar manifesto: ${err.message}`,
|
|
427
|
+
vulnerabilities: [],
|
|
428
|
+
licenseIssues: []
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const gateway = options.gateway || getKnowledgeGateway(options);
|
|
433
|
+
const prodDeps = manifest.dependencies || {};
|
|
434
|
+
const devDeps = manifest.devDependencies || {};
|
|
435
|
+
const allDeps = { ...prodDeps, ...devDeps };
|
|
436
|
+
|
|
437
|
+
const vulnerabilities = [];
|
|
438
|
+
const licenseIssues = [];
|
|
439
|
+
const wildcardIssues = [];
|
|
440
|
+
|
|
441
|
+
// Checagem de licença do projeto
|
|
442
|
+
const projectLicense = manifest.license || 'UNLICENSED';
|
|
443
|
+
if (RESTRICTIVE_LICENSES.some(lic => String(projectLicense).toUpperCase().includes(lic))) {
|
|
444
|
+
licenseIssues.push({
|
|
445
|
+
package: manifest.name || 'root-project',
|
|
446
|
+
license: projectLicense,
|
|
447
|
+
severity: 'ALTA',
|
|
448
|
+
message: `Licença restritiva detectada: ${projectLicense}. Pode exigir abertura compulsória do código-fonte.`
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Consulta de vulnerabilidades OSV para cada dependência
|
|
453
|
+
const ecosystem = options.ecosystem || 'npm';
|
|
454
|
+
for (const [pkgName, versionSpec] of Object.entries(allDeps)) {
|
|
455
|
+
const rawVersion = String(versionSpec).replace(/^[\^~>=<]+/, '').trim();
|
|
456
|
+
if (versionSpec === '*' || versionSpec === 'latest') {
|
|
457
|
+
wildcardIssues.push({
|
|
458
|
+
package: pkgName,
|
|
459
|
+
version: versionSpec,
|
|
460
|
+
severity: 'MÉDIA',
|
|
461
|
+
message: `Uso de versão não determinística ("${versionSpec}"). Fere a Regra 1 de reprodutibilidade.`
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
try {
|
|
466
|
+
const osvRes = await gateway.queryOsv(pkgName, rawVersion, ecosystem);
|
|
467
|
+
if (osvRes && osvRes.hasVulnerabilities) {
|
|
468
|
+
for (const v of osvRes.vulnerabilities) {
|
|
469
|
+
vulnerabilities.push({
|
|
470
|
+
package: pkgName,
|
|
471
|
+
installedVersion: versionSpec,
|
|
472
|
+
severity: v.severity || 'ALTA',
|
|
473
|
+
cve: v.cve || v.id,
|
|
474
|
+
description: v.summary || 'Vulnerabilidade reportada via OSV',
|
|
475
|
+
recommendation: v.recommendation || `Atualize ${pkgName}`
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
} catch (err) {
|
|
480
|
+
void err;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const criticalOrHighVulns = vulnerabilities.filter(v => v.severity === 'CRÍTICA' || v.severity === 'ALTA');
|
|
485
|
+
const approved = criticalOrHighVulns.length === 0 && licenseIssues.length === 0;
|
|
486
|
+
|
|
487
|
+
return {
|
|
488
|
+
engine: 'osv_gateway_augmentation',
|
|
489
|
+
source: 'osv_api',
|
|
490
|
+
approved,
|
|
491
|
+
totalDependencies: Object.keys(allDeps).length,
|
|
492
|
+
vulnerabilitiesCount: vulnerabilities.length,
|
|
493
|
+
criticalVulnerabilities: criticalOrHighVulns.length,
|
|
494
|
+
vulnerabilities,
|
|
495
|
+
licenseIssues,
|
|
496
|
+
wildcardIssues,
|
|
497
|
+
verdict: approved ? 'CONFORME' : 'VETADO'
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Formata os resultados da auditoria SCA em relatório executivo Markdown/texto.
|
|
503
|
+
* @param {object} auditResult
|
|
504
|
+
* @returns {string}
|
|
505
|
+
*/
|
|
506
|
+
function formatReport(auditResult) {
|
|
507
|
+
if (!auditResult || typeof auditResult !== 'object') {
|
|
508
|
+
return 'Relatório SCA inválido ou vazio.';
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const vulns = Array.isArray(auditResult.vulnerabilities) ? auditResult.vulnerabilities : [];
|
|
512
|
+
const licenses = Array.isArray(auditResult.licenseIssues) ? auditResult.licenseIssues : [];
|
|
513
|
+
const wildcards = Array.isArray(auditResult.wildcardIssues) ? auditResult.wildcardIssues : [];
|
|
514
|
+
|
|
515
|
+
let output = '================================================================================\n';
|
|
516
|
+
output += `RELATÓRIO DE AUDITORIA DE DEPENDÊNCIAS & SCA [ ${auditResult.verdict || (auditResult.approved ? 'CONFORME' : 'VETADO')} ]\n`;
|
|
517
|
+
output += '================================================================================\n\n';
|
|
518
|
+
|
|
519
|
+
output += `Mecanismo de Análise: ${auditResult.engine || 'catálogo embutido'}\n`;
|
|
520
|
+
output += `Total de Dependências Auditadas: ${auditResult.totalDependencies || 0}\n`;
|
|
521
|
+
output += `Total de Vulnerabilidades Encontradas: ${auditResult.vulnerabilitiesCount || vulns.length}\n`;
|
|
522
|
+
output += `- Críticas / Altas: ${auditResult.criticalVulnerabilities || vulns.filter(v => v.severity === 'CRÍTICA' || v.severity === 'ALTA').length}\n`;
|
|
523
|
+
output += `- Problemas de Licença Restritiva: ${licenses.length}\n`;
|
|
524
|
+
output += `- Problemas de Versão Não-Determinística (Wildcard): ${wildcards.length}\n\n`;
|
|
525
|
+
|
|
526
|
+
if (auditResult.advisoryNotice) {
|
|
527
|
+
output += `Nota Consultiva: ${auditResult.advisoryNotice}\n\n`;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
output += 'VULNERABILIDADES IDENTIFICADAS:\n';
|
|
531
|
+
if (vulns.length === 0) {
|
|
532
|
+
output += 'Nenhuma vulnerabilidade crítica ou conhecida identificada.\n';
|
|
533
|
+
} else {
|
|
534
|
+
vulns.forEach((v, idx) => {
|
|
535
|
+
output += `${idx + 1}. [${v.severity || 'ALTA'}] ${v.package} (versão instalada: ${v.installedVersion || 'N/A'})\n`;
|
|
536
|
+
output += ` CVE/Alerta: ${v.cve || 'N/A'}\n`;
|
|
537
|
+
output += ` Descrição: ${v.description || 'Vulnerabilidade em dependência'}\n`;
|
|
538
|
+
output += ` Recomendação: ${v.recommendation || 'Atualize a dependência'}\n\n`;
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
output += 'CONFORMIDADE DE LICENÇAS (IP & COPYLEFT):\n';
|
|
543
|
+
if (licenses.length === 0) {
|
|
544
|
+
output += 'Nenhuma licença restritiva ou copyleft tóxica detectada.\n';
|
|
545
|
+
} else {
|
|
546
|
+
licenses.forEach((lic, idx) => {
|
|
547
|
+
output += `${idx + 1}. [${lic.severity || 'ALTA'}] Pacote: ${lic.package} · Licença: ${lic.license}\n`;
|
|
548
|
+
output += ` Impacto: ${lic.message}\n\n`;
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
output += '================================================================================\n';
|
|
553
|
+
return output;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Executa a auditoria SCA e exporta o resultado em SARIF v2.1.0 oficial.
|
|
558
|
+
* @param {string|object} manifestInput
|
|
559
|
+
* @param {object} [options={}]
|
|
560
|
+
* @returns {object} SARIF v2.1.0
|
|
561
|
+
*/
|
|
562
|
+
function auditToSarif(manifestInput, options = {}) {
|
|
563
|
+
const auditRes = auditDependenciesSCA(manifestInput, options);
|
|
564
|
+
const findings = [];
|
|
565
|
+
|
|
566
|
+
for (const v of auditRes.vulnerabilities || []) {
|
|
567
|
+
findings.push({
|
|
568
|
+
ruleId: v.cve || 'SCA-VULN',
|
|
569
|
+
name: `Vulnerabilidade em Dependência: ${v.package}`,
|
|
570
|
+
severity: v.severity,
|
|
571
|
+
package: v.package,
|
|
572
|
+
installedVersion: v.installedVersion,
|
|
573
|
+
filename: typeof manifestInput === 'string' && fs.existsSync(manifestInput) ? manifestInput : 'package.json',
|
|
574
|
+
line: 1,
|
|
575
|
+
column: 1,
|
|
576
|
+
snippet: `"${v.package}": "${v.installedVersion}"`,
|
|
577
|
+
message: `${v.package} (${v.installedVersion}): ${v.description || v.cve}`,
|
|
578
|
+
remediation: v.recommendation
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
for (const lic of auditRes.licenseIssues || []) {
|
|
583
|
+
findings.push({
|
|
584
|
+
ruleId: 'SCA-RESTRICTIVE-LICENSE',
|
|
585
|
+
name: `Licença Restritiva Copyleft: ${lic.license}`,
|
|
586
|
+
severity: lic.severity,
|
|
587
|
+
package: lic.package,
|
|
588
|
+
filename: typeof manifestInput === 'string' && fs.existsSync(manifestInput) ? manifestInput : 'package.json',
|
|
589
|
+
line: 1,
|
|
590
|
+
column: 1,
|
|
591
|
+
snippet: `"license": "${lic.license}"`,
|
|
592
|
+
message: lic.message,
|
|
593
|
+
remediation: 'Substitua a dependência ou solicite aprovação jurídica.'
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
return createSarifReport({
|
|
598
|
+
toolName: auditRes.engine === 'trivy_cli_industrial' ? 'trivy-sca' : 'agent-master-sca-auditor',
|
|
599
|
+
toolVersion: '5.1.0',
|
|
600
|
+
findings,
|
|
601
|
+
properties: {
|
|
602
|
+
engine: auditRes.engine,
|
|
603
|
+
approved: auditRes.approved,
|
|
604
|
+
verdict: auditRes.verdict,
|
|
605
|
+
totalDependencies: auditRes.totalDependencies
|
|
606
|
+
}
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Classe canônica do Auditor de Dependências e Composição de Software (SCA).
|
|
612
|
+
*/
|
|
613
|
+
class ScaDependencyAuditor {
|
|
614
|
+
constructor(options = {}) {
|
|
615
|
+
this.options = Object.freeze({ ...options });
|
|
616
|
+
this.restrictiveLicenses = Array.isArray(options.restrictiveLicenses)
|
|
617
|
+
? options.restrictiveLicenses
|
|
618
|
+
: RESTRICTIVE_LICENSES;
|
|
619
|
+
this.knownVulnerabilities = Array.isArray(options.knownVulnerabilities)
|
|
620
|
+
? options.knownVulnerabilities
|
|
621
|
+
: KNOWN_VULNERABLE_PACKAGES;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
isNpmAuditAvailable() {
|
|
625
|
+
return isNpmAuditAvailable();
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
isTrivyAvailable() {
|
|
629
|
+
return isTrivyAvailable();
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
auditWithNpmAudit(targetDir) {
|
|
633
|
+
return auditWithNpmAudit(targetDir);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
auditWithTrivy(targetDir) {
|
|
637
|
+
return auditWithTrivy(targetDir);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
audit(manifestInput, options = {}) {
|
|
641
|
+
return auditDependenciesSCA(manifestInput, { ...this.options, ...options });
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
auditDependenciesSCA(manifestInput, options = {}) {
|
|
645
|
+
return auditDependenciesSCA(manifestInput, { ...this.options, ...options });
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
async auditWithOsvApi(manifestInput, options = {}) {
|
|
649
|
+
return auditWithOsvApi(manifestInput, { ...this.options, ...options });
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
auditToSarif(manifestInput, options = {}) {
|
|
653
|
+
return auditToSarif(manifestInput, { ...this.options, ...options });
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
formatReport(auditResult) {
|
|
657
|
+
return formatReport(auditResult);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
module.exports = {
|
|
662
|
+
ScaDependencyAuditor,
|
|
663
|
+
auditDependenciesSCA,
|
|
664
|
+
auditWithOsvApi,
|
|
665
|
+
auditWithNpmAudit,
|
|
666
|
+
auditWithTrivy,
|
|
667
|
+
auditToSarif,
|
|
668
|
+
formatReport,
|
|
669
|
+
isNpmAuditAvailable,
|
|
670
|
+
isTrivyAvailable,
|
|
671
|
+
RESTRICTIVE_LICENSES,
|
|
672
|
+
KNOWN_VULNERABLE_PACKAGES
|
|
673
|
+
};
|