Skip to content

v0.6.0

Latest

Choose a tag to compare

@AndersCan AndersCan released this 28 Oct 12:13
· 1 commit to main since this release
968e811

Security

  • Per-port session cookie: the session cookie is now bare_session_<port>
    (was bare_session). Cookies are scoped to the host, not the port, so two
    loopback servers sharing one cookie store overwrote each other's session.

  • No session token in handoff.json: the file now holds
    { origin, port, bootstrap }. A host that read token from it gets it from
    runtime.start() inside the worklet instead.

  • Gated directory mounts are sandboxed: files served from a mountDir
    without { public: true } now carry the same sandbox Content-Security-Policy
    as file mounts. An HTML or SVG file in a mounted folder (for example one a
    peer synced) could previously run script in the app origin and drive every
    plugin. Breaking for a consumer that served app code from a gated mount:
    move it to the public mount.

  • No unauthenticated non-loopback bind: createLoopbackServer (and so
    createWorkletRuntime, CLI host=) throws when host is not a loopback
    address and auth is off. The Host/Origin rule only stops browsers, so
    mode=dev host=0.0.0.0 exposed every plugin to the network.

  • Host calls use their own request ids: the host IPC bridge no longer
    reuses the page-supplied requestId on the host channel, so a page cannot
    supersede or cross-wire other pending host calls.

  • vendor.media validates kind: values other than image/video are
    refused before reaching the host or the mount path.

  • Bounded log ingest: POST /logs truncates messages to 8 KiB and tags to
    64 chars, and GET /logs (text) escapes line breaks so a page entry cannot
    pose as a backend line.

  • Android navigation confinement (BareWebViewClient): new
    allowedOrigin; main-frame navigations off the loopback origin are cancelled
    and http(s) links open in the system browser, matching iOS. The reference app
    sets it and disables WebView file and content access.

  • Dev harness: the supervisor no longer sends Access-Control-Allow-Origin: *
    and refuses requests with an Origin or a foreign Host, so a website can no
    longer read instance credentials from it.

  • Supply chain: the release job installs dependencies with
    --ignore-scripts (esbuild rebuilt explicitly) while it holds the npm OIDC
    token, and fetch-prebuilds refuses to unpack without a real lockfile hash.

  • Security model: docs/security.md (threat model and review checklist),
    SECURITY.md, the security-review agent skill, and security.test.ts
    guarding greppable invariants.

  • Origin rule in every mode (core/server/loopback-gate.ts): with auth
    off (the dev backend), the loopback server no longer accepts any Host or
    page Origin. A website open in the same browser could previously open the
    dev protocol socket (WebSocket upgrades bypass CORS) and invoke every plugin.
    Cross-origin dev pages must be listed in allowedOrigins (CLI origin=).

  • Secure defaults (@ekrooh/bare/runtime): createWorkletRuntime
    defaults to device mode (auth on, ephemeral port) whether or not storage is
    set, and resolveCliConfig keeps those defaults unless mode=dev is passed
    (auth=off without mode=dev is ignored with a warning).
    The desktop sidecar forces auth on. A broken host configuration no longer
    falls back to an unauthenticated dev server.

  • Nonce-only login: POST /login no longer accepts the raw session token,
    and the web transport no longer reads window.__ekrooh.token.

  • Path containment: path segments are percent-decoded and rejected (400)
    when they decode to ., .., a separator or NUL; a directory mount never
    serves a file whose real path (symlinks resolved) is outside its root.

  • No error detail in responses: a throwing route or handler answers a bare
    500 Internal Server Error; the cause goes to the log.

  • Tighter headers: the session cookie is SameSite=Strict; HTML
    connect-src names the one protocol socket instead of any ws: host, and
    the policy adds object-src 'none', base-uri 'none', form-action 'self'
    and frame-ancestors 'none'. Mounted session files (media) are served with a
    sandboxing CSP so a hostile file opened as a document cannot run script at
    the app origin. Every file response sends X-Content-Type-Options: nosniff.

  • Protocol socket: an error on the WebSocket (a peer reset) is contained
    even when no connection handler listens, instead of crashing the worklet;
    inbound messages are capped at the protocol's 16 MiB frame size.

  • Log ingest: POST /logs records every entry as source: 'web' and
    validates level/tag; a non-object body or a malformed GET /logs query
    is a 400, and only an oversized body is reported as 413.

  • Mount paths are normalized like request paths (/media/a%20b/ and
    /media/a b name one mount); a mount path no request could match throws.

Changed

  • BREAKING: dev mode is opt-in (deviceMode: false or mode=dev); see
    ADR 0006. Dev runners pass
    mode=dev and the Vite dev origin; the example Vite servers are pinned to
    port 5173.
  • BREAKING: getIPC() returns null when there is no host (it returned
    undefined typed as the channel).
  • BREAKING: handoff.json no longer carries token, and the session
    cookie is named bare_session_<port> (see Security).
  • A protocol socket upgrade inside the replacement interval
    (wsReplaceMinIntervalMs) is held until the interval passes instead of being
    dropped, so a quick reconnect no longer costs the page one of its 5 retries.
    A refused upgrade is answered with its HTTP status (401, 403, or 429 for a
    held upgrade superseded by a newer one) instead of a bare connection close.
  • The loopback server is split into the loopback gate (admission), the
    protocol socket slot (core/server/protocol-socket.ts) and the HTTP serving
    that remains in static-file-server.ts. The host IPC and the WebSocket share
    one frame channel (core/messages/frame-channel.ts) for reassembly, routing
    and malformed-frame policy.
  • createWorkletRuntime builds its protocol, registry and router through
    createBareRuntimeContext, which now allocates one text encoder/decoder per
    context instead of one per frame.

Added

  • allowedOrigins on LoopbackServerOptions and WorkletRuntimeOptions, and
    the mode= and repeatable origin= CLI tokens.

Removed

  • The unused mantaq messenger prototype (rpc-messenger.mantaq.ts,
    pending-call.ts) and the @mantaq/sugar dependency.