codesentry 0.1.10 → 0.1.11

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 (3) hide show
  1. package/README.md +12 -2
  2. package/dist/index.js +287 -81
  3. package/package.json +4 -4
package/README.md CHANGED
@@ -140,6 +140,7 @@ codesentry scan ./src
140
140
  codesentry scan . --json
141
141
  codesentry scan . --concurrency 4
142
142
  codesentry scan . --config ./rules/security.yml
143
+ codesentry scan . --tests
143
144
  ```
144
145
 
145
146
  O Semgrep CE embutido é executado automaticamente depois das regras nativas,
@@ -153,6 +154,11 @@ apenas inteiros positivos. Sem valor, o limite é ajustado para a máquina
153
154
  Semgrep e das regras OWASP chegam em novas releases do CodeSentry. Quando
154
155
  necessário, `--config` aceita exclusivamente um arquivo YAML local.
155
156
 
157
+ Por padrão, arquivos de teste não são analisados: nenhum diretório chamado
158
+ `tests`, `test` ou `__tests__` (em qualquer profundidade) e nenhum arquivo
159
+ com sufixo `.spec.*`/`.test.*` (em qualquer lugar, mesmo fora dessas pastas)
160
+ entra no scan. Use `--tests` para incluí-los.
161
+
156
162
  Em macOS, ARM e plataformas sem runtime publicado, o comando interrompe
157
163
  explicitamente em vez de declarar uma análise parcial como completa.
158
164
 
@@ -181,7 +187,7 @@ o scan completo (e sem o Semgrep, que só roda como parte de `scan`). Todos
181
187
  seguem o mesmo formato:
182
188
 
183
189
  ```bash
184
- codesentry <comando> [path] [--json]
190
+ codesentry <comando> [path] [--json] [--tests]
185
191
  ```
186
192
 
187
193
  Alguns exemplos:
@@ -193,9 +199,13 @@ codesentry xss ./src --json # possíveis XSS (innerHTML, document.write
193
199
  codesentry unsafe-sql ./src # SQL injection por concatenação
194
200
  codesentry command-injection ./src # child_process com entrada não sanitizada
195
201
  codesentry weak-hash-algorithm ./src # uso de MD5/SHA-1 para hashing sensível
196
- codesentry dependency-audit . # `npm audit` das dependências do projeto
202
+ codesentry dependency-audit . # `npm audit` das dependências do projeto (sem --tests: não lê arquivos-fonte)
197
203
  ```
198
204
 
205
+ Assim como em `scan`, `--tests` inclui arquivos de teste na análise (por
206
+ padrão são ignorados) — exceto em `dependency-audit`, que nunca lê
207
+ arquivos-fonte e por isso não tem essa flag.
208
+
199
209
  A lista completa (30+ comandos, um por regra) sai de `codesentry help` —
200
210
  mantê-la sempre em sincronia aqui manualmente não seria viável.
201
211
 
package/dist/index.js CHANGED
@@ -141,6 +141,9 @@ var commandInjectionRule = {
141
141
  }
142
142
  };
143
143
 
144
+ // src/commands/scan/scan-options.ts
145
+ var withScanOptions = (command) => command.option("--json", "exibe o resultado em JSON").option("--tests", "inclui arquivos de teste na an\xE1lise (por padr\xE3o s\xE3o ignorados)");
146
+
144
147
  // src/commands/scan/scan-runner.ts
145
148
  import { writeFile } from "fs/promises";
146
149
  import { join as join2 } from "path";
@@ -169,6 +172,26 @@ var formatErrorChain = (error) => {
169
172
  // src/reporters/console.reporter.ts
170
173
  import chalk from "chalk";
171
174
  import Table from "cli-table3";
175
+
176
+ // src/scanner/scan-result.ts
177
+ var DEPENDENCY_AUDIT_NOTE = 'Dependency audit n\xE3o foi inclu\xEDdo neste scan \u2014 rode "codesentry dependency-audit" separadamente.';
178
+ var ZERO_SEMGREP_COVERAGE_WARNING = "Semgrep n\xE3o analisou nenhum arquivo nesta execu\xE7\xE3o \u2014 verifique se o ruleset offline est\xE1 instalado/preparado.";
179
+ var mergeScanResults = (nativeResult, semgrepResult) => ({
180
+ scannedFiles: nativeResult.scannedFiles,
181
+ findings: [...nativeResult.findings, ...semgrepResult.findings],
182
+ durationMs: nativeResult.durationMs + semgrepResult.durationMs,
183
+ engines: {
184
+ codesentry: nativeResult.engines?.codesentry ?? nativeResult.scannedFiles,
185
+ semgrep: semgrepResult.engines?.semgrep ?? semgrepResult.scannedFiles
186
+ }
187
+ });
188
+ var finalizeScanResult = (result) => ({
189
+ ...result,
190
+ engines: { ...result.engines, dependencyAudit: false },
191
+ warnings: result.engines?.semgrep === 0 ? [...result.warnings ?? [], ZERO_SEMGREP_COVERAGE_WARNING] : result.warnings
192
+ });
193
+
194
+ // src/reporters/console.reporter.ts
172
195
  var SEVERITY_COLOR = {
173
196
  low: (text2) => chalk.gray(text2),
174
197
  medium: (text2) => chalk.yellow(text2),
@@ -176,8 +199,17 @@ var SEVERITY_COLOR = {
176
199
  critical: (text2) => chalk.bgRed.white(text2)
177
200
  };
178
201
  var coverageText = (result) => result.engines?.semgrep === void 0 ? "" : ` CodeSentry: ${result.engines.codesentry ?? result.scannedFiles} JS/TS; Semgrep: ${result.engines.semgrep} arquivo(s).`;
202
+ var printNotes = (result) => {
203
+ if (result.engines?.dependencyAudit === false) {
204
+ console.log(chalk.cyan(DEPENDENCY_AUDIT_NOTE));
205
+ }
206
+ for (const warning of result.warnings ?? []) {
207
+ console.log(chalk.yellow(`Aviso: ${warning}`));
208
+ }
209
+ };
179
210
  var printCleanReport = (result, coverage) => {
180
211
  console.log(chalk.green(`Nenhum problema encontrado (${result.scannedFiles} arquivos analisados).${coverage}`));
212
+ printNotes(result);
181
213
  };
182
214
  var findingsTable = (result) => {
183
215
  const table = new Table({ head: ["Severity", "Rule", "File", "Line", "Message"] });
@@ -201,6 +233,7 @@ var printFindingsReport = (result, coverage) => {
201
233
  ${result.findings.length} problema(s) encontrado(s) em ${result.scannedFiles} arquivo(s) (${result.durationMs}ms).${coverage}`
202
234
  )
203
235
  );
236
+ printNotes(result);
204
237
  };
205
238
  var printConsoleReport = (result) => {
206
239
  const coverage = coverageText(result);
@@ -261,8 +294,10 @@ var reportHeader = (result, generatedAt) => [
261
294
  ],
262
295
  `- **Dura\xE7\xE3o:** ${result.durationMs}ms`,
263
296
  `- **Total de problemas:** ${result.findings.length}`,
297
+ ...result.engines?.dependencyAudit === false ? [`- **Nota:** ${DEPENDENCY_AUDIT_NOTE}`] : [],
264
298
  ""
265
299
  ];
300
+ var warningsSection = (result) => (result.warnings ?? []).length === 0 ? [] : ["## Avisos", "", ...(result.warnings ?? []).map((warning) => `- ${warning}`), ""];
266
301
  var summaryTable = (bySeverity) => {
267
302
  const lines = ["## Resumo por severidade", "", "| Severidade | Quantidade |", "| --- | --- |"];
268
303
  for (const severity of SEVERITY_ORDER) {
@@ -283,22 +318,14 @@ var severitySections = (bySeverity) => {
283
318
  };
284
319
  var toMarkdownReport = (result, generatedAt = /* @__PURE__ */ new Date()) => {
285
320
  const bySeverity = groupBy(result.findings, (f) => f.severity);
286
- return [...reportHeader(result, generatedAt), ...summaryTable(bySeverity), ...severitySections(bySeverity)].join(
287
- "\n"
288
- );
321
+ return [
322
+ ...reportHeader(result, generatedAt),
323
+ ...warningsSection(result),
324
+ ...summaryTable(bySeverity),
325
+ ...severitySections(bySeverity)
326
+ ].join("\n");
289
327
  };
290
328
 
291
- // src/scanner/scan-result.ts
292
- var mergeScanResults = (nativeResult, semgrepResult) => ({
293
- scannedFiles: nativeResult.scannedFiles,
294
- findings: [...nativeResult.findings, ...semgrepResult.findings],
295
- durationMs: nativeResult.durationMs + semgrepResult.durationMs,
296
- engines: {
297
- codesentry: nativeResult.engines?.codesentry ?? nativeResult.scannedFiles,
298
- semgrep: semgrepResult.engines?.semgrep ?? semgrepResult.scannedFiles
299
- }
300
- });
301
-
302
329
  // src/scanner/scanner.ts
303
330
  import { readFile } from "fs/promises";
304
331
  import { availableParallelism } from "os";
@@ -306,26 +333,35 @@ import { availableParallelism } from "os";
306
333
  // src/scanner/file-finder.ts
307
334
  import { readdir } from "fs/promises";
308
335
  import { join } from "path";
336
+
337
+ // src/scanner/ignore-patterns.ts
338
+ var ALWAYS_IGNORED_DIR_NAMES = ["node_modules", ".git", "dist", ".next"];
339
+ var TEST_DIR_NAMES = ["tests", "test", "__tests__"];
340
+ var TEST_FILE_GLOBS = ["*.spec.*", "*.test.*"];
341
+ var TEST_FILE_NAME_PATTERN = /\.(spec|test)\.[^./]+$/;
342
+ var isIgnoredDirName = (name, includeTests = false) => ALWAYS_IGNORED_DIR_NAMES.includes(name) || !includeTests && TEST_DIR_NAMES.includes(name);
343
+ var isTestFileName = (fileName) => TEST_FILE_NAME_PATTERN.test(fileName);
344
+
345
+ // src/scanner/file-finder.ts
309
346
  var SCANNABLE_EXTENSIONS = [".js", ".ts", ".jsx", ".tsx"];
310
- var IGNORED_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".next", "tests"]);
311
347
  var isScannable = (fileName) => SCANNABLE_EXTENSIONS.some((ext) => fileName.endsWith(ext));
312
- var filesFromEntry = async (currentDir, entry) => {
348
+ var filesFromEntry = async (currentDir, entry, includeTests) => {
313
349
  if (entry.isDirectory()) {
314
- return IGNORED_DIRS.has(entry.name) ? [] : walk(join(currentDir, entry.name));
350
+ return isIgnoredDirName(entry.name, includeTests) ? [] : walk(join(currentDir, entry.name), includeTests);
315
351
  }
316
- return entry.isFile() && isScannable(entry.name) ? [join(currentDir, entry.name)] : [];
352
+ return entry.isFile() && isScannable(entry.name) && (includeTests || !isTestFileName(entry.name)) ? [join(currentDir, entry.name)] : [];
317
353
  };
318
- var walk = async (currentDir) => {
354
+ var walk = async (currentDir, includeTests) => {
319
355
  try {
320
356
  const entries = await readdir(currentDir, { withFileTypes: true });
321
- return (await Promise.all(entries.map((entry) => filesFromEntry(currentDir, entry)))).flat();
357
+ return (await Promise.all(entries.map((entry) => filesFromEntry(currentDir, entry, includeTests)))).flat();
322
358
  } catch (error) {
323
359
  throw new Error(`N\xE3o foi poss\xEDvel listar os arquivos em "${currentDir}".`, { cause: error });
324
360
  }
325
361
  };
326
- var findFiles = async (targetDir) => {
362
+ var findFiles = async (targetDir, includeTests = false) => {
327
363
  try {
328
- return await walk(targetDir);
364
+ return await walk(targetDir, includeTests);
329
365
  } catch (error) {
330
366
  throw new Error(`N\xE3o foi poss\xEDvel listar os arquivos em "${targetDir}".`, { cause: error });
331
367
  }
@@ -380,10 +416,10 @@ var scanFile = async (filePath, rules) => {
380
416
  throw new Error(`N\xE3o foi poss\xEDvel analisar o arquivo "${filePath}".`, { cause: error });
381
417
  }
382
418
  };
383
- var runScan = async (targetDir, rules, concurrency = DEFAULT_SCAN_CONCURRENCY) => {
419
+ var runScan = async (targetDir, rules, concurrency = DEFAULT_SCAN_CONCURRENCY, includeTests = false) => {
384
420
  try {
385
421
  const startedAt = Date.now();
386
- const files = await findFiles(targetDir);
422
+ const files = await findFiles(targetDir, includeTests);
387
423
  const findings = (await runWithConcurrencyLimit(files, concurrency, (filePath) => scanFile(filePath, rules))).flat();
388
424
  return {
389
425
  scannedFiles: files.length,
@@ -482,23 +518,16 @@ var mapSemgrepReportToFindings = (report) => report.results.map((finding) => ({
482
518
  severity: SEMGREP_SEVERITIES[finding.extra.severity] ?? "medium"
483
519
  }));
484
520
  var outputFromError = (error) => typeof error.stdout === "string" ? error.stdout : void 0;
485
- var semgrepArgs = (ruleset) => [
521
+ var excludeArgs = (names) => names.flatMap((name) => ["--exclude", name]);
522
+ var semgrepArgs = (ruleset, includeTests) => [
486
523
  "scan",
487
524
  "--config",
488
525
  ruleset,
489
526
  "--metrics=off",
490
527
  "--json",
491
528
  "--quiet",
492
- "--exclude",
493
- "node_modules",
494
- "--exclude",
495
- ".git",
496
- "--exclude",
497
- "dist",
498
- "--exclude",
499
- ".next",
500
- "--exclude",
501
- "tests",
529
+ ...excludeArgs(ALWAYS_IGNORED_DIR_NAMES),
530
+ ...includeTests ? [] : excludeArgs([...TEST_DIR_NAMES, ...TEST_FILE_GLOBS]),
502
531
  // Não repetir targetDir aqui: o processo já roda com cwd = targetDir,
503
532
  // então o alvo relativo a esse cwd é o diretório atual.
504
533
  "."
@@ -511,9 +540,9 @@ var semgrepEnvironment = (runtime) => {
511
540
  PATH: `${semgrepDir}${delimiter}${dirname2(semgrepDir)}${delimiter}${process.env.PATH ?? ""}${delimiter}${systemPathFallback}`
512
541
  };
513
542
  };
514
- var executeSemgrep = async (targetDir, runtime, ruleset, execute) => {
543
+ var executeSemgrep = async (targetDir, runtime, ruleset, execute, includeTests) => {
515
544
  try {
516
- const { stdout } = await execute(runtime.semgrep, semgrepArgs(ruleset), {
545
+ const { stdout } = await execute(runtime.semgrep, semgrepArgs(ruleset, includeTests), {
517
546
  cwd: targetDir,
518
547
  maxBuffer: 20 * 1024 * 1024,
519
548
  env: semgrepEnvironment(runtime)
@@ -527,10 +556,10 @@ var executeSemgrep = async (targetDir, runtime, ruleset, execute) => {
527
556
  throw new Error("N\xE3o foi poss\xEDvel executar o Semgrep embutido.", { cause: error });
528
557
  }
529
558
  };
530
- var runBundledSemgrep = async (targetDir, runtime = resolveBundledSemgrepRuntime(), ruleset = resolveBundledSemgrepRuleset(), execute = execFileAsync) => {
559
+ var runBundledSemgrep = async (targetDir, runtime = resolveBundledSemgrepRuntime(), ruleset = resolveBundledSemgrepRuleset(), execute = execFileAsync, includeTests = false) => {
531
560
  const startedAt = Date.now();
532
561
  try {
533
- const report = parseSemgrepReport(await executeSemgrep(targetDir, runtime, ruleset, execute));
562
+ const report = parseSemgrepReport(await executeSemgrep(targetDir, runtime, ruleset, execute, includeTests));
534
563
  const scannedFiles = report.paths?.scanned.length ?? 0;
535
564
  return {
536
565
  scannedFiles,
@@ -578,12 +607,13 @@ Relat\xF3rio detalhado gerado em: ${filePath}`));
578
607
  };
579
608
  var runScanEngines = async (path, rules, options) => {
580
609
  try {
581
- const nativeResult = await runScan(path, rules, options.concurrency);
610
+ const includeTests = options.tests ?? false;
611
+ const nativeResult = await runScan(path, rules, options.concurrency, includeTests);
582
612
  if (!options.semgrep) {
583
- return nativeResult;
613
+ return finalizeScanResult(nativeResult);
584
614
  }
585
- const semgrepResult = await runBundledSemgrep(path, void 0, options.config);
586
- return mergeScanResults(nativeResult, semgrepResult);
615
+ const semgrepResult = await runBundledSemgrep(path, void 0, options.config, void 0, includeTests);
616
+ return finalizeScanResult(mergeScanResults(nativeResult, semgrepResult));
587
617
  } catch (error) {
588
618
  throw new Error(`Falha durante a an\xE1lise: ${errorMessage2(error)}`, { cause: error });
589
619
  }
@@ -618,7 +648,9 @@ var scanAndReport = async (path, rules, taskTitle, options) => {
618
648
 
619
649
  // src/commands/command-injection/command-injection.command.ts
620
650
  var registerCommandInjectionCommand = (program) => {
621
- program.command("command-injection").description("Detecta child_process.exec()/execSync() recebendo entrada externa").argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
651
+ withScanOptions(
652
+ program.command("command-injection").description("Detecta child_process.exec()/execSync() recebendo entrada externa").argument("[path]", "diret\xF3rio a ser analisado", ".")
653
+ ).action(
622
654
  (path, options) => scanAndReport(path, [commandInjectionRule], "Checking command injection...", options)
623
655
  );
624
656
  };
@@ -717,7 +749,9 @@ var deepNestingRule = {
717
749
 
718
750
  // src/commands/deep-nesting/deep-nesting.command.ts
719
751
  var registerDeepNestingCommand = (program) => {
720
- program.command("deep-nesting").description("Detecta blocos aninhados al\xE9m do limite recomendado").argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
752
+ withScanOptions(
753
+ program.command("deep-nesting").description("Detecta blocos aninhados al\xE9m do limite recomendado").argument("[path]", "diret\xF3rio a ser analisado", ".")
754
+ ).action(
721
755
  (path, options) => scanAndReport(path, [deepNestingRule], "Checking nesting depth...", options)
722
756
  );
723
757
  };
@@ -829,7 +863,9 @@ var emptyCatchRule = {
829
863
 
830
864
  // src/commands/empty-catch/empty-catch.command.ts
831
865
  var registerEmptyCatchCommand = (program) => {
832
- program.command("empty-catch").description("Detecta blocos catch vazios").argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
866
+ withScanOptions(
867
+ program.command("empty-catch").description("Detecta blocos catch vazios").argument("[path]", "diret\xF3rio a ser analisado", ".")
868
+ ).action(
833
869
  (path, options) => scanAndReport(path, [emptyCatchRule], "Checking empty catch blocks...", options)
834
870
  );
835
871
  };
@@ -881,11 +917,85 @@ var expressMissingBodyLimitRule = {
881
917
 
882
918
  // src/commands/express-missing-body-limit/express-missing-body-limit.command.ts
883
919
  var registerExpressMissingBodyLimitCommand = (program) => {
884
- program.command("express-missing-body-limit").description("Detecta middlewares de body parsing do Express sem limite de tamanho de requisi\xE7\xE3o").argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
920
+ withScanOptions(
921
+ program.command("express-missing-body-limit").description("Detecta middlewares de body parsing do Express sem limite de tamanho de requisi\xE7\xE3o").argument("[path]", "diret\xF3rio a ser analisado", ".")
922
+ ).action(
885
923
  (path, options) => scanAndReport(path, [expressMissingBodyLimitRule], "Checking Express body limits...", options)
886
924
  );
887
925
  };
888
926
 
927
+ // src/rules/hardcoded-authorization-value.rule.ts
928
+ var HEADER_OR_COOKIE_NAME_PATTERN = /headers|cookies?/i;
929
+ var accessObjectName = (object) => {
930
+ const objectProperty = object?.type === "MemberExpression" ? object.property : object;
931
+ return objectProperty?.type === "Identifier" ? objectProperty.name : void 0;
932
+ };
933
+ var isHeaderOrCookieAccessCall = (node) => {
934
+ const callee = node?.callee;
935
+ const property = callee?.property;
936
+ const object = callee?.object;
937
+ return node?.type === "CallExpression" && callee?.type === "MemberExpression" && property?.type === "Identifier" && property.name === "get" && HEADER_OR_COOKIE_NAME_PATTERN.test(accessObjectName(object) ?? "");
938
+ };
939
+ var collectStringConstants = (sourceFile) => {
940
+ const constants = /* @__PURE__ */ new Map();
941
+ visitSourceNodes(sourceFile, (node) => {
942
+ if (node.type !== "VariableDeclarator") {
943
+ return;
944
+ }
945
+ const id = node.id;
946
+ const init = node.init;
947
+ if (id?.type === "Identifier" && init?.type === "StringLiteral") {
948
+ constants.set(id.name, init.value);
949
+ }
950
+ });
951
+ return constants;
952
+ };
953
+ var isConstantStringSide = (node, constants) => {
954
+ if (node?.type === "StringLiteral") {
955
+ return true;
956
+ }
957
+ return node?.type === "Identifier" && constants.has(node.name);
958
+ };
959
+ var isHardcodedAuthorizationComparison = (node, constants) => {
960
+ const left = node.left;
961
+ const right = node.right;
962
+ const isEquality = node.operator === "===" || node.operator === "==";
963
+ return node.type === "BinaryExpression" && isEquality && (isHeaderOrCookieAccessCall(left) && isConstantStringSide(right, constants) || isHeaderOrCookieAccessCall(right) && isConstantStringSide(left, constants));
964
+ };
965
+ var findHardcodedAuthorizationLines = (filePath, content) => {
966
+ const lines = /* @__PURE__ */ new Set();
967
+ const sourceFile = parseSourceFile(filePath, content);
968
+ const constants = collectStringConstants(sourceFile);
969
+ visitSourceNodes(sourceFile, (node) => {
970
+ if (isHardcodedAuthorizationComparison(node, constants) && node.loc) {
971
+ lines.add(node.loc.start.line);
972
+ }
973
+ });
974
+ return [...lines].sort((a, b) => a - b);
975
+ };
976
+ var hardcodedAuthorizationValueRule = {
977
+ id: "hardcoded-authorization-value",
978
+ description: "Detecta um header/cookie de requisi\xE7\xE3o comparado com um valor fixo no c\xF3digo para conceder acesso",
979
+ check(filePath, content) {
980
+ return findHardcodedAuthorizationLines(filePath, content).map((line) => ({
981
+ ruleId: "hardcoded-authorization-value",
982
+ message: "Autoriza\xE7\xE3o baseada em compara\xE7\xE3o de header/cookie com valor fixo no c\xF3digo-fonte",
983
+ file: filePath,
984
+ line,
985
+ severity: "critical"
986
+ }));
987
+ }
988
+ };
989
+
990
+ // src/commands/hardcoded-authorization-value/hardcoded-authorization-value.command.ts
991
+ var registerHardcodedAuthorizationValueCommand = (program) => {
992
+ withScanOptions(
993
+ program.command("hardcoded-authorization-value").description("Detecta compara\xE7\xE3o de headers/cookies de autoriza\xE7\xE3o com um valor fixo no c\xF3digo").argument("[path]", "diret\xF3rio a ser analisado", ".")
994
+ ).action(
995
+ (path, options) => scanAndReport(path, [hardcodedAuthorizationValueRule], "Checking hardcoded authorization values...", options)
996
+ );
997
+ };
998
+
889
999
  // src/commands/help/help.command.ts
890
1000
  import Table2 from "cli-table3";
891
1001
  var buildHelpTable = (commands) => {
@@ -1040,7 +1150,9 @@ var highComplexityRule = createFunctionStatementCountRule({
1040
1150
 
1041
1151
  // src/commands/high-complexity/high-complexity.command.ts
1042
1152
  var registerHighComplexityCommand = (program) => {
1043
- program.command("high-complexity").description("Detecta fun\xE7\xF5es com muitos condicionais/loops (complexidade alta)").argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
1153
+ withScanOptions(
1154
+ program.command("high-complexity").description("Detecta fun\xE7\xF5es com muitos condicionais/loops (complexidade alta)").argument("[path]", "diret\xF3rio a ser analisado", ".")
1155
+ ).action(
1044
1156
  (path, options) => scanAndReport(path, [highComplexityRule], "Checking function complexity...", options)
1045
1157
  );
1046
1158
  };
@@ -1190,7 +1302,9 @@ var insecureRandomTokenRule = {
1190
1302
 
1191
1303
  // src/commands/insecure-random-token/insecure-random-token.command.ts
1192
1304
  var registerInsecureRandomTokenCommand = (program) => {
1193
- program.command("insecure-random-token").description("Detecta o uso de Math.random() para gerar tokens/segredos previs\xEDveis").argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
1305
+ withScanOptions(
1306
+ program.command("insecure-random-token").description("Detecta o uso de Math.random() para gerar tokens/segredos previs\xEDveis").argument("[path]", "diret\xF3rio a ser analisado", ".")
1307
+ ).action(
1194
1308
  (path, options) => scanAndReport(path, [insecureRandomTokenRule], "Checking insecure random tokens...", options)
1195
1309
  );
1196
1310
  };
@@ -1274,13 +1388,12 @@ var jwtDecodeWithoutVerifyRule = {
1274
1388
 
1275
1389
  // src/commands/jwt-decode-without-verify/jwt-decode-without-verify.command.ts
1276
1390
  var registerJwtDecodeWithoutVerifyCommand = (program) => {
1277
- program.command("jwt-decode-without-verify").description("Detecta decodifica\xE7\xE3o manual de um token (base64 + JSON.parse) sem verificar a assinatura").argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
1278
- (path, options) => scanAndReport(
1279
- path,
1280
- [jwtDecodeWithoutVerifyRule],
1281
- "Checking JWT decode without verification...",
1282
- options
1283
- )
1391
+ withScanOptions(
1392
+ program.command("jwt-decode-without-verify").description(
1393
+ "Detecta decodifica\xE7\xE3o manual de um token (base64 + JSON.parse) sem verificar a assinatura"
1394
+ ).argument("[path]", "diret\xF3rio a ser analisado", ".")
1395
+ ).action(
1396
+ (path, options) => scanAndReport(path, [jwtDecodeWithoutVerifyRule], "Checking JWT decode without verification...", options)
1284
1397
  );
1285
1398
  };
1286
1399
 
@@ -1333,7 +1446,9 @@ var jwtNoExpirationRule = {
1333
1446
 
1334
1447
  // src/commands/jwt-no-expiration/jwt-no-expiration.command.ts
1335
1448
  var registerJwtNoExpirationCommand = (program) => {
1336
- program.command("jwt-no-expiration").description("Detecta jwt.sign() sem expira\xE7\xE3o configurada").argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
1449
+ withScanOptions(
1450
+ program.command("jwt-no-expiration").description("Detecta jwt.sign() sem expira\xE7\xE3o configurada").argument("[path]", "diret\xF3rio a ser analisado", ".")
1451
+ ).action(
1337
1452
  (path, options) => scanAndReport(path, [jwtNoExpirationRule], "Checking JWT expiration...", options)
1338
1453
  );
1339
1454
  };
@@ -1386,7 +1501,9 @@ var longFunctionRule = {
1386
1501
 
1387
1502
  // src/commands/long-functions/long-functions.command.ts
1388
1503
  var registerLongFunctionsCommand = (program) => {
1389
- program.command("long-functions").description("Detecta fun\xE7\xF5es com mais de 30 linhas").argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
1504
+ withScanOptions(
1505
+ program.command("long-functions").description("Detecta fun\xE7\xF5es com mais de 30 linhas").argument("[path]", "diret\xF3rio a ser analisado", ".")
1506
+ ).action(
1390
1507
  (path, options) => scanAndReport(path, [longFunctionRule], "Checking function length...", options)
1391
1508
  );
1392
1509
  };
@@ -1418,7 +1535,9 @@ var noAnyRule = {
1418
1535
 
1419
1536
  // src/commands/no-any/no-any.command.ts
1420
1537
  var registerNoAnyCommand = (program) => {
1421
- program.command("no-any").description('Detecta o uso do tipo "any" no TypeScript').argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
1538
+ withScanOptions(
1539
+ program.command("no-any").description('Detecta o uso do tipo "any" no TypeScript').argument("[path]", "diret\xF3rio a ser analisado", ".")
1540
+ ).action(
1422
1541
  (path, options) => scanAndReport(path, [noAnyRule], "Checking any usage...", options)
1423
1542
  );
1424
1543
  };
@@ -1466,7 +1585,9 @@ var noEvalRule = {
1466
1585
 
1467
1586
  // src/commands/no-eval/no-eval.command.ts
1468
1587
  var registerNoEvalCommand = (program) => {
1469
- program.command("no-eval").description("Detecta o uso de eval() ou new Function()").argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
1588
+ withScanOptions(
1589
+ program.command("no-eval").description("Detecta o uso de eval() ou new Function()").argument("[path]", "diret\xF3rio a ser analisado", ".")
1590
+ ).action(
1470
1591
  (path, options) => scanAndReport(path, [noEvalRule], "Checking eval/new Function usage...", options)
1471
1592
  );
1472
1593
  };
@@ -1534,7 +1655,9 @@ var noHardcodedSecretRule = {
1534
1655
 
1535
1656
  // src/commands/no-hardcoded-secret/no-hardcoded-secret.command.ts
1536
1657
  var registerNoHardcodedSecretCommand = (program) => {
1537
- program.command("no-hardcoded-secret").description("Detecta segredos/credenciais hardcoded no c\xF3digo-fonte").argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
1658
+ withScanOptions(
1659
+ program.command("no-hardcoded-secret").description("Detecta segredos/credenciais hardcoded no c\xF3digo-fonte").argument("[path]", "diret\xF3rio a ser analisado", ".")
1660
+ ).action(
1538
1661
  (path, options) => scanAndReport(path, [noHardcodedSecretRule], "Checking hardcoded secrets...", options)
1539
1662
  );
1540
1663
  };
@@ -1597,7 +1720,9 @@ var permissiveCorsRule = {
1597
1720
 
1598
1721
  // src/commands/permissive-cors/permissive-cors.command.ts
1599
1722
  var registerPermissiveCorsCommand = (program) => {
1600
- program.command("permissive-cors").description("Detecta CORS configurado para permitir qualquer origem").argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
1723
+ withScanOptions(
1724
+ program.command("permissive-cors").description("Detecta CORS configurado para permitir qualquer origem").argument("[path]", "diret\xF3rio a ser analisado", ".")
1725
+ ).action(
1601
1726
  (path, options) => scanAndReport(path, [permissiveCorsRule], "Checking permissive CORS...", options)
1602
1727
  );
1603
1728
  };
@@ -1642,7 +1767,11 @@ var publicEnvVarSecretRule = {
1642
1767
 
1643
1768
  // src/commands/public-env-var-secret/public-env-var-secret.command.ts
1644
1769
  var registerPublicEnvVarSecretCommand = (program) => {
1645
- program.command("public-env-var-secret").description("Detecta uma vari\xE1vel de ambiente p\xFAblica (NEXT_PUBLIC_/VITE_/REACT_APP_) com nome de segredo").argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
1770
+ withScanOptions(
1771
+ program.command("public-env-var-secret").description(
1772
+ "Detecta uma vari\xE1vel de ambiente p\xFAblica (NEXT_PUBLIC_/VITE_/REACT_APP_) com nome de segredo"
1773
+ ).argument("[path]", "diret\xF3rio a ser analisado", ".")
1774
+ ).action(
1646
1775
  (path, options) => scanAndReport(path, [publicEnvVarSecretRule], "Checking public env var secrets...", options)
1647
1776
  );
1648
1777
  };
@@ -2143,6 +2272,40 @@ var unsafeSqlRule = {
2143
2272
  }
2144
2273
  };
2145
2274
 
2275
+ // src/rules/weak-cipher-mode.rule.ts
2276
+ var CIPHER_METHOD_NAMES = /* @__PURE__ */ new Set(["createCipheriv", "createDecipheriv"]);
2277
+ var WEAK_MODE_PATTERN = /-(cbc|ecb)$/i;
2278
+ var isWeakCipherModeCall = (node) => {
2279
+ const callee = node.callee;
2280
+ const property = callee?.property;
2281
+ const args = node.arguments;
2282
+ const algorithm = args?.[0];
2283
+ return node.type === "CallExpression" && callee?.type === "MemberExpression" && property?.type === "Identifier" && CIPHER_METHOD_NAMES.has(property.name) && algorithm?.type === "StringLiteral" && WEAK_MODE_PATTERN.test(algorithm.value);
2284
+ };
2285
+ var findWeakCipherModeLines = (filePath, content) => {
2286
+ const lines = /* @__PURE__ */ new Set();
2287
+ const sourceFile = parseSourceFile(filePath, content);
2288
+ visitSourceNodes(sourceFile, (node) => {
2289
+ if (isWeakCipherModeCall(node) && node.loc) {
2290
+ lines.add(node.loc.start.line);
2291
+ }
2292
+ });
2293
+ return [...lines].sort((a, b) => a - b);
2294
+ };
2295
+ var weakCipherModeRule = {
2296
+ id: "weak-cipher-mode",
2297
+ description: "Detecta cifragem em modo n\xE3o autenticado (CBC/ECB) sem AEAD/HMAC associado",
2298
+ check(filePath, content) {
2299
+ return findWeakCipherModeLines(filePath, content).map((line) => ({
2300
+ ruleId: "weak-cipher-mode",
2301
+ message: "Modo de cifra sem autentica\xE7\xE3o (CBC/ECB) \u2014 considere um modo AEAD como GCM",
2302
+ file: filePath,
2303
+ line,
2304
+ severity: "high"
2305
+ }));
2306
+ }
2307
+ };
2308
+
2146
2309
  // src/rules/weak-hash-algorithm.rule.ts
2147
2310
  var WEAK_ALGORITHMS = /^(md5|sha1)$/i;
2148
2311
  var isWeakCreateHashCall = (node) => {
@@ -2352,7 +2515,9 @@ var allRules = [
2352
2515
  jwtDecodeWithoutVerifyRule,
2353
2516
  xxeUnsafeXmlParsingRule,
2354
2517
  sensitiveDataInLogsRule,
2355
- publicEnvVarSecretRule
2518
+ publicEnvVarSecretRule,
2519
+ weakCipherModeRule,
2520
+ hardcodedAuthorizationValueRule
2356
2521
  ];
2357
2522
 
2358
2523
  // src/commands/rules/rules.command.ts
@@ -2382,70 +2547,90 @@ var parseLocalSemgrepConfig = (value) => {
2382
2547
  return resolve2(value);
2383
2548
  };
2384
2549
  var registerScanCommand = (program) => {
2385
- program.command("scan").description("Analisa um diret\xF3rio em busca de vulnerabilidades e problemas de qualidade").argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").option("--concurrency <n>", "limita arquivos processados em paralelo", parseConcurrency).option("--config <file>", "usa um ruleset Semgrep YAML local", parseLocalSemgrepConfig).action(
2550
+ withScanOptions(
2551
+ program.command("scan").description("Analisa um diret\xF3rio em busca de vulnerabilidades e problemas de qualidade").argument("[path]", "diret\xF3rio a ser analisado", ".").option("--concurrency <n>", "limita arquivos processados em paralelo", parseConcurrency).option("--config <file>", "usa um ruleset Semgrep YAML local", parseLocalSemgrepConfig)
2552
+ ).action(
2386
2553
  (path, options) => scanAndReport(path, allRules, "Scanning files...", { ...options, semgrep: true })
2387
2554
  );
2388
2555
  };
2389
2556
 
2390
2557
  // src/commands/security-lint/security-lint.command.ts
2391
2558
  var registerSecurityLintCommand = (program) => {
2392
- program.command("security-lint").description("Detecta padr\xF5es de seguran\xE7a gen\xE9ricos via eslint-plugin-security").argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
2559
+ withScanOptions(
2560
+ program.command("security-lint").description("Detecta padr\xF5es de seguran\xE7a gen\xE9ricos via eslint-plugin-security").argument("[path]", "diret\xF3rio a ser analisado", ".")
2561
+ ).action(
2393
2562
  (path, options) => scanAndReport(path, [securityLintRule], "Checking generic security patterns...", options)
2394
2563
  );
2395
2564
  };
2396
2565
 
2397
2566
  // src/commands/sensitive-data-in-logs/sensitive-data-in-logs.command.ts
2398
2567
  var registerSensitiveDataInLogsCommand = (program) => {
2399
- program.command("sensitive-data-in-logs").description("Detecta senhas/segredos/tokens sendo passados para chamadas de log").argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
2568
+ withScanOptions(
2569
+ program.command("sensitive-data-in-logs").description("Detecta senhas/segredos/tokens sendo passados para chamadas de log").argument("[path]", "diret\xF3rio a ser analisado", ".")
2570
+ ).action(
2400
2571
  (path, options) => scanAndReport(path, [sensitiveDataInLogsRule], "Checking sensitive data in logs...", options)
2401
2572
  );
2402
2573
  };
2403
2574
 
2404
2575
  // src/commands/tls-validation-disabled/tls-validation-disabled.command.ts
2405
2576
  var registerTlsValidationDisabledCommand = (program) => {
2406
- program.command("tls-validation-disabled").description("Detecta a desativa\xE7\xE3o da valida\xE7\xE3o de certificados TLS").argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
2577
+ withScanOptions(
2578
+ program.command("tls-validation-disabled").description("Detecta a desativa\xE7\xE3o da valida\xE7\xE3o de certificados TLS").argument("[path]", "diret\xF3rio a ser analisado", ".")
2579
+ ).action(
2407
2580
  (path, options) => scanAndReport(path, [tlsValidationDisabledRule], "Checking TLS validation...", options)
2408
2581
  );
2409
2582
  };
2410
2583
 
2411
2584
  // src/commands/too-many-for-loops/too-many-for-loops.command.ts
2412
2585
  var registerTooManyForLoopsCommand = (program) => {
2413
- program.command("too-many-for-loops").description('Detecta fun\xE7\xF5es com muitos loops "for"/"for-in"/"for-of"').argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
2586
+ withScanOptions(
2587
+ program.command("too-many-for-loops").description('Detecta fun\xE7\xF5es com muitos loops "for"/"for-in"/"for-of"').argument("[path]", "diret\xF3rio a ser analisado", ".")
2588
+ ).action(
2414
2589
  (path, options) => scanAndReport(path, [tooManyForLoopsRule], "Checking for-loop count...", options)
2415
2590
  );
2416
2591
  };
2417
2592
 
2418
2593
  // src/commands/too-many-ifs/too-many-ifs.command.ts
2419
2594
  var registerTooManyIfsCommand = (program) => {
2420
- program.command("too-many-ifs").description('Detecta fun\xE7\xF5es com muitos "if" (incluindo "else if")').argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
2595
+ withScanOptions(
2596
+ program.command("too-many-ifs").description('Detecta fun\xE7\xF5es com muitos "if" (incluindo "else if")').argument("[path]", "diret\xF3rio a ser analisado", ".")
2597
+ ).action(
2421
2598
  (path, options) => scanAndReport(path, [tooManyIfsRule], "Checking if count...", options)
2422
2599
  );
2423
2600
  };
2424
2601
 
2425
2602
  // src/commands/too-many-switch-cases/too-many-switch-cases.command.ts
2426
2603
  var registerTooManySwitchCasesCommand = (program) => {
2427
- program.command("too-many-switch-cases").description('Detecta "switch" com muitos "case" (considere um mapa/lookup)').argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
2604
+ withScanOptions(
2605
+ program.command("too-many-switch-cases").description('Detecta "switch" com muitos "case" (considere um mapa/lookup)').argument("[path]", "diret\xF3rio a ser analisado", ".")
2606
+ ).action(
2428
2607
  (path, options) => scanAndReport(path, [tooManySwitchCasesRule], "Checking switch cases...", options)
2429
2608
  );
2430
2609
  };
2431
2610
 
2432
2611
  // src/commands/too-many-try-catch/too-many-try-catch.command.ts
2433
2612
  var registerTooManyTryCatchCommand = (program) => {
2434
- program.command("too-many-try-catch").description('Detecta fun\xE7\xF5es com muitos blocos "try/catch"').argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
2613
+ withScanOptions(
2614
+ program.command("too-many-try-catch").description('Detecta fun\xE7\xF5es com muitos blocos "try/catch"').argument("[path]", "diret\xF3rio a ser analisado", ".")
2615
+ ).action(
2435
2616
  (path, options) => scanAndReport(path, [tooManyTryCatchRule], "Checking try/catch count...", options)
2436
2617
  );
2437
2618
  };
2438
2619
 
2439
2620
  // src/commands/too-many-while-loops/too-many-while-loops.command.ts
2440
2621
  var registerTooManyWhileLoopsCommand = (program) => {
2441
- program.command("too-many-while-loops").description('Detecta fun\xE7\xF5es com muitos loops "while"/"do-while"').argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
2622
+ withScanOptions(
2623
+ program.command("too-many-while-loops").description('Detecta fun\xE7\xF5es com muitos loops "while"/"do-while"').argument("[path]", "diret\xF3rio a ser analisado", ".")
2624
+ ).action(
2442
2625
  (path, options) => scanAndReport(path, [tooManyWhileLoopsRule], "Checking while-loop count...", options)
2443
2626
  );
2444
2627
  };
2445
2628
 
2446
2629
  // src/commands/unhandled-promises/unhandled-promises.command.ts
2447
2630
  var registerUnhandledPromisesCommand = (program) => {
2448
- program.command("unhandled-promises").description("Detecta promises sem tratamento (sem .catch, await sem try/catch ou promises soltas)").argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
2631
+ withScanOptions(
2632
+ program.command("unhandled-promises").description("Detecta promises sem tratamento (sem .catch, await sem try/catch ou promises soltas)").argument("[path]", "diret\xF3rio a ser analisado", ".")
2633
+ ).action(
2449
2634
  (path, options) => scanAndReport(
2450
2635
  path,
2451
2636
  [promiseNoCatchRule, awaitNoTryCatchRule, floatingPromiseRule],
@@ -2457,37 +2642,56 @@ var registerUnhandledPromisesCommand = (program) => {
2457
2642
 
2458
2643
  // src/commands/unsafe-sql/unsafe-sql.command.ts
2459
2644
  var registerUnsafeSqlCommand = (program) => {
2460
- program.command("unsafe-sql").description("Detecta concatena\xE7\xE3o insegura de SQL (risco de SQL injection)").argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
2645
+ withScanOptions(
2646
+ program.command("unsafe-sql").description("Detecta concatena\xE7\xE3o insegura de SQL (risco de SQL injection)").argument("[path]", "diret\xF3rio a ser analisado", ".")
2647
+ ).action(
2461
2648
  (path, options) => scanAndReport(path, [unsafeSqlRule], "Checking unsafe SQL...", options)
2462
2649
  );
2463
2650
  };
2464
2651
 
2652
+ // src/commands/weak-cipher-mode/weak-cipher-mode.command.ts
2653
+ var registerWeakCipherModeCommand = (program) => {
2654
+ withScanOptions(
2655
+ program.command("weak-cipher-mode").description("Detecta o uso de modos de cifra fracos (CBC, ECB) em createCipheriv/createDecipheriv").argument("[path]", "diret\xF3rio a ser analisado", ".")
2656
+ ).action(
2657
+ (path, options) => scanAndReport(path, [weakCipherModeRule], "Checking weak cipher modes...", options)
2658
+ );
2659
+ };
2660
+
2465
2661
  // src/commands/weak-hash-algorithm/weak-hash-algorithm.command.ts
2466
2662
  var registerWeakHashAlgorithmCommand = (program) => {
2467
- program.command("weak-hash-algorithm").description("Detecta o uso de algoritmos de hash fracos (MD5, SHA-1)").argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
2663
+ withScanOptions(
2664
+ program.command("weak-hash-algorithm").description("Detecta o uso de algoritmos de hash fracos (MD5, SHA-1)").argument("[path]", "diret\xF3rio a ser analisado", ".")
2665
+ ).action(
2468
2666
  (path, options) => scanAndReport(path, [weakHashAlgorithmRule], "Checking weak hash algorithms...", options)
2469
2667
  );
2470
2668
  };
2471
2669
 
2472
2670
  // src/commands/weak-secret-fallback/weak-secret-fallback.command.ts
2473
2671
  var registerWeakSecretFallbackCommand = (program) => {
2474
- program.command("weak-secret-fallback").description(
2475
- "Detecta uma vari\xE1vel de ambiente de segredo/chave com um valor hardcoded como fallback (|| ou ??)"
2476
- ).argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
2672
+ withScanOptions(
2673
+ program.command("weak-secret-fallback").description(
2674
+ "Detecta uma vari\xE1vel de ambiente de segredo/chave com um valor hardcoded como fallback (|| ou ??)"
2675
+ ).argument("[path]", "diret\xF3rio a ser analisado", ".")
2676
+ ).action(
2477
2677
  (path, options) => scanAndReport(path, [weakSecretFallbackRule], "Checking weak secret fallbacks...", options)
2478
2678
  );
2479
2679
  };
2480
2680
 
2481
2681
  // src/commands/xss/xss.command.ts
2482
2682
  var registerXssCommand = (program) => {
2483
- program.command("xss").description("Detecta sinks perigosos de XSS (innerHTML, document.write, etc.)").argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
2683
+ withScanOptions(
2684
+ program.command("xss").description("Detecta sinks perigosos de XSS (innerHTML, document.write, etc.)").argument("[path]", "diret\xF3rio a ser analisado", ".")
2685
+ ).action(
2484
2686
  (path, options) => scanAndReport(path, [xssRule], "Checking XSS sinks...", options)
2485
2687
  );
2486
2688
  };
2487
2689
 
2488
2690
  // src/commands/xxe-unsafe-xml-parsing/xxe-unsafe-xml-parsing.command.ts
2489
2691
  var registerXxeUnsafeXmlParsingCommand = (program) => {
2490
- program.command("xxe-unsafe-xml-parsing").description("Detecta parsing de XML com noent/dtdload habilitados (risco de XXE)").argument("[path]", "diret\xF3rio a ser analisado", ".").option("--json", "exibe o resultado em JSON").action(
2692
+ withScanOptions(
2693
+ program.command("xxe-unsafe-xml-parsing").description("Detecta parsing de XML com noent/dtdload habilitados (risco de XXE)").argument("[path]", "diret\xF3rio a ser analisado", ".")
2694
+ ).action(
2491
2695
  (path, options) => scanAndReport(path, [xxeUnsafeXmlParsingRule], "Checking unsafe XML parsing...", options)
2492
2696
  );
2493
2697
  };
@@ -2537,6 +2741,8 @@ var registerSecurityCommands = (program) => {
2537
2741
  registerXxeUnsafeXmlParsingCommand(program);
2538
2742
  registerSensitiveDataInLogsCommand(program);
2539
2743
  registerPublicEnvVarSecretCommand(program);
2744
+ registerWeakCipherModeCommand(program);
2745
+ registerHardcodedAuthorizationValueCommand(program);
2540
2746
  };
2541
2747
  var require5 = createRequire4(import.meta.url);
2542
2748
  var readPackageVersion = () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codesentry",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "workspaces": [
5
5
  "packages/semgrep-rules"
6
6
  ],
@@ -48,7 +48,7 @@
48
48
  "@clack/prompts": "^1.7.0",
49
49
  "chalk": "^6.0.0",
50
50
  "cli-table3": "^0.6.5",
51
- "codesentry-semgrep-rules": "0.1.10",
51
+ "codesentry-semgrep-rules": "0.1.11",
52
52
  "commander": "^15.0.0",
53
53
  "eslint": "^10.10.0",
54
54
  "eslint-plugin-no-unsanitized": "^4.1.5",
@@ -59,8 +59,8 @@
59
59
  "p-limit": "^7.3.2"
60
60
  },
61
61
  "optionalDependencies": {
62
- "codesentry-semgrep-linux-x64": "0.1.10",
63
- "codesentry-semgrep-win32-x64": "0.1.10"
62
+ "codesentry-semgrep-linux-x64": "0.1.11",
63
+ "codesentry-semgrep-win32-x64": "0.1.11"
64
64
  },
65
65
  "devDependencies": {
66
66
  "@types/node": "^26.4.1",