Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion apps/api/src/app/api/hosts/jobs/sync-presence/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ export async function POST(request: Request): Promise<Response> {
});
}

// Pass the connected set as a single Postgres array-literal parameter
// rather than letting drizzle expand the JS array into N placeholders
// (`($1, $2, ...)::text[]` is a row-cast, not an array). Routing keys are
// `${uuid}:${32-char-hex}` so the unquoted `{a,b,c}` literal is safe.
const connectedArrayLiteral = `{${connected.join(",")}}`;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Unvalidated Redis values flow into Postgres array literal

The fix is not a SQL-injection risk — Drizzle parameterizes ${connectedArrayLiteral} as a single $1 binding — but it creates a silent failure path: if any value returned by redis.zrange ever contains {, }, ,, or whitespace (e.g., after a key-format change in directory.ts or due to Redis data corruption), Postgres will reject the $1::text[] cast and the reconciler will 502. Because the guard at line 63 only checks length, not shape, a single malformed key silences the whole run. Adding a format check before building the literal would make that failure mode explicit and diagnosable.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/api/src/app/api/hosts/jobs/sync-presence/route.ts
Line: 79

Comment:
**Unvalidated Redis values flow into Postgres array literal**

The fix is not a SQL-injection risk — Drizzle parameterizes `${connectedArrayLiteral}` as a single `$1` binding — but it creates a silent failure path: if any value returned by `redis.zrange` ever contains `{`, `}`, `,`, or whitespace (e.g., after a key-format change in `directory.ts` or due to Redis data corruption), Postgres will reject the `$1::text[]` cast and the reconciler will 502. Because the guard at line 63 only checks length, not shape, a single malformed key silences the whole run. Adding a format check before building the literal would make that failure mode explicit and diagnosable.

How can I resolve this? If you propose a fix, please make it concise.


let rows: Array<{
organization_id: string;
machine_id: string;
Expand All @@ -87,7 +93,7 @@ export async function POST(request: Request): Promise<Response> {
SELECT
organization_id,
machine_id,
(organization_id::text || ':' || machine_id) = ANY(${connected}::text[]) AS expected
(organization_id::text || ':' || machine_id) = ANY(${connectedArrayLiteral}::text[]) AS expected
FROM v2_hosts
)
UPDATE v2_hosts h
Expand Down
Loading