Skip to content

Commit

Permalink
webidl: add argumentLengthCheck guard (#1755)
Browse files Browse the repository at this point in the history
  • Loading branch information
KhafraDev committed Nov 5, 2022
1 parent 744431a commit f91cd5e
Show file tree
Hide file tree
Showing 9 changed files with 40 additions and 110 deletions.
4 changes: 1 addition & 3 deletions lib/fetch/file.js
Expand Up @@ -13,9 +13,7 @@ class File extends Blob {
// The File constructor is invoked with two or three parameters, depending
// on whether the optional dictionary parameter is used. When the File()
// constructor is invoked, user agents must run the following steps:
if (arguments.length < 2) {
throw new TypeError('2 arguments required')
}
webidl.argumentLengthCheck(arguments, 2, { header: 'File constructor' })

fileBits = webidl.converters['sequence<BlobPart>'](fileBits)
fileName = webidl.converters.USVString(fileName)
Expand Down
42 changes: 7 additions & 35 deletions lib/fetch/formdata.js
Expand Up @@ -23,11 +23,7 @@ class FormData {
append (name, value, filename = undefined) {
webidl.brandCheck(this, FormData)

if (arguments.length < 2) {
throw new TypeError(
`Failed to execute 'append' on 'FormData': 2 arguments required, but only ${arguments.length} present.`
)
}
webidl.argumentLengthCheck(arguments, 2, { header: 'FormData.append' })

if (arguments.length === 3 && !isBlobLike(value)) {
throw new TypeError(
Expand Down Expand Up @@ -56,11 +52,7 @@ class FormData {
delete (name) {
webidl.brandCheck(this, FormData)

if (arguments.length < 1) {
throw new TypeError(
`Failed to execute 'delete' on 'FormData': 1 arguments required, but only ${arguments.length} present.`
)
}
webidl.argumentLengthCheck(arguments, 1, { header: 'FormData.delete' })

name = webidl.converters.USVString(name)

Expand All @@ -79,11 +71,7 @@ class FormData {
get (name) {
webidl.brandCheck(this, FormData)

if (arguments.length < 1) {
throw new TypeError(
`Failed to execute 'get' on 'FormData': 1 arguments required, but only ${arguments.length} present.`
)
}
webidl.argumentLengthCheck(arguments, 1, { header: 'FormData.get' })

name = webidl.converters.USVString(name)

Expand All @@ -102,11 +90,7 @@ class FormData {
getAll (name) {
webidl.brandCheck(this, FormData)

if (arguments.length < 1) {
throw new TypeError(
`Failed to execute 'getAll' on 'FormData': 1 arguments required, but only ${arguments.length} present.`
)
}
webidl.argumentLengthCheck(arguments, 1, { header: 'FormData.getAll' })

name = webidl.converters.USVString(name)

Expand All @@ -122,11 +106,7 @@ class FormData {
has (name) {
webidl.brandCheck(this, FormData)

if (arguments.length < 1) {
throw new TypeError(
`Failed to execute 'has' on 'FormData': 1 arguments required, but only ${arguments.length} present.`
)
}
webidl.argumentLengthCheck(arguments, 1, { header: 'FormData.has' })

name = webidl.converters.USVString(name)

Expand All @@ -138,11 +118,7 @@ class FormData {
set (name, value, filename = undefined) {
webidl.brandCheck(this, FormData)

if (arguments.length < 2) {
throw new TypeError(
`Failed to execute 'set' on 'FormData': 2 arguments required, but only ${arguments.length} present.`
)
}
webidl.argumentLengthCheck(arguments, 2, { header: 'FormData.set' })

if (arguments.length === 3 && !isBlobLike(value)) {
throw new TypeError(
Expand Down Expand Up @@ -219,11 +195,7 @@ class FormData {
forEach (callbackFn, thisArg = globalThis) {
webidl.brandCheck(this, FormData)

if (arguments.length < 1) {
throw new TypeError(
`Failed to execute 'forEach' on 'FormData': 1 argument required, but only ${arguments.length} present.`
)
}
webidl.argumentLengthCheck(arguments, 1, { header: 'FormData.forEach' })

if (typeof callbackFn !== 'function') {
throw new TypeError(
Expand Down
36 changes: 6 additions & 30 deletions lib/fetch/headers.js
Expand Up @@ -175,11 +175,7 @@ class Headers {
append (name, value) {
webidl.brandCheck(this, Headers)

if (arguments.length < 2) {
throw new TypeError(
`Failed to execute 'append' on 'Headers': 2 arguments required, but only ${arguments.length} present.`
)
}
webidl.argumentLengthCheck(arguments, 2, { header: 'Headers.append' })

name = webidl.converters.ByteString(name)
value = webidl.converters.ByteString(value)
Expand Down Expand Up @@ -227,11 +223,7 @@ class Headers {
delete (name) {
webidl.brandCheck(this, Headers)

if (arguments.length < 1) {
throw new TypeError(
`Failed to execute 'delete' on 'Headers': 1 argument required, but only ${arguments.length} present.`
)
}
webidl.argumentLengthCheck(arguments, 1, { header: 'Headers.delete' })

name = webidl.converters.ByteString(name)

Expand Down Expand Up @@ -276,11 +268,7 @@ class Headers {
get (name) {
webidl.brandCheck(this, Headers)

if (arguments.length < 1) {
throw new TypeError(
`Failed to execute 'get' on 'Headers': 1 argument required, but only ${arguments.length} present.`
)
}
webidl.argumentLengthCheck(arguments, 1, { header: 'Headers.get' })

name = webidl.converters.ByteString(name)

Expand All @@ -302,11 +290,7 @@ class Headers {
has (name) {
webidl.brandCheck(this, Headers)

if (arguments.length < 1) {
throw new TypeError(
`Failed to execute 'has' on 'Headers': 1 argument required, but only ${arguments.length} present.`
)
}
webidl.argumentLengthCheck(arguments, 1, { header: 'Headers.has' })

name = webidl.converters.ByteString(name)

Expand All @@ -328,11 +312,7 @@ class Headers {
set (name, value) {
webidl.brandCheck(this, Headers)

if (arguments.length < 2) {
throw new TypeError(
`Failed to execute 'set' on 'Headers': 2 arguments required, but only ${arguments.length} present.`
)
}
webidl.argumentLengthCheck(arguments, 2, { header: 'Headers.set' })

name = webidl.converters.ByteString(name)
value = webidl.converters.ByteString(value)
Expand Down Expand Up @@ -421,11 +401,7 @@ class Headers {
forEach (callbackFn, thisArg = globalThis) {
webidl.brandCheck(this, Headers)

if (arguments.length < 1) {
throw new TypeError(
`Failed to execute 'forEach' on 'Headers': 1 argument required, but only ${arguments.length} present.`
)
}
webidl.argumentLengthCheck(arguments, 1, { header: 'Headers.forEach' })

if (typeof callbackFn !== 'function') {
throw new TypeError(
Expand Down
7 changes: 2 additions & 5 deletions lib/fetch/index.js
Expand Up @@ -57,6 +57,7 @@ const { isErrored, isReadable } = require('../core/util')
const { dataURLProcessor, serializeAMimeType } = require('./dataURL')
const { TransformStream } = require('stream/web')
const { getGlobalDispatcher } = require('../../index')
const { webidl } = require('./webidl')

/** @type {import('buffer').resolveObjectURL} */
let resolveObjectURL
Expand Down Expand Up @@ -122,11 +123,7 @@ class Fetch extends EE {

// https://fetch.spec.whatwg.org/#fetch-method
async function fetch (input, init = {}) {
if (arguments.length < 1) {
throw new TypeError(
`Failed to execute 'fetch' on 'Window': 1 argument required, but only ${arguments.length} present.`
)
}
webidl.argumentLengthCheck(arguments, 1, { header: 'globalThis.fetch' })

// 1. Let p be a new promise.
const p = createDeferredPromise()
Expand Down
6 changes: 1 addition & 5 deletions lib/fetch/request.js
Expand Up @@ -45,11 +45,7 @@ class Request {
return
}

if (arguments.length < 1) {
throw new TypeError(
`Failed to construct 'Request': 1 argument required, but only ${arguments.length} present.`
)
}
webidl.argumentLengthCheck(arguments, 1, { header: 'Request constructor' })

input = webidl.converters.RequestInfo(input)
init = webidl.converters.RequestInit(init)
Expand Down
12 changes: 2 additions & 10 deletions lib/fetch/response.js
Expand Up @@ -50,11 +50,7 @@ class Response {

// https://fetch.spec.whatwg.org/#dom-response-json
static json (data = undefined, init = {}) {
if (arguments.length === 0) {
throw new TypeError(
'Failed to execute \'json\' on \'Response\': 1 argument required, but 0 present.'
)
}
webidl.argumentLengthCheck(arguments, 1, { header: 'Response.json' })

if (init !== null) {
init = webidl.converters.ResponseInit(init)
Expand Down Expand Up @@ -87,11 +83,7 @@ class Response {
static redirect (url, status = 302) {
const relevantRealm = { settingsObject: {} }

if (arguments.length < 1) {
throw new TypeError(
`Failed to execute 'redirect' on 'Response': 1 argument required, but only ${arguments.length} present.`
)
}
webidl.argumentLengthCheck(arguments, 1, { header: 'Response.redirect' })

url = webidl.converters.USVString(url)
status = webidl.converters['unsigned short'](status)
Expand Down
14 changes: 12 additions & 2 deletions lib/fetch/webidl.js
Expand Up @@ -39,6 +39,16 @@ webidl.brandCheck = function (V, I) {
}
}

webidl.argumentLengthCheck = function ({ length }, min, ctx) {
if (length < min) {
throw webidl.errors.exception({
message: `${min} argument${min !== 1 ? 's' : ''} required, ` +
`but${length ? ' only' : ''} ${length} found.`,
...ctx
})
}
}

// https://tc39.es/ecma262/#sec-ecmascript-data-types-and-values
webidl.util.Type = function (V) {
switch (typeof V) {
Expand Down Expand Up @@ -98,7 +108,7 @@ webidl.util.ConvertToInt = function (V, bitLength, signedness, opts = {}) {
let x = Number(V)

// 5. If x is −0, then set x to +0.
if (Object.is(-0, x)) {
if (x === 0) {
x = 0
}

Expand Down Expand Up @@ -156,7 +166,7 @@ webidl.util.ConvertToInt = function (V, bitLength, signedness, opts = {}) {
// 8. If x is NaN, +0, +∞, or −∞, then return +0.
if (
Number.isNaN(x) ||
Object.is(0, x) ||
(x === 0 && Object.is(0, x)) ||
x === Number.POSITIVE_INFINITY ||
x === Number.NEGATIVE_INFINITY
) {
Expand Down
24 changes: 4 additions & 20 deletions lib/fileapi/filereader.js
Expand Up @@ -39,11 +39,7 @@ class FileReader extends EventTarget {
readAsArrayBuffer (blob) {
webidl.brandCheck(this, FileReader)

if (arguments.length === 0) {
throw new TypeError(
'Failed to execute \'readAsArrayBuffer\' on \'FileReader\': 1 argument required, but 0 present.'
)
}
webidl.argumentLengthCheck(arguments, 1, { header: 'FileReader.readAsArrayBuffer' })

blob = webidl.converters.Blob(blob, { strict: false })

Expand All @@ -59,11 +55,7 @@ class FileReader extends EventTarget {
readAsBinaryString (blob) {
webidl.brandCheck(this, FileReader)

if (arguments.length === 0) {
throw new TypeError(
'Failed to execute \'readAsBinaryString\' on \'FileReader\': 1 argument required, but 0 present.'
)
}
webidl.argumentLengthCheck(arguments, 1, { header: 'FileReader.readAsBinaryString' })

blob = webidl.converters.Blob(blob, { strict: false })

Expand All @@ -80,11 +72,7 @@ class FileReader extends EventTarget {
readAsText (blob, encoding = undefined) {
webidl.brandCheck(this, FileReader)

if (arguments.length === 0) {
throw new TypeError(
'Failed to execute \'readAsText\' on \'FileReader\': 1 argument required, but 0 present.'
)
}
webidl.argumentLengthCheck(arguments, 1, { header: 'FileReader.readAsText' })

blob = webidl.converters.Blob(blob, { strict: false })

Expand All @@ -104,11 +92,7 @@ class FileReader extends EventTarget {
readAsDataURL (blob) {
webidl.brandCheck(this, FileReader)

if (arguments.length === 0) {
throw new TypeError(
'Failed to execute \'readAsDataURL\' on \'FileReader\': 1 argument required, but 0 present.'
)
}
webidl.argumentLengthCheck(arguments, 1, { header: 'FileReader.readAsDataURL' })

blob = webidl.converters.Blob(blob, { strict: false })

Expand Down
5 changes: 5 additions & 0 deletions types/webidl.d.ts
Expand Up @@ -205,4 +205,9 @@ export interface Webidl {
nullableConverter <T>(
converter: Converter<T>
): (V: unknown) => ReturnType<typeof converter> | null

argumentLengthCheck (args: { length: number }, min: number, context: {
header: string
message?: string
}): void
}

0 comments on commit f91cd5e

Please sign in to comment.