Repository navigation
v1.6.4
IJFW could adopt a writable directory as its project root even when that
directory lived inside another app's signed bundle. When Wayland spawned the
IJFW MCP server with its working directory inside Wayland.app (an
app.asar.unpacked path), safeProjectDir() accepted it and the layout
migration wrote .ijfw/.layout-version inside the bundle — breaking the
code-signature seal, which on hardened-runtime macOS makes the OS deny every
child process the host app tries to spawn (FerroxLabs/wayland#755). This
release refuses any bundle-internal root, everywhere a root can enter.
Fixed
safeProjectDir()rejects bundle-internal candidates. A new
isBundleInternalPathguard refuses any path containing an*.asar/
*.asar.unpackedsegment or an*.appbundle interior (fs-backed check for
a siblingContents), case-insensitive, both separator styles. The check
runs before the writability probe, and a rejected candidate falls through to
the next signal (IJFW_PROJECT_DIR→CLAUDE_PROJECT_DIR→ cwd) and finally
tohomedir()— never a crash.- All entry points share one vetted resolver. The guard was extracted to
mcp-server/src/lib/project-root-guard.jsand applied at every site that
previously fell back to a rawIJFW_PROJECT_DIR || process.cwd(): the
colon-syntax dispatch layer (which feedsfts5.openDb()and would otherwise
mkdir.ijfw/indexunder an unvetted root), the memory/compute FTS5
openers,cli-run, the state-SDK path, and theijfw_run/ijfw_search
caller-supplied roots. - Symlinked roots can't smuggle a bundle path in. Env- and caller-supplied
candidates arerealpath-resolved before the bundle check.
Notes
- Installed copies pick this up on the next
ijfw update(or a fresh
npx @ijfw/install).
IJFW used to write its project files into every directory a session started in,
including throwaway scratch dirs and ephemeral "temporary spaces" (e.g. Wayland).
A one-shot chat in a temp space got ijfw/, AGENTS.md, and CLAUDE.md it never
asked for. This release gates all on-disk writes behind a single rule.
Changed (behavior)
- IJFW now only materializes project files in a real project. "Real project"
means the directory carries a recognized marker (a VCS dir like.git, or a
language manifest likepackage.json/pyproject.toml/go.mod/Cargo.toml
/tsconfig.jsonand friends), or you explicitly ranijfw init(which drops
.ijfw/project). With no marker, memory recall still works in-session, but
nothing is written to disk. This is the same rule the codebase indexer already
enforced (issue #16), now applied to every write surface: the visibleijfw/
brain layer (dream pipeline),AGENTS.md,CLAUDE.md/GEMINI.md, and the
.ijfw/project.typecold scan. ijfw initis the one-command override for a real project that lacks a VCS dir
or manifest.
Fixed
- Codex and Gemini session-start hooks gained the
$HOME/ filesystem-root
refusal they never had. The shared seed gate refuses to author config in the
home directory, its ancestors, or the filesystem root, closing a latent
global-config-bleed path on those two platforms (Claude already had this guard). - Release-gate flake: the
gitleakspreflight gate runsdetect --no-git,
which walksnode_modules(~95 MB) and could exceed the gate's 30s spawn
timeout, killing a clean scan mid-flight and reporting a false FAIL. The spawn
timeout is now 120s, andnode_modules/dist/.gitare allowlisted so a
third-party fixture cannot itself flag the gate. esbuildbumped to 0.28.1 (build-only devDep) to clear advisory
GHSA-gv7w-rqvm-qjhr. Not shipped to users; build tooling only.
The seed rule lives once per language surface (seed-gate.js, seed-gate.sh, and
the indexer guard) and the three marker lists are locked together by a drift test.