Skip to content

Explicit permissions object replaces the secure default instead of merging over it (drops binding auto-grant) #1960

Description

@defiufo

Split out from #1884 so that #1958 (which fixes the other half of that report — network rule patterns never matching the URI-formatted resource) can close cleanly without silently closing this one.

Problem

The permissions docs state that a partial policy is merged over a secure default:

Your policy is merged over this baseline. Omitted scopes keep their default; they are not denied. So { network: "allow" } grants the network while keeping the execution essentials.

docs/content/docs/permissions.mdx:32

The implementation does a wholesale replacement instead:

// packages/core/src/agent-os.ts:3276
const hostPermissions = options?.permissions ?? {
    ...allowAll,
    binding: "allow",
};

The ?? only supplies the default when permissions is entirely absent. Any explicit object replaces it in full.

Consequences

  1. Omitted scopes are denied, not defaulted. { network: "allow" } leaves fs, childProcess, process, and env undefined. Whatever the sidecar treats as the missing-scope default applies — not the documented baseline — so the documented one-liner for "grant the network, leave everything else alone" silently removes the execution essentials.

  2. The binding auto-grant is lost. The docs say binding is auto-granted when bindings are registered. Because the binding: "allow" in the fallback lives inside the branch that only runs when no policy is passed, registering bindings alongside any explicit policy leaves binding undefined → denied.

  3. It masks unrelated bugs. Verifying fix(permissions): match network rules against the resource host #1958's fix end-to-end requires spelling out all six scopes in the repro, otherwise { network: { default: "deny", rules: [...] } } alone also denies fs/process and the failure looks like a network-policy bug. The original reporter in Network permission rule sets never match; explicit permission objects also bypass the documented merge-over-default #1884 hit exactly this.

Reproduction

import { AgentOs } from '@rivet-dev/agentos';

// Documented as: grant network, keep execution essentials.
const vm = await AgentOs.create({ permissions: { network: 'allow' } });
await vm.filesystem.writeFile('/tmp/t.js', 'console.log("hi")');
// fs/process operations do not behave as the documented baseline implies

Reported against 0.2.15 and 0.2.16-rc.1 in #1884.

Suggested fix

Merge scope-by-scope over the baseline rather than replacing:

const hostPermissions = {
    ...allowAll,
    binding: "allow",
    ...(options?.permissions ?? {}),
};

…with the binding auto-grant applied whenever bindings are registered, independent of whether an explicit policy was supplied. Either that, or update permissions.mdx to document replacement semantics — but the merge behavior is the one the docs promise and the safer default, since the failure mode of replacement is silent over-denial.

Worth a test asserting that { network: "allow" } leaves fs at its documented baseline, and that registering a binding with an explicit policy still grants binding.

/cc @Scorpion197 — this is the half of #1884 your PR intentionally doesn't cover.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions