diff options
author | Nayeem Rahman <nayeemrmn99@gmail.com> | 2020-06-04 03:32:27 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-06-03 22:32:27 -0400 |
commit | 97d876f6db55f5f32ac06155dcb4823e1c636215 (patch) | |
tree | 0171130f480548663608dd9440e8bf37fff68c5f /std/http/_io.ts | |
parent | 9bd5c08d5a2a9cdd2649dd24a4d6b26d9af2a4c4 (diff) |
fix(std/http): Don't use assert() for user input validation (#6092)
Diffstat (limited to 'std/http/_io.ts')
-rw-r--r-- | std/http/_io.ts | 84 |
1 files changed, 49 insertions, 35 deletions
diff --git a/std/http/_io.ts b/std/http/_io.ts index 6b3796c5b..82954ccee 100644 --- a/std/http/_io.ts +++ b/std/http/_io.ts @@ -118,27 +118,37 @@ function isProhibidedForTrailer(key: string): boolean { return s.has(key.toLowerCase()); } -/** - * Read trailer headers from reader and append values to headers. - * "trailer" field will be deleted. - * */ +/** Read trailer headers from reader and append values to headers. "trailer" + * field will be deleted. */ export async function readTrailers( headers: Headers, r: BufReader ): Promise<void> { - const headerKeys = parseTrailer(headers.get("trailer")); - if (!headerKeys) return; + const trailers = parseTrailer(headers.get("trailer")); + if (trailers == null) return; + const trailerNames = [...trailers.keys()]; const tp = new TextProtoReader(r); const result = await tp.readMIMEHeader(); - assert(result !== null, "trailer must be set"); + if (result == null) { + throw new Deno.errors.InvalidData("Missing trailer header."); + } + const undeclared = [...result.keys()].filter( + (k) => !trailerNames.includes(k) + ); + if (undeclared.length > 0) { + throw new Deno.errors.InvalidData( + `Undeclared trailers: ${Deno.inspect(undeclared)}.` + ); + } for (const [k, v] of result) { - if (!headerKeys.has(k)) { - throw new Error("Undeclared trailer field"); - } - headerKeys.delete(k); headers.append(k, v); } - assert(Array.from(headerKeys).length === 0, "Missing trailers"); + const missingTrailers = trailerNames.filter((k) => !result.has(k)); + if (missingTrailers.length > 0) { + throw new Deno.errors.InvalidData( + `Missing trailers: ${Deno.inspect(missingTrailers)}.` + ); + } headers.delete("trailer"); } @@ -146,16 +156,17 @@ function parseTrailer(field: string | null): Headers | undefined { if (field == null) { return undefined; } - const keys = field.split(",").map((v) => v.trim().toLowerCase()); - if (keys.length === 0) { - throw new Error("Empty trailer"); + const trailerNames = field.split(",").map((v) => v.trim().toLowerCase()); + if (trailerNames.length === 0) { + throw new Deno.errors.InvalidData("Empty trailer header."); } - for (const key of keys) { - if (isProhibidedForTrailer(key)) { - throw new Error(`Prohibited field for trailer`); - } + const prohibited = trailerNames.filter((k) => isProhibidedForTrailer(k)); + if (prohibited.length > 0) { + throw new Deno.errors.InvalidData( + `Prohibited trailer names: ${Deno.inspect(prohibited)}.` + ); } - return new Headers(keys.map((key) => [key, ""])); + return new Headers(trailerNames.map((key) => [key, ""])); } export async function writeChunkedBody( @@ -176,7 +187,8 @@ export async function writeChunkedBody( await writer.write(endChunk); } -/** write trailer headers to writer. it mostly should be called after writeResponse */ +/** Write trailer headers to writer. It should mostly should be called after + * `writeResponse()`. */ export async function writeTrailers( w: Deno.Writer, headers: Headers, @@ -184,29 +196,31 @@ export async function writeTrailers( ): Promise<void> { const trailer = headers.get("trailer"); if (trailer === null) { - throw new Error('response headers must have "trailer" header field'); + throw new TypeError("Missing trailer header."); } const transferEncoding = headers.get("transfer-encoding"); if (transferEncoding === null || !transferEncoding.match(/^chunked/)) { - throw new Error( - `trailer headers is only allowed for "transfer-encoding: chunked": got "${transferEncoding}"` + throw new TypeError( + `Trailers are only allowed for "transfer-encoding: chunked", got "transfer-encoding: ${transferEncoding}".` ); } const writer = BufWriter.create(w); - const trailerHeaderFields = trailer - .split(",") - .map((s) => s.trim().toLowerCase()); - for (const f of trailerHeaderFields) { - assert( - !isProhibidedForTrailer(f), - `"${f}" is prohibited for trailer header` + const trailerNames = trailer.split(",").map((s) => s.trim().toLowerCase()); + const prohibitedTrailers = trailerNames.filter((k) => + isProhibidedForTrailer(k) + ); + if (prohibitedTrailers.length > 0) { + throw new TypeError( + `Prohibited trailer names: ${Deno.inspect(prohibitedTrailers)}.` ); } + const undeclared = [...trailers.keys()].filter( + (k) => !trailerNames.includes(k) + ); + if (undeclared.length > 0) { + throw new TypeError(`Undeclared trailers: ${Deno.inspect(undeclared)}.`); + } for (const [key, value] of trailers) { - assert( - trailerHeaderFields.includes(key), - `Not trailer header field: ${key}` - ); await writer.write(encoder.encode(`${key}: ${value}\r\n`)); } await writer.write(encoder.encode("\r\n")); |