agent-master-devsecops 3.9.0 → 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.
Files changed (82) hide show
  1. package/README.md +205 -371
  2. package/agents/AGENT_DEVSECOPS.md +32 -417
  3. package/agents/AGENT_INFRA.md +31 -0
  4. package/agents/AGENT_ORCHESTRATOR.md +39 -264
  5. package/agents/AGENT_QA.md +58 -186
  6. package/agents/AGENT_SCRUM.md +23 -190
  7. package/agents/AGENT_SRE.md +22 -173
  8. package/agents/AGENT_UIUX.md +26 -162
  9. package/docs/ARCHITECTURE.md +152 -148
  10. package/docs/CALCULATIONS.md +17 -17
  11. package/docs/CHANGELOG.md +112 -49
  12. package/docs/ENGINEERING_RULES.md +56 -252
  13. package/docs/IDEAS.md +17 -17
  14. package/docs/LOCAL_BACKLOG.md +20 -0
  15. package/docs/STATE.md +111 -43
  16. package/docs/playbooks/PLAYBOOK_CIRCUIT_BREAKER_LLM.md +58 -58
  17. package/docs/playbooks/PLAYBOOK_OPENTELEMETRY_TRACING.md +58 -58
  18. package/package.json +59 -47
  19. package/scripts/auditors/a11y_auditor.js +462 -0
  20. package/scripts/auditors/code_quality_auditor.js +444 -0
  21. package/scripts/auditors/contract_test_validator.js +396 -0
  22. package/scripts/auditors/eval_harness.js +213 -0
  23. package/scripts/auditors/git_hygiene_linter.js +391 -0
  24. package/scripts/auditors/pr_reviewer.js +627 -0
  25. package/scripts/auditors/sast_scanner.js +442 -0
  26. package/scripts/auditors/sca_dependency_auditor.js +673 -0
  27. package/scripts/{bootstrap_cli.js → bin/bootstrap_cli.js} +597 -468
  28. package/scripts/bin/domain_idea_miner.js +658 -0
  29. package/scripts/bin/smart_onboarding.js +905 -0
  30. package/scripts/{web_inspector.js → bin/web_inspector.js} +578 -355
  31. package/scripts/core/architecture_advisor.js +378 -0
  32. package/scripts/core/ast_skeletonizer.js +413 -0
  33. package/scripts/core/autonomous_remediation_engine.js +305 -0
  34. package/scripts/{circuit_breaker.js → core/circuit_breaker.js} +546 -330
  35. package/scripts/core/context_compressor.js +237 -0
  36. package/scripts/core/fast_feature_scaffold.js +840 -0
  37. package/scripts/core/hot_state_cache.js +238 -0
  38. package/scripts/core/knowledge_gateway.js +888 -0
  39. package/scripts/{magic_prompt_generator.js → core/magic_prompt_generator.js} +250 -205
  40. package/scripts/{multi_agent_orchestrator.js → core/multi_agent_orchestrator.js} +421 -258
  41. package/scripts/{otel_tracer.js → core/otel_tracer.js} +335 -228
  42. package/scripts/{perf_benchmark.js → core/perf_benchmark.js} +212 -153
  43. package/scripts/core/sarif_engine.js +299 -0
  44. package/scripts/{sre_remediation_engine.js → core/sre_remediation_engine.js} +236 -174
  45. package/scripts/{universal_packager.js → core/universal_packager.js} +357 -313
  46. package/scripts/integrations/atlassian_client.js +600 -0
  47. package/scripts/integrations/atlassian_sync_action.js +576 -0
  48. package/scripts/integrations/confluence_sync.js +684 -0
  49. package/scripts/integrations/credentials_guard.js +500 -0
  50. package/scripts/integrations/jira_manager.js +722 -0
  51. package/scripts/mcp/handlers/atlassian_handlers.js +300 -0
  52. package/scripts/mcp/handlers/core_handlers.js +419 -0
  53. package/scripts/mcp/handlers/qa_handlers.js +502 -0
  54. package/scripts/mcp/handlers/rules_handlers.js +379 -0
  55. package/scripts/mcp/handlers/scaffold_handlers.js +334 -0
  56. package/scripts/mcp/handlers/sre_handlers.js +443 -0
  57. package/scripts/mcp/resources_registry.js +453 -0
  58. package/scripts/mcp/server.js +408 -0
  59. package/scripts/mcp/tool_handlers.js +444 -0
  60. package/scripts/mcp/tools_registry.js +1123 -0
  61. package/scripts/mcp_server.js +236 -1884
  62. package/.cursorignore +0 -23
  63. package/agents/compact/AGENT_ORCHESTRATOR_COMPACTO.md +0 -53
  64. package/scripts/a11y_auditor.js +0 -187
  65. package/scripts/ast_skeletonizer.js +0 -220
  66. package/scripts/atlassian_client.js +0 -238
  67. package/scripts/atlassian_sync_action.js +0 -269
  68. package/scripts/code_quality_auditor.js +0 -142
  69. package/scripts/confluence_sync.js +0 -337
  70. package/scripts/context_compressor.js +0 -139
  71. package/scripts/contract_test_validator.js +0 -166
  72. package/scripts/credentials_guard.js +0 -216
  73. package/scripts/domain_idea_miner.js +0 -364
  74. package/scripts/eval_harness.js +0 -100
  75. package/scripts/fast_feature_scaffold.js +0 -338
  76. package/scripts/git_hygiene_linter.js +0 -186
  77. package/scripts/hot_state_cache.js +0 -95
  78. package/scripts/jira_manager.js +0 -408
  79. package/scripts/pr_reviewer.js +0 -373
  80. package/scripts/sast_scanner.js +0 -180
  81. package/scripts/sca_dependency_auditor.js +0 -167
  82. package/scripts/smart_onboarding.js +0 -610
@@ -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
+ };