@mocoto/mahoraga 0.14.5 → 0.14.7

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.
@@ -13,7 +13,7 @@ async function obterCommit() {
13
13
  try {
14
14
  // usar helper seguro
15
15
  const { executarShellSeguro } = await import('../../core/utils/index.js');
16
- return executarShellSeguro('git rev-parse --short HEAD', {
16
+ return executarShellSeguro('git', ['rev-parse', '--short', 'HEAD'], {
17
17
  stdio: ['ignore', 'pipe', 'ignore']
18
18
  }).toString().trim();
19
19
  }
@@ -38,9 +38,10 @@ export function comandoAtualizar(aplicarFlagsGlobais) {
38
38
  log.aviso(CliAtualizarExtraMensagens.guardianBaselineAlterado);
39
39
  log.info(CliAtualizarExtraMensagens.recomendadoGuardianDiff);
40
40
  }
41
- const cmd = opts.global ? 'npm install -g mahoraga@latest' : 'npm install mahoraga@latest';
41
+ const cmd = opts.global ? 'npm install -g @mocoto/mahoraga@latest' : 'npm install @mocoto/mahoraga@latest';
42
42
  logSistema.atualizacaoExecutando(cmd);
43
- executarShellSeguro(cmd, {
43
+ const npmArgs = opts.global ? ['install', '-g', '@mocoto/mahoraga@latest'] : ['install', '@mocoto/mahoraga@latest'];
44
+ executarShellSeguro('npm', npmArgs, {
44
45
  stdio: 'inherit'
45
46
  });
46
47
  logSistema.atualizacaoSucesso();
@@ -13,6 +13,7 @@ export const FormattersMensagens = {
13
13
  };
14
14
  export const ExecSafeMensagens = {
15
15
  safeModeBloqueado: 'Command execution disabled in SAFE_MODE. Set SUKUNA_ALLOW_EXEC=1 to allow it.',
16
+ binarioNaoPermitido: (bin) => `Binary '${bin}' is not in the allowed list.`,
16
17
  };
17
18
  export const WorkerExecutorMensagens = {
18
19
  timeoutTecnica: (nome, ms) => `Timeout: technique '${nome}' exceeded ${ms}ms`,
@@ -13,6 +13,7 @@ export const FormattersMensagens = {
13
13
  };
14
14
  export const ExecSafeMensagens = {
15
15
  safeModeBloqueado: 'SAFE_MODEではコマンド実行が無効です。許可するには SUKUNA_ALLOW_EXEC=1 を設定してください。',
16
+ binarioNaoPermitido: (bin) => `バイナリ '${bin}' は許可リストに含まれていません。`,
16
17
  };
17
18
  export const WorkerExecutorMensagens = {
18
19
  timeoutTecnica: (nome, ms) => `タイムアウト: 手法 '${nome}' が ${ms}ms を超えました`,
@@ -21,6 +21,7 @@ export const FormattersMensagens = {
21
21
  };
22
22
  export const ExecSafeMensagens = {
23
23
  safeModeBloqueado: 'Execução de comandos desabilitada em SAFE_MODE. Defina SUKUNA_ALLOW_EXEC=1 para permitir.',
24
+ binarioNaoPermitido: (bin) => `Binário '${bin}' não está na lista de permitidos.`,
24
25
  };
25
26
  export const WorkerExecutorMensagens = {
26
27
  timeoutTecnica: (nome, ms) => `Timeout: tecnica '${nome}' excedeu ${ms}ms`,
@@ -13,6 +13,7 @@ export const FormattersMensagens = {
13
13
  };
14
14
  export const ExecSafeMensagens = {
15
15
  safeModeBloqueado: 'SAFE_MODE下命令执行已禁用。设置 SUKUNA_ALLOW_EXEC=1 以允许。',
16
+ binarioNaoPermitido: (bin) => `二进制文件 '${bin}' 不在允许列表中。`,
16
17
  };
17
18
  export const WorkerExecutorMensagens = {
18
19
  timeoutTecnica: (nome, ms) => `超时: 技术 '${nome}' 已超过 ${ms}ms`,
@@ -1,15 +1,35 @@
1
1
  // SPDX-License-Identifier: MIT
2
- import { createRequire } from 'module';
2
+ import { parse as babelParse } from '@babel/parser';
3
3
  import { getCurrentParsingFile, logCore, wrapMinimal } from '../utils.js';
4
- const localRequire = createRequire(import.meta.url);
5
- /** Analisa código TypeScript usando parser nativo TSC */
4
+ /**
5
+ * Analisa código TypeScript/TSX.
6
+ * Nota de migração (TS 7 / compilador nativo em Go): o compilador TypeScript
7
+ * não expõe mais a API programática (ts.createSourceFile etc.). Usamos o
8
+ * @babel/parser (já dependência do projeto) para obter o AST de forma estável
9
+ * e compatível com o TS 7.
10
+ */
6
11
  export function parseComTypeScript(codigo, tsx = false) {
7
12
  try {
8
- const ts = localRequire('typescript');
9
- const sf = ts.createSourceFile(tsx ? 'file.tsx' : 'file.ts', codigo, ts.ScriptTarget.Latest, false, tsx ? ts.ScriptKind.TSX : ts.ScriptKind.TS);
13
+ // Lista de plugins alinhada aos demais parsers do projeto (babel 8).
14
+ // Tipada como string[] para acomodar plugins que deixaram de figurar no
15
+ // enum ParserPlugin do babel 8 (passaram a ser comportamento padrão).
16
+ const plugins = [
17
+ 'decorators-legacy', 'classProperties', 'classPrivateProperties',
18
+ 'classPrivateMethods', 'optionalChaining', 'nullishCoalescingOperator',
19
+ 'topLevelAwait', 'importAttributes'
20
+ ];
21
+ if (tsx) {
22
+ plugins.unshift('jsx');
23
+ }
24
+ plugins.unshift('typescript');
25
+ const ast = babelParse(codigo, {
26
+ sourceType: 'unambiguous',
27
+ errorRecovery: true,
28
+ plugins: plugins
29
+ });
10
30
  return wrapMinimal(tsx ? 'tsx-tsc' : 'ts-tsc', {
11
- kind: sf.kind,
12
- statements: sf.statements.length
31
+ kind: 'SourceFile',
32
+ statements: ast.program.body.length
13
33
  });
14
34
  }
15
35
  catch (erro) {
@@ -1,31 +1,71 @@
1
1
  // SPDX-License-Identifier: MIT
2
- import { execSync } from 'node:child_process';
2
+ import { execFileSync } from 'node:child_process';
3
+ import path from 'node:path';
3
4
  import { config } from '../config/index.js';
4
5
  import { getMessages } from '../messages/index.js';
5
6
  /**
6
- * Executa um comando shell de forma segura.
7
- * Bloqueia a execução quando SAFE_MODE está ativo e ALLOW_EXEC não está habilitado.
8
- * @param cmd - Comando shell a executar
9
- * @param opts - Opções do execSync do Node.js
10
- * @returns Buffer ou string com a saída do comando
11
- * @throws Error se SAFE_MODE estiver ativo sem ALLOW_EXEC
7
+ * Allowlist de binários que o CLI pode executar externamente.
8
+ * Apenas comandos essenciais para operação do ferramenta são permitidos.
9
+ * Isso substitui a abordagem anterior de blacklist de caracteres shell,
10
+ * eliminando o vetor de injeção por shell intermediário.
11
+ */
12
+ const BINARIOS_PERMITIDOS = new Set([
13
+ 'git',
14
+ 'npm',
15
+ 'npx',
16
+ 'node',
17
+ 'pnpm',
18
+ 'yarn',
19
+ 'corepack',
20
+ ]);
21
+ /**
22
+ * Valida que o binário está na allowlist.
23
+ * @param bin - Caminho ou nome do binário
24
+ * @throws Error se o binário não for permitido
12
25
  */
13
- export function executarShellSeguro(cmd, opts = {}) {
14
- // only block if SAFE_MODE is explicitly true and ALLOW_EXEC is falsy
26
+ function validarBinario(bin) {
27
+ const nome = path.basename(bin);
28
+ if (!BINARIOS_PERMITIDOS.has(nome)) {
29
+ throw new Error(getMessages().ExecSafeMensagens.binarioNaoPermitido(bin));
30
+ }
31
+ }
32
+ function verificarSafeMode() {
15
33
  if (config.SAFE_MODE && !config.ALLOW_EXEC) {
16
34
  throw new Error(getMessages().ExecSafeMensagens.safeModeBloqueado);
17
35
  }
18
- return execSync(cmd, opts);
36
+ }
37
+ /**
38
+ * Executa um binário de forma segura sem shell intermediário.
39
+ *
40
+ * Bloqueia a execução quando SAFE_MODE está ativo e ALLOW_EXEC não está habilitado.
41
+ * Valida o binário contra uma allowlist de comandos permitidos.
42
+ * Força `shell: false` para eliminar o risco de injeção de comandos.
43
+ *
44
+ * @param bin - Binário a executar (ex: 'git', 'npm')
45
+ * @param args - Argumentos do binário (array de strings)
46
+ * @param opts - Opções adicionais do child_process
47
+ * @returns Buffer ou string com a saída do comando
48
+ * @throws Error se SAFE_MODE estiver ativo sem ALLOW_EXEC,
49
+ * se o binário não estiver na allowlist,
50
+ * ou se a execução falhar
51
+ */
52
+ export function executarShellSeguro(bin, args = [], opts = {}) {
53
+ validarBinario(bin);
54
+ verificarSafeMode();
55
+ return execFileSync(bin, args, { ...opts, shell: false });
19
56
  }
20
57
  /**
21
58
  * Versão assíncrona do `executarShellSeguro`.
22
59
  * Wrapper async que delega ao sync, útil em contextos que já utilizam await.
23
- * @param cmd - Comando shell a executar
24
- * @param opts - Opções do execSync do Node.js
60
+ *
61
+ * @param bin - Binário a executar (ex: 'git', 'npm')
62
+ * @param args - Argumentos do binário (array de strings)
63
+ * @param opts - Opções adicionais do child_process
25
64
  * @returns Promise resolvida com Buffer ou string da saída do comando
26
- * @throws Error se SAFE_MODE estiver ativo sem ALLOW_EXEC
65
+ * @throws Error se SAFE_MODE estiver ativo sem ALLOW_EXEC,
66
+ * se o binário não estiver na allowlist,
67
+ * ou se a execução falhar
27
68
  */
28
- export function executarShellSeguroAsync(cmd, opts = {}) {
29
- // wrapper assíncrono que delega ao sync (usado onde já há await)
30
- return executarShellSeguro(cmd, opts);
69
+ export function executarShellSeguroAsync(bin, args = [], opts = {}) {
70
+ return executarShellSeguro(bin, args, opts);
31
71
  }
@@ -197,7 +197,6 @@ const aliases = {
197
197
  "@reports": "./src/reports/index",
198
198
  "@reports/compliance": "./src/reports/compliance/index",
199
199
  "@scripts": "./src/scripts/index",
200
- "@sdk": "./src/sdk/index",
201
200
  "@shared": "./src/shared/index",
202
201
  "@shared/data-processing": "./src/shared/data-processing/index",
203
202
  "@shared/formatters": "./src/shared/formatters/index",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mocoto/mahoraga",
3
- "version": "0.14.5",
3
+ "version": "0.14.7",
4
4
  "type": "module",
5
5
  "description": "C.L.I modular para análise, diagnóstico e manutenção de projetos JavaScript/TypeScript e multi-stack leve",
6
6
  "author": "rei macaco",
@@ -116,46 +116,39 @@
116
116
  "THIRD-PARTY-NOTICES.txt"
117
117
  ],
118
118
  "dependencies": {
119
- "@babel/parser": "^8.0.0",
120
- "@babel/traverse": "^8.0.0",
121
- "@babel/types": "^8.0.0",
119
+ "@babel/parser": "^8.0.4",
120
+ "@babel/traverse": "^8.0.4",
121
+ "@babel/types": "^8.0.4",
122
122
  "chalk": "^5.6.2",
123
123
  "commander": "^15.0.0",
124
124
  "dotenv": "^17.4.2",
125
125
  "fast-xml-parser": "^5.9.3",
126
126
  "htmlparser2": "^12.0.0",
127
- "marked": "^18.0.5",
127
+ "marked": "^18.0.6",
128
128
  "micromatch": "^4.0.8",
129
129
  "minimatch": "^10.2.5",
130
130
  "openpgp": "^6.3.1",
131
131
  "ora": "^9.4.1",
132
132
  "p-limit": "^7.3.0",
133
+ "postcss": "^8.5.16",
133
134
  "postcss-sass": "^0.5.0",
134
135
  "postcss-scss": "^4.0.9",
135
136
  "xxhashjs": "^0.2.2",
136
- "yaml": "^2.9.0",
137
- "postcss": "^8.5.16"
137
+ "yaml": "^2.9.0"
138
138
  },
139
139
  "devDependencies": {
140
140
  "@types/micromatch": "^4.0.10",
141
- "@types/node": "^26.1.0",
142
- "@typescript-eslint/eslint-plugin": "^8.62.1",
143
- "@typescript-eslint/parser": "^8.62.1",
141
+ "@types/node": "^26.1.1",
144
142
  "@vitest/coverage-istanbul": "^4.1.8",
145
143
  "@vitest/coverage-v8": "^4.1.8",
146
144
  "autoprefixer": "^10.5.2",
147
- "eslint": "^10.6.0",
148
- "eslint-import-resolver-typescript": "^4.4.5",
149
- "eslint-plugin-import-x": "^4.17.1",
150
- "eslint-plugin-simple-import-sort": "^13.0.0",
151
- "eslint-plugin-unused-imports": "^4.4.1",
152
145
  "husky": "^9.1.7",
153
146
  "license-checker-rseidelsohn": "^5.0.1",
154
147
  "lint-staged": "^17.0.8",
155
148
  "rimraf": "^6.1.3",
156
- "tsc-alias": "^1.8.17",
149
+ "tsc-alias": "^1.9.0",
157
150
  "tsx": "^4.23.0",
158
- "typescript": "^6.0.3",
151
+ "typescript": "^7.0.2",
159
152
  "vitest": "^4.1.8"
160
153
  },
161
154
  "lint-staged": {
package/dist/index.js DELETED
@@ -1,2 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- export { analyzeFile, analyzeProject } from './sdk/analyzer.js';
@@ -1,88 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- import path from 'node:path';
3
- import { executarInquisicao, scanRepository } from '../core/execution/index.js';
4
- import { decifrarSintaxe } from '../core/parsing/index.js';
5
- import { asTecnicas } from '../types/index.js';
6
- const EXTENSOES_COM_AST = new Set([
7
- '.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs',
8
- '.css', '.scss', '.html', '.xml', '.svg',
9
- '.py', '.go', '.rs', '.php', '.sh',
10
- '.yaml', '.yml', '.json',
11
- ]);
12
- function extensaoRelevante(relPath) {
13
- const ext = path.extname(relPath).toLowerCase();
14
- return EXTENSOES_COM_AST.has(ext);
15
- }
16
- async function carregarTecnicas() {
17
- const { registroAnalistas } = await import('../analysts/registry/index.js');
18
- return asTecnicas(registroAnalistas);
19
- }
20
- function tecnicasParaArquivo(tecnicas, relPath) {
21
- return tecnicas.filter(t => {
22
- if (t.global) {
23
- return false;
24
- }
25
- if (!t.test) {
26
- return extensaoRelevante(relPath);
27
- }
28
- return t.test(relPath);
29
- });
30
- }
31
- const AST_SENTINEL = {};
32
- export async function analyzeFile(filePath, content, baseDir) {
33
- const inicio = performance.now();
34
- const relPath = baseDir ? path.relative(baseDir, filePath) : path.basename(filePath);
35
- if (!extensaoRelevante(relPath)) {
36
- return { ocorrencias: [], arquivo: relPath, tempoMs: 0 };
37
- }
38
- const ext = path.extname(filePath).toLowerCase();
39
- const parsed = await decifrarSintaxe(content, ext, { relPath });
40
- const ast = parsed ? AST_SENTINEL : null;
41
- const tecnicas = await carregarTecnicas();
42
- const tecnicasFile = tecnicasParaArquivo(tecnicas, relPath);
43
- if (tecnicasFile.length === 0) {
44
- return { ocorrencias: [], arquivo: relPath, tempoMs: performance.now() - inicio };
45
- }
46
- const ocorrencias = [];
47
- for (const tecnica of tecnicasFile) {
48
- try {
49
- const resultado = await tecnica.aplicar(content, relPath, ast, filePath);
50
- if (Array.isArray(resultado)) {
51
- ocorrencias.push(...resultado);
52
- }
53
- else if (resultado) {
54
- ocorrencias.push(resultado);
55
- }
56
- }
57
- catch {
58
- // Silencia erros por analista
59
- }
60
- }
61
- return {
62
- ocorrencias,
63
- arquivo: relPath,
64
- tempoMs: performance.now() - inicio,
65
- };
66
- }
67
- export async function analyzeProject(projectDir, opts) {
68
- const inicio = performance.now();
69
- const fileEntries = await scanRepository(projectDir, {
70
- includeContent: true,
71
- });
72
- const entries = Object.values(fileEntries);
73
- const fileEntriesComAst = entries.map(f => ({ ...f, ast: undefined }));
74
- let filteredEntries = fileEntriesComAst;
75
- if (opts?.fast) {
76
- filteredEntries = fileEntriesComAst.filter(fe => {
77
- const rel = fe.relPath || '';
78
- return !rel.includes('node_modules') && !rel.includes('.test.') && !rel.includes('.spec.');
79
- });
80
- }
81
- const tecnicas = await carregarTecnicas();
82
- const resultadoExecucao = await executarInquisicao(filteredEntries, tecnicas, projectDir, null, { verbose: false, compact: true, fast: opts?.fast });
83
- return {
84
- ocorrencias: resultadoExecucao.ocorrencias,
85
- totalArquivos: entries.length,
86
- tempoMs: performance.now() - inicio,
87
- };
88
- }
package/dist/sdk/index.js DELETED
@@ -1,2 +0,0 @@
1
- export { analyzeFile, analyzeProject } from './analyzer.js';
2
- export { MahoragaSDK } from './sdk-legacy.js';
@@ -1,36 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- import { analistaGithubActions, analistaGithubActionsGlobal, registrarDetectorGithubActions } from '../analysts/github-actions/index.js';
3
- /**
4
- * Mahoraga SDK Principal
5
- * Fornece acesso programático aos analistas e sistema de plugins
6
- */
7
- export const MahoragaSDK = {
8
- /**
9
- * Analisa um conteúdo de workflow individual
10
- */
11
- async analisarGithubActions(conteudo, relPath = '.github/workflows/main.yml') {
12
- try {
13
- const res = await analistaGithubActions.aplicar(conteudo, relPath, null, undefined, undefined);
14
- return Array.isArray(res) ? res : [];
15
- }
16
- catch {
17
- return [];
18
- }
19
- },
20
- /**
21
- * Analisa um repositório completo (Contexto global)
22
- */
23
- async analisarRepositorio(contexto) {
24
- try {
25
- const res = await analistaGithubActionsGlobal.aplicar('', '', null, undefined, contexto);
26
- return Array.isArray(res) ? res : [];
27
- }
28
- catch {
29
- return [];
30
- }
31
- },
32
- /**
33
- * Registra um novo plugin de detector
34
- */
35
- registrarDetector: registrarDetectorGithubActions
36
- };
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export * from './analyzer.js';