Files
Martin Vogel d58afe562d revert(release): re-embed runtime assets into the single shipped binary
Externalizing the integration templates (#1492/#1493) and the UI bundle
(#1501/#1503) was done to reduce the Microsoft `Wacatac.B!ml` surface. It did
not work: across dry runs the flagged artifact count stayed at ~3 and the
detections merely moved between artifacts.

Dissection of run 31286803592 shows there is no structural cause to fix. The
verdicts split across every axis at once — linux-amd64 (dynamic) flagged while
linux-amd64-portable (static) is clean, but linux-arm64 (dynamic) clean while
linux-arm64-portable (static) is flagged. The two macOS binaries have identical
segment structure and split clean/flagged. Siblings from one build landed in
different variant buckets (.B vs .C). Entropy is low everywhere
(code_vectors.bin 4.166, grammar tables 3.464 bits/byte, against 7.5-8.0 for
packed payloads), so the packed-payload hypothesis is excluded too.

So the complexity bought nothing, and installation goes back to being
self-contained: one binary that carries its own UI and agent integration
templates, with no adjacent data file that has to resolve before `install`
works. Only the UI-capable composition ships from now on, under the historical
unsuffixed archive name.

Removed: src/ui/asset_pack.{c,h}, asset_pack_stub.c, asset_manifest_stub.c,
scripts/pack-ui-assets.mjs, src/cli/integration_assets.{c,h},
assets/cbm-integrations.json, scripts/gen-integrations-hash.sh, the
--verify-runtime-assets probe (nothing adjacent left to verify), and the
composition gates A6/A7 whose property is now deliberately inverted.

Restored: scripts/embed-frontend.sh, src/ui/embedded_{assets.h,stub.c}, the
compiled-in hook/adapter template bodies, and the embed/EMBED_OBJS build path.

Kept from the reverted commits, re-applied by hand where a wholesale file
restore would have dropped them:
  - cbm_module_path_utf8() in both self-path sites. GetModuleFileNameA renders
    through the ANSI code page and mangles non-ASCII install paths.
  - the /__cbm/ui-readiness HMAC proof, secure_random and cbm_hmac_sha256, so
    `daemon start --open` still waits for a genuine CBM listener.
  - X-Content-Type-Options: nosniff on served assets.
  - the MinGW noexecstack gate, -lbcrypt, and the cppcheck/zip CI fixes.

Archives are now codebase-memory-mcp-<os>-<arch>[-portable] with exactly four
members (binary, LICENSE, installer, THIRD_PARTY_NOTICES.md). That restores the
names every static package manifest already points at — aur, chocolatey,
homebrew, scoop, winget and glama were all broken by the -ui- rename.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-09 13:06:42 +02:00

361 lines
14 KiB
Python

r"""Product guard for ``daemon start --open`` UI readiness.
The control socket becomes available before the HTTP listener does. ``--open``
is the one synchronous user request in that flow: it must wait for the *CBM HTTP
endpoint*, not merely for the daemon process, before it reports or opens the URL.
This guard uses a real UI build and isolated daemon generations:
* a temporarily occupied port proves the command waits and then succeeds once
the verified UI listener becomes available;
* a foreign service returning the exact formerly accepted HTML markers proves
public page text is not treated as daemon identity;
* a second ``daemon start --open`` proves the already-active daemon path obtains
the same generation-bound proof over a fresh authenticated IPC client.
Build the fixture with test seams so browser launching is recorded rather than
performed and the two negative cases can use a short deterministic deadline:
make -f Makefile.cbm cbm-with-ui TEST_SEAMS=1 BUILD_DIR=build/ui-open-test
python3 tests/test_daemon_open_readiness.py build/ui-open-test/codebase-memory-mcp
Exit code: 0 == green, 1 == behavior regression, 2 == fixture/setup error.
"""
import os
import re
import shutil
import socket
import stat
import subprocess
import sys
import tempfile
import threading
import time
OPEN_MARKER_ENV = "CBM_TEST_DAEMON_OPEN_MARKER"
READY_TIMEOUT_ENV = "CBM_TEST_DAEMON_UI_READY_TIMEOUT_MS"
RUNTIME_PARENT_ENV = "CBM_TEST_DAEMON_RUNTIME_PARENT"
def output_text(result):
return ((result.stdout or b"") + (result.stderr or b"")).decode("utf-8", "replace")
def pid_from(text):
match = re.search(r"pid[: ]+(\d+)", text)
return int(match.group(1)) if match else 0
def force_kill(pid):
if not pid:
return
if os.name == "nt":
subprocess.run(["taskkill", "/F", "/PID", str(pid)], capture_output=True, timeout=30)
else:
subprocess.run(["kill", "-9", str(pid)], capture_output=True, timeout=30)
def stop_daemon(binary, env, pid):
stopped = False
try:
subprocess.run([binary, "daemon", "stop"], capture_output=True, timeout=30, env=env)
except (OSError, subprocess.TimeoutExpired):
pass
deadline = time.monotonic() + 5
while time.monotonic() < deadline:
try:
status = subprocess.run([binary, "daemon", "status"], capture_output=True,
timeout=2, env=env)
if status.returncode != 0 and "not running" in output_text(status):
stopped = True
break
except (OSError, subprocess.TimeoutExpired):
pass
time.sleep(0.1)
if not stopped:
force_kill(pid)
def occupied_loopback_port():
listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if os.name != "nt":
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
listener.bind(("127.0.0.1", 0))
listener.listen(16)
return listener, listener.getsockname()[1]
class OldMarkerResponder:
"""Foreign HTTP service that exactly matches the former acceptance rule."""
BODY = b"<!doctype html><html><head><title>Codebase Memory</title></head></html>"
def __init__(self):
self.listener, self.port = occupied_loopback_port()
self.listener.settimeout(0.1)
self.stop_event = threading.Event()
self.request_count = 0
self.thread = threading.Thread(target=self._serve, name="old-marker-responder",
daemon=True)
self.thread.start()
def _serve(self):
response = (
b"HTTP/1.1 200 OK\r\n"
b"Content-Type: text/html; charset=utf-8\r\n"
b"Cache-Control: no-cache\r\n"
b"Content-Length: " + str(len(self.BODY)).encode("ascii") + b"\r\n"
b"Connection: close\r\n\r\n" + self.BODY
)
while not self.stop_event.is_set():
try:
connection, _ = self.listener.accept()
except socket.timeout:
continue
except OSError:
break
try:
connection.settimeout(0.5)
self.request_count += 1
try:
connection.recv(4096)
except (OSError, socket.timeout):
pass
connection.sendall(response)
except OSError:
pass
finally:
connection.close()
def close(self):
self.stop_event.set()
self.listener.close()
self.thread.join(timeout=2)
def fixture_environment(work, cache, marker, timeout_ms):
env = dict(os.environ)
env["CBM_CACHE_DIR"] = cache
env[OPEN_MARKER_ENV] = marker
env[READY_TIMEOUT_ENV] = str(timeout_ms)
runtime_parent = os.path.join(work, "runtime-" + os.path.basename(cache))
os.makedirs(runtime_parent, mode=0o700, exist_ok=True)
env[RUNTIME_PARENT_ENV] = runtime_parent
env["CBM_TEST_FAKE_BROWSER_MARKER"] = marker
# Before the production fix exists, POSIX builds do not know the marker
# seam. A PATH-local opener lets the very same behavior test demonstrate
# the original premature-open RED instead of failing during setup.
if os.name != "nt":
fake_bin = os.path.join(work, "fake-bin")
os.makedirs(fake_bin, exist_ok=True)
opener = "open" if sys.platform == "darwin" else "xdg-open"
opener_path = os.path.join(fake_bin, opener)
if not os.path.exists(opener_path):
with open(opener_path, "w", encoding="utf-8", newline="\n") as handle:
handle.write("#!/bin/sh\nprintf '%s' \"$1\" > \"$CBM_TEST_FAKE_BROWSER_MARKER\"\n")
os.chmod(opener_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
env["PATH"] = fake_bin + os.pathsep + env.get("PATH", "")
return env
def launch_start(binary, work, cache, marker, port, timeout_ms):
os.makedirs(cache, exist_ok=True)
env = fixture_environment(work, cache, marker, timeout_ms)
process = subprocess.Popen(
[binary, "daemon", "start", "--port=%d" % port, "--open"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env,
)
return process, env
def collect(process, timeout=30):
stdout, stderr = process.communicate(timeout=timeout)
result = subprocess.CompletedProcess(process.args, process.returncode, stdout, stderr)
return result, output_text(result)
def wait_daemon_active(binary, env, process, timeout=10):
deadline = time.monotonic() + timeout
last_probe = "status probe was not attempted"
while time.monotonic() < deadline:
if process.poll() is not None:
return False
try:
status = subprocess.run([binary, "daemon", "status"], capture_output=True,
timeout=2, env=env)
last_probe = "status rc=%d: %s" % (status.returncode, output_text(status)[:600])
except subprocess.TimeoutExpired as timeout_error:
# The starter may still own the short bootstrap handoff. A timed
# status probe is not evidence either way; retry within this
# function's independent bounded deadline.
partial = (timeout_error.stdout or b"") + (timeout_error.stderr or b"")
last_probe = "status timed out: %s" % partial.decode("utf-8", "replace")[:600]
continue
if status.returncode == 0 and "daemon: active" in output_text(status):
return True
time.sleep(0.1)
print("diagnostic: %s" % last_probe)
return False
def assert_delayed_success(binary, work):
cache = os.path.join(work, "cache-delayed")
marker = os.path.join(work, "browser-delayed.txt")
blocker, port = occupied_loopback_port()
process, env = launch_start(binary, work, cache, marker, port, 10000)
daemon_pid = 0
try:
if not wait_daemon_active(binary, env, process):
result, text = collect(process)
daemon_pid = pid_from(text)
print("RED: daemon control service never became active during --open:\n%s"
% text[:600])
return False
# Generic daemon readiness is explicitly not UI readiness. Once status
# is active, leave enough time for the buggy command to report/open;
# the fixed command remains blocked on the authenticated HTTP probe.
time.sleep(0.4)
if process.poll() is not None or os.path.exists(marker):
result, text = collect(process)
daemon_pid = pid_from(text)
print("RED: --open returned/opened while another listener still owned the UI port:\n%s"
% text[:600])
return False
# The daemon's first bind failed without delaying its control service.
# Releasing the port lets the background retry publish the real CBM UI.
blocker.close()
blocker = None
result, text = collect(process, timeout=25)
daemon_pid = pid_from(text)
expected_url = "http://127.0.0.1:%d" % port
marker_text = ""
if os.path.exists(marker):
with open(marker, "r", encoding="utf-8") as handle:
marker_text = handle.read()
if result.returncode != 0 or expected_url not in text or marker_text != expected_url:
print("RED: --open did not wait through the background UI retry and then open the "
"verified endpoint:\n%s\nmarker=%r" % (text[:700], marker_text))
return False
print("PASS: --open waited for the verified UI endpoint before reporting/opening it")
return True
finally:
if blocker is not None:
blocker.close()
if process.poll() is None:
process.kill()
_, text = collect(process)
daemon_pid = daemon_pid or pid_from(text)
stop_daemon(binary, env, daemon_pid)
def assert_bounded_foreign_port_failure(binary, work):
cache = os.path.join(work, "cache-occupied")
marker = os.path.join(work, "browser-occupied.txt")
blocker = OldMarkerResponder()
port = blocker.port
process, env = launch_start(binary, work, cache, marker, port, 900)
daemon_pid = 0
try:
result, text = collect(process, timeout=10)
daemon_pid = pid_from(text)
url = "http://127.0.0.1:%d" % port
if (result.returncode == 0 or os.path.exists(marker) or url in text or
"UI endpoint did not become ready" not in text or "--port" not in text):
print("RED: --open must reject a bounded wait on a foreign/occupied endpoint "
"without reporting or opening its URL:\n%s" % text[:700])
return False
request_count = blocker.request_count
status = subprocess.run([binary, "daemon", "status"], capture_output=True,
timeout=3, env=env)
status_text = output_text(status)
if (status.returncode != 0 or "daemon: active" not in status_text or
blocker.request_count != request_count):
print("RED: daemon status must remain a bounded control-plane probe and must not "
"contact a configured foreign HTTP listener:\n%s\nrequests=%d->%d" %
(status_text[:700], request_count, blocker.request_count))
return False
print("PASS: exact legacy HTML markers cannot spoof daemon-generation readiness")
return True
finally:
blocker.close()
if process.poll() is None:
process.kill()
_, text = collect(process)
daemon_pid = daemon_pid or pid_from(text)
stop_daemon(binary, env, daemon_pid)
def assert_active_daemon_open(binary, work):
cache = os.path.join(work, "cache-active")
marker = os.path.join(work, "browser-active.txt")
probe, port = occupied_loopback_port()
probe.close()
os.makedirs(cache, exist_ok=True)
env = fixture_environment(work, cache, marker, 10000)
daemon_pid = 0
try:
first = subprocess.run([binary, "daemon", "start", "--port=%d" % port],
capture_output=True, timeout=30, env=env)
first_text = output_text(first)
daemon_pid = pid_from(first_text)
if first.returncode != 0 or "daemon: started" not in first_text:
print("RED: initial asynchronous daemon start failed:\n%s" % first_text[:700])
return False
second = subprocess.run([binary, "daemon", "start", "--open"], capture_output=True,
timeout=20, env=env)
second_text = output_text(second)
expected_url = "http://127.0.0.1:%d" % port
marker_text = ""
if os.path.exists(marker):
with open(marker, "r", encoding="utf-8") as handle:
marker_text = handle.read()
if (second.returncode != 0 or "daemon: already active" not in second_text or
expected_url not in second_text or marker_text != expected_url):
print("RED: active-daemon --open did not authenticate and open its exact "
"generation:\n%s\nmarker=%r" % (second_text[:700], marker_text))
return False
print("PASS: an already-active daemon is opened only after generation-bound proof")
return True
finally:
stop_daemon(binary, env, daemon_pid)
def main():
if len(sys.argv) != 2:
print("usage: python3 test_daemon_open_readiness.py <ui-binary>")
return 2
binary = os.path.abspath(sys.argv[1])
if not os.path.isfile(binary):
print("SETUP FAIL: binary not found: %s" % binary)
return 2
if os.name == "nt":
with open(binary, "rb") as handle:
if OPEN_MARKER_ENV.encode("ascii") not in handle.read():
print("SETUP FAIL: Windows fixture must be built with TEST_SEAMS=1")
return 2
# macOS sockaddr_un paths are capped at 104 bytes. Anchor the isolated
# runtime directly under /private/tmp rather than the much longer per-user
# Darwin temporary root so the product endpoint itself remains valid.
short_temp_root = "/private/tmp" if sys.platform == "darwin" else tempfile.gettempdir()
with tempfile.TemporaryDirectory(prefix="cbm_uiopen_", dir=short_temp_root) as work:
if not assert_delayed_success(binary, work):
return 1
if not assert_bounded_foreign_port_failure(binary, work):
return 1
if not assert_active_daemon_open(binary, work):
return 1
print("\nGREEN: daemon --open is bound to verified UI endpoint readiness.")
return 0
if __name__ == "__main__":
sys.exit(main())