fix: reject malformed file upload base64 (#3784)

This commit is contained in:
Neil
2026-05-30 10:05:25 -07:00
committed by GitHub
parent 0bf62e83d2
commit d699e12f08
2 changed files with 23 additions and 0 deletions
@@ -391,6 +391,10 @@ describe('file RPC methods', () => {
[
'non-string content',
{ worktree: 'id:wt-1', relativePath: 'assets/logo.png', contentBase64: 0 }
],
[
'malformed content',
{ worktree: 'id:wt-1', relativePath: 'assets/logo.png', contentBase64: '!!!!' }
]
])('rejects a base64 write with %s', async (_name, params) => {
const runtime = {
@@ -456,6 +460,15 @@ describe('file RPC methods', () => {
contentBase64: 0,
append: true
}
],
[
'malformed content',
{
worktree: 'id:wt-1',
relativePath: 'assets/video.mov',
contentBase64: '!!!!',
append: true
}
]
])('rejects a base64 chunk write with %s (inherits the schema)', async (_name, params) => {
const runtime = {
+10
View File
@@ -4,6 +4,13 @@ import { defineMethod, defineStreamingMethod, type RpcAnyMethod } from '../core'
import { createFileWatchEventBatcher } from './file-watch-event-batcher'
let filesWatchSubscriptionSeq = 0
const RUNTIME_FILE_BASE64_PATTERN = /^[A-Za-z0-9+/]*={0,2}$/
function isValidRuntimeFileBase64(value: unknown): value is string {
return (
typeof value === 'string' && value.length % 4 !== 1 && RUNTIME_FILE_BASE64_PATTERN.test(value)
)
}
const WorktreeSelector = z.object({
worktree: z
@@ -43,6 +50,9 @@ const FileWriteBase64 = FileOpen.extend({
contentBase64: z
.unknown()
.refine((v): v is string => typeof v === 'string', { message: 'Missing file content' })
// Why: Buffer.from(..., 'base64') accepts malformed input by dropping
// invalid bytes, which can silently create empty or corrupt uploaded files.
.refine(isValidRuntimeFileBase64, 'File content must be base64')
})
const FileWriteBase64Chunk = FileWriteBase64.extend({