From 25c908ac063cfa076f38767428b30fd20565f432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Javier=20Garc=C3=ADa=20L=C3=B3pez?= <6173685+LaloLalo1999@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:18:38 -0600 Subject: [PATCH 01/12] fix(AGE-65): address PR #62 review feedback - Validate source and externalRef in isBacklogIssueLike (Codex review). Addresses: - https://github.com/Agentic-Engineering-Agency/code-colony/pull/62#discussion_r3160579723 --- __tests__/triageRouteCore.test.ts | 2 ++ src/server/triageRouteCore.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/__tests__/triageRouteCore.test.ts b/__tests__/triageRouteCore.test.ts index 101dc9d..12bba44 100644 --- a/__tests__/triageRouteCore.test.ts +++ b/__tests__/triageRouteCore.test.ts @@ -43,6 +43,8 @@ describe("triage route core", () => { it("isBacklogIssueLike validates required fields", () => { expect(isBacklogIssueLike(sampleIssue)).toBe(true); expect(isBacklogIssueLike({ ...sampleIssue, id: 42 })).toBe(false); + expect(isBacklogIssueLike({ ...sampleIssue, source: 42 })).toBe(false); + expect(isBacklogIssueLike({ ...sampleIssue, externalRef: 42 })).toBe(false); expect(isBacklogIssueLike(null)).toBe(false); }); diff --git a/src/server/triageRouteCore.ts b/src/server/triageRouteCore.ts index a338345..65d4d7f 100644 --- a/src/server/triageRouteCore.ts +++ b/src/server/triageRouteCore.ts @@ -25,6 +25,8 @@ export function isBacklogIssueLike(value: unknown): value is BacklogIssue { const candidate = value as Partial; return ( typeof candidate.id === "string" && + typeof candidate.source === "string" && + typeof candidate.externalRef === "string" && typeof candidate.title === "string" && typeof candidate.summary === "string" && typeof candidate.suggestedRegionId === "string" From 17cbd8340a91eceab80e0442ed1902183659cd6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Javier=20Garc=C3=ADa=20L=C3=B3pez?= <6173685+LaloLalo1999@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:22:28 -0600 Subject: [PATCH 02/12] fix(AGE-65): address PR #77 review feedback - Add MAX_BODY_BYTES (1MB) limit to verifyHmac to prevent DoS (Copilot). - Cache imported CryptoKey in signHmac to reduce per-request latency (Copilot). - Remove unused ExecRequest import from index.ts (Copilot). - Redact credentials from repoUrl in /git-clone response (Copilot). Addresses: - https://github.com/Agentic-Engineering-Agency/code-colony/pull/77#discussion_r3160599004 - https://github.com/Agentic-Engineering-Agency/code-colony/pull/77#discussion_r3160599028 - https://github.com/Agentic-Engineering-Agency/code-colony/pull/77#discussion_r3160599053 - https://github.com/Agentic-Engineering-Agency/code-colony/pull/77#discussion_r3160599080 - https://github.com/Agentic-Engineering-Agency/code-colony/pull/77#discussion_r3160599104 --- apps/sandbox-runner/__tests__/auth.test.ts | 11 ++++++ apps/sandbox-runner/src/auth.ts | 40 ++++++++++++++++++---- apps/sandbox-runner/src/index.ts | 16 +++++++-- 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/apps/sandbox-runner/__tests__/auth.test.ts b/apps/sandbox-runner/__tests__/auth.test.ts index ee73011..7b1a922 100644 --- a/apps/sandbox-runner/__tests__/auth.test.ts +++ b/apps/sandbox-runner/__tests__/auth.test.ts @@ -139,4 +139,15 @@ describe("verifyHmac", () => { expect(result.ok).toBe(false); if (!result.ok) expect(result.error).toBe("invalid timestamp"); }); + + it("rejects bodies larger than MAX_BODY_BYTES", async () => { + const bigBody = "x".repeat(1 * 1024 * 1024 + 1); + const req = await signedRequest(bigBody); + const result = await verifyHmac(req, SECRET, { now: () => NOW }); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error).toBe("body too large"); + expect(result.status).toBe(413); + } + }); }); diff --git a/apps/sandbox-runner/src/auth.ts b/apps/sandbox-runner/src/auth.ts index d682857..b43ac5d 100644 --- a/apps/sandbox-runner/src/auth.ts +++ b/apps/sandbox-runner/src/auth.ts @@ -19,6 +19,26 @@ const ENCODER = new TextEncoder(); export const TIMESTAMP_HEADER = "x-sandbox-ts"; export const SIGNATURE_HEADER = "x-sandbox-sig"; export const DEFAULT_SKEW_MS = 5 * 60 * 1000; +export const MAX_BODY_BYTES = 1 * 1024 * 1024; + + +const keyCache = new Map(); + +async function importKeyCached(secret: string): Promise { + let key = keyCache.get(secret); + if (!key) { + key = await crypto.subtle.importKey( + "raw", + ENCODER.encode(secret), + { name: "HMAC", hash: "SHA-256" }, + false, + ["sign"], + ); + keyCache.set(secret, key); + } + return key; +} + export interface VerifyHmacOptions { /** Time source, defaults to `Date.now`. Injected for testing. */ @@ -41,13 +61,7 @@ export async function signHmac( body: string, secret: string, ): Promise { - const key = await crypto.subtle.importKey( - "raw", - ENCODER.encode(secret), - { name: "HMAC", hash: "SHA-256" }, - false, - ["sign"], - ); + const key = await importKeyCached(secret); const sig = await crypto.subtle.sign( "HMAC", key, @@ -56,6 +70,8 @@ export async function signHmac( return bytesToHex(new Uint8Array(sig)); } + + /** * Validate the auth headers on a Worker request. Returns the consumed body on * success so the handler can `JSON.parse` it directly. @@ -82,7 +98,17 @@ export async function verifyHmac( return { ok: false, error: "timestamp expired", status: 401 }; } + const contentLength = request.headers.get("content-length"); + if (contentLength && Number.parseInt(contentLength, 10) > MAX_BODY_BYTES) { + return { ok: false, error: "body too large", status: 413 }; + } + const body = await request.text(); + if (body.length > MAX_BODY_BYTES) { + return { ok: false, error: "body too large", status: 413 }; + } + + const expected = await signHmac(timestamp, body, secret); if (!constantTimeEqualHex(expected, signature)) { return { ok: false, error: "invalid signature", status: 401 }; diff --git a/apps/sandbox-runner/src/index.ts b/apps/sandbox-runner/src/index.ts index 929a696..0d53b7e 100644 --- a/apps/sandbox-runner/src/index.ts +++ b/apps/sandbox-runner/src/index.ts @@ -17,7 +17,6 @@ import { getSandbox, type Sandbox as SandboxClass } from "@cloudflare/sandbox"; import { verifyHmac } from "./auth"; import type { ErrorResponseBody, - ExecRequest, ExecResponse, GitCloneRequest, GitCloneResponse, @@ -120,7 +119,7 @@ async function handleGitClone( }); const body: GitCloneResponse = { ok: result.success, - repoUrl: result.repoUrl, + repoUrl: redactCredentials(result.repoUrl), branch: result.branch, targetDir: result.targetDir, exitCode: result.exitCode, @@ -153,6 +152,19 @@ function optionalString(value: unknown): string | undefined { function optionalNumber(value: unknown): number | undefined { return typeof value === "number" && Number.isFinite(value) ? value : undefined; } +function redactCredentials(url: string): string { + try { + const u = new URL(url); + if (u.password || u.username) { + u.password = ""; + u.username = ""; + return u.toString(); + } + } catch { + // Not a valid URL — return unchanged. + } + return url; +} function toErrorMessage(error: unknown, fallback: string): string { if (error instanceof Error && error.message) return error.message; From 0029862c089049697331bbf5a63f228012235b0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Javier=20Garc=C3=ADa=20L=C3=B3pez?= <6173685+LaloLalo1999@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:24:27 -0600 Subject: [PATCH 03/12] fix(AGE-65): address PR #78 review feedback - Fix design-system footer link to point at docs/design/design-system.html (Codex/Copilot). - Replace empty anchor with
for a11y (Copilot). - Harden tweaks-panel postMessage to only accept from window.parent when embedded (Copilot). - Fix "Watch the demo" scroll targets from missing #demo to existing #vignette (Copilot). Addresses: - https://github.com/Agentic-Engineering-Agency/code-colony/pull/78#discussion_r3160601636 - https://github.com/Agentic-Engineering-Agency/code-colony/pull/78#discussion_r3160617844 - https://github.com/Agentic-Engineering-Agency/code-colony/pull/78#discussion_r3160617892 - https://github.com/Agentic-Engineering-Agency/code-colony/pull/78#discussion_r3160617929 - https://github.com/Agentic-Engineering-Agency/code-colony/pull/78#discussion_r3160618029 - https://github.com/Agentic-Engineering-Agency/code-colony/pull/78#discussion_r3160618053 --- apps/landing/src/App.tsx | 3 +-- apps/landing/src/hero.tsx | 4 ++-- apps/landing/src/sections.tsx | 2 +- apps/landing/src/tweaks-panel.tsx | 6 +++++- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/landing/src/App.tsx b/apps/landing/src/App.tsx index 9ff33fa..c453d38 100644 --- a/apps/landing/src/App.tsx +++ b/apps/landing/src/App.tsx @@ -58,9 +58,8 @@ export function App() { <> setWaitlistOpen(true)} /> {tw.tickerOn && } - +