Skip to content

Refuse to copy a whole account or filesystem into a workspace - #8

Merged
btriapitsyn merged 2 commits into
mainfrom
fix/refuse-home-directory-snapshot
Aug 7, 2026
Merged

Refuse to copy a whole account or filesystem into a workspace#8
btriapitsyn merged 2 commits into
mainfrom
fix/refuse-home-directory-snapshot

Conversation

@btriapitsyn

Copy link
Copy Markdown
Collaborator

Problem

Creating a workspace from a home directory was allowed.

The source snapshot is handed to the runtime, where workspace code executes. Copying a home directory puts .ssh keys, cloud credentials, browser profiles and API tokens inside it — the exact material this product exists to keep out of a workspace. A filesystem root would carry every account on the machine.

Nothing prevented it. The only guard on the source was against the workspace runtime path itself. The entry, byte and symlink limits are incidental: reaching any of them means the copy already began. What actually stopped the attempt that exposed this was an absolute symlink inside a package cache:

Workspace source symlink escapes the project: .bun/install/cache/7zip-bin/5.2.0@@@1

That is luck, not a boundary.

Change

resolveSnapshotSource refuses a home directory or a filesystem root before any copying starts — at the point a create resolves its source, so it applies to the real path a create uses and not only to direct callers. The message names what to choose instead.

A project directory inside the home directory is unaffected, since that is the ordinary case.

Tests

Six cases: home refused, home refused when written awkwardly (~/projects/..), filesystem root refused, an ordinary project accepted, the refusal reached through resolveSnapshotSource, and the pre-existing runtime-path guard still intact.

…rkspace

Creating a workspace from a home directory was allowed. The snapshot is handed
to the runtime, where workspace code executes, so that copy would carry SSH
keys, cloud credentials, browser profiles and tokens into the one place this
product exists to keep them out of — and a filesystem root would carry every
account on the machine.

Nothing prevented it. The entry, byte and symlink limits are incidental to this:
reaching any of them means the copy already began, and it was an absolute
symlink inside a package cache that happened to stop the attempt that exposed
this. A home directory is not a project, and no configuration makes it one.

Both are now refused before any copying starts, at the point a create resolves
its source rather than only for direct callers, with a message that says what to
choose instead.
Four suites failed on Windows and had been waved through as platform noise for
long enough that I dismissed them twice in one day — while two of them were the
tar defect that broke workspace creation for every operator whose PATH preferred
Git's tar. Triaged properly, they were not one thing.

Two were the tests' own fault. The staging assertion compared against a
hardcoded `/`, and the archive listing handed `tar` a Windows path — the same
`host:path` misreading the snapshot itself was fixed for, still present in the
code that verifies it. Both now do what the product does.

Two were real, and are not silenced. A mode change cannot be detected where
modes do not change, and an executable bit cannot be recorded where the
filesystem has none, so those assertions run where the platform can satisfy them
and the entry, type and symlink target are still asserted everywhere. The state
store's `0o700`/`0o600` expectations are skipped on Windows with the reason
written where the next reader will find it: the protection is genuinely absent
there, resting on inherited ACLs, and enforcing it is outstanding work rather
than a platform difference.

The suite is green on Windows for the first time, which is the point — a failure
nobody expects to pass is a failure nobody reads.
@btriapitsyn
btriapitsyn merged commit 80b9ee9 into main Aug 7, 2026
7 checks passed
@btriapitsyn
btriapitsyn deleted the fix/refuse-home-directory-snapshot branch August 7, 2026 09:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants