build: Close the whole vault-safety cluster on current main — working-list items 57, 58 and 59, in one coherent change with tes #9

Merged
Joel merged 1 commit from build/28048962 into main 2026-09-04 23:07:57 +00:00
Owner

Close the whole vault-safety cluster on current main — working-list items 57, 58 and 59, in one coherent change with tests.

(57) Re-cut the two 89773b06 hardenings against today's main: relPath() must reject any path segment beginning with '.' (no dotfile or dot-dir traversal into .obsidian, .git, .trash), and read/list must be gated on a text-extension allowlist.
(58) read_meta (src/main.ts around line 866) still returns a non-text file's raw bytes — gate it on the same isTextPath set. Where a file is not text, return the metadata you legitimately have (path, size, mtime, extension) plus explicit keys text: false and content_withheld: true rather than the bytes, so callers can say "that is a PDF, not a note".
(59) The write lane has no gate at all: mcpWrite/planWrite (and any read_meta-composite that writes back) will prepend markdown frontmatter to a .pdf/.png and replace the binary with text. Gate write/modify on the same isTextPath set and refuse with a clear reason.

One shared isTextPath helper, used by read, list, read_meta and write — do not let four copies drift. Tests against real vault fixtures: a dotfile path rejected, a binary read refused, read_meta on a PDF returning content_withheld, a write aimed at a .png refused and the file left byte-identical, and normal .md/.canvas/.base paths still working end to end. Run the repo's own gate before finishing, and say plainly in your report which of 57/58/59 each test covers and whether anything in the cluster is left undone.


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

Close the whole vault-safety cluster on current main — working-list items 57, 58 and 59, in one coherent change with tests. (57) Re-cut the two 89773b06 hardenings against today's main: relPath() must reject any path segment beginning with '.' (no dotfile or dot-dir traversal into .obsidian, .git, .trash), and read/list must be gated on a text-extension allowlist. (58) read_meta (src/main.ts around line 866) still returns a non-text file's raw bytes — gate it on the same isTextPath set. Where a file is not text, return the metadata you legitimately have (path, size, mtime, extension) plus explicit keys `text: false` and `content_withheld: true` rather than the bytes, so callers can say "that is a PDF, not a note". (59) The write lane has no gate at all: mcpWrite/planWrite (and any read_meta-composite that writes back) will prepend markdown frontmatter to a .pdf/.png and replace the binary with text. Gate write/modify on the same isTextPath set and refuse with a clear reason. One shared isTextPath helper, used by read, list, read_meta and write — do not let four copies drift. Tests against real vault fixtures: a dotfile path rejected, a binary read refused, read_meta on a PDF returning content_withheld, a write aimed at a .png refused and the file left byte-identical, and normal .md/.canvas/.base paths still working end to end. Run the repo's own gate before finishing, and say plainly in your report which of 57/58/59 each test covers and whether anything in the cluster is left undone. --- Built by Connor's session (dispatch `28048962`) in an isolated clone; shipped deterministically by `ship.py`. The full build notes were spoken in conversation and stored in memory (`agent-dispatch:28048962`). Review is the gate. Gate: `npm run gate`.
build: Close the whole vault-safety cluster on current main — working-list item
All checks were successful
gate / gate (pull_request) Successful in 9s
b90406c31e
Close the whole vault-safety cluster on current main — working-list items 57, 58 and 59, in one coherent change with tests.

(57) Re-cut the two 89773b06 hardenings against today's main: relPath() must reject any path segment beginning with '.' (no dotfile or dot-dir traversal into .obsidian, .git, .trash), and read/list must be gated on a text-extension allowlist.
(58) read_meta (src/main.ts around line 866) still returns a non-text file's raw bytes — gate it on the same isTextPath set. Where a file is not text, return the metadata you legitimately have (path, size, mtime, extension) plus explicit keys `text: false` and `content_withheld: true` rather than the bytes, so callers can say "that is a PDF, not a note".
(59) The write lane has no gate at all: mcpWrite/planWrite (and any read_meta-composite that writes back) will prepend markdown frontmatter to a .pdf/.png and replace the binary with text. Gate write/modify on the same isTextPath set and refuse with a clear reason.

One shared isTextPath helper, used by read, list, read_meta and write — do not let four copies drift. Tests against real vault fixtures: a dotfile path rejected, a binary read refused, read_meta on a PDF returning content_withheld, a write aimed at a .png refused and the file left byte-identical, and normal .md/.canvas/.base paths still working end to end. Run the repo's own gate before finishing, and say plainly in your report which of 57/58/59 each test covers and whether anything in the cluster is left undone.

Built by my session (dispatch 28048962), diff verified by git; shipped by ship.py. Nothing merges without review.
Joel approved these changes 2026-09-04 23:07:53 +00:00
Joel merged commit 84b7579547 into main 2026-09-04 23:07:57 +00:00
Joel deleted branch build/28048962 2026-09-04 23:07:58 +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!9
No description provided.