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.
- package/README.md +205 -371
- package/agents/AGENT_DEVSECOPS.md +32 -417
- package/agents/AGENT_INFRA.md +31 -0
- package/agents/AGENT_ORCHESTRATOR.md +39 -264
- package/agents/AGENT_QA.md +58 -186
- package/agents/AGENT_SCRUM.md +23 -190
- package/agents/AGENT_SRE.md +22 -173
- package/agents/AGENT_UIUX.md +26 -162
- package/docs/ARCHITECTURE.md +152 -148
- package/docs/CALCULATIONS.md +17 -17
- package/docs/CHANGELOG.md +112 -49
- package/docs/ENGINEERING_RULES.md +56 -252
- package/docs/IDEAS.md +17 -17
- package/docs/LOCAL_BACKLOG.md +20 -0
- package/docs/STATE.md +111 -43
- package/docs/playbooks/PLAYBOOK_CIRCUIT_BREAKER_LLM.md +58 -58
- package/docs/playbooks/PLAYBOOK_OPENTELEMETRY_TRACING.md +58 -58
- package/package.json +59 -47
- 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/{bootstrap_cli.js → bin/bootstrap_cli.js} +597 -468
- package/scripts/bin/domain_idea_miner.js +658 -0
- package/scripts/bin/smart_onboarding.js +905 -0
- package/scripts/{web_inspector.js → bin/web_inspector.js} +578 -355
- 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/{circuit_breaker.js → core/circuit_breaker.js} +546 -330
- 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/{magic_prompt_generator.js → core/magic_prompt_generator.js} +250 -205
- package/scripts/{multi_agent_orchestrator.js → core/multi_agent_orchestrator.js} +421 -258
- package/scripts/{otel_tracer.js → core/otel_tracer.js} +335 -228
- package/scripts/{perf_benchmark.js → core/perf_benchmark.js} +212 -153
- package/scripts/core/sarif_engine.js +299 -0
- package/scripts/{sre_remediation_engine.js → core/sre_remediation_engine.js} +236 -174
- package/scripts/{universal_packager.js → core/universal_packager.js} +357 -313
- 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 -1884
- package/.cursorignore +0 -23
- package/agents/compact/AGENT_ORCHESTRATOR_COMPACTO.md +0 -53
- package/scripts/a11y_auditor.js +0 -187
- package/scripts/ast_skeletonizer.js +0 -220
- package/scripts/atlassian_client.js +0 -238
- package/scripts/atlassian_sync_action.js +0 -269
- package/scripts/code_quality_auditor.js +0 -142
- package/scripts/confluence_sync.js +0 -337
- package/scripts/context_compressor.js +0 -139
- package/scripts/contract_test_validator.js +0 -166
- package/scripts/credentials_guard.js +0 -216
- package/scripts/domain_idea_miner.js +0 -364
- package/scripts/eval_harness.js +0 -100
- package/scripts/fast_feature_scaffold.js +0 -338
- package/scripts/git_hygiene_linter.js +0 -186
- package/scripts/hot_state_cache.js +0 -95
- package/scripts/jira_manager.js +0 -408
- package/scripts/pr_reviewer.js +0 -373
- package/scripts/sast_scanner.js +0 -180
- package/scripts/sca_dependency_auditor.js +0 -167
- package/scripts/smart_onboarding.js +0 -610
|
@@ -1,153 +1,212 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* scripts/perf_benchmark.js
|
|
4
|
-
*
|
|
5
|
-
* Scanner de Regressão de Performance em CI (AGDEV-25 / I-9).
|
|
6
|
-
* Executa micro-benchmarks comparativos de latência e consumo de RAM/Heap,
|
|
7
|
-
* detectando e bloqueando regressões superiores a 10% em relação ao baseline de referência.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const fs = require('fs');
|
|
11
|
-
const path = require('path');
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
processMessage({ jsonrpc: '2.0', id: i, method: 'tools/
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
report
|
|
76
|
-
report +=
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
report +=
|
|
82
|
-
report += `-
|
|
83
|
-
report += `-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
getOrInitBaseline
|
|
152
|
-
|
|
153
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* scripts/perf_benchmark.js
|
|
4
|
+
*
|
|
5
|
+
* Scanner de Regressão de Performance em CI (AGDEV-25 / I-9).
|
|
6
|
+
* Executa micro-benchmarks comparativos de latência e consumo de RAM/Heap,
|
|
7
|
+
* detectando e bloqueando regressões superiores a 10% em relação ao baseline de referência.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const { createSarifReport } = require('./sarif_engine.js');
|
|
13
|
+
|
|
14
|
+
const ROOT_DIR = path.resolve(__dirname, '../..');
|
|
15
|
+
const BASELINE_FILE = path.join(ROOT_DIR, 'benchmarks', 'baseline.json');
|
|
16
|
+
|
|
17
|
+
function runBenchmark(iterations = 100) {
|
|
18
|
+
const { processMessage } = require('../mcp_server.js');
|
|
19
|
+
const { generateCompactPrompt } = require('./context_compressor.js');
|
|
20
|
+
|
|
21
|
+
const startMem = process.memoryUsage();
|
|
22
|
+
const startTime = process.hrtime.bigint();
|
|
23
|
+
|
|
24
|
+
// Benchmark 1: Processamento de JSON-RPC Initialize e Tools List
|
|
25
|
+
for (let i = 0; i < iterations; i++) {
|
|
26
|
+
processMessage({ jsonrpc: '2.0', id: i, method: 'tools/list' });
|
|
27
|
+
processMessage({ jsonrpc: '2.0', id: i, method: 'tools/call', params: { name: 'agent_full_dev_status', arguments: {} } });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Benchmark 2: Compactação Semântica de Prompt
|
|
31
|
+
for (let i = 0; i < Math.floor(iterations / 2); i++) {
|
|
32
|
+
generateCompactPrompt();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const endTime = process.hrtime.bigint();
|
|
36
|
+
const endMem = process.memoryUsage();
|
|
37
|
+
|
|
38
|
+
const durationNs = Number(endTime - startTime);
|
|
39
|
+
const durationMs = durationNs / 1000000;
|
|
40
|
+
const avgLatencyUs = Math.round((durationNs / (iterations * 2)) / 1000);
|
|
41
|
+
const heapUsedMb = Math.round((endMem.heapUsed / (1024 * 1024)) * 100) / 100;
|
|
42
|
+
const opsPerSec = Math.round(((iterations * 2.5) / (durationMs / 1000)));
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
iterations,
|
|
46
|
+
totalDurationMs: Math.round(durationMs * 100) / 100,
|
|
47
|
+
avgLatencyUs,
|
|
48
|
+
opsPerSec,
|
|
49
|
+
heapUsedMb
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function comparePerformance(current, baseline, maxDegradationPct = 10) {
|
|
54
|
+
const latencyDeltaPct = Math.round(((current.avgLatencyUs - baseline.avgLatencyUs) / baseline.avgLatencyUs) * 100);
|
|
55
|
+
const memoryDeltaPct = Math.round(((current.heapUsedMb - baseline.heapUsedMb) / (baseline.heapUsedMb || 1)) * 100);
|
|
56
|
+
|
|
57
|
+
const hasLatencyRegression = latencyDeltaPct > maxDegradationPct;
|
|
58
|
+
const hasMemoryRegression = memoryDeltaPct > (maxDegradationPct * 2); // 20% tolerância para GC jitter
|
|
59
|
+
const passed = !hasLatencyRegression && !hasMemoryRegression;
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
passed,
|
|
63
|
+
verdict: passed ? 'APROVADO' : 'REGRESSÃO_DETECTADA',
|
|
64
|
+
thresholdPct: maxDegradationPct,
|
|
65
|
+
current,
|
|
66
|
+
baseline,
|
|
67
|
+
deltas: {
|
|
68
|
+
latencyDeltaPct,
|
|
69
|
+
memoryDeltaPct
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function formatPerfReport(comparison) {
|
|
75
|
+
let report = '================================================================================\n';
|
|
76
|
+
report += `RELATÓRIO DE PERFORMANCE & REGRESSÃO DE CI: [ ${comparison.verdict} ]\n`;
|
|
77
|
+
report += '================================================================================\n\n';
|
|
78
|
+
|
|
79
|
+
report += `Limiar Máximo de Degradação Permitido: ${comparison.thresholdPct}%\n\n`;
|
|
80
|
+
|
|
81
|
+
report += 'MÉTRICAS COMPARATIVAS:\n';
|
|
82
|
+
report += `- Latência Média por Operação: ${comparison.current.avgLatencyUs}µs (Baseline: ${comparison.baseline.avgLatencyUs}µs) -> Variação: ${comparison.deltas.latencyDeltaPct > 0 ? '+' : ''}${comparison.deltas.latencyDeltaPct}%\n`;
|
|
83
|
+
report += `- Throughput (Ops/sec): ${comparison.current.opsPerSec} ops/s (Baseline: ${comparison.baseline.opsPerSec} ops/s)\n`;
|
|
84
|
+
report += `- Memória Heap Utilizada: ${comparison.current.heapUsedMb} MB (Baseline: ${comparison.baseline.heapUsedMb} MB) -> Variação: ${comparison.deltas.memoryDeltaPct > 0 ? '+' : ''}${comparison.deltas.memoryDeltaPct}%\n\n`;
|
|
85
|
+
|
|
86
|
+
if (comparison.passed) {
|
|
87
|
+
report += 'STATUS: Nenhuma degradação crítica detectada. Aprovado para merge na esteira de CI.\n';
|
|
88
|
+
} else {
|
|
89
|
+
report += 'ALERTA SRE: Regressão superior a 10% identificada! Merge bloqueado conforme política DevSecOps.\n';
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
report += '================================================================================\n';
|
|
93
|
+
return report;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function getOrInitBaseline() {
|
|
97
|
+
const benchDir = path.dirname(BASELINE_FILE);
|
|
98
|
+
if (!fs.existsSync(benchDir)) {
|
|
99
|
+
fs.mkdirSync(benchDir, { recursive: true });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (fs.existsSync(BASELINE_FILE)) {
|
|
103
|
+
try {
|
|
104
|
+
return JSON.parse(fs.readFileSync(BASELINE_FILE, 'utf8'));
|
|
105
|
+
} catch (err) {
|
|
106
|
+
void err;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Baseline padrão de referência calibrado para Node.js 22
|
|
111
|
+
const initialBaseline = {
|
|
112
|
+
iterations: 100,
|
|
113
|
+
totalDurationMs: 15.0,
|
|
114
|
+
avgLatencyUs: 60,
|
|
115
|
+
opsPerSec: 15000,
|
|
116
|
+
heapUsedMb: 18.5
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
fs.writeFileSync(BASELINE_FILE, JSON.stringify(initialBaseline, null, 2), 'utf8');
|
|
120
|
+
return initialBaseline;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function runCli() {
|
|
124
|
+
const args = process.argv.slice(2);
|
|
125
|
+
const baseline = getOrInitBaseline();
|
|
126
|
+
const current = runBenchmark(100);
|
|
127
|
+
|
|
128
|
+
if (args.includes('--save-baseline')) {
|
|
129
|
+
fs.writeFileSync(BASELINE_FILE, JSON.stringify(current, null, 2), 'utf8');
|
|
130
|
+
console.log(`[Perf Scanner] Novo baseline salvo com sucesso em ${BASELINE_FILE}`);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const comparison = comparePerformance(current, baseline, 15);
|
|
135
|
+
const report = formatPerfReport(comparison);
|
|
136
|
+
console.log(report);
|
|
137
|
+
|
|
138
|
+
if (args.includes('--strict') && !comparison.passed) {
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (require.main === module) {
|
|
144
|
+
runCli().catch(err => {
|
|
145
|
+
console.error(err);
|
|
146
|
+
process.exit(1);
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function auditToSarif(iterations = 50, thresholdPct = 15) {
|
|
151
|
+
const baseline = getOrInitBaseline();
|
|
152
|
+
const current = runBenchmark(iterations);
|
|
153
|
+
const comparison = comparePerformance(current, baseline, thresholdPct);
|
|
154
|
+
const findings = [];
|
|
155
|
+
|
|
156
|
+
if (!comparison.passed) {
|
|
157
|
+
findings.push({
|
|
158
|
+
ruleId: 'RULE-PERF-001',
|
|
159
|
+
name: 'PerformanceRegressionDetected',
|
|
160
|
+
severity: 'WARNING',
|
|
161
|
+
message: `Regressão de performance detectada: Latência +${comparison.deltas.latencyDeltaPct}% (Limiar: ${thresholdPct}%).`,
|
|
162
|
+
remediation: 'Investigue o overhead recente ou recalibre o baseline em benchmarks/baseline.json.'
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return createSarifReport({
|
|
167
|
+
toolName: 'agent-master-perf-benchmark',
|
|
168
|
+
rules: [
|
|
169
|
+
{
|
|
170
|
+
id: 'RULE-PERF-001',
|
|
171
|
+
name: 'PerformanceRegressionDetected',
|
|
172
|
+
description: 'Bloqueia merges com regressão de latência ou memória superior ao limiar permitido',
|
|
173
|
+
severity: 'MEDIUM',
|
|
174
|
+
cwe: 'CWE-400'
|
|
175
|
+
}
|
|
176
|
+
],
|
|
177
|
+
findings,
|
|
178
|
+
properties: {
|
|
179
|
+
passed: comparison.passed,
|
|
180
|
+
avgLatencyUs: current.avgLatencyUs,
|
|
181
|
+
baselineLatencyUs: baseline.avgLatencyUs,
|
|
182
|
+
opsPerSec: current.opsPerSec
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
class PerfBenchmark {
|
|
188
|
+
static runBenchmark(iterations) {
|
|
189
|
+
return runBenchmark(iterations);
|
|
190
|
+
}
|
|
191
|
+
static comparePerformance(current, baseline, threshold) {
|
|
192
|
+
return comparePerformance(current, baseline, threshold);
|
|
193
|
+
}
|
|
194
|
+
static formatReport(comparison) {
|
|
195
|
+
return formatPerfReport(comparison);
|
|
196
|
+
}
|
|
197
|
+
static auditToSarif(iterations, threshold) {
|
|
198
|
+
return auditToSarif(iterations, threshold);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
module.exports = {
|
|
203
|
+
PerfBenchmark,
|
|
204
|
+
runBenchmark,
|
|
205
|
+
comparePerformance,
|
|
206
|
+
formatPerfReport,
|
|
207
|
+
formatReport: formatPerfReport,
|
|
208
|
+
auditToSarif,
|
|
209
|
+
getOrInitBaseline,
|
|
210
|
+
BASELINE_FILE
|
|
211
|
+
};
|
|
212
|
+
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scripts/core/sarif_engine.js
|
|
3
|
+
* Motor Canônico de Ingestão, Geração e Validação de SARIF v2.1.0
|
|
4
|
+
* (Static Analysis Results Interchange Format - OASIS Standard)
|
|
5
|
+
*
|
|
6
|
+
* Permite interoperabilidade com GitHub Code Scanning, SonarQube,
|
|
7
|
+
* GitLab SAST, VS Code SARIF Viewer e ferramentas industriais (Semgrep, Trivy, CodeQL).
|
|
8
|
+
*/
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const SARIF_SCHEMA = 'https://json.schemastore.org/sarif-2.1.0.json';
|
|
12
|
+
const SARIF_VERSION = '2.1.0';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Mapeia níveis de severidade industriais para os níveis do padrão SARIF.
|
|
16
|
+
* @param {string} severity
|
|
17
|
+
* @returns {'error' | 'warning' | 'note' | 'none'}
|
|
18
|
+
*/
|
|
19
|
+
function mapSeverityToSarifLevel(severity) {
|
|
20
|
+
const s = String(severity || '').toUpperCase();
|
|
21
|
+
if (s === 'CRITICAL' || s === 'CRÍTICA' || s === 'HIGH' || s === 'ALTA' || s === 'ERROR') {
|
|
22
|
+
return 'error';
|
|
23
|
+
}
|
|
24
|
+
if (s === 'MEDIUM' || s === 'MÉDIA' || s === 'WARNING' || s === 'MODERATE') {
|
|
25
|
+
return 'warning';
|
|
26
|
+
}
|
|
27
|
+
if (s === 'LOW' || s === 'BAIXA' || s === 'NOTE' || s === 'INFO') {
|
|
28
|
+
return 'note';
|
|
29
|
+
}
|
|
30
|
+
return 'warning';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Mapeia o nível SARIF de volta para a taxonomia padrão DevSecOps.
|
|
35
|
+
* @param {string} level
|
|
36
|
+
* @returns {'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW' | 'INFO'}
|
|
37
|
+
*/
|
|
38
|
+
function mapSarifLevelToSeverity(level) {
|
|
39
|
+
const l = String(level || '').toLowerCase();
|
|
40
|
+
if (l === 'error') return 'HIGH';
|
|
41
|
+
if (l === 'warning') return 'MEDIUM';
|
|
42
|
+
if (l === 'note') return 'LOW';
|
|
43
|
+
return 'INFO';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Cria um relatório SARIF v2.1.0 oficial.
|
|
48
|
+
* @param {object} params
|
|
49
|
+
* @param {string} params.toolName
|
|
50
|
+
* @param {string} [params.toolVersion='5.1.0']
|
|
51
|
+
* @param {string} [params.informationUri]
|
|
52
|
+
* @param {Array<object>} [params.rules=[]]
|
|
53
|
+
* @param {Array<object>} params.findings
|
|
54
|
+
* @param {object} [params.properties={}]
|
|
55
|
+
* @returns {object} SARIF v2.1.0 JSON object
|
|
56
|
+
*/
|
|
57
|
+
function createSarifReport({
|
|
58
|
+
toolName = 'agent-master-devsecops',
|
|
59
|
+
toolVersion = '5.1.0',
|
|
60
|
+
informationUri = 'https://github.com/ContalApps/agent-master-devsecops',
|
|
61
|
+
rules = [],
|
|
62
|
+
findings = [],
|
|
63
|
+
properties = {}
|
|
64
|
+
}) {
|
|
65
|
+
const ruleMap = new Map();
|
|
66
|
+
|
|
67
|
+
// Registra regras pré-definidas
|
|
68
|
+
for (const r of rules) {
|
|
69
|
+
const id = r.id || r.ruleId;
|
|
70
|
+
if (id && !ruleMap.has(id)) {
|
|
71
|
+
ruleMap.set(id, {
|
|
72
|
+
id,
|
|
73
|
+
name: r.name || id,
|
|
74
|
+
shortDescription: { text: r.name || id },
|
|
75
|
+
fullDescription: { text: r.message || r.description || id },
|
|
76
|
+
defaultConfiguration: {
|
|
77
|
+
level: mapSeverityToSarifLevel(r.severity)
|
|
78
|
+
},
|
|
79
|
+
help: {
|
|
80
|
+
text: r.remediation || r.recommendation || 'Consulte a documentação da regra para correção.'
|
|
81
|
+
},
|
|
82
|
+
properties: {
|
|
83
|
+
cwe: r.cwe || 'CWE-Unknown',
|
|
84
|
+
precision: 'high'
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Mapeia findings para results do SARIF
|
|
91
|
+
const results = findings.map((f, idx) => {
|
|
92
|
+
const ruleId = f.ruleId || f.id || `RULE-${idx + 1}`;
|
|
93
|
+
const level = mapSeverityToSarifLevel(f.severity);
|
|
94
|
+
|
|
95
|
+
// Se a regra não existia na lista, registra dinamicamente
|
|
96
|
+
if (!ruleMap.has(ruleId)) {
|
|
97
|
+
ruleMap.set(ruleId, {
|
|
98
|
+
id: ruleId,
|
|
99
|
+
name: f.name || ruleId,
|
|
100
|
+
shortDescription: { text: f.name || ruleId },
|
|
101
|
+
defaultConfiguration: { level },
|
|
102
|
+
properties: {
|
|
103
|
+
cwe: f.cwe || (f.cve ? 'CVE-Dependency' : 'CWE-Unknown')
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const artifactUri = (f.filename || f.path || f.package || 'unknown').replace(/\\/g, '/');
|
|
109
|
+
const startLine = Number(f.line) > 0 ? Number(f.line) : 1;
|
|
110
|
+
const startColumn = Number(f.column) > 0 ? Number(f.column) : 1;
|
|
111
|
+
|
|
112
|
+
const resultObj = {
|
|
113
|
+
ruleId,
|
|
114
|
+
level,
|
|
115
|
+
message: {
|
|
116
|
+
text: f.message || f.description || `Violação detectada: ${ruleId}`
|
|
117
|
+
},
|
|
118
|
+
locations: [
|
|
119
|
+
{
|
|
120
|
+
physicalLocation: {
|
|
121
|
+
artifactLocation: {
|
|
122
|
+
uri: artifactUri,
|
|
123
|
+
uriBaseId: '%SRCROOT%'
|
|
124
|
+
},
|
|
125
|
+
region: {
|
|
126
|
+
startLine,
|
|
127
|
+
startColumn,
|
|
128
|
+
snippet: {
|
|
129
|
+
text: f.snippet || ''
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
],
|
|
135
|
+
properties: {
|
|
136
|
+
cwe: f.cwe || null,
|
|
137
|
+
cve: f.cve || null,
|
|
138
|
+
remediation: f.remediation || f.recommendation || null,
|
|
139
|
+
package: f.package || null,
|
|
140
|
+
installedVersion: f.installedVersion || null
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
return resultObj;
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
$schema: SARIF_SCHEMA,
|
|
149
|
+
version: SARIF_VERSION,
|
|
150
|
+
runs: [
|
|
151
|
+
{
|
|
152
|
+
tool: {
|
|
153
|
+
driver: {
|
|
154
|
+
name: toolName,
|
|
155
|
+
version: toolVersion,
|
|
156
|
+
informationUri,
|
|
157
|
+
rules: Array.from(ruleMap.values())
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
results,
|
|
161
|
+
properties: {
|
|
162
|
+
...properties,
|
|
163
|
+
generatedAt: new Date().toISOString()
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
]
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Valida a integridade estrutural mínima de um objeto SARIF v2.1.0.
|
|
172
|
+
* @param {object} sarifObj
|
|
173
|
+
* @returns {{ valid: boolean, errors: string[] }}
|
|
174
|
+
*/
|
|
175
|
+
function validateSarif(sarifObj) {
|
|
176
|
+
const errors = [];
|
|
177
|
+
if (!sarifObj || typeof sarifObj !== 'object') {
|
|
178
|
+
return { valid: false, errors: ['O payload SARIF deve ser um objeto JSON válido.'] };
|
|
179
|
+
}
|
|
180
|
+
if (sarifObj.version !== '2.1.0') {
|
|
181
|
+
errors.push(`Versão SARIF inválida: esperado '2.1.0', recebido '${sarifObj.version}'.`);
|
|
182
|
+
}
|
|
183
|
+
if (!Array.isArray(sarifObj.runs)) {
|
|
184
|
+
errors.push("Propriedade 'runs' obrigatória do SARIF ausente ou não é um array.");
|
|
185
|
+
} else {
|
|
186
|
+
sarifObj.runs.forEach((run, idx) => {
|
|
187
|
+
if (!run.tool || !run.tool.driver || !run.tool.driver.name) {
|
|
188
|
+
errors.push(`Run #${idx} não possui tool.driver.name definido.`);
|
|
189
|
+
}
|
|
190
|
+
if (!Array.isArray(run.results)) {
|
|
191
|
+
errors.push(`Run #${idx} não possui 'results' como array.`);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
valid: errors.length === 0,
|
|
198
|
+
errors
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Ingestão e normalização de qualquer relatório SARIF v2.1.0 (Semgrep, Trivy, CodeQL, etc).
|
|
204
|
+
* @param {string|object} sarifInput
|
|
205
|
+
* @returns {{ toolName: string, totalFindings: number, findings: Array<object> }}
|
|
206
|
+
*/
|
|
207
|
+
function parseSarifReport(sarifInput) {
|
|
208
|
+
let sarif;
|
|
209
|
+
if (typeof sarifInput === 'string') {
|
|
210
|
+
sarif = JSON.parse(sarifInput);
|
|
211
|
+
} else {
|
|
212
|
+
sarif = sarifInput;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const validation = validateSarif(sarif);
|
|
216
|
+
if (!validation.valid) {
|
|
217
|
+
throw new Error(`SARIF Inválido: ${validation.errors.join('; ')}`);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const allFindings = [];
|
|
221
|
+
let primaryToolName = 'unknown-tool';
|
|
222
|
+
|
|
223
|
+
for (const run of sarif.runs) {
|
|
224
|
+
const driver = run.tool?.driver || {};
|
|
225
|
+
primaryToolName = driver.name || primaryToolName;
|
|
226
|
+
const rulesList = driver.rules || [];
|
|
227
|
+
const ruleDict = new Map();
|
|
228
|
+
for (const r of rulesList) {
|
|
229
|
+
if (r.id) ruleDict.set(r.id, r);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const results = run.results || [];
|
|
233
|
+
for (const res of results) {
|
|
234
|
+
const rule = ruleDict.get(res.ruleId) || {};
|
|
235
|
+
const loc = res.locations?.[0]?.physicalLocation || {};
|
|
236
|
+
const uri = loc.artifactLocation?.uri || 'unknown';
|
|
237
|
+
const region = loc.region || {};
|
|
238
|
+
|
|
239
|
+
allFindings.push({
|
|
240
|
+
ruleId: res.ruleId,
|
|
241
|
+
name: rule.name || rule.shortDescription?.text || res.ruleId,
|
|
242
|
+
level: res.level || 'warning',
|
|
243
|
+
severity: mapSarifLevelToSeverity(res.level),
|
|
244
|
+
cwe: res.properties?.cwe || rule.properties?.cwe || 'CWE-Unknown',
|
|
245
|
+
cve: res.properties?.cve || null,
|
|
246
|
+
filename: uri,
|
|
247
|
+
line: region.startLine || 1,
|
|
248
|
+
column: region.startColumn || 1,
|
|
249
|
+
snippet: region.snippet?.text || '',
|
|
250
|
+
message: res.message?.text || '',
|
|
251
|
+
remediation: res.properties?.remediation || rule.help?.text || null,
|
|
252
|
+
rawResult: res
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
toolName: primaryToolName,
|
|
259
|
+
totalFindings: allFindings.length,
|
|
260
|
+
findings: allFindings
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Combina múltiplos relatórios SARIF em um documento unificado.
|
|
266
|
+
* @param {Array<object>} sarifReports
|
|
267
|
+
* @param {string} [unifiedToolName='agent-master-devsecops-unified']
|
|
268
|
+
* @returns {object} SARIF consolidado
|
|
269
|
+
*/
|
|
270
|
+
function mergeSarifReports(sarifReports, unifiedToolName = 'agent-master-devsecops-unified') {
|
|
271
|
+
const mergedRuns = [];
|
|
272
|
+
for (const report of sarifReports) {
|
|
273
|
+
if (report && Array.isArray(report.runs)) {
|
|
274
|
+
mergedRuns.push(...report.runs);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return {
|
|
279
|
+
$schema: SARIF_SCHEMA,
|
|
280
|
+
version: SARIF_VERSION,
|
|
281
|
+
runs: mergedRuns.length > 0 ? mergedRuns : [
|
|
282
|
+
{
|
|
283
|
+
tool: { driver: { name: unifiedToolName, version: '5.1.0' } },
|
|
284
|
+
results: []
|
|
285
|
+
}
|
|
286
|
+
]
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
module.exports = {
|
|
291
|
+
SARIF_SCHEMA,
|
|
292
|
+
SARIF_VERSION,
|
|
293
|
+
mapSeverityToSarifLevel,
|
|
294
|
+
mapSarifLevelToSeverity,
|
|
295
|
+
createSarifReport,
|
|
296
|
+
validateSarif,
|
|
297
|
+
parseSarifReport,
|
|
298
|
+
mergeSarifReports
|
|
299
|
+
};
|