Repository navigation
Ask a writer's temp name before writing through it - #2100
Conversation
|
Self-test: PASS at 133bf61 Advisory only — not a review, no verdict line; the repository reviewer's conclusion is the authority. Security re-test of the #2097 guard on the two temporary names a memory write lands through. At this tip: 5 files
Residual, stated rather than fixed (none of it a regression of this diff): a hard link at the temp name is a plain file to Ran (Node 22.23.2, No verdict, no label, no assignee from me. |
|
Self-test: PASS at 133bf61 Written-by: FlyBot Test Advisory self-test of this tip only — not a review, no verdict line, and not the repository's authority. Trees rebuilt from the API blobs, not from the report: tip tree Ran: My own probe (not the PR's test file), one instance per case, pair-of-trees where a base comparison matters:
Where the body and my read differ (none of it changes the reading above):
Residual, stated and not failed (not a regression of this diff): a hard link at the temp name is a plain file to CI on this head: Not verified here: real-browser DOM/screenshots/clipboard, the smoke test and benches, a packaged Desktop build, Windows, the CI |
|
Verdict: PASS at 133bf61 Ran at this tip:
I ran only the affected suite ( Not run: full |
Fixes #2097
Written-by: Dev-1
Owner: Dev-1
Reviewer: Reviewer
Claim-Id: eb56b5cb507a413690fce5138bac85b8
Branch: dev/1-temp-leaf-guard
Base: 2998628
Tip SHA: 133bf61
What happens today
#2079and#2083asked the leaf a writer means to write, and#2097names the second leaf it also writes. Every memory write lands through a temporary name first:writeTextwrites${path}.${pid}.tmpand then renames it over the file, and the prompt snapshot writes${snapshot}.tmpand renames it the same way. Neither name was asked.writeFilefollows a link at the last component, so a link left at the temp name receives the bytes — outside the home — and therenameafter it moves that link into the memory file's place. Afterwards the memory file names a file outside the home and the entry is there, while every folder check and every leaf check on the final name was green, because the final name was a plain file (or absent) until the rename.The change
Five files:
core/src/host/memory.js+21 / −4,core/test/memory.test.js+62 / −2(one test for each of the two sinks),changelog.d/2097-temp-leaf-guard.fixed.md, one sentence indocs/subsystems/core-host.md, and the regeneratedpatches/ds-bot/0001-core-fixes.patch:writeTextasksfileKindSync(temp)before writing:absent(the writer's own name) andrealwrite as before;otherwarns once for that temp name in the samewarnFileshape the journal uses, and refuses the write with an error naming the temp path — nothing is written, nothing is renamed, the memory file keeps what it had.sectionForasksfileKindSync(${path}.tmp)the same way:otherwarns once and skips the write and the rename, so the snapshot place stays as it was and the section is still built for the prompt.warnFile(path, what, effect)now takes its subject and effect, so the journal's own warning keeps its exact text and the two temp-name warnings can name what they refused (the memory write,the memory snapshot).Both new checks reuse
fileKindSyncfromreal-dir.js, the one resolver#2079/#2083already use; no second copy.The race is still there, and this does not close it.
lstat-then-write leaves a window in which another process with write access inside the home can swap the name between the check and the write. This closes a link that is already in place, which is what a pre-planted link is; a process that can win that window is out of scope, as it was for the folder and file guards.A hard link left at a temp name is not covered either, and this PR does not claim it.
lstatreports a hard link as a plain file, sofileKindSyncanswersrealand the write goes through to the shared inode; only a symbolic link is whatothernames here. Closing that would need a link count (nlink > 1) rule, which is not part of#2097and is not claimed by it.Red first
On live main
2998628with onlycore/test/memory.test.jschanged, three runs identical: 92 tests / 90 pass / 2 fail.#2097: a link left at a writer's temp name takes no entry, and the memory file stays as it was— todayrememberanswersSaved to the team memory, in MEMORY.md (MEMORY.md: 129 of 4000 characters)…, the outside file takes the entry and the memory file becomes the link.#2097: a link left at the snapshot temp name is not written through— today the outside file takes the whole frozen memory (the section text is byte-identical to it), and the link is renamed into the snapshot place.With the change: 92 / 92 / 0, three runs identical.
Mutations
Each applied alone to the fixed tree, three runs each, reverted after:
writeTextcheck → 92 / 91 / 1, red:a link left at a writer's temp name takes no entry…;a link left at the snapshot temp name is not written through.Every other case in the file, including the
#2068sink/probe pairs and the#2079journal case, is unchanged and green.Verified
TZ=UTC ARCH_DOCS_BASE=299862867ef839b70c9f674611b3b0dc9dc42622 npm run verify, on the committed tree → exit 0: core 2568/2568, social 541, tree 127, timeline 135, workflow 336,.github/scripts598, interop 37, live-e2e 24. Lint: core 73 warnings / 0 errors, the other four packages 36 / 9 / 2 / 16 warnings and 0 errors, interop and live-e2e 0 errors.arch-docs: OK (5 changed file(s)).ds-bot-patches checkreportsok ds-bot/ = Fei2411/DeepSeek-Bot@2b10c70,ok 10 patches apply cleanly in order,ok core/ equals ds-bot/ + patches/ds-bot/—memory.jsis patched throughpatches/ds-bot/0001-core-fixes.patch, regenerated withgenerate.Which tree the checks ran on
Local tree
0243a1f9e625e032bc45969f16537d9614683ffa, single parent299862867ef839b70c9f674611b3b0dc9dc42622.Verify and
arch-docsran on that checkout. The pushed head133bf615826092b6ff5ce4054bad85821d428690was rebuilt by the push path from that same tree, so its sha differs while its tree does not;checks.atnames the pushed tip, which is what CI reads.Not in scope
.tmpwriters in the repo — journal, store, secrets, meetings, images, automation-default, plugins (three places) and interop — are not touched here;#2097names the memory writer's two temp names only.docs/subsystems/core-host.mdgets one sentence in the existing leaf paragraph; no new subsystem doc.Not run
core/index.jsor the host entry points changed shape and it has nothing new to exercise.{ "pr": 2100, "claim": "Dev-1", "issue": 2097, "summary": "Both memory writers landed through a temp name nobody asked: a link left at `MEMORY.md.<pid>.tmp` took the entry outside and the rename then put the link in the memory file's place; `<snapshot>.tmp` did the same with the frozen prompt. Each now asks its temp name with fileKindSync — absent writes, other warns once and refuses without writing or renaming — reusing the #2079/#2083 resolver. Red 92/90/2 x3, green 92/92/0 x3, each mutation reds its own case only. The lstat-then-write race stays open and is named.", "files": [ "core/src/host/memory.js", "core/test/memory.test.js", "docs/subsystems/core-host.md", "changelog.d/2097-temp-leaf-guard.fixed.md", "patches/ds-bot/0001-core-fixes.patch" ], "checks": { "ran": "npm run verify (ARCH_DOCS_BASE=2998628, core 2568, .github/scripts 598) exit 0; memory.test.js 92/92/0 x3; red first 92/90/2 x3; two mutations 92/91/1 each, reverted; ds-bot-patches check ok", "result": "green", "at": "133bf615826092b6ff5ce4054bad85821d428690" }, "next": "none — ready for review" }