FrameBridge 0.3.1 community source release
This commit is contained in:
commit
b23819fff1
1354 files changed
+343716
No files matched your search
@@ -0,0 +1,315 @@
|
||||
import base64
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import json
|
||||
import math
|
||||
import random
|
||||
import shlex
|
||||
import struct
|
||||
import unittest
|
||||
import zlib
|
||||
from pathlib import Path
|
||||
|
||||
from framebridge_expanded.real_screen.screen_frontdoor import CHUNK_BYTES, ScreenClient, ScreenError, register_framebridge_screen_tools
|
||||
|
||||
|
||||
CAPTURE_ID = "1" * 32
|
||||
SESSION_ID = "screen_" + "2" * 32
|
||||
NODE_ID = "12345678-1234-1234-1234-123456789abc"
|
||||
OTHER_NODE_ID = "87654321-4321-4321-4321-cba987654321"
|
||||
|
||||
|
||||
def png(width=100, height=40):
|
||||
def chunk(kind, data):
|
||||
return struct.pack(">I", len(data)) + kind + data + struct.pack(">I", zlib.crc32(kind + data))
|
||||
source = random.Random(20261022)
|
||||
scanlines = b"".join(b"\0" + source.randbytes(width * 3) for _ in range(height))
|
||||
return (b"\x89PNG\r\n\x1a\n" +
|
||||
chunk(b"IHDR", struct.pack(">IIBBBBB", width, height, 8, 2, 0, 0, 0)) +
|
||||
chunk(b"IDAT", zlib.compress(scanlines)) + chunk(b"IEND", b""))
|
||||
|
||||
|
||||
class Image:
|
||||
def __init__(self, data, format):
|
||||
self.data = data
|
||||
self.format = format
|
||||
|
||||
|
||||
class MCP:
|
||||
def __init__(self):
|
||||
self.tools = {}
|
||||
self.annotations = {}
|
||||
|
||||
def tool(self, annotations):
|
||||
def register(fn):
|
||||
self.tools[fn.__name__] = fn
|
||||
self.annotations[fn.__name__] = annotations
|
||||
return fn
|
||||
return register
|
||||
|
||||
|
||||
class Executor:
|
||||
def __init__(self, variant=None, hostname="fb-central-lab"):
|
||||
self.data = png()
|
||||
self.variant = variant
|
||||
self.hostname = hostname
|
||||
self.requests = []
|
||||
|
||||
def __call__(self, **kwargs):
|
||||
request = json.loads(base64.b64decode(shlex.split(kwargs["script"])[-1], validate=True))
|
||||
self.requests.append(request)
|
||||
digest = hashlib.sha256(self.data).hexdigest()
|
||||
manifest = {"capture_id": CAPTURE_ID, "node": self.hostname,
|
||||
"bytes": len(self.data), "sha256": digest, "width": 100, "height": 40,
|
||||
"source": "linux-framebuffer", "screen_type": "tty", "active_vt": "tty1",
|
||||
"session_id": SESSION_ID, "full_screen": True, "authentic_pixels": True,
|
||||
"path": "/private/internal.png"}
|
||||
if request.get("session_id"):
|
||||
manifest["requested_session_visible"] = request["session_id"] == SESSION_ID
|
||||
if self.variant == "node":
|
||||
manifest["node"] = "invalid hostname"
|
||||
elif self.variant == "source":
|
||||
manifest["source"] = "terminal-text-renderer"
|
||||
elif self.variant == "provenance":
|
||||
manifest["authentic_pixels"] = False
|
||||
elif self.variant == "dimensions":
|
||||
manifest["width"] = 99
|
||||
elif self.variant == "x11":
|
||||
manifest["source"] = "x11-root-XGetImage"
|
||||
manifest["screen_type"] = "x11"
|
||||
if request["op"] == "read_chunk":
|
||||
index = request["index"]
|
||||
data = self.data[index * CHUNK_BYTES:(index + 1) * CHUNK_BYTES]
|
||||
result = {"capture_id": CAPTURE_ID, "index": index,
|
||||
"total_chunks": math.ceil(len(self.data) / CHUNK_BYTES),
|
||||
"bytes": len(self.data), "sha256": digest, "encoding": "base64",
|
||||
"data": base64.b64encode(data).decode(),
|
||||
"more": (index + 1) * CHUNK_BYTES < len(self.data)}
|
||||
if self.variant == "chunk_index":
|
||||
result["index"] = index + 1
|
||||
elif self.variant == "chunk_hash":
|
||||
result["sha256"] = "0" * 64
|
||||
elif self.variant == "chunk_more":
|
||||
result["more"] = not result["more"]
|
||||
elif self.variant == "chunk_encoding":
|
||||
result["data"] = "?invalid?"
|
||||
elif self.variant == "chunk_length":
|
||||
result["data"] = base64.b64encode(data[:-1]).decode()
|
||||
elif self.variant == "image_hash":
|
||||
changed = bytes([data[0] ^ 1]) + data[1:]
|
||||
result["data"] = base64.b64encode(changed).decode()
|
||||
else:
|
||||
result = manifest
|
||||
error = (self.variant in ("error_exit1", "uncertain_input") or
|
||||
request["op"] in ("key", "text") and
|
||||
request.get("session_id", SESSION_ID) != SESSION_ID)
|
||||
envelope = {"ok": False, "error": {"code": "SESSION_NOT_VISIBLE"}} if error else {"ok": True, "result": result}
|
||||
if self.variant == "uncertain_input":
|
||||
envelope["error"] = {"code": "INPUT_SENT_OBSERVE_FAILED", "performed": True,
|
||||
"capture_consumed": True, "observe_again": True,
|
||||
"capture_id": CAPTURE_ID, "cause": "SCREEN_IO_FAILED",
|
||||
"text": "MUST NOT ESCAPE"}
|
||||
outer = {"ok": not error, "exit_code": "1" if error else "0",
|
||||
"target": kwargs["target"], "node_id": NODE_ID, "mcp_job_id": kwargs["job_id"],
|
||||
"mcp_script_sha256": kwargs["sha256"],
|
||||
"stdout": json.dumps(envelope), "stdout_truncated": self.variant == "truncated"}
|
||||
if self.variant == "outer_job":
|
||||
outer["mcp_job_id"] = "different"
|
||||
elif self.variant == "outer_hash":
|
||||
outer["mcp_script_sha256"] = "0" * 64
|
||||
elif self.variant == "outer_target":
|
||||
outer["target"] = "OTHER"
|
||||
elif self.variant == "outer_target_chunk" and request["op"] == "read_chunk":
|
||||
outer["target"] = "OTHER"
|
||||
elif self.variant == "outer_node_id_chunk" and request["op"] == "read_chunk":
|
||||
outer["node_id"] = OTHER_NODE_ID
|
||||
elif self.variant == "outer_node_id_missing":
|
||||
outer.pop("node_id")
|
||||
elif self.variant == "outer_node_id_invalid":
|
||||
outer["node_id"] = "invalid"
|
||||
return outer
|
||||
|
||||
|
||||
class ScreenTests(unittest.TestCase):
|
||||
def client(self, variant=None):
|
||||
executor = Executor(variant)
|
||||
return ScreenClient(executor, Image), executor
|
||||
|
||||
def test_native_image_return_preserves_bytes_and_omits_internal_path(self):
|
||||
client, executor = self.client()
|
||||
result = client.call("observe", "gerameg", True)
|
||||
self.assertEqual(result[0]["node"], "GERAMEG")
|
||||
self.assertEqual(result[1].data, executor.data)
|
||||
self.assertEqual(result[1].format, "png")
|
||||
self.assertNotIn("path", result[0]["result"])
|
||||
self.assertNotIn("image_base64", json.dumps(result[0]))
|
||||
self.assertGreater(len(executor.requests), 2)
|
||||
|
||||
def test_authorization_required_before_any_exec(self):
|
||||
client, executor = self.client()
|
||||
self.assertEqual(client.call("observe", "GERAMEG", False)["error"], "AUTHORIZED_TRUE_REQUIRED")
|
||||
self.assertEqual(executor.requests, [])
|
||||
|
||||
def test_invalid_node_cannot_reach_shell(self):
|
||||
client, executor = self.client()
|
||||
self.assertEqual(client.call("observe", "GERAMEG;id", True)["error"], "SCREEN_NODE_INVALID")
|
||||
self.assertEqual(executor.requests, [])
|
||||
|
||||
def test_visible_action_requires_capture_id(self):
|
||||
client, executor = self.client()
|
||||
self.assertEqual(client.call("key", "GERAMEG", True, key="ENTER")["error"], "SCREEN_CAPTURE_ID_REQUIRED")
|
||||
self.assertEqual(executor.requests, [])
|
||||
|
||||
def test_hidden_reference_observe_shows_full_screen_and_visibility_false(self):
|
||||
client, executor = self.client()
|
||||
result = client.call("observe", "GERAMEG", True, session_id="hidden_virtual_session")
|
||||
self.assertFalse(result[0]["result"]["requested_session_visible"])
|
||||
self.assertIsInstance(result[1], Image)
|
||||
|
||||
def test_hidden_action_error_survives_exit1_without_image(self):
|
||||
client, executor = self.client()
|
||||
result = client.call("key", "GERAMEG", True, capture_id=CAPTURE_ID,
|
||||
session_id="hidden_virtual_session", key="ENTER")
|
||||
self.assertEqual(result["error"], "SESSION_NOT_VISIBLE")
|
||||
self.assertEqual(len(executor.requests), 1)
|
||||
|
||||
def test_backend_structured_error_survives_failure_flag_and_exit1(self):
|
||||
client, executor = self.client("error_exit1")
|
||||
self.assertEqual(client.call("observe", "GERAMEG", True)["error"], "SESSION_NOT_VISIBLE")
|
||||
|
||||
def test_key_action_returns_new_real_image(self):
|
||||
client, executor = self.client()
|
||||
result = client.call("key", "GERAMEG", True, capture_id=CAPTURE_ID, key="CTRL_L")
|
||||
self.assertEqual(executor.requests[0]["op"], "key")
|
||||
self.assertEqual(result[1].data, executor.data)
|
||||
|
||||
def test_uncertain_input_details_preserved_without_payload(self):
|
||||
client, executor = self.client("uncertain_input")
|
||||
result = client.call("key", "GERAMEG", True, capture_id=CAPTURE_ID, key="ENTER")
|
||||
self.assertEqual(result["error"], "INPUT_SENT_OBSERVE_FAILED")
|
||||
self.assertIs(result["performed"], True)
|
||||
self.assertIs(result["capture_consumed"], True)
|
||||
self.assertIs(result["observe_again"], True)
|
||||
self.assertEqual(result["capture_id"], CAPTURE_ID)
|
||||
self.assertNotIn("text", result)
|
||||
self.assertEqual(len(executor.requests), 1)
|
||||
|
||||
def test_utf8_text_limit_matches_backend(self):
|
||||
client, executor = self.client()
|
||||
result = client.call("text", "GERAMEG", True, capture_id=CAPTURE_ID, text="界" * 1400)
|
||||
self.assertEqual(result["error"], "SCREEN_TEXT_INVALID")
|
||||
self.assertEqual(executor.requests, [])
|
||||
|
||||
def test_text_is_base64_transport_and_not_shell_interpolation(self):
|
||||
client, executor = self.client()
|
||||
text = "literal $(touch /tmp/no) `id` ' quote"
|
||||
result = client.call("text", "GERAMEG", True, capture_id=CAPTURE_ID, text=text)
|
||||
self.assertEqual(executor.requests[0]["text"], text)
|
||||
self.assertIsInstance(result[1], Image)
|
||||
|
||||
def test_integrity_rejections(self):
|
||||
expected = {"outer_job": "SCREEN_EXEC_BINDING_INVALID",
|
||||
"outer_hash": "SCREEN_EXEC_BINDING_INVALID",
|
||||
"outer_target": "SCREEN_EXEC_BINDING_INVALID",
|
||||
"truncated": "SCREEN_STDOUT_INVALID", "node": "SCREEN_IMAGE_HOSTNAME_INVALID",
|
||||
"outer_target_chunk": "SCREEN_EXEC_BINDING_INVALID",
|
||||
"outer_node_id_chunk": "SCREEN_CHUNK_NODE_MISMATCH",
|
||||
"outer_node_id_missing": "SCREEN_EXEC_NODE_ID_INVALID",
|
||||
"outer_node_id_invalid": "SCREEN_EXEC_NODE_ID_INVALID",
|
||||
"source": "SCREEN_PIXEL_SOURCE_INVALID", "provenance": "SCREEN_PIXEL_PROVENANCE_INVALID",
|
||||
"dimensions": "SCREEN_PNG_INVALID", "chunk_index": "SCREEN_CHUNK_BINDING_INVALID",
|
||||
"chunk_hash": "SCREEN_CHUNK_MANIFEST_INVALID", "chunk_more": "SCREEN_CHUNK_MANIFEST_INVALID",
|
||||
"chunk_encoding": "SCREEN_CHUNK_INVALID", "chunk_length": "SCREEN_CHUNK_LENGTH_INVALID",
|
||||
"image_hash": "SCREEN_IMAGE_HASH_MISMATCH"}
|
||||
for variant, code in expected.items():
|
||||
with self.subTest(variant=variant):
|
||||
client, _ = self.client(variant)
|
||||
self.assertEqual(client.call("observe", "GERAMEG", True)["error"], code)
|
||||
|
||||
def test_generic_node_is_passed_to_existing_executor(self):
|
||||
client, executor = self.client()
|
||||
self.assertEqual(client.call("observe", "ASUS", True)[0]["node"], "ASUS")
|
||||
|
||||
def test_exact_x11_capture_source_is_accepted(self):
|
||||
client, executor = self.client("x11")
|
||||
result = client.call("observe", "ASUS", True)
|
||||
self.assertEqual(result[0]["result"]["source"], "x11-root-XGetImage")
|
||||
self.assertEqual(result[1].data, executor.data)
|
||||
|
||||
def test_trusted_alias_or_uuid_resolver_is_injected(self):
|
||||
executor = Executor()
|
||||
client = ScreenClient(executor, Image, lambda node: "GERAMEG")
|
||||
self.assertEqual(client.call("observe", "node-alias", True)[0]["node"], "GERAMEG")
|
||||
|
||||
def test_registration_annotations_and_callable_functions(self):
|
||||
mcp = MCP()
|
||||
register_framebridge_screen_tools(mcp, Executor(), image_factory=Image)
|
||||
self.assertEqual(set(mcp.tools), {"framebridge_screen_observe", "framebridge_screen_key", "framebridge_screen_text"})
|
||||
self.assertTrue(mcp.annotations["framebridge_screen_observe"]["readOnlyHint"])
|
||||
self.assertFalse(mcp.annotations["framebridge_screen_key"]["readOnlyHint"])
|
||||
self.assertIsInstance(mcp.tools["framebridge_screen_observe"]("GERAMEG", True)[1], Image)
|
||||
|
||||
def test_local_route_preserves_distinct_real_hostname_and_uuid(self):
|
||||
executor = Executor(hostname="fb-central-lab")
|
||||
client = ScreenClient(executor, Image, lambda alias: "LOCAL")
|
||||
result = client.call("observe", "LOCAL", True)
|
||||
self.assertEqual(result[0]["node"], "LOCAL")
|
||||
self.assertEqual(result[0]["hostname"], "fb-central-lab")
|
||||
self.assertEqual(result[0]["node_id"], NODE_ID)
|
||||
self.assertEqual(result[0]["result"]["node"], "fb-central-lab")
|
||||
self.assertEqual(result[0]["result"]["transport_node"], "LOCAL")
|
||||
self.assertEqual(result[1].data, executor.data)
|
||||
|
||||
def test_remote_uuid_alias_and_hostname_remain_distinct(self):
|
||||
executor = Executor(hostname="fb-node-lab")
|
||||
client = ScreenClient(executor, Image, lambda alias: "fb-node")
|
||||
result = client.call("observe", NODE_ID, True)
|
||||
self.assertEqual(result[0]["requested_node"], NODE_ID)
|
||||
self.assertEqual(result[0]["node"], "fb-node")
|
||||
self.assertEqual(result[0]["hostname"], "fb-node-lab")
|
||||
self.assertEqual(result[0]["result"]["node"], "fb-node-lab")
|
||||
self.assertEqual(result[0]["result"]["transport_node"], "fb-node")
|
||||
self.assertEqual(result[1].data, executor.data)
|
||||
|
||||
def test_screen_action_keeps_capture_and_session_with_distinct_hostname(self):
|
||||
executor = Executor(hostname="fb-node-lab")
|
||||
client = ScreenClient(executor, Image, lambda alias: "fb-node")
|
||||
result = client.call("key", "fb-node", True, capture_id=CAPTURE_ID,
|
||||
session_id=SESSION_ID, key="ENTER")
|
||||
self.assertEqual(executor.requests[0]["capture_id"], CAPTURE_ID)
|
||||
self.assertEqual(executor.requests[0]["session_id"], SESSION_ID)
|
||||
self.assertEqual(result[0]["hostname"], "fb-node-lab")
|
||||
self.assertEqual(result[1].data, executor.data)
|
||||
|
||||
def test_arbitrary_dictionary_cannot_be_treated_as_authenticated_manifest(self):
|
||||
client, _ = self.client()
|
||||
verified = client.remote("LOCAL", {"op": "observe"})
|
||||
with self.assertRaisesRegex(ScreenError, "SCREEN_IMAGE_NODE_MISMATCH"):
|
||||
client.image("LOCAL", dict(verified))
|
||||
|
||||
def test_another_client_cannot_supply_authenticated_manifest(self):
|
||||
first, _ = self.client()
|
||||
second, _ = self.client()
|
||||
verified = first.remote("LOCAL", {"op": "observe"})
|
||||
with self.assertRaisesRegex(ScreenError, "SCREEN_IMAGE_NODE_MISMATCH"):
|
||||
second.image("LOCAL", verified)
|
||||
|
||||
def test_authenticated_hostname_cannot_be_changed_after_transport_validation(self):
|
||||
client, _ = self.client()
|
||||
verified = client.remote("LOCAL", {"op": "observe"})
|
||||
verified["node"] = "forged-hostname"
|
||||
with self.assertRaisesRegex(ScreenError, "SCREEN_IMAGE_NODE_MISMATCH"):
|
||||
client.image("LOCAL", verified)
|
||||
|
||||
def test_hostname_must_be_a_bounded_nonempty_string(self):
|
||||
for hostname in ("", None, 123, "invalid hostname", "x" * 81):
|
||||
with self.subTest(hostname=hostname):
|
||||
executor = Executor(hostname=hostname)
|
||||
client = ScreenClient(executor, Image, lambda alias: "LOCAL")
|
||||
self.assertEqual(client.call("observe", "LOCAL", True)["error"],
|
||||
"SCREEN_IMAGE_HOSTNAME_INVALID")
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user