@orkastery/cli 0.5.6 → 0.6.1
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/adapters/README.md +18 -0
- package/adapters/claude-code/.claude-plugin/plugin.json +1 -0
- package/adapters/claude-code/README.md +37 -5
- package/adapters/claude-code/agents/ork-check.md +1 -0
- package/adapters/claude-code/agents/ork-go.md +1 -0
- package/adapters/claude-code/agents/ork-goal.md +1 -0
- package/adapters/claude-code/agents/ork-master.md +1 -0
- package/adapters/claude-code/agents/ork-plan.md +1 -0
- package/adapters/claude-code/agents/ork-ship.md +1 -0
- package/adapters/claude-code/hooks/hooks.json +2 -1
- package/adapters/claude-code/hooks/ork-guard.js +15 -1
- package/adapters/claude-code/hooks/ork-resposta.js +91 -0
- package/adapters/claude-code/hooks/ork-sensor.js +18 -2
- package/adapters/claude-code/hooks/quota-signal.js +14 -0
- package/adapters/claude-code/mods/ork-mensageiro/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-mensageiro/hooks/caixa.ts +215 -0
- package/adapters/claude-code/mods/ork-mensageiro/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-mensageiro/hooks/painel.tsx +165 -0
- package/adapters/claude-code/mods/ork-mensageiro/hooks/recepcao.ts +256 -0
- package/adapters/claude-code/mods/ork-mensageiro/hooks/register.tsx +481 -0
- package/adapters/claude-code/mods/ork-mensageiro/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-mensageiro/types/index.d.ts +85 -0
- package/adapters/claude-code/mods/ork-pista/hooks/ci.ts +9 -5
- package/adapters/claude-code/mods/ork-ritual/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-ritual/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-ritual/hooks/nota.ts +88 -0
- package/adapters/claude-code/mods/ork-ritual/hooks/register.tsx +494 -0
- package/adapters/claude-code/mods/ork-ritual/hooks/replay.tsx +271 -0
- package/adapters/claude-code/mods/ork-ritual/hooks/ritual.ts +218 -0
- package/adapters/claude-code/mods/ork-ritual/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-ritual/types/index.d.ts +106 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/vigia.ts +1 -1
- package/adapters/codex/skills/ork/SKILL.md +2 -0
- package/adapters/hermes/README.md +64 -1
- package/adapters/hermes/bin/ork-conhecimento-colecoes.sh +14 -0
- package/adapters/hermes/bin/ork-conhecimento-listar.sh +15 -0
- package/adapters/hermes/bin/ork-conhecimento-mostrar.sh +16 -0
- package/adapters/hermes/bin/ork-conhecimento-verificar.sh +15 -0
- package/adapters/hermes/bin/ork-vigia-batida.py +127 -0
- package/adapters/hermes/hermes.plugin.json +4 -0
- package/adapters/hermes/hitl-ingress/__init__.py +101 -8
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +2 -2
- package/adapters/openclaw/README.md +81 -9
- package/adapters/openclaw/bin/ork-vigia-batida.js +86 -0
- package/adapters/openclaw/dist/hitl-ingress.js +106 -9
- package/adapters/openclaw/dist/index.js +86 -0
- package/adapters/openclaw/openclaw.plugin.json +34 -4
- package/adapters/openclaw/package.json +2 -2
- package/adapters/openclaw/src/hitl-ingress.ts +103 -9
- package/adapters/openclaw/src/index.ts +94 -0
- package/assets/adapters-en/claude-code/README.md +403 -0
- package/assets/adapters-en/claude-code/agents/ork-check.md +33 -0
- package/assets/adapters-en/claude-code/agents/ork-go.md +33 -0
- package/assets/adapters-en/claude-code/agents/ork-goal.md +34 -0
- package/assets/adapters-en/claude-code/agents/ork-master.md +35 -0
- package/assets/adapters-en/claude-code/agents/ork-plan.md +33 -0
- package/assets/adapters-en/claude-code/agents/ork-ship.md +33 -0
- package/assets/adapters-en/claude-code/commands/check.md +46 -0
- package/assets/adapters-en/claude-code/commands/go.md +46 -0
- package/assets/adapters-en/claude-code/commands/goal.md +55 -0
- package/assets/adapters-en/claude-code/commands/master.md +54 -0
- package/assets/adapters-en/claude-code/commands/onboarding.md +22 -0
- package/assets/adapters-en/claude-code/commands/ork.md +126 -0
- package/assets/adapters-en/claude-code/commands/plan.md +47 -0
- package/assets/adapters-en/claude-code/commands/ship.md +45 -0
- package/assets/adapters-en/codex/skills/ork/SKILL.md +85 -0
- package/assets/adapters-en/hermes/README.md +273 -0
- package/assets/adapters-en/hermes/skills/orkastery-devmaster/SKILL.md +169 -0
- package/assets/references-en/README.md +25 -0
- package/assets/references-en/code-review-axes.md +82 -0
- package/assets/references-en/definition-of-done.md +74 -0
- package/assets/references-en/performance-checklist.md +53 -0
- package/assets/references-en/security-checklist.md +101 -0
- package/assets/references-en/testing-patterns.md +51 -0
- package/assets/security-scan/defaults/.golangci.yml +2 -0
- package/assets/security-scan/defaults/eslint.config.mjs +11 -0
- package/assets/security-scan/defaults/gitleaks.toml +3 -0
- package/assets/security-scan/defaults/ruff.toml +1 -0
- package/assets/security-scan/tools-agil.json +87 -0
- package/assets/security-scan/tools.json +625 -0
- package/assets/skills-en/core/onboarding/SKILL.md +58 -0
- package/assets/skills-en/core/orkastery-bootstrap/SKILL.md +119 -0
- package/assets/skills-en/core/thread-state/SKILL.md +88 -0
- package/assets/skills-en/governance/decision-triage/SKILL.md +65 -0
- package/assets/skills-en/governance/narrative-guardian/SKILL.md +66 -0
- package/assets/skills-en/governance/roadmap-keeper/SKILL.md +60 -0
- package/assets/skills-en/governance/scope-check-capability-map/SKILL.md +71 -0
- package/assets/skills-en/observability/thread-tracing/SKILL.md +63 -0
- package/assets/skills-en/phases/check-quality/SKILL.md +103 -0
- package/assets/skills-en/phases/go-implementation/SKILL.md +78 -0
- package/assets/skills-en/phases/goal-definition/SKILL.md +69 -0
- package/assets/skills-en/phases/master-metrics/SKILL.md +72 -0
- package/assets/skills-en/phases/plan-specification/SKILL.md +67 -0
- package/assets/skills-en/phases/security-remediation/SKILL.md +70 -0
- package/assets/skills-en/phases/ship-release/SKILL.md +71 -0
- package/assets/skills-en/reviewers/code-reviewer/SKILL.md +61 -0
- package/assets/skills-en/reviewers/security-auditor/SKILL.md +93 -0
- package/assets/skills-en/reviewers/test-engineer/SKILL.md +66 -0
- package/assets/skills-en/reviewers/web-performance-auditor/SKILL.md +63 -0
- package/dist/adapters/claude-bg.js +123 -9
- package/dist/adapters/codex-controller-worker.js +12 -5
- package/dist/adapters/codex-events.js +16 -4
- package/dist/adapters/codex.js +3 -1
- package/dist/adapters/grok-events.js +227 -0
- package/dist/adapters/grok-mcp-servidor.js +612 -0
- package/dist/adapters/grok-mcp.js +178 -0
- package/dist/adapters/grok.js +686 -0
- package/dist/agendador-cpu.js +92 -0
- package/dist/atualizar-branch.js +245 -0
- package/dist/auditoria.js +13 -11
- package/dist/auditrun.js +51 -46
- package/dist/autorizacao-duravel.js +327 -0
- package/dist/board.js +94 -40
- package/dist/branch-de-estado.js +23 -10
- package/dist/busca-semantica.js +7 -5
- package/dist/catalogo.js +2 -1
- package/dist/changelog-reconciliacao.js +290 -0
- package/dist/check-agil.js +528 -0
- package/dist/check-modo.js +41 -0
- package/dist/check-paralelo.js +238 -0
- package/dist/ci.js +248 -45
- package/dist/ciclos.js +8 -6
- package/dist/claim-lint.js +12 -8
- package/dist/claims-conferir.js +47 -39
- package/dist/claims.js +10 -9
- package/dist/company-brain-client.js +10 -3
- package/dist/company-brain-migration.js +11 -5
- package/dist/conducao-batida.js +759 -0
- package/dist/conducao-sentinela.js +25 -8
- package/dist/conducao.js +89 -29
- package/dist/conhecimento-consulta.js +270 -0
- package/dist/conhecimento.js +111 -87
- package/dist/conta-esgotada.js +127 -0
- package/dist/dado-privado.js +816 -0
- package/dist/decisao-autonoma.js +5 -4
- package/dist/demo.js +14 -12
- package/dist/dependencias-da-worktree.js +25 -0
- package/dist/divida.js +38 -36
- package/dist/docs.js +89 -89
- package/dist/doctor-forja.js +343 -0
- package/dist/doctor-pulse-cron.js +113 -1
- package/dist/doctor.js +164 -6
- package/dist/dsn-da-memoria.js +417 -0
- package/dist/entrega-por-mod.js +503 -0
- package/dist/entrega-pr.js +154 -54
- package/dist/esgotamentos.js +13 -11
- package/dist/estado-thread.js +21 -18
- package/dist/evalrunner.js +35 -32
- package/dist/fabrica-batida.js +34 -21
- package/dist/fabrica-estado.js +33 -21
- package/dist/fix.js +21 -19
- package/dist/forja/adaptador.js +6 -0
- package/dist/forja/ambiente-de-ci.js +27 -0
- package/dist/forja/api.js +256 -0
- package/dist/forja/cache.js +89 -0
- package/dist/forja/cli.js +60 -0
- package/dist/forja/contrato.js +9 -0
- package/dist/forja/endereco.js +43 -0
- package/dist/forja/estado.js +22 -0
- package/dist/forja/executor.js +73 -0
- package/dist/forja/github/ci.js +95 -0
- package/dist/forja/github/dono-da-rede.js +18 -0
- package/dist/forja/github/metadados.js +15 -0
- package/dist/forja/github/pedidos.js +200 -0
- package/dist/forja/gitlab/ci.js +141 -0
- package/dist/forja/gitlab/consultas.js +59 -0
- package/dist/forja/gitlab/dono-da-rede.js +43 -0
- package/dist/forja/gitlab/metadados.js +15 -0
- package/dist/forja/gitlab/pedidos-abertos.js +158 -0
- package/dist/forja/gitlab/pedidos.js +141 -0
- package/dist/forja/produtor.js +32 -0
- package/dist/forja/registro.js +31 -0
- package/dist/forja/resolver.js +121 -0
- package/dist/forja/texto.js +13 -0
- package/dist/forja/versao.js +71 -0
- package/dist/forja.js +123 -80
- package/dist/handoff.js +23 -13
- package/dist/hitl-contract.js +46 -43
- package/dist/hitl-curto.js +37 -25
- package/dist/hitl-gates.js +13 -11
- package/dist/hitl-local-receipt.js +51 -0
- package/dist/hitl-local.js +11 -2
- package/dist/hitl-lock.js +5 -11
- package/dist/hitl-public-receipt.js +11 -0
- package/dist/hitl-resumo.js +44 -1
- package/dist/hitl-sessions.js +62 -9
- package/dist/hitl-texto-livre.js +7 -4
- package/dist/hitl.js +34 -31
- package/dist/horario.js +20 -1
- package/dist/hosts.js +104 -134
- package/dist/index.js +864 -554
- package/dist/indice-vetorial.js +14 -12
- package/dist/init.js +11 -2
- package/dist/integracoes-locais.js +4 -0
- package/dist/intelligence-graph-cli.js +123 -69
- package/dist/intelligence-graph-query.js +37 -22
- package/dist/leases.js +19 -18
- package/dist/ledger-stats.js +17 -16
- package/dist/ledger.js +31 -1
- package/dist/licoes.js +12 -11
- package/dist/locale-accounts.js +228 -0
- package/dist/locale-adapter.js +291 -0
- package/dist/locale-ajuda.js +86 -10
- package/dist/locale-audit.js +199 -0
- package/dist/locale-claims.js +213 -0
- package/dist/locale-conducao.js +138 -0
- package/dist/locale-conhecimento.js +181 -0
- package/dist/locale-decisao.js +205 -0
- package/dist/locale-docs.js +230 -0
- package/dist/locale-estado.js +101 -0
- package/dist/locale-forja.js +288 -0
- package/dist/locale-gerados.js +5 -0
- package/dist/locale-grafo.js +129 -0
- package/dist/locale-master.js +83 -0
- package/dist/locale-network.js +524 -0
- package/dist/locale-operacao-restante.js +931 -0
- package/dist/locale-projetos.js +100 -0
- package/dist/locale-prompts.js +194 -0
- package/dist/locale-pulse.js +140 -2
- package/dist/locale-reservas.js +87 -0
- package/dist/locale-retry.js +183 -6
- package/dist/locale-security-scan.js +69 -0
- package/dist/locale-setup.js +192 -0
- package/dist/locale-ship.js +177 -66
- package/dist/locale-thread.js +149 -5
- package/dist/locale-verify.js +80 -4
- package/dist/locale-worktree.js +246 -0
- package/dist/locale.js +553 -9
- package/dist/maestro-runtime.js +22 -3
- package/dist/manifest.js +293 -12
- package/dist/maquina-ociosa.js +211 -0
- package/dist/maquina.js +10 -5
- package/dist/master-nota-local.js +200 -0
- package/dist/master-nota.js +10 -2
- package/dist/master.js +118 -41
- package/dist/mcp-conhecimento.js +52 -0
- package/dist/mcp-experiencia.js +26 -5
- package/dist/mcp-install.js +90 -15
- package/dist/mcp-noctum.js +101 -0
- package/dist/mcp-server.js +116 -22
- package/dist/mcp-ship-transporte.js +977 -0
- package/dist/mcp-ship.js +68 -24
- package/dist/mcp-transportes.js +17 -0
- package/dist/memoria.js +63 -58
- package/dist/merge-da-entrega.js +85 -0
- package/dist/modos-migracao.js +8 -6
- package/dist/modos.js +17 -18
- package/dist/network-roadmap.js +94 -54
- package/dist/noctum-aceite.js +929 -0
- package/dist/noctum-arco.js +540 -33
- package/dist/noctum-capacidade.js +3 -3
- package/dist/noctum-changelog.js +399 -0
- package/dist/noctum-cli.js +539 -0
- package/dist/noctum-coordenacao.js +1225 -0
- package/dist/noctum-entrega.js +1217 -0
- package/dist/noctum-envelope.js +253 -17
- package/dist/noctum-execucao.js +100 -24
- package/dist/noctum-fechamento.js +416 -0
- package/dist/noctum-fila.js +49 -10
- package/dist/noctum-guarda.js +333 -0
- package/dist/noctum-integracao.js +507 -0
- package/dist/noctum-maquinas.js +387 -0
- package/dist/noctum-medicao.js +450 -0
- package/dist/noctum-modo.js +390 -0
- package/dist/noctum-pergunta-canal.js +96 -0
- package/dist/noctum-pergunta.js +115 -0
- package/dist/noctum-provas.js +838 -0
- package/dist/noctum-reconciliacao.js +594 -0
- package/dist/noctum-rede.js +102 -0
- package/dist/noctum-registro.js +215 -0
- package/dist/noctum-relatorio.js +861 -0
- package/dist/noctum-resposta.js +207 -0
- package/dist/noctum-retomada-rede.js +71 -0
- package/dist/noctum-sessoes-em-voo.js +93 -0
- package/dist/noctum-supervisao.js +649 -0
- package/dist/noctum-terminal.js +459 -0
- package/dist/noctum-transporte.js +655 -0
- package/dist/noctum-vigia.js +679 -0
- package/dist/ocupacao.js +3 -1
- package/dist/onboarding.js +16 -5
- package/dist/orkmind.js +39 -23
- package/dist/orquestracao.js +31 -35
- package/dist/parado-no-condutor.js +160 -139
- package/dist/phase.js +96 -42
- package/dist/policies.js +33 -0
- package/dist/postura-local.js +11 -6
- package/dist/preflight.js +2 -1
- package/dist/procedencia.js +9 -14
- package/dist/projeto-alvo.js +59 -20
- package/dist/prompts.js +136 -42
- package/dist/prova-minima.js +5 -6
- package/dist/pulse-cadencia.js +5 -4
- package/dist/pulse-consentimento.js +53 -1
- package/dist/pulse-delivery.js +143 -27
- package/dist/pulse-resposta.js +368 -4
- package/dist/pulse.js +136 -3
- package/dist/ratelimit.js +13 -4
- package/dist/recall.js +14 -13
- package/dist/rede-adesao.js +13 -8
- package/dist/rede-forja.js +108 -24
- package/dist/rede-status.js +63 -45
- package/dist/rede.js +86 -46
- package/dist/retomada-checkpoint.js +101 -0
- package/dist/retry.js +326 -78
- package/dist/roadmap-reservas.js +32 -32
- package/dist/runtime-ambiente.js +5 -0
- package/dist/runtime-context.js +279 -85
- package/dist/runtime-escada.js +429 -0
- package/dist/runtime-profiles.js +130 -63
- package/dist/runtimes.js +42 -5
- package/dist/saida-segura.js +3 -2
- package/dist/security-scan/agil.js +366 -0
- package/dist/security-scan/cli.js +76 -0
- package/dist/security-scan/compare.js +149 -0
- package/dist/security-scan/config.js +93 -0
- package/dist/security-scan/detect.js +97 -0
- package/dist/security-scan/executor.js +196 -0
- package/dist/security-scan/guard.js +180 -0
- package/dist/security-scan/imagem-de-lint.js +96 -0
- package/dist/security-scan/normalize/actionlint.js +12 -0
- package/dist/security-scan/normalize/eslint.js +12 -0
- package/dist/security-scan/normalize/gitleaks.js +13 -0
- package/dist/security-scan/normalize/golangci-lint.js +13 -0
- package/dist/security-scan/normalize/govulncheck.js +36 -0
- package/dist/security-scan/normalize/index.js +25 -0
- package/dist/security-scan/normalize/npm-audit.js +30 -0
- package/dist/security-scan/normalize/osv-scanner.js +32 -0
- package/dist/security-scan/normalize/oxlint.js +51 -0
- package/dist/security-scan/normalize/pip-audit.js +16 -0
- package/dist/security-scan/normalize/ruff.js +12 -0
- package/dist/security-scan/normalize/semgrep.js +20 -0
- package/dist/security-scan/normalize/trivy.js +33 -0
- package/dist/security-scan/normalize/util.js +204 -0
- package/dist/security-scan/normalize/zizmor.js +19 -0
- package/dist/security-scan/report.js +156 -0
- package/dist/security-scan/run.js +448 -0
- package/dist/security-scan/tools.js +62 -0
- package/dist/security-scan/trabalhador.js +74 -0
- package/dist/security-scan/types.js +15 -0
- package/dist/session-events.js +19 -2
- package/dist/session-watcher-claude.js +74 -19
- package/dist/session-watcher-grok.js +425 -0
- package/dist/session-watcher.js +174 -13
- package/dist/sessoes-contas.js +42 -5
- package/dist/sessoes-inventario.js +1 -1
- package/dist/sessoes.js +25 -23
- package/dist/setup.js +67 -66
- package/dist/ship-publicar.js +263 -92
- package/dist/ship.js +160 -17
- package/dist/superficie.js +14 -11
- package/dist/thread-close.js +9 -9
- package/dist/thread.js +121 -18
- package/dist/tokens.js +15 -11
- package/dist/trabalho-thread.js +297 -0
- package/dist/transporte-do-canal.js +189 -0
- package/dist/types.js +12 -1
- package/dist/util.js +5 -2
- package/dist/varredura-acoes.js +500 -0
- package/dist/verify.js +491 -32
- package/dist/worktree-local.js +12 -10
- package/dist/worktree.js +81 -85
- package/eval/casos/check-quality.json +77 -0
- package/eval/casos/security-auditor.json +148 -0
- package/eval/casos/security-remediation.json +161 -0
- package/eval/fixtures/security-scan/README.md +8 -0
- package/eval/fixtures/security-scan/go/.github/workflows/ci.yml +12 -0
- package/eval/fixtures/security-scan/go/Dockerfile +4 -0
- package/eval/fixtures/security-scan/go/README.md +4 -0
- package/eval/fixtures/security-scan/go/config.go +4 -0
- package/eval/fixtures/security-scan/go/go.mod +5 -0
- package/eval/fixtures/security-scan/go/go.sum +2 -0
- package/eval/fixtures/security-scan/go/main.go +31 -0
- package/eval/fixtures/security-scan/js/.github/workflows/ci.yml +12 -0
- package/eval/fixtures/security-scan/js/Dockerfile +3 -0
- package/eval/fixtures/security-scan/js/README.md +4 -0
- package/eval/fixtures/security-scan/js/package-lock.json +844 -0
- package/eval/fixtures/security-scan/js/package.json +10 -0
- package/eval/fixtures/security-scan/js/src/config.js +4 -0
- package/eval/fixtures/security-scan/js/src/server.js +17 -0
- package/eval/fixtures/security-scan/js/src/util.ts +4 -0
- package/eval/fixtures/security-scan/js/tsconfig.json +4 -0
- package/eval/fixtures/security-scan/python/.github/workflows/ci.yml +12 -0
- package/eval/fixtures/security-scan/python/Dockerfile +3 -0
- package/eval/fixtures/security-scan/python/README.md +4 -0
- package/eval/fixtures/security-scan/python/app/__init__.py +0 -0
- package/eval/fixtures/security-scan/python/app/config.py +2 -0
- package/eval/fixtures/security-scan/python/app/main.py +19 -0
- package/eval/fixtures/security-scan/python/requirements.txt +1 -0
- package/package.json +10 -5
- package/references/security-checklist.md +3 -3
- package/skills/README.md +1 -0
- package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
- package/skills/phases/check-quality/SKILL.md +16 -0
- package/skills/phases/go-implementation/SKILL.md +6 -0
- package/skills/phases/security-remediation/SKILL.md +70 -0
- package/skills/reviewers/security-auditor/SKILL.md +25 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.reportDir = reportDir;
|
|
37
|
+
exports.writeReport = writeReport;
|
|
38
|
+
exports.readReport = readReport;
|
|
39
|
+
exports.isExcusedUnscanned = isExcusedUnscanned;
|
|
40
|
+
exports.unscannedLoopTools = unscannedLoopTools;
|
|
41
|
+
exports.exitCodeOf = exitCodeOf;
|
|
42
|
+
exports.tally = tally;
|
|
43
|
+
exports.renderMarkdown = renderMarkdown;
|
|
44
|
+
/**
|
|
45
|
+
* The report of a scan round (spec section 5.4) and the exit code (section 5.5). The files live under
|
|
46
|
+
* `<state>/security-scan/<head-sha>/` of the main tree, so a scan inside a thread worktree and `ork ship` read the same
|
|
47
|
+
* place. A finding carries no snippet (see `Finding`), and nothing here prints raw tool output: no secret value reaches
|
|
48
|
+
* `report.json` or `report.md`.
|
|
49
|
+
*/
|
|
50
|
+
const fs = __importStar(require("node:fs"));
|
|
51
|
+
const path = __importStar(require("node:path"));
|
|
52
|
+
const locale_1 = require("../locale");
|
|
53
|
+
const manifest_1 = require("../manifest");
|
|
54
|
+
const types_1 = require("./types");
|
|
55
|
+
function reportDir(stateRoot, sha) {
|
|
56
|
+
return path.join((0, manifest_1.dirEstado)(stateRoot), 'security-scan', sha);
|
|
57
|
+
}
|
|
58
|
+
function atomicWrite(file, content) {
|
|
59
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
60
|
+
fs.writeFileSync(tmp, content, { mode: 0o600 });
|
|
61
|
+
fs.renameSync(tmp, file);
|
|
62
|
+
}
|
|
63
|
+
function writeReport(stateRoot, report) {
|
|
64
|
+
const dir = reportDir(stateRoot, report.head);
|
|
65
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
66
|
+
const json = path.join(dir, 'report.json');
|
|
67
|
+
const md = path.join(dir, 'report.md');
|
|
68
|
+
atomicWrite(json, `${JSON.stringify(report, null, 2)}\n`);
|
|
69
|
+
atomicWrite(md, renderMarkdown(report));
|
|
70
|
+
return { json, md };
|
|
71
|
+
}
|
|
72
|
+
/** The stored report of `sha`, or null when absent or not a report of this schema. */
|
|
73
|
+
function readReport(stateRoot, sha) {
|
|
74
|
+
let raw;
|
|
75
|
+
try {
|
|
76
|
+
raw = fs.readFileSync(path.join(reportDir(stateRoot, sha), 'report.json'), 'utf8');
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const r = JSON.parse(raw);
|
|
83
|
+
return r && r.schema === 'ork.security-scan/v1' && Array.isArray(r.tools) && Array.isArray(r.findings) ? r : null;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* "Nothing this tool can scan", declared in the report but not counted as an unscanned loop category: `unsupported-ci`
|
|
91
|
+
* (a GitLab CI file and no GitHub workflow: no analyzer for it, a known product gap) and `no-requirements-file`
|
|
92
|
+
* (pip-audit without `requirements*.txt`) when another tool of the same category ran in the round. Counted, either one
|
|
93
|
+
* would keep every such project red under REQUIRED and every ship asking for authorization.
|
|
94
|
+
*/
|
|
95
|
+
function isExcusedUnscanned(tool, tools) {
|
|
96
|
+
if (tool.state !== 'unavailable')
|
|
97
|
+
return false;
|
|
98
|
+
if (tool.reason === 'unsupported-ci')
|
|
99
|
+
return true;
|
|
100
|
+
if (tool.reason === 'no-requirements-file')
|
|
101
|
+
return tools.some(o => o !== tool && o.category === tool.category && o.state === 'ran');
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
/** Loop-category tools that did not run and are not excused (`isExcusedUnscanned`): the single rule of exit code and ship gate. */
|
|
105
|
+
function unscannedLoopTools(report) {
|
|
106
|
+
return report.tools.filter(t => t.state !== 'ran' && types_1.LOOP_CATEGORIES.includes(t.category) && !isExcusedUnscanned(t, report.tools));
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* 1 when a `new` finding is in the loop, or when `required` and a tool of a loop category (secret, dependency, sast, iac,
|
|
110
|
+
* workflow) is unavailable or failed (never clean), unless excused by `isExcusedUnscanned`; `quality` and `license`
|
|
111
|
+
* tools never drive the code. Invalid dismissals or config never reach a report: the command fails before.
|
|
112
|
+
*/
|
|
113
|
+
function exitCodeOf(report, required) {
|
|
114
|
+
if (report.findings.some(f => f.status === 'new' && f.loop))
|
|
115
|
+
return 1;
|
|
116
|
+
if (required && unscannedLoopTools(report).length > 0)
|
|
117
|
+
return 1;
|
|
118
|
+
return 0;
|
|
119
|
+
}
|
|
120
|
+
function tally(report) {
|
|
121
|
+
const t = { new: 0, existing: 0, dismissed: 0, loop: 0 };
|
|
122
|
+
for (const f of report.findings) {
|
|
123
|
+
t[f.status]++;
|
|
124
|
+
if (f.status === 'new' && f.loop)
|
|
125
|
+
t.loop++;
|
|
126
|
+
}
|
|
127
|
+
return t;
|
|
128
|
+
}
|
|
129
|
+
const cell = (s) => s.replace(/\|/g, '\\|').replace(/[\r\n]+/g, ' ');
|
|
130
|
+
const SEVERITY_ORDER = ['critical', 'high', 'medium', 'low', 'info'];
|
|
131
|
+
const bySeverity = (a, b) => SEVERITY_ORDER.indexOf(a.severity) - SEVERITY_ORDER.indexOf(b.severity) || a.file.localeCompare(b.file) || (a.line ?? 0) - (b.line ?? 0);
|
|
132
|
+
function findingLine(f) {
|
|
133
|
+
const where = f.line === undefined ? f.file : `${f.file}:${f.line}`;
|
|
134
|
+
return `- \`${f.severity}\` ${f.tool} \`${f.rule}\` ${where}: ${f.message.replace(/[\r\n]+/g, ' ')} (\`${f.fingerprint}\`)`;
|
|
135
|
+
}
|
|
136
|
+
function renderMarkdown(report) {
|
|
137
|
+
const m = (0, locale_1.msg)().securityScan;
|
|
138
|
+
const t = tally(report);
|
|
139
|
+
const out = [`# ${m.title}`, '', m.headBase(report.head, report.base), '', m.generatedAt(report.generatedAt), '',
|
|
140
|
+
m.stacks(report.stacks.length ? report.stacks.join(', ') : m.noStacks), '', `## ${m.executionHeading}`, ''];
|
|
141
|
+
if (report.tools.length === 0)
|
|
142
|
+
out.push(m.noTools);
|
|
143
|
+
else {
|
|
144
|
+
out.push(`| ${m.tableTool} | ${m.tableExecutor} | ${m.tableState} | ${m.tableReason} |`, '| --- | --- | --- | --- |');
|
|
145
|
+
for (const r of report.tools) {
|
|
146
|
+
out.push(`| ${cell(r.name)} | ${r.executor ?? m.noExecutor} | ${m.states[r.state] ?? r.state} | ${cell(r.reason ?? '')} |`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const news = report.findings.filter(f => f.status === 'new');
|
|
150
|
+
const loop = news.filter(f => f.loop).sort(bySeverity);
|
|
151
|
+
const warn = news.filter(f => !f.loop).sort(bySeverity);
|
|
152
|
+
out.push('', `## ${m.countsHeading}`, '', m.counts(t.new, t.existing, t.dismissed, t.loop), '', `## ${m.newLoopHeading}`, '', ...(loop.length ? loop.map(findingLine) : [m.none]), '', `## ${m.newWarnHeading}`, '', ...(warn.length ? warn.map(findingLine) : [m.none]), '', m.existingTotal(t.existing), '', m.dismissedTotal(t.dismissed));
|
|
153
|
+
if (report.excluded.length)
|
|
154
|
+
out.push('', `## ${m.excludedHeading}`, '', ...report.excluded.map(e => `- ${e}`));
|
|
155
|
+
return `${out.join('\n')}\n`;
|
|
156
|
+
}
|
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.failedAll = exports.SCAN_CACHE_VERSION = exports.defaultProbe = exports.chooseExecutor = void 0;
|
|
37
|
+
exports.git = git;
|
|
38
|
+
exports.emptyCounts = emptyCounts;
|
|
39
|
+
exports.resolveBase = resolveBase;
|
|
40
|
+
exports.stackPairs = stackPairs;
|
|
41
|
+
exports.unitsOf = unitsOf;
|
|
42
|
+
exports.commitsScanned = commitsScanned;
|
|
43
|
+
exports.checkout = checkout;
|
|
44
|
+
exports.cacheRoot = cacheRoot;
|
|
45
|
+
exports.scanTool = scanTool;
|
|
46
|
+
exports.runScan = runScan;
|
|
47
|
+
/**
|
|
48
|
+
* The scan round of `ork security-scan` (spec section 5.2): resolve the base, detect the stacks of the HEAD tree, run
|
|
49
|
+
* every applicable catalog tool on the HEAD tree and on the base (a temporary detached worktree, or the cache), make
|
|
50
|
+
* each output a list of RawFinding, and classify HEAD against the base. Reports are written by Task 8 (report.ts).
|
|
51
|
+
*
|
|
52
|
+
* Rules that hold for every tool:
|
|
53
|
+
* - `unavailable` and `failed` contribute no finding and are never clean: their counts are zero and the state says why.
|
|
54
|
+
* - A tool runs once per detected (stack, dir) when its command uses {stackDir} (or has `argsByStack`), else once at
|
|
55
|
+
* the root. The per-pair outcomes combine as failed > unavailable > ran.
|
|
56
|
+
* - The base is the `--base` ref, else `git merge-base HEAD <remote>/<worktree.base_branch>`, else the merge-base with
|
|
57
|
+
* the local branch; unresolved, every tool is `failed`/`base-unresolved`. Base equal to HEAD is valid: the HEAD
|
|
58
|
+
* results are the base ones and every finding is `existing` (the baseline of a project adopting the scan).
|
|
59
|
+
* - Gitleaks: HEAD = mergeByFingerprint(dir scan of the HEAD tree, git scan of base..HEAD); base = dir scan of the base
|
|
60
|
+
* tree. The git scan of a linked worktree mounts the main `.git` (read-only, at its own path) so that the worktree's
|
|
61
|
+
* `.git` file resolves in the container; base..HEAD with commits and "0 commits scanned" is `zero-commits-scanned`.
|
|
62
|
+
* - The base results are cached as normalized RawFindings (never raw output) in
|
|
63
|
+
* `$XDG_CACHE_HOME/orkastery/security-scan/<base-sha>/<tool>-<id>[-<stack>-<dir>].json`, keyed by the whole ToolSpec,
|
|
64
|
+
* the resolved tool config content and SCAN_CACHE_VERSION.
|
|
65
|
+
* - HEAD is scanned from a detached worktree of the HEAD commit, like the base: only committed content is scanned
|
|
66
|
+
* (uncommitted or ignored files, node_modules, .env, are not), and `.ork-security/` is read from that tree. The report
|
|
67
|
+
* carries the HEAD sha, which is what `ork verify` and `ork ship` prove. A scan started from a subdirectory scans the
|
|
68
|
+
* repository (`git rev-parse --show-toplevel`).
|
|
69
|
+
* - `deadlineMs` bounds the whole round: each process gets the tool `timeoutMs` capped by the time left, and once it is
|
|
70
|
+
* spent the remaining units are `failed`/`timeout` without running. A round killed by a signal is cleaned up by the
|
|
71
|
+
* reaper of guard.ts (worktrees, raw output dirs, the running container, the native process group).
|
|
72
|
+
* - A failure on the base side carries the `base-` prefix (`base-timeout`, `base-exit-2: ...`).
|
|
73
|
+
* - Errors the catalog tolerates (`expect.tolerate`: Semgrep warn-level Timeout and PartialParsing) leave the tool
|
|
74
|
+
* `ran` with the HEAD reason `partial-timeout: <n>` / `partial-parsing: <n>`; the findings obtained count.
|
|
75
|
+
*/
|
|
76
|
+
const node_child_process_1 = require("node:child_process");
|
|
77
|
+
const crypto = __importStar(require("node:crypto"));
|
|
78
|
+
const fs = __importStar(require("node:fs"));
|
|
79
|
+
const os = __importStar(require("node:os"));
|
|
80
|
+
const path = __importStar(require("node:path"));
|
|
81
|
+
const estado_thread_1 = require("../estado-thread");
|
|
82
|
+
const manifest_1 = require("../manifest");
|
|
83
|
+
const compare_1 = require("./compare");
|
|
84
|
+
const config_1 = require("./config");
|
|
85
|
+
const detect_1 = require("./detect");
|
|
86
|
+
const executor_1 = require("./executor");
|
|
87
|
+
const guard_1 = require("./guard");
|
|
88
|
+
const normalize_1 = require("./normalize");
|
|
89
|
+
const tools_1 = require("./tools");
|
|
90
|
+
var executor_2 = require("./executor");
|
|
91
|
+
Object.defineProperty(exports, "chooseExecutor", { enumerable: true, get: function () { return executor_2.chooseExecutor; } });
|
|
92
|
+
Object.defineProperty(exports, "defaultProbe", { enumerable: true, get: function () { return executor_2.defaultProbe; } });
|
|
93
|
+
/** Version of the normalized cache content; bump when a normalizer or the RawFinding shape changes. */
|
|
94
|
+
exports.SCAN_CACHE_VERSION = 1;
|
|
95
|
+
const STATUSES = ['new', 'existing', 'dismissed'];
|
|
96
|
+
const SEVERITIES = ['critical', 'high', 'medium', 'low', 'info'];
|
|
97
|
+
const GIT_TIMEOUT_MS = 60000;
|
|
98
|
+
function git(cwd, args) {
|
|
99
|
+
const r = (0, node_child_process_1.spawnSync)('git', args, { cwd, encoding: 'utf8', timeout: GIT_TIMEOUT_MS });
|
|
100
|
+
return r.error || r.status !== 0 ? null : r.stdout.trim();
|
|
101
|
+
}
|
|
102
|
+
function emptyCounts() {
|
|
103
|
+
return Object.fromEntries(STATUSES.map(s => [s, Object.fromEntries(SEVERITIES.map(v => [v, 0]))]));
|
|
104
|
+
}
|
|
105
|
+
/** Base sha: `--base`, else merge-base with `<remote>/<base_branch>`, else with the local branch; null when unresolved. */
|
|
106
|
+
function resolveBase(root, stateRoot, ref) {
|
|
107
|
+
const commit = (r) => git(root, ['rev-parse', '--verify', '--quiet', `${r}^{commit}`]);
|
|
108
|
+
if (ref !== undefined)
|
|
109
|
+
return commit(ref);
|
|
110
|
+
let branch = 'main';
|
|
111
|
+
let remote = 'origin';
|
|
112
|
+
try {
|
|
113
|
+
const m = (0, manifest_1.carregarManifesto)(stateRoot) ?? (0, manifest_1.carregarManifesto)(root);
|
|
114
|
+
if (m) {
|
|
115
|
+
branch = m.manifesto.worktree.base_branch || branch;
|
|
116
|
+
remote = m.manifesto.fabrica.remoto || remote;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch { /* an invalid manifest leaves the defaults; `ork verify` reports it on its own */ }
|
|
120
|
+
for (const target of [`${remote}/${branch}`, branch]) {
|
|
121
|
+
if (!commit(target))
|
|
122
|
+
continue;
|
|
123
|
+
const mb = git(root, ['merge-base', 'HEAD', target]);
|
|
124
|
+
if (mb)
|
|
125
|
+
return mb;
|
|
126
|
+
}
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
/** Detected (stack, dir) pairs adjusted by `config.stacks`: `remove` drops a stack, `add` forces it at the root. */
|
|
130
|
+
function stackPairs(root, config) {
|
|
131
|
+
const pairs = (0, detect_1.detectStacks)(root).filter(p => !config.stacks.remove.includes(p.stack));
|
|
132
|
+
for (const s of config.stacks.add)
|
|
133
|
+
if (!pairs.some(p => p.stack === s))
|
|
134
|
+
pairs.push({ stack: s, dir: '.', forced: true });
|
|
135
|
+
return pairs;
|
|
136
|
+
}
|
|
137
|
+
const usesStackDir = (t) => !!t.argsByStack || t.args.some(a => a.includes('{stackDir}'));
|
|
138
|
+
const argvFor = (t, stack) => (stack && t.argsByStack?.[stack]) || t.args;
|
|
139
|
+
function unitsOf(t, pairs) {
|
|
140
|
+
if (!usesStackDir(t))
|
|
141
|
+
return [{ dir: '.' }];
|
|
142
|
+
return pairs.filter(p => (t.stacks === 'all' || t.stacks.includes(p.stack)) && argvFor(t, p.stack).length > 0)
|
|
143
|
+
.map(p => ({ stack: p.stack, dir: p.dir, ...(p.forced ? { forced: true } : {}) }));
|
|
144
|
+
}
|
|
145
|
+
const hasWorkflows = (tree) => {
|
|
146
|
+
const dir = path.join(tree, '.github', 'workflows');
|
|
147
|
+
try {
|
|
148
|
+
return fs.readdirSync(dir).some(f => /\.ya?ml$/.test(f));
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
/** The report files one argv produces: those named in it as `{out}/<file>`, or the `stdoutFile`. */
|
|
155
|
+
const filesOf = (t, argv) => t.expect.files.filter(f => argv.some(a => a.includes(`{out}/${f}`)) || t.stdoutFile === f);
|
|
156
|
+
const firstLine = (text) => (text.split('\n').find(l => l.trim()) ?? '').trim().slice(0, 200);
|
|
157
|
+
/** The last non-empty stderr line: wrappers print install noise first and the fatal message last. */
|
|
158
|
+
const lastLine = (text) => (text.split('\n').filter(l => l.trim()).pop() ?? '').trim().slice(0, 200);
|
|
159
|
+
const stripAnsi = (text) => text.replace(/\x1b\[[0-9;]*m/g, '');
|
|
160
|
+
/** Commits Gitleaks says it read ("N commits scanned."); null when the line is absent. */
|
|
161
|
+
function commitsScanned(stderr) {
|
|
162
|
+
const m = /(\d+) commits? scanned/.exec(stripAnsi(stderr));
|
|
163
|
+
return m ? Number(m[1]) : null;
|
|
164
|
+
}
|
|
165
|
+
/** A detached worktree of `sha` from the main repository, registered in the guard (removed by `guard.close()`). */
|
|
166
|
+
function checkout(mainRoot, sha, guard) {
|
|
167
|
+
const tmp = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'ork-security-scan-tree-')));
|
|
168
|
+
const tree = path.join(tmp, 'tree');
|
|
169
|
+
guard.add('worktrees', tree);
|
|
170
|
+
const r = (0, node_child_process_1.spawnSync)('git', ['worktree', 'add', '--detach', tree, sha], { cwd: mainRoot, encoding: 'utf8', timeout: GIT_TIMEOUT_MS * 5 });
|
|
171
|
+
if (r.error || r.status !== 0)
|
|
172
|
+
throw Error(`worktree-failed: ${firstLine(r.stderr ?? '')}`);
|
|
173
|
+
return tree;
|
|
174
|
+
}
|
|
175
|
+
function cacheRoot() {
|
|
176
|
+
const xdg = process.env.XDG_CACHE_HOME;
|
|
177
|
+
return path.join(xdg && path.isAbsolute(xdg) ? xdg : path.join(os.homedir(), '.cache'), 'orkastery', 'security-scan');
|
|
178
|
+
}
|
|
179
|
+
const sha256 = (text) => crypto.createHash('sha256').update(text).digest('hex');
|
|
180
|
+
const slug = (dir) => (dir === '.' ? 'root' : dir.replace(/[^A-Za-z0-9._-]/g, '_'));
|
|
181
|
+
const remaining = (ctx) => ctx.deadline - Date.now();
|
|
182
|
+
/**
|
|
183
|
+
* Whether base..HEAD has any patch content for Gitleaks to read. A range of merges without conflict resolution, empty
|
|
184
|
+
* commits or pure renames has none, and Gitleaks then reports 0 commits scanned with nothing skipped. A git error
|
|
185
|
+
* (including output too large for the buffer) counts as content: the strict reading keeps `zero-commits-scanned`.
|
|
186
|
+
*/
|
|
187
|
+
function rangeHasPatch(ctx) {
|
|
188
|
+
if (ctx.rangePatch === undefined) {
|
|
189
|
+
const out = git(ctx.mainRoot, ['log', '-p', '--format=', `${ctx.base}..${ctx.head}`]);
|
|
190
|
+
ctx.rangePatch = out === null || out !== '';
|
|
191
|
+
}
|
|
192
|
+
return ctx.rangePatch;
|
|
193
|
+
}
|
|
194
|
+
/** Host path of the tool config: the project's `.ork-security/<file>` in the HEAD tree, else the embedded default. */
|
|
195
|
+
function configOf(t, ctx) {
|
|
196
|
+
const name = executor_1.CONFIG_FILES[t.name];
|
|
197
|
+
if (!name)
|
|
198
|
+
return undefined;
|
|
199
|
+
const project = path.join(ctx.headTree, '.ork-security', name);
|
|
200
|
+
return fs.existsSync(project) ? project : path.join((0, executor_1.defaultsDir)(), name);
|
|
201
|
+
}
|
|
202
|
+
/** The scan of one tool on one tree for one unit; `extra` adds the `extraArgs` (Gitleaks git scan, HEAD only). */
|
|
203
|
+
function scanTree(t, executor, tree, unit, ctx, extra) {
|
|
204
|
+
const fail = (kind, reason) => ({ outcome: { kind, reason }, lists: [] });
|
|
205
|
+
const configHost = configOf(t, ctx);
|
|
206
|
+
const rangeArgs = extra ? t.extraArgs ?? [] : [];
|
|
207
|
+
if (rangeArgs.length && ctx.rangeCommits === null)
|
|
208
|
+
return fail('failed', 'commit-range-unresolved');
|
|
209
|
+
const argvs = [argvFor(t, unit.stack), ...(ctx.rangeCommits ? rangeArgs : [])];
|
|
210
|
+
const lists = [];
|
|
211
|
+
const partial = [];
|
|
212
|
+
for (const argv of argvs) {
|
|
213
|
+
const timeoutMs = Math.min(t.timeoutMs, remaining(ctx));
|
|
214
|
+
if (timeoutMs <= 0)
|
|
215
|
+
return fail('failed', 'timeout');
|
|
216
|
+
const isGitScan = argv !== argvs[0] && t.baseMode === 'commit-range';
|
|
217
|
+
const out = fs.mkdtempSync(path.join(os.tmpdir(), 'ork-security-scan-out-'));
|
|
218
|
+
ctx.guard.add('outDirs', out);
|
|
219
|
+
try {
|
|
220
|
+
const inv = {
|
|
221
|
+
tool: t, argv, srcHost: tree, outHost: out, base: ctx.base, stackDir: unit.dir, configHost, timeoutMs, guard: ctx.guard,
|
|
222
|
+
// The tree is a linked worktree: its `.git` file points into the main `.git`, mounted at its own path.
|
|
223
|
+
sameMounts: isGitScan ? [path.join(ctx.mainRoot, '.git')] : [],
|
|
224
|
+
};
|
|
225
|
+
const r = executor === 'docker' ? (0, executor_1.runDocker)(inv) : (0, executor_1.runNative)(inv);
|
|
226
|
+
if (r.timedOut)
|
|
227
|
+
return fail('failed', 'timeout');
|
|
228
|
+
// actionlint exits 4 when there is no workflow to read (the HEAD precheck already caught .gitlab-ci.yml).
|
|
229
|
+
if (r.status === 4 && t.category === 'workflow') {
|
|
230
|
+
lists.push([]);
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
if (r.status === 3 && /no-requirements-file/.test(r.stderr))
|
|
234
|
+
return fail('unavailable', 'no-requirements-file');
|
|
235
|
+
if (r.status !== 0) {
|
|
236
|
+
// A secret scanner's stderr is never quoted: it could carry the value.
|
|
237
|
+
const detail = t.category === 'secret' ? '' : lastLine(stripAnsi(r.stderr));
|
|
238
|
+
return fail('failed', `exit-${r.status ?? 'signal'}${detail ? `: ${detail}` : ''}`);
|
|
239
|
+
}
|
|
240
|
+
if (isGitScan && !commitsScanned(r.stderr) && rangeHasPatch(ctx))
|
|
241
|
+
return fail('failed', 'zero-commits-scanned');
|
|
242
|
+
if (t.stdoutFile)
|
|
243
|
+
fs.writeFileSync(path.join(out, t.stdoutFile), r.stdout, { mode: 0o600 });
|
|
244
|
+
const findings = [];
|
|
245
|
+
for (const file of filesOf(t, argv)) {
|
|
246
|
+
const f = path.join(out, file);
|
|
247
|
+
if (!fs.existsSync(f))
|
|
248
|
+
return fail('failed', `missing-output: ${file}`);
|
|
249
|
+
const nctx = { root: executor === 'docker' ? '/src' : tree, stackDir: unit.dir };
|
|
250
|
+
try {
|
|
251
|
+
const content = fs.readFileSync(f, 'utf8');
|
|
252
|
+
findings.push(...normalize_1.NORMALIZERS[t.name](content, nctx));
|
|
253
|
+
const p = (0, normalize_1.partialOf)(t.name, content);
|
|
254
|
+
if (p)
|
|
255
|
+
partial.push(p);
|
|
256
|
+
}
|
|
257
|
+
catch (e) {
|
|
258
|
+
const msg = e.message;
|
|
259
|
+
return fail('failed', msg.startsWith('security-scan.parse') || t.category !== 'secret' ? msg : 'security-scan.parse: unreadable output');
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
lists.push(findings);
|
|
263
|
+
}
|
|
264
|
+
finally {
|
|
265
|
+
// The raw output (Gitleaks keeps the secret value) never outlives the run.
|
|
266
|
+
fs.rmSync(out, { recursive: true, force: true });
|
|
267
|
+
ctx.guard.remove('outDirs', out);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
const findings = lists.length > 1 ? (0, compare_1.mergeByFingerprint)(lists) : lists.flat();
|
|
271
|
+
return { outcome: { kind: 'ran', findings, ...(partial.length ? { reason: partial.join('; ') } : {}) }, lists };
|
|
272
|
+
}
|
|
273
|
+
/** Base findings of one unit: cache, else the base worktree; "nothing to scan" on the base is an empty list. */
|
|
274
|
+
function scanBase(t, executor, unit, ctx, headMain) {
|
|
275
|
+
if (ctx.base === ctx.head)
|
|
276
|
+
return { kind: 'ran', findings: headMain };
|
|
277
|
+
const nativeVersion = executor === 'native' ? (0, executor_1.nativeVersionOf)(t, ctx.probe) : '';
|
|
278
|
+
const id = executor === 'docker' ? (t.image.split('@sha256:')[1] ?? sha256(t.image)).slice(0, 12) : sha256(nativeVersion).slice(0, 12);
|
|
279
|
+
const file = path.join(cacheRoot(), ctx.base, `${t.name}-${id}${unit.stack ? `-${unit.stack}-${slug(unit.dir)}` : ''}.json`);
|
|
280
|
+
const configHost = configOf(t, ctx);
|
|
281
|
+
const key = sha256(JSON.stringify({ version: exports.SCAN_CACHE_VERSION, tool: t, executor, nativeVersion, unit,
|
|
282
|
+
config: configHost ? fs.readFileSync(configHost, 'utf8') : null }));
|
|
283
|
+
try {
|
|
284
|
+
const cached = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
285
|
+
if (cached.key === key && Array.isArray(cached.findings))
|
|
286
|
+
return { kind: 'ran', findings: cached.findings };
|
|
287
|
+
}
|
|
288
|
+
catch { /* absent or unreadable: scan again */ }
|
|
289
|
+
ctx.baseTree ??= checkout(ctx.mainRoot, ctx.base, ctx.guard);
|
|
290
|
+
const tree = ctx.baseTree;
|
|
291
|
+
if (unit.dir !== '.' && !fs.existsSync(path.join(tree, unit.dir)))
|
|
292
|
+
return { kind: 'ran', findings: [] };
|
|
293
|
+
if (t.category === 'workflow' && !hasWorkflows(tree))
|
|
294
|
+
return { kind: 'ran', findings: [] };
|
|
295
|
+
// A detected stack whose manifest is absent from the base at that dir (a project adopting it, or moving it, in this
|
|
296
|
+
// change): nothing to scan on the base, not a failure. A stack forced by `config.stacks.add` has no manifest by design
|
|
297
|
+
// and is always scanned. A manifest moved from `a/` to `b/` leaves `b/` empty on the base, so its findings are `new`.
|
|
298
|
+
if (unit.stack && !unit.forced && !(0, detect_1.hasStackManifest)(tree, unit.stack, unit.dir))
|
|
299
|
+
return { kind: 'ran', findings: [] };
|
|
300
|
+
const { outcome } = scanTree(t, executor, tree, unit, ctx, false);
|
|
301
|
+
if (outcome.kind === 'unavailable')
|
|
302
|
+
return { kind: 'ran', findings: [] };
|
|
303
|
+
if (outcome.kind === 'ran') {
|
|
304
|
+
try {
|
|
305
|
+
fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
|
|
306
|
+
fs.writeFileSync(file, JSON.stringify({ key, findings: outcome.findings }), { mode: 0o600 });
|
|
307
|
+
}
|
|
308
|
+
catch { /* a cache that cannot be written only costs time */ }
|
|
309
|
+
}
|
|
310
|
+
return outcome;
|
|
311
|
+
}
|
|
312
|
+
const DOCKER_OFF = (p) => ({ ...p, dockerCli: () => false, dockerDaemon: () => false });
|
|
313
|
+
function scanTool(t, units, config, ctx) {
|
|
314
|
+
const result = (state, executor, reason, head = [], base = []) => ({
|
|
315
|
+
run: { name: t.name, category: t.category, version: executor === 'native' ? (0, executor_1.nativeVersionOf)(t, ctx.probe) : t.version, executor, state, ...(reason ? { reason } : {}), counts: emptyCounts() },
|
|
316
|
+
head, base,
|
|
317
|
+
});
|
|
318
|
+
if (config.tools[t.name]?.disabled)
|
|
319
|
+
return result('unavailable', null, 'disabled');
|
|
320
|
+
const choice = (0, executor_1.chooseExecutor)(t, ctx.probe);
|
|
321
|
+
if (choice.kind === 'unavailable')
|
|
322
|
+
return result('unavailable', null, choice.reason);
|
|
323
|
+
let executor = choice.kind;
|
|
324
|
+
if (executor === 'docker') {
|
|
325
|
+
if (!ctx.images.has(t.image))
|
|
326
|
+
ctx.images.set(t.image, (0, executor_1.ensureImage)(t.image, Math.min(t.timeoutMs, remaining(ctx))));
|
|
327
|
+
if (!ctx.images.get(t.image)) {
|
|
328
|
+
// Docker -> native -> unavailable (spec 5.2.1): an image that cannot be pulled falls back to the native binary.
|
|
329
|
+
const native = (0, executor_1.chooseExecutor)(t, DOCKER_OFF(ctx.probe));
|
|
330
|
+
if (native.kind !== 'native') {
|
|
331
|
+
return result(remaining(ctx) <= 0 ? 'failed' : 'unavailable', null, remaining(ctx) <= 0 ? 'timeout' : 'image-pull-failed');
|
|
332
|
+
}
|
|
333
|
+
executor = 'native';
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (t.category === 'workflow' && !hasWorkflows(ctx.headTree)) {
|
|
337
|
+
if (fs.existsSync(path.join(ctx.headTree, '.gitlab-ci.yml')))
|
|
338
|
+
return result('unavailable', executor, 'unsupported-ci');
|
|
339
|
+
return result('ran', executor); // no CI workflow at all: nothing to scan, nothing found
|
|
340
|
+
}
|
|
341
|
+
const outcomes = [];
|
|
342
|
+
const head = [];
|
|
343
|
+
const base = [];
|
|
344
|
+
const partial = [];
|
|
345
|
+
for (const unit of units) {
|
|
346
|
+
const h = scanTree(t, executor, ctx.headTree, unit, ctx, true);
|
|
347
|
+
if (h.outcome.kind !== 'ran') {
|
|
348
|
+
outcomes.push(h.outcome);
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
let b;
|
|
352
|
+
try {
|
|
353
|
+
b = scanBase(t, executor, unit, ctx, h.lists[0] ?? []);
|
|
354
|
+
}
|
|
355
|
+
catch (e) {
|
|
356
|
+
b = { kind: 'failed', reason: firstLine(e.message) };
|
|
357
|
+
}
|
|
358
|
+
if (b.kind === 'ran') {
|
|
359
|
+
outcomes.push(h.outcome);
|
|
360
|
+
head.push(...h.outcome.findings);
|
|
361
|
+
base.push(...b.findings);
|
|
362
|
+
// Partial coverage of the HEAD scan is reported; on the base it only makes HEAD findings `new` (stricter).
|
|
363
|
+
if (h.outcome.reason)
|
|
364
|
+
partial.push(h.outcome.reason);
|
|
365
|
+
}
|
|
366
|
+
else
|
|
367
|
+
outcomes.push({ kind: b.kind, reason: `base-${b.reason}` });
|
|
368
|
+
}
|
|
369
|
+
const worst = outcomes.find(o => o.kind === 'failed') ?? outcomes.find(o => o.kind === 'unavailable');
|
|
370
|
+
if (worst && worst.kind !== 'ran')
|
|
371
|
+
return result(worst.kind, executor, worst.reason);
|
|
372
|
+
return result('ran', executor, partial.join('; ') || undefined, head, base);
|
|
373
|
+
}
|
|
374
|
+
const failedAll = (tools, config, reason) => tools.map(t => ({
|
|
375
|
+
name: t.name, category: t.category, version: t.version, executor: null,
|
|
376
|
+
state: config.tools[t.name]?.disabled ? 'unavailable' : 'failed', reason: config.tools[t.name]?.disabled ? 'disabled' : reason, counts: emptyCounts(),
|
|
377
|
+
}));
|
|
378
|
+
exports.failedAll = failedAll;
|
|
379
|
+
function runScan(opts) {
|
|
380
|
+
const start = path.resolve(opts.root);
|
|
381
|
+
const root = git(start, ['rev-parse', '--show-toplevel']) ?? start;
|
|
382
|
+
const generatedAt = (opts.now ?? (() => new Date()))().toISOString();
|
|
383
|
+
const head = git(root, ['rev-parse', 'HEAD']) ?? '';
|
|
384
|
+
let mainRoot = root;
|
|
385
|
+
try {
|
|
386
|
+
mainRoot = (0, estado_thread_1.raizDoEstado)(root);
|
|
387
|
+
}
|
|
388
|
+
catch { /* not a linked worktree git understands: use the root itself */ }
|
|
389
|
+
const report = { schema: 'ork.security-scan/v1', base: '', head, generatedAt, stacks: [], tools: [], excluded: [], findings: [] };
|
|
390
|
+
const catalog = opts.tools ?? (0, tools_1.loadTools)();
|
|
391
|
+
if (!head) {
|
|
392
|
+
// No commit to scan: nothing is checked out; stacks and config come from the directory as it is.
|
|
393
|
+
const config = (0, config_1.loadConfig)(root);
|
|
394
|
+
const pairs = stackPairs(root, config);
|
|
395
|
+
report.stacks = [...new Set(pairs.map(p => p.stack))];
|
|
396
|
+
report.tools = (0, exports.failedAll)((0, tools_1.toolsFor)(report.stacks, catalog).filter(t => unitsOf(t, pairs).length > 0), config, 'base-unresolved');
|
|
397
|
+
return report;
|
|
398
|
+
}
|
|
399
|
+
const guard = new guard_1.RoundGuard(mainRoot);
|
|
400
|
+
try {
|
|
401
|
+
let headTree;
|
|
402
|
+
try {
|
|
403
|
+
headTree = checkout(mainRoot, head, guard);
|
|
404
|
+
}
|
|
405
|
+
catch (e) {
|
|
406
|
+
const config = (0, config_1.loadConfig)(root);
|
|
407
|
+
const pairs = stackPairs(root, config);
|
|
408
|
+
report.stacks = [...new Set(pairs.map(p => p.stack))];
|
|
409
|
+
report.tools = (0, exports.failedAll)((0, tools_1.toolsFor)(report.stacks, catalog).filter(t => unitsOf(t, pairs).length > 0), config, `head-${firstLine(e.message)}`);
|
|
410
|
+
return report;
|
|
411
|
+
}
|
|
412
|
+
const config = (0, config_1.loadConfig)(headTree);
|
|
413
|
+
const dismissals = (0, config_1.loadDismissals)(headTree);
|
|
414
|
+
const pairs = stackPairs(headTree, config);
|
|
415
|
+
report.stacks = [...new Set(pairs.map(p => p.stack))];
|
|
416
|
+
const tools = (0, tools_1.toolsFor)(report.stacks, catalog).filter(t => unitsOf(t, pairs).length > 0);
|
|
417
|
+
const base = resolveBase(root, path.resolve(opts.stateRoot), opts.base);
|
|
418
|
+
if (!base) {
|
|
419
|
+
report.tools = (0, exports.failedAll)(tools, config, 'base-unresolved');
|
|
420
|
+
return report;
|
|
421
|
+
}
|
|
422
|
+
report.base = base;
|
|
423
|
+
const count = git(root, ['rev-list', '--count', `${base}..${head}`]);
|
|
424
|
+
const ctx = {
|
|
425
|
+
headTree, mainRoot, base, head, rangeCommits: count === null ? null : Number(count),
|
|
426
|
+
probe: opts.probe ?? (0, executor_1.defaultProbe)(), guard, images: new Map(),
|
|
427
|
+
deadline: opts.deadlineMs === undefined ? Infinity : Date.now() + opts.deadlineMs,
|
|
428
|
+
baseTree: base === head ? headTree : undefined,
|
|
429
|
+
};
|
|
430
|
+
const excluded = new Set();
|
|
431
|
+
for (const t of tools) {
|
|
432
|
+
const r = scanTool(t, unitsOf(t, pairs), config, ctx);
|
|
433
|
+
if (r.run.state === 'ran') {
|
|
434
|
+
const c = (0, compare_1.classify)(r.head, r.base, dismissals, config.exclude);
|
|
435
|
+
for (const f of c.findings)
|
|
436
|
+
r.run.counts[f.status][f.severity]++;
|
|
437
|
+
report.findings.push(...c.findings);
|
|
438
|
+
c.excluded.forEach(e => excluded.add(e));
|
|
439
|
+
}
|
|
440
|
+
report.tools.push(r.run);
|
|
441
|
+
}
|
|
442
|
+
report.excluded = [...excluded].sort();
|
|
443
|
+
return report;
|
|
444
|
+
}
|
|
445
|
+
finally {
|
|
446
|
+
guard.close();
|
|
447
|
+
}
|
|
448
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.loadTools = loadTools;
|
|
37
|
+
exports.loadAgileTools = loadAgileTools;
|
|
38
|
+
exports.toolsFor = toolsFor;
|
|
39
|
+
const fs = __importStar(require("node:fs"));
|
|
40
|
+
const path = __importStar(require("node:path"));
|
|
41
|
+
/** Catalog location: dist/security-scan (and the package) or dist-test/src/security-scan (tests), the same two-level lookup the other readers use. */
|
|
42
|
+
function catalogFile() {
|
|
43
|
+
const file = [path.join(__dirname, '..', '..', 'assets', 'security-scan', 'tools.json'),
|
|
44
|
+
path.join(__dirname, '..', '..', '..', 'assets', 'security-scan', 'tools.json')].find(fs.existsSync);
|
|
45
|
+
if (!file)
|
|
46
|
+
throw Error('security-scan.catalog.unavailable');
|
|
47
|
+
return file;
|
|
48
|
+
}
|
|
49
|
+
function loadTools() {
|
|
50
|
+
return JSON.parse(fs.readFileSync(catalogFile(), 'utf8'));
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* RM-072 (D13): o catalogo so do Agile Check (`tools-agil.json`, ao lado do `tools.json`): `osv-scanner` e `oxlint`,
|
|
54
|
+
* atras de `check.agile.osv_scanner` e `check.agile.oxlint`. O Classic nunca le este arquivo.
|
|
55
|
+
*/
|
|
56
|
+
function loadAgileTools() {
|
|
57
|
+
return JSON.parse(fs.readFileSync(path.join(path.dirname(catalogFile()), 'tools-agil.json'), 'utf8'));
|
|
58
|
+
}
|
|
59
|
+
/** The tools that run for at least one of the stacks (`all` tools always run). */
|
|
60
|
+
function toolsFor(stacks, tools = loadTools()) {
|
|
61
|
+
return tools.filter(t => t.stacks === 'all' || t.stacks.some(s => stacks.includes(s)));
|
|
62
|
+
}
|