test: verify filesystem extension and principal security contracts
Add 21 independent memory-backend external-style tests, both principal HTTP flows, atomic validator/target/effect checks, budgets and sanitized failures; document migration and implementer responsibilities. All T-19 acceptance commands pass; Clippy runs in the repository Nix development shell.
This commit is contained in:
@@ -6,6 +6,99 @@ keep one object instance per entry. It uses `wip-host` for publication and typed
|
|||||||
operation dispatch and uses the standard `wip-http` routes, JSON codecs,
|
operation dispatch and uses the standard `wip-http` routes, JSON codecs,
|
||||||
envelopes, and HTTP status mapping.
|
envelopes, and HTTP status mapping.
|
||||||
|
|
||||||
|
## Library assembly and migration
|
||||||
|
|
||||||
|
The existing `ServerConfig`, `WipFsServer::new`, `handle_http`, CLI flags and wire
|
||||||
|
representation remain compatible. The formerly private projection has become a
|
||||||
|
public, generic library engine. No package version or publication policy changes
|
||||||
|
are required.
|
||||||
|
|
||||||
|
Use `default_projection(root, access, limits)` to obtain the default Linux engine
|
||||||
|
without starting a listener. `projection.view(&principal)` is the effective view;
|
||||||
|
it owns canonical `observe` and `fetch_interface` and implements the standard
|
||||||
|
`wip_host::ObjectProvider` typed `call_operation` API. `DefaultPolicy` has the unit
|
||||||
|
principal `()`; default engines also offer unit-principal convenience methods.
|
||||||
|
|
||||||
|
For an external backend/composition:
|
||||||
|
|
||||||
|
1. Implement `BackendEntry` on a local, cloneable, root-confined stable capability.
|
||||||
|
Its `WipObject` metadata is public-only; its validator binds identity and the
|
||||||
|
protected state. Implement `FilesystemBackend` to resolve capabilities,
|
||||||
|
enumerate bounded children, and execute against an already-resolved entry.
|
||||||
|
2. Register generated `#[wip_interface::interface]` definitions using the existing
|
||||||
|
`wip_host::InterfaceRegistry`. `DefaultInterfaces::register` optionally supplies
|
||||||
|
reusable built-in tokens; using those tokens does not force their publication.
|
||||||
|
3. Implement `Composition<Entry>` on a local type. Its local Object wraps the entry
|
||||||
|
and implements both `WipObject` and the chosen generated traits, avoiding orphan
|
||||||
|
rules. `PublishedObject::expose` proves each implementation at compile time.
|
||||||
|
Publication can add, replace, reorder or omit built-ins based on entry metadata
|
||||||
|
and actual backend capabilities, not just entry kind. `LinuxEntry` implements
|
||||||
|
bounded `FileRead`, `FileWrite` and `SymlinkInspect` for safe delegation; never
|
||||||
|
advertise write in read-only mode or link operations on a non-link capability.
|
||||||
|
4. Assemble `Projection::new(backend, registry, composition, policy, limits)`.
|
||||||
|
`ProjectionPolicy` supplies an opaque associated `Principal`, hides Objects and
|
||||||
|
entire hidden subtrees, selects an ordered subset of implemented interfaces,
|
||||||
|
controls descriptor/operation visibility, and permits or denies execution.
|
||||||
|
5. An authenticated deployment chooses the principal and `ExecutionContext`. To
|
||||||
|
use HTTP, construct `HttpAdapter::new(&view, &endpoint, limits)` and invoke
|
||||||
|
`handle_http_with_context`. This uses the standard `wip-http` binding, not a
|
||||||
|
filesystem wire profile. Enforce body collection, header, concurrency and
|
||||||
|
deadline bounds in the deployment's listener; the adapter owns none of these
|
||||||
|
intake resources. The bundled loopback listener remains a thin default user.
|
||||||
|
|
||||||
|
A complete external-style memory backend, local Object with multiple custom
|
||||||
|
Operations, typed registration, principal policy and HTTP flow is exercised in
|
||||||
|
[`tests/extensions.rs`](tests/extensions.rs). It uses no Linux filesystem for its
|
||||||
|
backend. The public library APIs do not require private types, a fork, an HTTP
|
||||||
|
listener, a global object registry, or dynamic plugin loading.
|
||||||
|
|
||||||
|
### Extension security contract
|
||||||
|
|
||||||
|
Backend implementations and Operation authors are trusted code, not sandboxed
|
||||||
|
plugins. `FilesystemBackend::execute` must refresh state/validators **inside** its
|
||||||
|
transaction or lock before calling the engine callback, retain the same resolved
|
||||||
|
target through validation and dispatch, and return the committed post-operation
|
||||||
|
validator. Confirm rollback only after rollback succeeds. Pre-dispatch failures
|
||||||
|
must never commit; failed started operations without a non-commit guarantee remain
|
||||||
|
`OperationOutcomeUnknown`. Object implementations must delegate validators to
|
||||||
|
that entry and declare requirements per `(InterfaceRef, operation)`, rather than
|
||||||
|
classify all operations by a read/write name. Operations use the scoped entry and
|
||||||
|
propagated context, not a fresh raw-path resolution; they must check deadlines,
|
||||||
|
cancellation and resource budgets throughout backend work and report effects
|
||||||
|
truthfully. POSIX symlinks, identity, atomic mutations and locking cannot be assumed
|
||||||
|
for other backends.
|
||||||
|
|
||||||
|
The default Linux backend uses advisory exclusive locks on regular files,
|
||||||
|
refreshes validators after acquiring the lock and holds it through post-dispatch
|
||||||
|
state observation. This serializes cooperating library calls; it does **not**
|
||||||
|
promise transactional isolation from unrelated processes ignoring `flock`.
|
||||||
|
Descriptor-relative pinned handles still prevent replacement from redirecting an
|
||||||
|
operation to a different file. Linux capabilities only expose bounded methods,
|
||||||
|
not raw descriptors or backend paths.
|
||||||
|
|
||||||
|
A policy must be stable/deterministic within one effective Worldspace view.
|
||||||
|
Interface/operation visibility is uniform per registered reference within that
|
||||||
|
view, so observation, fetch and dispatch use the identical filtered descriptor.
|
||||||
|
For entry-specific operation sets, register distinct interface definitions and
|
||||||
|
select them by entry. Different principals may have different effective views.
|
||||||
|
Changing a policy must establish a new view/reference lifetime (e.g. rebuild the
|
||||||
|
registry/engine); do not keep validator-free references while changing their
|
||||||
|
meaning. Execution permission may deny a visible Operation without hiding it.
|
||||||
|
A published interface is always fetchable by that principal; hidden operations
|
||||||
|
are omitted (along with their unreachable named types and interface-wide docs)
|
||||||
|
and guessed calls return `OperationNotFound`, whereas visible denied
|
||||||
|
operations return `PermissionDenied`. Hidden Objects and descendants return
|
||||||
|
`NotFound` and are absent from parent observations. Work spent scanning hidden
|
||||||
|
entries still counts against traversal bounds; resource limits are not a
|
||||||
|
constant-time/side-channel guarantee.
|
||||||
|
|
||||||
|
Never put absolute paths, hidden metadata, credentials or policy internals in
|
||||||
|
public `WipObject` metadata, descriptions, validators or normal logs. Validators
|
||||||
|
must be opaque public tokens, not confidential content. Engine/HTTP failures use
|
||||||
|
bounded generic diagnostics; arbitrary trusted implementation metadata/logging
|
||||||
|
cannot be sanitized automatically. Do not log credentials or sensitive panic
|
||||||
|
payloads from extensions.
|
||||||
|
|
||||||
## Security model
|
## Security model
|
||||||
|
|
||||||
- The effective root is opened once as a directory capability. Root path
|
- The effective root is opened once as a directory capability. Root path
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user