Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webidl: add argumentLengthCheck guard #1755

Merged
merged 1 commit into from Nov 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
}