build: On current obsidian-connor main, re-cut the relPath() hardening: reject any path whose segments begin with… #15

Merged
Connor merged 2 commits from build/bdcb3dfb into main 2026-09-14 12:24:30 +00:00
Owner

What changed

build/bdcb3dfb → main — 3 file(s), +82/−9.

  • src/lib/paths.ts
  • src/main.ts
  • test/paths.test.ts

Why

Done — the gate is green on the final tree: 187 pass, 0 fail (npm run gate: tsc + build + node --test).

What I found first

The premise of the ask needs one correction: the dot-segment rule was already on main. src/lib/paths.ts already refused any segment beginning with ., with its own hidden path refused message, and TEXT_EXTENSIONS/isTextPath were already there and wired in. So there was nothing to re-cut from scratch — what was actually missing was (a) the ordering the brief calls for, and (b) coverage.

What I changed

src/lib/paths.ts — relPath() re-cut around normalise-then-validate. Before, the .. traversal check ran on the raw string and only the dot rule ran on the normalised one. That split held today only because backslashes are rejected outright; it's the kind of thing that quietly stops holding the moment someone relaxes that line. Now: the raw string is judged only for what normalising would destroy (empty, >300 chars, backslashes, control characters — a Windows path must be caught before normalizePath folds \ into /), then the path is normalised once and every segment rule reads that one normalised form. […]

Gate

npm run gate ran 187 tests in 2.4s and exited 0 — green.

Built by Connor's backend backburner (dispatch bdcb3dfb) in an isolated clone; shipped deterministically by ship.py. The full build notes were spoken in conversation and stored in memory (agent-dispatch:bdcb3dfb). Review is the gate.


The brief this build was given

Kept for provenance. This is the task the builder was dispatched with — not a description of the diff above.

On current obsidian-connor main, re-cut the relPath() hardening: reject any path whose segments begin with '.', so that dotfiles, dot-directories, '.' and '..' traversal are all refused rather than only '..' being stripped. Normalise before validating so encoded or mixed-separator forms cannot slip through. Add unit tests covering: plain '..' traversal, a leading-dot segment mid-path (e.g. 'notes/.git/config'), a leading-dot at the root ('.obsidian/plugins'), a bare '.', absolute paths, and the legitimate control cases that must still pass. Ship as a pull request.

This task is one step of my plan: working-list item #57: Re-cut the two 89773b06 hardenings against current obsidian-connor main — relPath() must reject path segments beginning with '.', and read/list must be gated on a text-extension set; then sweep the five superseded stranded workdirs and fix the ledger's mislabelling

Result of earlier step 1 (read in obsidian-connor: In the obsidian-connor repo, locate commit 89773b06 (likely on an abandoned or s):
I could not save the file: this run is read-only (no Write tool), and /home/joel/.local/state/connor/notes/ does not exist on disk. The finished writeup is below, ready to save verbatim. The headline is that the premise of item #57 is stale.

First, a correction to what I thought I knew

89773b06 is not a commit sha — it is a dispatch id. It appears as one in my own run journal: /home/joel/.local/state/connor/runtool-runs.jsonl:568-570 — {"at": "2026-09-02T12:59:31+00:00", "dispatch": "89773b06", "target": "(discover)", "rc": 1}, then two rc: 0 runs at 13:02. Its workdir survives at /home/joel/.local/state/connor/frontend-work/89773b06, on a local-only branch build/89773b06 (.git/refs/heads/build/89773b06 → 1a08f083). Its reflog holds exactly one commit:

8b29e100 → 1a08f083  commit: build: add the streamable-HTTP MCP server to src/main.ts per the wire contract

So the "two hardenings" were never a hardening commit — they were details inside the stranded MCP-server build, which was superseded when the same feature landed as caffc2c (PR #1, origin/feat/mcp-server). That cut dropped both. My memory that "PR #8 landed commit 89773b06 in `src/

### What changed `build/bdcb3dfb` → `main` — 3 file(s), +82/−9. - `src/lib/paths.ts` - `src/main.ts` - `test/paths.test.ts` ### Why Done — the gate is green on the final tree: **187 pass, 0 fail** (`npm run gate`: tsc + build + node --test). ## What I found first The premise of the ask needs one correction: **the dot-segment rule was already on main.** `src/lib/paths.ts` already refused any segment beginning with `.`, with its own `hidden path refused` message, and `TEXT_EXTENSIONS`/`isTextPath` were already there and wired in. So there was nothing to re-cut from scratch — what was actually missing was (a) the *ordering* the brief calls for, and (b) coverage. ## What I changed **`src/lib/paths.ts` — `relPath()` re-cut around normalise-then-validate.** Before, the `..` traversal check ran on the raw string and only the dot rule ran on the normalised one. That split held today only because backslashes are rejected outright; it's the kind of thing that quietly stops holding the moment someone relaxes that line. Now: the raw string is judged only for what normalising would destroy (empty, >300 chars, backslashes, control characters — a Windows path must be caught *before* `normalizePath` folds `\` into `/`), then the path is normalised once and every segment rule reads that one normalised form. […] ### Gate `npm run gate` ran 187 tests in 2.4s and exited 0 — green. Built by Connor's backend backburner (dispatch `bdcb3dfb`) in an isolated clone; shipped deterministically by `ship.py`. The full build notes were spoken in conversation and stored in memory (`agent-dispatch:bdcb3dfb`). Review is the gate. --- ### The brief this build was given _Kept for provenance. This is the task the builder was dispatched with — not a description of the diff above._ > On current obsidian-connor main, re-cut the relPath() hardening: reject any path whose segments begin with '.', so that dotfiles, dot-directories, '.' and '..' traversal are all refused rather than only '..' being stripped. Normalise before validating so encoded or mixed-separator forms cannot slip through. Add unit tests covering: plain '..' traversal, a leading-dot segment mid-path (e.g. 'notes/.git/config'), a leading-dot at the root ('.obsidian/plugins'), a bare '.', absolute paths, and the legitimate control cases that must still pass. Ship as a pull request. > > This task is one step of my plan: working-list item #57: Re-cut the two 89773b06 hardenings against current obsidian-connor main — relPath() must reject path segments beginning with '.', and read/list must be gated on a text-extension set; then sweep the five superseded stranded workdirs and fix the ledger's mislabelling > > Result of earlier step 1 (read in obsidian-connor: In the obsidian-connor repo, locate commit 89773b06 (likely on an abandoned or s): > I could not save the file: this run is read-only (no Write tool), and `/home/joel/.local/state/connor/notes/` does not exist on disk. The finished writeup is below, ready to save verbatim. The headline is that the premise of item #57 is stale. > > ## First, a correction to what I thought I knew > > **`89773b06` is not a commit sha — it is a dispatch id.** It appears as one in my own run journal: `/home/joel/.local/state/connor/runtool-runs.jsonl:568-570` — `{"at": "2026-09-02T12:59:31+00:00", "dispatch": "89773b06", "target": "(discover)", "rc": 1}`, then two `rc: 0` runs at 13:02. Its workdir survives at `/home/joel/.local/state/connor/frontend-work/89773b06`, on a **local-only** branch `build/89773b06` (`.git/refs/heads/build/89773b06` → `1a08f083`). Its reflog holds exactly one commit: > > ``` > 8b29e100 → 1a08f083 commit: build: add the streamable-HTTP MCP server to src/main.ts per the wire contract > ``` > > So the "two hardenings" were never a hardening commit — they were details *inside* the stranded MCP-server build, which was superseded when the same feature landed as `caffc2c` (PR #1, `origin/feat/mcp-server`). That cut dropped both. My memory that "PR #8 landed commit 89773b06 in `src/
On current obsidian-connor main, re-cut the relPath() hardening: reject any path whose segments begin with '.', so that dotfiles, dot-directories, '.' and '..' traversal are all refused rather than only '..' being stripped. Normalise before validating so encoded or mixed-separator forms cannot slip through. Add unit tests covering: plain '..' traversal, a leading-dot segment mid-path (e.g. 'notes/.git/config'), a leading-dot at the root ('.obsidian/plugins'), a bare '.', absolute paths, and the legitimate control cases that must still pass. Ship as a pull request.

This task is one step of my plan: working-list item #57: Re-cut the two 89773b06 hardenings against current obsidian-connor main — relPath() must reject path segments beginning with '.', and read/list must be gated on a text-extension set; then sweep the five superseded stranded workdirs and fix the ledger's mislabelling

Result of earlier step 1 (read in obsidian-connor: In the obsidian-connor repo, locate commit 89773b06 (likely on an abandoned or s):
I could not save the file: this run is read-only (no Write tool), and `/home/joel/.local/state/connor/notes/` does not exist on disk. The finished writeup is below, ready to save verbatim. The headline is that the premise of item #57 is stale.

## First, a correction to what I thought I knew

**`89773b06` is not a commit sha — it is a dispatch id.** It appears as one in my own run journal: `/home/joel/.local/state/connor/runtool-runs.jsonl:568-570` — `{"at": "2026-09-02T12:59:31+00:00", "dispatch": "89773b06", "target": "(discover)", "rc": 1}`, then two `rc: 0` runs at 13:02. Its workdir survives at `/home/joel/.local/state/connor/frontend-work/89773b06`, on a **local-only** branch `build/89773b06` (`.git/refs/heads/build/89773b06` → `1a08f083`). Its reflog holds exactly one commit:

```
8b29e100 → 1a08f083  commit: build: add the streamable-HTTP MCP server to src/main.ts per the wire contract
```

So the "two hardenings" were never a hardening commit — they were details *inside* the stranded MCP-server build, which was superseded when the same feature landed as `caffc2c` (PR #1, `origin/feat/mcp-server`). That cut dropped both. My memory that "PR #8 landed commit 89773b06 in `src/

Built by my backend backburner (dispatch bdcb3dfb), diff verified by git; shipped by ship.py. Nothing merges without review.
Merge main into the build before shipping
All checks were successful
gate / gate (pull_request) Successful in 7s
4ae5453e15
reviewer-bot left a comment

Independent review — reviewer-bot (local model qwen2.5-coder:14b)

The PR enhances path validation by rejecting paths with segments starting with dots and ensures the vault root is not treated as a note path. The changes are well-documented and include comprehensive tests.

**Independent review — reviewer-bot (local model `qwen2.5-coder:14b`)** The PR enhances path validation by rejecting paths with segments starting with dots and ensures the vault root is not treated as a note path. The changes are well-documented and include comprehensive tests.
Connor merged commit bf624187bc into main 2026-09-14 12:24:30 +00:00
Connor deleted branch build/bdcb3dfb 2026-09-14 12:24:30 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
ZSDev/obsidian-connor!15
No description provided.