fix(security): resolve all 17 codex findings — auth, grants, crypto, ops
Critical: broker HTTP auth via cli_session bearer token on all /cli/*; file download requires auth+membership; v2 claim gated; duplicate claimInviteV2Core removed; grant enforcement tries member then session pubkey; audit hash uses canonical sorted-keys JSON. High: rate limit args fixed (burst 10, 60/min) + both buckets swept; BROKER_ENCRYPTION_KEY fail-fast in prod; migrate uses pg_try + lock_ timeout; hello validates sessionPubkey hex; blocked DMs rejected pre- queue; watch timers cleaned on disconnect. Medium: inbound pushes serialized; reconnect jitter + timer guard; hardcoded URLs through env; v2 claim path configurable. Low: WSHelloMessage optional protocolVersion+capabilities. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,29 @@ const lastHash = new Map<string, string>();
|
||||
// Core audit logging
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Deterministic JSON serialization: keys sorted recursively. The store
|
||||
* is JSONB, which does NOT preserve key order, so hashing a naive
|
||||
* JSON.stringify(row.payload) on verify can yield a different string
|
||||
* from insert-time — false tamper reports. Canonical form guarantees
|
||||
* both sides agree.
|
||||
*/
|
||||
function canonicalJson(value: unknown): string {
|
||||
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
||||
if (Array.isArray(value)) {
|
||||
return "[" + value.map(canonicalJson).join(",") + "]";
|
||||
}
|
||||
const obj = value as Record<string, unknown>;
|
||||
const keys = Object.keys(obj).sort();
|
||||
return (
|
||||
"{" +
|
||||
keys
|
||||
.map((k) => JSON.stringify(k) + ":" + canonicalJson(obj[k]))
|
||||
.join(",") +
|
||||
"}"
|
||||
);
|
||||
}
|
||||
|
||||
function computeHash(
|
||||
prevHash: string,
|
||||
meshId: string,
|
||||
@@ -33,7 +56,7 @@ function computeHash(
|
||||
payload: Record<string, unknown>,
|
||||
createdAt: Date,
|
||||
): string {
|
||||
const input = `${prevHash}|${meshId}|${eventType}|${actorMemberId}|${JSON.stringify(payload)}|${createdAt.toISOString()}`;
|
||||
const input = `${prevHash}|${meshId}|${eventType}|${actorMemberId}|${canonicalJson(payload)}|${createdAt.toISOString()}`;
|
||||
return createHash("sha256").update(input).digest("hex");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user