wize-dev-kit 0.4.1 → 0.6.0
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/CHANGELOG.md +49 -0
- package/package.json +1 -1
- package/src/core-skills/wize-customize/skill.md +114 -0
- package/src/core-skills/wize-editorial-review-prose/skill.md +92 -0
- package/src/core-skills/wize-editorial-review-structure/skill.md +97 -0
- package/src/core-skills/wize-index-docs/skill.md +117 -0
- package/src/core-skills/wize-review-edge-case-hunter/skill.md +112 -0
- package/src/method-skills/2-plan-workflows/wize-edit-prd/workflow.md +108 -0
- package/src/method-skills/3-solutioning/wize-project-context/workflow.md +118 -0
- package/src/method-skills/4-implementation/wize-checkpoint-preview/workflow.md +115 -0
- package/src/method-skills/4-implementation/wize-correct-course/workflow.md +89 -0
- package/src/method-skills/4-implementation/wize-investigate/workflow.md +121 -0
- package/src/method-skills/4-implementation/wize-sprint-planning/workflow.md +58 -71
- package/src/method-skills/4-implementation/wize-sprint-status/workflow.md +29 -82
- package/src/orchestrator-skills/wize-onboarding/workflow.md +76 -14
- package/src/security-overlay/_shared/allowlist.js +154 -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 +120 -0
- package/src/security-overlay/agents/red-teamer/agent.yaml +51 -0
- package/src/security-overlay/agents/red-teamer/persona.md +43 -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 +32 -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 +157 -0
- package/src/security-overlay/skills/wize-sec-pentest/skill.md +52 -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 +35 -0
- package/src/security-overlay/skills/wize-sec-report/scripts/render-report.js +999 -0
- package/src/tea-skills/wize-qa-generate-e2e-tests/workflow.md +119 -0
- package/tools/installer/onboarding.js +1 -0
- package/tools/installer/render-shared.js +50 -3
- package/tools/installer/wize-cli.js +72 -5
|
@@ -8,92 +8,79 @@ status: ready
|
|
|
8
8
|
|
|
9
9
|
# Sprint Planning
|
|
10
10
|
|
|
11
|
-
**Goal.** Pick what enters this sprint. Capacity-honest, priority-honest, risk-honest.
|
|
11
|
+
**Goal.** Pick what enters this sprint. Capacity-honest, priority-honest, risk-honest.
|
|
12
12
|
|
|
13
|
-
Maria Hill chairs. Tony advises on slicing. Hawkeye flags risk
|
|
13
|
+
Maria Hill chairs. Tony advises on slicing. Hawkeye flags risk. Shuri commits to the load.
|
|
14
14
|
|
|
15
15
|
## Inputs
|
|
16
16
|
|
|
17
17
|
- Story backlog: `.wize/solutioning/stories/`
|
|
18
|
-
-
|
|
18
|
+
- Previous sprint state: `.wize/implementation/sprint-status.yaml`
|
|
19
19
|
- `.wize/implementation/tea/risk-profile.md`
|
|
20
|
-
-
|
|
21
|
-
- Team availability for the next interval (vacations, on-call rotation, planned meetings).
|
|
20
|
+
- Team availability for the next interval.
|
|
22
21
|
|
|
23
22
|
## Output
|
|
24
23
|
|
|
25
|
-
-
|
|
26
|
-
- Story files updated `priority: 1` for chosen stories.
|
|
27
|
-
|
|
28
|
-
## Rules
|
|
29
|
-
|
|
30
|
-
1. **Capacity = min(history velocity, declared availability).** Not the average of optimistic estimates.
|
|
31
|
-
2. **High-risk stories** (linked to `R-x` HIGH in risk profile) get TEA design done in the planning meeting, not at story start.
|
|
32
|
-
3. **Stretch goals** are explicit, named, not silent. If a stretch ships, great. If not, the sprint isn't a failure.
|
|
33
|
-
4. **Don't carry over without reason.** A carried-over story gets a one-line "why" in the sprint log.
|
|
24
|
+
- Updated `.wize/implementation/sprint-status.yaml`.
|
|
25
|
+
- Story files updated with `priority: 1` for chosen stories.
|
|
34
26
|
|
|
35
27
|
## Steps
|
|
36
28
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
**Capacity:** 24 person-days (3 engineers × 10 days × 0.8 utilization)
|
|
73
|
-
**Carry-over:** E01-S05 (90% done; Shuri); E03-S01 (TEA review pending)
|
|
74
|
-
**Pulled:**
|
|
75
|
-
- E01-S06 — M — owner: Shuri — gate cadence: design+gate
|
|
76
|
-
- E02-S02 — L — owner: Shuri — gate cadence: design+trace+review+gate (R-3)
|
|
77
|
-
- E03-S02 — M — owner: Aaliyah — gate cadence: design+trace+review+gate (R-1)
|
|
78
|
-
- E04-S01 — S — owner: Shuri — gate cadence: smoke (quick-dev pattern)
|
|
79
|
-
- E02-S03 — S — stretch
|
|
80
|
-
|
|
81
|
-
**Out (deferred to Sprint 8):**
|
|
82
|
-
- E03-S03 — reason: depends on E03-S02 ADR
|
|
83
|
-
- E05-S01 — reason: out of NFR-cost budget; revisit
|
|
84
|
-
|
|
85
|
-
**Risks flagged:**
|
|
86
|
-
- E02-S02 — auth refresh story; high-risk; TEA design done at planning.
|
|
29
|
+
1. **Look back** — what shipped, what slipped, what surprised.
|
|
30
|
+
2. **Refresh capacity** — person-days × utilization − overhead.
|
|
31
|
+
3. **Pull stories** — continuation first, then priority, then risk.
|
|
32
|
+
4. **Reserve 10–15% buffer** for unknowns.
|
|
33
|
+
5. **Walk the gate plan** — design/trace/review/gate per story.
|
|
34
|
+
6. **Commit** — verbal + written into YAML.
|
|
35
|
+
|
|
36
|
+
## Status state machine
|
|
37
|
+
|
|
38
|
+
- Epic: `backlog` → `in-progress` → `done`
|
|
39
|
+
- Story: `backlog` → `ready-for-dev` → `in-progress` → `review` → `done`
|
|
40
|
+
- Retrospective: `optional` ↔ `done`
|
|
41
|
+
|
|
42
|
+
## Sprint block template
|
|
43
|
+
|
|
44
|
+
```yaml
|
|
45
|
+
# generated: YYYY-MM-DD
|
|
46
|
+
# last_updated: YYYY-MM-DD
|
|
47
|
+
# project: {project_name}
|
|
48
|
+
# project_key: {project_key}
|
|
49
|
+
# tracking_system: file-system
|
|
50
|
+
# story_location: .wize/solutioning/stories
|
|
51
|
+
|
|
52
|
+
generated: YYYY-MM-DD
|
|
53
|
+
last_updated: YYYY-MM-DD
|
|
54
|
+
project: {project_name}
|
|
55
|
+
project_key: {project_key}
|
|
56
|
+
tracking_system: file-system
|
|
57
|
+
story_location: .wize/solutioning/stories
|
|
58
|
+
|
|
59
|
+
development_status:
|
|
60
|
+
epic-1: backlog
|
|
61
|
+
1-1-story-one: backlog
|
|
62
|
+
1-2-story-two: backlog
|
|
63
|
+
epic-1-retrospective: optional
|
|
87
64
|
```
|
|
88
65
|
|
|
89
|
-
## Anti-patterns
|
|
66
|
+
## Anti-patterns
|
|
90
67
|
|
|
91
|
-
-
|
|
92
|
-
-
|
|
93
|
-
-
|
|
94
|
-
-
|
|
95
|
-
-
|
|
68
|
+
- Optimistic velocity.
|
|
69
|
+
- Stories without owners.
|
|
70
|
+
- Stretch goals that are really plan.
|
|
71
|
+
- Pulling blocked dependencies.
|
|
72
|
+
- Zero buffer.
|
|
96
73
|
|
|
97
74
|
## Hand-off
|
|
98
75
|
|
|
99
|
-
> Sprint
|
|
76
|
+
> Sprint committed at `.wize/implementation/sprint-status.yaml`. Stories in `ready-for-dev` are now eligible for the dev loop.
|
|
77
|
+
>
|
|
78
|
+
> **Recommended next loop:**
|
|
79
|
+
>
|
|
80
|
+
> ```
|
|
81
|
+
> /loop /wize-dev-story
|
|
82
|
+
> ```
|
|
83
|
+
>
|
|
84
|
+
> `/loop /wize-dev-story` drives one story at a time: TDD red-green-refactor, AC IDs in commits, `tea-design.md` contract, knowledge update on the 5 baseline axes, and a clean gate at the end. `/loop` keeps it going across the sprint's `ready-for-dev` queue until the user pauses.
|
|
85
|
+
>
|
|
86
|
+
> Next: `/wize-sprint-status` (Maria Hill) to acompanhar o progresso.
|
|
@@ -8,103 +8,50 @@ status: ready
|
|
|
8
8
|
|
|
9
9
|
# Sprint Status
|
|
10
10
|
|
|
11
|
-
**Goal.**
|
|
12
|
-
|
|
13
|
-
Update **daily** during a sprint, or after any state change (story moves, blocker appears, gate fails).
|
|
11
|
+
**Goal.** Read `.wize/implementation/sprint-status.yaml` in 60 seconds and say what to do next.
|
|
14
12
|
|
|
15
13
|
## Inputs
|
|
16
14
|
|
|
17
|
-
- `.wize/
|
|
18
|
-
- `.wize/implementation/tea/{epic}/{story}/gate.md`
|
|
19
|
-
- The team (verbal stand-up or async update)
|
|
15
|
+
- `.wize/implementation/sprint-status.yaml`
|
|
16
|
+
- `.wize/implementation/tea/{epic}/{story}/gate.md`
|
|
20
17
|
|
|
21
18
|
## Output
|
|
22
19
|
|
|
23
|
-
- Updated
|
|
20
|
+
- Updated sprint-status.yaml (if statuses changed).
|
|
21
|
+
- One recommended next workflow.
|
|
24
22
|
|
|
25
23
|
## Steps
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
- Specific ask (the action they should take).
|
|
44
|
-
- Deadline.
|
|
45
|
-
|
|
46
|
-
If a blocker sits longer than 2 days, Hill escalates. Stalled blockers are how sprints fail silently.
|
|
47
|
-
|
|
48
|
-
### 3. Trend
|
|
49
|
-
|
|
50
|
-
Daily, write a one-line trend: *"On track."* / *"At risk for E03-S02 due to vendor outage."* / *"Slipping; will defer E04-S01 to next sprint."*
|
|
51
|
-
|
|
52
|
-
### 4. Capture decisions
|
|
53
|
-
|
|
54
|
-
If something material was decided during the sprint that affects the plan (story sliced, scope dropped, ADR opened), append a one-line entry.
|
|
55
|
-
|
|
56
|
-
## File template
|
|
25
|
+
1. **Load YAML.** Parse `development_status`.
|
|
26
|
+
2. **Classify** each key: epic, story, retrospective.
|
|
27
|
+
3. **Count** statuses.
|
|
28
|
+
4. **Detect risks:**
|
|
29
|
+
- Story `in-progress` older than 2 days with no update.
|
|
30
|
+
- Blocked story without owner/deadline.
|
|
31
|
+
- Epic `in-progress` with no stories.
|
|
32
|
+
5. **Recommend next step** (in priority order):
|
|
33
|
+
1. Story `in-progress` → `/wize-dev-story`
|
|
34
|
+
2. Story `review` → `/wize-tea-review`
|
|
35
|
+
3. Story `ready-for-dev` → `/wize-dev-story`
|
|
36
|
+
4. Story `backlog` → `/wize-create-story`
|
|
37
|
+
5. Retrospective `optional` → `/wize-retrospective`
|
|
38
|
+
6. All done → congratulate team.
|
|
39
|
+
|
|
40
|
+
## Summary output
|
|
57
41
|
|
|
58
42
|
```markdown
|
|
59
|
-
|
|
43
|
+
## Sprint Status
|
|
60
44
|
|
|
61
|
-
|
|
45
|
+
- Project: {project} ({project_key})
|
|
46
|
+
- Tracking: {tracking_system}
|
|
47
|
+
- Status file: .wize/implementation/sprint-status.yaml
|
|
62
48
|
|
|
63
|
-
|
|
64
|
-
**
|
|
49
|
+
**Stories:** backlog {n} | ready-for-dev {n} | in-progress {n} | review {n} | done {n}
|
|
50
|
+
**Epics:** backlog {n} | in-progress {n} | done {n}
|
|
65
51
|
|
|
66
|
-
**
|
|
67
|
-
- (none)
|
|
68
|
-
|
|
69
|
-
**Stories:**
|
|
70
|
-
- E01-S05 — gate-PASS — shipped (carry-over from S6).
|
|
71
|
-
- E01-S06 — in-progress — Shuri.
|
|
72
|
-
- E02-S02 — in-review — PR #418; Hawkeye doing trace.
|
|
73
|
-
- E03-S02 — in-progress — Aaliyah.
|
|
74
|
-
- E04-S01 — pulled — Shuri starts after E01-S06.
|
|
75
|
-
- E02-S03 (stretch) — pulled — Aaliyah picks up if capacity allows.
|
|
76
|
-
|
|
77
|
-
**Decisions:**
|
|
78
|
-
- E03-S03 sliced into two stories (E03-S03a, E03-S03b) — ADR-009 incoming.
|
|
79
|
-
|
|
80
|
-
### Day 5 (2026-06-16)
|
|
81
|
-
**Trend:** At risk on E02-S02 (vendor sandbox down; Hawkeye unblocked at 14:00).
|
|
82
|
-
**Blockers:** Resolved.
|
|
83
|
-
**Stories:** (changes from Day 4)
|
|
84
|
-
- E02-S02 — gate-PASS at 16:30; merged.
|
|
85
|
-
- E01-S06 — in-review — PR #419.
|
|
86
|
-
|
|
87
|
-
## Sprint 6 — 2026-05-29 → 2026-06-11
|
|
88
|
-
{{archived}}
|
|
52
|
+
**Next:** /{next_workflow} ({next_story_id})
|
|
89
53
|
```
|
|
90
54
|
|
|
91
|
-
## Daily cadence (lean)
|
|
92
|
-
|
|
93
|
-
A daily standup, when present, is 5 minutes max:
|
|
94
|
-
|
|
95
|
-
- "What did I ship since last time?"
|
|
96
|
-
- "What am I shipping next?"
|
|
97
|
-
- "Anything blocking me?"
|
|
98
|
-
|
|
99
|
-
Hill updates `sprint-status.md` immediately after; Wizer reads it before any other agent's session that day.
|
|
100
|
-
|
|
101
|
-
## Anti-patterns Hill rejects
|
|
102
|
-
|
|
103
|
-
- "Status: in progress" for 4 days with no further detail. Either it really is, in which case slice progress, or it's stuck.
|
|
104
|
-
- Blockers without an owner or a deadline.
|
|
105
|
-
- Sprint goals that drift silently (added stories without removing others).
|
|
106
|
-
- Stale entries in the file. Update daily or delegate the update.
|
|
107
|
-
|
|
108
55
|
## Hand-off
|
|
109
56
|
|
|
110
|
-
>
|
|
57
|
+
> Status updated. Run the recommended workflow or call `/wize-sprint-planning` to reprioritize.
|
|
@@ -2,27 +2,89 @@
|
|
|
2
2
|
code: wize-onboarding
|
|
3
3
|
name: Onboarding
|
|
4
4
|
owner: wize-orchestrator # Wizer
|
|
5
|
-
status:
|
|
5
|
+
status: ready
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Onboarding
|
|
9
9
|
|
|
10
|
-
**Goal.**
|
|
10
|
+
**Goal.** First-contact triage after `npx wize-dev-kit install`. Decide greenfield vs brownfield, profile, objective, and route to the right persona. Always ask who the user is so the rest of the session feels personal.
|
|
11
|
+
|
|
12
|
+
Wizer drives. Each branch ends by handing off to a specific workflow with explicit handoff copy.
|
|
11
13
|
|
|
12
14
|
## Inputs
|
|
13
|
-
|
|
14
|
-
-
|
|
15
|
+
|
|
16
|
+
- `.wize/config/project.toml` (always present after install)
|
|
17
|
+
- `.wize/config/user.toml` (per-developer)
|
|
18
|
+
- `.wize/implementation/sprint-status.yaml` (when an active sprint exists)
|
|
19
|
+
- `.wize/planning/brief.md` (when Phase 1 started)
|
|
20
|
+
- `.wize/planning/prd.md` (when Phase 2 finished)
|
|
21
|
+
- Optional: chat message describing the user’s goal.
|
|
15
22
|
|
|
16
23
|
## Outputs
|
|
17
|
-
|
|
18
|
-
- A
|
|
24
|
+
|
|
25
|
+
- A **single handoff message** naming the next workflow to run, with the user’s name.
|
|
26
|
+
- Optional: `.wize/knowledge/onboarding-summary.md` written when state is ambiguous.
|
|
19
27
|
|
|
20
28
|
## Steps
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
|
|
30
|
+
### 1. Greet the user
|
|
31
|
+
|
|
32
|
+
Read `name` from `.wize/config/user.toml`. Greet by name. Speak in `communication_language` from `project.toml`. If `name` is blank, ask once and persist it back to `user.toml` before continuing.
|
|
33
|
+
|
|
34
|
+
> "Welcome, {name}. You are at Wize onboarding for *{project_name}*."
|
|
35
|
+
|
|
36
|
+
### 2. Detect project state
|
|
37
|
+
|
|
38
|
+
Inspect, in order:
|
|
39
|
+
|
|
40
|
+
| Path | Meaning |
|
|
41
|
+
|---|---|
|
|
42
|
+
| `.wize/implementation/sprint-status.yaml` | Active sprint exists. |
|
|
43
|
+
| `.wize/planning/prd.md` | Phase 2 (PRD) is done. |
|
|
44
|
+
| `.wize/planning/brief.md` | Phase 1 (Brief) is done. |
|
|
45
|
+
| `.wize/knowledge/document-project/*.md` | Brownfield baseline exists. |
|
|
46
|
+
| `package.json`, `src/`, etc. | Brownfield signals (vs. greenfield). |
|
|
47
|
+
|
|
48
|
+
If multiple artifacts exist, treat the **latest** (PRD > brief > baseline) as the current phase.
|
|
49
|
+
|
|
50
|
+
### 3. State machine
|
|
51
|
+
|
|
52
|
+
- **S0 — No artifacts** → greenfield or no planning yet.
|
|
53
|
+
- **S1 — Baseline only** → brownfield, no brief.
|
|
54
|
+
- **S2 — Brief exists** → ready for PRD.
|
|
55
|
+
- **S3 — PRD exists** → ready for sprint planning.
|
|
56
|
+
- **S4 — Active sprint** → resume in-flight.
|
|
57
|
+
|
|
58
|
+
### 4. Branch by state
|
|
59
|
+
|
|
60
|
+
| State | Action | Hand-off |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| S0 | Ask: "What are we building?" Confirm: brownfield or greenfield. Offer `/wize-document-project` (brownfield) or `/wize-product-brief` (greenfield). | "Run `/wize-document-project` (Tony + Peggy) to baseline the repo, or `/wize-product-brief` (Pepper) to write a brief." |
|
|
63
|
+
| S1 | Read `document-project/overview.md`; summarize the project in 3 bullets. Offer brief. | "Repo baselined. Run `/wize-product-brief` (Pepper)." |
|
|
64
|
+
| S2 | Read `brief.md` (3 bullets). Offer PRD. | "Brief ready. Run `/wize-create-prd` (Maria Hill)." |
|
|
65
|
+
| S3 | Read `prd.md` summary + `architecture.md` (if present). Offer sprint planning. | "PRD ready. Run `/wize-sprint-planning` (Maria Hill)." |
|
|
66
|
+
| S4 | Read `sprint-status.yaml` and surface: which stories are in progress, last gate. | "Sprint active. Run `/wize-sprint-status` (Maria Hill) for the full picture." |
|
|
67
|
+
|
|
68
|
+
### 5. Confirm and exit
|
|
69
|
+
|
|
70
|
+
End every onboarding session with:
|
|
71
|
+
|
|
72
|
+
> "Onboarding complete. Next: `/wize-<next-workflow>` ({persona})."
|
|
73
|
+
|
|
74
|
+
Never auto-launch the next workflow. The user must confirm.
|
|
75
|
+
|
|
76
|
+
## When to skip
|
|
77
|
+
|
|
78
|
+
- When the user already knows what they want and explicitly invokes another workflow, route directly. Do not force onboarding.
|
|
79
|
+
- When `WIZE_SKIP_ONBOARDING=1` is set, print a 1-line state summary and exit.
|
|
80
|
+
|
|
81
|
+
## Anti-patterns Wizer rejects
|
|
82
|
+
|
|
83
|
+
- Launching the next workflow without confirmation.
|
|
84
|
+
- Re-asking the user’s name when it’s already in `user.toml`.
|
|
85
|
+
- Dumping the full project state to the user. Summarize in ≤ 5 bullets.
|
|
86
|
+
- Suggesting `wize-onboarding` from `/wize-help` once onboarding is complete. (Help should bypass onboarding for return users.)
|
|
87
|
+
|
|
88
|
+
## Hand-off
|
|
89
|
+
|
|
90
|
+
> "Onboarding done. You are at **{state}**. Next: `/wize-<next-workflow>` ({persona})."
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// allowlist.js — gate that filters arguments for an external pentest tool
|
|
4
|
+
// before they are passed to child_process.execFile. The list of allowed
|
|
5
|
+
// flags per tool lives in src/security-overlay/data/tool-allowlist.json.
|
|
6
|
+
//
|
|
7
|
+
// Schema (per tool, array of strings):
|
|
8
|
+
// "-foo" switch with no value
|
|
9
|
+
// "-foo:" switch that consumes the next argv as its value
|
|
10
|
+
// "-foo=bar" switch with a fixed value (only the literal "bar" is allowed)
|
|
11
|
+
// "--flag=" switch that consumes a value joined by '=' (e.g. --level=1)
|
|
12
|
+
//
|
|
13
|
+
// Invariant: args NOT in the allowlist (or args that look like values for
|
|
14
|
+
// flags not in the allowlist) are dropped. Positional args (targets, URLs)
|
|
15
|
+
// pass through unchanged.
|
|
16
|
+
|
|
17
|
+
const fs = require('node:fs');
|
|
18
|
+
const path = require('node:path');
|
|
19
|
+
|
|
20
|
+
class UnknownToolError extends Error {
|
|
21
|
+
constructor(tool) {
|
|
22
|
+
super(`Unknown tool "${tool}" — not in tool-allowlist.json. Refusing to invoke.`);
|
|
23
|
+
this.name = 'UnknownToolError';
|
|
24
|
+
this.tool = tool;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const DEFAULT_ALLOWLIST_PATH = path.join(__dirname, '..', 'data', 'tool-allowlist.json');
|
|
29
|
+
|
|
30
|
+
let _cache = null;
|
|
31
|
+
function _loadDefault() {
|
|
32
|
+
if (_cache) return _cache;
|
|
33
|
+
const raw = fs.readFileSync(DEFAULT_ALLOWLIST_PATH, 'utf8');
|
|
34
|
+
_cache = JSON.parse(raw);
|
|
35
|
+
return _cache;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function loadAllowlist(filePath) {
|
|
39
|
+
const fp = filePath || DEFAULT_ALLOWLIST_PATH;
|
|
40
|
+
return JSON.parse(fs.readFileSync(fp, 'utf8'));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Heuristic for "is this arg a flag?": starts with '-' and is more than 1 char
|
|
44
|
+
// (a bare "-" is sometimes used as stdin, treat as positional).
|
|
45
|
+
function isFlag(arg) {
|
|
46
|
+
return typeof arg === 'string' && arg.length > 1 && arg[0] === '-';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Classify one allowlist token into one of:
|
|
50
|
+
// { kind: 'switch' } — no value
|
|
51
|
+
// { kind: 'colon', prefix: '-foo' } — consumes next argv (e.g. -f path)
|
|
52
|
+
// { kind: 'equals', prefix: '--flag=' } — consumes value joined by '=' (e.g. --level=1)
|
|
53
|
+
// { kind: 'literal', full: '-foo=bar' } — only the exact form is allowed
|
|
54
|
+
//
|
|
55
|
+
// When matching an arg, we try in order: literal, then colon (exact prefix
|
|
56
|
+
// match, e.g. arg === '-u'), then equals (arg starts with prefix, e.g.
|
|
57
|
+
// arg === '--level=1'), then switch (exact equality).
|
|
58
|
+
function classify(token) {
|
|
59
|
+
if (token.endsWith(':')) {
|
|
60
|
+
return { kind: 'colon', prefix: token.slice(0, -1) };
|
|
61
|
+
}
|
|
62
|
+
if (token.endsWith('=') && token.startsWith('--')) {
|
|
63
|
+
return { kind: 'equals', prefix: token };
|
|
64
|
+
}
|
|
65
|
+
if (token.includes('=') && !token.endsWith('=')) {
|
|
66
|
+
return { kind: 'literal', full: token };
|
|
67
|
+
}
|
|
68
|
+
return { kind: 'switch' };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Given a flag arg, find the first allowlist token that matches it.
|
|
72
|
+
function matchFlag(toolData, arg) {
|
|
73
|
+
// Literal first (most specific).
|
|
74
|
+
for (const tok of toolData) {
|
|
75
|
+
if (classify(tok).kind === 'literal' && arg === tok) return { consumeNext: false };
|
|
76
|
+
}
|
|
77
|
+
// Then colon (exact prefix match) — e.g. arg === '-u'.
|
|
78
|
+
for (const tok of toolData) {
|
|
79
|
+
if (classify(tok).kind === 'colon' && arg === classify(tok).prefix) return { consumeNext: true };
|
|
80
|
+
}
|
|
81
|
+
// Then equals — e.g. arg starts with '--level='.
|
|
82
|
+
for (const tok of toolData) {
|
|
83
|
+
if (classify(tok).kind === 'equals' && arg.startsWith(classify(tok).prefix)) return { consumeNext: false };
|
|
84
|
+
}
|
|
85
|
+
// Then switch.
|
|
86
|
+
for (const tok of toolData) {
|
|
87
|
+
if (classify(tok).kind === 'switch' && tok === arg) return { consumeNext: false };
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// filterArgs(tool, args, allowlist) — keep only args allowed by the tool's
|
|
93
|
+
// allowlist, handling value-bearing flags correctly.
|
|
94
|
+
function filterArgs(tool, args, allowlist) {
|
|
95
|
+
const data = allowlist || _loadDefault();
|
|
96
|
+
if (!Object.prototype.hasOwnProperty.call(data, tool)) {
|
|
97
|
+
throw new UnknownToolError(tool);
|
|
98
|
+
}
|
|
99
|
+
const toolData = data[tool];
|
|
100
|
+
|
|
101
|
+
const out = [];
|
|
102
|
+
let i = 0;
|
|
103
|
+
const list = args || [];
|
|
104
|
+
while (i < list.length) {
|
|
105
|
+
const arg = list[i];
|
|
106
|
+
if (!isFlag(arg)) {
|
|
107
|
+
// Positional: target, URL, output path.
|
|
108
|
+
out.push(arg);
|
|
109
|
+
i++;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const m = matchFlag(toolData, arg);
|
|
113
|
+
if (!m) {
|
|
114
|
+
// Unknown flag — drop the flag. We do NOT also drop the next arg,
|
|
115
|
+
// because a positional after a stripped flag is still a positional
|
|
116
|
+
// (caller may have intended both to pass). However, common patterns
|
|
117
|
+
// are value-bearing: e.g. `--script vuln` — if the flag is dropped,
|
|
118
|
+
// "vuln" is also dropped to avoid leaking. This is the safe default.
|
|
119
|
+
if (looksLikeValueArg(list[i + 1])) {
|
|
120
|
+
// Consume the next arg as part of the dropped flag's expected value.
|
|
121
|
+
i += 2;
|
|
122
|
+
} else {
|
|
123
|
+
i++;
|
|
124
|
+
}
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
out.push(arg);
|
|
128
|
+
if (m.consumeNext) {
|
|
129
|
+
// Value-bearing: the next argv is the value. Pass it through.
|
|
130
|
+
if (i + 1 < list.length) {
|
|
131
|
+
out.push(list[i + 1]);
|
|
132
|
+
i += 2;
|
|
133
|
+
} else {
|
|
134
|
+
i++;
|
|
135
|
+
}
|
|
136
|
+
} else {
|
|
137
|
+
i++;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// A value arg is one that does NOT start with '-' (or is the bare "-").
|
|
144
|
+
function looksLikeValueArg(arg) {
|
|
145
|
+
return arg !== undefined && (typeof arg !== 'string' || arg.length === 0 || arg[0] !== '-');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
module.exports = {
|
|
149
|
+
filterArgs,
|
|
150
|
+
loadAllowlist,
|
|
151
|
+
UnknownToolError,
|
|
152
|
+
classify,
|
|
153
|
+
matchFlag
|
|
154
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// cli-runner.js — small helper so each phase script can be invoked
|
|
4
|
+
// either as a module (import + call) or as a CLI (with --securityDir /
|
|
5
|
+
// --scope / --active argv). The invoke-phase helper spawns the script
|
|
6
|
+
// as a Node subprocess; this module bridges that to the per-phase
|
|
7
|
+
// `runX` function exported by each script.
|
|
8
|
+
//
|
|
9
|
+
// Usage from a script:
|
|
10
|
+
// const { runX } = require('./run-x');
|
|
11
|
+
// module.exports = { runX };
|
|
12
|
+
// if (require.main === module) {
|
|
13
|
+
// require('../../../_shared/cli-runner.js').runFromArgv({
|
|
14
|
+
// fn: runX,
|
|
15
|
+
// argMap: { securityDir: 'securityDir', scopePath: 'scopePath', active: 'active' }
|
|
16
|
+
// });
|
|
17
|
+
// }
|
|
18
|
+
//
|
|
19
|
+
// argv shape: --securityDir=PATH --scope=PATH --active [script-specific]
|
|
20
|
+
// The function `fn` is called with the parsed object spread.
|
|
21
|
+
|
|
22
|
+
function parseArgv(argv) {
|
|
23
|
+
const out = {};
|
|
24
|
+
for (let i = 0; i < (argv || []).length; i++) {
|
|
25
|
+
const a = argv[i];
|
|
26
|
+
if (a === '--active') { out.active = true; continue; }
|
|
27
|
+
const eq = a.indexOf('=');
|
|
28
|
+
if (eq > 0) {
|
|
29
|
+
const key = a.slice(0, eq).replace(/^--/, '');
|
|
30
|
+
const val = a.slice(eq + 1);
|
|
31
|
+
out[camel(key)] = val;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (a.startsWith('--') && i + 1 < argv.length && !argv[i + 1].startsWith('--')) {
|
|
35
|
+
out[camel(a.slice(2))] = argv[i + 1];
|
|
36
|
+
i++;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function camel(s) {
|
|
43
|
+
return s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function runFromArgv({ fn, argMap = {} }) {
|
|
47
|
+
const argv = process.argv.slice(2);
|
|
48
|
+
const parsed = parseArgv(argv);
|
|
49
|
+
// Map --securityDir -> securityDir, --scope -> scopePath, etc.
|
|
50
|
+
const opts = {};
|
|
51
|
+
for (const [cliKey, optKey] of Object.entries(argMap)) {
|
|
52
|
+
if (cliKey in parsed) opts[optKey] = parsed[cliKey];
|
|
53
|
+
}
|
|
54
|
+
// Also accept any unprefixed camelCase keys directly.
|
|
55
|
+
for (const [k, v] of Object.entries(parsed)) {
|
|
56
|
+
if (!(k in opts)) opts[k] = v;
|
|
57
|
+
}
|
|
58
|
+
if (!('active' in opts)) opts.active = false;
|
|
59
|
+
// The phase script may also export its own CLI flags (--target for recon).
|
|
60
|
+
// We forward remaining --flags as a target-agnostic extraArgs-style list.
|
|
61
|
+
const extras = {};
|
|
62
|
+
for (const a of argv) {
|
|
63
|
+
if (a === '--active') continue;
|
|
64
|
+
const m = a.match(/^--([a-z0-9-]+)/);
|
|
65
|
+
if (m && !argMap[m[1]] && !argMap[camel(m[1])]) {
|
|
66
|
+
// Stash any flag the script might want to read itself.
|
|
67
|
+
extras[m[1]] = a.includes('=') ? a.slice(a.indexOf('=') + 1) : true;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
Object.assign(opts, extras);
|
|
71
|
+
try {
|
|
72
|
+
const r = await fn(opts);
|
|
73
|
+
if (r && typeof r === 'object') {
|
|
74
|
+
// Print a one-line summary the orchestrator can show in its summary.
|
|
75
|
+
const summary = r.partialStatus
|
|
76
|
+
? `${process.argv[1].split('/').pop().replace(/\.js$/, '')}: partial_status=${r.partialStatus} mode=${r.mode || 'passive'}`
|
|
77
|
+
: '';
|
|
78
|
+
if (summary) process.stdout.write(summary + '\n');
|
|
79
|
+
}
|
|
80
|
+
process.exit(0);
|
|
81
|
+
} catch (e) {
|
|
82
|
+
process.stderr.write(`✖ ${process.argv[1]}: ${e && e.message ? e.message : e}\n`);
|
|
83
|
+
process.exit(2);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
module.exports = { parseArgv, runFromArgv };
|