Skip to content

Commit 81744ee

Browse files
committed
Make sure file proxy endpoint only serves JSON or YAML
Does so by parsing the file's text using the yaml library. If it is parsed correctly we return the file's text. If not we return an error.
1 parent 0c00f35 commit 81744ee

File tree

1 file changed

+23
-5
lines changed

1 file changed

+23
-5
lines changed

src/app/api/proxy/route.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { NextRequest, NextResponse } from "next/server"
22
import { env, makeAPIErrorResponse, makeUnauthenticatedAPIErrorResponse } from "@/common"
33
import { session } from "@/composition"
4+
import { parse as parseYaml } from "yaml"
45

56
const ErrorName = {
67
MAX_FILE_SIZE_EXCEEDED: "MaxFileSizeExceededError",
7-
TIMEOUT: "TimeoutError"
8+
TIMEOUT: "TimeoutError",
9+
NOT_JSON_OR_YAML: "NotJsonOrYamlError",
810
}
911

1012
export async function GET(req: NextRequest) {
@@ -26,8 +28,9 @@ export async function GET(req: NextRequest) {
2628
const maxMegabytes = Number(env.getOrThrow("PROXY_API_MAXIMUM_FILE_SIZE_IN_MEGABYTES"))
2729
const timeoutInSeconds = Number(env.getOrThrow("PROXY_API_TIMEOUT_IN_SECONDS"))
2830
const maxBytes = maxMegabytes * 1024 * 1024
29-
const file = await downloadFile({ url, maxBytes, timeoutInSeconds })
30-
return new NextResponse(file, { status: 200 })
31+
const fileText = await downloadFile({ url, maxBytes, timeoutInSeconds })
32+
checkIfJsonOrYaml(fileText)
33+
return new NextResponse(fileText, { status: 200 })
3134
} catch (error) {
3235
if (error instanceof Error == false) {
3336
return makeAPIErrorResponse(500, "An unknown error occurred.")
@@ -36,6 +39,8 @@ export async function GET(req: NextRequest) {
3639
return makeAPIErrorResponse(413, "The operation was aborted.")
3740
} else if (error.name === ErrorName.TIMEOUT) {
3841
return makeAPIErrorResponse(408, "The operation timed out.")
42+
} else if (error.name === ErrorName.NOT_JSON_OR_YAML) {
43+
return makeAPIErrorResponse(400, "Url does not point to a JSON or YAML file.")
3944
} else {
4045
return makeAPIErrorResponse(500, error.message)
4146
}
@@ -46,7 +51,7 @@ async function downloadFile(params: {
4651
url: URL,
4752
maxBytes: number,
4853
timeoutInSeconds: number
49-
}): Promise<Blob> {
54+
}): Promise<string> {
5055
const { url, maxBytes, timeoutInSeconds } = params
5156
const abortController = new AbortController()
5257
const timeoutSignal = AbortSignal.timeout(timeoutInSeconds * 1000)
@@ -80,5 +85,18 @@ async function downloadFile(params: {
8085
error.name = ErrorName.MAX_FILE_SIZE_EXCEEDED
8186
throw error
8287
}
83-
return new Blob(chunks)
88+
const blob = new Blob(chunks)
89+
const arrayBuffer = await blob.arrayBuffer()
90+
const decoder = new TextDecoder()
91+
return decoder.decode(arrayBuffer)
92+
}
93+
94+
function checkIfJsonOrYaml(fileText: string) {
95+
try {
96+
parseYaml(fileText) // will also parse JSON as it is a subset of YAML
97+
} catch {
98+
const error = new Error("File is not JSON or YAML")
99+
error.name = ErrorName.NOT_JSON_OR_YAML
100+
throw error
101+
}
84102
}

0 commit comments

Comments
 (0)