-
Notifications
You must be signed in to change notification settings - Fork 35
fix(ai-gateway): sanitize request logs #2807
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| /** | ||
| * Strip NUL bytes (\u0000) in place from every string-typed field on `value`. | ||
| * | ||
| * Postgres `text` columns reject NUL bytes with `22021 invalid byte sequence | ||
| * for encoding "UTF8": 0x00`, which crashes inserts into affected tables. | ||
| * | ||
| * Any sanitized field paths are appended to `dirtyFields` so the caller can | ||
| * log them for source attribution. | ||
| */ | ||
| export function stripNulBytesInPlace(value: object, dirtyFields: string[]): void { | ||
| stripNulBytesFromValue(value, dirtyFields, ''); | ||
| } | ||
|
|
||
| function stripNulBytesFromValue(value: unknown, dirtyFields: string[], path: string): void { | ||
| if (Array.isArray(value)) { | ||
| value.forEach((item, index) => { | ||
| stripNulBytesFromValue(item, dirtyFields, `${path}[${index}]`); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. WARNING: Arrays of strings still bypass sanitization The new recursion fixes nested objects, but this branch immediately recurses into each array element without handling the case where the element itself is a string. Values like |
||
| }); | ||
| return; | ||
| } | ||
|
|
||
| if (!isPlainObject(value)) { | ||
| return; | ||
| } | ||
|
|
||
| for (const [key, item] of Object.entries(value)) { | ||
| const itemPath = path ? `${path}.${key}` : key; | ||
| if (typeof item === 'string' && item.indexOf('\u0000') >= 0) { | ||
| value[key] = item.split('\u0000').join(''); | ||
Check failureCode scanning / CodeQL Remote property injection High
A property name to write to depends on a
user-provided value Error loading related location Loading |
||
|
|
||
| dirtyFields.push(itemPath); | ||
| continue; | ||
| } | ||
|
|
||
| stripNulBytesFromValue(item, dirtyFields, itemPath); | ||
| } | ||
| } | ||
|
|
||
| function isPlainObject(value: unknown): value is Record<string, unknown> { | ||
| return ( | ||
| typeof value === 'object' && value !== null && Object.getPrototypeOf(value) === Object.prototype | ||
| ); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WARNING: Nested request fields are still unsanitized
stripNulBytesInPlaceonly touches the top-level properties onrequest.body, but gateway payloads keep the actual prompt text inside nested structures likemessages[].content,input[].content, and tool payloads. A NUL byte in one of those strings will still make theapi_request_log.requestjsonbinsert fail, so this change does not fully cover the bug the PR is trying to fix.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in f56d7e5 by making
stripNulBytesInPlacerecurse through arrays and plain objects, so nested prompt/tool strings are sanitized before insertingapi_request_log.request.