Skip to content

Commit b2f609e

Browse files
mnriemCopilot
andcommitted
Allow absent assessment dir in ancestor path-safety check
Addresses review 4723955260 on PR #3568. The ancestor path-safety clause required `.specify/assessments` to already be a real directory, which blocked the first-run commands (intake, research, define) from ever reaching the step that creates it. Reword the clause in all five commands so a not-yet-created directory is permitted, while still refusing when `.specify` or `.specify/assessments` exists as a symlink or escapes the project root. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, supervised) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1f25cf9d-b7eb-4b2b-b811-3e91d8db8f6a
1 parent cb4b95e commit b2f609e

5 files changed

Lines changed: 5 additions & 5 deletions

File tree

extensions/assess/commands/speckit.assess.decide.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Decide **judges; it does not spec or build.** It weighs the evidence already gat
1414
$ARGUMENTS
1515
```
1616

17-
**Ancestor path safety (before any filesystem lookup here)**: verify `.specify` and `.specify/assessments` are real directories — not symlinks — resolving inside the project root; refuse and report otherwise. Only then resolve the slug: explicit `slug=…` → conversation context (a slug reported earlier this session, confirmed by an existing `.specify/assessments/<slug>/` directory) → ask (interactive) → single existing directory (automated) → otherwise stop and ask. **Slug safety**: normalize any explicit or user-supplied slug — lowercase; whitespace/underscores → `-`; keep only `[a-z0-9-]` (drop every other character, including `.`, `/`, `\`); collapse and trim `-`; reject an empty normalized result. Only then set `ASSESS_SLUG` (the normalized value) and `ASSESS_DIR = .specify/assessments/<ASSESS_SLUG>` — this keeps every read and write inside `.specify/assessments/`.
17+
**Ancestor path safety (before any filesystem lookup here)**: where `.specify` or `.specify/assessments` already exist, verify each is a real directory (not a symlink) resolving inside the project root, and refuse and report if either exists as a symlink or escapes the root — a not-yet-created directory is allowed and will be created safely later. Only then resolve the slug: explicit `slug=…` → conversation context (a slug reported earlier this session, confirmed by an existing `.specify/assessments/<slug>/` directory) → ask (interactive) → single existing directory (automated) → otherwise stop and ask. **Slug safety**: normalize any explicit or user-supplied slug — lowercase; whitespace/underscores → `-`; keep only `[a-z0-9-]` (drop every other character, including `.`, `/`, `\`); collapse and trim `-`; reject an empty normalized result. Only then set `ASSESS_SLUG` (the normalized value) and `ASSESS_DIR = .specify/assessments/<ASSESS_SLUG>` — this keeps every read and write inside `.specify/assessments/`.
1818

1919
## Prerequisites
2020

extensions/assess/commands/speckit.assess.define.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Define **frames the problem; it does not shape or choose a solution.** If the in
1414
$ARGUMENTS
1515
```
1616

17-
**Ancestor path safety (before any filesystem lookup here)**: verify `.specify` and `.specify/assessments` are real directories — not symlinks — resolving inside the project root; refuse and report otherwise. Only then resolve the slug: explicit `slug=…` → conversation context (a slug reported earlier this session, confirmed by an existing `.specify/assessments/<slug>/` directory) → ask (interactive) → single existing directory (automated) → otherwise stop and ask. **Slug safety**: normalize any explicit or user-supplied slug — lowercase; whitespace/underscores → `-`; keep only `[a-z0-9-]` (drop every other character, including `.`, `/`, `\`); collapse and trim `-`; reject an empty normalized result. Only then set `ASSESS_SLUG` (the normalized value) and `ASSESS_DIR = .specify/assessments/<ASSESS_SLUG>` — this keeps every read and write inside `.specify/assessments/`.
17+
**Ancestor path safety (before any filesystem lookup here)**: where `.specify` or `.specify/assessments` already exist, verify each is a real directory (not a symlink) resolving inside the project root, and refuse and report if either exists as a symlink or escapes the root — a not-yet-created directory is allowed and will be created safely later. Only then resolve the slug: explicit `slug=…` → conversation context (a slug reported earlier this session, confirmed by an existing `.specify/assessments/<slug>/` directory) → ask (interactive) → single existing directory (automated) → otherwise stop and ask. **Slug safety**: normalize any explicit or user-supplied slug — lowercase; whitespace/underscores → `-`; keep only `[a-z0-9-]` (drop every other character, including `.`, `/`, `\`); collapse and trim `-`; reject an empty normalized result. Only then set `ASSESS_SLUG` (the normalized value) and `ASSESS_DIR = .specify/assessments/<ASSESS_SLUG>` — this keeps every read and write inside `.specify/assessments/`.
1818

1919
## Prerequisites
2020

extensions/assess/commands/speckit.assess.intake.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ If the input is empty, ask the user for the idea (interactive), or stop with a n
2525

2626
## Slug Resolution
2727

28-
**Ancestor path safety (do this before any filesystem lookup in this section)**: verify that `.specify` and `.specify/assessments` are real directories — not symlinks — that resolve inside the project root, and refuse and report if either is a symlink or escapes the root. Only then run any existence check or directory enumeration below.
28+
**Ancestor path safety (do this before any filesystem lookup in this section)**: where `.specify` or `.specify/assessments` already exist, verify each is a real directory (not a symlink) that resolves inside the project root, and refuse and report if either exists as a symlink or escapes the root — a not-yet-created directory is allowed and will be created safely later. Only then run any existence check or directory enumeration below.
2929

3030
Each idea gets its own directory under `.specify/assessments/<slug>/`. Resolve the slug in this order:
3131

extensions/assess/commands/speckit.assess.research.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Research **collects and cites evidence; it does not decide.** No verdict, no sol
1414
$ARGUMENTS
1515
```
1616

17-
The input carries the slug and (optionally) research direction or links. **Ancestor path safety (before any filesystem lookup here)**: verify `.specify` and `.specify/assessments` are real directories — not symlinks — resolving inside the project root; refuse and report otherwise. Only then resolve the slug:
17+
The input carries the slug and (optionally) research direction or links. **Ancestor path safety (before any filesystem lookup here)**: where `.specify` or `.specify/assessments` already exist, verify each is a real directory (not a symlink) resolving inside the project root, and refuse and report if either exists as a symlink or escapes the root — a not-yet-created directory is allowed and will be created safely later. Only then resolve the slug:
1818

1919
1. **Explicit slug** (`slug=…`, `--slug …`, or an obvious token) — normalize it (see **Slug safety** below).
2020
2. **Conversation context** — if this session just ran `__SPECKIT_COMMAND_ASSESS_INTAKE__`, reuse the slug it reported. Confirm by checking that `.specify/assessments/<slug>/intake.md` exists; if not, fall through.

extensions/assess/commands/speckit.assess.shape.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Shape **outlines options at the boundaries; it does not produce a spec or a plan
1414
$ARGUMENTS
1515
```
1616

17-
**Ancestor path safety (before any filesystem lookup here)**: verify `.specify` and `.specify/assessments` are real directories — not symlinks — resolving inside the project root; refuse and report otherwise. Only then resolve the slug: explicit `slug=…` → conversation context (a slug reported earlier this session, confirmed by an existing `.specify/assessments/<slug>/` directory) → ask (interactive) → single existing directory (automated) → otherwise stop and ask. **Slug safety**: normalize any explicit or user-supplied slug — lowercase; whitespace/underscores → `-`; keep only `[a-z0-9-]` (drop every other character, including `.`, `/`, `\`); collapse and trim `-`; reject an empty normalized result. Only then set `ASSESS_SLUG` (the normalized value) and `ASSESS_DIR = .specify/assessments/<ASSESS_SLUG>` — this keeps every read and write inside `.specify/assessments/`.
17+
**Ancestor path safety (before any filesystem lookup here)**: where `.specify` or `.specify/assessments` already exist, verify each is a real directory (not a symlink) resolving inside the project root, and refuse and report if either exists as a symlink or escapes the root — a not-yet-created directory is allowed and will be created safely later. Only then resolve the slug: explicit `slug=…` → conversation context (a slug reported earlier this session, confirmed by an existing `.specify/assessments/<slug>/` directory) → ask (interactive) → single existing directory (automated) → otherwise stop and ask. **Slug safety**: normalize any explicit or user-supplied slug — lowercase; whitespace/underscores → `-`; keep only `[a-z0-9-]` (drop every other character, including `.`, `/`, `\`); collapse and trim `-`; reject an empty normalized result. Only then set `ASSESS_SLUG` (the normalized value) and `ASSESS_DIR = .specify/assessments/<ASSESS_SLUG>` — this keeps every read and write inside `.specify/assessments/`.
1818

1919
## Prerequisites
2020

0 commit comments

Comments
 (0)