Harden loopback HTTP and document AMO unlisted signing.

This commit is contained in:
alexveley
2026-09-22 06:31:23 -04:00
parent 6dcf068365
commit 535b3ce837
4 changed files with 80 additions and 4 deletions
+22 -1
View File
@@ -2,6 +2,7 @@
"""Native messaging host: 127.0.0.1 HTTP <-> Firefox extension stdio."""
from __future__ import annotations
import hmac
import json
import os
import struct
@@ -21,6 +22,8 @@ if sys.platform == "win32":
HOST = "127.0.0.1"
PORT = int(os.environ.get("FAB_PORT", "17634"))
MAX_BODY = 256 * 1024
ALLOWED_HOSTS = {f"127.0.0.1:{PORT}", f"localhost:{PORT}"}
STATE_DIR = Path(os.environ.get("LOCALAPPDATA", str(Path.home()))) / "firefox-agent-bridge"
TOKEN_PATH = STATE_DIR / "token"
STDIN_LOCK = threading.Lock()
@@ -91,20 +94,36 @@ class Handler(BaseHTTPRequestHandler):
self.end_headers()
self.wfile.write(body)
def _gate_ok(self) -> bool:
"""Block browser-origin calls and non-loopback Host headers."""
origin = self.headers.get("Origin")
if origin:
self._send(403, {"ok": False, "error": "browser origin not allowed"})
return False
host = (self.headers.get("Host") or "").split("%")[0].lower()
if host not in ALLOWED_HOSTS:
self._send(403, {"ok": False, "error": "host not allowed"})
return False
return True
def _auth_ok(self) -> bool:
if not self._gate_ok():
return False
token = read_token()
if not token:
self._send(500, {"ok": False, "error": "bridge token missing; run install-native-host.ps1"})
return False
header = self.headers.get("Authorization", "")
got = header[7:].strip() if header.lower().startswith("bearer ") else ""
if got != token:
if not hmac.compare_digest(got, token):
self._send(401, {"ok": False, "error": "missing or invalid bearer token"})
return False
return True
def _json_body(self) -> dict[str, Any]:
length = int(self.headers.get("Content-Length") or "0")
if length > MAX_BODY:
raise ValueError("request body too large")
if length <= 0:
return {}
raw = self.rfile.read(length)
@@ -118,6 +137,8 @@ class Handler(BaseHTTPRequestHandler):
def do_GET(self) -> None:
parsed = urlparse(self.path)
if parsed.path == "/health":
if not self._gate_ok():
return
self._send(200, {"ok": True, "service": "firefox-agent-bridge", "port": PORT})
return
if not self._auth_ok():