feat: add headless Zoko source checks

This commit is contained in:
2026-10-25 18:01:47 +01:00
parent e6a2759497
commit b95f7d251e
11 changed files with 329 additions and 12 deletions
+79
View File
@@ -0,0 +1,79 @@
#!/usr/bin/env python3
"""Actual CLI contract checks; --live adds bounded public embed/root probes only."""
import json
import os
from pathlib import Path
import shutil
import subprocess
import sys
import tempfile
def require(condition, message):
if not condition:
raise AssertionError(message)
def snapshot(*roots):
return {
str(path): path.read_bytes() if path.is_file() else None
for root in roots
for path in root.rglob("*")
}
def main():
executable = Path(sys.argv[1]).resolve(strict=True)
live = "--live" in sys.argv[2:]
watch = "https://zokoanime.video/stream/ani/1/1/sub"
secret = "private-fixture-token"
with tempfile.TemporaryDirectory(prefix="terebi-source-check-") as temporary:
root = Path(temporary)
home, config = root / "home", root / "config"
home.mkdir()
(config / "terebi").mkdir(parents=True)
# A launch would parse this malformed configuration or reject this
# executable outside its bundle. A source check must do neither.
(config / "terebi" / "config.toml").write_text("not valid TOML = [\n")
cli = root / "terebi"
shutil.copy2(executable, cli)
environment = dict(os.environ, HOME=str(home), CFFIXED_USER_HOME=str(home), XDG_CONFIG_HOME=str(config))
before = snapshot(home, config)
def invoke(arguments):
try:
result = subprocess.run([str(cli), *arguments], env=environment, capture_output=True, timeout=20)
except subprocess.TimeoutExpired:
raise AssertionError("source-check CLI exceeded total deadline") from None
require(len(result.stdout) <= 128 and len(result.stderr) <= 1024, "bounded CLI output")
require(secret.encode() not in result.stdout + result.stderr, "CLI output redacts private input")
require(b"https://" not in result.stdout, "no source URLs in JSON")
require(snapshot(home, config) == before, "source checking must not write configuration or resume/progress state")
return result
for arguments in [
["--check-source"], ["--check-source", watch + "?token=" + secret],
["--check-source", "https://" + secret + "@zokoanime.video/stream/ani/1/1/sub"],
["--new-instance", "--check-source", watch], ["--check-source", watch, "--no-config"],
["--check-source", watch, "--progress-file", str(root / secret)],
["--check-source", "https://127.0.0.1/" + secret],
]:
result = invoke(arguments)
require(result.returncode != 0 and not result.stdout, "invalid syntax/unsupported URL must fail without JSON or launch")
require(not (root / secret).exists(), "invalid source check must not create progress file")
if live:
for episode, expected in [(1, "available"), (27, "unavailable")]:
result = invoke(["--check-source", f"https://zokoanime.video/stream/ani/1/{episode}/sub"])
require(result.returncode == 0, "completed provider checks exit zero")
require(result.stdout.endswith(b"\n") and result.stdout.count(b"\n") == 1, "exactly one JSON line")
row = json.loads(result.stdout)
require(row == {"version": 1, "provider": "zokoanime", "track": "sub", "status": expected},
f"live episode {episode} expected {expected}; result is redacted")
require(not result.stderr, "successful source checks have no error logs")
print("PASS: actual CLI resolves episode 1/root manifest and explicit missing episode 27 headlessly, without state writes")
else:
print("PASS: actual CLI rejects hostile URLs/flags privately, without configuration reads, state writes or bundle launch")
if __name__ == "__main__":
main()
+101
View File
@@ -0,0 +1,101 @@
import Foundation
@main
struct SourceCheckChecks {
struct Failure: Error {}
static func require(_ condition: @autoclosure () -> Bool, _ message: String) throws {
guard condition() else {
FileHandle.standardError.write(Data("FAIL: \(message)\n".utf8))
throw Failure()
}
}
static func main() async throws {
let watch = "https://zokoanime.video/stream/ani/1/1/sub"
guard case .checkSource(let id, let episode) = try PlayerCommand.parse(["--check-source", watch]) else {
throw Failure()
}
try require(id == "1" && episode == 1, "check command is not a launch")
let secret = "private-fixture-token"
for arguments in [
["--check-source"], ["--check-source", watch, watch],
["--new-instance", "--check-source", watch], ["--no-config", "--check-source", watch],
["--check-source", watch, "--progress-file", "/tmp/" + secret],
["--source-file", "/tmp/" + secret, "--check-source", watch],
["--check-source", watch, "--help"], ["--check-source", "--check-source"],
["--check-source", "https://youtu.be/jNQXAC9IVRw"],
["--check-source", "https://barelystarted.miruro.tv/watch/1/show?ep=1"],
["--check-source", " " + watch], ["--check-source", watch + "\n"],
["--check-source", watch + "?token=" + secret], ["--check-source", watch + "#" + secret],
["--check-source", watch.replacingOccurrences(of: "https:", with: "http:")],
["--check-source", watch.replacingOccurrences(of: "/1/1/", with: "/01/1/")],
["--check-source", watch.replacingOccurrences(of: "/1/sub", with: "/2147483648/sub")],
["--check-source", watch.replacingOccurrences(of: "/sub", with: "/dub")],
["--check-source", "https://" + secret + "@zokoanime.video/stream/ani/1/1/sub"],
["--check-source", "https://zokoanime.video.evil.example/stream/ani/1/1/sub"],
["--check-source", "https://zokoanime.video/stream/ani/%31/1/sub"],
["--check-source", String(repeating: secret, count: 100)]
] {
do {
_ = try PlayerCommand.parse(arguments)
} catch {
try require(!error.localizedDescription.contains(secret), "argument errors redact input")
continue
}
throw Failure()
}
for status in SourceCheck.Status.allCases {
let json = status.json
let row = try JSONSerialization.jsonObject(with: Data(json.utf8)) as! [String: Any]
try require(row.count == 4 && row["version"] as? Int == 1 && row["provider"] as? String == "zokoanime" &&
row["track"] as? String == "sub" && row["status"] as? String == status.rawValue,
"one bounded result schema without source addresses or error strings")
try require(json.utf8.count < 128 && !json.contains("\n"), "bounded single-line JSON")
}
let root = URL(string: "https://hls.dramahot.top/master.m3u8?token=" + secret)!
let master = "#EXTM3U\n#EXT-X-STREAM-INF:BANDWIDTH=1000000\nvideo.m3u8\n"
let media = "#EXTM3U\n#EXT-X-TARGETDURATION:10\n#EXTINF:10,\nhttps://hls1.drama1.cfd/segment.jpg\n#EXT-X-ENDLIST\n"
try MiruroTransport.validateZokoManifest(master, base: root)
try MiruroTransport.validateZokoManifest(media, base: root)
for malformed in ["<html>" + secret + "</html>", "#EXTM3U", "#EXTM3Ugarbage\n" + master,
"#EXTM3U\n#EXT-X-STREAM-INF:BANDWIDTH=1\n", "#EXTM3U\nrandom.txt\n",
master.replacingOccurrences(of: "video.m3u8", with: "https://127.0.0.1/private.m3u8"),
master.replacingOccurrences(of: "video.m3u8", with: "https://hls1.drama1.cfd/private.m3u8"),
media.replacingOccurrences(of: "https://hls1.drama1.cfd/segment.jpg", with: "https://evil.example/" + secret),
"#EXTM3U\n#EXT-X-KEY:METHOD=AES-128,URI=\"http://127.0.0.1/" + secret + "\"\n" + media,
String(repeating: "x", count: ZokoResolver.bodyLimit + 1)] {
do { try MiruroTransport.validateZokoManifest(malformed, base: root) }
catch {
try require(!error.localizedDescription.contains(secret), "manifest errors redact source details")
continue
}
throw Failure()
}
// Exercise error classification, not fake provider availability. Live proof uses the actual CLI below.
for category in [ZokoError.unavailable, .transport, .protection, .malformed] {
let result = await SourceCheck.run(mediaID: "1", episode: 1, resolve: { _, _ in throw category },
validate: { _ in throw Failure() })
try require(result == (category == .unavailable ? .unavailable : .failed), "only explicit missing episode is unavailable")
}
let source = ResolvedSource(url: root.absoluteString, headers: "", identity: "anilist:1:1", title: "Fixture",
subtitles: [], requirements: .init(zoko: true))
let resourceFailure = await SourceCheck.run(mediaID: "1", episode: 1, resolve: { _, _ in source },
validate: { _ in throw URLError(.badServerResponse, userInfo: [NSURLErrorFailingURLStringErrorKey: root.absoluteString]) })
try require(resourceFailure == .failed && !resourceFailure.json.contains(secret), "resource errors are failed and private")
let deadline = await SourceCheck.run(mediaID: "1", episode: 1, timeout: .zero, resolve: { _, _ in
try await Task.sleep(for: .seconds(60))
return source
}, validate: { _ in throw Failure() })
try require(deadline == .failed, "total deadline cancels pending work")
let cancelled = Task {
await SourceCheck.run(mediaID: "1", episode: 1, resolve: { _, _ in
try await Task.sleep(for: .seconds(60))
return source
}, validate: { _ in throw Failure() })
}
cancelled.cancel()
let cancelledResult = await cancelled.value
try require(cancelledResult == .failed, "caller cancellation stops source check")
print("PASS: source command isolation, redaction, bounded JSON, shared manifest validation, error taxonomy, total deadline and cancellation")
}
}
+1 -1
View File
@@ -46,7 +46,7 @@ struct ZokoCheck {
let missing = "<span>// Error</span><span>404</span><h1>Not <span>found.</span></h1><p>We couldn't find anything to play here.</p>"
try expect(.unavailable) { _ = try ZokoResolver.parse(page: missing, mediaID: "1", episode: 27) }
for protected in ["<title>Just a moment</title>", "<script src='/cdn-cgi/challenge-platform/x'></script>",
"<h1>Sorry, you have been blocked</h1>"] {
"<h1>Sorry, you have been blocked</h1>", missing + "<div>captcha</div>"] {
try expect(.protection) { _ = try ZokoResolver.parse(page: protected, mediaID: "1", episode: 1) }
}
for malformed in ["<html></html>", "window.__P='broken';", try page(config) + page(config), String(repeating: "x", count: ZokoResolver.bodyLimit + 1)] {