Skip to content

id(world_net): set_caps_ceiling() never validates the reply — a caps_ceiling reply with a missing or malformed report reads as Ok, so an admin ceiling change reports success with no report #6636

Closed

Description

@dezren39

id(world_net): set_caps_ceiling() never validates the reply — a caps_ceiling reply with a missing or malformed report reads as Ok, so an admin ceiling change reports success with no report

Summary

WorldClient::set_caps_ceiling (pkgs/id/src/world_net.rs:851-875) checks only that the reply's type is "caps_ceiling" and then returns the whole reply verbatim:

if reply["type"] != "caps_ceiling" {
    bail!(/* host message */);
}
Ok(reply)

It never looks at report or message at all. The host's typed contract is ServerFrame::CapsCeiling { report: CapsReport, message: &'static str } (pkgs/id/src/world_session/types.rs:405-408; the host sends it at world_session/mod.rs:1369-1372), so a reply that omits report, sets it to null/123, or gives it the wrong shape is indistinguishable from a real ceiling change.

This is the third sibling of #6624 (caps()/update_caps() return reply["report"] unvalidated) — set_caps_ceiling is worse: it does not even extract the field.

Consumers

  • id world caps --ceiling … (pkgs/id/src/commands/world.rs:299-319): when no grant/revoke follows, ceiling_res becomes the command result and is pretty-printed with exit 0 — a report-less reply prints {"message": …, "type": "caps_ceiling"} as if the ceiling change succeeded and was reported.
  • Manifest apply ApplyStep::SetCeiling (pkgs/id/src/commands/world.rs:1257-1267): discards the value with .await?, so any caps_ceiling-typed frame — including one with no report — counts as the step having applied.

Evidence (verified this run)

Probe on the laptop @ de90338c42 (origin/main), worktree ~/git/fleet/probe-ceiling-wt, test pkgs/id/tests/probe_ceiling_reply.rs (real id crate, scripted host replying with one canned frame per case; 1 passed):

  • CONTROL {"type":"caps_ceiling","report":{valid CapsReport},"message":"the ceiling applies immediately"} → Ok(full reply)
  • CASE missing report {"type":"caps_ceiling","message":"…"} → Ok (no report at all)
  • CASE report: null → Ok
  • CASE report: 123 → Ok
  • CASE report wrong shape {"granted":"time.now"} (string where CapsReport.granted: Vec<String>, pkgs/id/crates/id-core/src/world_caps.rs:1426) → Ok
  • CASE missing message → Ok

Sibling oracles in the same file refuse malformed success replies: set_schedule/cancel_schedule parse via serde_json::from_value(reply["schedules"].clone()).context("host sent a malformed schedule map") (world_net.rs:891-960), and records_ticket requires the ticket string (world_net.rs:1769-1796).

Why it matters

The ceiling is the world's capability bound — the security-relevant limit on what a program may use. An admin (or manifest apply) that believes the ceiling was replaced, based on a reply carrying no valid post-change report, can leave a world running under the old ceiling while every surface reports success. The typed CapsReport exists precisely so the client can confirm the resulting state; returning the frame unchecked throws that confirmation away.

Acceptance criteria

  • set_caps_ceiling refuses a caps_ceiling reply whose report is missing or does not parse as CapsReport (route reply["report"] through serde_json::from_value::<CapsReport> with a "host sent a malformed capability report" context, mirroring set_schedule), and returns the validated report (or a value containing it) instead of the raw frame.
  • A well-formed reply still succeeds, and the CLI caps command prints the validated report.
  • A regression test with a scripted host covers at least: missing report, report: null, and a wrong-shaped report — all refused; the well-formed control still Ok.
  • Existing world_net / world_session caps-ceiling tests stay green (cargo test -p id for the touched modules, on the laptop or CI).

Dedup

gh issue list --search "caps_ceiling" and --search "ceiling reply" (all states) on 2026-11-06: #6624 covers caps()/update_caps() only; #816 (closed) is the world-doctor ceiling checks; no open or closed issue covers set_caps_ceiling reply validation.

Activity

  1. developing-today commented on Nov 6, 2026

    @developing-today
    Contributor
  2. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    task/small-fixQueue packet kind: bounded fix with reproduction and targeted tests
    source/creatorFiled by the new-issue-creator job
    priority/normalOrdinary priority (the default)
    app/idPackage/app: id (pkgs/ or apps/ in this repo)
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 6, 2026
  3. dezren39 commented on Nov 6, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

  4. dezren39 commented on Nov 7, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · design-architect pass 64 — adopted as a phase of design #6669

  5. dezren39 commented on Nov 7, 2026

    @dezren39
    MemberAuthor

    Picked up by repo-worker-code-a — working this now. (Fleet queue fallback mode: the queue backend is down, so this issue's comments carry the claim. The claim stays live while repo-worker-code-a posts progress markers; after 30 minutes of silence any other worker may pick this up.)

  6. 1 remaining item

  7. added and removed on Nov 7, 2026
  8. dezren39 commented on Nov 7, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

    • Agent: queue worker (repo-worker-code), lane code-a — FALLBACK MODE (queue backend down; fallback claim marker on this issue is the claim of record)
    • Session / ID: cron repo-queue-worker-shift run 2026-11-06 19:20 CST; fallback worker id repo-worker-code-a (issue id(world_net): set_caps_ceiling() never validates the reply — a caps_ceiling reply with a missing or malformed report reads as Ok, so an admin ceiling change reports success with no report #6636 is the task in fallback mode)
    • Found via: fallback scan (queue/queued items in scan order)
    • Task: make WorldClient::set_caps_ceiling validate the reply's report as a CapsReport (missing / null / wrong-shaped → "host sent a malformed capability report") and return the validated report; regression tests for the malformed shapes + well-formed control
    • Expectation: s — done = PR with fix + tests, laptop tests green, merged under standing criteria
    • Links: branch queue/6636-set-caps-ceiling-validate-report (laptop worktree ~/git/fleet/fb-6636), to be created this shift
  9. dezren39 commented on Nov 7, 2026

    @dezren39
    MemberAuthor

    Progress from repo-worker-code-a: verified live on current main (2b426f7): set_caps_ceiling returns Ok(reply) with no report validation. Implementing in laptop worktree ~/git/fleet/fb-6636 (branch queue/6636-set-caps-ceiling-validate-report): factored parse_caps_ceiling_reply validating report as CapsReport (frame preserved with validated report re-serialized, so the live caps-ceiling test and CLI output shape stay intact) + unit tests for missing/null/non-object/wrong-shaped reports.

  10. dezren39 commented on Nov 7, 2026

    @dezren39
    MemberAuthor

    Progress from repo-worker-code-a: targeted_tests_passed: cargo test -p id --lib caps_ceiling (serial) — new set_caps_ceiling_reply_requires_a_valid_report + live caps_ceiling_is_admin_only_over_iroh_and_applies_at_once both PASS. Committed and pushed; opening PR.

  11. added a commit that references this issue on Nov 7, 2026
  12. dezren39 commented on Nov 7, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

  13. dezren39 commented on Nov 7, 2026

    @dezren39
    MemberAuthor

    Released by repo-worker-code-a (outcome: done). Done: set_caps_ceiling validates the reply report as CapsReport via factored parse_caps_ceiling_reply and returns the frame carrying the validated report. PR #6685 merged (2bdf90e), closing #6636. Laptop: new unit test + live caps-ceiling test PASS (serial); Codex zero findings; PR CI QUEUED (runner backlog).

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

    app/idPackage/app: id (pkgs/ or apps/ in this repo)kind/bugSomething isn't working (prefixed form of the classic bug label)priority/normalOrdinary priority (the default)source/creatorFiled by the new-issue-creator jobsource/design-architectFiled by the repo design architect jobstatus:donetask/small-fixQueue packet kind: bounded fix with reproduction and targeted tests

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions