The per-session random write SID forced a full tree propagation per session per server lifetime (minutes on large workspaces). The write SID is now the per-workspace identity derived from the canonical workspace path (workspaceWriteSid: sha256 -> S-1-4-x-y), stored nowhere: the workspace-root ACE materializes once per workspace per machine and every later provision hits the exact-ACE skip. - workspace ACEs are STANDING (never revoked - the reuse cache); temp ACEs stay revocable (disposed with the provider), so an inheritable ACE never outlives its session's temp dir on the ambient temp root - AclSandbox requires the write SID under workspace-write; read-only parses/grants nothing; the runner derives the SID itself (the --write-sid flag's presence still marks the seam-managed contract) - the acl-session record drops writeSid (sessionId/workspace/tempDir remain): the SID-tamper surface and its validation are gone - sandbox-local holds two grant maps: standing workspace grants and revocable per-session temp grants Docs (README pair, design note pair, catalogs, type-equiv) and the acl-session/grant/acl/probe/runner suites updated; workspace-sid.spec pins the derivation contract.
31 lines
1.3 KiB
TypeScript
31 lines
1.3 KiB
TypeScript
/**
|
|
* workspaceWriteSid tests: the per-workspace write identity is deterministic
|
|
* (the same canonical path always derives the same SID — the property the
|
|
* cross-session grant reuse rests on), orphan-shaped, distinct across
|
|
* workspaces, and byte-sensitive (the canonical path is the caller's
|
|
* contract; an alias spelling derives a second identity, self-healing at
|
|
* the cost of one extra tree propagation).
|
|
*/
|
|
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
import { workspaceWriteSid } from '../src/index.ts'
|
|
|
|
describe('workspaceWriteSid', () => {
|
|
it('derives a stable orphan-shaped SID per workspace path', () => {
|
|
const first = workspaceWriteSid('C:\\Users\\agent\\repo')
|
|
const second = workspaceWriteSid('C:\\Users\\agent\\repo')
|
|
expect(first).toBe(second)
|
|
expect(first).toMatch(/^S-1-4-\d+-\d+$/u)
|
|
})
|
|
|
|
it('derives distinct identities for distinct workspaces', () => {
|
|
expect(workspaceWriteSid('C:\\Users\\agent\\repo-a')).not.toBe(workspaceWriteSid('C:\\Users\\agent\\repo-b'))
|
|
})
|
|
|
|
it('is byte-sensitive: the canonical path is the caller\'s contract (an alias spelling derives a second identity)', () => {
|
|
expect(workspaceWriteSid('C:\\Repo')).not.toBe(workspaceWriteSid('c:\\repo'))
|
|
expect(workspaceWriteSid('C:\\Repo\\')).not.toBe(workspaceWriteSid('C:\\Repo'))
|
|
})
|
|
})
|