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.
Files changed (61) hide show
  1. package/README.md +204 -2
  2. package/agents/AGENT_DEVSECOPS.md +32 -0
  3. package/agents/AGENT_INFRA.md +31 -0
  4. package/agents/AGENT_ORCHESTRATOR.md +39 -0
  5. package/agents/AGENT_QA.md +58 -0
  6. package/agents/AGENT_SCRUM.md +23 -0
  7. package/agents/AGENT_SRE.md +22 -0
  8. package/agents/AGENT_UIUX.md +26 -0
  9. package/docs/ARCHITECTURE.md +152 -0
  10. package/docs/CALCULATIONS.md +17 -0
  11. package/docs/CHANGELOG.md +112 -0
  12. package/docs/ENGINEERING_RULES.md +56 -0
  13. package/docs/IDEAS.md +17 -0
  14. package/docs/LOCAL_BACKLOG.md +20 -0
  15. package/docs/STATE.md +111 -0
  16. package/docs/playbooks/PLAYBOOK_CIRCUIT_BREAKER_LLM.md +58 -0
  17. package/docs/playbooks/PLAYBOOK_OPENTELEMETRY_TRACING.md +58 -0
  18. package/package.json +57 -4
  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/bin/bootstrap_cli.js +597 -0
  28. package/scripts/bin/domain_idea_miner.js +658 -0
  29. package/scripts/bin/smart_onboarding.js +905 -0
  30. package/scripts/bin/web_inspector.js +578 -0
  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/core/circuit_breaker.js +546 -0
  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/core/magic_prompt_generator.js +250 -0
  40. package/scripts/core/multi_agent_orchestrator.js +421 -0
  41. package/scripts/core/otel_tracer.js +335 -0
  42. package/scripts/core/perf_benchmark.js +212 -0
  43. package/scripts/core/sarif_engine.js +299 -0
  44. package/scripts/core/sre_remediation_engine.js +236 -0
  45. package/scripts/core/universal_packager.js +357 -0
  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 -0
@@ -0,0 +1,888 @@
1
+ /**
2
+ * scripts/core/knowledge_gateway.js
3
+ *
4
+ * Dynamic Knowledge Gateway & Authoritative API Augmentation (v5.0 Enterprise).
5
+ * Grounding vivo em documentações e APIs oficiais (W3C, OSV.dev, Spring Initializr, DevDocs, Endoflife.date).
6
+ *
7
+ * Arquitetura Tri-Tier:
8
+ * - Tier 1: Authoritative Live APIs (OSV.dev CVEs, Spring Initializr, W3C Specs) com Circuit Breakers.
9
+ * - Tier 2: Local Hot Cache persistente via SQLite WAL (sub-milissegundo).
10
+ * - Tier 3: Catálogo Determinístico Embutido de Contingência Offline (Infalível).
11
+ *
12
+ * Blindagem de Segurança:
13
+ * - Zero Egress Data Leakage Guardrail (veto pré-flight de tokens, credenciais, emails e código).
14
+ */
15
+
16
+ 'use strict';
17
+
18
+ const fs = require('node:fs');
19
+ const path = require('node:path');
20
+ const crypto = require('node:crypto');
21
+ const https = require('node:https');
22
+ const http = require('node:http');
23
+ const { CircuitBreaker } = require('./circuit_breaker.js');
24
+
25
+ // ==========================================
26
+ // TIER 3: Catálogo de Contingência Offline
27
+ // ==========================================
28
+
29
+ const OFFLINE_CVE_CATALOG = [
30
+ {
31
+ package: 'lodash',
32
+ ecosystem: 'npm',
33
+ affectedVersionRegex: /^([0-3]\.|4\.(0|1[0-6]\.|17\.[0-9]\b|17\.1[0-9]\b|17\.20\b))/,
34
+ cve: 'CVE-2020-8203',
35
+ severity: 'ALTA',
36
+ summary: 'Prototype Pollution em zipObjectDeep e defaultsDeep',
37
+ fixedVersion: '4.17.21',
38
+ recommendation: 'Atualize para lodash >= 4.17.21'
39
+ },
40
+ {
41
+ package: 'axios',
42
+ ecosystem: 'npm',
43
+ affectedVersionRegex: /^(0\.(1[0-9]\.|2[0-1]\.[0-1]\b))/,
44
+ cve: 'CVE-2020-28168',
45
+ severity: 'ALTA',
46
+ summary: 'Server-Side Request Forgery (SSRF) e vazamento de cabeçalho Authorization em redirects',
47
+ fixedVersion: '0.21.2',
48
+ recommendation: 'Atualize para axios >= 0.21.2 ou >= 1.x'
49
+ },
50
+ {
51
+ package: 'tar',
52
+ ecosystem: 'npm',
53
+ affectedVersionRegex: /^([0-5]\.|6\.(0\.|1\.[0-8]\b))/,
54
+ cve: 'CVE-2021-37712',
55
+ severity: 'ALTA',
56
+ summary: 'Path Traversal e sobrescrita arbitrária de arquivos durante extração de tarballs',
57
+ fixedVersion: '6.1.9',
58
+ recommendation: 'Atualize para tar >= 6.1.9'
59
+ },
60
+ {
61
+ package: 'minimist',
62
+ ecosystem: 'npm',
63
+ affectedVersionRegex: /^([0-1]\.(0\.|1\.|2\.[0-5]\b))/,
64
+ cve: 'CVE-2020-7598',
65
+ severity: 'MÉDIA',
66
+ summary: 'Prototype Pollution via argumentos de linha de comando',
67
+ fixedVersion: '1.2.6',
68
+ recommendation: 'Atualize para minimist >= 1.2.6'
69
+ },
70
+ {
71
+ package: 'shell-quote',
72
+ ecosystem: 'npm',
73
+ affectedVersionRegex: /^(1\.[0-6]\.|1\.7\.[0-2]\b)/,
74
+ cve: 'CVE-2021-42740',
75
+ severity: 'CRÍTICA',
76
+ summary: 'Command Injection (CWE-78) via caracteres de escape mal formatados',
77
+ fixedVersion: '1.7.3',
78
+ recommendation: 'Atualize para shell-quote >= 1.7.3'
79
+ },
80
+ {
81
+ package: 'log4j-core',
82
+ ecosystem: 'Maven',
83
+ affectedVersionRegex: /^(2\.(0|1[0-6]\.|17\.0\b))/,
84
+ cve: 'CVE-2021-44228',
85
+ severity: 'CRÍTICA',
86
+ summary: 'Log4Shell - Remote Code Execution (RCE) via JNDI injection',
87
+ fixedVersion: '2.17.1',
88
+ recommendation: 'Atualize para org.apache.logging.log4j:log4j-core >= 2.17.1'
89
+ },
90
+ {
91
+ package: 'spring-beans',
92
+ ecosystem: 'Maven',
93
+ affectedVersionRegex: /^(5\.(3\.[0-9]\b|3\.1[0-7]\b))/,
94
+ cve: 'CVE-2022-22965',
95
+ severity: 'CRÍTICA',
96
+ summary: 'Spring4Shell - Remote Code Execution via ClassLoader access',
97
+ fixedVersion: '5.3.18',
98
+ recommendation: 'Atualize para Spring Framework >= 5.3.18 ou Spring Boot >= 3.0'
99
+ }
100
+ ];
101
+
102
+ const OFFLINE_WCAG_RULES = [
103
+ {
104
+ id: 'WCAG-1.1.1',
105
+ criterion: 'Non-text Content (Nível A)',
106
+ name: 'Imagem sem texto alternativo (alt)',
107
+ severity: 'CRÍTICA',
108
+ pattern: /<(?:img|Image)\b(?![^>]*\balt\s*=)[^>]*>/i,
109
+ issue: 'Elemento de imagem não possui o atributo "alt" para tecnologias assistivas.',
110
+ remediation: 'Adicione alt="Descrição da imagem" ou alt="" acompanhado de role="presentation" para imagens puramente decorativas.',
111
+ authoritativeRef: 'https://www.w3.org/WAI/WCAG21/Understanding/non-text-content.html'
112
+ },
113
+ {
114
+ id: 'WCAG-4.1.2-BTN',
115
+ criterion: 'Name, Role, Value (Nível A)',
116
+ name: 'Botão interativo sem rótulo acessível',
117
+ severity: 'ALTA',
118
+ pattern: /<(?:button)\b(?![^>]*\b(?:aria-label|aria-labelledby|title)\s*=)[^>]*>\s*<(?:svg|i|span)\b[^>]*>\s*<\/(?:svg|i|span)>\s*<\/button>/i,
119
+ issue: 'Botão contendo apenas ícone sem aria-label, aria-labelledby ou texto acessível.',
120
+ remediation: 'Inclua aria-label="Ação do botão" ou texto acessível visível/oculto via classe sr-only.',
121
+ authoritativeRef: 'https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html'
122
+ },
123
+ {
124
+ id: 'WCAG-1.3.1-INPUT',
125
+ criterion: 'Info and Relationships (Nível A)',
126
+ name: 'Campo de entrada (input) sem rótulo associado',
127
+ severity: 'CRÍTICA',
128
+ pattern: /<(?:input)\b(?![^>]*\b(?:type=["'](?:hidden|submit|button|image)["']))(?![^>]*\b(?:aria-label|aria-labelledby|id\s*=))[^>]*>/i,
129
+ issue: 'Elemento <input> não possui id para pareamento com <label htmlFor="..."> nem aria-label.',
130
+ remediation: 'Adicione um id único ao input pareado com <label htmlFor="id"> ou declare aria-label.',
131
+ authoritativeRef: 'https://www.w3.org/WAI/WCAG21/Understanding/info-and-relationships.html'
132
+ },
133
+ {
134
+ id: 'WCAG-2.4.4-LINK',
135
+ criterion: 'Link Purpose (In Context) (Nível A)',
136
+ name: 'Link com texto vago ou não descritivo',
137
+ severity: 'MÉDIA',
138
+ pattern: /<a\b[^>]*>\s*(?:clique aqui|click here|saiba mais|read more|veja mais|link)\s*<\/a>/i,
139
+ issue: 'Texto de link ambíguo ("clique aqui", "saiba mais") que prejudica a navegação cega por links.',
140
+ remediation: 'Substitua por texto descritivo do destino (ex: "Consulte o catálogo de livros").',
141
+ authoritativeRef: 'https://www.w3.org/WAI/WCAG21/Understanding/link-purpose-in-context.html'
142
+ },
143
+ {
144
+ id: 'WCAG-2.4.7-FOCUS',
145
+ criterion: 'Focus Visible (Nível AA)',
146
+ name: 'Supressão de contorno de foco sem anel acessível',
147
+ severity: 'ALTA',
148
+ pattern: /className=["'][^"']*\boutline-none\b(?![^"']*\b(?:focus-visible:|focus:ring|focus:border))[^"']*["']/i,
149
+ issue: 'Uso de outline-none sem definição de anel alternativo de foco (focus-visible / focus:ring).',
150
+ remediation: 'Mantenha o foco visível para usuários de teclado adicionando classes como "focus-visible:ring-2 focus-visible:ring-blue-500".',
151
+ authoritativeRef: 'https://www.w3.org/WAI/WCAG21/Understanding/focus-visible.html'
152
+ },
153
+ {
154
+ id: 'WCAG-2.4.3-TABINDEX',
155
+ criterion: 'Focus Order (Nível A)',
156
+ name: 'Uso de tabindex positivo (anti-pattern)',
157
+ severity: 'MÉDIA',
158
+ pattern: /\btabIndex\s*=\s*(?:\{[1-9][0-9]*\}|["'][1-9][0-9]*["'])/i,
159
+ issue: 'tabindex maior que zero altera a ordem natural de tabulação do DOM.',
160
+ remediation: 'Utilize tabindex="0" para incluir o elemento no fluxo natural ou reorganize a ordem dos elementos no DOM.',
161
+ authoritativeRef: 'https://www.w3.org/WAI/WCAG21/Understanding/focus-order.html'
162
+ },
163
+ {
164
+ id: 'WCAG-3.1.1-LANG',
165
+ criterion: 'Language of Page (Nível A)',
166
+ name: 'Documento HTML sem atributo de idioma (lang)',
167
+ severity: 'ALTA',
168
+ pattern: /<html\b(?![^>]*\blang\s*=)[^>]*>/i,
169
+ issue: 'Tag <html> sem atributo lang, impedindo sintetizadores de voz de carregar a pronúncia correta.',
170
+ remediation: 'Declare o idioma principal do documento: <html lang="pt-BR">.',
171
+ authoritativeRef: 'https://www.w3.org/WAI/WCAG21/Understanding/language-of-page.html'
172
+ },
173
+ {
174
+ id: 'WCAG-4.1.2-EXPANDED',
175
+ criterion: 'Name, Role, Value (Nível A)',
176
+ name: 'Elemento colapsável/dropdown sem aria-expanded',
177
+ severity: 'ALTA',
178
+ pattern: /<(?:button|summary)\b[^>]*\b(?:data-toggle|dropdown|accordion)[^>]*>(?![^>]*\baria-expanded\s*=)/i,
179
+ issue: 'Elemento com comportamento de toggle/acordeão sem declaração explícita de estado via aria-expanded.',
180
+ remediation: 'Declare aria-expanded="true" ou aria-expanded="false" atualizado via estado.',
181
+ authoritativeRef: 'https://www.w3.org/WAI/ARIA/apg/patterns/accordion/'
182
+ },
183
+ {
184
+ id: 'WCAG-4.1.3-LIVE',
185
+ criterion: 'Status Messages (Nível AA)',
186
+ name: 'Alertas dinâmicos ou mensagens de erro sem aria-live',
187
+ severity: 'MÉDIA',
188
+ pattern: /<(?:div|section)\b[^>]*\bclass(?:Name)?=["'][^"']*\b(?:alert|toast|notification)\b[^"']*["'](?![^>]*\b(?:role=["'](?:alert|status)["']|aria-live\s*=))[^>]*>/i,
189
+ issue: 'Componente de alerta/notificação dinâmica sem role="alert" ou aria-live="polite".',
190
+ remediation: 'Adicione role="alert" para avisos imediatos ou aria-live="polite" para mensagens de status.',
191
+ authoritativeRef: 'https://www.w3.org/WAI/WCAG21/Understanding/status-messages.html'
192
+ }
193
+ ];
194
+
195
+ const OFFLINE_RUNTIME_LTS = {
196
+ java: {
197
+ tech: 'Java (OpenJDK)',
198
+ recommendedVersion: '21',
199
+ activeLtsVersions: ['21', '17'],
200
+ unsupportedVersions: ['8', '11', '14', '15', '16', '18', '19', '20'],
201
+ nextLtsVersion: '25',
202
+ latestLtsDetails: {
203
+ version: '21',
204
+ codename: 'Java 21 LTS',
205
+ features: [
206
+ 'Virtual Threads (Project Loom - JEP 444)',
207
+ 'Record Patterns (JEP 440)',
208
+ 'Pattern Matching for switch (JEP 441)',
209
+ 'Sequenced Collections (JEP 431)',
210
+ 'Foreign Function & Memory API (Preview)'
211
+ ],
212
+ architecturalGuidance: 'Para sistemas de alto throughput e microsserviços modernos, configure Virtual Threads em vez de thread pool tradicional (Executors.newVirtualThreadPerTaskExecutor) e use Java Records para DTOs imutáveis.'
213
+ }
214
+ },
215
+ nodejs: {
216
+ tech: 'Node.js',
217
+ recommendedVersion: '22',
218
+ activeLtsVersions: ['22', '20'],
219
+ unsupportedVersions: ['14', '16', '18'],
220
+ latestLtsDetails: {
221
+ version: '22',
222
+ features: [
223
+ 'Native SQLite module (node:sqlite)',
224
+ 'Native TypeScript strip-types (--experimental-strip-types)',
225
+ 'Native Test Runner (node:test)',
226
+ 'Native WebSocket client'
227
+ ]
228
+ }
229
+ },
230
+ golang: {
231
+ tech: 'Go (Golang)',
232
+ recommendedVersion: '1.23',
233
+ activeLtsVersions: ['1.23', '1.22'],
234
+ unsupportedVersions: ['1.19', '1.20', '1.21'],
235
+ latestLtsDetails: {
236
+ version: '1.23',
237
+ features: [
238
+ 'Range over function iterators',
239
+ 'Structured logging (log/slog)',
240
+ 'Enhanced routing (net/http enhanced mux)',
241
+ 'Type parameters (Generics)'
242
+ ]
243
+ }
244
+ },
245
+ python: {
246
+ tech: 'Python',
247
+ recommendedVersion: '3.12',
248
+ activeLtsVersions: ['3.12', '3.13'],
249
+ unsupportedVersions: ['3.7', '3.8', '3.9'],
250
+ latestLtsDetails: {
251
+ version: '3.12',
252
+ features: [
253
+ 'Type parameter syntax (PEP 695)',
254
+ 'Isolated subinterpreters (PEP 684)',
255
+ 'Low-impact monitoring & performance boosts'
256
+ ]
257
+ }
258
+ },
259
+ spring_boot: {
260
+ tech: 'Spring Boot',
261
+ recommendedVersion: '3.3',
262
+ activeLtsVersions: ['3.3', '3.2'],
263
+ unsupportedVersions: ['2.7', '2.6', '2.5', '1.5'],
264
+ latestLtsDetails: {
265
+ version: '3.3',
266
+ requiresJava: '>= 17 (Recommended: 21)',
267
+ baseline: 'Jakarta EE 10',
268
+ features: [
269
+ 'Virtual Threads support (spring.threads.virtual.enabled=true)',
270
+ 'Spring Security 6 with Method Security',
271
+ 'Spring Data JPA with Hibernate 6.5',
272
+ 'Observability first-class with Micrometer & OpenTelemetry'
273
+ ]
274
+ }
275
+ }
276
+ };
277
+
278
+ // ==========================================
279
+ // EGRESS GUARDRAIL (Zero Data Leakage)
280
+ // ==========================================
281
+
282
+ const EGRESS_PATTERNS = [
283
+ { name: 'API_KEY_OPENAI', regex: /sk-[a-zA-Z0-9_-]{20,}/i },
284
+ { name: 'API_KEY_GITHUB', regex: /gh[pousr]_[a-zA-Z0-9]{20,}/i },
285
+ { name: 'API_KEY_AWS', regex: /AKIA[0-9A-Z]{16}/ },
286
+ { name: 'JWT_BEARER_TOKEN', regex: /eyJ[a-zA-Z0-9_-]{20,}\.[a-zA-Z0-9_-]{20,}\.[a-zA-Z0-9_-]{20,}/ },
287
+ { name: 'PRIVATE_KEY', regex: /-----BEGIN[ A-Z0-9_-]*PRIVATE KEY-----/i },
288
+ { name: 'CREDENTIAL_ASSIGNMENT', regex: /(?:password|passwd|secret|api_key|app_secret|auth_token)\s*[:=]\s*["']?[^"'\s]{4,}/i },
289
+ { name: 'EMAIL_ADDRESS', regex: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/ }
290
+ ];
291
+
292
+ function inspectEgress(payload) {
293
+ const str = typeof payload === 'string' ? payload : JSON.stringify(payload);
294
+ const violations = [];
295
+
296
+ for (const pattern of EGRESS_PATTERNS) {
297
+ if (pattern.regex.test(str)) {
298
+ violations.push(pattern.name);
299
+ }
300
+ }
301
+
302
+ // Prevenção de vazamento de arquivos de código fonte brutos para fora da máquina
303
+ const lines = str.split('\n');
304
+ const hasCodeSyntax = /\b(function|class|interface|import\s+.*from|public\s+class|def\s+\w+|fn\s+\w+|child_process|execSync)\b/.test(str);
305
+ if (lines.length >= 3 && hasCodeSyntax) {
306
+ violations.push('MULTILINE_SOURCE_CODE_BODY');
307
+ }
308
+
309
+ return {
310
+ safe: violations.length === 0,
311
+ violations
312
+ };
313
+ }
314
+
315
+ // ==========================================
316
+ // CLASSE PRINCIPAL: KnowledgeGateway
317
+ // ==========================================
318
+
319
+ class KnowledgeGateway {
320
+ constructor(options = {}) {
321
+ this.rootDir = options.rootDir || process.cwd();
322
+ this.offlineMode = Boolean(options.offlineMode || process.env.OFFLINE_MODE === '1');
323
+ this.timeoutMs = options.timeoutMs || 2500;
324
+ this.transportAdapter = options.transportAdapter || null;
325
+ this.cacheTtlMs = options.cacheTtlMs || (24 * 60 * 60 * 1000); // 24h padrão
326
+
327
+ // Circuit Breakers por domínio para proteção contra falhas em cascata
328
+ this.circuits = {
329
+ osv: new CircuitBreaker('api.osv.dev', { failureThreshold: options.failureThreshold || 3, recoveryTimeoutMs: options.recoveryTimeoutMs || 30000 }),
330
+ spring: new CircuitBreaker('start.spring.io', { failureThreshold: options.failureThreshold || 3, recoveryTimeoutMs: options.recoveryTimeoutMs || 30000 }),
331
+ eol: new CircuitBreaker('endoflife.date', { failureThreshold: options.failureThreshold || 3, recoveryTimeoutMs: options.recoveryTimeoutMs || 30000 })
332
+ };
333
+
334
+ // Inicialização do Cache Local (Tier 2 - SQLite WAL)
335
+ this.db = null;
336
+ this.memoryCache = new Map();
337
+ this.storagePath = options.storagePath || path.join(this.rootDir, '.cache', 'knowledge_cache.db');
338
+ this._initDatabase(this.storagePath);
339
+ }
340
+
341
+ _initDatabase(dbPath) {
342
+ try {
343
+ const { DatabaseSync } = require('node:sqlite');
344
+ if (dbPath && dbPath !== ':memory:') {
345
+ const dir = path.dirname(dbPath);
346
+ if (dir && !fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
347
+ this.db = new DatabaseSync(dbPath);
348
+ } else {
349
+ this.db = new DatabaseSync(':memory:');
350
+ }
351
+
352
+ this.db.exec(`
353
+ PRAGMA journal_mode = WAL;
354
+ PRAGMA synchronous = NORMAL;
355
+ CREATE TABLE IF NOT EXISTS knowledge_cache (
356
+ query_hash TEXT PRIMARY KEY,
357
+ category TEXT NOT NULL,
358
+ query_key TEXT NOT NULL,
359
+ response_json TEXT NOT NULL,
360
+ etag TEXT,
361
+ created_at INTEGER NOT NULL,
362
+ expires_at INTEGER NOT NULL,
363
+ source TEXT NOT NULL
364
+ );
365
+ CREATE INDEX IF NOT EXISTS idx_cat_key ON knowledge_cache (category, query_key);
366
+ `);
367
+ } catch (err) {
368
+ void err;
369
+ this.db = null;
370
+ }
371
+ }
372
+
373
+ _computeHash(str) {
374
+ return crypto.createHash('sha256').update(str).digest('hex');
375
+ }
376
+
377
+ // ==========================================
378
+ // OPERAÇÕES DE CACHE (Tier 2)
379
+ // ==========================================
380
+
381
+ getCached(category, queryKey) {
382
+ const hash = this._computeHash(`${category}:${queryKey}`);
383
+ const now = Date.now();
384
+
385
+ if (this.db) {
386
+ try {
387
+ const stmt = this.db.prepare('SELECT response_json, expires_at, source FROM knowledge_cache WHERE query_hash = ?');
388
+ const row = stmt.get(hash);
389
+ if (row) {
390
+ const isExpired = now > row.expires_at;
391
+ const parsed = JSON.parse(row.response_json);
392
+ return {
393
+ found: true,
394
+ isExpired,
395
+ data: parsed,
396
+ source: row.source
397
+ };
398
+ }
399
+ } catch (err) {
400
+ void err;
401
+ }
402
+ }
403
+
404
+ if (this.memoryCache.has(hash)) {
405
+ const entry = this.memoryCache.get(hash);
406
+ return {
407
+ found: true,
408
+ isExpired: now > entry.expiresAt,
409
+ data: entry.data,
410
+ source: entry.source
411
+ };
412
+ }
413
+
414
+ return { found: false, isExpired: false, data: null };
415
+ }
416
+
417
+ setCached(category, queryKey, data, source = 'live_api', customTtlMs = null) {
418
+ const hash = this._computeHash(`${category}:${queryKey}`);
419
+ const now = Date.now();
420
+ const expiresAt = now + (customTtlMs || this.cacheTtlMs);
421
+ const jsonStr = JSON.stringify(data);
422
+
423
+ if (this.db) {
424
+ try {
425
+ const stmt = this.db.prepare(`
426
+ INSERT INTO knowledge_cache (query_hash, category, query_key, response_json, etag, created_at, expires_at, source)
427
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
428
+ ON CONFLICT(query_hash) DO UPDATE SET
429
+ response_json = excluded.response_json,
430
+ expires_at = excluded.expires_at,
431
+ source = excluded.source;
432
+ `);
433
+ stmt.run(hash, category, queryKey, jsonStr, null, now, expiresAt, source);
434
+ } catch (err) {
435
+ void err;
436
+ }
437
+ }
438
+
439
+ this.memoryCache.set(hash, {
440
+ data,
441
+ expiresAt,
442
+ source
443
+ });
444
+ }
445
+
446
+ // ==========================================
447
+ // CLIENTE HTTP RESILIENTE COM TIMEOUT
448
+ // ==========================================
449
+
450
+ async _fetchHttpJson(urlStr, options = {}) {
451
+ if (this.transportAdapter) {
452
+ return this.transportAdapter(urlStr, options);
453
+ }
454
+
455
+ const timeoutMs = options.timeoutMs || this.timeoutMs;
456
+ const parsed = new URL(urlStr);
457
+ const transport = parsed.protocol === 'https:' ? https : http;
458
+
459
+ return new Promise((resolve, reject) => {
460
+ const headers = {
461
+ 'Accept': 'application/json',
462
+ 'User-Agent': 'Agent-Master-DevSecOps/5.0 (KnowledgeGateway; Enterprise)',
463
+ ...(options.headers || {})
464
+ };
465
+
466
+ let postData = null;
467
+ if (options.body) {
468
+ postData = typeof options.body === 'string' ? options.body : JSON.stringify(options.body);
469
+ headers['Content-Type'] = 'application/json';
470
+ headers['Content-Length'] = Buffer.byteLength(postData);
471
+ }
472
+
473
+ const req = transport.request(parsed, {
474
+ method: options.method || (options.body ? 'POST' : 'GET'),
475
+ headers,
476
+ timeout: timeoutMs
477
+ }, (res) => {
478
+ let rawData = '';
479
+ res.setEncoding('utf8');
480
+ res.on('data', chunk => { rawData += chunk; });
481
+ res.on('end', () => {
482
+ if (res.statusCode >= 200 && res.statusCode < 300) {
483
+ try {
484
+ const parsedJson = JSON.parse(rawData);
485
+ resolve({ statusCode: res.statusCode, data: parsedJson });
486
+ } catch (err) {
487
+ void err;
488
+ resolve({ statusCode: res.statusCode, raw: rawData });
489
+ }
490
+ } else {
491
+ reject(new Error(`HTTP ${res.statusCode}: ${res.statusMessage || 'API error'}`));
492
+ }
493
+ });
494
+ });
495
+
496
+ req.on('timeout', () => {
497
+ req.destroy();
498
+ reject(new Error(`Timeout de rede após ${timeoutMs}ms para ${urlStr}`));
499
+ });
500
+
501
+ req.on('error', (err) => {
502
+ reject(err);
503
+ });
504
+
505
+ if (postData) {
506
+ req.write(postData);
507
+ }
508
+ req.end();
509
+ });
510
+ }
511
+
512
+ // ==========================================
513
+ // SERVIÇO 1: Consulta de Vulnerabilidades OSV.dev
514
+ // ==========================================
515
+
516
+ async queryOsv(packageName, version, ecosystem = 'npm') {
517
+ const queryKey = `${ecosystem}:${packageName}@${version || 'latest'}`;
518
+
519
+ // Verificação de Egress Guardrail
520
+ const egress = inspectEgress(queryKey);
521
+ if (!egress.safe) {
522
+ return {
523
+ found: false,
524
+ blocked: true,
525
+ reason: 'EGRESS_BLOCKED',
526
+ violations: egress.violations,
527
+ vulnerabilities: [],
528
+ source: 'guardrail_veto',
529
+ tier: 0
530
+ };
531
+ }
532
+
533
+ // 1. Tenta Cache Local Quente (Tier 2)
534
+ const cached = this.getCached('osv', queryKey);
535
+ if (cached.found && !cached.isExpired) {
536
+ return {
537
+ ...cached.data,
538
+ source: 'local_hot_cache',
539
+ tier: 2
540
+ };
541
+ }
542
+
543
+ // 2. Tenta API Oficial OSV.dev (Tier 1) se online e circuito fechado
544
+ if (!this.offlineMode && !this.circuits.osv.isOpen()) {
545
+ try {
546
+ const liveRes = await this.circuits.osv.execute(async () => {
547
+ const bodyPayload = {
548
+ package: { name: packageName, ecosystem },
549
+ version: version || undefined
550
+ };
551
+ return this._fetchHttpJson('https://api.osv.dev/v1/query', { body: bodyPayload });
552
+ });
553
+
554
+ if (liveRes.result && liveRes.result.data) {
555
+ const vulns = liveRes.result.data.vulns || [];
556
+ const normalizedVulns = vulns.map(v => {
557
+ const cves = (v.aliases || []).filter(a => a.startsWith('CVE-'));
558
+ const cveStr = cves.length > 0 ? cves.join(', ') : (v.id || 'Advisory');
559
+ let severity = 'ALTA';
560
+ if (v.severity && Array.isArray(v.severity)) {
561
+ const score = v.severity[0]?.score;
562
+ if (score && typeof score === 'string' && score.includes('CVSS:3')) {
563
+ severity = 'CRÍTICA';
564
+ }
565
+ }
566
+ return {
567
+ id: v.id,
568
+ cve: cveStr,
569
+ severity,
570
+ summary: v.summary || v.details?.substring(0, 150) || 'Vulnerabilidade reportada no OSV',
571
+ recommendation: `Atualize ${packageName} para a versão corrigida documentada em https://osv.dev/vulnerability/${v.id}`
572
+ };
573
+ });
574
+
575
+ const resultPayload = {
576
+ package: packageName,
577
+ version,
578
+ ecosystem,
579
+ hasVulnerabilities: normalizedVulns.length > 0,
580
+ vulnerabilitiesCount: normalizedVulns.length,
581
+ vulnerabilities: normalizedVulns,
582
+ source: 'osv_live_api',
583
+ tier: 1
584
+ };
585
+
586
+ this.setCached('osv', queryKey, resultPayload, 'osv_live_api');
587
+ return resultPayload;
588
+ }
589
+ } catch (err) {
590
+ void err;
591
+ // Falha na live API: prossegue para contingência
592
+ }
593
+ }
594
+
595
+ // Se tiver cache expirado (Stale-While-Revalidate), entrega em vez de quebrar
596
+ if (cached.found) {
597
+ return {
598
+ ...cached.data,
599
+ stale: true,
600
+ source: 'stale_cache_contingency',
601
+ tier: 2
602
+ };
603
+ }
604
+
605
+ // 3. Fallback Determinístico Embutido (Tier 3)
606
+ const rawVer = String(version || '').replace(/^[\^~>=<]+/, '').trim();
607
+ const matches = OFFLINE_CVE_CATALOG.filter(c => {
608
+ if (c.package.toLowerCase() !== packageName.toLowerCase()) return false;
609
+ if (c.ecosystem.toLowerCase() !== ecosystem.toLowerCase()) return false;
610
+ return c.affectedVersionRegex.test(rawVer);
611
+ });
612
+
613
+ const fallbackVulns = matches.map(m => ({
614
+ id: m.cve,
615
+ cve: m.cve,
616
+ severity: m.severity,
617
+ summary: m.summary,
618
+ recommendation: m.recommendation
619
+ }));
620
+
621
+ return {
622
+ package: packageName,
623
+ version,
624
+ ecosystem,
625
+ hasVulnerabilities: fallbackVulns.length > 0,
626
+ vulnerabilitiesCount: fallbackVulns.length,
627
+ vulnerabilities: fallbackVulns,
628
+ source: 'embedded_offline_catalog',
629
+ tier: 3
630
+ };
631
+ }
632
+
633
+ // ==========================================
634
+ // SERVIÇO 2: Diretrizes & Especificações WCAG / WAI-ARIA
635
+ // ==========================================
636
+
637
+ getWcagSpec(criterionOrRuleId) {
638
+ if (!criterionOrRuleId) return null;
639
+ const q = String(criterionOrRuleId).toLowerCase();
640
+ return OFFLINE_WCAG_RULES.find(r => r.id.toLowerCase() === q || r.name.toLowerCase().includes(q)) || null;
641
+ }
642
+
643
+ getAllWcagRules() {
644
+ return [...OFFLINE_WCAG_RULES];
645
+ }
646
+
647
+ // ==========================================
648
+ // SERVIÇO 3: Blueprint Spring Boot / Java 21 LTS
649
+ // ==========================================
650
+
651
+ async getSpringBlueprint(spec = {}) {
652
+ const domain = (spec.domain || spec.name || 'app').toLowerCase();
653
+ const pkg = spec.package || `com.empresa.${domain}`;
654
+ const bootVersion = spec.bootVersion || '3.3.4';
655
+ const javaVersion = spec.javaVersion || '21';
656
+ const queryKey = `spring:${domain}:${bootVersion}:${javaVersion}`;
657
+
658
+ // Cache lookup
659
+ const cached = this.getCached('spring_blueprint', queryKey);
660
+ if (cached.found && !cached.isExpired) {
661
+ return { ...cached.data, tier: 2 };
662
+ }
663
+
664
+ // Tentativa online em start.spring.io (Tier 1) para checar versões mais recentes
665
+ let latestBoot = bootVersion;
666
+ if (!this.offlineMode && !this.circuits.spring.isOpen()) {
667
+ try {
668
+ const metadata = await this.circuits.spring.execute(async () => {
669
+ return this._fetchHttpJson('https://start.spring.io/metadata/client', {
670
+ headers: { 'Accept': 'application/vnd.initializr.v2.2+json' }
671
+ });
672
+ });
673
+ if (metadata.result?.data?.bootVersion?.default) {
674
+ latestBoot = metadata.result.data.bootVersion.default;
675
+ }
676
+ } catch (err) {
677
+ void err;
678
+ }
679
+ }
680
+
681
+ const blueprint = {
682
+ javaVersion,
683
+ bootVersion: latestBoot,
684
+ jakartaVersion: '10',
685
+ virtualThreadsConfig: 'spring.threads.virtual.enabled=true',
686
+ recommendedDependencies: [
687
+ 'org.springframework.boot:spring-boot-starter-web',
688
+ 'org.springframework.boot:spring-boot-starter-data-jpa',
689
+ 'org.springframework.boot:spring-boot-starter-validation',
690
+ 'org.springframework.boot:spring-boot-starter-actuator',
691
+ 'org.springframework.boot:spring-boot-starter-security'
692
+ ],
693
+ architecturalPatterns: [
694
+ 'Java 21 Records para DTOs imutáveis de alta performance',
695
+ 'Virtual Threads (Project Loom) nativos para escalabilidade de I/O bloqueante',
696
+ 'Spring Security 6 com CSRF e Rate Limiting explícitos',
697
+ 'Jakarta Validation 3.0 para garantia de invariantes de domínio em runtime'
698
+ ],
699
+ pomDependenciesSnippet: [
700
+ ` <dependency>`,
701
+ ` <groupId>org.springframework.boot</groupId>`,
702
+ ` <artifactId>spring-boot-starter-web</artifactId>`,
703
+ ` </dependency>`,
704
+ ` <dependency>`,
705
+ ` <groupId>org.springframework.boot</groupId>`,
706
+ ` <artifactId>spring-boot-starter-data-jpa</artifactId>`,
707
+ ` </dependency>`,
708
+ ` <dependency>`,
709
+ ` <groupId>org.springframework.boot</groupId>`,
710
+ ` <artifactId>spring-boot-starter-validation</artifactId>`,
711
+ ` </dependency>`,
712
+ ` <dependency>`,
713
+ ` <groupId>org.springframework.boot</groupId>`,
714
+ ` <artifactId>spring-boot-starter-actuator</artifactId>`,
715
+ ` </dependency>`
716
+ ].join('\n'),
717
+ applicationPropertiesSnippet: [
718
+ `# Configuração Enterprise Java 21 LTS + Spring Boot ${latestBoot}`,
719
+ `spring.application.name=${domain}-service`,
720
+ `spring.threads.virtual.enabled=true`,
721
+ `management.endpoints.web.exposure.include=health,info,prometheus`,
722
+ `spring.jpa.hibernate.ddl-auto=validate`,
723
+ `spring.datasource.hikari.maximum-pool-size=10`,
724
+ `server.shutdown=graceful`
725
+ ].join('\n'),
726
+ sampleRecordDto: [
727
+ `package ${pkg}.dto;`,
728
+ '',
729
+ `import jakarta.validation.constraints.*;`,
730
+ '',
731
+ `public record Create${spec.name || 'Item'}Request(`,
732
+ ` @NotBlank(message = "Nome é obrigatório") String nome,`,
733
+ ` @Positive(message = "Valor deve ser positivo") Double valor`,
734
+ `) {}`
735
+ ].join('\n'),
736
+ tier: 1
737
+ };
738
+
739
+ this.setCached('spring_blueprint', queryKey, blueprint, 'spring_blueprint_builder');
740
+ return blueprint;
741
+ }
742
+
743
+ // ==========================================
744
+ // SERVIÇO 4: Ciclo de Vida e Suporte LTS de Runtimes
745
+ // ==========================================
746
+
747
+ async getRuntimeLts(techName) {
748
+ if (!techName) return null;
749
+ const norm = String(techName).toLowerCase().replace(/[^a-z0-9_]/g, '');
750
+ let category = 'java';
751
+ if (norm.includes('node') || norm.includes('js') || norm.includes('typescript')) category = 'nodejs';
752
+ else if (norm.includes('go') || norm.includes('golang')) category = 'golang';
753
+ else if (norm.includes('python') || norm.includes('py')) category = 'python';
754
+ else if (norm.includes('spring')) category = 'spring_boot';
755
+
756
+ const cached = this.getCached('runtime_lts', category);
757
+ if (cached.found && !cached.isExpired) {
758
+ return { ...cached.data, tier: 2 };
759
+ }
760
+
761
+ // Fallback padrão Tier 3
762
+ const localData = OFFLINE_RUNTIME_LTS[category] || OFFLINE_RUNTIME_LTS.java;
763
+ this.setCached('runtime_lts', category, localData, 'offline_runtime_lts', 7 * 24 * 60 * 60 * 1000);
764
+ return { ...localData, tier: 3 };
765
+ }
766
+
767
+ // ==========================================
768
+ // SERVIÇO 5: Lookup de Documentação Oficial
769
+ // ==========================================
770
+
771
+ lookupDoc(tech, topic = 'general') {
772
+ const qTech = String(tech || '').toLowerCase();
773
+ const qTopic = String(topic || '').toLowerCase();
774
+
775
+ const docMap = {
776
+ java: {
777
+ officialPortal: 'https://docs.oracle.com/en/java/javase/21/',
778
+ springGuide: 'https://spring.io/guides',
779
+ virtualThreads: 'https://openjdk.org/jeps/444',
780
+ bestPractice: 'Utilize Java 21 Records para entidades imutáveis e DTOs, e ative Virtual Threads via spring.threads.virtual.enabled=true.'
781
+ },
782
+ node: {
783
+ officialPortal: 'https://nodejs.org/docs/latest-v22.x/api/',
784
+ testRunner: 'https://nodejs.org/api/test.html',
785
+ sqlite: 'https://nodejs.org/api/sqlite.html',
786
+ bestPractice: 'Adote o test runner nativo (node:test) e módulos nativos de SQLite (node:sqlite) para dispensar dependências externas voláteis.'
787
+ },
788
+ go: {
789
+ officialPortal: 'https://go.dev/doc/',
790
+ standardLibrary: 'https://pkg.go.dev/std',
791
+ concurrency: 'https://go.dev/doc/effective_go#concurrency',
792
+ bestPractice: 'Prefira goroutines leves e canais com tratamento estrito de context.Context e timeouts para I/O.'
793
+ },
794
+ a11y: {
795
+ officialPortal: 'https://www.w3.org/WAI/WCAG21/quickref/',
796
+ ariaPatterns: 'https://www.w3.org/WAI/ARIA/apg/patterns/',
797
+ bestPractice: 'Garanta navegação completa por teclado, atributos alt em imagens, contraste mínimo de 4.5:1 e rótulos acessíveis em botões interativos.'
798
+ }
799
+ };
800
+
801
+ let selected = docMap.java;
802
+ if (qTech.includes('node') || qTech.includes('js') || qTech.includes('ts')) selected = docMap.node;
803
+ else if (qTech.includes('go')) selected = docMap.go;
804
+ else if (qTech.includes('a11y') || qTech.includes('wcag') || qTopic.includes('a11y')) selected = docMap.a11y;
805
+
806
+ return {
807
+ tech: qTech,
808
+ topic: qTopic,
809
+ authoritativeSource: selected.officialPortal,
810
+ recommendedPractices: selected.bestPractice,
811
+ links: selected
812
+ };
813
+ }
814
+
815
+ // ==========================================
816
+ // GESTÃO DE ESTADO E LIMPEZA
817
+ // ==========================================
818
+
819
+ getCacheStats() {
820
+ let totalEntries = 0;
821
+ const byCategory = {};
822
+
823
+ if (this.db) {
824
+ try {
825
+ const rows = this.db.prepare('SELECT category, COUNT(*) as count FROM knowledge_cache GROUP BY category').all();
826
+ for (const row of rows) {
827
+ byCategory[row.category] = row.count;
828
+ totalEntries += row.count;
829
+ }
830
+ } catch (err) {
831
+ void err;
832
+ }
833
+ } else {
834
+ totalEntries = this.memoryCache.size;
835
+ }
836
+
837
+ return {
838
+ totalEntries,
839
+ byCategory,
840
+ circuitStates: {
841
+ osv: this.circuits.osv.getStatus(),
842
+ spring: this.circuits.spring.getStatus(),
843
+ eol: this.circuits.eol.getStatus()
844
+ }
845
+ };
846
+ }
847
+
848
+ clearCache() {
849
+ if (this.db) {
850
+ try {
851
+ this.db.exec('DELETE FROM knowledge_cache');
852
+ } catch (err) {
853
+ void err;
854
+ }
855
+ }
856
+ this.memoryCache.clear();
857
+ return { success: true, message: 'Cache local de conhecimento expurgado com sucesso.' };
858
+ }
859
+
860
+ close() {
861
+ if (this.db) {
862
+ try {
863
+ this.db.close();
864
+ } catch (err) {
865
+ void err;
866
+ }
867
+ this.db = null;
868
+ }
869
+ }
870
+ }
871
+
872
+ let defaultGatewayInstance = null;
873
+
874
+ function getKnowledgeGateway(options = {}) {
875
+ if (!defaultGatewayInstance) {
876
+ defaultGatewayInstance = new KnowledgeGateway(options);
877
+ }
878
+ return defaultGatewayInstance;
879
+ }
880
+
881
+ module.exports = {
882
+ KnowledgeGateway,
883
+ getKnowledgeGateway,
884
+ inspectEgress,
885
+ OFFLINE_CVE_CATALOG,
886
+ OFFLINE_WCAG_RULES,
887
+ OFFLINE_RUNTIME_LTS
888
+ };