Skip to content

Restrict workspace state on Windows, where the declared modes do nothing - #9

Merged
btriapitsyn merged 1 commit into
mainfrom
fix/protect-windows-state-acl
Aug 7, 2026
Merged

Restrict workspace state on Windows, where the declared modes do nothing#9
btriapitsyn merged 1 commit into
mainfrom
fix/protect-windows-state-acl

Conversation

@btriapitsyn

Copy link
Copy Markdown
Collaborator

The state store creates 0o700 directories and 0o600 files so container endpoint tokens stay with one account. Windows implements neither — chmod is close to a no-op there and every file reports 0o666.

Under a default profile the store is private regardless, because it inherits the profile's permissions. That is why this went unnoticed, and it is also why it cannot be relied on: point OPENCHAMBER_WORKSPACE_STATE_DIR at a second drive or a shared folder and the store inherits that location's permissions instead, with nothing in the code objecting.

Windows expresses this with an access list, which Node cannot set. The state root now gets one through icacls, and everything created beneath it inherits the result — once per root, not once per write.

Details that are load-bearing

  • SYSTEM and Administrators are named by security identifier, not by name. Their names are localised; Administrators does not exist on a Ukrainian or German install.
  • Both system tools are named by absolute path. Git for Windows ships a POSIX whoami that rejects /user, and it usually comes first on PATH — the same shadowing that made its tar read C:\… as a remote host and broke workspace creation earlier on this branch. Found here the same way: the first run failed with whoami: extra operand '/user'.
  • The list is emptied before it is rebuilt. /inheritance:r drops what the parent contributed and /grant:r replaces only the principals it names, so an entry someone added explicitly for a third party would otherwise survive being "restricted".
  • It uses child_process, not the shared runner. Redaction and provider attribution mean nothing for an OS primitive, and provider suites replace that runner wholesale — routing through it left the state store unable to write a file in every test that mocks a container CLI.

Testing

The state store test no longer skips itself on Windows; it asks each platform about the mechanism that actually restricts the store there. Both new suites open their own directory to Everyone first, because a directory made under %TEMP% is already private and the assertion would otherwise pass whether or not the code protects anything.

Verified by disabling the protection and confirming both suites fail, then restoring it. Full suite on Windows: 151 passed, 3 skipped, 0 failed.

The store creates 0o700 directories and 0o600 files to keep container endpoint
tokens to one account, and Windows implements neither: chmod is close to a no-op
and every file reports 0o666. Under a default profile the store is private
anyway, by inheritance — which is why this went unnoticed. Point
OPENCHAMBER_WORKSPACE_STATE_DIR at a second drive or a shared folder and it
inherits that location's permissions instead, and nothing objects.

Windows states this with an access list, which Node cannot set, so the state root
gets one through icacls and everything created beneath it inherits the result.
Once per root, not once per write.

Three details are load-bearing. SYSTEM and Administrators are named by security
identifier, because their names are localised and "Administrators" does not exist
on a Ukrainian install. Both system tools are named by absolute path, because Git
for Windows ships a POSIX whoami that rejects /user and usually comes first on
PATH — the same shadowing that made its tar read C:\ as a remote host. And the
list is emptied before it is rebuilt: /inheritance:r drops what the parent
contributed and /grant:r replaces only the principals it names, so an entry
someone added explicitly for a third party would otherwise survive being
"restricted".

It runs through child_process rather than the shared runner. Redaction and
provider attribution mean nothing for an OS primitive, and provider suites
replace that runner wholesale — routing through it left the store unable to write
a file in every test that mocks a container CLI.

The state store test no longer skips itself on Windows. It asks each platform
about the mechanism that actually restricts the store there, and it opens its own
root to Everyone first: a directory made under %TEMP% is already private, so
without that the assertion passes whether or not the code protects anything.
Verified by disabling the protection and watching both suites fail.
@btriapitsyn
btriapitsyn merged commit fa8eefd into main Aug 7, 2026
7 checks passed
@btriapitsyn
btriapitsyn deleted the fix/protect-windows-state-acl branch August 7, 2026 10:39
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