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.
- package/README.md +12 -2
- package/dist/index.js +287 -81
- 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 [
|
|
287
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
493
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1278
|
-
(
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2475
|
-
"
|
|
2476
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
63
|
-
"codesentry-semgrep-win32-x64": "0.1.
|
|
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",
|