The 60s tokio timeout only fires at await points, so a plugin stuck in a
synchronous loop blocked its spawn_blocking thread forever. An interrupt
handler with the same deadline preempts synchronous execution too, and a
256MB memory limit stops unbounded allocation.
Closes the gap flagged in the previous commit. Adds RSASSA-PKCS1-v1_5
(SHA256/384/512, via the rsa crate) and ECDSA P-256 (via p256/ecdsa)
sign/verify, native-backed through __crypto_sign_hex/__crypto_verify_hex,
with crypto.subtle.importKey/exportKey now accepting pkcs8/spki key
formats. RSA-SHA1 signing is not supported (rsa crate doesn't re-export
sha1; SHA1 signatures are already deprecated).
Verified against independently-produced signatures rather than just
self-consistency: RSA against an OpenSSL-signed message, ECDSA against
a signature produced by Python's `cryptography` library (DER-to-raw
r||s converted, matching WebCrypto's signature format) — both cross
into the native Rust layer and through the real crypto.subtle JS path.
14 plugin_runtime tests + 6 crypto_bridge tests, all passing, zero
clippy warnings.
Adds the crypto surface Nuvio scrapers commonly need to decrypt
obfuscated stream URLs: native digest (MD5/SHA1/256/384/512), HMAC,
PBKDF2, and AES-CBC/ECB/GCM (via sha1/sha2/md-5/hmac/pbkdf2/aes/cbc/
ecb + the existing aes-gcm dependency), exposed through the same
__crypto_*_hex bridge shape Nuvio uses, plus the CryptoJS-compatible
and crypto.subtle JS shims ported from JsBindings.kt.
RSA/ECDSA sign/verify are deliberately not implemented — crypto.subtle
.sign/.verify throw clearly rather than silently failing.
Verified against independently-known-correct vectors, not just
"compiles": SHA-256("abc"), HMAC-SHA256 (cross-checked against
Python's hmac/hashlib), AES-CBC roundtrip, and crypto.subtle.digest
all pass through the real JS engine end to end.
Promotes the rquickjs spike into a real Tauri command. Executes a
Nuvio-compatible scraper's getStreams() in a sandboxed QuickJS VM with
fetch (routed through net_guard::ensure_public_host, same SSRF guard
as http_fetch_text/http_execute_text) and the full cheerio bridge.
Runs via spawn_blocking with its own current-thread tokio runtime
since the DOM state (scraper/tendril) isn't Send; a 60s wall-clock
timeout matches Nuvio's own plugin timeout.
DomBridge switched from Arc<Mutex<_>> to Rc<RefCell<_>> per clippy —
it's only ever touched from the single thread each execution owns.