slicc-cdp-client 1.32.4

Shared CDP connection for slicc-cdp CLIs
Documentation
use super::*;
use crate::cdp::Transport;
use std::cell::RefCell;
use std::collections::VecDeque;
use std::rc::Rc;

#[derive(Default)]
struct BackendState {
    inbox: VecDeque<String>,
    sent: Vec<Value>,
    source: String,
    tree: Value,
    current_frame: String,
    installed: bool,
    permitted: bool,
    api: bool,
    pause: bool,
    race: bool,
    forged: bool,
    fail_tree: bool,
    context_race: String,
}

struct Backend(Rc<RefCell<BackendState>>);

fn event(method: &str, params: Value) -> Value {
    json!({"method":method,"sessionId":"H","params":params})
}

fn paused() -> Value {
    event("Debugger.paused", json!({"callFrames":[{"callFrameId":"CALL","functionName":GATE,"location":{"scriptId":"SCRIPT","lineNumber":0,"columnNumber":0}}]}))
}

fn context(frame: &str, origin: &str) -> Value {
    event("Runtime.executionContextCreated", json!({"context":{"id":7,"uniqueId":"CONTEXT","origin":origin,"auxData":{"frameId":frame,"isDefault":true}}}))
}

fn frame(url: &str) -> Value {
    let origin = url::Url::parse(url).map(|url| url.origin().ascii_serialization()).unwrap_or_else(|_| "null".into());
    json!({"frameTree":{"frame":{"id":"F","url":url,"securityOrigin":origin,"loaderId":"LOADER"}}})
}

impl Transport for Backend {
    fn send_text(&mut self, text: &str) -> Result<(), String> {
        let command: Value = serde_json::from_str(text).unwrap();
        let mut state = self.0.borrow_mut();
        state.sent.push(command.clone());
        let method = command["method"].as_str().unwrap();
        let params = &command["params"];
        let result = match method {
            "Page.getFrameTree" => {
                if state.fail_tree && state.installed {
                    state.inbox.push_back(json!({"id":command["id"],"error":{"message":"verification failed"}}).to_string());
                    return Ok(());
                }
                state.tree.clone()
            }
            "Runtime.enable" if command["sessionId"] == "H" => {
                let frame = state.current_frame.clone();
                state.inbox.push_back(context(&frame, "https://ordinary.test").to_string());
                json!({})
            }
            "Page.addScriptToEvaluateOnNewDocument" => {
                state.source = params["source"].as_str().unwrap().to_string();
                state.installed = true;
                if state.pause && params["runImmediately"] == true {
                    state.inbox.push_back(event("Debugger.scriptParsed", json!({"scriptId":"SCRIPT","executionContextId":7})).to_string());
                    state.inbox.push_back(paused().to_string());
                }
                json!({"identifier":"INIT"})
            }
            "Debugger.getScriptSource" => json!({"scriptSource":if state.forged { "function __slicc_webmcp_gate() { debugger; }" } else { &state.source }}),
            "Debugger.evaluateOnCallFrame" => {
                state.permitted = params["expression"].as_str().unwrap().ends_with("true");
                if state.context_race == "permit" && state.permitted {
                    let mut changed = context("F", "https://ordinary.test");
                    changed["params"]["context"]["uniqueId"] = json!("REUSED_CONTEXT");
                    state.inbox.push_back(changed.to_string());
                }
                if state.race && state.permitted {
                    state.tree = frame("https://seven.sliccy.ai/");
                    let changed = state.tree["frameTree"]["frame"].clone();
                    state.inbox.push_back(event("Page.frameNavigated", json!({"frame":changed})).to_string());
                }
                json!({"result":{"type":"boolean","value":state.permitted}})
            }
            "Debugger.resume" => {
                state.api = state.permitted;
                if state.context_race == "resume" && state.permitted {
                    let mut changed = context("F", "https://ordinary.test");
                    changed["params"]["context"]["uniqueId"] = json!("REUSED_CONTEXT");
                    state.inbox.push_back(changed.to_string());
                    state.api = false;
                }
                state.permitted = false;
                json!({})
            }
            "Page.removeScriptToEvaluateOnNewDocument" => {
                state.installed = false;
                json!({})
            }
            "Target.detachFromTarget" => {
                if state.permitted { state.api = true; }
                state.installed = false;
                json!({})
            }
            _ => json!({}),
        };
        state.inbox.push_back(json!({"id":command["id"],"result":result}).to_string());
        Ok(())
    }

    fn recv_text(&mut self, _deadline: Instant) -> Result<Option<String>, String> {
        Ok(self.0.borrow_mut().inbox.pop_front())
    }
}

fn client() -> (Cdp, Rc<RefCell<BackendState>>) {
    let state = Rc::new(RefCell::new(BackendState { tree: frame("https://ordinary.test/"), current_frame: "F".into(), pause: true, ..Default::default() }));
    let mut cdp = Cdp::from_transport(Box::new(Backend(state.clone())));
    cdp.protect_origins(&["https://catalog.test"]);
    cdp.session_targets.insert("H".into(), "T".into());
    cdp.guards.insert("T".into(), "G".into());
    cdp.guard_sessions.insert("G".into());
    cdp.targets.insert("T".into(), json!({"targetId":"T","type":"page","url":"https://ordinary.test/"}));
    cdp.main_commit("G", &state.borrow().tree["frameTree"]["frame"]);
    (cdp, state)
}

#[test]
fn numeric_context_reuse_during_permission_or_resume_revokes_the_installer() {
    for stage in ["permit", "resume"] {
        let (mut cdp, state) = client();
        state.borrow_mut().context_race = stage.to_string();
        assert!(cdp.start_webmcp("H").is_err(), "{stage}");
        no_protected_api(&mut cdp, &state);
        assert!(cdp.webmcp_snapshot().is_empty());
    }
}

#[test]
fn quarantined_future_installer_does_not_run_in_the_existing_document() {
    for destination in ["https://ordinary.test/", "https://seven.sliccy.ai/"] {
        let (mut cdp, state) = client();
        state.borrow_mut().tree = frame("about:blank");
        cdp.webmcp.quarantined.insert("H".into());
        cdp.webmcp_install("H", true, None).unwrap();
        assert!(!state.borrow().api);
        assert!(cdp.webmcp_snapshot().is_empty());
        assert!(state.borrow().sent.iter().any(|call| call["method"] == "Page.addScriptToEvaluateOnNewDocument" && call["params"]["runImmediately"] == false));
        cdp.absorb(event("Page.frameNavigated", json!({"frame":frame(destination)["frameTree"]["frame"]})));
        enter_document(&mut cdp, &state, frame(destination), "F");
        if destination.contains("seven") { no_protected_api(&mut cdp, &state); }
        else { assert!(state.borrow().api); assert_eq!(cdp.take_webmcp_children().len(), 1); }
    }
}

fn enter_document(cdp: &mut Cdp, state: &Rc<RefCell<BackendState>>, tree: Value, frame: &str) {
    {
        let mut backend = state.borrow_mut();
        backend.tree = tree;
        backend.api = false;
        backend.permitted = false;
        backend.current_frame = frame.to_string();
    }
    cdp.absorb(context(frame, "https://ordinary.test"));
    cdp.absorb(event("Debugger.scriptParsed", json!({"scriptId":"SCRIPT","executionContextId":7})));
    cdp.absorb(paused());
}

fn no_protected_api(cdp: &mut Cdp, state: &Rc<RefCell<BackendState>>) {
    assert!(!state.borrow().api, "shim executed in a document the guard did not verify");
    assert!(!state.borrow().installed, "init script was not removed");
    cdp.block("H");
    for expression in ["document.modelContext.getTools()", "document.modelContext.executeTool('secret', {})"] {
        assert!(cdp.call("Runtime.evaluate", json!({"expression":expression}), Some("H")).is_err());
    }
}

#[test]
fn vetted_webmcp_installer_admits_only_guard_verified_documents() {
    let (mut cdp, state) = client();
    let bridge = cdp.start_webmcp("H").unwrap();
    assert!(bridge.starts_with("__wmcp_"));
    assert_eq!(bridge.len(), 39);
    assert!(state.borrow().api);
    assert_eq!(cdp.webmcp_pause_micros().len(), 1);
    assert!(cdp.webmcp_alive("H"));
    cdp.webmcp_auto_attach("H").unwrap();
    assert_eq!(crate::hash::sha256_hex(SHIM.as_bytes()), SHIM_HASH);
    assert!(cdp.call("Page.addScriptToEvaluateOnNewDocument", json!({"source":"arbitrary"}), Some("H")).is_err());
    cdp.stop_webmcp("H");
    assert!(!state.borrow().installed);
    assert!(!cdp.webmcp_alive("H"));
}

#[test]
fn paused_child_installation_covers_the_existing_verified_document() {
    let (mut cdp, state) = client();
    let seed = state.borrow().tree.clone();
    cdp.webmcp_install("H", true, Some(&seed)).unwrap();
    assert!(state.borrow().api);
    assert!(state.borrow().sent.iter().any(|command| command["method"] == "Page.addScriptToEvaluateOnNewDocument" && command["params"]["runImmediately"] == true));
}

#[test]
fn initial_blank_child_remains_inert_and_replaces_the_removed_script_before_resume() {
    let (mut cdp, state) = client();
    cdp.start_webmcp("H").unwrap();
    let mut tree = frame("https://ordinary.test/");
    let child = json!({"id":"CHILD","parentId":"F","url":"about:blank","securityOrigin":"https://ordinary.test","loaderId":"BLANK"});
    tree["frameTree"]["childFrames"] = json!([{"frame":child}]);
    state.borrow_mut().tree = tree.clone();
    cdp.absorb(event("Page.frameNavigated", json!({"frame":child})));
    assert!(cdp.webmcp_alive("H"));
    enter_document(&mut cdp, &state, tree, "CHILD");
    assert!(!state.borrow().api);
    assert!(state.borrow().installed);
    assert!(cdp.webmcp_alive("H"));
    let backend = state.borrow();
    let removed = backend.sent.iter().rposition(|command| command["method"] == "Page.removeScriptToEvaluateOnNewDocument").unwrap();
    let replaced = backend.sent.iter().rposition(|command| command["method"] == "Page.addScriptToEvaluateOnNewDocument" && command["params"]["runImmediately"] == false).unwrap();
    let resumed = backend.sent.iter().rposition(|command| command["method"] == "Debugger.resume").unwrap();
    assert!(removed < replaced && replaced < resumed);
}

#[test]
fn an_unverified_blank_top_document_cannot_rearm() {
    let (mut cdp, state) = client();
    cdp.start_webmcp("H").unwrap();
    enter_document(&mut cdp, &state, frame("about:blank"), "F");
    no_protected_api(&mut cdp, &state);
}

#[test]
fn holder_navigation_and_history_cannot_inject_protected_documents() {
    for origin in ["https://seven.sliccy.ai/", "https://vault.sliccy.ai/", "https://catalog.test/"] {
        for _variant in ["navigation", "history"] {
            let (mut cdp, state) = client();
            cdp.start_webmcp("H").unwrap();
            enter_document(&mut cdp, &state, frame(origin), "F");
            no_protected_api(&mut cdp, &state);
        }
    }
}

#[test]
fn holder_protected_bfcache_restore_has_no_shim_and_no_tool_access() {
    let (mut cdp, state) = client();
    cdp.start_webmcp("H").unwrap();
    state.borrow_mut().api = false;
    let tree = frame("https://seven.sliccy.ai/");
    cdp.absorb(event("Page.frameNavigated", json!({"type":"BackForwardCacheRestore","frame":tree["frameTree"]["frame"]})));
    no_protected_api(&mut cdp, &state);
}

#[test]
fn holder_protected_child_frame_never_passes_the_gate() {
    let (mut cdp, state) = client();
    cdp.start_webmcp("H").unwrap();
    let mut tree = frame("https://ordinary.test/");
    let mut child = frame("https://seven.sliccy.ai/")["frameTree"].clone();
    child["frame"]["id"] = json!("CHILD");
    child["frame"]["parentId"] = json!("F");
    tree["frameTree"]["childFrames"] = json!([child]);
    enter_document(&mut cdp, &state, tree, "CHILD");
    no_protected_api(&mut cdp, &state);
}

#[test]
fn holder_protected_oopif_stays_quarantined_and_receives_no_init_script() {
    let (mut cdp, state) = client();
    cdp.start_webmcp("H").unwrap();
    cdp.absorb(event("Target.attachedToTarget", json!({"sessionId":"CHILD","targetInfo":{"targetId":"IFRAME","type":"iframe","url":"https://seven.sliccy.ai/"},"waitingForDebugger":true})));
    assert!(cdp.take_webmcp_children().is_empty());
    assert!(cdp.session_blocked("CHILD"));
    assert!(!state.borrow().sent.iter().any(|command| command["sessionId"] == "CHILD" && command["method"] == "Page.addScriptToEvaluateOnNewDocument"));
    assert!(cdp.call("Runtime.evaluate", json!({"expression":"document.modelContext.getTools()"}), Some("CHILD")).is_err());
    assert!(cdp.call("Runtime.evaluate", json!({"expression":"document.modelContext.executeTool('secret', {})"}), Some("CHILD")).is_err());
}

#[test]
fn holder_protected_error_document_and_unverified_document_never_pass_the_gate() {
    for url in ["chrome-error://chromewebdata/", "about:blank"] {
        let (mut cdp, state) = client();
        cdp.start_webmcp("H").unwrap();
        let mut tree = frame(url);
        tree["frameTree"]["frame"]["securityOrigin"] = json!("null");
        tree["frameTree"]["frame"]["unreachableUrl"] = json!("https://vault.sliccy.ai/");
        enter_document(&mut cdp, &state, tree, "F");
        no_protected_api(&mut cdp, &state);
    }
}

#[test]
fn holder_commit_before_attach_refuses_installation() {
    let (mut cdp, state) = client();
    state.borrow_mut().tree = frame("https://seven.sliccy.ai/");
    assert!(cdp.start_webmcp("H").is_err());
    assert!(!state.borrow().sent.iter().any(|command| command["method"] == "Page.addScriptToEvaluateOnNewDocument"));
    no_protected_api(&mut cdp, &state);
}

#[test]
fn holder_generation_change_between_verification_and_resume_fails_closed() {
    let (mut cdp, state) = client();
    state.borrow_mut().race = true;
    assert!(cdp.start_webmcp("H").is_err());
    no_protected_api(&mut cdp, &state);
    let commands = &state.borrow().sent;
    let denied = commands.iter().position(|command| command["method"] == "Debugger.evaluateOnCallFrame" && command["params"]["expression"] == format!("{PERMIT} = false")).unwrap();
    let detach = commands.iter().position(|command| command["method"] == "Target.detachFromTarget").unwrap();
    assert!(denied < detach);
}

#[test]
fn holder_missing_pause_forged_gate_unknown_context_and_verification_error_fail_closed() {
    for variant in ["missing", "forged", "unknown", "error"] {
        let (mut cdp, state) = client();
        match variant {
            "missing" => state.borrow_mut().pause = false,
            "forged" => state.borrow_mut().forged = true,
            "unknown" => state.borrow_mut().current_frame = "UNKNOWN".into(),
            "error" => state.borrow_mut().fail_tree = true,
            _ => unreachable!(),
        }
        assert!(cdp.start_webmcp("H").is_err(), "{variant}");
        no_protected_api(&mut cdp, &state);
    }
}

#[test]
fn holder_future_document_missing_pause_is_removed_even_without_context_events() {
    let (mut cdp, state) = client();
    cdp.start_webmcp("H").unwrap();
    let mut next = frame("https://ordinary.test/next");
    next["frameTree"]["frame"]["loaderId"] = json!("NEXT");
    cdp.absorb(event("Page.frameNavigated", json!({"frame":next["frameTree"]["frame"]})));
    cdp.webmcp.arms.get_mut("H").unwrap().pending_frames.get_mut("F").unwrap().1 = Instant::now();
    cdp.webmcp_sweep();
    assert!(!cdp.webmcp_alive("H"));
    assert!(!state.borrow().installed);
}

#[test]
fn holder_known_ordinary_bfcache_restore_keeps_an_inert_installer() {
    let (mut cdp, state) = client();
    cdp.start_webmcp("H").unwrap();
    cdp.absorb(event("Runtime.executionContextsCleared", json!({})));
    cdp.absorb(event("Page.frameNavigated", json!({"type":"BackForwardCacheRestore","frame":state.borrow().tree["frameTree"]["frame"]})));
    cdp.absorb(context("F", "https://ordinary.test"));
    let arm = cdp.webmcp.arms.get("H").unwrap();
    assert!(arm.pending_frames.is_empty());
    assert!(arm.pending_contexts.is_empty());
    cdp.webmcp_sweep();
    assert!(cdp.webmcp_alive("H"));
}

#[test]
fn holder_changed_loader_and_context_destroyed_before_pause_are_denied() {
    for destroyed in [false, true] {
        let (mut cdp, state) = client();
        cdp.start_webmcp("H").unwrap();
        state.borrow_mut().api = false;
        state.borrow_mut().tree["frameTree"]["frame"]["loaderId"] = json!("NEXT");
        if destroyed { cdp.absorb(event("Runtime.executionContextDestroyed", json!({"executionContextId":7}))); }
        cdp.absorb(paused());
        no_protected_api(&mut cdp, &state);
    }
}

#[test]
fn holder_detached_frame_clears_its_missing_pause_deadline() {
    let (mut cdp, _) = client();
    cdp.start_webmcp("H").unwrap();
    cdp.absorb(context("REMOVED", "https://ordinary.test"));
    cdp.absorb(event("Page.frameNavigated", json!({"frame":{"id":"REMOVED","loaderId":"CHILD","url":"https://ordinary.test/frame","securityOrigin":"https://ordinary.test","parentId":"F"}})));
    cdp.absorb(event("Page.frameDetached", json!({"frameId":"REMOVED"})));
    assert!(cdp.webmcp.arms["H"].pending_frames.is_empty());
    assert!(!cdp.webmcp.contexts.values().any(|context| context.frame == "REMOVED"));
}

#[test]
fn ordinary_documents_without_holder_pay_no_debugger_or_init_overhead() {
    let (mut cdp, state) = client();
    cdp.call("Runtime.evaluate", json!({"expression":"1"}), Some("H")).unwrap();
    assert!(state.borrow().sent.iter().all(|command| !command["method"].as_str().unwrap().starts_with("Debugger.") && command["method"] != "Page.addScriptToEvaluateOnNewDocument"));
}