Site-agnostic parts of the X app, behind a small site adapter: - crates/waybetter-host: webview-label security policy and tab rect clamp, account profiles with native-owned accounts (AccountRules), plugin store with bundled-plugin upgrades (PluginHost), signed data-only hotfix channel, update scheduling and crash handshake, tab payloads/link routing/tab-chrome validation, and (feature "tauri") webview helpers and the Windows system menu. - packages/runtime: DOM hub, request hub, plugin API core, OKLab theme color model, split-view rail hiding, link routing and account reporting, each configured by a SiteAdapter. - packages/shell: address bar, pane, link-drop, profile, palette and update-chip models, and window controls with the Tauri API injected. Tests moved/adapted from the app: 45 Rust (headless), 40 TypeScript. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
63 lines
2.9 KiB
Rust
63 lines
2.9 KiB
Rust
use chrono::{TimeZone, Utc};
|
|
use waybetter_host::hotfix::{accept, minisign_text, script, validate, verify, Outcome};
|
|
|
|
// Throwaway test key pair (not a production hotfix key); fixture signed with `tauri signer sign`.
|
|
const PUBKEY: &str = include_str!("fixtures/hotfix-test.key.pub");
|
|
const BODY: &[u8] = include_bytes!("fixtures/hotfix-test.json");
|
|
const SIG: &str = include_str!("fixtures/hotfix-test.json.sig");
|
|
|
|
fn now() -> chrono::DateTime<Utc> {
|
|
Utc.with_ymd_and_hms(2026, 9, 27, 12, 0, 0).unwrap()
|
|
}
|
|
|
|
#[test]
|
|
fn accepts_a_signed_hotfix() {
|
|
let Outcome::Accepted(hotfix) = accept(PUBKEY, BODY, SIG, now(), "0.2.0", None).unwrap() else {
|
|
panic!("expected accepted");
|
|
};
|
|
assert_eq!(hotfix.version, 7);
|
|
assert_eq!(hotfix.flags["hideGrok"], serde_json::json!(true));
|
|
assert!(minisign_text(PUBKEY).unwrap().contains("minisign public key"));
|
|
}
|
|
|
|
#[test]
|
|
fn rejects_tampered_body_and_garbage() {
|
|
let tampered = String::from_utf8(BODY.to_vec()).unwrap().replace("\"version\":7", "\"version\":8");
|
|
assert!(verify(PUBKEY, tampered.as_bytes(), SIG).is_err());
|
|
assert!(verify(PUBKEY, BODY, "garbage").is_err());
|
|
assert!(verify("garbage", BODY, SIG).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn monotonic_versions() {
|
|
assert_eq!(accept(PUBKEY, BODY, SIG, now(), "0.2.0", Some(7)).unwrap(), Outcome::Unchanged);
|
|
assert!(accept(PUBKEY, BODY, SIG, now(), "0.2.0", Some(8)).is_err());
|
|
assert!(matches!(accept(PUBKEY, BODY, SIG, now(), "0.2.0", Some(6)).unwrap(), Outcome::Accepted(_)));
|
|
}
|
|
|
|
#[test]
|
|
fn expiry_and_min_native() {
|
|
let late = Utc.with_ymd_and_hms(2099, 1, 1, 0, 0, 1).unwrap();
|
|
assert!(accept(PUBKEY, BODY, SIG, late, "0.2.0", None).is_err());
|
|
assert!(accept(PUBKEY, BODY, SIG, now(), "0.0.9", None).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn schema_rejects_code_like_or_unknown_content() {
|
|
let base = |extra: &str| format!(r#"{{"version":1,"expires":"2099-01-01T00:00:00Z","minNative":"0.1.0"{extra}}}"#);
|
|
assert!(validate(base("").as_bytes(), now(), "0.2.0").is_ok());
|
|
assert!(validate(base(r#","script":"alert(1)""#).as_bytes(), now(), "0.2.0").is_err());
|
|
assert!(validate(base(r#","flags":{"x":{"nested":1}}"#).as_bytes(), now(), "0.2.0").is_err());
|
|
assert!(validate(base(r#","selectors":{"bad key!":"div"}"#).as_bytes(), now(), "0.2.0").is_err());
|
|
assert!(validate(base(r#","selectors":{"k":""}"#).as_bytes(), now(), "0.2.0").is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn script_delivers_data_only_under_the_given_names() {
|
|
let Outcome::Accepted(hotfix) = accept(PUBKEY, BODY, SIG, now(), "0.2.0", None).unwrap() else { panic!() };
|
|
let script = script(&hotfix, "__demoHotfix", "demo-hotfix");
|
|
assert!(script.starts_with("(()=>{const h=JSON.parse(\""));
|
|
assert!(script.contains("window.__demoHotfix=h") && script.contains("new CustomEvent('demo-hotfix')"));
|
|
// The selector's quotes are escaped inside a JSON string literal, never raw JS.
|
|
assert!(!script.contains("[data-testid=\"tweetTextarea_0\"]"));
|
|
}
|