Commit Graph
6 Commits
Author SHA1 Message Date
Moheeb Zara 42fd3859a4 fix(door): defeat DNS-rebind by connecting to a validated resolved IP (D12)
Deferred deeper-audit P1. validateDoorTarget only string-checked the
host; a bare fetch did its own DNS resolution, so a hostname could
resolve to 127.0.0.1/169.254.169.254 between check and connect. The
executor now resolves once (dns.lookup all), rejects if ANY resolved
address is blocked (isBlockedDoorIp: loopback/unspecified/link-local/
metadata incl IPv4-mapped IPv6; RFC1918/LAN/ULA allowed since door
controllers live there), then connects to the validated IP literal so
fetch performs no second resolution (TOCTOU closed). Original Host kept
best-effort; redirect:'manual' retained. Dependency-free (no undici
seam). 33 door-logic tests; suite 532.
2026-05-19 01:33:01 -07:00
Moheeb Zara 66cb84bc57 fix(door): never return raw controller reason to the client (D10)
Every door action bubbled the executor's reason/e.message to the
client; redaction was password-literal + a fixed param-name regex, so a
generic-adapter connection with a custom auth param + verbose upstream
error could surface the secret client-side. Client returns are now a
generic 'controller call failed' message; the redacted detail still
goes to door_access_log for operators. redactDoorSecrets + callDoor
gained an optional authParam (scrub the connection's actual auth param
value regardless of name); threading conn.auth_param through the
door-call selects is a deferred defense-in-depth follow-up (client
leak is fully closed; native HeatSync e= is already regex-covered).
2026-05-18 18:44:54 -07:00
Moheeb Zara 1e1b93e8c4 harden(door): correct IPv6 pin handling via pure normalizeHost
The SSRF pin parser did pinnedHost.split(':')[0], which mangled any
IPv6 pin (e.g. '::1' -> '') so IPv6 controllers could never be pinned
(failed closed). New pure, exhaustively-tested normalizeHost strips
scheme/userinfo/port/path and unwraps [ipv6]/[ipv6]:port/bare-ipv6,
applied to both the request host and the pin so they compare
apples-to-apples. All prior behavior preserved (IPv4, host:port,
userinfo-spoof reject, trailing-dot reject, metadata always blocked);
IPv4-mapped-IPv6 vs IPv4 pin still fails closed. 29 door-logic tests
(was 21); suite 454.
2026-05-16 22:39:25 -07:00
Moheeb Zara 1db93167db feat(door): flexible transport + clearer controller/generic API docs
The app is cloud-hosted, so a connection's target is not assumed to be
LAN-only: any pinned host (public controller/proxy or VPN-reachable LAN
device) is allowed except cloud-metadata/link-local. KNOWN_DOOR_
CONTROLLERS surfaces the controller type with source links (HeatSync/23b
native -> the heatsynclabs + zyphlar repos; otherwise generic HTTP).
The generic adapter form now explains the request model and every
{slot}/{tag}/{perm}/{door}/{pw} placeholder. Copy and the migration
comment corrected away from "LAN-only".
2026-05-16 20:38:52 -07:00
Moheeb Zara 59e9fcbd4f feat(door): P2 connections + SSRF-safe executor + native adapter + audit
Migration 035 adds door_connections (per-space controller integration:
secret_ref -> AES-256-GCM secrets vault, never a plaintext password;
pinned_host SSRF pin; native_heatsync|generic_http adapter;
allow_member_self_entry opt-in for phase 3) and door_access_log
(append-only, secrets redacted, service-client-only writes; immutable).
RLS additive/default-deny: connections CRUD = door.manage; log SELECT =
door.manage/operate.

Safety core is pure and heavily unit-tested (door-logic, suite 409):
validateDoorTarget (request host must equal the pinned host;
metadata/link-local always blocked; http(s) only), the
firmware-verified HeatSync encoders with fixed-width zero-padding
(slot 3 / perm 3 / tag 8 / 4-char password), generic template
substitution, and redactDoorSecrets. lib/door/executor.ts is the single
hardened egress: re-validates the pin, refuses redirects, caps time and
body, redacts before any audit write. The door password is read from the
secrets vault and decrypted server-side only, never returned or logged.
/door/manage (door.manage) configures connections, picks a vault secret,
and runs a safe status-only test. Sidebar Admin link added; "Learn"
moved before "People" per request. Docs updated.
2026-05-16 20:31:43 -07:00
Moheeb Zara a8892a2dc8 feat(door): P1 member access cards + door permissions
Migration 034 adds member_cards (RFID/NFC UID -> member). The card UID
is treated as a credential: door.manage-only RLS with no member SELECT
policy; the member's own "My access cards" view goes through a service
-client action that returns only count + last4 (never the raw UID).
New door.manage / door.operate permissions (group Access), seeded to
board and backfilled, introduced now for the whole Door epic. Pure
door-logic masking is unit-tested. A per-member "Cards" panel on
/members is gated to door.manage (independent of admin). No controller
calls and no anonymous path in this phase. Mirrored in schema.sql; types
and schema docs updated.
2026-05-16 20:13:22 -07:00