slicc-cdp-client 1.42.2

Shared CDP connection for slicc-cdp CLIs
Documentation
use std::cell::RefCell;
use std::rc::Rc;

#[derive(Default)]
struct PersistentState {
    installed: bool,
    executions: usize,
    sent: Vec<Value>,
    inbox: VecDeque<String>,
}

struct PersistentBackend(Rc<RefCell<PersistentState>>);

impl Transport for PersistentBackend {
    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());
        match command["method"].as_str().unwrap_or("") {
            "Page.addScriptToEvaluateOnNewDocument" => state.installed = true,
            "Page.removeScriptToEvaluateOnNewDocument" | "Target.detachFromTarget" => state.installed = false,
            _ => {}
        }
        state.inbox.push_back(json!({"id":command["id"],"result":{"identifier":"INIT"}}).to_string());
        Ok(())
    }

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

fn persistent_client() -> (Cdp, Rc<RefCell<PersistentState>>) {
    let state = Rc::new(RefCell::new(PersistentState::default()));
    let mut cdp = Cdp::from_transport(Box::new(PersistentBackend(state.clone())));
    cdp.protect_origins(&["https://catalog.ordinary.test"]);
    cdp.session_targets.insert("S".into(), "T".into());
    cdp.absorb(json!({"method":"Target.targetInfoChanged","params":{"targetInfo":{"targetId":"T","type":"page","url":"https://ordinary.test/"}}}));
    cdp.absorb(json!({"method":"Page.frameNavigated","sessionId":"S","params":{"frame":{"id":"F","url":"https://ordinary.test/","securityOrigin":"https://ordinary.test"}}}));
    (cdp, state)
}

fn persistent_commit(cdp: &mut Cdp, state: &Rc<RefCell<PersistentState>>, url: &str, variant: &str) {
    {
        let mut backend = state.borrow_mut();
        if backend.installed {
            backend.executions += 1;
        }
    }
    let mut frame = json!({"id":"F","url":url,"securityOrigin":url});
    if variant == "child" {
        frame["id"] = json!("CHILD");
        frame["parentId"] = json!("F");
    }
    if variant == "error" {
        frame["url"] = json!("chrome-error://chromewebdata/");
        frame["unreachableUrl"] = json!(url);
    }
    cdp.absorb(json!({"method":"Page.frameNavigated","sessionId":"S","params":{"frame":frame,"type":if variant == "bfcache" {"BackForwardCacheRestore"} else {"Navigation"}}}));
}

fn persistent_variant(variant: &str) {
    for url in ["https://seven.sliccy.ai/", "https://vault.sliccy.ai/", "https://catalog.ordinary.test/"] {
        let (mut cdp, state) = persistent_client();
        assert!(cdp.guard().url(url));
        let installed = cdp.call("Page.addScriptToEvaluateOnNewDocument", json!({"source":"globalThis.injected = true"}), Some("S"));
        if variant == "commit-before-attach" {
            cdp.session_targets.remove("S");
        }
        persistent_commit(&mut cdp, &state, url, variant);
        assert_eq!(state.borrow().executions, 0, "{variant}: persistent script executed before the guard observed the commit");
        assert!(installed.is_err(), "unguarded persistent script was admitted");
        assert!(!state.borrow().sent.iter().any(|command| command["method"] == "Page.addScriptToEvaluateOnNewDocument"));
    }
}

#[test]
fn persistent_navigation_is_refused_before_install() { persistent_variant("navigation"); }

#[test]
fn persistent_history_is_refused_before_install() { persistent_variant("history"); }

#[test]
fn persistent_bfcache_is_refused_before_install() { persistent_variant("bfcache"); }

#[test]
fn persistent_child_is_refused_before_install() { persistent_variant("child"); }

#[test]
fn persistent_error_is_refused_before_install() { persistent_variant("error"); }

#[test]
fn persistent_commit_before_attach_is_refused_before_install() { persistent_variant("commit-before-attach"); }

#[test]
fn persistent_capabilities_are_refused_for_calls_and_notifications() {
    let capabilities = [
        ("Page.addScriptToEvaluateOnNewDocument", json!({"source":"globalThis.injected = true","runImmediately":true})),
        ("Page.addScriptToEvaluateOnLoad", json!({"scriptSource":"globalThis.injected = true"})),
        ("Page.setBypassCSP", json!({"enabled":true})),
        ("Page.setDownloadBehavior", json!({"behavior":"allow","downloadPath":"/tmp"})),
        ("Page.startScreencast", json!({})),
        ("Page.setWebLifecycleState", json!({"state":"frozen"})),
        ("Network.setExtraHTTPHeaders", json!({"headers":{"X-Control":"changed"}})),
        ("Network.setUserAgentOverride", json!({"userAgent":"changed"})),
        ("Network.setBypassServiceWorker", json!({"bypass":true})),
        ("Network.setCacheDisabled", json!({"cacheDisabled":true})),
        ("Network.emulateNetworkConditions", json!({"offline":true,"latency":0,"downloadThroughput":0,"uploadThroughput":0})),
        ("Emulation.setScriptExecutionDisabled", json!({"value":true})),
        ("Emulation.setVirtualTimePolicy", json!({"policy":"pause"})),
        ("Emulation.setGeolocationOverride", json!({"latitude":1,"longitude":1,"accuracy":1})),
        ("Input.setIgnoreInputEvents", json!({"ignore":true})),
        ("Security.setIgnoreCertificateErrors", json!({"ignore":true})),
        ("Debugger.setInstrumentationBreakpoint", json!({"instrumentation":"beforeScriptExecution"})),
        ("Debugger.setBreakpointByUrl", json!({"lineNumber":0,"urlRegex":".*"})),
        ("Runtime.setCustomObjectFormatterEnabled", json!({"enabled":true})),
        ("Overlay.setShowPaintRects", json!({"result":true})),
        ("Browser.setDownloadBehavior", json!({"behavior":"allow","downloadPath":"/tmp"})),
        ("Profiler.start", json!({})),
        ("HeapProfiler.startTrackingHeapObjects", json!({})),
        ("Target.setAutoAttach", json!({"autoAttach":true,"flatten":true,"waitForDebuggerOnStart":true})),
    ];
    for (method, params) in capabilities {
        let (mut cdp, state) = persistent_client();
        assert!(cdp.call(method, params.clone(), Some("S")).unwrap_err().starts_with("slicc: refused:"), "{method}");
        assert!(cdp.notify(method, params, Some("S")).unwrap_err().starts_with("slicc: refused:"), "{method}");
        assert!(!state.borrow().sent.iter().any(|command| command["method"] == method), "{method} reached the transport");
    }
}

#[test]
fn scoped_bindings_are_refused_even_in_a_verified_page_world() {
    let (mut cdp, state) = persistent_client();
    cdp.worlds.enable("S");
    cdp.absorb(json!({"method":"Runtime.executionContextCreated","sessionId":"S","params":{"context":{"id":1,"uniqueId":"ordinary-context","name":"page","origin":"https://ordinary.test","auxData":{"isDefault":true,"frameId":"F"}}}}));
    for selector in [json!({"executionContextId":1}), json!({"executionContextName":"page"})] {
        let mut params = selector;
        params["name"] = json!("persistentBinding");
        assert!(cdp.call("Runtime.addBinding", params.clone(), Some("S")).unwrap_err().contains("persistent page capabilities"));
        assert!(cdp.notify("Runtime.addBinding", params, Some("S")).unwrap_err().contains("persistent page capabilities"));
    }
    assert!(!state.borrow().sent.iter().any(|command| command["method"] == "Runtime.addBinding"));
}

fn network_state_params(offline: bool) -> Value {
    json!({"offline":offline,"latency":0,"downloadThroughput":-1,"uploadThroughput":-1})
}

fn network_state_sent(state: &Rc<RefCell<PersistentState>>) -> Vec<Value> {
    state.borrow().sent.iter().filter(|command| command["method"] == "Network.emulateNetworkConditions").cloned().collect()
}

#[test]
fn network_state_admits_only_the_offline_toggle_on_an_ordinary_page_session() {
    for offline in [true, false] {
        let (mut cdp, state) = persistent_client();
        cdp.call("Network.emulateNetworkConditions", network_state_params(offline), Some("S")).unwrap();
        let sent = network_state_sent(&state);
        assert_eq!(sent.len(), 1);
        assert_eq!(sent[0]["params"], network_state_params(offline));
        assert_eq!(sent[0]["sessionId"], "S");
    }
}

#[test]
fn network_state_refuses_every_other_shape_and_condition_method() {
    let shapes = [
        json!({"offline":true,"latency":0,"downloadThroughput":0,"uploadThroughput":0}),
        json!({"offline":true,"latency":5,"downloadThroughput":-1,"uploadThroughput":-1}),
        json!({"offline":true,"latency":0.0,"downloadThroughput":-1,"uploadThroughput":-1}),
        json!({"offline":true,"latency":0,"downloadThroughput":-1.0,"uploadThroughput":-1}),
        json!({"offline":"true","latency":0,"downloadThroughput":-1,"uploadThroughput":-1}),
        json!({"offline":null,"latency":0,"downloadThroughput":-1,"uploadThroughput":-1}),
        json!({"offline":true,"latency":0,"downloadThroughput":-1}),
        json!({"latency":0,"downloadThroughput":-1,"uploadThroughput":-1,"connectionType":"none"}),
        json!({"offline":false,"latency":0,"downloadThroughput":-1,"uploadThroughput":-1,"connectionType":"wifi"}),
        json!({"offline":false,"latency":0,"downloadThroughput":-1,"uploadThroughput":-1,"packetLoss":100}),
        json!([]),
        json!(null),
    ];
    for params in shapes {
        let (mut cdp, state) = persistent_client();
        assert!(cdp.call("Network.emulateNetworkConditions", params.clone(), Some("S")).unwrap_err().contains("persistent page capabilities"), "{params}");
        assert!(cdp.notify("Network.emulateNetworkConditions", params.clone(), Some("S")).unwrap_err().contains("persistent page capabilities"), "{params}");
        assert!(network_state_sent(&state).is_empty(), "{params}");
    }
    for method in ["Network.emulateNetworkConditionsByRule", "Network.overrideNetworkState", "Network.setBlockedURLs", "Network.setBypassServiceWorker"] {
        let (mut cdp, state) = persistent_client();
        assert!(cdp.call(method, network_state_params(true), Some("S")).unwrap_err().starts_with("slicc: refused:"), "{method}");
        assert!(!state.borrow().sent.iter().any(|command| command["method"] == method), "{method}");
    }
}

#[test]
fn network_state_is_refused_off_an_ordinary_page_session() {
    let (mut cdp, state) = persistent_client();
    let method = "Network.emulateNetworkConditions";
    assert!(cdp.call(method, network_state_params(true), None).is_err());
    assert!(cdp.call("Network.emulateNetworkConditions", network_state_params(true), Some("UNKNOWN")).is_err());
    cdp.guard_sessions.insert("G".into());
    cdp.session_targets.insert("G".into(), "T".into());
    assert!(cdp.call("Network.emulateNetworkConditions", network_state_params(true), Some("G")).is_err());
    cdp.dialog_sessions.insert("D".into());
    cdp.session_targets.insert("D".into(), "T".into());
    assert!(cdp.call("Network.emulateNetworkConditions", network_state_params(true), Some("D")).is_err());
    for kind in ["iframe", "worker", "service_worker", "shared_worker", "other"] {
        let target = format!("T-{kind}");
        let session = format!("S-{kind}");
        cdp.session_targets.insert(session.clone(), target.clone());
        cdp.absorb(json!({"method":"Target.targetInfoChanged","params":{"targetInfo":{"targetId":target,"type":kind,"url":"https://ordinary.test/"}}}));
        assert!(cdp.call("Network.emulateNetworkConditions", network_state_params(true), Some(&session)).is_err(), "{kind}");
    }
    assert!(network_state_sent(&state).is_empty());
    for url in ["https://seven.sliccy.ai/", "https://vault.sliccy.ai/", "https://catalog.ordinary.test/"] {
        let (mut cdp, state) = persistent_client();
        cdp.absorb(json!({"method":"Target.targetInfoChanged","params":{"targetInfo":{"targetId":"T","type":"page","url":url}}}));
        assert!(cdp.call("Network.emulateNetworkConditions", network_state_params(true), Some("S")).is_err(), "{url}");
        assert!(network_state_sent(&state).is_empty(), "{url}");
    }
    for mark in ["protected", "abandoned", "blocked"] {
        let (mut cdp, state) = persistent_client();
        match mark {
            "protected" => { cdp.protected_targets.insert("T".into()); }
            "abandoned" => { cdp.abandoned.insert("T".into()); }
            _ => { cdp.blocked.insert("S".into()); }
        }
        assert!(cdp.call("Network.emulateNetworkConditions", network_state_params(false), Some("S")).is_err(), "{mark}");
        assert!(network_state_sent(&state).is_empty(), "{mark}");
    }
    let (mut cdp, state) = persistent_client();
    cdp.targets.remove("T");
    assert!(cdp.call("Network.emulateNetworkConditions", network_state_params(true), Some("S")).is_err());
    assert!(network_state_sent(&state).is_empty());
}

#[test]
fn network_state_cannot_reach_a_page_after_a_protected_commit() {
    for url in ["https://seven.sliccy.ai/", "https://vault.sliccy.ai/"] {
        let (mut cdp, state) = persistent_client();
        persistent_commit(&mut cdp, &state, url, "navigation");
        assert!(cdp.call("Network.emulateNetworkConditions", network_state_params(true), Some("S")).is_err(), "{url}");
        assert!(network_state_sent(&state).is_empty(), "{url}");
    }
}