fix(relay): protect sessions against replayed commands (#7610)

Protect relay sessions against replayed commands with authenticated Noise NK negotiation, ordered records, and identity-bound security pins. Preserve legacy admission during migration and add daemon enforcement policy.

All 21 CI checks passed. General engineering review is complete; specialist cryptographic review of the handwritten Noise implementation and static-key reuse remains required before release or deployment.

Closes #359
This commit is contained in:
Mohamed Boudra
2026-10-22 19:50:06 +07:00
committed by GitHub
parent 91d749f82c
commit 793b16c0df
55 changed files with 4887 additions and 619 deletions
@@ -14,7 +14,7 @@ import { join } from "path";
import { win32 } from "node:path";
import { tmpdir } from "os";
import pino from "pino";
import { base64EncryptedWireByteLength } from "@getpaseo/relay";
import { base64EncryptedWireByteLength, maxSecuredPlaintextByteLength } from "@getpaseo/relay";
import {
__resetCheckoutShortstatCacheForTests,
__resetPullRequestStatusCacheForTests,
@@ -1654,9 +1654,14 @@ const x = 1;
CHECKOUT_DIFF_MAX_STRUCTURED_BYTES + frameEnvelopeHeadroomBytes,
);
expect(CHECKOUT_DIFF_MAX_STRUCTURED_BYTES).toBe(24_117_208);
// The cap is sized for the tighter of the legacy and protected relay framings.
expect(CHECKOUT_DIFF_MAX_STRUCTURED_BYTES).toBe(
maxSecuredPlaintextByteLength(relayFrameBytes) - frameEnvelopeHeadroomBytes,
);
expect(CHECKOUT_DIFF_MAX_STRUCTURED_BYTES).toBeLessThanOrEqual(24_117_208);
expect(CHECKOUT_DIFF_MAX_STRUCTURED_BYTES).toBeGreaterThan(24_000_000);
expect(diffWireBytes).toBeLessThan(relayFrameBytes);
expect(maximumFrameWireBytes).toBe(relayFrameBytes);
expect(maximumFrameWireBytes).toBeLessThanOrEqual(relayFrameBytes);
});
it("marks tracked generated one-line diffs as too_large by content size", async () => {
+2 -2
View File
@@ -7,7 +7,7 @@ import { setImmediate } from "node:timers/promises";
import { TTLCache } from "@isaacs/ttlcache";
import type { CheckoutCommit, CheckoutCommitFile } from "@getpaseo/protocol/messages";
import { parseGitHubRemoteIdentity, parseGitRemoteLocation } from "@getpaseo/protocol/git-remote";
import { maxBase64EncryptedPlaintextByteLength } from "@getpaseo/relay";
import { maxSecuredPlaintextByteLength } from "@getpaseo/relay";
import type { Logger } from "pino";
import type { ParsedDiffFile } from "../server/utils/diff-highlighter.js";
import {
@@ -2162,7 +2162,7 @@ const CHECKOUT_DIFF_FRAME_HEADROOM_BYTES = 1024 * 1024;
// binding: string frames are encrypted and base64-encoded. Reserve 1 MiB plaintext for
// the surrounding WebSocket JSON envelope after inverting that exact wire expansion.
export const CHECKOUT_DIFF_MAX_STRUCTURED_BYTES =
maxBase64EncryptedPlaintextByteLength(MAX_RELAY_WEBSOCKET_MESSAGE_BYTES) -
maxSecuredPlaintextByteLength(MAX_RELAY_WEBSOCKET_MESSAGE_BYTES) -
CHECKOUT_DIFF_FRAME_HEADROOM_BYTES;
interface StructuredDiffAccumulator {