BLOCKER: require account authority for public Calendar feed grants #743

Open
opened 2026-10-02 13:08:11 +00:00 by kayg · 2 comments
Owner

F2 — BLOCKER: data authority can manage public Calendar feed links

crates/plugins/calendar/src/feeds/publication.rs:1483 resolves only the
authenticated User ID. The Calendar manifest supplies the outer data guard.
create_feed (line 315) creates a token and persists its hash in Security
state. rotate_feed_link (line 453) returns a new public capability. Feed
update and revoke use the same principal helper. None requires account
authority. These operations are data mutations in the registry.

DESIGN §27 denies share/link management to autonomous Agent container tokens.
A Calendar feed URL grants public read access to the selected Journal, Tasks
or Calendar data. The current guard permits the Agent to change this grant.
The principal helper is unchanged in merge-round-7a.

Fix: use account authority for publication and capability management, before
Home writes or token changes. Keep subscription reads separate from public
grant management. Update registry policy. Regression checks: data-only and
read-only callers cannot create, rotate, change or revoke a public grant;
failed requests leave definitions and token hashes unchanged; another User
cannot manage the owner's feed; the owning account session still works.

Audit context: sec-mcp-scopes job, base origin/dev c4a61e8cf0; merge-round-7a 2f4482ded also reviewed. Source review only. No product change or exploit reproduction. DESIGN §§21, 27, 41 and 55. Duplicate search: scope, MCP, injection, Trash and feed issue titles; existing #328/#329/#431 cover features, not these specific missing guards.

F2 — BLOCKER: data authority can manage public Calendar feed links `crates/plugins/calendar/src/feeds/publication.rs:1483` resolves only the authenticated User ID. The Calendar manifest supplies the outer data guard. `create_feed` (line 315) creates a token and persists its hash in Security state. `rotate_feed_link` (line 453) returns a new public capability. Feed update and revoke use the same principal helper. None requires account authority. These operations are data mutations in the registry. DESIGN §27 denies share/link management to autonomous Agent container tokens. A Calendar feed URL grants public read access to the selected Journal, Tasks or Calendar data. The current guard permits the Agent to change this grant. The principal helper is unchanged in merge-round-7a. Fix: use account authority for publication and capability management, before Home writes or token changes. Keep subscription reads separate from public grant management. Update registry policy. Regression checks: data-only and read-only callers cannot create, rotate, change or revoke a public grant; failed requests leave definitions and token hashes unchanged; another User cannot manage the owner's feed; the owning account session still works. Audit context: sec-mcp-scopes job, base origin/dev c4a61e8cf090170f35b1bed3350d9de20c83ecd5; merge-round-7a 2f4482ded also reviewed. Source review only. No product change or exploit reproduction. DESIGN §§21, 27, 41 and 55. Duplicate search: scope, MCP, injection, Trash and feed issue titles; existing #328/#329/#431 cover features, not these specific missing guards.
Author
Owner

The shared feed management principal currently accepts only a User ID. All six feed management handlers call it before persistence. The fix adds account and data authority to this principal, retains the User resource boundary, and declares the same scopes in the registry. Five grant mutations also require recent auth at the server route boundary; listing does not. Subscription routes keep their existing data guard. Web writes reuse withStepUp so a stale account session can complete the passkey check and retry the same action.

The shared feed management principal currently accepts only a User ID. All six feed management handlers call it before persistence. The fix adds account and data authority to this principal, retains the User resource boundary, and declares the same scopes in the registry. Five grant mutations also require recent auth at the server route boundary; listing does not. Subscription routes keep their existing data guard. Web writes reuse `withStepUp` so a stale account session can complete the passkey check and retry the same action.
Author
Owner

Implemented scopefix; validation incomplete at the 3-hour limit. Not ready for merge.

Branch: job/scopefix. Base: 2f4482ded0. Head: 1f1ba91cc3.
The required origin/dev and origin/job/merge-round-7a merges both returned Already up to date. No push or deploy was performed.

Built:

  • Files checks owning account and data authority before the mutation lock, permanent Trash removal, Index changes or publication. A regression test checks unchanged Trash and Index after denial and owning account success.
  • Calendar public feed management checks account/data authority and the existing data_user resource boundary before writes. Subscriptions retain data authority.
  • Registry scope policy corrects the 26 audited mismatches plus set_user_plugin and the seven Trash/Calendar actions. The server applies the reviewed requirements before body extraction. MCP discovery filters declared scopes. Tests audit detected data/account/admin guards across declared handlers and exercise every eligible action through the registry middleware.
  • Generated and legacy MCP API results share an untrusted-data/provenance envelope; errors, server instructions, llms.txt and the public Skill carry the trust boundary. Legacy Log selection preserves the envelope after a successful write. Inert Note/Mail/search fixtures also check reserved payload fields.
  • Web Trash and Calendar grant writes use the existing passkey step-up helper.
  • A focused cross-User/admin/MCP/API/CLI probe, serial/burst bench profile and twelve production screenshot captures with macOS rendering are prepared.

UX gaps closed: stale account sessions retry through the shared passkey flow; cancelling the Trash check says Nothing changed. UX gaps left: real-server pointer/touch/keyboard walkthrough and all screenshots remain unverified.

Decisions: only reviewed declarations opt into central enforcement, to preserve existing public capability flows (including download_app_password_profile) and legacy route guards. Permanent removal and Calendar grant mutations require recent assertions; grant reads require account/data without an assertion. MCP payloads move under data with fixed trust/source fields; these identify API provenance, not content authorship, and do not guarantee prompt-injection prevention.

Verified gate output (verbatim excerpts):

registry-test.log

..............
----------------------------------------------------------------------
Ran 14 tests in 2.631s

OK

calternal-api-clippy.log

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 4m 51s

calternal-api-test.log

    Finished `test` profile [unoptimized + debuginfo] target(s) in 3m 12s
test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.31s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

calternal-plugin-files-clippy.log

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 40m 00s

web-check-final.log

svelte-check found 0 errors and 0 warnings

web-test-head.log

 Test Files  154 passed (154)
      Tests  1073 passed (1073)
   Duration  664.68s (transform 31%, environment 28%, import 21%, tests 14%, setup 6%)

web-build-final.log

✓ built in 3m 13s
✓ built in 245ms
✓ built in 9m 2s
  Wrote site to "build"
  ✔ done

Exit statuses confirmed: cargo fmt --check 0 (no output); calternal-api clippy/test 0; calternal-plugin-files clippy 0; web check/test/build 0. Registry has 340 operations and 322 generated tools; all 14 registry tests pass. Python syntax, shell syntax, screenshot-script syntax and git diff --check pass.

Known gaps: Files tests were stopped during compilation; Calendar/server clippy and tests did not run to completion. The local server/CLI binary was not built, so no real-server round, MCP probe, bench measurements or screenshots ran. No measured performance regression claim is made; the profile includes notes.list baseline reference p50 1.3ms/p95 3.1ms, with no equivalent account-denial/envelope baseline. No screenshots exist to attach.

Build evidence: the first dependency build spent over an hour through the shared sccache. Active clients waited 81–145 seconds. Restarting this job without RUSTC_WRAPPER produced API gates and Files clippy; the latter finished in 40m 00s. The first default web test run had one 5000ms KeyboardShortcutsCard timeout; full runs with two workers and 30000ms timeouts passed. No existing assertion or fixture was changed to hide a failure. Remaining Rust checks must run before merge.

Source comments were re-read. No migrations or dependency versions were changed. Build output is being cleaned with cargo clean; web build output and Python caches were removed. Review artifacts remain gitignored.

Files:

  • apps/web/src/lib/files/api.ts
  • apps/web/src/lib/webmcp/tools.ts
  • apps/web/src/routes/settings/apps/CalendarFeedsGroup.svelte
  • bench/scopefix-739.py
  • contracts/action-authority.json
  • contracts/actions.json
  • crates/calternal-api/src/actions.rs
  • crates/calternal-server/src/agent_docs.rs
  • crates/calternal-server/src/agent_docs/skill_intro.md
  • crates/calternal-server/src/authz.rs
  • crates/calternal-server/src/mcp.rs
  • crates/calternal-server/src/wire.rs
  • crates/plugins/calendar/src/feeds/publication.rs
  • crates/plugins/files/src/lib.rs
  • docs/action-registry.md
  • docs/mcp.md
  • scripts/action_registry.py
  • scripts/test_action_registry.py
  • tests/adversarial/mcp_probe.py
  • tests/adversarial/run.sh
  • tests/adversarial/scopefix_review.mjs
Implemented scopefix; validation incomplete at the 3-hour limit. Not ready for merge. Branch: job/scopefix. Base: 2f4482ded066d9c5d9c59130377907f7fd2916c9. Head: 1f1ba91cc39ac1b8abcdec8c266919c5e3691302. The required origin/dev and origin/job/merge-round-7a merges both returned Already up to date. No push or deploy was performed. Built: - Files checks owning account and data authority before the mutation lock, permanent Trash removal, Index changes or publication. A regression test checks unchanged Trash and Index after denial and owning account success. - Calendar public feed management checks account/data authority and the existing data_user resource boundary before writes. Subscriptions retain data authority. - Registry scope policy corrects the 26 audited mismatches plus set_user_plugin and the seven Trash/Calendar actions. The server applies the reviewed requirements before body extraction. MCP discovery filters declared scopes. Tests audit detected data/account/admin guards across declared handlers and exercise every eligible action through the registry middleware. - Generated and legacy MCP API results share an untrusted-data/provenance envelope; errors, server instructions, llms.txt and the public Skill carry the trust boundary. Legacy Log selection preserves the envelope after a successful write. Inert Note/Mail/search fixtures also check reserved payload fields. - Web Trash and Calendar grant writes use the existing passkey step-up helper. - A focused cross-User/admin/MCP/API/CLI probe, serial/burst bench profile and twelve production screenshot captures with macOS rendering are prepared. UX gaps closed: stale account sessions retry through the shared passkey flow; cancelling the Trash check says Nothing changed. UX gaps left: real-server pointer/touch/keyboard walkthrough and all screenshots remain unverified. Decisions: only reviewed declarations opt into central enforcement, to preserve existing public capability flows (including download_app_password_profile) and legacy route guards. Permanent removal and Calendar grant mutations require recent assertions; grant reads require account/data without an assertion. MCP payloads move under data with fixed trust/source fields; these identify API provenance, not content authorship, and do not guarantee prompt-injection prevention. Verified gate output (verbatim excerpts): registry-test.log ```text .............. ---------------------------------------------------------------------- Ran 14 tests in 2.631s OK ``` calternal-api-clippy.log ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 4m 51s ``` calternal-api-test.log ```text Finished `test` profile [unoptimized + debuginfo] target(s) in 3m 12s test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.31s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` calternal-plugin-files-clippy.log ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 40m 00s ``` web-check-final.log ```text svelte-check found 0 errors and 0 warnings ``` web-test-head.log ```text Test Files 154 passed (154) Tests 1073 passed (1073) Duration 664.68s (transform 31%, environment 28%, import 21%, tests 14%, setup 6%) ``` web-build-final.log ```text ✓ built in 3m 13s ✓ built in 245ms ✓ built in 9m 2s Wrote site to "build" ✔ done ``` Exit statuses confirmed: cargo fmt --check 0 (no output); calternal-api clippy/test 0; calternal-plugin-files clippy 0; web check/test/build 0. Registry has 340 operations and 322 generated tools; all 14 registry tests pass. Python syntax, shell syntax, screenshot-script syntax and git diff --check pass. Known gaps: Files tests were stopped during compilation; Calendar/server clippy and tests did not run to completion. The local server/CLI binary was not built, so no real-server round, MCP probe, bench measurements or screenshots ran. No measured performance regression claim is made; the profile includes notes.list baseline reference p50 1.3ms/p95 3.1ms, with no equivalent account-denial/envelope baseline. No screenshots exist to attach. Build evidence: the first dependency build spent over an hour through the shared sccache. Active clients waited 81–145 seconds. Restarting this job without RUSTC_WRAPPER produced API gates and Files clippy; the latter finished in 40m 00s. The first default web test run had one 5000ms KeyboardShortcutsCard timeout; full runs with two workers and 30000ms timeouts passed. No existing assertion or fixture was changed to hide a failure. Remaining Rust checks must run before merge. Source comments were re-read. No migrations or dependency versions were changed. Build output is being cleaned with cargo clean; web build output and Python caches were removed. Review artifacts remain gitignored. Files: - `apps/web/src/lib/files/api.ts` - `apps/web/src/lib/webmcp/tools.ts` - `apps/web/src/routes/settings/apps/CalendarFeedsGroup.svelte` - `bench/scopefix-739.py` - `contracts/action-authority.json` - `contracts/actions.json` - `crates/calternal-api/src/actions.rs` - `crates/calternal-server/src/agent_docs.rs` - `crates/calternal-server/src/agent_docs/skill_intro.md` - `crates/calternal-server/src/authz.rs` - `crates/calternal-server/src/mcp.rs` - `crates/calternal-server/src/wire.rs` - `crates/plugins/calendar/src/feeds/publication.rs` - `crates/plugins/files/src/lib.rs` - `docs/action-registry.md` - `docs/mcp.md` - `scripts/action_registry.py` - `scripts/test_action_registry.py` - `tests/adversarial/mcp_probe.py` - `tests/adversarial/run.sh` - `tests/adversarial/scopefix_review.mjs`
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#743
No description provided.