Skip to content

Commit 57e53c1

Browse files
committed
fix(integrations): use write-only scope for vanta document submit and stream-cap document downloads
1 parent a65122b commit 57e53c1

3 files changed

Lines changed: 38 additions & 5 deletions

File tree

apps/sim/app/api/tools/vanta/download/route.ts

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,33 @@ function downloadSizeError(bytes: number): NextResponse {
2828
)
2929
}
3030

31+
/**
32+
* Reads a response body incrementally, aborting as soon as the accumulated
33+
* size exceeds the limit so oversized files are never fully buffered.
34+
* Returns null when the limit is exceeded.
35+
*/
36+
async function readBodyWithLimit(response: Response, maxBytes: number): Promise<Buffer | null> {
37+
const reader = response.body?.getReader()
38+
if (!reader) {
39+
const buffer = Buffer.from(await response.arrayBuffer())
40+
return buffer.length > maxBytes ? null : buffer
41+
}
42+
43+
const chunks: Uint8Array[] = []
44+
let total = 0
45+
while (true) {
46+
const { done, value } = await reader.read()
47+
if (done) break
48+
total += value.byteLength
49+
if (total > maxBytes) {
50+
await reader.cancel()
51+
return null
52+
}
53+
chunks.push(value)
54+
}
55+
return Buffer.concat(chunks)
56+
}
57+
3158
/**
3259
* Extracts the filename from a Content-Disposition header, if present.
3360
*/
@@ -99,9 +126,12 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
99126
return downloadSizeError(contentLength)
100127
}
101128

102-
const buffer = Buffer.from(await response.arrayBuffer())
103-
if (buffer.length > MAX_DOWNLOAD_SIZE_BYTES) {
104-
return downloadSizeError(buffer.length)
129+
const buffer = await readBodyWithLimit(response, MAX_DOWNLOAD_SIZE_BYTES)
130+
if (buffer === null) {
131+
return NextResponse.json(
132+
{ success: false, error: 'File exceeds download limit of 100MB' },
133+
{ status: 400 }
134+
)
105135
}
106136

107137
const mimeType = response.headers.get('content-type') || 'application/octet-stream'

apps/sim/app/api/tools/vanta/query/route.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ import {
3232
normalizeVantaVulnerabilityRemediation,
3333
normalizeVantaVulnerableAsset,
3434
splitVantaCommaList,
35-
VANTA_DOCUMENT_UPLOAD_SCOPE,
3635
VANTA_READ_SCOPE,
36+
VANTA_WRITE_SCOPE,
3737
} from '@/tools/vanta/utils'
3838

3939
export const dynamic = 'force-dynamic'
@@ -382,7 +382,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
382382

383383
const baseUrl = getVantaBaseUrl(params.region)
384384
const scope =
385-
params.operation === 'vanta_submit_document' ? VANTA_DOCUMENT_UPLOAD_SCOPE : VANTA_READ_SCOPE
385+
params.operation === 'vanta_submit_document' ? VANTA_WRITE_SCOPE : VANTA_READ_SCOPE
386386
const accessToken = await getVantaAccessToken({
387387
clientId: params.clientId,
388388
clientSecret: params.clientSecret,

apps/sim/tools/vanta/utils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ export const VANTA_API_BASE_URLS: Record<VantaRegion, string> = {
3535
/** Read-only scope used by every query and download operation. */
3636
export const VANTA_READ_SCOPE = 'vanta-api.all:read'
3737

38+
/** Read-write scope used by write operations that do not upload files. */
39+
export const VANTA_WRITE_SCOPE = 'vanta-api.all:read vanta-api.all:write'
40+
3841
/**
3942
* Scope string for document evidence uploads, taken verbatim from Vanta's
4043
* "Upload a document" guide (the upload scope cannot be requested alone).

0 commit comments

Comments
 (0)