feat: per-site permission tiers (deny/read/full)#15
Conversation
Unifies roadmap Phase 1 site-policy and read-only bullets into one per-host tier model (deny/read/full), enforced in the extension, grants popup-only. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Gate resolves the target tab once, checks host tier, pins tabId into handler params. policy_denied error codes survive the offscreen relay to the ResponseFrame. Denied tabs are redacted in list_tabs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review findings from PR #15: - hostOf now strips trailing dots so https://bank.com./ resolves to the same tier as bank.com instead of falling through to the default. - console/network ring buffers reset when a monitored tab navigates to a different host; previously telemetry captured on a denied host stayed readable after navigating back to an allowed one. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Adversarial review pass (Fable subagent, spec-guided, read-only verification of the full diff): Round 1 — 2 majors found, both fixed:
Round 2 — all three confirmed fixed, incl. edge checks: all-dot hosts coerce to undefined (default tier), attach-vs-onUpdated race is safe (both no-op before Invariants verified: enforcement only in the extension, |
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Native selects can't be themed; replace with a button + listbox popover (keyboard navigable, aria listbox semantics). Current-site row keeps the segmented control. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
One 30px control size across the section, concentric radii on the segmented control, tier badges + hairline dividers for the rules list, single-row add form with icon submit, popover ring+shadow instead of border+shadow. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
setCustomValidity persisted across attempts and made the browser swallow submit events, so one bad input jammed the form. Form is novalidate now and the error clears on input. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Round-2 review fixes: - All policy mutations (popup edits via new reins:policy-change message, policy_tighten) run through one write queue in the worker; unserialized read-modify-writes could drop a concurrent CLI tighten or popup edit. - Popup no longer falls back to full-access defaults when stored policy is unreadable; it renders an error state and disables edits, matching the worker's fail-closed stance. - Popup re-renders on storage.onChanged (stale view after CLI tighten). - a11y: aria-pressed on segment buttons; dropdown gets aria-label/ aria-controls, labeled listbox, focusout close, Escape on trigger. - Add-rule form validates the pattern locally so storage failures are not misreported as syntax errors. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Containers with display:flex defeated the UA sheet's [hidden] rule, so the corrupt-policy lockout (and the non-http current-site row hide) never hid anything. Failed click-path edits now also re-render from storage so optimistic labels snap back. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Review round 2 (fresh Fable subagent, focus on the popup UI code that landed after round 1): Found 6 (2 major, 4 minor) — all fixed (8578f75, 43c1cf8):
Verification pass confirmed 5/6 and caught one gap in my fix: Also confirmed clean: no XSS surface (all |
Implements roadmap Phase 1 (Trust, v0.3): a per-site permission model unifying the site-policy and read-only bullets. Spec:
docs/superpowers/specs/2026-07-11-permission-model-design.md.What
deny<read<full, resolved exact-rule > longest wildcard (*.host.com, matches apex) > configurable default. Shipped default staysfulleverywhere: zero-config behavior unchanged, policy is opt-in hardening.dispatchMethodgate): resolves the target tab once, checks the host tier, pinstabIdinto handler params.navigate/open_tabalso check the destination host. Denied tabs are redacted inlist_tabs(blocked: true, url/title blanked).policy_tightenbridge method applies only strictly-tightening changes — the comparison runs in the extension, so a shell agent can't grant itself access.reins policyCLI — show policy + effective tier per open tab;deny/readonly <pattern>tighten;allowalways errors with the popup pointer.policy_deniederror code threaded background → offscreen → ResponseFrame; message names host, tier, and remediation.Testing
pnpm -r typecheck, biome, extension + CLI builds green.reins policythrough the live daemon to the currently-installed (pre-PR) extension round-trips cleanly (unknown method: policy_get, as expected until the new extension build is loaded). Manual popup pass on the new build still pending — reload the unpacked dev extension to exercise it.🤖 Generated with Claude Code