util: add MIME utilities (#21128)
Co-authored-by: Rich Trott <rtrott@gmail.com> Co-authored-by: Antoine du Hamel <duhamelantoine1995@gmail.com> PR-URL: https://github.com/nodejs/node/pull/21128 Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Jacob Smith <jacob@frende.me> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
parent
1f7e8d609a
commit
87cdf7d412
@ -1967,6 +1967,12 @@ An invalid HTTP token was supplied.
|
||||
|
||||
An IP address is not valid.
|
||||
|
||||
<a id="ERR_INVALID_MIME_SYNTAX"></a>
|
||||
|
||||
### `ERR_INVALID_MIME_SYNTAX`
|
||||
|
||||
The syntax of a MIME is not valid.
|
||||
|
||||
<a id="ERR_INVALID_MODULE"></a>
|
||||
|
||||
### `ERR_INVALID_MODULE`
|
||||
|
353
doc/api/util.md
353
doc/api/util.md
@ -1024,6 +1024,355 @@ Otherwise, returns `false`.
|
||||
See [`assert.deepStrictEqual()`][] for more information about deep strict
|
||||
equality.
|
||||
|
||||
## Class: `util.MIMEType`
|
||||
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
> Stability: 1 - Experimental
|
||||
|
||||
An implementation of [the MIMEType class](https://bmeck.github.io/node-proposal-mime-api/).
|
||||
|
||||
In accordance with browser conventions, all properties of `MIMEType` objects
|
||||
are implemented as getters and setters on the class prototype, rather than as
|
||||
data properties on the object itself.
|
||||
|
||||
A MIME string is a structured string containing multiple meaningful
|
||||
components. When parsed, a `MIMEType` object is returned containing
|
||||
properties for each of these components.
|
||||
|
||||
### Constructor: `new MIMEType(input)`
|
||||
|
||||
* `input` {string} The input MIME to parse
|
||||
|
||||
Creates a new `MIMEType` object by parsing the `input`.
|
||||
|
||||
```mjs
|
||||
import { MIMEType } from 'node:util';
|
||||
|
||||
const myMIME = new MIMEType('text/plain');
|
||||
```
|
||||
|
||||
```cjs
|
||||
const { MIMEType } = require('node:util');
|
||||
|
||||
const myMIME = new MIMEType('text/plain');
|
||||
```
|
||||
|
||||
A `TypeError` will be thrown if the `input` is not a valid MIME. Note
|
||||
that an effort will be made to coerce the given values into strings. For
|
||||
instance:
|
||||
|
||||
```mjs
|
||||
import { MIMEType } from 'node:util';
|
||||
const myMIME = new MIMEType({ toString: () => 'text/plain' });
|
||||
console.log(String(myMIME));
|
||||
// Prints: text/plain
|
||||
```
|
||||
|
||||
```cjs
|
||||
const { MIMEType } = require('node:util');
|
||||
const myMIME = new MIMEType({ toString: () => 'text/plain' });
|
||||
console.log(String(myMIME));
|
||||
// Prints: text/plain
|
||||
```
|
||||
|
||||
#### `mime.type`
|
||||
|
||||
* {string}
|
||||
|
||||
Gets and sets the type portion of the MIME.
|
||||
|
||||
```mjs
|
||||
import { MIMEType } from 'node:util';
|
||||
|
||||
const myMIME = new MIMEType('text/javascript');
|
||||
console.log(myMIME.type);
|
||||
// Prints: text
|
||||
myMIME.type = 'application';
|
||||
console.log(myMIME.type);
|
||||
// Prints: application
|
||||
console.log(String(myMIME));
|
||||
// Prints: application/javascript
|
||||
```
|
||||
|
||||
```cjs
|
||||
const { MIMEType } = require('node:util');
|
||||
|
||||
const myMIME = new MIMEType('text/javascript');
|
||||
console.log(myMIME.type);
|
||||
// Prints: text
|
||||
myMIME.type = 'application';
|
||||
console.log(myMIME.type);
|
||||
// Prints: application
|
||||
console.log(String(myMIME));
|
||||
// Prints: application/javascript/javascript
|
||||
```
|
||||
|
||||
#### `mime.subtype`
|
||||
|
||||
* {string}
|
||||
|
||||
Gets and sets the subtype portion of the MIME.
|
||||
|
||||
```mjs
|
||||
import { MIMEType } from 'node:util';
|
||||
|
||||
const myMIME = new MIMEType('text/ecmascript');
|
||||
console.log(myMIME.subtype);
|
||||
// Prints: ecmascript
|
||||
myMIME.subtype = 'javascript';
|
||||
console.log(myMIME.subtype);
|
||||
// Prints: javascript
|
||||
console.log(String(myMIME));
|
||||
// Prints: text/javascript
|
||||
```
|
||||
|
||||
```cjs
|
||||
const { MIMEType } = require('node:util');
|
||||
|
||||
const myMIME = new MIMEType('text/ecmascript');
|
||||
console.log(myMIME.subtype);
|
||||
// Prints: ecmascript
|
||||
myMIME.subtype = 'javascript';
|
||||
console.log(myMIME.subtype);
|
||||
// Prints: javascript
|
||||
console.log(String(myMIME));
|
||||
// Prints: text/javascript
|
||||
```
|
||||
|
||||
#### `mime.essence`
|
||||
|
||||
* {string}
|
||||
|
||||
Gets the essence of the MIME. This property is read only.
|
||||
Use `mime.type` or `mime.subtype` to alter the MIME.
|
||||
|
||||
```mjs
|
||||
import { MIMEType } from 'node:util';
|
||||
|
||||
const myMIME = new MIMEType('text/javascript;key=value');
|
||||
console.log(myMIME.essence);
|
||||
// Prints: text/javascript
|
||||
myMIME.type = 'application';
|
||||
console.log(myMIME.essence);
|
||||
// Prints: application/javascript
|
||||
console.log(String(myMIME));
|
||||
// Prints: application/javascript;key=value
|
||||
```
|
||||
|
||||
```cjs
|
||||
const { MIMEType } = require('node:util');
|
||||
|
||||
const myMIME = new MIMEType('text/javascript;key=value');
|
||||
console.log(myMIME.essence);
|
||||
// Prints: text/javascript
|
||||
myMIME.type = 'application';
|
||||
console.log(myMIME.essence);
|
||||
// Prints: application/javascript
|
||||
console.log(String(myMIME));
|
||||
// Prints: application/javascript;key=value
|
||||
```
|
||||
|
||||
#### `mime.params`
|
||||
|
||||
* {MIMEParams}
|
||||
|
||||
Gets the [`MIMEParams`][] object representing the
|
||||
parameters of the MIME. This property is read-only. See
|
||||
[`MIMEParams`][] documentation for details.
|
||||
|
||||
#### `mime.toString()`
|
||||
|
||||
* Returns: {string}
|
||||
|
||||
The `toString()` method on the `MIMEType` object returns the serialized MIME.
|
||||
|
||||
Because of the need for standard compliance, this method does not allow users
|
||||
to customize the serialization process of the MIME.
|
||||
|
||||
#### `mime.toJSON()`
|
||||
|
||||
* Returns: {string}
|
||||
|
||||
Alias for [`mime.toString()`][].
|
||||
|
||||
This method is automatically called when an `MIMEType` object is serialized
|
||||
with [`JSON.stringify()`][].
|
||||
|
||||
```mjs
|
||||
import { MIMEType } from 'node:util';
|
||||
|
||||
const myMIMES = [
|
||||
new MIMEType('image/png'),
|
||||
new MIMEType('image/gif'),
|
||||
];
|
||||
console.log(JSON.stringify(myMIMES));
|
||||
// Prints: ["image/png", "image/gif"]
|
||||
```
|
||||
|
||||
```cjs
|
||||
const { MIMEType } = require('node:util');
|
||||
|
||||
const myMIMES = [
|
||||
new MIMEType('image/png'),
|
||||
new MIMEType('image/gif'),
|
||||
];
|
||||
console.log(JSON.stringify(myMIMES));
|
||||
// Prints: ["image/png", "image/gif"]
|
||||
```
|
||||
|
||||
### Class: `util.MIMEParams`
|
||||
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
The `MIMEParams` API provides read and write access to the parameters of a
|
||||
`MIMEType`.
|
||||
|
||||
#### Constructor: `new MIMEParams()`
|
||||
|
||||
Creates a new `MIMEParams` object by with empty parameters
|
||||
|
||||
```mjs
|
||||
import { MIMEParams } from 'node:util';
|
||||
|
||||
const myParams = new MIMEParams();
|
||||
```
|
||||
|
||||
```cjs
|
||||
const { MIMEParams } = require('node:util');
|
||||
|
||||
const myParams = new MIMEParams();
|
||||
```
|
||||
|
||||
#### `mimeParams.delete(name)`
|
||||
|
||||
* `name` {string}
|
||||
|
||||
Remove all name-value pairs whose name is `name`.
|
||||
|
||||
#### `mimeParams.entries()`
|
||||
|
||||
* Returns: {Iterator}
|
||||
|
||||
Returns an iterator over each of the name-value pairs in the parameters.
|
||||
Each item of the iterator is a JavaScript `Array`. The first item of the array
|
||||
is the `name`, the second item of the array is the `value`.
|
||||
|
||||
#### `mimeParams.get(name)`
|
||||
|
||||
* `name` {string}
|
||||
* Returns: {string} or `null` if there is no name-value pair with the given
|
||||
`name`.
|
||||
|
||||
Returns the value of the first name-value pair whose name is `name`. If there
|
||||
are no such pairs, `null` is returned.
|
||||
|
||||
#### `mimeParams.has(name)`
|
||||
|
||||
* `name` {string}
|
||||
* Returns: {boolean}
|
||||
|
||||
Returns `true` if there is at least one name-value pair whose name is `name`.
|
||||
|
||||
#### `mimeParams.keys()`
|
||||
|
||||
* Returns: {Iterator}
|
||||
|
||||
Returns an iterator over the names of each name-value pair.
|
||||
|
||||
```mjs
|
||||
import { MIMEType } from 'node:util';
|
||||
|
||||
const { params } = new MIMEType('text/plain;foo=0;bar=1');
|
||||
for (const name of params.keys()) {
|
||||
console.log(name);
|
||||
}
|
||||
// Prints:
|
||||
// foo
|
||||
// bar
|
||||
```
|
||||
|
||||
```cjs
|
||||
const { MIMEType } = require('node:util');
|
||||
|
||||
const { params } = new MIMEType('text/plain;foo=0;bar=1');
|
||||
for (const name of params.keys()) {
|
||||
console.log(name);
|
||||
}
|
||||
// Prints:
|
||||
// foo
|
||||
// bar
|
||||
```
|
||||
|
||||
#### `mimeParams.set(name, value)`
|
||||
|
||||
* `name` {string}
|
||||
* `value` {string}
|
||||
|
||||
Sets the value in the `MIMEParams` object associated with `name` to
|
||||
`value`. If there are any pre-existing name-value pairs whose names are `name`,
|
||||
set the first such pair's value to `value`.
|
||||
|
||||
```mjs
|
||||
import { MIMEType } from 'node:util';
|
||||
|
||||
const { params } = new MIMEType('text/plain;foo=0;bar=1');
|
||||
params.set('foo', 'def');
|
||||
params.set('baz', 'xyz');
|
||||
console.log(params.toString());
|
||||
// Prints: foo=def&bar=1&baz=xyz
|
||||
```
|
||||
|
||||
```cjs
|
||||
const { MIMEType } = require('node:util');
|
||||
|
||||
const { params } = new MIMEType('text/plain;foo=0;bar=1');
|
||||
params.set('foo', 'def');
|
||||
params.set('baz', 'xyz');
|
||||
console.log(params.toString());
|
||||
// Prints: foo=def&bar=1&baz=xyz
|
||||
```
|
||||
|
||||
#### `mimeParams.values()`
|
||||
|
||||
* Returns: {Iterator}
|
||||
|
||||
Returns an iterator over the values of each name-value pair.
|
||||
|
||||
#### `mimeParams[@@iterator]()`
|
||||
|
||||
* Returns: {Iterator}
|
||||
|
||||
Alias for [`mimeParams.entries()`][].
|
||||
|
||||
```mjs
|
||||
import { MIMEType } from 'node:util';
|
||||
|
||||
const { params } = new MIMEType('text/plain;foo=bar;xyz=baz');
|
||||
for (const [name, value] of params) {
|
||||
console.log(name, value);
|
||||
}
|
||||
// Prints:
|
||||
// foo bar
|
||||
// xyz baz
|
||||
```
|
||||
|
||||
```cjs
|
||||
const { MIMEType } = require('node:util');
|
||||
|
||||
const { params } = new MIMEType('text/plain;foo=bar;xyz=baz');
|
||||
for (const [name, value] of params) {
|
||||
console.log(name, value);
|
||||
}
|
||||
// Prints:
|
||||
// foo bar
|
||||
// xyz baz
|
||||
```
|
||||
|
||||
## `util.parseArgs([config])`
|
||||
|
||||
<!-- YAML
|
||||
@ -2903,6 +3252,8 @@ util.log('Timestamped message.');
|
||||
[`Int16Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int16Array
|
||||
[`Int32Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int32Array
|
||||
[`Int8Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int8Array
|
||||
[`JSON.stringify()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
|
||||
[`MIMEparams`]: #class-utilmimeparams
|
||||
[`Map`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
|
||||
[`Object.assign()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
|
||||
[`Object.freeze()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
|
||||
@ -2920,6 +3271,8 @@ util.log('Timestamped message.');
|
||||
[`WebAssembly.Module`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Module
|
||||
[`assert.deepStrictEqual()`]: assert.md#assertdeepstrictequalactual-expected-message
|
||||
[`console.error()`]: console.md#consoleerrordata-args
|
||||
[`mime.toString()`]: #mimetostring
|
||||
[`mimeParams.entries()`]: #mimeparamsentries
|
||||
[`napi_create_external()`]: n-api.md#napi_create_external
|
||||
[`target` and `handler`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy#Terminology
|
||||
[`tty.hasColors()`]: tty.md#writestreamhascolorscount-env
|
||||
|
@ -639,6 +639,9 @@ RegExp.prototype.exec = () => null;
|
||||
// Core
|
||||
console.log(RegExpPrototypeTest(/o/, 'foo')); // false
|
||||
console.log(RegExpPrototypeExec(/o/, 'foo') !== null); // true
|
||||
|
||||
console.log(RegExpPrototypeSymbolSearch(/o/, 'foo')); // -1
|
||||
console.log(SafeStringPrototypeSearch('foo', /o/)); // 1
|
||||
```
|
||||
|
||||
#### Don't trust `RegExp` flags
|
||||
@ -668,19 +671,7 @@ Object.defineProperty(RegExp.prototype, 'global', { value: false });
|
||||
|
||||
// Core
|
||||
console.log(RegExpPrototypeSymbolReplace(/o/g, 'foo', 'a')); // 'fao'
|
||||
|
||||
const regex = /o/g;
|
||||
ObjectDefineProperties(regex, {
|
||||
dotAll: { value: false },
|
||||
exec: { value: undefined },
|
||||
flags: { value: 'g' },
|
||||
global: { value: true },
|
||||
ignoreCase: { value: false },
|
||||
multiline: { value: false },
|
||||
unicode: { value: false },
|
||||
sticky: { value: false },
|
||||
});
|
||||
console.log(RegExpPrototypeSymbolReplace(regex, 'foo', 'a')); // 'faa'
|
||||
console.log(RegExpPrototypeSymbolReplace(hardenRegExp(/o/g), 'foo', 'a')); // 'faa'
|
||||
```
|
||||
|
||||
### Defining object own properties
|
||||
|
@ -1310,6 +1310,10 @@ E('ERR_INVALID_FILE_URL_PATH', 'File URL path %s', TypeError);
|
||||
E('ERR_INVALID_HANDLE_TYPE', 'This handle type cannot be sent', TypeError);
|
||||
E('ERR_INVALID_HTTP_TOKEN', '%s must be a valid HTTP token ["%s"]', TypeError);
|
||||
E('ERR_INVALID_IP_ADDRESS', 'Invalid IP address: %s', TypeError);
|
||||
E('ERR_INVALID_MIME_SYNTAX', (production, str, invalidIndex) => {
|
||||
const msg = invalidIndex !== -1 ? ` at ${invalidIndex}` : '';
|
||||
return `The MIME syntax for a ${production} in "${str}" is invalid` + msg;
|
||||
}, TypeError);
|
||||
E('ERR_INVALID_MODULE_SPECIFIER', (request, reason, base = undefined) => {
|
||||
return `Invalid module "${request}" ${reason}${base ?
|
||||
` imported from ${base}` : ''}`;
|
||||
|
367
lib/internal/mime.js
Normal file
367
lib/internal/mime.js
Normal file
@ -0,0 +1,367 @@
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
FunctionPrototypeCall,
|
||||
ObjectDefineProperty,
|
||||
RegExpPrototypeExec,
|
||||
SafeMap,
|
||||
SafeStringPrototypeSearch,
|
||||
StringPrototypeCharAt,
|
||||
StringPrototypeIndexOf,
|
||||
StringPrototypeSlice,
|
||||
StringPrototypeToLowerCase,
|
||||
SymbolIterator,
|
||||
} = primordials;
|
||||
const {
|
||||
ERR_INVALID_MIME_SYNTAX,
|
||||
} = require('internal/errors').codes;
|
||||
|
||||
const NOT_HTTP_TOKEN_CODE_POINT = /[^!#$%&'*+\-.^_`|~A-Za-z0-9]/g;
|
||||
const NOT_HTTP_QUOTED_STRING_CODE_POINT = /[^\t\u0020-~\u0080-\u00FF]/g;
|
||||
|
||||
const END_BEGINNING_WHITESPACE = /[^\r\n\t ]|$/;
|
||||
const START_ENDING_WHITESPACE = /[\r\n\t ]*$/;
|
||||
|
||||
function toASCIILower(str) {
|
||||
let result = '';
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const char = str[i];
|
||||
|
||||
result += char >= 'A' && char <= 'Z' ?
|
||||
StringPrototypeToLowerCase(char) :
|
||||
char;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const SOLIDUS = '/';
|
||||
const SEMICOLON = ';';
|
||||
function parseTypeAndSubtype(str) {
|
||||
// Skip only HTTP whitespace from start
|
||||
let position = SafeStringPrototypeSearch(str, END_BEGINNING_WHITESPACE);
|
||||
// read until '/'
|
||||
const typeEnd = StringPrototypeIndexOf(str, SOLIDUS, position);
|
||||
const trimmedType = typeEnd === -1 ?
|
||||
StringPrototypeSlice(str, position) :
|
||||
StringPrototypeSlice(str, position, typeEnd);
|
||||
const invalidTypeIndex = SafeStringPrototypeSearch(trimmedType,
|
||||
NOT_HTTP_TOKEN_CODE_POINT);
|
||||
if (trimmedType === '' || invalidTypeIndex !== -1 || typeEnd === -1) {
|
||||
throw new ERR_INVALID_MIME_SYNTAX('type', str, invalidTypeIndex);
|
||||
}
|
||||
// skip type and '/'
|
||||
position = typeEnd + 1;
|
||||
const type = toASCIILower(trimmedType);
|
||||
// read until ';'
|
||||
const subtypeEnd = StringPrototypeIndexOf(str, SEMICOLON, position);
|
||||
const rawSubtype = subtypeEnd === -1 ?
|
||||
StringPrototypeSlice(str, position) :
|
||||
StringPrototypeSlice(str, position, subtypeEnd);
|
||||
position += rawSubtype.length;
|
||||
if (subtypeEnd !== -1) {
|
||||
// skip ';'
|
||||
position += 1;
|
||||
}
|
||||
const trimmedSubtype = StringPrototypeSlice(
|
||||
rawSubtype,
|
||||
0,
|
||||
SafeStringPrototypeSearch(rawSubtype, START_ENDING_WHITESPACE));
|
||||
const invalidSubtypeIndex = SafeStringPrototypeSearch(trimmedSubtype,
|
||||
NOT_HTTP_TOKEN_CODE_POINT);
|
||||
if (trimmedSubtype === '' || invalidSubtypeIndex !== -1) {
|
||||
throw new ERR_INVALID_MIME_SYNTAX('subtype', str, trimmedSubtype);
|
||||
}
|
||||
const subtype = toASCIILower(trimmedSubtype);
|
||||
return {
|
||||
__proto__: null,
|
||||
type,
|
||||
subtype,
|
||||
parametersStringIndex: position,
|
||||
};
|
||||
}
|
||||
|
||||
const EQUALS_SEMICOLON_OR_END = /[;=]|$/;
|
||||
const QUOTED_VALUE_PATTERN = /^(?:([\\]$)|[\\][\s\S]|[^"])*(?:(")|$)/u;
|
||||
|
||||
function removeBackslashes(str) {
|
||||
let ret = '';
|
||||
// We stop at str.length - 1 because we want to look ahead one character.
|
||||
let i;
|
||||
for (i = 0; i < str.length - 1; i++) {
|
||||
const c = str[i];
|
||||
if (c === '\\') {
|
||||
i++;
|
||||
ret += str[i];
|
||||
} else {
|
||||
ret += c;
|
||||
}
|
||||
}
|
||||
// We add the last character if we didn't skip to it.
|
||||
if (i === str.length - 1) {
|
||||
ret += str[i];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
function escapeQuoteOrSolidus(str) {
|
||||
let result = '';
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const char = str[i];
|
||||
result += (char === '"' || char === '\\') ? `\\${char}` : char;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const encode = (value) => {
|
||||
if (value.length === 0) return '""';
|
||||
const encode = SafeStringPrototypeSearch(value, NOT_HTTP_TOKEN_CODE_POINT) !== -1;
|
||||
if (!encode) return value;
|
||||
const escaped = escapeQuoteOrSolidus(value);
|
||||
return `"${escaped}"`;
|
||||
};
|
||||
|
||||
class MIMEParams {
|
||||
#data = new SafeMap();
|
||||
|
||||
delete(name) {
|
||||
this.#data.delete(name);
|
||||
}
|
||||
|
||||
get(name) {
|
||||
const data = this.#data;
|
||||
if (data.has(name)) {
|
||||
return data.get(name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
has(name) {
|
||||
return this.#data.has(name);
|
||||
}
|
||||
|
||||
set(name, value) {
|
||||
const data = this.#data;
|
||||
name = `${name}`;
|
||||
value = `${value}`;
|
||||
const invalidNameIndex = SafeStringPrototypeSearch(name, NOT_HTTP_TOKEN_CODE_POINT);
|
||||
if (name.length === 0 || invalidNameIndex !== -1) {
|
||||
throw new ERR_INVALID_MIME_SYNTAX(
|
||||
'parameter name',
|
||||
name,
|
||||
invalidNameIndex,
|
||||
);
|
||||
}
|
||||
const invalidValueIndex = SafeStringPrototypeSearch(
|
||||
value,
|
||||
NOT_HTTP_QUOTED_STRING_CODE_POINT);
|
||||
if (invalidValueIndex !== -1) {
|
||||
throw new ERR_INVALID_MIME_SYNTAX(
|
||||
'parameter value',
|
||||
value,
|
||||
invalidValueIndex,
|
||||
);
|
||||
}
|
||||
data.set(name, value);
|
||||
}
|
||||
|
||||
*entries() {
|
||||
yield* this.#data.entries();
|
||||
}
|
||||
|
||||
*keys() {
|
||||
yield* this.#data.keys();
|
||||
}
|
||||
|
||||
*values() {
|
||||
yield* this.#data.values();
|
||||
}
|
||||
|
||||
toString() {
|
||||
let ret = '';
|
||||
for (const { 0: key, 1: value } of this.#data) {
|
||||
const encoded = encode(value);
|
||||
// Ensure they are separated
|
||||
if (ret.length) ret += ';';
|
||||
ret += `${key}=${encoded}`;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Used to act as a friendly class to stringifying stuff
|
||||
// not meant to be exposed to users, could inject invalid values
|
||||
static parseParametersString(str, position, params) {
|
||||
const paramsMap = params.#data;
|
||||
const endOfSource = SafeStringPrototypeSearch(
|
||||
StringPrototypeSlice(str, position),
|
||||
START_ENDING_WHITESPACE
|
||||
) + position;
|
||||
while (position < endOfSource) {
|
||||
// Skip any whitespace before parameter
|
||||
position += SafeStringPrototypeSearch(
|
||||
StringPrototypeSlice(str, position),
|
||||
END_BEGINNING_WHITESPACE
|
||||
);
|
||||
// Read until ';' or '='
|
||||
const afterParameterName = SafeStringPrototypeSearch(
|
||||
StringPrototypeSlice(str, position),
|
||||
EQUALS_SEMICOLON_OR_END
|
||||
) + position;
|
||||
const parameterString = toASCIILower(
|
||||
StringPrototypeSlice(str, position, afterParameterName)
|
||||
);
|
||||
position = afterParameterName;
|
||||
// If we found a terminating character
|
||||
if (position < endOfSource) {
|
||||
// Safe to use because we never do special actions for surrogate pairs
|
||||
const char = StringPrototypeCharAt(str, position);
|
||||
// Skip the terminating character
|
||||
position += 1;
|
||||
// Ignore parameters without values
|
||||
if (char === ';') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// If we are at end of the string, it cannot have a value
|
||||
if (position >= endOfSource) break;
|
||||
// Safe to use because we never do special actions for surrogate pairs
|
||||
const char = StringPrototypeCharAt(str, position);
|
||||
let parameterValue = null;
|
||||
if (char === '"') {
|
||||
// Handle quoted-string form of values
|
||||
// skip '"'
|
||||
position += 1;
|
||||
// Find matching closing '"' or end of string
|
||||
// use $1 to see if we terminated on unmatched '\'
|
||||
// use $2 to see if we terminated on a matching '"'
|
||||
// so we can skip the last char in either case
|
||||
const insideMatch = RegExpPrototypeExec(
|
||||
QUOTED_VALUE_PATTERN,
|
||||
StringPrototypeSlice(str, position));
|
||||
position += insideMatch[0].length;
|
||||
// Skip including last character if an unmatched '\' or '"' during
|
||||
// unescape
|
||||
const inside = insideMatch[1] || insideMatch[2] ?
|
||||
StringPrototypeSlice(insideMatch[0], 0, -1) :
|
||||
insideMatch[0];
|
||||
// Unescape '\' quoted characters
|
||||
parameterValue = removeBackslashes(inside);
|
||||
// If we did have an unmatched '\' add it back to the end
|
||||
if (insideMatch[1]) parameterValue += '\\';
|
||||
} else {
|
||||
// Handle the normal parameter value form
|
||||
const valueEnd = StringPrototypeIndexOf(str, SEMICOLON, position);
|
||||
const rawValue = valueEnd === -1 ?
|
||||
StringPrototypeSlice(str, position) :
|
||||
StringPrototypeSlice(str, position, valueEnd);
|
||||
position += rawValue.length;
|
||||
const trimmedValue = StringPrototypeSlice(
|
||||
rawValue,
|
||||
0,
|
||||
SafeStringPrototypeSearch(rawValue, START_ENDING_WHITESPACE)
|
||||
);
|
||||
// Ignore parameters without values
|
||||
if (trimmedValue === '') continue;
|
||||
parameterValue = trimmedValue;
|
||||
}
|
||||
if (
|
||||
parameterString !== '' &&
|
||||
SafeStringPrototypeSearch(parameterString,
|
||||
NOT_HTTP_TOKEN_CODE_POINT) === -1 &&
|
||||
SafeStringPrototypeSearch(parameterValue,
|
||||
NOT_HTTP_QUOTED_STRING_CODE_POINT) === -1 &&
|
||||
params.has(parameterString) === false
|
||||
) {
|
||||
paramsMap.set(parameterString, parameterValue);
|
||||
}
|
||||
position++;
|
||||
}
|
||||
return paramsMap;
|
||||
}
|
||||
}
|
||||
const MIMEParamsStringify = MIMEParams.prototype.toString;
|
||||
ObjectDefineProperty(MIMEParams.prototype, SymbolIterator, {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
value: MIMEParams.prototype.entries,
|
||||
writable: true,
|
||||
});
|
||||
ObjectDefineProperty(MIMEParams.prototype, 'toJSON', {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
value: MIMEParamsStringify,
|
||||
writable: true,
|
||||
});
|
||||
|
||||
const { parseParametersString } = MIMEParams;
|
||||
delete MIMEParams.parseParametersString;
|
||||
|
||||
class MIMEType {
|
||||
#type;
|
||||
#subtype;
|
||||
#parameters;
|
||||
constructor(string) {
|
||||
string = `${string}`;
|
||||
const data = parseTypeAndSubtype(string);
|
||||
this.#type = data.type;
|
||||
this.#subtype = data.subtype;
|
||||
this.#parameters = new MIMEParams();
|
||||
parseParametersString(
|
||||
string,
|
||||
data.parametersStringIndex,
|
||||
this.#parameters,
|
||||
);
|
||||
}
|
||||
|
||||
get type() {
|
||||
return this.#type;
|
||||
}
|
||||
|
||||
set type(v) {
|
||||
v = `${v}`;
|
||||
const invalidTypeIndex = SafeStringPrototypeSearch(v, NOT_HTTP_TOKEN_CODE_POINT);
|
||||
if (v.length === 0 || invalidTypeIndex !== -1) {
|
||||
throw new ERR_INVALID_MIME_SYNTAX('type', v, invalidTypeIndex);
|
||||
}
|
||||
this.#type = toASCIILower(v);
|
||||
}
|
||||
|
||||
get subtype() {
|
||||
return this.#subtype;
|
||||
}
|
||||
|
||||
set subtype(v) {
|
||||
v = `${v}`;
|
||||
const invalidSubtypeIndex = SafeStringPrototypeSearch(v, NOT_HTTP_TOKEN_CODE_POINT);
|
||||
if (v.length === 0 || invalidSubtypeIndex !== -1) {
|
||||
throw new ERR_INVALID_MIME_SYNTAX('subtype', v, invalidSubtypeIndex);
|
||||
}
|
||||
this.#subtype = toASCIILower(v);
|
||||
}
|
||||
|
||||
get essence() {
|
||||
return `${this.#type}/${this.#subtype}`;
|
||||
}
|
||||
|
||||
get params() {
|
||||
return this.#parameters;
|
||||
}
|
||||
|
||||
toString() {
|
||||
let ret = `${this.#type}/${this.#subtype}`;
|
||||
const paramStr = FunctionPrototypeCall(MIMEParamsStringify, this.#parameters);
|
||||
if (paramStr.length) ret += `;${paramStr}`;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
ObjectDefineProperty(MIMEType.prototype, 'toJSON', {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
value: MIMEType.prototype.toString,
|
||||
writable: true,
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
MIMEParams,
|
||||
MIMEType,
|
||||
};
|
@ -266,12 +266,36 @@ const {
|
||||
FinalizationRegistry,
|
||||
FunctionPrototypeCall,
|
||||
Map,
|
||||
ObjectDefineProperties,
|
||||
ObjectDefineProperty,
|
||||
ObjectFreeze,
|
||||
ObjectSetPrototypeOf,
|
||||
Promise,
|
||||
PromisePrototypeThen,
|
||||
ReflectApply,
|
||||
ReflectConstruct,
|
||||
ReflectSet,
|
||||
ReflectGet,
|
||||
RegExp,
|
||||
RegExpPrototype,
|
||||
RegExpPrototypeExec,
|
||||
RegExpPrototypeGetDotAll,
|
||||
RegExpPrototypeGetFlags,
|
||||
RegExpPrototypeGetGlobal,
|
||||
RegExpPrototypeGetHasIndices,
|
||||
RegExpPrototypeGetIgnoreCase,
|
||||
RegExpPrototypeGetMultiline,
|
||||
RegExpPrototypeGetSource,
|
||||
RegExpPrototypeGetSticky,
|
||||
RegExpPrototypeGetUnicode,
|
||||
Set,
|
||||
SymbolIterator,
|
||||
SymbolMatch,
|
||||
SymbolMatchAll,
|
||||
SymbolReplace,
|
||||
SymbolSearch,
|
||||
SymbolSpecies,
|
||||
SymbolSplit,
|
||||
WeakMap,
|
||||
WeakRef,
|
||||
WeakSet,
|
||||
@ -490,5 +514,128 @@ primordials.SafePromiseRace = (promises, mapFn) =>
|
||||
);
|
||||
|
||||
|
||||
const {
|
||||
exec: OriginalRegExpPrototypeExec,
|
||||
[SymbolMatch]: OriginalRegExpPrototypeSymbolMatch,
|
||||
[SymbolMatchAll]: OriginalRegExpPrototypeSymbolMatchAll,
|
||||
[SymbolReplace]: OriginalRegExpPrototypeSymbolReplace,
|
||||
[SymbolSearch]: OriginalRegExpPrototypeSymbolSearch,
|
||||
[SymbolSplit]: OriginalRegExpPrototypeSymbolSplit,
|
||||
} = RegExpPrototype;
|
||||
|
||||
class RegExpLikeForStringSplitting {
|
||||
#regex;
|
||||
constructor() {
|
||||
this.#regex = ReflectConstruct(RegExp, arguments);
|
||||
}
|
||||
|
||||
get lastIndex() {
|
||||
return ReflectGet(this.#regex, 'lastIndex');
|
||||
}
|
||||
set lastIndex(value) {
|
||||
ReflectSet(this.#regex, 'lastIndex', value);
|
||||
}
|
||||
|
||||
exec() {
|
||||
return ReflectApply(OriginalRegExpPrototypeExec, this.#regex, arguments);
|
||||
}
|
||||
}
|
||||
ObjectSetPrototypeOf(RegExpLikeForStringSplitting.prototype, null);
|
||||
|
||||
primordials.hardenRegExp = function hardenRegExp(pattern) {
|
||||
ObjectDefineProperties(pattern, {
|
||||
[SymbolMatch]: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
value: OriginalRegExpPrototypeSymbolMatch,
|
||||
},
|
||||
[SymbolMatchAll]: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
value: OriginalRegExpPrototypeSymbolMatchAll,
|
||||
},
|
||||
[SymbolReplace]: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
value: OriginalRegExpPrototypeSymbolReplace,
|
||||
},
|
||||
[SymbolSearch]: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
value: OriginalRegExpPrototypeSymbolSearch,
|
||||
},
|
||||
[SymbolSplit]: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
value: OriginalRegExpPrototypeSymbolSplit,
|
||||
},
|
||||
constructor: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
value: {
|
||||
[SymbolSpecies]: RegExpLikeForStringSplitting,
|
||||
}
|
||||
},
|
||||
dotAll: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
value: RegExpPrototypeGetDotAll(pattern),
|
||||
},
|
||||
exec: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
value: OriginalRegExpPrototypeExec,
|
||||
},
|
||||
global: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
value: RegExpPrototypeGetGlobal(pattern),
|
||||
},
|
||||
hasIndices: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
value: RegExpPrototypeGetHasIndices(pattern),
|
||||
},
|
||||
ignoreCase: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
value: RegExpPrototypeGetIgnoreCase(pattern),
|
||||
},
|
||||
multiline: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
value: RegExpPrototypeGetMultiline(pattern),
|
||||
},
|
||||
source: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
value: RegExpPrototypeGetSource(pattern),
|
||||
},
|
||||
sticky: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
value: RegExpPrototypeGetSticky(pattern),
|
||||
},
|
||||
unicode: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
value: RegExpPrototypeGetUnicode(pattern),
|
||||
},
|
||||
});
|
||||
ObjectDefineProperty(pattern, 'flags', {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
value: RegExpPrototypeGetFlags(pattern),
|
||||
});
|
||||
return pattern;
|
||||
};
|
||||
|
||||
|
||||
primordials.SafeStringPrototypeSearch = (str, regexp) => {
|
||||
regexp.lastIndex = 0;
|
||||
const match = RegExpPrototypeExec(regexp, str);
|
||||
return match ? match.index : -1;
|
||||
};
|
||||
|
||||
ObjectSetPrototypeOf(primordials, null);
|
||||
ObjectFreeze(primordials);
|
||||
|
@ -68,6 +68,7 @@ const {
|
||||
validateNumber,
|
||||
} = require('internal/validators');
|
||||
const { TextDecoder, TextEncoder } = require('internal/encoding');
|
||||
const { MIMEType, MIMEParams } = require('internal/mime');
|
||||
const { isBuffer } = require('buffer').Buffer;
|
||||
const types = require('internal/util/types');
|
||||
|
||||
@ -385,6 +386,8 @@ module.exports = {
|
||||
isFunction,
|
||||
isPrimitive,
|
||||
log,
|
||||
MIMEType,
|
||||
MIMEParams,
|
||||
parseArgs,
|
||||
promisify,
|
||||
stripVTControlCharacters,
|
||||
|
3533
test/fixtures/mime-whatwg-generated.js
vendored
Normal file
3533
test/fixtures/mime-whatwg-generated.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
392
test/fixtures/mime-whatwg.js
vendored
Normal file
392
test/fixtures/mime-whatwg.js
vendored
Normal file
@ -0,0 +1,392 @@
|
||||
'use strict';
|
||||
|
||||
// TODO: Incorporate this with the other WPT tests if it makes sense to do that.
|
||||
|
||||
/* The following tests are copied from WPT. Modifications to them should be
|
||||
upstreamed first. Refs:
|
||||
https://github.com/w3c/web-platform-tests/blob/88b75886e/url/urltestdata.json
|
||||
License: http://www.w3.org/Consortium/Legal/2008/04-testsuite-copyright.html
|
||||
*/
|
||||
module.exports = [
|
||||
"Basics",
|
||||
{
|
||||
"input": "text/html;charset=gbk",
|
||||
"output": "text/html;charset=gbk",
|
||||
"navigable": true,
|
||||
"encoding": "GBK"
|
||||
},
|
||||
{
|
||||
"input": "TEXT/HTML;CHARSET=GBK",
|
||||
"output": "text/html;charset=GBK",
|
||||
"navigable": true,
|
||||
"encoding": "GBK"
|
||||
},
|
||||
"Legacy comment syntax",
|
||||
{
|
||||
"input": "text/html;charset=gbk(",
|
||||
"output": "text/html;charset=\"gbk(\"",
|
||||
"navigable": true,
|
||||
"encoding": null
|
||||
},
|
||||
{
|
||||
"input": "text/html;x=(;charset=gbk",
|
||||
"output": "text/html;x=\"(\";charset=gbk",
|
||||
"navigable": true,
|
||||
"encoding": "GBK"
|
||||
},
|
||||
"Duplicate parameter",
|
||||
{
|
||||
"input": "text/html;charset=gbk;charset=windows-1255",
|
||||
"output": "text/html;charset=gbk",
|
||||
"navigable": true,
|
||||
"encoding": "GBK"
|
||||
},
|
||||
{
|
||||
"input": "text/html;charset=();charset=GBK",
|
||||
"output": "text/html;charset=\"()\"",
|
||||
"navigable": true,
|
||||
"encoding": null
|
||||
},
|
||||
"Spaces",
|
||||
{
|
||||
"input": "text/html;charset =gbk",
|
||||
"output": "text/html",
|
||||
"navigable": true,
|
||||
"encoding": null
|
||||
},
|
||||
{
|
||||
"input": "text/html ;charset=gbk",
|
||||
"output": "text/html;charset=gbk",
|
||||
"navigable": true,
|
||||
"encoding": "GBK"
|
||||
},
|
||||
{
|
||||
"input": "text/html; charset=gbk",
|
||||
"output": "text/html;charset=gbk",
|
||||
"navigable": true,
|
||||
"encoding": "GBK"
|
||||
},
|
||||
{
|
||||
"input": "text/html;charset= gbk",
|
||||
"output": "text/html;charset=\" gbk\"",
|
||||
"navigable": true,
|
||||
"encoding": "GBK"
|
||||
},
|
||||
{
|
||||
"input": "text/html;charset= \"gbk\"",
|
||||
"output": "text/html;charset=\" \\\"gbk\\\"\"",
|
||||
"navigable": true,
|
||||
"encoding": null
|
||||
},
|
||||
"0x0B and 0x0C",
|
||||
{
|
||||
"input": "text/html;charset=\u000Bgbk",
|
||||
"output": "text/html",
|
||||
"navigable": true,
|
||||
"encoding": null
|
||||
},
|
||||
{
|
||||
"input": "text/html;charset=\u000Cgbk",
|
||||
"output": "text/html",
|
||||
"navigable": true,
|
||||
"encoding": null
|
||||
},
|
||||
{
|
||||
"input": "text/html;\u000Bcharset=gbk",
|
||||
"output": "text/html",
|
||||
"navigable": true,
|
||||
"encoding": null
|
||||
},
|
||||
{
|
||||
"input": "text/html;\u000Ccharset=gbk",
|
||||
"output": "text/html",
|
||||
"navigable": true,
|
||||
"encoding": null
|
||||
},
|
||||
"Single quotes are a token, not a delimiter",
|
||||
{
|
||||
"input": "text/html;charset='gbk'",
|
||||
"output": "text/html;charset='gbk'",
|
||||
"navigable": true,
|
||||
"encoding": null
|
||||
},
|
||||
{
|
||||
"input": "text/html;charset='gbk",
|
||||
"output": "text/html;charset='gbk",
|
||||
"navigable": true,
|
||||
"encoding": null
|
||||
},
|
||||
{
|
||||
"input": "text/html;charset=gbk'",
|
||||
"output": "text/html;charset=gbk'",
|
||||
"navigable": true,
|
||||
"encoding": null
|
||||
},
|
||||
{
|
||||
"input": "text/html;charset=';charset=GBK",
|
||||
"output": "text/html;charset='",
|
||||
"navigable": true,
|
||||
"encoding": null
|
||||
},
|
||||
"Invalid parameters",
|
||||
{
|
||||
"input": "text/html;test;charset=gbk",
|
||||
"output": "text/html;charset=gbk",
|
||||
"navigable": true,
|
||||
"encoding": "GBK"
|
||||
},
|
||||
{
|
||||
"input": "text/html;test=;charset=gbk",
|
||||
"output": "text/html;charset=gbk",
|
||||
"navigable": true,
|
||||
"encoding": "GBK"
|
||||
},
|
||||
{
|
||||
"input": "text/html;';charset=gbk",
|
||||
"output": "text/html;charset=gbk",
|
||||
"navigable": true,
|
||||
"encoding": "GBK"
|
||||
},
|
||||
{
|
||||
"input": "text/html;\";charset=gbk",
|
||||
"output": "text/html;charset=gbk",
|
||||
"navigable": true,
|
||||
"encoding": "GBK"
|
||||
},
|
||||
{
|
||||
"input": "text/html ; ; charset=gbk",
|
||||
"output": "text/html;charset=gbk",
|
||||
"navigable": true,
|
||||
"encoding": "GBK"
|
||||
},
|
||||
{
|
||||
"input": "text/html;;;;charset=gbk",
|
||||
"output": "text/html;charset=gbk",
|
||||
"navigable": true,
|
||||
"encoding": "GBK"
|
||||
},
|
||||
{
|
||||
"input": "text/html;charset= \"\u007F;charset=GBK",
|
||||
"output": "text/html;charset=GBK",
|
||||
"navigable": true,
|
||||
"encoding": "GBK"
|
||||
},
|
||||
{
|
||||
"input": "text/html;charset=\"\u007F;charset=foo\";charset=GBK",
|
||||
"output": "text/html;charset=GBK",
|
||||
"navigable": true,
|
||||
"encoding": "GBK"
|
||||
},
|
||||
"Double quotes",
|
||||
{
|
||||
"input": "text/html;charset=\"gbk\"",
|
||||
"output": "text/html;charset=gbk",
|
||||
"navigable": true,
|
||||
"encoding": "GBK"
|
||||
},
|
||||
{
|
||||
"input": "text/html;charset=\"gbk",
|
||||
"output": "text/html;charset=gbk",
|
||||
"navigable": true,
|
||||
"encoding": "GBK"
|
||||
},
|
||||
{
|
||||
"input": "text/html;charset=gbk\"",
|
||||
"output": "text/html;charset=\"gbk\\\"\"",
|
||||
"navigable": true,
|
||||
"encoding": null
|
||||
},
|
||||
{
|
||||
"input": "text/html;charset=\" gbk\"",
|
||||
"output": "text/html;charset=\" gbk\"",
|
||||
"navigable": true,
|
||||
"encoding": "GBK"
|
||||
},
|
||||
{
|
||||
"input": "text/html;charset=\"gbk \"",
|
||||
"output": "text/html;charset=\"gbk \"",
|
||||
"navigable": true,
|
||||
"encoding": "GBK"
|
||||
},
|
||||
{
|
||||
"input": "text/html;charset=\"\\ gbk\"",
|
||||
"output": "text/html;charset=\" gbk\"",
|
||||
"navigable": true,
|
||||
"encoding": "GBK"
|
||||
},
|
||||
{
|
||||
"input": "text/html;charset=\"\\g\\b\\k\"",
|
||||
"output": "text/html;charset=gbk",
|
||||
"navigable": true,
|
||||
"encoding": "GBK"
|
||||
},
|
||||
{
|
||||
"input": "text/html;charset=\"gbk\"x",
|
||||
"output": "text/html;charset=gbk",
|
||||
"navigable": true,
|
||||
"encoding": "GBK"
|
||||
},
|
||||
{
|
||||
"input": "text/html;charset=\"\";charset=GBK",
|
||||
"output": "text/html;charset=\"\"",
|
||||
"navigable": true,
|
||||
"encoding": null
|
||||
},
|
||||
{
|
||||
"input": "text/html;charset=\";charset=GBK",
|
||||
"output": "text/html;charset=\";charset=GBK\"",
|
||||
"navigable": true,
|
||||
"encoding": null
|
||||
},
|
||||
"Unexpected code points",
|
||||
{
|
||||
"input": "text/html;charset={gbk}",
|
||||
"output": "text/html;charset=\"{gbk}\"",
|
||||
"navigable": true,
|
||||
"encoding": null
|
||||
},
|
||||
"Parameter name longer than 127",
|
||||
{
|
||||
"input": "text/html;0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789=x;charset=gbk",
|
||||
"output": "text/html;0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789=x;charset=gbk",
|
||||
"navigable": true,
|
||||
"encoding": "GBK"
|
||||
},
|
||||
"type/subtype longer than 127",
|
||||
{
|
||||
"input": "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789/0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789",
|
||||
"output": "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789/0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
|
||||
},
|
||||
"Valid",
|
||||
{
|
||||
"input": "!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz;!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
|
||||
"output": "!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz/!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz;!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz=!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
},
|
||||
{
|
||||
"input": "x/x;x=\"\t !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008A\u008B\u008C\u008D\u008E\u008F\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009A\u009B\u009C\u009D\u009E\u009F\u00A0\u00A1\u00A2\u00A3\u00A4\u00A5\u00A6\u00A7\u00A8\u00A9\u00AA\u00AB\u00AC\u00AD\u00AE\u00AF\u00B0\u00B1\u00B2\u00B3\u00B4\u00B5\u00B6\u00B7\u00B8\u00B9\u00BA\u00BB\u00BC\u00BD\u00BE\u00BF\u00C0\u00C1\u00C2\u00C3\u00C4\u00C5\u00C6\u00C7\u00C8\u00C9\u00CA\u00CB\u00CC\u00CD\u00CE\u00CF\u00D0\u00D1\u00D2\u00D3\u00D4\u00D5\u00D6\u00D7\u00D8\u00D9\u00DA\u00DB\u00DC\u00DD\u00DE\u00DF\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5\u00E6\u00E7\u00E8\u00E9\u00EA\u00EB\u00EC\u00ED\u00EE\u00EF\u00F0\u00F1\u00F2\u00F3\u00F4\u00F5\u00F6\u00F7\u00F8\u00F9\u00FA\u00FB\u00FC\u00FD\u00FE\u00FF\"",
|
||||
"output": "x/x;x=\"\t !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008A\u008B\u008C\u008D\u008E\u008F\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009A\u009B\u009C\u009D\u009E\u009F\u00A0\u00A1\u00A2\u00A3\u00A4\u00A5\u00A6\u00A7\u00A8\u00A9\u00AA\u00AB\u00AC\u00AD\u00AE\u00AF\u00B0\u00B1\u00B2\u00B3\u00B4\u00B5\u00B6\u00B7\u00B8\u00B9\u00BA\u00BB\u00BC\u00BD\u00BE\u00BF\u00C0\u00C1\u00C2\u00C3\u00C4\u00C5\u00C6\u00C7\u00C8\u00C9\u00CA\u00CB\u00CC\u00CD\u00CE\u00CF\u00D0\u00D1\u00D2\u00D3\u00D4\u00D5\u00D6\u00D7\u00D8\u00D9\u00DA\u00DB\u00DC\u00DD\u00DE\u00DF\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5\u00E6\u00E7\u00E8\u00E9\u00EA\u00EB\u00EC\u00ED\u00EE\u00EF\u00F0\u00F1\u00F2\u00F3\u00F4\u00F5\u00F6\u00F7\u00F8\u00F9\u00FA\u00FB\u00FC\u00FD\u00FE\u00FF\""
|
||||
},
|
||||
"End-of-file handling",
|
||||
{
|
||||
"input": "x/x;test",
|
||||
"output": "x/x"
|
||||
},
|
||||
{
|
||||
"input": "x/x;test=\"\\",
|
||||
"output": "x/x;test=\"\\\\\""
|
||||
},
|
||||
"Whitespace (not handled by generated-mime-types.json or above)",
|
||||
{
|
||||
"input": "x/x;x= ",
|
||||
"output": "x/x"
|
||||
},
|
||||
{
|
||||
"input": "x/x;x=\t",
|
||||
"output": "x/x"
|
||||
},
|
||||
{
|
||||
"input": "x/x\n\r\t ;x=x",
|
||||
"output": "x/x;x=x"
|
||||
},
|
||||
{
|
||||
"input": "\n\r\t x/x;x=x\n\r\t ",
|
||||
"output": "x/x;x=x"
|
||||
},
|
||||
{
|
||||
"input": "x/x;\n\r\t x=x\n\r\t ;x=y",
|
||||
"output": "x/x;x=x"
|
||||
},
|
||||
"Latin1",
|
||||
{
|
||||
"input": "text/html;test=\u00FF;charset=gbk",
|
||||
"output": "text/html;test=\"\u00FF\";charset=gbk",
|
||||
"navigable": true,
|
||||
"encoding": "GBK"
|
||||
},
|
||||
">Latin1",
|
||||
{
|
||||
"input": "x/x;test=\uFFFD;x=x",
|
||||
"output": "x/x;x=x"
|
||||
},
|
||||
"Failure",
|
||||
{
|
||||
"input": "\u000Bx/x",
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"input": "\u000Cx/x",
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"input": "x/x\u000B",
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"input": "x/x\u000C",
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"input": "",
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"input": "\t",
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"input": "/",
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"input": "bogus",
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"input": "bogus/",
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"input": "bogus/ ",
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"input": "bogus/bogus/;",
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"input": "</>",
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"input": "(/)",
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"input": "ÿ/ÿ",
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"input": "text/html(;doesnot=matter",
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"input": "{/}",
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"input": "\u0100/\u0100",
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"input": "text /html",
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"input": "text/ html",
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"input": "\"text/html\"",
|
||||
"output": null
|
||||
}
|
||||
];
|
@ -72,6 +72,7 @@ const expectedModules = new Set([
|
||||
'NativeModule internal/histogram',
|
||||
'NativeModule internal/idna',
|
||||
'NativeModule internal/linkedlist',
|
||||
'NativeModule internal/mime',
|
||||
'NativeModule internal/modules/cjs/helpers',
|
||||
'NativeModule internal/modules/cjs/loader',
|
||||
'NativeModule internal/modules/esm/assert',
|
||||
|
@ -164,7 +164,7 @@ new RuleTester({
|
||||
},
|
||||
{
|
||||
code: 'RegExpPrototypeSymbolSearch(/some regex/, "some string")',
|
||||
errors: [{ message: /looks up the "exec" property/ }],
|
||||
errors: [{ message: /SafeStringPrototypeSearch/ }],
|
||||
},
|
||||
{
|
||||
code: 'StringPrototypeMatch("some string", /some regex/)',
|
||||
@ -204,7 +204,7 @@ new RuleTester({
|
||||
},
|
||||
{
|
||||
code: 'StringPrototypeSearch("some string", /some regex/)',
|
||||
errors: [{ message: /looks up the Symbol\.search property/ }],
|
||||
errors: [{ message: /SafeStringPrototypeSearch/ }],
|
||||
},
|
||||
{
|
||||
code: 'StringPrototypeSplit("some string", /some regex/)',
|
||||
|
160
test/parallel/test-mime-api.js
Normal file
160
test/parallel/test-mime-api.js
Normal file
@ -0,0 +1,160 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const { MIMEType, MIMEParams } = require('util');
|
||||
|
||||
|
||||
const WHITESPACES = '\t\n\f\r ';
|
||||
const NOT_HTTP_TOKEN_CODE_POINT = ',';
|
||||
const NOT_HTTP_QUOTED_STRING_CODE_POINT = '\n';
|
||||
|
||||
const mime = new MIMEType('application/ecmascript; ');
|
||||
const mime_descriptors = Object.getOwnPropertyDescriptors(mime);
|
||||
const mime_proto = Object.getPrototypeOf(mime);
|
||||
const mime_impersonator = Object.create(mime_proto);
|
||||
for (const key of Object.keys(mime_descriptors)) {
|
||||
const descriptor = mime_descriptors[key];
|
||||
if (descriptor.get) {
|
||||
assert.throws(descriptor.get.call(mime_impersonator), /Invalid receiver/i);
|
||||
}
|
||||
if (descriptor.set) {
|
||||
assert.throws(descriptor.set.call(mime_impersonator, 'x'), /Invalid receiver/i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
assert.strictEqual(
|
||||
JSON.stringify(mime),
|
||||
JSON.stringify('application/ecmascript'));
|
||||
assert.strictEqual(`${mime}`, 'application/ecmascript');
|
||||
assert.strictEqual(mime.essence, 'application/ecmascript');
|
||||
assert.strictEqual(mime.type, 'application');
|
||||
assert.strictEqual(mime.subtype, 'ecmascript');
|
||||
assert.ok(mime.params);
|
||||
assert.deepStrictEqual([], [...mime.params]);
|
||||
assert.strictEqual(mime.params.has('not found'), false);
|
||||
assert.strictEqual(mime.params.get('not found'), null);
|
||||
assert.strictEqual(mime.params.delete('not found'), undefined);
|
||||
|
||||
|
||||
mime.type = 'text';
|
||||
assert.strictEqual(mime.type, 'text');
|
||||
assert.strictEqual(JSON.stringify(mime), JSON.stringify('text/ecmascript'));
|
||||
assert.strictEqual(`${mime}`, 'text/ecmascript');
|
||||
assert.strictEqual(mime.essence, 'text/ecmascript');
|
||||
|
||||
assert.throws(() => {
|
||||
mime.type = `${WHITESPACES}text`;
|
||||
}, /ERR_INVALID_MIME_SYNTAX/);
|
||||
|
||||
assert.throws(() => mime.type = '', /type/i);
|
||||
assert.throws(() => mime.type = '/', /type/i);
|
||||
assert.throws(() => mime.type = 'x/', /type/i);
|
||||
assert.throws(() => mime.type = '/x', /type/i);
|
||||
assert.throws(() => mime.type = NOT_HTTP_TOKEN_CODE_POINT, /type/i);
|
||||
assert.throws(() => mime.type = `${NOT_HTTP_TOKEN_CODE_POINT}/`, /type/i);
|
||||
assert.throws(() => mime.type = `/${NOT_HTTP_TOKEN_CODE_POINT}`, /type/i);
|
||||
|
||||
|
||||
mime.subtype = 'javascript';
|
||||
assert.strictEqual(mime.type, 'text');
|
||||
assert.strictEqual(JSON.stringify(mime), JSON.stringify('text/javascript'));
|
||||
assert.strictEqual(`${mime}`, 'text/javascript');
|
||||
assert.strictEqual(mime.essence, 'text/javascript');
|
||||
assert.strictEqual(`${mime.params}`, '');
|
||||
assert.strictEqual(`${new MIMEParams()}`, '');
|
||||
assert.strictEqual(`${new MIMEParams(mime.params)}`, '');
|
||||
assert.strictEqual(`${new MIMEParams(`${mime.params}`)}`, '');
|
||||
|
||||
assert.throws(() => {
|
||||
mime.subtype = `javascript${WHITESPACES}`;
|
||||
}, /ERR_INVALID_MIME_SYNTAX/);
|
||||
|
||||
assert.throws(() => mime.subtype = '', /subtype/i);
|
||||
assert.throws(() => mime.subtype = ';', /subtype/i);
|
||||
assert.throws(() => mime.subtype = 'x;', /subtype/i);
|
||||
assert.throws(() => mime.subtype = ';x', /subtype/i);
|
||||
assert.throws(() => mime.subtype = NOT_HTTP_TOKEN_CODE_POINT, /subtype/i);
|
||||
assert.throws(
|
||||
() => mime.subtype = `${NOT_HTTP_TOKEN_CODE_POINT};`,
|
||||
/subtype/i);
|
||||
assert.throws(
|
||||
() => mime.subtype = `;${NOT_HTTP_TOKEN_CODE_POINT}`,
|
||||
/subtype/i);
|
||||
|
||||
|
||||
const params = mime.params;
|
||||
params.set('charset', 'utf-8');
|
||||
assert.strictEqual(params.has('charset'), true);
|
||||
assert.strictEqual(params.get('charset'), 'utf-8');
|
||||
assert.deepStrictEqual([...params], [['charset', 'utf-8']]);
|
||||
assert.strictEqual(
|
||||
JSON.stringify(mime),
|
||||
JSON.stringify('text/javascript;charset=utf-8'));
|
||||
assert.strictEqual(`${mime}`, 'text/javascript;charset=utf-8');
|
||||
assert.strictEqual(mime.essence, 'text/javascript');
|
||||
assert.strictEqual(`${mime.params}`, 'charset=utf-8');
|
||||
assert.strictEqual(`${new MIMEParams(mime.params)}`, '');
|
||||
assert.strictEqual(`${new MIMEParams(`${mime.params}`)}`, '');
|
||||
|
||||
params.set('goal', 'module');
|
||||
assert.strictEqual(params.has('goal'), true);
|
||||
assert.strictEqual(params.get('goal'), 'module');
|
||||
assert.deepStrictEqual([...params], [['charset', 'utf-8'], ['goal', 'module']]);
|
||||
assert.strictEqual(
|
||||
JSON.stringify(mime),
|
||||
JSON.stringify('text/javascript;charset=utf-8;goal=module'));
|
||||
assert.strictEqual(`${mime}`, 'text/javascript;charset=utf-8;goal=module');
|
||||
assert.strictEqual(mime.essence, 'text/javascript');
|
||||
assert.strictEqual(`${mime.params}`, 'charset=utf-8;goal=module');
|
||||
assert.strictEqual(`${new MIMEParams(mime.params)}`, '');
|
||||
assert.strictEqual(`${new MIMEParams(`${mime.params}`)}`, '');
|
||||
|
||||
assert.throws(() => {
|
||||
params.set(`${WHITESPACES}goal`, 'module');
|
||||
}, /ERR_INVALID_MIME_SYNTAX/);
|
||||
|
||||
params.set('charset', 'iso-8859-1');
|
||||
assert.strictEqual(params.has('charset'), true);
|
||||
assert.strictEqual(params.get('charset'), 'iso-8859-1');
|
||||
assert.deepStrictEqual(
|
||||
[...params],
|
||||
[['charset', 'iso-8859-1'], ['goal', 'module']]);
|
||||
assert.strictEqual(
|
||||
JSON.stringify(mime),
|
||||
JSON.stringify('text/javascript;charset=iso-8859-1;goal=module'));
|
||||
assert.strictEqual(`${mime}`, 'text/javascript;charset=iso-8859-1;goal=module');
|
||||
assert.strictEqual(mime.essence, 'text/javascript');
|
||||
|
||||
params.delete('charset');
|
||||
assert.strictEqual(params.has('charset'), false);
|
||||
assert.strictEqual(params.get('charset'), null);
|
||||
assert.deepStrictEqual([...params], [['goal', 'module']]);
|
||||
assert.strictEqual(
|
||||
JSON.stringify(mime),
|
||||
JSON.stringify('text/javascript;goal=module'));
|
||||
assert.strictEqual(`${mime}`, 'text/javascript;goal=module');
|
||||
assert.strictEqual(mime.essence, 'text/javascript');
|
||||
|
||||
params.set('x', '');
|
||||
assert.strictEqual(params.has('x'), true);
|
||||
assert.strictEqual(params.get('x'), '');
|
||||
assert.deepStrictEqual([...params], [['goal', 'module'], ['x', '']]);
|
||||
assert.strictEqual(
|
||||
JSON.stringify(mime),
|
||||
JSON.stringify('text/javascript;goal=module;x=""'));
|
||||
assert.strictEqual(`${mime}`, 'text/javascript;goal=module;x=""');
|
||||
assert.strictEqual(mime.essence, 'text/javascript');
|
||||
|
||||
assert.throws(() => params.set('', 'x'), /parameter name/i);
|
||||
assert.throws(() => params.set('=', 'x'), /parameter name/i);
|
||||
assert.throws(() => params.set('x=', 'x'), /parameter name/i);
|
||||
assert.throws(() => params.set('=x', 'x'), /parameter name/i);
|
||||
assert.throws(() => params.set(`${NOT_HTTP_TOKEN_CODE_POINT}=`, 'x'), /parameter name/i);
|
||||
assert.throws(() => params.set(`${NOT_HTTP_TOKEN_CODE_POINT}x`, 'x'), /parameter name/i);
|
||||
assert.throws(() => params.set(`x${NOT_HTTP_TOKEN_CODE_POINT}`, 'x'), /parameter name/i);
|
||||
|
||||
assert.throws(() => params.set('x', `${NOT_HTTP_QUOTED_STRING_CODE_POINT};`), /parameter value/i);
|
||||
assert.throws(() => params.set('x', `${NOT_HTTP_QUOTED_STRING_CODE_POINT}x`), /parameter value/i);
|
||||
assert.throws(() => params.set('x', `x${NOT_HTTP_QUOTED_STRING_CODE_POINT}`), /parameter value/i);
|
23
test/parallel/test-mime-whatwg.js
Normal file
23
test/parallel/test-mime-whatwg.js
Normal file
@ -0,0 +1,23 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const { MIMEType } = require('util');
|
||||
const fixtures = require('../common/fixtures');
|
||||
|
||||
function test(mimes) {
|
||||
for (const entry of mimes) {
|
||||
if (typeof entry === 'string') continue;
|
||||
const { input, output } = entry;
|
||||
if (output === null) {
|
||||
assert.throws(() => new MIMEType(input), /ERR_INVALID_MIME_SYNTAX/i);
|
||||
} else {
|
||||
const str = `${new MIMEType(input)}`;
|
||||
assert.strictEqual(str, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// These come from https://github.com/web-platform-tests/wpt/tree/master/mimesniff/mime-types/resources
|
||||
test(require(fixtures.path('./mime-whatwg.js')));
|
||||
test(require(fixtures.path('./mime-whatwg-generated.js')));
|
119
test/parallel/test-primordials-regexp.js
Normal file
119
test/parallel/test-primordials-regexp.js
Normal file
@ -0,0 +1,119 @@
|
||||
// Flags: --expose-internals
|
||||
'use strict';
|
||||
|
||||
const { mustNotCall } = require('../common');
|
||||
const assert = require('assert');
|
||||
|
||||
const {
|
||||
RegExpPrototypeSymbolReplace,
|
||||
RegExpPrototypeSymbolSearch,
|
||||
RegExpPrototypeSymbolSplit,
|
||||
SafeStringPrototypeSearch,
|
||||
hardenRegExp,
|
||||
} = require('internal/test/binding').primordials;
|
||||
|
||||
|
||||
Object.defineProperties(RegExp.prototype, {
|
||||
[Symbol.match]: {
|
||||
get: mustNotCall('get %RegExp.prototype%[@@match]'),
|
||||
set: mustNotCall('set %RegExp.prototype%[@@match]'),
|
||||
},
|
||||
[Symbol.matchAll]: {
|
||||
get: mustNotCall('get %RegExp.prototype%[@@matchAll]'),
|
||||
set: mustNotCall('set %RegExp.prototype%[@@matchAll]'),
|
||||
},
|
||||
[Symbol.replace]: {
|
||||
get: mustNotCall('get %RegExp.prototype%[@@replace]'),
|
||||
set: mustNotCall('set %RegExp.prototype%[@@replace]'),
|
||||
},
|
||||
[Symbol.search]: {
|
||||
get: mustNotCall('get %RegExp.prototype%[@@search]'),
|
||||
set: mustNotCall('set %RegExp.prototype%[@@search]'),
|
||||
},
|
||||
[Symbol.split]: {
|
||||
get: mustNotCall('get %RegExp.prototype%[@@split]'),
|
||||
set: mustNotCall('set %RegExp.prototype%[@@split]'),
|
||||
},
|
||||
dotAll: {
|
||||
get: mustNotCall('get %RegExp.prototype%.dotAll'),
|
||||
set: mustNotCall('set %RegExp.prototype%.dotAll'),
|
||||
},
|
||||
exec: {
|
||||
get: mustNotCall('get %RegExp.prototype%.exec'),
|
||||
set: mustNotCall('set %RegExp.prototype%.exec'),
|
||||
},
|
||||
flags: {
|
||||
get: mustNotCall('get %RegExp.prototype%.flags'),
|
||||
set: mustNotCall('set %RegExp.prototype%.flags'),
|
||||
},
|
||||
global: {
|
||||
get: mustNotCall('get %RegExp.prototype%.global'),
|
||||
set: mustNotCall('set %RegExp.prototype%.global'),
|
||||
},
|
||||
hasIndices: {
|
||||
get: mustNotCall('get %RegExp.prototype%.hasIndices'),
|
||||
set: mustNotCall('set %RegExp.prototype%.hasIndices'),
|
||||
},
|
||||
ignoreCase: {
|
||||
get: mustNotCall('get %RegExp.prototype%.ignoreCase'),
|
||||
set: mustNotCall('set %RegExp.prototype%.ignoreCase'),
|
||||
},
|
||||
multiline: {
|
||||
get: mustNotCall('get %RegExp.prototype%.multiline'),
|
||||
set: mustNotCall('set %RegExp.prototype%.multiline'),
|
||||
},
|
||||
source: {
|
||||
get: mustNotCall('get %RegExp.prototype%.source'),
|
||||
set: mustNotCall('set %RegExp.prototype%.source'),
|
||||
},
|
||||
sticky: {
|
||||
get: mustNotCall('get %RegExp.prototype%.sticky'),
|
||||
set: mustNotCall('set %RegExp.prototype%.sticky'),
|
||||
},
|
||||
test: {
|
||||
get: mustNotCall('get %RegExp.prototype%.test'),
|
||||
set: mustNotCall('set %RegExp.prototype%.test'),
|
||||
},
|
||||
toString: {
|
||||
get: mustNotCall('get %RegExp.prototype%.toString'),
|
||||
set: mustNotCall('set %RegExp.prototype%.toString'),
|
||||
},
|
||||
unicode: {
|
||||
get: mustNotCall('get %RegExp.prototype%.unicode'),
|
||||
set: mustNotCall('set %RegExp.prototype%.unicode'),
|
||||
},
|
||||
});
|
||||
|
||||
hardenRegExp(hardenRegExp(/1/));
|
||||
|
||||
// IMO there are no valid use cases in node core to use RegExpPrototypeSymbolMatch
|
||||
// or RegExpPrototypeSymbolMatchAll, they are inherently unsafe.
|
||||
|
||||
{
|
||||
const myRegex = hardenRegExp(/a/);
|
||||
assert.strictEqual(RegExpPrototypeSymbolReplace(myRegex, 'baar', 'e'), 'bear');
|
||||
}
|
||||
{
|
||||
const myRegex = hardenRegExp(/a/g);
|
||||
assert.strictEqual(RegExpPrototypeSymbolReplace(myRegex, 'baar', 'e'), 'beer');
|
||||
}
|
||||
{
|
||||
const myRegex = hardenRegExp(/a/);
|
||||
assert.strictEqual(RegExpPrototypeSymbolSearch(myRegex, 'baar'), 1);
|
||||
}
|
||||
{
|
||||
const myRegex = /a/;
|
||||
assert.strictEqual(SafeStringPrototypeSearch('baar', myRegex), 1);
|
||||
}
|
||||
{
|
||||
const myRegex = hardenRegExp(/a/);
|
||||
assert.deepStrictEqual(RegExpPrototypeSymbolSplit(myRegex, 'baar', 0), []);
|
||||
}
|
||||
{
|
||||
const myRegex = hardenRegExp(/a/);
|
||||
assert.deepStrictEqual(RegExpPrototypeSymbolSplit(myRegex, 'baar', 1), ['b']);
|
||||
}
|
||||
{
|
||||
const myRegex = hardenRegExp(/a/);
|
||||
assert.deepStrictEqual(RegExpPrototypeSymbolSplit(myRegex, 'baar'), ['b', '', 'r']);
|
||||
}
|
@ -217,6 +217,8 @@ const customTypesMap = {
|
||||
'URL': 'url.html#the-whatwg-url-api',
|
||||
'URLSearchParams': 'url.html#class-urlsearchparams',
|
||||
|
||||
'MIMEParams': 'util.html#class-utilmimeparams',
|
||||
|
||||
'vm.Module': 'vm.html#class-vmmodule',
|
||||
'vm.Script': 'vm.html#class-vmscript',
|
||||
'vm.SourceTextModule': 'vm.html#class-vmsourcetextmodule',
|
||||
|
@ -135,17 +135,22 @@ module.exports = {
|
||||
}],
|
||||
});
|
||||
},
|
||||
[CallExpression(/^RegExpPrototypeSymbol(Match|MatchAll|Search)$/)](node) {
|
||||
[CallExpression(/^RegExpPrototypeSymbol(Match|MatchAll)$/)](node) {
|
||||
context.report({
|
||||
node,
|
||||
message: node.callee.name + ' looks up the "exec" property of `this` value',
|
||||
});
|
||||
},
|
||||
[CallExpression(/^(RegExpPrototypeSymbol|StringPrototype)Search$/)](node) {
|
||||
context.report({
|
||||
node,
|
||||
message: node.callee.name + ' is unsafe, use SafeStringPrototypeSearch instead',
|
||||
});
|
||||
},
|
||||
...createUnsafeStringMethodReport(context, '%String.prototype.match%', 'Symbol.match'),
|
||||
...createUnsafeStringMethodReport(context, '%String.prototype.matchAll%', 'Symbol.matchAll'),
|
||||
...createUnsafeStringMethodOnRegexReport(context, '%String.prototype.replace%', 'Symbol.replace'),
|
||||
...createUnsafeStringMethodOnRegexReport(context, '%String.prototype.replaceAll%', 'Symbol.replace'),
|
||||
...createUnsafeStringMethodReport(context, '%String.prototype.search%', 'Symbol.search'),
|
||||
...createUnsafeStringMethodOnRegexReport(context, '%String.prototype.split%', 'Symbol.split'),
|
||||
|
||||
'NewExpression[callee.name="Proxy"][arguments.1.type="ObjectExpression"]'(node) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user