mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
ci(quality): make zizmor/gitleaks/osv scanners functional + freeze advisory baselines (#3947)
Three CI security gates in the quality-extended job never produced a value;
diagnose + fix each, then freeze the real measured numbers as advisory ratchet
baselines (dedicatedGate => SKIP in the blocking quality-gate ratchet).
FIX 1 — .zizmor.yml: migrate the config from the pre-1.0 'ignores: []' schema to
the 'rules: {}' schema. zizmor 1.25.2 rejected the old field ('unknown field
`ignores`, expected `rules`') and performed NO audit. Now check:workflows emits
zizmorFindings=195.
FIX 2 — scripts/check/check-secrets.mjs: the gate ran 'gitleaks detect --no-git
--source .', which walks the WHOLE tree including a real node_modules/ (90k+ files
under npm ci) and times out (ETIMEDOUT) — gitleaks has no traversal-exclude flag
(.gitleaks.toml paths filter findings AFTER reading). Scope the scan to the source
dirs (src/open-sse/bin/electron/scripts), one 'gitleaks dir <dir>' invocation each
('gitleaks dir' takes a single path; multiple args fall back to scanning the CWD).
Also fix .gitleaks.toml: it lacked [extend].useDefault=true, so the custom config
REPLACED the default ruleset with zero rules and detected nothing — the gate always
reported 0 regardless of real secrets. Now: ~10s (was 120s timeout), secretFindings=3
(generic-api-key false positives in beta-header strings / column names).
FIX 3 — .github/workflows/ci.yml: the scanner install resolved release URLs via
unauthenticated api.github.com (60 req/hr/IP; returns empty when throttled -> silent
no-op install -> every gate self-skips). Switch gitleaks + osv-scanner to 'gh release
download' (preinstalled + GITHUB_TOKEN-authed, 5000 req/hr); add GH_TOKEN to the step
env. actionlint/zizmor install paths unchanged.
MEASURE + FREEZE (advisory, dedicatedGate:true, direction down) in
config/quality/quality-baseline.json: secretFindings=3, zizmorFindings=195,
vulnCount=13 (LOW=4/MOD=7/HIGH=2), bundleSize=5601. Seeded from a local run with the
real binaries on PATH (2026-06-15). They stay advisory (SKIP in the blocking ratchet;
quality-extended is continue-on-error) until a green CI run confirms the fixed tooling
produces values; the flip to blocking is a follow-up PR. continue-on-error untouched.
Validated locally: zizmor --config parses; check:secrets <60s + real count;
check:workflows/check:vuln-ratchet emit real numbers; ci.yml actionlint-clean; baseline
JSON valid; 103 build-scanner unit tests + 19 check-secrets + 18 quality-ratchet pass;
the 4 keys SKIP in the ratchet. FIX 3 logic is sound but CI-only (cannot run gh release
download against the runner locally).
This commit is contained in:
committed by
GitHub
parent
28d57bf5f8
commit
8981b322d7
39
.github/workflows/ci.yml
vendored
39
.github/workflows/ci.yml
vendored
@@ -162,26 +162,35 @@ jobs:
|
||||
# CodeQL ratchet foi PROMOVIDO a BLOQUEANTE no job quality-gate (v3.8.26) —
|
||||
# não roda aqui para evitar duplo run/duplo report.
|
||||
# Install the advisory security scanners so the gates below actually run
|
||||
# (they self-skip when the binaries are absent). Robustness over `go install`:
|
||||
# `go install github.com/gitleaks/gitleaks/v8@latest` produces a binary WITHOUT
|
||||
# the version ldflags gitleaks needs (and often fails) — under `bash -e` that
|
||||
# aborts the whole step before the PATH export, so NO scanner lands on PATH and
|
||||
# every check self-skips. Here we `set +e` (no single failure aborts the step),
|
||||
# install from official RELEASE DOWNLOADS, ALWAYS export $GITHUB_PATH at the end,
|
||||
# and print diagnostics so the next CI run proves exactly what installed. The job
|
||||
# is continue-on-error too, so an install hiccup never blocks the build.
|
||||
# (they self-skip when the binaries are absent). Robustness lessons baked in:
|
||||
# • `go install …/gitleaks/v8@latest` produces a binary WITHOUT the version
|
||||
# ldflags gitleaks needs (and often fails) — avoided.
|
||||
# • `curl …api.github.com/…/releases/latest` is UNAUTHENTICATED and
|
||||
# rate-limited to 60 req/hr/IP; when throttled it returns an empty body,
|
||||
# so the asset URL resolves to nothing and the install silently no-ops —
|
||||
# every gate then self-skips and the metric is never produced. We instead
|
||||
# use `gh release download`, which is preinstalled on GitHub runners and
|
||||
# authenticated via GITHUB_TOKEN (5000 req/hr) — robust under load.
|
||||
# • actionlint keeps its official download script; zizmor stays on pipx.
|
||||
# We `set +e` (no single failure aborts the step), ALWAYS export $GITHUB_PATH
|
||||
# at the end, and print diagnostics so the next CI run proves exactly what
|
||||
# installed. The job is continue-on-error too, so an install hiccup never
|
||||
# blocks the build.
|
||||
- name: Install advisory security scanners (gitleaks/osv/actionlint/zizmor)
|
||||
continue-on-error: true
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set +e
|
||||
mkdir -p "$HOME/.local/bin"
|
||||
# gitleaks — resolve latest linux x64 tarball, extract just the binary
|
||||
GL=$(curl -fsSL https://api.github.com/repos/gitleaks/gitleaks/releases/latest | grep -oE 'https://[^"]+linux_x64\.tar\.gz' | head -1)
|
||||
curl -fsSL "$GL" | tar -xz -C "$HOME/.local/bin" gitleaks
|
||||
# osv-scanner — resolve latest linux amd64 binary
|
||||
OSV=$(curl -fsSL https://api.github.com/repos/google/osv-scanner/releases/latest | grep -oE 'https://[^"]+linux_amd64' | head -1)
|
||||
curl -fsSL -o "$HOME/.local/bin/osv-scanner" "$OSV"
|
||||
chmod +x "$HOME/.local/bin/osv-scanner"
|
||||
# gitleaks — download latest linux x64 tarball via gh (authed), extract binary
|
||||
rm -rf /tmp/gl && mkdir -p /tmp/gl
|
||||
gh release download --repo gitleaks/gitleaks --pattern '*linux_x64.tar.gz' --dir /tmp/gl
|
||||
tar -xzf /tmp/gl/*linux_x64.tar.gz -C "$HOME/.local/bin" gitleaks
|
||||
# osv-scanner — download latest linux amd64 bare binary via gh (authed)
|
||||
rm -rf /tmp/osv && mkdir -p /tmp/osv
|
||||
gh release download --repo google/osv-scanner --pattern '*linux_amd64' --dir /tmp/osv
|
||||
install -m 0755 /tmp/osv/*linux_amd64 "$HOME/.local/bin/osv-scanner"
|
||||
# actionlint — official download script
|
||||
bash <(curl -fsSL https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) latest "$HOME/.local/bin"
|
||||
# zizmor — PyPI (pipx preferred, pip --user fallback); lands in ~/.local/bin
|
||||
|
||||
@@ -15,7 +15,15 @@
|
||||
# Referência: docs/security/PUBLIC_CREDS.md (credenciais OAuth públicas conhecidas)
|
||||
# CLAUDE.md Hard Rule #11 (resolvePublicCred obrigatório)
|
||||
|
||||
# Usar as regras padrão do gitleaks (não declaramos [rules] aqui para herdar tudo)
|
||||
# Herdar TODAS as regras padrão do gitleaks. ATENÇÃO: um config customizado
|
||||
# SEM [extend].useDefault = true (e sem [[rules]] próprias) resulta em ZERO
|
||||
# regras — o gitleaks SUBSTITUI o ruleset padrão pelo arquivo, não o estende
|
||||
# automaticamente. Sem esta seção, `gitleaks --config .gitleaks.toml` nunca
|
||||
# detecta nada (todo finding vira 0), tornando o gate inerte. Com useDefault,
|
||||
# a allowlist abaixo é aplicada POR CIMA das ~170 regras padrão.
|
||||
[extend]
|
||||
useDefault = true
|
||||
|
||||
# Para desabilitar uma regra específica, usar:
|
||||
# [[rules]]
|
||||
# id = "rule-id"
|
||||
|
||||
36
.zizmor.yml
36
.zizmor.yml
@@ -30,22 +30,30 @@
|
||||
# Uncomment to pin to a specific minimum severity level.
|
||||
# min-severity: low # low | medium | high | critical (default: low = all)
|
||||
|
||||
# ── Per-finding ignores ──────────────────────────────────────────────────────
|
||||
# ── Per-rule ignores ─────────────────────────────────────────────────────────
|
||||
# zizmor ≥1.0 replaced the old top-level `ignores:` list with a `rules:` map
|
||||
# keyed by audit id. Per-rule config lives under `rules.<audit-id>.ignore`,
|
||||
# where each entry is `filename` or `filename:line` (line optional, column
|
||||
# further-optional). See: https://docs.zizmor.sh/configuration/
|
||||
#
|
||||
# Format:
|
||||
# ignores:
|
||||
# - id: <zizmor-audit-id> # e.g. "unpinned-uses", "script-injection"
|
||||
# reason: "<justification>"
|
||||
# # Optional: scope to a specific workflow or step
|
||||
# # workflow: .github/workflows/ci.yml
|
||||
# rules:
|
||||
# <zizmor-audit-id>: # e.g. "unpinned-uses", "template-injection"
|
||||
# ignore:
|
||||
# - <workflow-filename> # ignore this audit across the file
|
||||
# - <workflow-filename>:<line> # or scope to a specific line
|
||||
#
|
||||
# Example (do not uncomment without real justification):
|
||||
#
|
||||
# ignores:
|
||||
# - id: unpinned-uses
|
||||
# reason: >
|
||||
# actions/checkout@v6 is pinned at the major-version tag intentionally:
|
||||
# GitHub-managed first-party action; SHA pinning buys little against a
|
||||
# GitHub-side compromise and adds significant maintenance burden.
|
||||
# workflow: .github/workflows/ci.yml
|
||||
# rules:
|
||||
# unpinned-uses:
|
||||
# # actions/checkout@v6 is pinned at the major-version tag intentionally:
|
||||
# # GitHub-managed first-party action; SHA pinning buys little against a
|
||||
# # GitHub-side compromise and adds significant maintenance burden.
|
||||
# ignore:
|
||||
# - ci.yml
|
||||
|
||||
ignores: []
|
||||
# BASELINE: empty rules map = nothing ignored. Every future ignore requires an
|
||||
# explicit per-rule entry above with a justification comment (same stale-review
|
||||
# discipline as the other allowlists in this project).
|
||||
rules: {}
|
||||
|
||||
@@ -109,6 +109,26 @@
|
||||
"value": 0,
|
||||
"direction": "down",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"secretFindings": {
|
||||
"value": 3,
|
||||
"direction": "down",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"zizmorFindings": {
|
||||
"value": 195,
|
||||
"direction": "down",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"vulnCount": {
|
||||
"value": 13,
|
||||
"direction": "down",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"bundleSize": {
|
||||
"value": 5601,
|
||||
"direction": "down",
|
||||
"dedicatedGate": true
|
||||
}
|
||||
},
|
||||
"_coverage_note": "Pisos anti-flake ~2pt abaixo do real do CI mergeado MEDIDO COM os 135 testes religados (run 27247237268: statements 78.4 / lines 78.4 / functions 83.84 / branches 75.73). O religamento da 6A.1 HONESTIFICOU a regua: os ~82.5 anteriores eram inflados porque modulos nunca importados ficavam fora do denominador do c8. Apertar via --require-tighten na Fase 6A (2026-06-16).",
|
||||
@@ -118,6 +138,7 @@
|
||||
"_eslint_rebaseline_2026_06_13_v3825": "3658 -> 3669: +11 drift consciente do ciclo v3.8.24->v3.8.25 (features #3799-#3806: free-provider-rankings, plugins menu, proxy IP-family). Medido em release/v3.8.25 (e38d22512). Crescimento de feature legitima, nao regressao; apertar via --require-tighten no fim do ciclo.",
|
||||
"_eslint_rebaseline_2026_06_15_release_v3826": "3669 -> 3760. Medido em origin/release/v3.8.26 e neste PR com npm run quality:collect: ambos retornam 3760 warnings, portanto este PR e neutro; o drift ja existe na base release/v3.8.26.",
|
||||
"_quality_rebaseline_2026_06_15_release_v3826": "deadExports 327 -> 339 e cognitiveComplexity 738 -> 753. Medido em origin/release/v3.8.26 e neste PR com os dedicated gates: ambos retornam os mesmos valores, portanto este PR e neutro; typeCoveragePct permanece acima do baseline.",
|
||||
"_scanner_baselines_seeded_2026_06_15": "secretFindings (3), zizmorFindings (195), vulnCount (13) e bundleSize (5601) congelados a partir de um run LOCAL em 2026-06-15 com os binarios reais no PATH (gitleaks 8.30.1, osv-scanner 2.3.8, zizmor 1.25.2, @size-limit/file 12.1.0). Medicoes: (a) secretFindings=3 via 'gitleaks dir <dir>' por diretorio de fonte (src/open-sse/bin/electron/scripts) APOS corrigir o .gitleaks.toml para [extend].useDefault=true (sem isso o config customizado zerava o ruleset e nunca detectava nada) e a invocacao para escopo por-dir (gitleaks dir aceita 1 path; multiplos caiam para escanear o CWD inteiro/node_modules->timeout). Os 3 sao falsos-positivos do heuristico generic-api-key (string de header beta Anthropic + nomes de coluna latencyP50Ms/latencyP95Ms), a serem allowlistados ao longo do tempo; (b) zizmorFindings=195 via 'npm run check:workflows' APOS migrar .zizmor.yml do schema antigo 'ignores: []' para 'rules: {}' (zizmor 1.25.2 rejeitava o campo 'ignores'); (c) vulnCount=13 (LOW=4/MOD=7/HIGH=2) via osv-scanner; (d) bundleSize=5601 (gzip dos 4 entrypoints bin/*.mjs) via size-limit+@size-limit/file. Todos os 4 sao dedicatedGate:true => SKIP no ratchet BLOQUEANTE (job quality-gate) e ADVISORY no job quality-extended (continue-on-error). Permanecem advisory ate um run VERDE de CI confirmar que a tooling corrigida (install via 'gh release download' em vez de api.github.com nao-autenticado) produz os valores; o flip para bloqueante (remover continue-on-error) fica para um PR de follow-up. Direction:down em todos.",
|
||||
"_codeql_promote_blocking_2026_06_15": "codeqlAlerts (value 0, direction down) PROMOVIDO de ADVISORY para RATCHET BLOQUEANTE como dedicated gate. CodeQL default-setup esta ON no repo e 'gh api repos/diegosouzapw/OmniRoute/code-scanning/alerts?state=open' retorna 0 alertas abertos (limpo). check-codeql-ratchet.mjs agora le metrics.codeqlAlerts.value, compara e sai 1 SOMENTE numa regressao real (medida > baseline); QUALQUER falha de medicao (gh ausente/sem auth/sem repo/erro de API) e SKIP gracioso com exit 0 — nunca bloqueia por falta de infraestrutura. O step bloqueante vive no job quality-gate (GH_TOKEN + permissions security-events:read); o step advisory duplicado foi removido do quality-extended. Substitui a nota _int_wiring_2026_06_13 que dizia 'codeqlAlerts permanece advisory'.",
|
||||
"_metrics_added_2026_06_13_6a11": "openapiCoverage.pct (38.3) e i18nUiCoverage.pct (80.1) promovidas de pisos manuais THRESHOLD para metricas de catraca (direction up) na Task 6A.11. eslintErrors fixado em 0 (era metrica orfa detectada pelo motor v2 da 6A.5).",
|
||||
"_int_wiring_2026_06_13": "Fase 7 INT: 3 metricas promovidas de ADVISORY para RATCHET BLOQUEANTE. Valores REAIS medidos em 2026-06-13 no HEAD desta branch: deadExports=327 (knip --reporter json, DEAD_TOTAL=exports+files), cognitiveComplexity=738 (eslint sonarjs/cognitive-complexity via eslint.sonarjs.config.mjs), typeCoveragePct=92.17 (type-coverage --json-output -p open-sse/tsconfig.json). Os scripts check-dead-code.mjs/check-cognitive-complexity.mjs/check-type-coverage.mjs foram convertidos de advisory (exit 0 sempre) para ratchet (exit 1 em regressao). vulnCount e codeqlAlerts permanecem advisory no job quality-extended: dependem de binarios externos (osv-scanner) e token GitHub (gh api) nao disponiveis localmente — nao podem ser medidos/congelados honestamente sem infraestrutura de CI.",
|
||||
|
||||
@@ -28,6 +28,21 @@ const QUIET = process.argv.includes("--quiet");
|
||||
const PRINT_JSON = process.argv.includes("--json");
|
||||
const GITLEAKS_CONFIG = path.join(ROOT, ".gitleaks.toml");
|
||||
|
||||
// Source directories to scan for secrets. We deliberately scope to the
|
||||
// production/source trees instead of scanning the whole working dir:
|
||||
// • `gitleaks dir .` (and `detect --no-git --source .`) WALKS the entire tree
|
||||
// and READS every file — including a real `node_modules/` (90k+ files) when
|
||||
// present (CI runs `npm ci`). gitleaks has no traversal-exclude flag: the
|
||||
// `.gitleaks.toml [allowlist].paths` list filters FINDINGS *after* each file
|
||||
// is read, so it does NOT speed up the walk. The full walk blows past the
|
||||
// timeout in CI (confirmed: ETIMEDOUT) → the gate silently never produces a
|
||||
// value. Scoping the scan to the source dirs keeps it fast (~6s) while still
|
||||
// covering every place an embedded secret would actually be a risk (the same
|
||||
// dirs Hard Rule #8 governs: src/open-sse/electron/bin, plus scripts/).
|
||||
// • We also drop git-history mode (scanning 4500+ commits is slow and grows
|
||||
// unbounded); the current working tree is what ships.
|
||||
const SECRET_SCAN_DIRS = ["src", "open-sse", "bin", "electron", "scripts"];
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Pure parsing function (exported for tests)
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -140,66 +155,94 @@ function main() {
|
||||
if (!QUIET) {
|
||||
process.stderr.write(
|
||||
"[check-secrets] SKIP — gitleaks não encontrado no PATH.\n" +
|
||||
"[check-secrets] Instale via: https://github.com/gitleaks/gitleaks\n" +
|
||||
"[check-secrets] ADVISORY — este gate sai 0 (ratchet entra no CI da Fase 7 INT).\n"
|
||||
"[check-secrets] Instale via: https://github.com/gitleaks/gitleaks\n" +
|
||||
"[check-secrets] ADVISORY — este gate sai 0 (ratchet entra no CI da Fase 7 INT).\n"
|
||||
);
|
||||
}
|
||||
process.exitCode = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Construir args sem interpolação de variáveis no script (Hard Rule #13)
|
||||
const args = [
|
||||
"detect",
|
||||
"--no-git", // escanear diretório em vez de histórico git (mais rápido em CI)
|
||||
"--report-format", "json",
|
||||
"--report-path", "-", // output para stdout
|
||||
"--source", ROOT,
|
||||
"--no-banner",
|
||||
];
|
||||
// Resolver os diretórios de fonte que realmente existem (robusto se um sumir).
|
||||
const scanDirs = SECRET_SCAN_DIRS.filter((d) => fs.existsSync(path.join(ROOT, d)));
|
||||
|
||||
// Adicionar config personalizada se existir
|
||||
if (fs.existsSync(GITLEAKS_CONFIG)) {
|
||||
args.push("--config", GITLEAKS_CONFIG);
|
||||
if (scanDirs.length === 0) {
|
||||
// Nenhum dir de fonte encontrado — nada a escanear (advisory, sai 0).
|
||||
console.log("secretFindings=0");
|
||||
if (!QUIET) {
|
||||
process.stderr.write("[check-secrets] Nenhum diretório de fonte encontrado para escanear.\n");
|
||||
}
|
||||
process.exitCode = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!QUIET) {
|
||||
process.stderr.write("[check-secrets] Rodando gitleaks detect --no-git --report-format json ...\n");
|
||||
process.stderr.write(
|
||||
`[check-secrets] Rodando gitleaks dir <dir> --report-format json para: ${scanDirs.join(", ")} ...\n`
|
||||
);
|
||||
}
|
||||
|
||||
let stdout = "";
|
||||
try {
|
||||
stdout = execFileSync(gitleaksBin, args, {
|
||||
cwd: ROOT,
|
||||
encoding: "utf8",
|
||||
maxBuffer: 32 * 1024 * 1024,
|
||||
timeout: 120_000, // 2 min
|
||||
});
|
||||
} catch (err) {
|
||||
// exit 1 com stdout = findings encontrados (comportamento esperado do gitleaks)
|
||||
stdout = err.stdout ? String(err.stdout) : "";
|
||||
const stderr = err.stderr ? String(err.stderr) : "";
|
||||
// `gitleaks dir` aceita UM ÚNICO path posicional (uso: `gitleaks dir [flags]
|
||||
// [path]`). Passar múltiplos paths faz o gitleaks ignorar os extras e cair para
|
||||
// escanear o CWD inteiro (`.`) — o que re-traz node_modules/docs/tests e o
|
||||
// timeout original. Por isso escaneamos CADA diretório de fonte em uma invocação
|
||||
// separada e concatenamos os findings.
|
||||
const gitleaksJson = [];
|
||||
for (const dir of scanDirs) {
|
||||
const args = [
|
||||
"dir",
|
||||
dir,
|
||||
"--report-format",
|
||||
"json",
|
||||
"--report-path",
|
||||
"-", // output para stdout
|
||||
"--no-banner",
|
||||
];
|
||||
if (fs.existsSync(GITLEAKS_CONFIG)) {
|
||||
args.push("--config", GITLEAKS_CONFIG);
|
||||
}
|
||||
|
||||
if (err.status === 1 && stdout.trim()) {
|
||||
// Normal: gitleaks achou findings e saiu com exit 1
|
||||
} else if (!stdout.trim()) {
|
||||
process.stderr.write(`[check-secrets] ERRO ao executar gitleaks: ${err.message}\n`);
|
||||
if (stderr) process.stderr.write(`[check-secrets] stderr: ${stderr.slice(0, 500)}\n`);
|
||||
let stdout = "";
|
||||
try {
|
||||
stdout = execFileSync(gitleaksBin, args, {
|
||||
cwd: ROOT,
|
||||
encoding: "utf8",
|
||||
maxBuffer: 32 * 1024 * 1024,
|
||||
timeout: 90_000, // 90s por dir — o scan escopado completa em ~10s; folga ampla
|
||||
});
|
||||
} catch (err) {
|
||||
// exit 1 com stdout = findings encontrados (comportamento esperado do gitleaks)
|
||||
stdout = err.stdout ? String(err.stdout) : "";
|
||||
const stderr = err.stderr ? String(err.stderr) : "";
|
||||
|
||||
if (err.status === 1 && stdout.trim()) {
|
||||
// Normal: gitleaks achou findings neste dir e saiu com exit 1
|
||||
} else if (!stdout.trim()) {
|
||||
process.stderr.write(
|
||||
`[check-secrets] ERRO ao executar gitleaks em '${dir}': ${err.message}\n`
|
||||
);
|
||||
if (stderr) process.stderr.write(`[check-secrets] stderr: ${stderr.slice(0, 500)}\n`);
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
if (!stdout.trim() || stdout.trim() === "null") {
|
||||
continue; // sem findings neste dir
|
||||
}
|
||||
let parsed;
|
||||
try {
|
||||
parsed = JSON.parse(stdout.trim());
|
||||
} catch (parseErr) {
|
||||
process.stderr.write(
|
||||
`[check-secrets] ERRO ao parsear JSON do gitleaks em '${dir}': ${parseErr.message}\n`
|
||||
);
|
||||
process.stderr.write(
|
||||
`[check-secrets] stdout (primeiros 500 chars): ${stdout.slice(0, 500)}\n`
|
||||
);
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
let gitleaksJson;
|
||||
if (!stdout.trim() || stdout.trim() === "null") {
|
||||
gitleaksJson = [];
|
||||
} else {
|
||||
try {
|
||||
const parsed = JSON.parse(stdout.trim());
|
||||
gitleaksJson = parsed === null ? [] : parsed;
|
||||
} catch (parseErr) {
|
||||
process.stderr.write(`[check-secrets] ERRO ao parsear JSON do gitleaks: ${parseErr.message}\n`);
|
||||
process.stderr.write(`[check-secrets] stdout (primeiros 500 chars): ${stdout.slice(0, 500)}\n`);
|
||||
process.exit(2);
|
||||
if (Array.isArray(parsed)) {
|
||||
gitleaksJson.push(...parsed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,7 +266,7 @@ function main() {
|
||||
process.stderr.write(`[check-secrets] Findings: ${findingCount} (top rules: ${topRules})\n`);
|
||||
process.stderr.write(
|
||||
"[check-secrets] Para allowlistar findings legítimos (fixtures de teste, creds públicas),\n" +
|
||||
"[check-secrets] adicione entradas em .gitleaks.toml [[allowlist]] com comentário.\n"
|
||||
"[check-secrets] adicione entradas em .gitleaks.toml [[allowlist]] com comentário.\n"
|
||||
);
|
||||
} else {
|
||||
process.stderr.write("[check-secrets] Nenhum finding detectado.\n");
|
||||
|
||||
Reference in New Issue
Block a user