wize-dev-kit 0.16.0 → 0.16.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/AGENTS.md +37 -0
- package/ARCH.md +77 -24
- package/CHANGELOG.md +4 -0
- package/DECISIONS.md +27 -1
- package/README.es.md +295 -0
- package/README.pt-BR.md +293 -0
- package/ROSTER.md +11 -3
- package/adapters/README.md +8 -7
- package/adapters/antigravity/README.md +5 -5
- package/adapters/claude-code/README.md +1 -1
- package/adapters/codex/README.md +5 -5
- package/adapters/codex/adapter.yaml +1 -1
- package/adapters/codex/render.js +1 -1
- package/adapters/continue/README.md +5 -5
- package/adapters/cursor/README.md +2 -2
- package/adapters/generic/README.md +1 -1
- package/adapters/hermes/README.md +23 -0
- package/adapters/hermes/adapter.yaml +8 -0
- package/adapters/hermes/render.js +17 -0
- package/adapters/kimi-code/README.md +5 -5
- package/adapters/kimi-code/render.js +2 -2
- package/adapters/opencode/README.md +3 -3
- package/adapters/opencode/render.js +27 -1
- package/adapters/windsurf/README.md +3 -3
- package/docs/harnesses/antigravity.md +20 -0
- package/docs/harnesses/antigravity.pt-BR.md +20 -0
- package/docs/harnesses/claude-code.md +23 -0
- package/docs/harnesses/claude-code.pt-BR.md +23 -0
- package/docs/harnesses/codex.md +21 -0
- package/docs/harnesses/codex.pt-BR.md +21 -0
- package/docs/harnesses/continue.md +21 -0
- package/docs/harnesses/continue.pt-BR.md +21 -0
- package/docs/harnesses/cursor.md +22 -0
- package/docs/harnesses/cursor.pt-BR.md +22 -0
- package/docs/harnesses/generic.md +20 -0
- package/docs/harnesses/generic.pt-BR.md +20 -0
- package/docs/harnesses/hermes.md +26 -0
- package/docs/harnesses/hermes.pt-BR.md +26 -0
- package/docs/harnesses/kimi-code.md +19 -0
- package/docs/harnesses/kimi-code.pt-BR.md +19 -0
- package/docs/harnesses/opencode.md +31 -0
- package/docs/harnesses/opencode.pt-BR.md +31 -0
- package/docs/harnesses/windsurf.md +22 -0
- package/docs/harnesses/windsurf.pt-BR.md +22 -0
- package/package.json +1 -1
- package/schemas/agent.schema.json +2 -2
- package/schemas/workflow.schema.json +1 -1
- package/src/app-overlay/wize-app-release-channels/workflow.md +2 -0
- package/src/app-overlay/wize-app-scaffold/workflow.md +2 -0
- package/src/app-overlay/wize-app-store-listing/workflow.md +2 -0
- package/src/builder-skills/wize-create-agent/workflow.md +2 -0
- package/src/builder-skills/wize-create-skill/workflow.md +2 -0
- package/src/builder-skills/wize-create-workflow/workflow.md +2 -0
- package/src/core-skills/wize-advanced-elicitation/skill.md +2 -0
- package/src/core-skills/wize-brainstorming/skill.md +2 -0
- package/src/core-skills/wize-customize/skill.md +116 -0
- package/src/core-skills/wize-editorial-review-prose/skill.md +94 -0
- package/src/core-skills/wize-editorial-review-structure/skill.md +99 -0
- package/src/core-skills/wize-grill/skill.md +77 -0
- package/src/core-skills/wize-index-docs/skill.md +119 -0
- package/src/core-skills/wize-pre-pr-check/skill.md +134 -0
- package/src/core-skills/wize-review-adversarial/skill.md +3 -0
- package/src/core-skills/wize-review-edge-case-hunter/skill.md +115 -0
- package/src/core-skills/wize-shard-doc/skill.md +2 -0
- package/src/core-skills/wize-spec/assets/headless-schemas.md +39 -0
- package/src/core-skills/wize-spec/assets/spec-template.md +40 -0
- package/src/core-skills/wize-spec/customize.toml +20 -0
- package/src/core-skills/wize-spec/skill.md +112 -0
- package/src/method-skills/1-analysis/wize-agent-analyst/agent.yaml +3 -0
- package/src/method-skills/1-analysis/wize-document-project/documentation-requirements.csv +12 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/api-contracts-template.md +38 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/architecture-template.md +54 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/component-inventory-template.md +40 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/contribution-guide-template.md +34 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/data-models-template.md +36 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/deep-dive-template.md +312 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/deployment-guide-template.md +42 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/development-guide-template.md +61 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/index-template.md +185 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/project-overview-template.md +110 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/project-scan-report-schema.json +159 -0
- package/src/method-skills/1-analysis/wize-document-project/templates/source-tree-template.md +142 -0
- package/src/method-skills/1-analysis/wize-document-project/workflow.md +213 -20
- package/src/method-skills/1-analysis/wize-domain-research/customize.toml +14 -0
- package/src/method-skills/1-analysis/wize-domain-research/domain-steps/step-01-init.md +49 -0
- package/src/method-skills/1-analysis/wize-domain-research/domain-steps/step-02-domain-analysis.md +39 -0
- package/src/method-skills/1-analysis/wize-domain-research/domain-steps/step-03-competitive-landscape.md +39 -0
- package/src/method-skills/1-analysis/wize-domain-research/domain-steps/step-04-regulatory-focus.md +39 -0
- package/src/method-skills/1-analysis/wize-domain-research/domain-steps/step-05-technical-trends.md +39 -0
- package/src/method-skills/1-analysis/wize-domain-research/domain-steps/step-06-research-synthesis.md +43 -0
- package/src/method-skills/1-analysis/wize-domain-research/research.template.md +31 -0
- package/src/method-skills/1-analysis/wize-domain-research/workflow.md +58 -0
- package/src/method-skills/1-analysis/wize-market-research/customize.toml +14 -0
- package/src/method-skills/1-analysis/wize-market-research/research.template.md +31 -0
- package/src/method-skills/1-analysis/wize-market-research/steps/step-01-init.md +57 -0
- package/src/method-skills/1-analysis/wize-market-research/steps/step-02-customer-behavior.md +39 -0
- package/src/method-skills/1-analysis/wize-market-research/steps/step-03-customer-pain-points.md +37 -0
- package/src/method-skills/1-analysis/wize-market-research/steps/step-04-customer-decisions.md +39 -0
- package/src/method-skills/1-analysis/wize-market-research/steps/step-05-competitive-analysis.md +37 -0
- package/src/method-skills/1-analysis/wize-market-research/steps/step-06-research-completion.md +47 -0
- package/src/method-skills/1-analysis/wize-market-research/workflow.md +66 -0
- package/src/method-skills/1-analysis/wize-prfaq/workflow.md +152 -11
- package/src/method-skills/1-analysis/wize-product-brief/workflow.md +92 -19
- package/src/method-skills/1-analysis/wize-refresh-knowledge/workflow.md +129 -0
- package/src/method-skills/1-analysis/wize-research/workflow.md +39 -12
- package/src/method-skills/1-analysis/wize-technical-research/customize.toml +14 -0
- package/src/method-skills/1-analysis/wize-technical-research/research.template.md +31 -0
- package/src/method-skills/1-analysis/wize-technical-research/technical-steps/step-01-init.md +49 -0
- package/src/method-skills/1-analysis/wize-technical-research/technical-steps/step-02-technical-overview.md +45 -0
- package/src/method-skills/1-analysis/wize-technical-research/technical-steps/step-03-integration-patterns.md +39 -0
- package/src/method-skills/1-analysis/wize-technical-research/technical-steps/step-04-architectural-patterns.md +39 -0
- package/src/method-skills/1-analysis/wize-technical-research/technical-steps/step-05-implementation-research.md +42 -0
- package/src/method-skills/1-analysis/wize-technical-research/technical-steps/step-06-research-synthesis.md +46 -0
- package/src/method-skills/1-analysis/wize-technical-research/workflow.md +60 -0
- package/src/method-skills/1-analysis/wize-trigger-map/workflow.md +82 -16
- package/src/method-skills/2-plan-workflows/wize-create-prd/workflow.md +135 -23
- package/src/method-skills/2-plan-workflows/wize-edit-prd/workflow.md +110 -0
- package/src/method-skills/2-plan-workflows/wize-ux-design/workflow.md +134 -28
- package/src/method-skills/2-plan-workflows/wize-ux-scenarios/workflow.md +93 -15
- package/src/method-skills/2-plan-workflows/wize-validate-prd/workflow.md +108 -12
- package/src/method-skills/3-solutioning/wize-check-implementation-readiness/workflow.md +103 -11
- package/src/method-skills/3-solutioning/wize-create-architecture/architecture-decision-template.md +41 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/customize.toml +20 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-01-init.md +95 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-01b-continue.md +32 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-02-context.md +126 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-03-starter.md +110 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-04-decisions.md +129 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-05-patterns.md +109 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-06-structure.md +97 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-07-validation.md +124 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-08-complete.md +59 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/workflow.md +55 -48
- package/src/method-skills/3-solutioning/wize-create-epics-and-stories/workflow.md +154 -15
- package/src/method-skills/3-solutioning/wize-design-system/workflow.md +184 -22
- package/src/method-skills/3-solutioning/wize-nfr-principles/workflow.md +145 -17
- package/src/method-skills/3-solutioning/wize-project-context/workflow.md +120 -0
- package/src/method-skills/3-solutioning/wize-tech-vision/workflow.md +130 -22
- package/src/method-skills/4-implementation/wize-agent-dev/persona.md +12 -0
- package/src/method-skills/4-implementation/wize-changelog/workflow.md +184 -0
- package/src/method-skills/4-implementation/wize-checkpoint-preview/workflow.md +117 -0
- package/src/method-skills/4-implementation/wize-code-review/customize.toml +21 -0
- package/src/method-skills/4-implementation/wize-code-review/steps/step-01-gather-context.md +64 -0
- package/src/method-skills/4-implementation/wize-code-review/steps/step-02-review.md +40 -0
- package/src/method-skills/4-implementation/wize-code-review/steps/step-03-triage.md +43 -0
- package/src/method-skills/4-implementation/wize-code-review/steps/step-04-present.md +100 -0
- package/src/method-skills/4-implementation/wize-code-review/workflow.md +54 -12
- package/src/method-skills/4-implementation/wize-correct-course/workflow.md +93 -0
- package/src/method-skills/4-implementation/wize-create-story/workflow.md +154 -10
- package/src/method-skills/4-implementation/wize-dev-story/workflow.md +185 -17
- package/src/method-skills/4-implementation/wize-investigate/workflow.md +123 -0
- package/src/method-skills/4-implementation/wize-quick-dev/workflow.md +136 -18
- package/src/method-skills/4-implementation/wize-release/workflow.md +162 -0
- package/src/method-skills/4-implementation/wize-retrospective/workflow.md +114 -10
- package/src/method-skills/4-implementation/wize-sprint-planning/workflow.md +78 -11
- package/src/method-skills/4-implementation/wize-sprint-status/workflow.md +45 -11
- package/src/method-skills/module.yaml +28 -0
- package/src/orchestrator-skills/module.yaml +7 -1
- package/src/orchestrator-skills/wize/skill.md +164 -0
- package/src/orchestrator-skills/wize-check/skill.md +98 -0
- package/src/orchestrator-skills/wize-help/skill.md +227 -67
- package/src/orchestrator-skills/wize-onboarding/workflow.md +78 -14
- package/src/orchestrator-skills/wize-orchestrator/agent.yaml +2 -0
- package/src/orchestrator-skills/wize-orchestrator/persona.md +44 -3
- package/src/orchestrator-skills/wize-party-mode/workflow.md +2 -0
- package/src/security-overlay/_shared/allowlist.js +154 -0
- package/src/security-overlay/_shared/backlog.js +169 -0
- package/src/security-overlay/_shared/cli-runner.js +87 -0
- package/src/security-overlay/_shared/cvss.js +108 -0
- package/src/security-overlay/_shared/detect.js +125 -0
- package/src/security-overlay/_shared/install-script.js +205 -0
- package/src/security-overlay/_shared/invoke-phase.js +86 -0
- package/src/security-overlay/_shared/owasp.js +56 -0
- package/src/security-overlay/_shared/partial.js +225 -0
- package/src/security-overlay/_shared/preflight.js +175 -0
- package/src/security-overlay/_shared/scope-gate.js +172 -0
- package/src/security-overlay/_shared/scope-parser.js +147 -0
- package/src/security-overlay/agents/red-teamer/agent.yaml +54 -0
- package/src/security-overlay/agents/red-teamer/persona.md +49 -0
- package/src/security-overlay/data/common.txt +115 -0
- package/src/security-overlay/data/owasp-top10.json +15 -0
- package/src/security-overlay/data/tool-allowlist.json +31 -0
- package/src/security-overlay/skills/wize-sec-enumerate/scripts/run-enumerate.js +180 -0
- package/src/security-overlay/skills/wize-sec-enumerate/skill.md +34 -0
- package/src/security-overlay/skills/wize-sec-exploit/data/common.txt +117 -0
- package/src/security-overlay/skills/wize-sec-exploit/scripts/run-ffuf.js +147 -0
- package/src/security-overlay/skills/wize-sec-exploit/scripts/run-nikto.js +145 -0
- package/src/security-overlay/skills/wize-sec-exploit/scripts/run-nuclei.js +176 -0
- package/src/security-overlay/skills/wize-sec-exploit/scripts/run-sqlmap.js +139 -0
- package/src/security-overlay/skills/wize-sec-pentest/scripts/run-pipeline.js +184 -0
- package/src/security-overlay/skills/wize-sec-pentest/skill.md +54 -0
- package/src/security-overlay/skills/wize-sec-recon/scripts/run-gitleaks.js +139 -0
- package/src/security-overlay/skills/wize-sec-recon/scripts/run-osv.js +227 -0
- package/src/security-overlay/skills/wize-sec-recon/scripts/run-recon.js +162 -0
- package/src/security-overlay/skills/wize-sec-recon/skill.md +37 -0
- package/src/security-overlay/skills/wize-sec-report/scripts/render-report.js +1033 -0
- package/src/security-overlay/skills/wize-sec-scope/scripts/generate-scope.js +106 -0
- package/src/security-overlay/skills/wize-sec-scope/skill.md +52 -0
- package/src/tea-skills/wize-qa-generate-e2e-tests/workflow.md +121 -0
- package/src/tea-skills/wize-tea-design/workflow.md +107 -13
- package/src/tea-skills/wize-tea-gate/workflow.md +118 -25
- package/src/tea-skills/wize-tea-nfr/workflow.md +107 -14
- package/src/tea-skills/wize-tea-review/workflow.md +123 -13
- package/src/tea-skills/wize-tea-risk/workflow.md +113 -10
- package/src/tea-skills/wize-tea-trace/workflow.md +86 -12
- package/src/web-overlay/wize-web-deploy/workflow.md +2 -0
- package/src/web-overlay/wize-web-scaffold/workflow.md +2 -0
- package/src/web-overlay/wize-web-seo-audit/workflow.md +2 -0
- package/tools/installer/baseline.js +133 -0
- package/tools/installer/commands/agent.js +197 -0
- package/tools/installer/commands/doctor.js +320 -0
- package/tools/installer/commands/document-project.js +93 -0
- package/tools/installer/commands/sync.js +45 -0
- package/tools/installer/commands/update.js +172 -0
- package/tools/installer/document-project/batch-scanner.js +93 -0
- package/tools/installer/document-project/classify.js +170 -0
- package/tools/installer/document-project/modes/deep-dive.js +196 -0
- package/tools/installer/document-project/modes/full-rescan.js +15 -0
- package/tools/installer/document-project/modes/initial-scan.js +100 -0
- package/tools/installer/document-project/modes/quick.js +211 -0
- package/tools/installer/document-project/render-index.js +101 -0
- package/tools/installer/document-project/state.js +110 -0
- package/tools/installer/onboarding.js +14 -20
- package/tools/installer/render-shared.js +159 -15
- package/tools/installer/setup-helpers.js +1 -0
- package/tools/installer/validators/run-all.js +3 -2
- package/tools/installer/version-check.js +117 -0
- package/tools/installer/wize-cli.js +500 -70
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
|
|
3
|
+
code: wize-sec-pentest
|
|
4
|
+
description: "Use quando o scope de segurança estiver definido e precisar executar o pipeline completo de pentest (recon → enumerate → exploit → report)."
|
|
5
|
+
name: wize-sec-pentest
|
|
6
|
+
overlay: security
|
|
7
|
+
module: security-overlay
|
|
8
|
+
owner: wize-sec-red-teamer # Natasha Romanoff
|
|
9
|
+
status: ready
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# wize-sec-pentest — Orchestrator (Security Overlay)
|
|
13
|
+
|
|
14
|
+
End-to-end security pipeline. Chains **recon → enumerate → sast → dast → report** through the four phase skills (`wize-sec-recon`, `wize-sec-enumerate`, `wize-sec-exploit`, `wize-sec-report`). Loads `.wize/security/scope.md` first; aborts the whole pipeline if the scope is missing or invalid.
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Default (passive — read-only checks only)
|
|
20
|
+
/wize-sec-pentest
|
|
21
|
+
|
|
22
|
+
# Active exploitation (sqlmap, ffuf active fuzzing) — requires scope acceptance
|
|
23
|
+
/wize-sec-pentest --active
|
|
24
|
+
|
|
25
|
+
# Custom scope path (default: .wize/security/scope.md)
|
|
26
|
+
/wize-sec-pentest --scope=/path/to/scope.md
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Phases (in order)
|
|
30
|
+
|
|
31
|
+
1. **recon** — nmap (passive) → `recon.md`
|
|
32
|
+
2. **enumerate** — nuclei passive, HTTP probing → `enumerate.md`
|
|
33
|
+
3. **sast** — gitleaks (secrets) + osv-scanner/grype (deps) → `sast.md` (run inside `wize-sec-recon` per the architecture decision)
|
|
34
|
+
4. **dast** — nuclei + nikto (passive) + sqlmap/ffuf (gated by `--active`) → `dast.md`
|
|
35
|
+
5. **report** — render `report.md` + `report.html` from all partials
|
|
36
|
+
|
|
37
|
+
Each phase produces a partial. The report phase consumes them. A phase that fails is marked `partial_status: skipped` and the pipeline continues with the next one.
|
|
38
|
+
|
|
39
|
+
## Gate
|
|
40
|
+
|
|
41
|
+
Before any phase, the orchestrator calls `loadScope()` (the shared gate). An invalid scope aborts the whole pipeline with the error code from `ScopeError`. The gate is the single source of truth — phases do not re-validate.
|
|
42
|
+
|
|
43
|
+
## Propagation
|
|
44
|
+
|
|
45
|
+
`--active` is read from argv and passed to every phase invocation. Each phase that supports active checks (currently `wize-sec-exploit`) decides what changes in active mode.
|
|
46
|
+
|
|
47
|
+
## Output
|
|
48
|
+
|
|
49
|
+
- `recon.md`, `enumerate.md`, `sast.md`, `dast.md` — partials (one per phase)
|
|
50
|
+
- `report.md`, `report.html` — final consolidated report
|
|
51
|
+
- `.refusals.log` — audit trail of out-of-scope refusals
|
|
52
|
+
- `.tools.json` — detection cache for the toolchain
|
|
53
|
+
|
|
54
|
+
Exits 0 if at least one phase ran; 1 if all phases failed; 2 if the scope gate aborted.
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// run-gitleaks.js — SAST secrets via gitleaks. Runs inside the
|
|
4
|
+
// wize-sec-recon skill (per the architecture decision that recon hosts
|
|
5
|
+
// the no-app-running checks). Writes a redaction into sast.md and stores
|
|
6
|
+
// the full gitleaks report in the security dir for the user to inspect.
|
|
7
|
+
|
|
8
|
+
const path = require('node:path');
|
|
9
|
+
const fs = require('node:fs');
|
|
10
|
+
const { execFileSync } = require('node:child_process');
|
|
11
|
+
|
|
12
|
+
const { filterArgs } = require('../../../_shared/allowlist.js');
|
|
13
|
+
const { writePartial, loadPartial } = require('../../../_shared/partial.js');
|
|
14
|
+
|
|
15
|
+
const REDACTED = '***REDACTED***';
|
|
16
|
+
|
|
17
|
+
// runGitleaks({ securityDir, scope, active, execFn?, detectFn?, reportFilename? })
|
|
18
|
+
// -> { ok, partialStatus, findingsCount }
|
|
19
|
+
async function runGitleaks(opts = {}) {
|
|
20
|
+
const sec = opts.securityDir;
|
|
21
|
+
const scope = opts.scope;
|
|
22
|
+
const active = opts.active === true;
|
|
23
|
+
const reportFilename = opts.reportFilename || 'gitleaks-report.json';
|
|
24
|
+
// The project to scan is the parent of `.wize/security/`. The scripts run
|
|
25
|
+
// with cwd = the kit, so we MUST point gitleaks at the target repo, not '.'.
|
|
26
|
+
const projectRoot = opts.projectRoot || (sec ? path.resolve(sec, '..', '..') : process.cwd());
|
|
27
|
+
|
|
28
|
+
const execFn = opts.execFn || ((bin, args) => {
|
|
29
|
+
return execFileSync(bin, args, { encoding: 'utf8', timeout: 5 * 60 * 1000 });
|
|
30
|
+
});
|
|
31
|
+
const detectFn = opts.detectFn || require('../../../_shared/detect.js').detectTools;
|
|
32
|
+
|
|
33
|
+
const tools = detectFn(['gitleaks'], { cacheDir: sec });
|
|
34
|
+
const present = !!(tools.gitleaks && tools.gitleaks.present);
|
|
35
|
+
|
|
36
|
+
if (!present) {
|
|
37
|
+
// Update or create sast.md with the degraded_checks entry.
|
|
38
|
+
mergeSast(sec, scope, active, tools, {
|
|
39
|
+
degraded: '- secrets: gitleaks ausente — instale gitleaks e re-rode para scan completo.'
|
|
40
|
+
});
|
|
41
|
+
return { ok: true, partialStatus: 'incomplete', findingsCount: 0 };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const reportPath = path.join(sec, reportFilename);
|
|
45
|
+
// gitleaks 8.18: `-s/--source` is the scan path, `-f/--report-format` is
|
|
46
|
+
// the output format (json), `-r/--report-path` is the file. (Earlier
|
|
47
|
+
// versions used `-f` for the path — do NOT use that here.)
|
|
48
|
+
const args = filterArgs('gitleaks', [
|
|
49
|
+
'detect', '--no-banner', '-s', projectRoot, '-f', 'json', '-r', reportPath, '--exit-code', '0'
|
|
50
|
+
]);
|
|
51
|
+
// gitleaks exits non-zero when it FINDS leaks (default exit-code 1). That
|
|
52
|
+
// is the success path for us, not an error — so we pass --exit-code 0 and
|
|
53
|
+
// also swallow any non-zero status defensively (we read the JSON report
|
|
54
|
+
// regardless).
|
|
55
|
+
try {
|
|
56
|
+
execFn('gitleaks', args, { timeout: 5 * 60 * 1000 });
|
|
57
|
+
} catch (_) {
|
|
58
|
+
// Leaks found (or gitleaks returned non-zero) — the report file is still
|
|
59
|
+
// written; we parse it below.
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Read the JSON report gitleaks wrote. It is an array of findings.
|
|
63
|
+
let findings = [];
|
|
64
|
+
if (fs.existsSync(reportPath)) {
|
|
65
|
+
try {
|
|
66
|
+
findings = JSON.parse(fs.readFileSync(reportPath, 'utf8'));
|
|
67
|
+
} catch (_) {
|
|
68
|
+
findings = [];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Build the secrets section from findings — ONLY redacted values.
|
|
73
|
+
const secretLines = findings.map(f => {
|
|
74
|
+
const file = f.File || f.file || '<unknown>';
|
|
75
|
+
const line = f.StartLine || f.startLine || '?';
|
|
76
|
+
const rule = f.RuleID || f.ruleID || 'unknown';
|
|
77
|
+
return `- **${file}** line ${line} rule \`${rule}\` — redacted_value: \`${REDACTED}\``;
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Merge into existing sast.md (preserve other sections like deps from E05-S02).
|
|
81
|
+
mergeSast(sec, scope, active, tools, {
|
|
82
|
+
secrets: secretLines.length ? secretLines.join('\n') : '_(nenhum secret encontrado)_'
|
|
83
|
+
});
|
|
84
|
+
return { ok: true, partialStatus: secretLines.length ? 'complete' : 'incomplete', findingsCount: secretLines.length };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Helper: load existing sast.md, update the relevant section, and write back.
|
|
88
|
+
// Preserves sections that other SAST scripts (e.g. run-osv) added.
|
|
89
|
+
function mergeSast(sec, scope, active, tools, update) {
|
|
90
|
+
const existing = loadPartial({ securityDir: sec, phase: 'sast' });
|
|
91
|
+
const sections = {};
|
|
92
|
+
let mergedTools = Object.assign({}, tools);
|
|
93
|
+
if (existing) {
|
|
94
|
+
if (existing.body) {
|
|
95
|
+
// Extract known section bodies from the existing partial.
|
|
96
|
+
const re = /## ([a-z_]+)\n\n([\s\S]*?)(?=\n## |$)/g;
|
|
97
|
+
let m;
|
|
98
|
+
while ((m = re.exec(existing.body)) !== null) {
|
|
99
|
+
sections[m[1]] = m[2].trim();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Preserve tools detected by sibling SAST scripts (gitleaks + osv each
|
|
103
|
+
// run separately; neither should clobber the other's tools block).
|
|
104
|
+
if (existing.frontmatter && existing.frontmatter.tools) {
|
|
105
|
+
mergedTools = Object.assign({}, existing.frontmatter.tools, tools);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Apply updates.
|
|
109
|
+
if (update.degraded) {
|
|
110
|
+
// Append to degraded_checks if present, else create.
|
|
111
|
+
if (sections.degraded_checks) sections.degraded_checks += '\n' + update.degraded;
|
|
112
|
+
else sections.degraded_checks = update.degraded;
|
|
113
|
+
}
|
|
114
|
+
if (update.secrets !== undefined) sections.secrets = update.secrets;
|
|
115
|
+
|
|
116
|
+
const status = sections.degraded_checks ? 'incomplete' : 'complete';
|
|
117
|
+
writePartial({
|
|
118
|
+
securityDir: sec,
|
|
119
|
+
phase: 'sast',
|
|
120
|
+
mode: active ? 'active' : 'passive',
|
|
121
|
+
scope,
|
|
122
|
+
status,
|
|
123
|
+
tools: mergedTools,
|
|
124
|
+
sections
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
module.exports = { runGitleaks, mergeSast, REDACTED };
|
|
129
|
+
|
|
130
|
+
if (require.main === module) {
|
|
131
|
+
require('../../../_shared/cli-runner.js').runFromArgv({
|
|
132
|
+
fn: ({ securityDir, scopePath, active, reportFilename } = {}) => {
|
|
133
|
+
const { loadScope } = require('../../../_shared/scope-gate.js');
|
|
134
|
+
const scope = loadScope(scopePath);
|
|
135
|
+
return runGitleaks({ securityDir, scope, active, reportFilename });
|
|
136
|
+
},
|
|
137
|
+
argMap: { 'securityDir': 'securityDir', 'scope': 'scopePath', 'active': 'active', 'reportFilename': 'reportFilename' }
|
|
138
|
+
});
|
|
139
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// run-osv.js — SAST dependencies via osv-scanner (primary) or grype (fallback).
|
|
4
|
+
// Auto-detects common manifest files in the project root and emits findings
|
|
5
|
+
// into sast.md (composing with secrets from run-gitleaks.js).
|
|
6
|
+
|
|
7
|
+
const path = require('node:path');
|
|
8
|
+
const fs = require('node:fs');
|
|
9
|
+
const { execFileSync } = require('node:child_process');
|
|
10
|
+
|
|
11
|
+
const { filterArgs } = require('../../../_shared/allowlist.js');
|
|
12
|
+
const { writePartial, loadPartial } = require('../../../_shared/partial.js');
|
|
13
|
+
|
|
14
|
+
const MANIFEST_FILES = [
|
|
15
|
+
'package.json', 'package-lock.json', 'yarn.lock', 'pnpm-lock.yaml',
|
|
16
|
+
'requirements.txt', 'Pipfile', 'Pipfile.lock', 'pyproject.toml', 'poetry.lock',
|
|
17
|
+
'go.mod', 'go.sum',
|
|
18
|
+
'Cargo.toml', 'Cargo.lock',
|
|
19
|
+
'composer.json', 'composer.lock',
|
|
20
|
+
'Gemfile', 'Gemfile.lock'
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
function detectManifests(root) {
|
|
24
|
+
const found = [];
|
|
25
|
+
for (const m of MANIFEST_FILES) {
|
|
26
|
+
if (fs.existsSync(path.join(root, m))) found.push(m);
|
|
27
|
+
}
|
|
28
|
+
return found;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function parseOsvReport(report) {
|
|
32
|
+
// osv-scanner JSON shape (simplified): { results: [{ packages: [{ package: {name, version}, vulnerabilities: [{id, severity, cvss: {score}}] }] }] }
|
|
33
|
+
const out = [];
|
|
34
|
+
const results = report && report.results ? report.results : [];
|
|
35
|
+
// Map a CVSS base score to a coarse severity label.
|
|
36
|
+
const sevFromScore = s => {
|
|
37
|
+
const n = parseFloat(s);
|
|
38
|
+
if (isNaN(n)) return null;
|
|
39
|
+
if (n === 0) return 'None';
|
|
40
|
+
if (n < 4) return 'Low';
|
|
41
|
+
if (n < 7) return 'Medium';
|
|
42
|
+
if (n < 9) return 'High';
|
|
43
|
+
return 'Critical';
|
|
44
|
+
};
|
|
45
|
+
for (const r of results) {
|
|
46
|
+
for (const p of (r.packages || [])) {
|
|
47
|
+
const name = p.package && p.package.name;
|
|
48
|
+
const version = p.package && p.package.version;
|
|
49
|
+
// osv-scanner v2 groups vulnerabilities and exposes a max_severity
|
|
50
|
+
// (CVSS) + aliases (CVE ids) per group. Prefer that; fall back to
|
|
51
|
+
// the raw vulnerabilities array for older shapes.
|
|
52
|
+
const groups = Array.isArray(p.groups) ? p.groups : [];
|
|
53
|
+
if (groups.length) {
|
|
54
|
+
for (const g of groups) {
|
|
55
|
+
const ids = g.aliases && g.aliases.length ? g.aliases : g.ids || [];
|
|
56
|
+
const cve = ids.find(x => /^CVE-/.test(x)) || ids[0] || '?';
|
|
57
|
+
const cvss = g.max_severity ? parseFloat(g.max_severity) : null;
|
|
58
|
+
out.push({ package: name, version, cve, severity: sevFromScore(g.max_severity) || 'UNKNOWN', cvss });
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
for (const v of (p.vulnerabilities || [])) {
|
|
62
|
+
const cve = v.id || '?';
|
|
63
|
+
const cvss = v.cvss && (typeof v.cvss === 'number' ? v.cvss : v.cvss.score);
|
|
64
|
+
out.push({ package: name, version, cve, severity: sevFromScore(cvss) || 'UNKNOWN', cvss });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function parseGrypeReport(report) {
|
|
73
|
+
// grype JSON shape: { matches: [{ artifact: {name, version}, vulnerability: {id, severity, cvss:[{metrics:{baseScore}}]} }] }
|
|
74
|
+
const out = [];
|
|
75
|
+
for (const m of (report && report.matches ? report.matches : [])) {
|
|
76
|
+
const name = m.artifact && m.artifact.name;
|
|
77
|
+
const version = m.artifact && m.artifact.version;
|
|
78
|
+
const v = m.vulnerability || {};
|
|
79
|
+
const cve = v.id || '?';
|
|
80
|
+
const severity = v.severity || 'UNKNOWN';
|
|
81
|
+
let cvss = null;
|
|
82
|
+
if (Array.isArray(v.cvss) && v.cvss[0] && v.cvss[0].metrics) {
|
|
83
|
+
cvss = v.cvss[0].metrics.baseScore;
|
|
84
|
+
} else if (typeof v.cvss === 'number') {
|
|
85
|
+
cvss = v.cvss;
|
|
86
|
+
}
|
|
87
|
+
out.push({ package: name, version, cve, severity, cvss });
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function renderDepsSection(findings) {
|
|
93
|
+
if (!findings.length) return '_(nenhuma dep vulnerável encontrada)_';
|
|
94
|
+
return findings.map(f => {
|
|
95
|
+
const cvss = f.cvss != null ? ` cvss=${f.cvss}` : '';
|
|
96
|
+
return `- **${f.package}@${f.version || '?'}** \`${f.cve}\` severity=${f.severity}${cvss}`;
|
|
97
|
+
}).join('\n');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// runOsv({ securityDir, scope, active, execFn?, detectFn?, manifestRoot?, reportFilename? })
|
|
101
|
+
async function runOsv(opts = {}) {
|
|
102
|
+
const sec = opts.securityDir;
|
|
103
|
+
const scope = opts.scope;
|
|
104
|
+
const active = opts.active === true;
|
|
105
|
+
// The project to scan is the parent of `.wize/security/`. Scripts run with
|
|
106
|
+
// cwd = the kit, so default to the target repo, not process.cwd().
|
|
107
|
+
const manifestRoot = opts.manifestRoot
|
|
108
|
+
|| (sec ? path.resolve(sec, '..', '..') : process.cwd());
|
|
109
|
+
const osvReportName = 'osv-report.json';
|
|
110
|
+
const grypeReportName = 'grype-report.json';
|
|
111
|
+
|
|
112
|
+
const execFn = opts.execFn || ((bin, args) => {
|
|
113
|
+
return execFileSync(bin, args, { encoding: 'utf8', timeout: 5 * 60 * 1000 });
|
|
114
|
+
});
|
|
115
|
+
const detectFn = opts.detectFn || require('../../../_shared/detect.js').detectTools;
|
|
116
|
+
|
|
117
|
+
const tools = detectFn(['osv-scanner', 'grype'], { cacheDir: sec });
|
|
118
|
+
const osvPresent = !!(tools['osv-scanner'] && tools['osv-scanner'].present);
|
|
119
|
+
const grypePresent = !!(tools.grype && tools.grype.present);
|
|
120
|
+
|
|
121
|
+
// No tools at all -> degraded.
|
|
122
|
+
if (!osvPresent && !grypePresent) {
|
|
123
|
+
mergeSast(sec, scope, active, tools, {
|
|
124
|
+
degraded: '- deps: osv-scanner e grype ausentes — instale um dos dois e re-rode.'
|
|
125
|
+
});
|
|
126
|
+
return { ok: true, partialStatus: 'incomplete', tool: null, findings: [] };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Manifest detection: warn if the project root has no known manifest.
|
|
130
|
+
const manifests = detectManifests(manifestRoot);
|
|
131
|
+
if (manifests.length === 0) {
|
|
132
|
+
mergeSast(sec, scope, active, tools, {
|
|
133
|
+
degraded: `- deps: nenhum manifesto encontrado em ${manifestRoot} (procurando package.json, requirements.txt, go.mod, Cargo.toml, pyproject.toml, composer.json, Gemfile).`
|
|
134
|
+
});
|
|
135
|
+
return { ok: true, partialStatus: 'incomplete', tool: null, findings: [] };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Pick tool. Prefer osv-scanner; fallback to grype.
|
|
139
|
+
let findings = [];
|
|
140
|
+
let tool = null;
|
|
141
|
+
if (osvPresent) {
|
|
142
|
+
tool = 'osv-scanner';
|
|
143
|
+
const reportPath = path.join(sec, osvReportName);
|
|
144
|
+
// osv-scanner v2 needs explicit lockfiles (`-L <path>`); recursive
|
|
145
|
+
// directory scan misses composer.lock/package-lock.json. We pass each
|
|
146
|
+
// detected lockfile we found. Non-zero exit = vulns found (success).
|
|
147
|
+
const LOCKFILES = ['composer.lock', 'package-lock.json', 'yarn.lock', 'pnpm-lock.yaml',
|
|
148
|
+
'requirements.txt', 'Pipfile.lock', 'poetry.lock', 'go.sum', 'Cargo.lock', 'Gemfile.lock'];
|
|
149
|
+
const lockArgs = [];
|
|
150
|
+
for (const lf of LOCKFILES) {
|
|
151
|
+
if (fs.existsSync(path.join(manifestRoot, lf))) {
|
|
152
|
+
lockArgs.push('-L', path.join(manifestRoot, lf));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const args = lockArgs.length
|
|
156
|
+
? filterArgs('osv-scanner', ['scan', 'source', ...lockArgs, '--format', 'json', '--output-file', reportPath])
|
|
157
|
+
: filterArgs('osv-scanner', ['scan', 'source', '-r', '--format', 'json', '--output-file', reportPath, manifestRoot]);
|
|
158
|
+
try {
|
|
159
|
+
execFn('osv-scanner', args, { timeout: 5 * 60 * 1000 });
|
|
160
|
+
} catch (_) { /* vulns found -> non-zero exit; report still written */ }
|
|
161
|
+
if (fs.existsSync(reportPath)) {
|
|
162
|
+
try { findings = parseOsvReport(JSON.parse(fs.readFileSync(reportPath, 'utf8'))); }
|
|
163
|
+
catch (_) { findings = []; }
|
|
164
|
+
}
|
|
165
|
+
} else if (grypePresent) {
|
|
166
|
+
tool = 'grype';
|
|
167
|
+
const reportPath = path.join(sec, grypeReportName);
|
|
168
|
+
const args = filterArgs('grype', ['dir:' + manifestRoot, '-o', 'json']);
|
|
169
|
+
execFn('grype', args, { timeout: 5 * 60 * 1000 });
|
|
170
|
+
if (fs.existsSync(reportPath)) {
|
|
171
|
+
try { findings = parseGrypeReport(JSON.parse(fs.readFileSync(reportPath, 'utf8'))); }
|
|
172
|
+
catch (_) { findings = []; }
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
mergeSast(sec, scope, active, tools, {
|
|
177
|
+
deps: renderDepsSection(findings)
|
|
178
|
+
});
|
|
179
|
+
return { ok: true, partialStatus: 'complete', tool, findings };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function mergeSast(sec, scope, active, tools, update) {
|
|
183
|
+
const existing = loadPartial({ securityDir: sec, phase: 'sast' });
|
|
184
|
+
const sections = {};
|
|
185
|
+
let mergedTools = Object.assign({}, tools);
|
|
186
|
+
if (existing) {
|
|
187
|
+
if (existing.body) {
|
|
188
|
+
const re = /## ([a-z_]+)\n\n([\s\S]*?)(?=\n## |$)/g;
|
|
189
|
+
let m;
|
|
190
|
+
while ((m = re.exec(existing.body)) !== null) {
|
|
191
|
+
sections[m[1]] = m[2].trim();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (existing.frontmatter && existing.frontmatter.tools) {
|
|
195
|
+
mergedTools = Object.assign({}, existing.frontmatter.tools, tools);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (update.degraded) {
|
|
199
|
+
sections.degraded_checks = sections.degraded_checks
|
|
200
|
+
? sections.degraded_checks + '\n' + update.degraded
|
|
201
|
+
: update.degraded;
|
|
202
|
+
}
|
|
203
|
+
if (update.deps !== undefined) sections.deps = update.deps;
|
|
204
|
+
const status = sections.degraded_checks ? 'incomplete' : 'complete';
|
|
205
|
+
writePartial({
|
|
206
|
+
securityDir: sec,
|
|
207
|
+
phase: 'sast',
|
|
208
|
+
mode: active ? 'active' : 'passive',
|
|
209
|
+
scope,
|
|
210
|
+
status,
|
|
211
|
+
tools: mergedTools,
|
|
212
|
+
sections
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
module.exports = { runOsv, parseOsvReport, parseGrypeReport, detectManifests, MANIFEST_FILES };
|
|
217
|
+
|
|
218
|
+
if (require.main === module) {
|
|
219
|
+
require('../../../_shared/cli-runner.js').runFromArgv({
|
|
220
|
+
fn: ({ securityDir, scopePath, active, manifestRoot, reportFilename } = {}) => {
|
|
221
|
+
const { loadScope } = require('../../../_shared/scope-gate.js');
|
|
222
|
+
const scope = loadScope(scopePath);
|
|
223
|
+
return runOsv({ securityDir, scope, active, manifestRoot, reportFilename });
|
|
224
|
+
},
|
|
225
|
+
argMap: { 'securityDir': 'securityDir', 'scope': 'scopePath', 'active': 'active', 'manifestRoot': 'manifestRoot', 'reportFilename': 'reportFilename' }
|
|
226
|
+
});
|
|
227
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// run-recon.js — the nmap portion of the wize-sec-recon skill.
|
|
4
|
+
// SAST (gitleaks, osv/grype) is implemented in E05 and lives in
|
|
5
|
+
// scripts/run-sast.js (sibling). The orchestrator (wize-sec-pentest) and
|
|
6
|
+
// any caller can invoke either script directly; this file only handles
|
|
7
|
+
// recon.
|
|
8
|
+
|
|
9
|
+
const path = require('node:path');
|
|
10
|
+
const { execFileSync } = require('node:child_process');
|
|
11
|
+
|
|
12
|
+
const { assertTargetInScope, ScopeError } = require('../../../_shared/scope-gate.js');
|
|
13
|
+
const { filterArgs } = require('../../../_shared/allowlist.js');
|
|
14
|
+
const { writePartial } = require('../../../_shared/partial.js');
|
|
15
|
+
|
|
16
|
+
// Default argument list for nmap. filterArgs() will drop anything not in
|
|
17
|
+
// the tool-allowlist, so this list is intentionally explicit and minimal.
|
|
18
|
+
function defaultArgs(active) {
|
|
19
|
+
const args = ['-Pn', '-T4'];
|
|
20
|
+
if (active) args.push('-sV');
|
|
21
|
+
else args.push('-sn'); // passive: ping scan only — no port scan, no service detection.
|
|
22
|
+
return args;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Parse a greppable nmap stdout into a markdown list. We only care about
|
|
26
|
+
// lines that look like `PORT/PROTO SERVICE VERSION`. Ports whose service
|
|
27
|
+
// nmap could not fingerprint (`?`, unknown, tcpwrapped, or empty version)
|
|
28
|
+
// are flagged for manual investigation — an unidentified open port is a
|
|
29
|
+
// medium-priority unknown, not just inventory.
|
|
30
|
+
function parseNmapGreppable(stdout) {
|
|
31
|
+
const lines = String(stdout || '').split('\n');
|
|
32
|
+
const out = [];
|
|
33
|
+
for (const line of lines) {
|
|
34
|
+
const m = line.match(/^(\d+\/[a-z]+)\s+(\S+)\s+(.*)$/);
|
|
35
|
+
if (!m) continue;
|
|
36
|
+
const port = m[1];
|
|
37
|
+
const service = m[2];
|
|
38
|
+
const version = m[3].trim();
|
|
39
|
+
const unidentified = /\?$/.test(service) || /^(unknown|tcpwrapped)$/i.test(service) || version === '';
|
|
40
|
+
if (unidentified) {
|
|
41
|
+
out.push(`- **${port}** \`${service}\` — ⚠️ serviço não identificado. **Investigar:** \`lsof -i :${port.split('/')[0]}\` · \`docker ps\` · \`ss -tulpn | grep ${port.split('/')[0]}\``);
|
|
42
|
+
} else {
|
|
43
|
+
out.push(`- **${port}** \`${service}\` — ${version}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return out.join('\n');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// runRecon({ securityDir, scope, target, active, execFn?, detectFn? }) ->
|
|
50
|
+
// { ok, partialStatus, mode }
|
|
51
|
+
// dependencies are injectable for tests.
|
|
52
|
+
async function runRecon(opts = {}) {
|
|
53
|
+
const sec = opts.securityDir;
|
|
54
|
+
const scope = opts.scope;
|
|
55
|
+
const target = opts.target;
|
|
56
|
+
const active = opts.active === true;
|
|
57
|
+
|
|
58
|
+
const execFn = opts.execFn || ((bin, args, opt) => {
|
|
59
|
+
return execFileSync(bin, args, { encoding: 'utf8', timeout: 60_000 });
|
|
60
|
+
});
|
|
61
|
+
const detectFn = opts.detectFn || require('../../../_shared/detect.js').detectTools;
|
|
62
|
+
|
|
63
|
+
// Gate — propagates ScopeError if scope is invalid.
|
|
64
|
+
const tools = detectFn(['nmap'], { cacheDir: sec });
|
|
65
|
+
const nmapTool = tools.nmap || { present: false };
|
|
66
|
+
|
|
67
|
+
// 1. nmap missing -> degraded partial, exit 0.
|
|
68
|
+
if (!nmapTool.present) {
|
|
69
|
+
writePartial({
|
|
70
|
+
securityDir: sec,
|
|
71
|
+
phase: 'recon',
|
|
72
|
+
mode: active ? 'active' : 'passive',
|
|
73
|
+
scope,
|
|
74
|
+
status: 'incomplete',
|
|
75
|
+
tools,
|
|
76
|
+
sections: {
|
|
77
|
+
degraded_checks: 'nmap ausente — instale nmap e re-rode a fase para resultados completos.'
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
return { ok: true, partialStatus: 'incomplete', mode: active ? 'active' : 'passive' };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// 2. target out of scope -> degraded partial, ok=false (gate refused).
|
|
84
|
+
const inScope = assertTargetInScope(scope, { host: target }, { refusalsDir: sec });
|
|
85
|
+
if (!inScope) {
|
|
86
|
+
writePartial({
|
|
87
|
+
securityDir: sec,
|
|
88
|
+
phase: 'recon',
|
|
89
|
+
mode: active ? 'active' : 'passive',
|
|
90
|
+
scope,
|
|
91
|
+
status: 'incomplete',
|
|
92
|
+
tools,
|
|
93
|
+
sections: {
|
|
94
|
+
degraded_checks: `target ${target} recusado pelo gate (host not in allowlist) — ver .wize/security/.refusals.log`
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
return { ok: false, partialStatus: 'incomplete', mode: active ? 'active' : 'passive' };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 3. happy path: filterArgs + execFile.
|
|
101
|
+
const args = filterArgs('nmap', [...defaultArgs(active), target]);
|
|
102
|
+
const out = execFn('nmap', args, { timeout: 60_000 });
|
|
103
|
+
const portsText = parseNmapGreppable(out && out.stdout ? out.stdout : out);
|
|
104
|
+
const body = portsText || '_(nmap returned no parseable ports)_';
|
|
105
|
+
|
|
106
|
+
writePartial({
|
|
107
|
+
securityDir: sec,
|
|
108
|
+
phase: 'recon',
|
|
109
|
+
mode: active ? 'active' : 'passive',
|
|
110
|
+
scope,
|
|
111
|
+
status: 'complete',
|
|
112
|
+
tools,
|
|
113
|
+
sections: { open_ports: body }
|
|
114
|
+
});
|
|
115
|
+
return { ok: true, partialStatus: 'complete', mode: active ? 'active' : 'passive' };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// --- CLI entrypoint ------------------------------------------------------
|
|
119
|
+
|
|
120
|
+
function parseArgv(argv) {
|
|
121
|
+
const out = { active: false, securityDir: null, scopePath: null, target: null };
|
|
122
|
+
for (let i = 0; i < argv.length; i++) {
|
|
123
|
+
const a = argv[i];
|
|
124
|
+
if (a === '--active') out.active = true;
|
|
125
|
+
else if (a === '--target' && argv[i + 1]) { out.target = argv[i + 1]; i++; }
|
|
126
|
+
else if (a.startsWith('--target=')) out.target = a.slice('--target='.length);
|
|
127
|
+
else if (a === '--scope' && argv[i + 1]) { out.scopePath = argv[i + 1]; i++; }
|
|
128
|
+
else if (a.startsWith('--scope=')) out.scopePath = a.slice('--scope='.length);
|
|
129
|
+
else if (a === '--securityDir' && argv[i + 1]) { out.securityDir = argv[i + 1]; i++; }
|
|
130
|
+
else if (a.startsWith('--securityDir=')) out.securityDir = a.slice('--securityDir='.length);
|
|
131
|
+
}
|
|
132
|
+
if (!out.securityDir) out.securityDir = path.join(process.cwd(), '.wize', 'security');
|
|
133
|
+
if (!out.scopePath) out.scopePath = path.join(process.cwd(), '.wize', 'security', 'scope.md');
|
|
134
|
+
if (!out.target) {
|
|
135
|
+
// Default to scope.md dast_target.url host if available.
|
|
136
|
+
out.target = 'localhost';
|
|
137
|
+
}
|
|
138
|
+
return out;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function main() {
|
|
142
|
+
const args = parseArgv(process.argv.slice(2));
|
|
143
|
+
const { loadScope } = require('../../../_shared/scope-gate.js');
|
|
144
|
+
const scope = loadScope(args.scopePath);
|
|
145
|
+
const r = await runRecon({
|
|
146
|
+
securityDir: args.securityDir,
|
|
147
|
+
scope,
|
|
148
|
+
target: args.target,
|
|
149
|
+
active: args.active
|
|
150
|
+
});
|
|
151
|
+
console.log(`recon: partial_status=${r.partialStatus} mode=${r.mode}`);
|
|
152
|
+
process.exit(r.ok ? 0 : 1);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (require.main === module) {
|
|
156
|
+
main().catch(err => {
|
|
157
|
+
console.error('✖ wize-sec-recon:', err && err.message ? err.message : err);
|
|
158
|
+
process.exit(2);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
module.exports = { runRecon, defaultArgs, parseNmapGreppable };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
|
|
3
|
+
code: wize-sec-recon
|
|
4
|
+
description: "Use quando o scope de segurança estiver autorizado e precisar executar reconhecimento passivo (nmap) nos alvos."
|
|
5
|
+
name: wize-sec-recon
|
|
6
|
+
overlay: security
|
|
7
|
+
module: security-overlay
|
|
8
|
+
owner: wize-sec-red-teamer # Natasha Romanoff
|
|
9
|
+
status: ready
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# wize-sec-recon — Recon (nmap)
|
|
13
|
+
|
|
14
|
+
Runs nmap against the targets in the `scope.md` allowlist and writes `recon.md`. **Default passive** (ping scan only); `--active` enables `-sV` service detection.
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
/wize-sec-recon
|
|
20
|
+
/wize-sec-recon --active
|
|
21
|
+
/wize-sec-recon --target=staging.example.internal
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Behavior
|
|
25
|
+
|
|
26
|
+
- Loads `.wize/security/scope.md` first; aborts loudly on invalid scope (HASH_MISMATCH / MISSING_FIELDS).
|
|
27
|
+
- Detects nmap via `command -v`. If absent, writes a `partial_status: incomplete` recon.md with a `degraded_checks` section and exits 0 — the pipeline continues.
|
|
28
|
+
- Calls `assertTargetInScope` for the target. Out-of-scope targets produce an `incomplete` partial and a refusal entry in `.refusals.log`; nmap is **not** invoked.
|
|
29
|
+
- Active vs passive flag set is recorded in the partial's `mode:` frontmatter.
|
|
30
|
+
- The SAST portion of recon (gitleaks, osv/grype) is implemented in a sibling script `scripts/run-sast.js` and is invoked separately or by the orchestrator — see E05.
|
|
31
|
+
|
|
32
|
+
## Output
|
|
33
|
+
|
|
34
|
+
- `.wize/security/recon.md` — partial with `## open_ports` (or `## degraded_checks` on missing tool / out-of-scope target).
|
|
35
|
+
- `.wize/security/.refusals.log` — appended on out-of-scope targets.
|
|
36
|
+
|
|
37
|
+
Exits 0 on success or graceful degradation; 1 when the gate refused the target; 2 on scope error.
|