first commit
This commit is contained in:
71
docs/api-stack/api/api-sandbox-wasm/src/preopens/preopens.md
Normal file
71
docs/api-stack/api/api-sandbox-wasm/src/preopens/preopens.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# preopens
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Maps `policy.fs[]` rules onto a WASI preopen table. The WASI capability
|
||||
model is file-descriptor-based: a guest can only open paths reachable
|
||||
from a preopened directory, and the host controls whether each preopen
|
||||
is read-only or read-write. This module owns that translation.
|
||||
|
||||
Paths outside the policy allowlist are NOT preopened, so the guest's
|
||||
`path_open` cannot reach them — denial is enforced by the absence of
|
||||
the preopen, not by a runtime check. Write access requires the `rw:`
|
||||
rule prefix; the default is read-only.
|
||||
|
||||
## Features
|
||||
|
||||
- `rw:` prefix promotes a preopen to read-write; absence keeps it read-only.
|
||||
- Globs `/*` and `/**` are stripped because WASI preopens are
|
||||
directory-granular — file-level scoping is enforced by the WASI
|
||||
runtime's resolved-path check.
|
||||
- Deduplication: duplicate rules collapse, with `rw` winning over `ro`.
|
||||
- `matchPreopen` returns the preopen for `path_open` gating, or
|
||||
`undefined` for a miss / write-against-ro.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import {
|
||||
buildWasiPreopens,
|
||||
resolvePermissionPolicy,
|
||||
} from "@suaveplan/api-sandbox-wasm";
|
||||
|
||||
const policy = resolvePermissionPolicy({ fs: ["./uploads/*", "rw:./tmp"] });
|
||||
const preopens = buildWasiPreopens(policy);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { matchPreopen } from "@suaveplan/api-sandbox-wasm/preopens";
|
||||
|
||||
const matched = matchPreopen(preopens, "/abs/host/path/to/file", "rw");
|
||||
if (matched === undefined) {
|
||||
metrics.permissionDenied("fs", "preopen_miss_or_ro");
|
||||
throw new SandWasmError(SAND_WASM_CODES.PREOPEN_DENIED, "denied");
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `buildWasiPreopens(policy): readonly WasiPreopen[]`
|
||||
- `matchPreopen(preopens, hostPath, "ro" | "rw"): WasiPreopen | undefined`
|
||||
- `WasiPreopen.{guestPath, hostPath, mode}`
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`hostPath` is computed via `node:path.resolve`, anchoring relative
|
||||
rules against `process.cwd()` AT POLICY-BUILD TIME — operators must
|
||||
build the policy in the directory the plugin expects to see. The
|
||||
resolved path is used as the dedup key; rules that produce the same
|
||||
absolute path collapse, with `rw` winning over `ro` because privilege
|
||||
escalation is more visible than silent downgrade.
|
||||
|
||||
WASI's preview1 file-rights bitmask is rich (read, write, allocate,
|
||||
truncate, etc.) — the implementation here intentionally uses a
|
||||
two-state mode so policy authoring stays readable. Sites that need
|
||||
finer granularity should layer their own bitmask check on top.
|
||||
Reference in New Issue
Block a user