100 lines
3.1 KiB
JavaScript
100 lines
3.1 KiB
JavaScript
/**
|
|
* Reduced `ctx` for `bareOsSandboxRunScript`: personal-drive writes only, no identity hooks.
|
|
*
|
|
* Stronger isolation (separate worker / bundle evaluate) is an optional future path; see
|
|
* holepunch `cross-worker` and `bare-bundle-evaluate` for Bare/Pear patterns. When
|
|
* `BARE_OS_SANDBOX_WORKER=1`, the booter defers evaluation on a fresh microtask only
|
|
* (not a separate thread); see developer-guide security chapter.
|
|
*/
|
|
|
|
import { applyGuestEnv, wipeSecret } from './identity-session.js'
|
|
import { isPersonalRoute } from './vfs-posix-meta.js'
|
|
|
|
/**
|
|
* @param {Record<string, unknown>} ctx
|
|
* @returns {boolean}
|
|
*/
|
|
function sandboxWritesOk(ctx, logicalPath) {
|
|
const vfs = ctx.vfs
|
|
if (!vfs || typeof vfs.resolveLogical !== 'function') return false
|
|
if (typeof vfs.route !== 'function') return false
|
|
const abs = vfs.resolveLogical(logicalPath)
|
|
const r = vfs.route(abs)
|
|
const pd = ctx.personalDrive
|
|
if (!pd) return false
|
|
return isPersonalRoute(pd, r)
|
|
}
|
|
|
|
/**
|
|
* @param {Record<string, unknown>} ctx
|
|
*/
|
|
export function createBareOsSandboxContext(ctx) {
|
|
const vfs = ctx.vfs
|
|
if (!vfs) throw new Error('bareOsSandboxRunScript: missing vfs')
|
|
|
|
/**
|
|
* @param {string} name
|
|
* @param {(...args: unknown[]) => unknown} fn
|
|
* @param {number} pathArgIndex
|
|
*/
|
|
const wrapWrite = (name, fn, pathArgIndex = 0) => {
|
|
if (typeof fn !== 'function') return fn
|
|
return async function (...args) {
|
|
const path = args[pathArgIndex]
|
|
if (typeof path === 'string' && !sandboxWritesOk(ctx, path)) {
|
|
throw new Error(`sandbox: ${name}: write denied outside personal namespace`)
|
|
}
|
|
return fn.apply(vfs, args)
|
|
}
|
|
}
|
|
|
|
const sandboxVfs = Object.assign(Object.create(Object.getPrototypeOf(vfs)), vfs, {
|
|
writeFile: wrapWrite('writeFile', vfs.writeFile, 0),
|
|
mkdir: wrapWrite('mkdir', vfs.mkdir, 0),
|
|
unlink: wrapWrite('unlink', vfs.unlink, 0),
|
|
rmdir: wrapWrite('rmdir', vfs.rmdir, 0),
|
|
chmod: wrapWrite('chmod', vfs.chmod, 0),
|
|
chown: wrapWrite('chown', vfs.chown, 0),
|
|
symlink: wrapWrite('symlink', vfs.symlink, 1)
|
|
})
|
|
|
|
/** @type {Record<string, unknown>} */
|
|
const o = Object.assign({}, ctx, {
|
|
vfs: sandboxVfs,
|
|
bareOsSandboxed: true,
|
|
async applyUnlock() {
|
|
throw new Error('sandbox: identity unlock disabled')
|
|
},
|
|
async applyRegister() {
|
|
throw new Error('sandbox: identity register disabled')
|
|
},
|
|
async applyLogin() {
|
|
throw new Error('sandbox: identity login disabled')
|
|
},
|
|
async applyLogout() {
|
|
wipeSecret(this)
|
|
try {
|
|
await applyGuestEnv(this)
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
this.console?.log?.('sandbox: logged out (guest session)')
|
|
},
|
|
async saveVault() {
|
|
this.console?.log?.('sandbox: saveVault skipped (no vault persistence in sandbox)')
|
|
},
|
|
registerKernelShutdownHook() {},
|
|
bareOsRegisterVirtualFile() {
|
|
throw new Error('sandbox: bareOsRegisterVirtualFile disabled')
|
|
},
|
|
bareOsInvalidateVirtualFile() {
|
|
throw new Error('sandbox: bareOsInvalidateVirtualFile disabled')
|
|
},
|
|
bareOsUpdateVirtualFileMeta() {
|
|
throw new Error('sandbox: bareOsUpdateVirtualFileMeta disabled')
|
|
}
|
|
})
|
|
|
|
return o
|
|
}
|