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,600 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * scripts/integrations/atlassian_client.js
4
+ *
5
+ * Cliente Unificado e Utilitários de Comunicação com APIs REST Atlassian (Jira e Confluence).
6
+ * Centraliza autenticação, sanitização de domínio, introspecção de git e execução de requisições HTTPS.
7
+ *
8
+ * Governança: Tríade Git + Jira + Confluence (Regra 11 e Regra 15).
9
+ * Conformidade: OASIS SARIF v2.1.0, Programação Defensiva e Clean Code.
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ const fs = require('node:fs');
15
+ const path = require('node:path');
16
+ const https = require('node:https');
17
+ const { execFileSync } = require('node:child_process');
18
+ const { createSarifReport } = require('../core/sarif_engine.js');
19
+
20
+ /**
21
+ * Sanitiza o domínio Atlassian removendo protocolos, caminhos e barras residuais.
22
+ * @param {string} domainStr
23
+ * @returns {string|null}
24
+ */
25
+ function sanitizeDomain(domainStr) {
26
+ if (!domainStr || typeof domainStr !== 'string') return null;
27
+ const trimmed = domainStr.trim();
28
+ if (!trimmed) return null;
29
+
30
+ return trimmed
31
+ .replace(/^https?:\/\//i, '')
32
+ .split('/')[0]
33
+ .replace(/\/+$/, '')
34
+ .trim() || null;
35
+ }
36
+
37
+ /**
38
+ * Resolve as configurações locais de MCP em busca de credenciais Atlassian.
39
+ * @returns {object|null}
40
+ */
41
+ function resolveMcpConfig() {
42
+ const homeDir = process.env.USERPROFILE || process.env.HOME || '';
43
+ const candidatePaths = [
44
+ path.join(homeDir, '.gemini', 'config', 'mcp_config.json'),
45
+ path.join(homeDir, '.antigravity', 'config', 'mcp_config.json'),
46
+ path.resolve(process.cwd(), '.mcp_config.json')
47
+ ];
48
+
49
+ for (const p of candidatePaths) {
50
+ if (fs.existsSync(p)) {
51
+ try {
52
+ return JSON.parse(fs.readFileSync(p, 'utf8'));
53
+ } catch (err) {
54
+ void err;
55
+ }
56
+ }
57
+ }
58
+ return null;
59
+ }
60
+
61
+ /**
62
+ * Obtém credenciais Atlassian via variáveis de ambiente, arquivos .env ou mcp_config.json.
63
+ * @param {string} [targetDir=process.cwd()]
64
+ * @returns {object}
65
+ */
66
+ function getAtlassianCredentials(targetDir = process.cwd()) {
67
+ const safeTargetDir = (typeof targetDir === 'string' && targetDir.trim()) ? targetDir : process.cwd();
68
+
69
+ let token = process.env.ATLASSIAN_API_TOKEN || process.env.JIRA_API_TOKEN || process.env.CONFLUENCE_API_TOKEN || process.env.PAT;
70
+ let email = process.env.ATLASSIAN_EMAIL || process.env.JIRA_EMAIL || process.env.CONFLUENCE_EMAIL || process.env.CONFLUENCE_USERNAME;
71
+ let rawDomain = process.env.ATLASSIAN_DOMAIN || process.env.JIRA_DOMAIN || process.env.CONFLUENCE_DOMAIN || process.env.JIRA_BASE_URL || process.env.CONFLUENCE_BASE_URL;
72
+ let source = (token && email && rawDomain) ? 'environment_variables' : null;
73
+
74
+ if (!process.env.IGNORE_LOCAL_ENV && (!token || !email || !rawDomain)) {
75
+ const isLocalWorkspace = path.resolve(safeTargetDir) === path.resolve(process.cwd());
76
+ const candidateFiles = isLocalWorkspace
77
+ ? [
78
+ path.resolve(safeTargetDir, '.env'),
79
+ path.resolve(safeTargetDir, '..', '.env'),
80
+ path.resolve(process.cwd(), '.env'),
81
+ path.resolve(process.cwd(), '.env.local')
82
+ ]
83
+ : [
84
+ path.resolve(safeTargetDir, '.env'),
85
+ path.resolve(safeTargetDir, '.env.local')
86
+ ];
87
+
88
+ for (const envPath of candidateFiles) {
89
+ if (fs.existsSync(envPath)) {
90
+ try {
91
+ const raw = fs.readFileSync(envPath, 'utf8');
92
+ const dMatch = raw.match(/(?:ATLASSIAN_DOMAIN|JIRA_DOMAIN|CONFLUENCE_DOMAIN)=(.*)/);
93
+ const eMatch = raw.match(/(?:ATLASSIAN_EMAIL|JIRA_EMAIL|CONFLUENCE_EMAIL)=(.*)/);
94
+ const tMatch = raw.match(/(?:ATLASSIAN_API_TOKEN|JIRA_API_TOKEN|CONFLUENCE_API_TOKEN)=(.*)/);
95
+
96
+ if (dMatch && dMatch[1] && !rawDomain) rawDomain = dMatch[1].trim().replace(/^['"]|['"]$/g, '');
97
+ if (eMatch && eMatch[1] && !email) email = eMatch[1].trim().replace(/^['"]|['"]$/g, '');
98
+ if (tMatch && tMatch[1] && !token) token = tMatch[1].trim().replace(/^['"]|['"]$/g, '');
99
+
100
+ if (rawDomain && email && token) {
101
+ source = source || `local_file:${path.basename(envPath)}`;
102
+ break;
103
+ }
104
+ } catch (err) {
105
+ void err;
106
+ }
107
+ }
108
+ }
109
+ }
110
+
111
+ const isLocalTarget = path.resolve(safeTargetDir) === path.resolve(process.cwd());
112
+ if ((!token || !email || !rawDomain) && isLocalTarget) {
113
+ const mcp = resolveMcpConfig();
114
+ if (mcp && mcp.mcpServers) {
115
+ if (mcp.mcpServers.jira && mcp.mcpServers.jira.env) {
116
+ token = token || mcp.mcpServers.jira.env.JIRA_API_TOKEN;
117
+ email = email || mcp.mcpServers.jira.env.JIRA_EMAIL;
118
+ rawDomain = rawDomain || mcp.mcpServers.jira.env.JIRA_BASE_URL;
119
+ if (token && email && rawDomain) source = source || 'mcp_config:jira';
120
+ }
121
+ if (mcp.mcpServers.confluence && mcp.mcpServers.confluence.env) {
122
+ token = token || mcp.mcpServers.confluence.env.PAT;
123
+ if (token) source = source || 'mcp_config:confluence';
124
+ }
125
+ }
126
+ }
127
+
128
+ const cleanDomain = sanitizeDomain(rawDomain);
129
+ const isConfigured = Boolean(token && email && cleanDomain);
130
+ const auth = isConfigured ? Buffer.from(`${email}:${token}`).toString('base64') : null;
131
+
132
+ return Object.freeze({
133
+ domain: cleanDomain,
134
+ email: email || null,
135
+ token: token || null,
136
+ auth,
137
+ authBase64: auth,
138
+ isConfigured,
139
+ source
140
+ });
141
+ }
142
+
143
+ /**
144
+ * Verifica autenticação do GitHub via gh CLI ou variáveis de ambiente.
145
+ * @param {object} [options={}]
146
+ * @returns {object}
147
+ */
148
+ function checkGithubAuth(options = {}) {
149
+ const { globalStateCache } = require('../core/hot_state_cache.js');
150
+ const cacheKey = `gh_auth_${process.env.GITHUB_TOKEN || ''}_${process.env.GH_TOKEN || ''}`;
151
+ if (options && options.forceFresh) {
152
+ globalStateCache.invalidate(cacheKey);
153
+ }
154
+
155
+ return globalStateCache.wrap(cacheKey, () => {
156
+ let token = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
157
+ let isAuthenticated = false;
158
+ let user = null;
159
+ let details = '';
160
+ let installed = true;
161
+
162
+ try {
163
+ const stdout = execFileSync('gh', ['auth', 'status'], { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] });
164
+ isAuthenticated = true;
165
+ details = stdout.trim();
166
+ const userMatch = stdout.match(/Logged in to [^\s]+ account ([^\s\(\)]+)/i) || stdout.match(/account ([^\s\(\)]+)/i);
167
+ if (userMatch) user = userMatch[1];
168
+ } catch (err) {
169
+ if (err && err.code === 'ENOENT') {
170
+ installed = false;
171
+ }
172
+ const output = (err.stdout || '') + (err.stderr || '');
173
+ if (/Logged in to/i.test(output)) {
174
+ isAuthenticated = true;
175
+ details = output.trim();
176
+ const userMatch = output.match(/account ([^\s\(\)]+)/i);
177
+ if (userMatch) user = userMatch[1];
178
+ } else if (token) {
179
+ isAuthenticated = true;
180
+ user = 'token-authenticated';
181
+ details = 'Autenticado via GITHUB_TOKEN.';
182
+ } else {
183
+ isAuthenticated = false;
184
+ details = output.trim() || (installed ? 'Usuário não autenticado no GitHub CLI.' : 'GitHub CLI (gh) não está instalado no PATH.');
185
+ }
186
+ }
187
+
188
+ return Object.freeze({
189
+ installed,
190
+ authenticated: isAuthenticated,
191
+ isAuthenticated,
192
+ token: token || null,
193
+ user,
194
+ details
195
+ });
196
+ }, 10000); // 10s TTL
197
+ }
198
+
199
+ /**
200
+ * Recupera a URL canônica do repositório Git.
201
+ * @returns {string}
202
+ */
203
+ function getGitRepositoryUrl() {
204
+ if (process.env.GITHUB_REPOSITORY) {
205
+ return `https://github.com/${process.env.GITHUB_REPOSITORY}`;
206
+ }
207
+
208
+ try {
209
+ const rawRemote = execFileSync('git', ['config', '--get', 'remote.origin.url'], { encoding: 'utf8' }).trim();
210
+ if (rawRemote.startsWith('git@github.com:')) {
211
+ return `https://github.com/${rawRemote.replace('git@github.com:', '').replace(/\.git$/, '')}`;
212
+ } else if (rawRemote.startsWith('https://github.com/')) {
213
+ return rawRemote.replace(/\.git$/, '');
214
+ }
215
+ } catch (err) {
216
+ void err;
217
+ }
218
+
219
+ return 'https://github.com/ContalApps/agent-master-devsecops';
220
+ }
221
+
222
+ /**
223
+ * Coleta metadados essenciais do repositório Git local (branch, SHA, commit, autor).
224
+ * @param {object} [options={}]
225
+ * @returns {object}
226
+ */
227
+ function getGitMetadata(options = {}) {
228
+ const { globalStateCache } = require('../core/hot_state_cache.js');
229
+ if (options && options.forceFresh) {
230
+ globalStateCache.invalidate('git_metadata');
231
+ }
232
+
233
+ return globalStateCache.wrap('git_metadata', () => {
234
+ let sha = '';
235
+ let branch = '';
236
+ let message = '';
237
+ let authorName = '';
238
+
239
+ try {
240
+ sha = execFileSync('git', ['rev-parse', 'HEAD'], { encoding: 'utf8' }).trim();
241
+ branch = execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { encoding: 'utf8' }).trim();
242
+ message = execFileSync('git', ['log', '-1', '--format=%s', sha], { encoding: 'utf8' }).trim();
243
+ authorName = execFileSync('git', ['log', '-1', '--format=%an', sha], { encoding: 'utf8' }).trim();
244
+ } catch (err) {
245
+ void err;
246
+ }
247
+
248
+ return Object.freeze({
249
+ sha,
250
+ shortSha: sha ? sha.slice(0, 7) : 'unknown',
251
+ branch,
252
+ message,
253
+ commitMessage: message,
254
+ authorName: authorName || process.env.GIT_AUTHOR || 'DevSecOps Bot',
255
+ repoUrl: getGitRepositoryUrl()
256
+ });
257
+ }, 5000); // 5s TTL
258
+ }
259
+
260
+ /**
261
+ * Executa requisições HTTPS para a API REST da Atlassian de forma segura e resiliente.
262
+ * @param {object} credentials
263
+ * @param {string} [method='GET']
264
+ * @param {string} [endpointPath='/']
265
+ * @param {any} [bodyData=null]
266
+ * @param {object} [options={}]
267
+ * @returns {Promise<{ status: number, body: any }>}
268
+ */
269
+ async function requestAtlassian(credentials, method = 'GET', endpointPath = '/', bodyData = null, options = {}) {
270
+ return new Promise((resolve, reject) => {
271
+ if (!credentials || typeof credentials !== 'object') {
272
+ return reject(new Error('AtlassianClient: credentials object is required'));
273
+ }
274
+
275
+ const domain = sanitizeDomain(credentials.domain);
276
+ if (!domain) {
277
+ return reject(new Error('AtlassianClient: valid domain is required'));
278
+ }
279
+
280
+ const safeOptions = (options && typeof options === 'object') ? options : {};
281
+ const timeout = Number.isInteger(safeOptions.timeout) && safeOptions.timeout > 0
282
+ ? safeOptions.timeout
283
+ : 30000;
284
+
285
+ const postData = (bodyData !== null && bodyData !== undefined)
286
+ ? (typeof bodyData === 'string' ? bodyData : JSON.stringify(bodyData))
287
+ : null;
288
+
289
+ let authHeader = '';
290
+ if (credentials.auth || credentials.authBase64) {
291
+ authHeader = `Basic ${credentials.auth || credentials.authBase64}`;
292
+ } else if (credentials.token) {
293
+ if (credentials.email) {
294
+ authHeader = `Basic ${Buffer.from(`${credentials.email}:${credentials.token}`).toString('base64')}`;
295
+ } else {
296
+ authHeader = `Bearer ${credentials.token}`;
297
+ }
298
+ }
299
+
300
+ const headers = {
301
+ 'Content-Type': 'application/json',
302
+ 'Accept': 'application/json',
303
+ 'User-Agent': safeOptions.userAgent || 'agent-master-devsecops/5.1',
304
+ ...(authHeader ? { 'Authorization': authHeader } : {}),
305
+ ...(postData ? { 'Content-Length': Buffer.byteLength(postData) } : {}),
306
+ ...(safeOptions.headers || {})
307
+ };
308
+
309
+ const req = https.request({
310
+ hostname: domain,
311
+ path: endpointPath,
312
+ method: (method || 'GET').toUpperCase(),
313
+ headers
314
+ }, res => {
315
+ let data = '';
316
+ res.on('data', chunk => { data += chunk; });
317
+ res.on('end', () => {
318
+ try {
319
+ resolve({ status: res.statusCode, body: data ? JSON.parse(data) : {} });
320
+ } catch (err) {
321
+ // Fallback quando resposta não for JSON
322
+ resolve({ status: res.statusCode, body: data });
323
+ }
324
+ });
325
+ });
326
+
327
+ req.setTimeout(timeout, () => {
328
+ req.destroy(new Error(`AtlassianClient: Request timed out after ${timeout}ms for ${domain}${endpointPath}`));
329
+ });
330
+
331
+ req.on('error', reject);
332
+
333
+ if (postData) {
334
+ req.write(postData);
335
+ }
336
+ req.end();
337
+ });
338
+ }
339
+
340
+ /**
341
+ * Formata um relatório executivo em Markdown sobre a saúde das integrações da Tríade.
342
+ * @param {string} [targetDir=process.cwd()]
343
+ * @returns {string}
344
+ */
345
+ function formatReport(targetDir = process.cwd()) {
346
+ const atlassian = getAtlassianCredentials(targetDir);
347
+ const github = checkGithubAuth();
348
+ const git = getGitMetadata();
349
+
350
+ let md = '# 🔗 Atlassian & Git Client Integration Status Report\n\n';
351
+
352
+ const isAllReady = atlassian.isConfigured && github.isAuthenticated && Boolean(git.sha);
353
+ md += `**Status da Tríade:** \`${isAllReady ? 'HOMOLOGADA' : 'PARCIAL / PENDENTE'}\`\n\n`;
354
+
355
+ md += '### 1. Integração Atlassian (Jira & Confluence)\n';
356
+ md += `- **Configurado:** \`${atlassian.isConfigured ? 'SIM' : 'NÃO'}\`\n`;
357
+ md += `- **Domínio:** \`${atlassian.domain || 'N/A'}\`\n`;
358
+ md += `- **E-mail:** \`${atlassian.email || 'N/A'}\`\n`;
359
+ md += `- **API Token:** \`${atlassian.token ? '***CONFIGURADO***' : 'AUSENTE'}\`\n`;
360
+ md += `- **Origem:** \`${atlassian.source || 'NÃO ENCONTRADO'}\`\n\n`;
361
+
362
+ md += '### 2. Autenticação GitHub\n';
363
+ md += `- **Autenticado:** \`${github.isAuthenticated ? 'SIM' : 'NÃO'}\`\n`;
364
+ md += `- **Usuário:** \`${github.user || 'N/A'}\`\n`;
365
+ md += `- **Detalhes:** ${github.details || 'N/A'}\n\n`;
366
+
367
+ md += '### 3. Repositório Git Local\n';
368
+ md += `- **Repositório:** \`${git.repoUrl || 'N/A'}\`\n`;
369
+ md += `- **Branch Ativa:** \`${git.branch || 'N/A'}\`\n`;
370
+ md += `- **Último Commit:** \`${git.shortSha || 'N/A'}\` — ${git.message || 'Sem mensagem'}\n`;
371
+ md += `- **Autor:** \`${git.authorName || 'N/A'}\`\n`;
372
+
373
+ return md;
374
+ }
375
+
376
+ /**
377
+ * Audita a integridade e prontidão da Tríade emitindo relatório OASIS SARIF v2.1.0.
378
+ * @param {string} [targetDir=process.cwd()]
379
+ * @returns {object} SARIF v2.1.0 JSON object
380
+ */
381
+ function auditToSarif(targetDir = process.cwd()) {
382
+ const atlassian = getAtlassianCredentials(targetDir);
383
+ const github = checkGithubAuth();
384
+ const git = getGitMetadata();
385
+
386
+ const rules = [
387
+ {
388
+ id: 'RULE-ATL-001',
389
+ name: 'AtlassianCredentialsMissing',
390
+ severity: 'WARNING',
391
+ message: 'Credenciais Atlassian não configuradas para Jira/Confluence.',
392
+ remediation: 'Configurar variáveis ATLASSIAN_DOMAIN, ATLASSIAN_EMAIL e ATLASSIAN_API_TOKEN ou executar agent_configure_credentials.',
393
+ cwe: 'CWE-200'
394
+ },
395
+ {
396
+ id: 'RULE-GH-001',
397
+ name: 'GitHubAuthMissing',
398
+ severity: 'NOTE',
399
+ message: 'GitHub CLI ou GITHUB_TOKEN não autenticado.',
400
+ remediation: 'Executar gh auth login ou exportar GITHUB_TOKEN.',
401
+ cwe: 'CWE-287'
402
+ },
403
+ {
404
+ id: 'RULE-GIT-001',
405
+ name: 'GitRepoUninitialized',
406
+ severity: 'WARNING',
407
+ message: 'Diretório não possui repositório Git ou commits válidos.',
408
+ remediation: 'Inicializar repositório Git com git init e criar o commit canônico.',
409
+ cwe: 'CWE-1059'
410
+ }
411
+ ];
412
+
413
+ const findings = [];
414
+
415
+ if (!atlassian.isConfigured) {
416
+ findings.push({
417
+ ruleId: 'RULE-ATL-001',
418
+ severity: 'WARNING',
419
+ message: `Credenciais Atlassian incompletas (domínio: ${atlassian.domain ? 'ok' : 'falta'}, email: ${atlassian.email ? 'ok' : 'falta'}, token: ${atlassian.token ? 'ok' : 'falta'}). Operando em resiliência local.`,
420
+ filename: '.env',
421
+ line: 1,
422
+ column: 1
423
+ });
424
+ }
425
+
426
+ if (!github.isAuthenticated) {
427
+ findings.push({
428
+ ruleId: 'RULE-GH-001',
429
+ severity: 'NOTE',
430
+ message: `GitHub não autenticado (${github.details}).`,
431
+ filename: '.env',
432
+ line: 1,
433
+ column: 1
434
+ });
435
+ }
436
+
437
+ if (!git.sha || !git.branch) {
438
+ findings.push({
439
+ ruleId: 'RULE-GIT-001',
440
+ severity: 'WARNING',
441
+ message: 'Metadados Git ausentes ou incompletos.',
442
+ filename: '.git',
443
+ line: 1,
444
+ column: 1
445
+ });
446
+ }
447
+
448
+ return createSarifReport({
449
+ toolName: 'agent-master-atlassian-client',
450
+ toolVersion: '5.1.0',
451
+ rules,
452
+ findings,
453
+ properties: {
454
+ category: 'Governance & Credentials Healthcheck',
455
+ atlassianConfigured: atlassian.isConfigured,
456
+ githubAuthenticated: github.isAuthenticated,
457
+ gitSha: git.sha || 'unknown'
458
+ }
459
+ });
460
+ }
461
+
462
+ /**
463
+ * Classe Canônica AtlassianClient.
464
+ * Fornece interface orientada a objetos para requisições Atlassian REST,
465
+ * inspeção de Git/GitHub e auditorias em SARIF v2.1.0.
466
+ */
467
+ class AtlassianClient {
468
+ constructor(credentialsOrTargetDir = {}, options = {}) {
469
+ let credentials = null;
470
+ let targetDir = process.cwd();
471
+ let safeOptions = {};
472
+
473
+ if (typeof credentialsOrTargetDir === 'string') {
474
+ targetDir = credentialsOrTargetDir;
475
+ safeOptions = (options && typeof options === 'object') ? options : {};
476
+ credentials = getAtlassianCredentials(targetDir);
477
+ } else if (credentialsOrTargetDir && typeof credentialsOrTargetDir === 'object') {
478
+ if (credentialsOrTargetDir.isConfigured !== undefined || credentialsOrTargetDir.domain || credentialsOrTargetDir.token) {
479
+ credentials = credentialsOrTargetDir;
480
+ safeOptions = (options && typeof options === 'object') ? options : {};
481
+ } else {
482
+ safeOptions = credentialsOrTargetDir;
483
+ targetDir = safeOptions.targetDir || process.cwd();
484
+ credentials = getAtlassianCredentials(targetDir);
485
+ }
486
+ } else {
487
+ credentials = getAtlassianCredentials(targetDir);
488
+ safeOptions = (options && typeof options === 'object') ? options : {};
489
+ }
490
+
491
+ this.credentials = credentials;
492
+ this.targetDir = targetDir;
493
+ this.options = Object.freeze({
494
+ timeout: safeOptions.timeout || 30000,
495
+ userAgent: safeOptions.userAgent || 'agent-master-devsecops/5.1',
496
+ ...safeOptions
497
+ });
498
+ }
499
+
500
+ isConfigured() {
501
+ return Boolean(this.credentials && this.credentials.isConfigured);
502
+ }
503
+
504
+ getCredentials() {
505
+ return this.credentials;
506
+ }
507
+
508
+ async request(method, endpointPath, bodyData = null, requestOptions = {}) {
509
+ return requestAtlassian(this.credentials, method, endpointPath, bodyData, {
510
+ ...this.options,
511
+ ...requestOptions
512
+ });
513
+ }
514
+
515
+ async get(endpointPath, requestOptions = {}) {
516
+ return this.request('GET', endpointPath, null, requestOptions);
517
+ }
518
+
519
+ async post(endpointPath, bodyData, requestOptions = {}) {
520
+ return this.request('POST', endpointPath, bodyData, requestOptions);
521
+ }
522
+
523
+ async put(endpointPath, bodyData, requestOptions = {}) {
524
+ return this.request('PUT', endpointPath, bodyData, requestOptions);
525
+ }
526
+
527
+ async delete(endpointPath, requestOptions = {}) {
528
+ return this.request('DELETE', endpointPath, null, requestOptions);
529
+ }
530
+
531
+ checkGithub(options = {}) {
532
+ return checkGithubAuth(options);
533
+ }
534
+
535
+ getGitMetadata(options = {}) {
536
+ return getGitMetadata(options);
537
+ }
538
+
539
+ getGitRepositoryUrl() {
540
+ return getGitRepositoryUrl();
541
+ }
542
+
543
+ formatReport() {
544
+ return formatReport(this.targetDir);
545
+ }
546
+
547
+ auditToSarif() {
548
+ return auditToSarif(this.targetDir);
549
+ }
550
+
551
+ // Métodos Estáticos Canônicos
552
+ static sanitizeDomain(domainStr) {
553
+ return sanitizeDomain(domainStr);
554
+ }
555
+
556
+ static resolveMcpConfig() {
557
+ return resolveMcpConfig();
558
+ }
559
+
560
+ static getCredentials(targetDir) {
561
+ return getAtlassianCredentials(targetDir);
562
+ }
563
+
564
+ static checkGithubAuth(options) {
565
+ return checkGithubAuth(options);
566
+ }
567
+
568
+ static getGitRepositoryUrl() {
569
+ return getGitRepositoryUrl();
570
+ }
571
+
572
+ static getGitMetadata(options) {
573
+ return getGitMetadata(options);
574
+ }
575
+
576
+ static async request(credentials, method, endpointPath, bodyData, options) {
577
+ return requestAtlassian(credentials, method, endpointPath, bodyData, options);
578
+ }
579
+
580
+ static formatReport(targetDir) {
581
+ return formatReport(targetDir);
582
+ }
583
+
584
+ static auditToSarif(targetDir) {
585
+ return auditToSarif(targetDir);
586
+ }
587
+ }
588
+
589
+ module.exports = {
590
+ AtlassianClient,
591
+ sanitizeDomain,
592
+ getAtlassianCredentials,
593
+ checkGithubAuth,
594
+ getGitRepositoryUrl,
595
+ getGitMetadata,
596
+ requestAtlassian,
597
+ formatReport,
598
+ auditToSarif
599
+ };
600
+