Skip to content

Custom-scheme privilege matrix and CSP profile prototype (P2): can a renderer turn a resource locator into filesystem authority, and does the host CSP admit draw.io? #415

Description

@0monish

Generated by an AI agent (Claude Code, Fable 5.1) on behalf of @0monish during Wayfinder charting of the Electron compatibility program, 2026-10-06. Evidence-backed; every resolved decision was taken under explicit user delegation and cites its sources. Planning only — no implementation is authorized by this issue.

Parent map: #391 · Unit: X04 (security-mapping) · Wayfinder type: prototype · Status: open (not resolvable from current evidence)

Tracker of record: this GitHub issue (decision ticket; claim by self-assignment). Linear reference: KEL-79 (Backlog) carries a link here.

Question

Build the falsifiable matrix: privilege declaration timing, standard/secure/CSP/CORS/fetch/service-worker combinations, malformed and encoded traversal URLs, and the CSP profile with draw.io's exact directives; also the missing spec for user-granted persisted filesystem roots under a frozen manifest (KEL-79/KEL-130 gap).

What is known / why it is still open

No evidence-backed resolution yet; see the unit research note for the proposed answer and refuter verdicts.

Context

Epic #504 · research note wayfinder/electron-compat/notes/X04.md on the research branch.

Activity

  1. added theissue type on Oct 6, 2026
  2. 0monish commented on Oct 6, 2026

    @0monish
    MemberAuthor

    Generated by an AI agent (Claude Code, Fable 5.1) on behalf of @0monish, 2026-10-06, under the map's execution doctrine. Statements are labelled FACT / INFERENCE / UNKNOWN by their author; nothing here authorizes implementation.

    Decision packet

    Decision: Which host mechanism loads draw.io's multi-file renderer on WKWebView with a content security policy at least as strict as draw.io's own and with reads confined to the app code directory; and, separately, how custom-scheme privileges and user-granted persisted filesystem roots are owned.

    Classification: needs-experiment · Milestone (YAGNI test against the first proof): first-proof · Reversible: Yes. The probe writes only scratch files and a result note; the mechanism is fixed later in the F05-T4 (#473) spec. · Owner: KEL-79 (Backlog, unassigned, no spec) is the named resource-transport owner and F05-T4 (#473) (#473) the map ticket; KEL-102 (In Progress) owns guard wiring for any broker; KEL-130 (Done) owns retained filesystem scopes as landed. Linear, fetched 2026-10-06.

    Facts

    Inferences

    • WKWebView offers no response-header hook for file-URL loads, so a host policy for a file-loaded app can arrive only as an injected meta policy or by serving the files through a host-owned scheme handler that sets the header. From platform knowledge, not receipted in this run.
    • Custom-scheme privilege mapping (Zettlr safe-file) and persisted user-granted roots are not needed by draw.io under the legacy profile: draw.io registers no scheme, and legacy keeps ambient filesystem access in the Bun main.
    • Earlier refuter finding that KEL-130 scopes are build-time literals bound to one snapshot with no runtime insertion is taken from the spec reading by that refuter; I confirmed only that KEL-130 is Done.

    Unknowns

    • Whether draw.io's renderer boots and reaches an editable canvas in WKWebView from a file-URL load at all.
    • Whether 'self' in a policy matches sibling file URLs for a file-loaded document in WebKit, and whether WebKit honours 'wasm-unsafe-eval' for the inlined router.
    • Whether a meta policy inserted by a document-start user script is enforced before the page's first script and subresource loads.
    • Whether a host scheme handler changes the renderer's origin-dependent behavior (storage, workers, relative URLs).
    • Electron's failure mode when registerSchemesAsPrivileged is called after ready (not quoted in the pinned docs, per the research note).
    • Who owns user-granted, persisted filesystem roots under a frozen manifest; no spec exists.

    Alternatives

    Option Cost New invariant created Existing invariant at risk
    Run the three-arm WKWebView probe now, then write the F05-T4 (#473) spec from its result (recommended) About one day in a scratch harness on macOS; needs the drawio submodule at the gitlink pinned by drawio-desktop@2edf9fb. None yet; the probe selects the mechanism the spec will state. None; no Keld code or contract changes.
    Specify file-URL load plus injected meta policy without measuring Risk of a spec that the engine does not enforce; the PANEL-D17 (#437) negative control would then fail late. Host policy is delivered by script injection. Proof rule: a policy that is not shown to be enforced is not a control; the webRequest no-op would silently drop draw.io's own protection.
    Specify a host-owned resource scheme for all app content now Pulls a slice of KEL-79 (Backlog, three-engine contract, no spec) into the first proof. App bytes reach the webview only through the host resource adapter. Sequencing; KEL-79 is scoped as a three-engine contract, so a macOS-only slice needs an explicit owner decision.
    Accept draw.io's listeners as no-ops with no host policy for the first proof None to build. None. Violates resolved map decision PANEL-D17 (#437) and always-on webview hardening in architecture 03 section 4.

    Recommendation

    Split the ticket into three atoms and act only on the first. (1) First proof: run the probe in the node below to choose between injected meta policy over a contained file-URL load and a host scheme handler with a header policy, and to list the exact directives the draw.io profile must admit. (2) Next: the custom-scheme privilege and traversal matrix belongs to F05-T2 (#471) with Zettlr as the demand. (3) Parked until a strict-profile app spec: persisted user-granted roots need one owner as a KEL-130 follow-on; cross-reference X04-D5 so one spec owns both.

    Falsifier: If arm A (file-URL load, renderer's own meta policy only) already refuses outside-directory reads and blocks an injected inline script, the host adds nothing and the mechanism question is moot. If no arm reaches an editable canvas, the first proof is blocked on the engine and the map's first-proof choice must be revisited.

    Missing evidence: The probe result: per arm, boot outcome, violation events, containment refusals and negative-control outcomes; the renderer's own meta policy text from the checked-out submodule; an Electron 44.4.5 oracle run of the same steps showing zero policy violations.

    Next action: Orchestrator files the node below in the Prompt Tracker and runs it on a macOS machine; first completion check is a result table with one row per arm (A, B, C) and each negative control marked fired or not fired, attached to #415.

    Copy-ready Prompt Tracker node draft (to be filed in the tracker's existing taxonomy by its single writer)
    Proposed path: prompts/NEW/webview/05-electron-compat-drawio-csp-transport-probe.md
    
    MODEL: GPT-5.6 Sol / Codex (incumbent admitted route; gpt-6.1-sol only after task-specific qualification per docs/04 refresh 2026-10-06)
    EFFORT: high
    HARNESS: direct (one session, no subagents)
    GRAPH_NODE: new-webview-05-drawio-csp-transport-probe
    GRAPH_ROLE: inspect
    SESSION: NEW
    OS: macOS (real machine; WKWebView from the installed system; record OS build and WebKit version from the machine)
    LINEAR: KEL-79 (read only; no claim, no status change) / GitHub gyldlab/keld#415 (X04-D3 (#415)), feeds #473 (F05-T4 (#473)) and #437 (PANEL-D17 (#437))
    PIN: refetch Keld origin/main, KEL-79 and KEL-102 state, and issues #415/#473 before starting; stop on any change that already answers the question
    PINS: keld=b4b907c3; drawio-desktop=2edf9fb with its drawio submodule at the recorded gitlink; electron=v44.4.5
    SOURCE: prompts/NEW/webview/05-electron-compat-drawio-csp-transport-probe.md
    READS: Keld architecture 03 sections 2 and 4, architecture 05 resource-path contract, KEL-96 boot spec; drawio-desktop main source (policy header and file-URL filter); pinned Electron docs web-request and protocol pages
    WRITES: a scratch harness directory outside the Keld repo and one result note for the research branch; no Keld, GitHub or Linear writes
    SINGLE_WRITER_KEYS: research-note:x04-d3-csp-transport-probe
    REQUIRES_ARTIFACTS: none
    PRODUCES_ARTIFACTS: keld.execution-artifact/v1 with node_id=new-webview-05-drawio-csp-transport-probe, the per-arm result table, raw violation logs, harness source hash
    AFTER: none | independent
    NEXT: F05-T4 (#473) spec node (not yet filed)
    DO_NOT_RUN_WITH: none
    LANE: research
    
    Role: Prototype inspector. Builds a minimal WKWebView harness that uses no Keld code. Does not design the Keld resource transport, does not write a spec, does not touch custom-scheme privileges for Zettlr or filesystem-root persistence.
    
    Goal: One result table that says, for draw.io's renderer on WKWebView, which delivery mechanism both boots the editor and enforces a host policy equal to draw.io's header policy with reads confined to the app code directory.
    
    Success criteria (all real-OS, none CI-only):
    - Oracle arm O: unmodified drawio-desktop on Electron 44.4.5 reaches an editable canvas, inserts one shape, initialises the WASM edge router; record the count of securitypolicyviolation events (expected zero) and the renderer's own meta policy text.
    - Arm A: file-URL load with read access limited to the code directory, no host policy. Record boot outcome and violations.
    - Arm B: arm A plus a document-start user script that inserts a meta policy equal to draw.io's header string.
    - Arm C: a host scheme handler serving the code directory with that string as a response header.
    - For each of A, B, C record: editable canvas reached (yes/no), shape insert (yes/no), WASM router initialised (yes/no), every violation event with directive and blocked URI.
    - Negative controls, each must be reported as fired or not fired per arm: (1) removing 'wasm-unsafe-eval' makes the router compile raise a violation; (2) an inline script element injected after load is blocked; (3) fetch and image loads of a file outside the code directory are refused before bytes arrive; (4) in arm C, dot-dot, percent-encoded dot-dot and encoded-slash paths are refused by the handler.
    - A control that does not fire in an arm disqualifies that arm as a host control.
    
    Prerequisites: macOS machine with Xcode command-line tools; drawio submodule checked out at the pinned gitlink; Electron 44.4.5 available for arm O. No approvals needed; no repository writes.
    
    Resources: scratch directory only. The harness may be Swift or Rust with objc2; record its source hash.
    
    Delegation: none.
    
    Verification: run each arm three times from a fresh process; results must agree. Collect violations through a page-side securitypolicyviolation listener forwarded by a script message handler, not by reading console text. No sleeps: wait on the editor-ready signal and on explicit message receipt, with a stated deadline that reports timeout as failure.
    
    Output: the result table, the raw logs, OS build and WebKit version, harness hash, and one paragraph naming which arm F05-T4 (#473) should specify or naming the blocker. Label every statement FACT, INFERENCE or UNKNOWN.
    
    Stop rules: stop and report if arm O itself shows violations (the oracle premise is wrong); if no arm reaches an editable canvas after two distinct legitimate attempts, report the kill condition with the failing directive or load error and do not add permissive workarounds; stop if Keld main has landed a file read root or policy injection since the pin; never weaken a negative control to obtain a pass; no Windows or Linux claims.
    
  3. added
    milestone:first-proofNeeded for the first migration proof (drawio-desktop on macOS, explicit legacy profile)
    on Oct 6, 2026
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

    electron-compatElectron compatibility program areamilestone:first-proofNeeded for the first migration proof (drawio-desktop on macOS, explicit legacy profile)wayfinder:prototypeWayfinder prototype decision

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions