75 lines
2.5 KiB
Python
75 lines
2.5 KiB
Python
"""Tests for `raycast_api.discovery.extractors`."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from raycast_api.discovery.extractors import (
|
|
extract_signing_spec,
|
|
extract_user_agent_template,
|
|
read_app_version,
|
|
)
|
|
from raycast_api.errors import DiscoveryError
|
|
|
|
|
|
def test_extract_signing_spec_from_synthetic(mock_bundle_source: str) -> None:
|
|
spec = extract_signing_spec(mock_bundle_source)
|
|
assert spec.rot_fn_name in {"RotXform", "RotXform2"}
|
|
assert spec.signing_fn_name == "SignReq"
|
|
assert spec.join_char == "."
|
|
assert spec.body_hash_algorithm == "SHA-256"
|
|
assert spec.hmac_algorithm == "SHA-256"
|
|
assert spec.key_encoding == "utf-8"
|
|
|
|
|
|
def test_rot_ranges_extracted(mock_bundle_source: str) -> None:
|
|
spec = extract_signing_spec(mock_bundle_source)
|
|
ranges = {(r.start, r.end, r.shift) for r in spec.rot_ranges}
|
|
assert ranges == {(65, 90, 13), (97, 122, 13), (48, 57, 5)}
|
|
|
|
|
|
def test_missing_rot_raises() -> None:
|
|
src = """
|
|
async function loneSigner(a, b, c, d) {
|
|
let k = await crypto.subtle.importKey("raw", new TextEncoder().encode(a), {name:"HMAC", hash:"SHA-256"}, false, ["sign"]);
|
|
return crypto.subtle.sign("HMAC", k, b);
|
|
}
|
|
"""
|
|
with pytest.raises(DiscoveryError, match="rot13"):
|
|
extract_signing_spec(src)
|
|
|
|
|
|
def test_signer_must_reference_rot() -> None:
|
|
"""If the only signing candidate doesn't .map() through the rot fn, fail loudly."""
|
|
src = """
|
|
function rotFn(s) {
|
|
let out = ""; for (let ch of s) {
|
|
let r = ch.charCodeAt(0);
|
|
if (r >= 65 && r <= 90) out += String.fromCharCode((r - 65 + 13) % 26 + 65);
|
|
else if (r >= 97 && r <= 122) out += String.fromCharCode((r - 97 + 13) % 26 + 97);
|
|
else if (r >= 48 && r <= 57) out += String.fromCharCode((r - 48 + 5) % 10 + 48);
|
|
else out += ch;
|
|
}
|
|
return out;
|
|
}
|
|
async function lone(a, b, c, d) {
|
|
// 4 params, HMAC/SHA-256/importKey — but doesn't call .map(rotFn).
|
|
let k = await crypto.subtle.importKey("raw", new TextEncoder().encode(a),
|
|
{name:"HMAC", hash:"SHA-256"}, false, ["sign"]);
|
|
return crypto.subtle.sign("HMAC", k, b);
|
|
}
|
|
"""
|
|
with pytest.raises(DiscoveryError, match=r"none of the signers calls\s+\.map"):
|
|
extract_signing_spec(src)
|
|
|
|
|
|
def test_read_app_version(mock_app: Path) -> None:
|
|
assert read_app_version(mock_app) == "9.9.9.0"
|
|
|
|
|
|
def test_user_agent_template(mock_app: Path) -> None:
|
|
ua = extract_user_agent_template(mock_app, platform_version="13.5")
|
|
assert ua == "Raycast/9.9.9.0 (x-macOS Version 13.5)"
|