fatsify核心功能示例测试!!!
This commit is contained in:
7
node_modules/secure-json-parse/.airtap.yml
generated
vendored
Normal file
7
node_modules/secure-json-parse/.airtap.yml
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
providers:
|
||||
- airtap-playwright
|
||||
|
||||
browsers:
|
||||
- name: chromium
|
||||
supports:
|
||||
headless: true
|
||||
2
node_modules/secure-json-parse/.gitattributes
generated
vendored
Normal file
2
node_modules/secure-json-parse/.gitattributes
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Set default behavior to automatically convert line endings
|
||||
* text=auto eol=lf
|
||||
13
node_modules/secure-json-parse/.github/dependabot.yml
generated
vendored
Normal file
13
node_modules/secure-json-parse/.github/dependabot.yml
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
open-pull-requests-limit: 10
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
open-pull-requests-limit: 10
|
||||
21
node_modules/secure-json-parse/.github/stale.yml
generated
vendored
Normal file
21
node_modules/secure-json-parse/.github/stale.yml
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 15
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- "discussion"
|
||||
- "feature request"
|
||||
- "bug"
|
||||
- "help wanted"
|
||||
- "plugin suggestion"
|
||||
- "good first issue"
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
||||
141
node_modules/secure-json-parse/.github/workflows/ci.yml
generated
vendored
Normal file
141
node_modules/secure-json-parse/.github/workflows/ci.yml
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- next
|
||||
- 'v*'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.md'
|
||||
|
||||
jobs:
|
||||
dependency-review:
|
||||
name: Dependency Review
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Dependency review
|
||||
uses: actions/dependency-review-action@v4
|
||||
|
||||
lint:
|
||||
name: Lint Code
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm i --ignore-scripts
|
||||
|
||||
- name: Lint code
|
||||
run: npm run lint
|
||||
|
||||
browsers:
|
||||
name: Test Browsers
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm i
|
||||
|
||||
- name: Install Playwright
|
||||
run: npx playwright install
|
||||
|
||||
- name: Run tests
|
||||
run: npm run test:browser
|
||||
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [20, 22]
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm i --ignore-scripts
|
||||
|
||||
- name: Run tests
|
||||
run: npm run test:unit
|
||||
|
||||
typescript:
|
||||
name: Test TypeScript
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm i --ignore-scripts
|
||||
|
||||
- name: tsd
|
||||
run: npm run test:typescript
|
||||
|
||||
automerge:
|
||||
name: Automerge Dependabot PRs
|
||||
if: >
|
||||
github.event_name == 'pull_request' &&
|
||||
github.event.pull_request.user.login == 'dependabot[bot]'
|
||||
needs: [browsers, lint, test, typescript]
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: fastify/github-action-merge-dependabot@v3
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
target: major
|
||||
17
node_modules/secure-json-parse/LICENSE
generated
vendored
Normal file
17
node_modules/secure-json-parse/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
Copyright (c) 2019 The Fastify Team
|
||||
Copyright (c) 2019, Sideway Inc, and project contributors
|
||||
All rights reserved.
|
||||
|
||||
The complete list of contributors can be found at:
|
||||
- https://github.com/hapijs/bourne/graphs/contributors
|
||||
- https://github.com/fastify/secure-json-parse/graphs/contributors
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
126
node_modules/secure-json-parse/README.md
generated
vendored
Normal file
126
node_modules/secure-json-parse/README.md
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
# secure-json-parse
|
||||
|
||||
[](https://github.com/fastify/secure-json-parse/actions/workflows/ci.yml)
|
||||
[](https://www.npmjs.com/package/secure-json-parse)
|
||||
[](https://github.com/neostandard/neostandard)
|
||||
|
||||
`JSON.parse()` drop-in replacement with prototype poisoning protection.
|
||||
|
||||
## Introduction
|
||||
|
||||
Consider this:
|
||||
|
||||
```js
|
||||
> const a = '{"__proto__":{ "b":5}}';
|
||||
'{"__proto__":{ "b":5}}'
|
||||
|
||||
> const b = JSON.parse(a);
|
||||
{ __proto__: { b: 5 } }
|
||||
|
||||
> b.b;
|
||||
undefined
|
||||
|
||||
> const c = Object.assign({}, b);
|
||||
{}
|
||||
|
||||
> c.b
|
||||
5
|
||||
```
|
||||
|
||||
The problem is that `JSON.parse()` retains the `__proto__` property as a plain object key. By
|
||||
itself, this is not a security issue. However, as soon as that object is assigned to another or
|
||||
iterated on and values copied, the `__proto__` property leaks and becomes the object's prototype.
|
||||
|
||||
## Install
|
||||
```
|
||||
npm i secure-json-parse
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Pass the option object as a second (or third) parameter for configuring the action to take in case of a bad JSON, if nothing is configured, the default is to throw a `SyntaxError`.<br/>
|
||||
You can choose which action to perform in case `__proto__` is present, and in case `constructor.prototype` is present.
|
||||
|
||||
```js
|
||||
const sjson = require('secure-json-parse')
|
||||
|
||||
const goodJson = '{ "a": 5, "b": 6 }'
|
||||
const badJson = '{ "a": 5, "b": 6, "__proto__": { "x": 7 }, "constructor": {"prototype": {"bar": "baz"} } }'
|
||||
|
||||
console.log(JSON.parse(goodJson), sjson.parse(goodJson, undefined, { protoAction: 'remove', constructorAction: 'remove' }))
|
||||
console.log(JSON.parse(badJson), sjson.parse(badJson, undefined, { protoAction: 'remove', constructorAction: 'remove' }))
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### `sjson.parse(text, [reviver], [options])`
|
||||
|
||||
Parses a given JSON-formatted text into an object where:
|
||||
- `text` - the JSON text string.
|
||||
- `reviver` - the `JSON.parse()` optional `reviver` argument.
|
||||
- `options` - optional configuration object where:
|
||||
- `protoAction` - optional string with one of:
|
||||
- `'error'` - throw a `SyntaxError` when a `__proto__` key is found. This is the default value.
|
||||
- `'remove'` - deletes any `__proto__` keys from the result object.
|
||||
- `'ignore'` - skips all validation (same as calling `JSON.parse()` directly).
|
||||
- `constructorAction` - optional string with one of:
|
||||
- `'error'` - throw a `SyntaxError` when a `constructor.prototype` key is found. This is the default value.
|
||||
- `'remove'` - deletes any `constructor` keys from the result object.
|
||||
- `'ignore'` - skips all validation (same as calling `JSON.parse()` directly).
|
||||
|
||||
### `sjson.scan(obj, [options])`
|
||||
|
||||
Scans a given object for prototype properties where:
|
||||
- `obj` - the object being scanned.
|
||||
- `options` - optional configuration object where:
|
||||
- `protoAction` - optional string with one of:
|
||||
- `'error'` - throw a `SyntaxError` when a `__proto__` key is found. This is the default value.
|
||||
- `'remove'` - deletes any `__proto__` keys from the input `obj`.
|
||||
- `constructorAction` - optional string with one of:
|
||||
- `'error'` - throw a `SyntaxError` when a `constructor.prototype` key is found. This is the default value.
|
||||
- `'remove'` - deletes any `constructor` keys from the input `obj`.
|
||||
|
||||
## Benchmarks
|
||||
|
||||
Machine: 2,7 GHz Quad-Core Intel Core i7
|
||||
|
||||
```
|
||||
v14.8.0
|
||||
|
||||
> node ignore.js
|
||||
|
||||
JSON.parse x 679,376 ops/sec ±1.15% (84 runs sampled)
|
||||
secure-json-parse x 649,605 ops/sec ±0.58% (87 runs sampled)
|
||||
reviver x 244,414 ops/sec ±1.05% (88 runs sampled)
|
||||
Fastest is JSON.parse
|
||||
|
||||
> node no__proto__.js
|
||||
|
||||
JSON.parse x 652,190 ops/sec ±0.67% (86 runs sampled)
|
||||
secure-json-parse x 589,785 ops/sec ±1.01% (88 runs sampled)
|
||||
reviver x 218,075 ops/sec ±1.58% (87 runs sampled)
|
||||
Fastest is JSON.parse
|
||||
|
||||
> node remove.js
|
||||
|
||||
JSON.parse x 683,527 ops/sec ±0.62% (88 runs sampled)
|
||||
secure-json-parse x 316,926 ops/sec ±0.63% (87 runs sampled)
|
||||
reviver x 214,167 ops/sec ±0.63% (86 runs sampled)
|
||||
Fastest is JSON.parse
|
||||
|
||||
> node throw.js
|
||||
|
||||
JSON.parse x 682,548 ops/sec ±0.60% (88 runs sampled)
|
||||
JSON.parse error x 170,716 ops/sec ±0.93% (87 runs sampled)
|
||||
secure-json-parse x 104,483 ops/sec ±0.62% (87 runs sampled)
|
||||
reviver x 114,197 ops/sec ±0.63% (87 runs sampled)
|
||||
Fastest is JSON.parse
|
||||
```
|
||||
|
||||
## Acknowledgments
|
||||
This project has been forked from [hapijs/bourne](https://github.com/hapijs/bourne).
|
||||
All credit before commit [4690682](https://github.com/hapijs/bourne/commit/4690682c6cdaa06590da7b2485d5df91c09da889) goes to the hapijs/bourne project contributors.
|
||||
After, the project will be maintained by the Fastify team.
|
||||
|
||||
## License
|
||||
Licensed under [BSD-3-Clause](./LICENSE).
|
||||
35
node_modules/secure-json-parse/benchmarks/ignore.js
generated
vendored
Normal file
35
node_modules/secure-json-parse/benchmarks/ignore.js
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
'use strict'
|
||||
|
||||
const Benchmark = require('benchmark')
|
||||
const sjson = require('..')
|
||||
|
||||
const internals = {
|
||||
text: '{ "a": 5, "b": 6, "__proto__": { "x": 7 }, "c": { "d": 0, "e": "text", "__proto__": { "y": 8 }, "f": { "g": 2 } } }'
|
||||
}
|
||||
|
||||
const suite = new Benchmark.Suite()
|
||||
|
||||
suite
|
||||
.add('JSON.parse', () => {
|
||||
JSON.parse(internals.text)
|
||||
})
|
||||
.add('secure-json-parse parse', () => {
|
||||
sjson.parse(internals.text, { protoAction: 'ignore' })
|
||||
})
|
||||
.add('secure-json-parse safeParse', () => {
|
||||
sjson.safeParse(internals.text)
|
||||
})
|
||||
.add('reviver', () => {
|
||||
JSON.parse(internals.text, internals.reviver)
|
||||
})
|
||||
.on('cycle', (event) => {
|
||||
console.log(String(event.target))
|
||||
})
|
||||
.on('complete', function () {
|
||||
console.log('Fastest is ' + this.filter('fastest').map('name'))
|
||||
})
|
||||
.run({ async: true })
|
||||
|
||||
internals.reviver = function (_key, value) {
|
||||
return value
|
||||
}
|
||||
40
node_modules/secure-json-parse/benchmarks/no__proto__.js
generated
vendored
Normal file
40
node_modules/secure-json-parse/benchmarks/no__proto__.js
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
'use strict'
|
||||
|
||||
const Benchmark = require('benchmark')
|
||||
const sjson = require('..')
|
||||
|
||||
const internals = {
|
||||
text: '{ "a": 5, "b": 6, "proto": { "x": 7 }, "c": { "d": 0, "e": "text", "\\u005f\\u005fproto": { "y": 8 }, "f": { "g": 2 } } }',
|
||||
suspectRx: /"(?:_|\\u005f)(?:_|\\u005f)(?:p|\\u0070)(?:r|\\u0072)(?:o|\\u006f)(?:t|\\u0074)(?:o|\\u006f)(?:_|\\u005f)(?:_|\\u005f)"/
|
||||
}
|
||||
|
||||
const suite = new Benchmark.Suite()
|
||||
|
||||
suite
|
||||
.add('JSON.parse', () => {
|
||||
JSON.parse(internals.text)
|
||||
})
|
||||
.add('secure-json-parse parse', () => {
|
||||
sjson.parse(internals.text)
|
||||
})
|
||||
.add('secure-json-parse safeParse', () => {
|
||||
sjson.safeParse(internals.text)
|
||||
})
|
||||
.add('reviver', () => {
|
||||
JSON.parse(internals.text, internals.reviver)
|
||||
})
|
||||
.on('cycle', (event) => {
|
||||
console.log(String(event.target))
|
||||
})
|
||||
.on('complete', function () {
|
||||
console.log('Fastest is ' + this.filter('fastest').map('name'))
|
||||
})
|
||||
.run({ async: true })
|
||||
|
||||
internals.reviver = function (key, value) {
|
||||
if (key.match(internals.suspectRx)) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
15
node_modules/secure-json-parse/benchmarks/package.json
generated
vendored
Normal file
15
node_modules/secure-json-parse/benchmarks/package.json
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "benchmarks",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"valid": "node valid.js",
|
||||
"ignore": "node ignore.js",
|
||||
"no_proto": "node no__proto__.js",
|
||||
"remove": "node remove.js",
|
||||
"throw": "node throw.js",
|
||||
"all": "node --version && npm run valid && npm run ignore && npm run no_proto && npm run remove && npm run throw"
|
||||
},
|
||||
"dependencies": {
|
||||
"benchmark": "^2.1.4"
|
||||
}
|
||||
}
|
||||
39
node_modules/secure-json-parse/benchmarks/remove.js
generated
vendored
Normal file
39
node_modules/secure-json-parse/benchmarks/remove.js
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
'use strict'
|
||||
|
||||
const Benchmark = require('benchmark')
|
||||
const sjson = require('..')
|
||||
|
||||
const internals = {
|
||||
text: '{ "a": 5, "b": 6, "__proto__": { "x": 7 }, "c": { "d": 0, "e": "text", "__proto__": { "y": 8 }, "f": { "g": 2 } } }'
|
||||
}
|
||||
|
||||
const suite = new Benchmark.Suite()
|
||||
|
||||
suite
|
||||
.add('JSON.parse', () => {
|
||||
JSON.parse(internals.text)
|
||||
})
|
||||
.add('secure-json-parse parse', () => {
|
||||
sjson.parse(internals.text, { protoAction: 'remove' })
|
||||
})
|
||||
.add('secure-json-parse safeParse', () => {
|
||||
sjson.safeParse(internals.text)
|
||||
})
|
||||
.add('reviver', () => {
|
||||
JSON.parse(internals.text, internals.reviver)
|
||||
})
|
||||
.on('cycle', (event) => {
|
||||
console.log(String(event.target))
|
||||
})
|
||||
.on('complete', function () {
|
||||
console.log('Fastest is ' + this.filter('fastest').map('name'))
|
||||
})
|
||||
.run({ async: true })
|
||||
|
||||
internals.reviver = function (key, value) {
|
||||
if (key === '__proto__') {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
49
node_modules/secure-json-parse/benchmarks/throw.js
generated
vendored
Normal file
49
node_modules/secure-json-parse/benchmarks/throw.js
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
'use strict'
|
||||
|
||||
const Benchmark = require('benchmark')
|
||||
const sjson = require('..')
|
||||
|
||||
const internals = {
|
||||
text: '{ "a": 5, "b": 6, "__proto__": { "x": 7 }, "c": { "d": 0, "e": "text", "__proto__": { "y": 8 }, "f": { "g": 2 } } }',
|
||||
invalid: '{ "a": 5, "b": 6, "__proto__": { "x": 7 }, "c": { "d": 0, "e": "text", "__proto__": { "y": 8 }, "f": { "g": 2 } } } }'
|
||||
}
|
||||
|
||||
const suite = new Benchmark.Suite()
|
||||
|
||||
suite
|
||||
.add('JSON.parse valid', () => {
|
||||
JSON.parse(internals.text)
|
||||
})
|
||||
.add('JSON.parse error', () => {
|
||||
try {
|
||||
JSON.parse(internals.invalid)
|
||||
} catch { }
|
||||
})
|
||||
.add('secure-json-parse parse', () => {
|
||||
try {
|
||||
sjson.parse(internals.invalid)
|
||||
} catch { }
|
||||
})
|
||||
.add('secure-json-parse safeParse', () => {
|
||||
sjson.safeParse(internals.invalid)
|
||||
})
|
||||
.add('reviver', () => {
|
||||
try {
|
||||
JSON.parse(internals.invalid, internals.reviver)
|
||||
} catch { }
|
||||
})
|
||||
.on('cycle', (event) => {
|
||||
console.log(String(event.target))
|
||||
})
|
||||
.on('complete', function () {
|
||||
console.log('Fastest is ' + this.filter('fastest').map('name'))
|
||||
})
|
||||
.run({ async: true })
|
||||
|
||||
internals.reviver = function (key, value) {
|
||||
if (key === '__proto__') {
|
||||
throw new Error('kaboom')
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
49
node_modules/secure-json-parse/benchmarks/valid.js
generated
vendored
Normal file
49
node_modules/secure-json-parse/benchmarks/valid.js
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
'use strict'
|
||||
|
||||
const Benchmark = require('benchmark')
|
||||
const sjson = require('..')
|
||||
|
||||
const internals = {
|
||||
text: '{ "a": 5, "b": 6, "c": { "d": 0, "e": "text", "f": { "g": 2 } } }',
|
||||
proto: '{ "a": 5, "b": 6, "__proto__": { "x": 7 }, "c": { "d": 0, "e": "text", "__proto__": { "y": 8 }, "f": { "g": 2 } } }'
|
||||
}
|
||||
|
||||
const suite = new Benchmark.Suite()
|
||||
|
||||
suite
|
||||
.add('JSON.parse', () => {
|
||||
JSON.parse(internals.text)
|
||||
})
|
||||
.add('JSON.parse proto', () => {
|
||||
JSON.parse(internals.proto)
|
||||
})
|
||||
.add('secure-json-parse parse', () => {
|
||||
sjson.parse(internals.text)
|
||||
})
|
||||
.add('secure-json-parse parse proto', () => {
|
||||
sjson.parse(internals.text, { constructorAction: 'ignore', protoAction: 'ignore' })
|
||||
})
|
||||
.add('secure-json-parse safeParse', () => {
|
||||
sjson.safeParse(internals.text)
|
||||
})
|
||||
.add('secure-json-parse safeParse proto', () => {
|
||||
sjson.safeParse(internals.proto)
|
||||
})
|
||||
.add('JSON.parse reviver', () => {
|
||||
JSON.parse(internals.text, internals.reviver)
|
||||
})
|
||||
.on('cycle', (event) => {
|
||||
console.log(String(event.target))
|
||||
})
|
||||
.on('complete', function () {
|
||||
console.log('Fastest is ' + this.filter('fastest').map('name'))
|
||||
})
|
||||
.run({ async: true })
|
||||
|
||||
internals.reviver = function (key, value) {
|
||||
if (key === '__proto__') {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
6
node_modules/secure-json-parse/eslint.config.js
generated
vendored
Normal file
6
node_modules/secure-json-parse/eslint.config.js
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = require('neostandard')({
|
||||
ignores: require('neostandard').resolveIgnoresFromGitignore(),
|
||||
ts: true
|
||||
})
|
||||
126
node_modules/secure-json-parse/index.js
generated
vendored
Normal file
126
node_modules/secure-json-parse/index.js
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
'use strict'
|
||||
|
||||
const hasBuffer = typeof Buffer !== 'undefined'
|
||||
const suspectProtoRx = /"(?:_|\\u005[Ff])(?:_|\\u005[Ff])(?:p|\\u0070)(?:r|\\u0072)(?:o|\\u006[Ff])(?:t|\\u0074)(?:o|\\u006[Ff])(?:_|\\u005[Ff])(?:_|\\u005[Ff])"\s*:/
|
||||
const suspectConstructorRx = /"(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)"\s*:/
|
||||
|
||||
function _parse (text, reviver, options) {
|
||||
// Normalize arguments
|
||||
if (options == null) {
|
||||
if (reviver !== null && typeof reviver === 'object') {
|
||||
options = reviver
|
||||
reviver = undefined
|
||||
}
|
||||
}
|
||||
|
||||
if (hasBuffer && Buffer.isBuffer(text)) {
|
||||
text = text.toString()
|
||||
}
|
||||
|
||||
// BOM checker
|
||||
if (text && text.charCodeAt(0) === 0xFEFF) {
|
||||
text = text.slice(1)
|
||||
}
|
||||
|
||||
// Parse normally, allowing exceptions
|
||||
const obj = JSON.parse(text, reviver)
|
||||
|
||||
// Ignore null and non-objects
|
||||
if (obj === null || typeof obj !== 'object') {
|
||||
return obj
|
||||
}
|
||||
|
||||
const protoAction = (options && options.protoAction) || 'error'
|
||||
const constructorAction = (options && options.constructorAction) || 'error'
|
||||
|
||||
// options: 'error' (default) / 'remove' / 'ignore'
|
||||
if (protoAction === 'ignore' && constructorAction === 'ignore') {
|
||||
return obj
|
||||
}
|
||||
|
||||
if (protoAction !== 'ignore' && constructorAction !== 'ignore') {
|
||||
if (suspectProtoRx.test(text) === false && suspectConstructorRx.test(text) === false) {
|
||||
return obj
|
||||
}
|
||||
} else if (protoAction !== 'ignore' && constructorAction === 'ignore') {
|
||||
if (suspectProtoRx.test(text) === false) {
|
||||
return obj
|
||||
}
|
||||
} else {
|
||||
if (suspectConstructorRx.test(text) === false) {
|
||||
return obj
|
||||
}
|
||||
}
|
||||
|
||||
// Scan result for proto keys
|
||||
return filter(obj, { protoAction, constructorAction, safe: options && options.safe })
|
||||
}
|
||||
|
||||
function filter (obj, { protoAction = 'error', constructorAction = 'error', safe } = {}) {
|
||||
let next = [obj]
|
||||
|
||||
while (next.length) {
|
||||
const nodes = next
|
||||
next = []
|
||||
|
||||
for (const node of nodes) {
|
||||
if (protoAction !== 'ignore' && Object.prototype.hasOwnProperty.call(node, '__proto__')) { // Avoid calling node.hasOwnProperty directly
|
||||
if (safe === true) {
|
||||
return null
|
||||
} else if (protoAction === 'error') {
|
||||
throw new SyntaxError('Object contains forbidden prototype property')
|
||||
}
|
||||
|
||||
delete node.__proto__ // eslint-disable-line no-proto
|
||||
}
|
||||
|
||||
if (constructorAction !== 'ignore' &&
|
||||
Object.prototype.hasOwnProperty.call(node, 'constructor') &&
|
||||
Object.prototype.hasOwnProperty.call(node.constructor, 'prototype')) { // Avoid calling node.hasOwnProperty directly
|
||||
if (safe === true) {
|
||||
return null
|
||||
} else if (constructorAction === 'error') {
|
||||
throw new SyntaxError('Object contains forbidden prototype property')
|
||||
}
|
||||
|
||||
delete node.constructor
|
||||
}
|
||||
|
||||
for (const key in node) {
|
||||
const value = node[key]
|
||||
if (value && typeof value === 'object') {
|
||||
next.push(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
function parse (text, reviver, options) {
|
||||
const { stackTraceLimit } = Error
|
||||
Error.stackTraceLimit = 0
|
||||
try {
|
||||
return _parse(text, reviver, options)
|
||||
} finally {
|
||||
Error.stackTraceLimit = stackTraceLimit
|
||||
}
|
||||
}
|
||||
|
||||
function safeParse (text, reviver) {
|
||||
const { stackTraceLimit } = Error
|
||||
Error.stackTraceLimit = 0
|
||||
try {
|
||||
return _parse(text, reviver, { safe: true })
|
||||
} catch {
|
||||
return undefined
|
||||
} finally {
|
||||
Error.stackTraceLimit = stackTraceLimit
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = parse
|
||||
module.exports.default = parse
|
||||
module.exports.parse = parse
|
||||
module.exports.safeParse = safeParse
|
||||
module.exports.scan = filter
|
||||
75
node_modules/secure-json-parse/package.json
generated
vendored
Normal file
75
node_modules/secure-json-parse/package.json
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"name": "secure-json-parse",
|
||||
"version": "4.0.0",
|
||||
"description": "JSON parse with prototype poisoning protection",
|
||||
"main": "index.js",
|
||||
"type": "commonjs",
|
||||
"types": "types/index.d.ts",
|
||||
"scripts": {
|
||||
"benchmark": "cd benchmarks && npm install && npm run all",
|
||||
"lint": "eslint",
|
||||
"lint:fix": "eslint --fix",
|
||||
"test": "nyc npm run test:unit && npm run test:typescript",
|
||||
"test:unit": "tape \"test/*.test.js\"",
|
||||
"test:typescript": "tsd",
|
||||
"test:browser": "airtap test/*.test.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/fastify/secure-json-parse.git"
|
||||
},
|
||||
"author": "Eran Hammer <eran@sideway.com>",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Matteo Collina",
|
||||
"email": "hello@matteocollina.com"
|
||||
},
|
||||
{
|
||||
"name": "Tomas Della Vedova",
|
||||
"url": "http://delved.org"
|
||||
},
|
||||
{
|
||||
"name": "Aras Abbasi",
|
||||
"email": "aras.abbasi@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Frazer Smith",
|
||||
"email": "frazer.dev@icloud.com",
|
||||
"url": "https://github.com/fdawgs"
|
||||
}
|
||||
],
|
||||
"keywords": [
|
||||
"JSON",
|
||||
"parse",
|
||||
"safe",
|
||||
"security",
|
||||
"prototype",
|
||||
"pollution"
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
"bugs": {
|
||||
"url": "https://github.com/fastify/secure-json-parse/issues"
|
||||
},
|
||||
"homepage": "https://github.com/fastify/secure-json-parse#readme",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fastify"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"devDependencies": {
|
||||
"@fastify/pre-commit": "^2.1.0",
|
||||
"airtap": "^5.0.0",
|
||||
"airtap-playwright": "^1.0.1",
|
||||
"eslint": "^9.17.0",
|
||||
"neostandard": "^0.12.0",
|
||||
"nyc": "^17.0.0",
|
||||
"playwright": "^1.43.1",
|
||||
"tape": "^5.7.5",
|
||||
"tsd": "^0.31.0"
|
||||
}
|
||||
}
|
||||
505
node_modules/secure-json-parse/test/index.test.js
generated
vendored
Normal file
505
node_modules/secure-json-parse/test/index.test.js
generated
vendored
Normal file
@@ -0,0 +1,505 @@
|
||||
'use strict'
|
||||
|
||||
const { test } = require('tape')
|
||||
const j = require('..')
|
||||
|
||||
test('parse', t => {
|
||||
t.test('parses object string', t => {
|
||||
t.deepEqual(
|
||||
j.parse('{"a": 5, "b": 6}'),
|
||||
JSON.parse('{"a": 5, "b": 6}')
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('parses null string', t => {
|
||||
t.strictEqual(
|
||||
j.parse('null'),
|
||||
JSON.parse('null')
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('parses 0 string', t => {
|
||||
t.strictEqual(
|
||||
j.parse('0'),
|
||||
JSON.parse('0')
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('parses string string', t => {
|
||||
t.strictEqual(
|
||||
j.parse('"X"'),
|
||||
JSON.parse('"X"')
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('parses buffer', t => {
|
||||
t.strictEqual(
|
||||
j.parse(Buffer.from('"X"')),
|
||||
JSON.parse(Buffer.from('"X"'))
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('parses object string (reviver)', t => {
|
||||
const reviver = (_key, value) => {
|
||||
return typeof value === 'number' ? value + 1 : value
|
||||
}
|
||||
|
||||
t.deepEqual(
|
||||
j.parse('{"a": 5, "b": 6}', reviver),
|
||||
JSON.parse('{"a": 5, "b": 6}', reviver)
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('protoAction', t => {
|
||||
t.test('sanitizes object string (reviver, options)', t => {
|
||||
const reviver = (_key, value) => {
|
||||
return typeof value === 'number' ? value + 1 : value
|
||||
}
|
||||
|
||||
t.deepEqual(
|
||||
j.parse('{"a": 5, "b": 6,"__proto__": { "x": 7 }}', reviver, { protoAction: 'remove' }),
|
||||
{ a: 6, b: 7 }
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('sanitizes object string (options)', t => {
|
||||
t.deepEqual(
|
||||
j.parse('{"a": 5, "b": 6,"__proto__": { "x": 7 }}', { protoAction: 'remove' }),
|
||||
{ a: 5, b: 6 }
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('sanitizes object string (null, options)', t => {
|
||||
t.deepEqual(
|
||||
j.parse('{"a": 5, "b": 6,"__proto__": { "x": 7 }}', null, { protoAction: 'remove' }),
|
||||
{ a: 5, b: 6 }
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('sanitizes object string (null, options)', t => {
|
||||
t.deepEqual(
|
||||
j.parse('{"a": 5, "b": 6,"__proto__": { "x": 7 }}', { protoAction: 'remove' }),
|
||||
{ a: 5, b: 6 }
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('sanitizes nested object string', t => {
|
||||
t.deepEqual(
|
||||
j.parse('{ "a": 5, "b": 6, "__proto__": { "x": 7 }, "c": { "d": 0, "e": "text", "__proto__": { "y": 8 }, "f": { "g": 2 } } }', { protoAction: 'remove' }),
|
||||
{ a: 5, b: 6, c: { d: 0, e: 'text', f: { g: 2 } } }
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('ignores proto property', t => {
|
||||
t.deepEqual(
|
||||
j.parse('{ "a": 5, "b": 6, "__proto__": { "x": 7 } }', { protoAction: 'ignore' }),
|
||||
JSON.parse('{ "a": 5, "b": 6, "__proto__": { "x": 7 } }')
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('ignores proto value', t => {
|
||||
t.deepEqual(
|
||||
j.parse('{"a": 5, "b": "__proto__"}'),
|
||||
{ a: 5, b: '__proto__' }
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('errors on proto property', t => {
|
||||
t.throws(() => j.parse('{ "a": 5, "b": 6, "__proto__": { "x": 7 } }'), SyntaxError)
|
||||
t.throws(() => j.parse('{ "a": 5, "b": 6, "__proto__" : { "x": 7 } }'), SyntaxError)
|
||||
t.throws(() => j.parse('{ "a": 5, "b": 6, "__proto__" \n\r\t : { "x": 7 } }'), SyntaxError)
|
||||
t.throws(() => j.parse('{ "a": 5, "b": 6, "__proto__" \n \r \t : { "x": 7 } }'), SyntaxError)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('errors on proto property (null, null)', t => {
|
||||
t.throws(() => j.parse('{ "a": 5, "b": 6, "__proto__": { "x": 7 } }', null, null), SyntaxError)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('errors on proto property (explicit options)', t => {
|
||||
t.throws(() => j.parse('{ "a": 5, "b": 6, "__proto__": { "x": 7 } }', { protoAction: 'error' }), SyntaxError)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('errors on proto property (unicode)', t => {
|
||||
t.throws(() => j.parse('{ "a": 5, "b": 6, "\\u005f_proto__": { "x": 7 } }'), SyntaxError)
|
||||
t.throws(() => j.parse('{ "a": 5, "b": 6, "_\\u005fp\\u0072oto__": { "x": 7 } }'), SyntaxError)
|
||||
t.throws(() => j.parse('{ "a": 5, "b": 6, "\\u005f\\u005f\\u0070\\u0072\\u006f\\u0074\\u006f\\u005f\\u005f": { "x": 7 } }'), SyntaxError)
|
||||
t.throws(() => j.parse('{ "a": 5, "b": 6, "\\u005F_proto__": { "x": 7 } }'), SyntaxError)
|
||||
t.throws(() => j.parse('{ "a": 5, "b": 6, "_\\u005Fp\\u0072oto__": { "x": 7 } }'), SyntaxError)
|
||||
t.throws(() => j.parse('{ "a": 5, "b": 6, "\\u005F\\u005F\\u0070\\u0072\\u006F\\u0074\\u006F\\u005F\\u005F": { "x": 7 } }'), SyntaxError)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('should reset stackTraceLimit', t => {
|
||||
const text = '{ "a": 5, "b": 6, "__proto__": { "x": 7 }, "c": { "d": 0, "e": "text", "__proto__": { "y": 8 }, "f": { "g": 2 } } }'
|
||||
Error.stackTraceLimit = 42
|
||||
t.throws(() => j.parse(text))
|
||||
t.same(Error.stackTraceLimit, 42)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('constructorAction', t => {
|
||||
t.test('sanitizes object string (reviver, options)', t => {
|
||||
const reviver = (_key, value) => {
|
||||
return typeof value === 'number' ? value + 1 : value
|
||||
}
|
||||
|
||||
t.deepEqual(
|
||||
j.parse('{"a": 5, "b": 6, "constructor":{"prototype":{"bar":"baz"}} }', reviver, { constructorAction: 'remove' }),
|
||||
{ a: 6, b: 7 }
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('sanitizes object string (options)', t => {
|
||||
t.deepEqual(
|
||||
j.parse('{"a": 5, "b": 6, "constructor":{"prototype":{"bar":"baz"}} }', { constructorAction: 'remove' }),
|
||||
{ a: 5, b: 6 }
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('sanitizes object string (null, options)', t => {
|
||||
t.deepEqual(
|
||||
j.parse('{"a": 5, "b": 6,"constructor":{"prototype":{"bar":"baz"}} }', null, { constructorAction: 'remove' }),
|
||||
{ a: 5, b: 6 }
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('sanitizes object string (null, options)', t => {
|
||||
t.deepEqual(
|
||||
j.parse('{"a": 5, "b": 6,"constructor":{"prototype":{"bar":"baz"}} }', { constructorAction: 'remove' }),
|
||||
{ a: 5, b: 6 }
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('sanitizes object string (no prototype key)', t => {
|
||||
t.deepEqual(
|
||||
j.parse('{"a": 5, "b": 6,"constructor":{"bar":"baz"} }', { constructorAction: 'remove' }),
|
||||
{ a: 5, b: 6, constructor: { bar: 'baz' } }
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('sanitizes nested object string', t => {
|
||||
t.deepEqual(
|
||||
j.parse('{ "a": 5, "b": 6, "constructor":{"prototype":{"bar":"baz"}}, "c": { "d": 0, "e": "text", "constructor":{"prototype":{"bar":"baz"}}, "f": { "g": 2 } } }', { constructorAction: 'remove' }),
|
||||
{ a: 5, b: 6, c: { d: 0, e: 'text', f: { g: 2 } } }
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('ignores proto property', t => {
|
||||
t.deepEqual(
|
||||
j.parse('{ "a": 5, "b": 6, "constructor":{"prototype":{"bar":"baz"}} }', { constructorAction: 'ignore' }),
|
||||
JSON.parse('{ "a": 5, "b": 6, "constructor":{"prototype":{"bar":"baz"}} }')
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('ignores proto value', t => {
|
||||
t.deepEqual(
|
||||
j.parse('{"a": 5, "b": "constructor"}'),
|
||||
{ a: 5, b: 'constructor' }
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('errors on proto property', t => {
|
||||
t.throws(() => j.parse('{ "a": 5, "b": 6, "constructor": {"prototype":{"bar":"baz"}} }'), SyntaxError)
|
||||
t.throws(() => j.parse('{ "a": 5, "b": 6, "constructor" : {"prototype":{"bar":"baz"}} }'), SyntaxError)
|
||||
t.throws(() => j.parse('{ "a": 5, "b": 6, "constructor" \n\r\t : {"prototype":{"bar":"baz"}} }'), SyntaxError)
|
||||
t.throws(() => j.parse('{ "a": 5, "b": 6, "constructor" \n \r \t : {"prototype":{"bar":"baz"}} }'), SyntaxError)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('Should not throw if the constructor key hasn\'t a child named prototype', t => {
|
||||
t.doesNotThrow(() => j.parse('{ "a": 5, "b": 6, "constructor":{"bar":"baz"} }', null, null), SyntaxError)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('errors on proto property (null, null)', t => {
|
||||
t.throws(() => j.parse('{ "a": 5, "b": 6, "constructor":{"prototype":{"bar":"baz"}} }', null, null), SyntaxError)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('errors on proto property (explicit options)', t => {
|
||||
t.throws(() => j.parse('{ "a": 5, "b": 6, "constructor":{"prototype":{"bar":"baz"}} }', { constructorAction: 'error' }), SyntaxError)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('errors on proto property (unicode)', t => {
|
||||
t.throws(() => j.parse('{ "a": 5, "b": 6, "\\u0063\\u006fnstructor": {"prototype":{"bar":"baz"}} }'), SyntaxError)
|
||||
t.throws(() => j.parse('{ "a": 5, "b": 6, "\\u0063\\u006f\\u006e\\u0073\\u0074ructor": {"prototype":{"bar":"baz"}} }'), SyntaxError)
|
||||
t.throws(() => j.parse('{ "a": 5, "b": 6, "\\u0063\\u006f\\u006e\\u0073\\u0074\\u0072\\u0075\\u0063\\u0074\\u006f\\u0072": {"prototype":{"bar":"baz"}} }'), SyntaxError)
|
||||
t.throws(() => j.parse('{ "a": 5, "b": 6, "\\u0063\\u006Fnstructor": {"prototype":{"bar":"baz"}} }'), SyntaxError)
|
||||
t.throws(() => j.parse('{ "a": 5, "b": 6, "\\u0063\\u006F\\u006E\\u0073\\u0074\\u0072\\u0075\\u0063\\u0074\\u006F\\u0072": {"prototype":{"bar":"baz"}} }'), SyntaxError)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('protoAction and constructorAction', t => {
|
||||
t.test('protoAction=remove constructorAction=remove', t => {
|
||||
t.deepEqual(
|
||||
j.parse(
|
||||
'{"a": 5, "b": 6, "constructor":{"prototype":{"bar":"baz"}}, "__proto__": { "x": 7 } }',
|
||||
{ protoAction: 'remove', constructorAction: 'remove' }
|
||||
),
|
||||
{ a: 5, b: 6 }
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('protoAction=ignore constructorAction=remove', t => {
|
||||
t.deepEqual(
|
||||
j.parse(
|
||||
'{"a": 5, "b": 6, "constructor":{"prototype":{"bar":"baz"}}, "__proto__": { "x": 7 } }',
|
||||
{ protoAction: 'ignore', constructorAction: 'remove' }
|
||||
),
|
||||
JSON.parse('{ "a": 5, "b": 6, "__proto__": { "x": 7 } }')
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('protoAction=remove constructorAction=ignore', t => {
|
||||
t.deepEqual(
|
||||
j.parse(
|
||||
'{"a": 5, "b": 6, "constructor":{"prototype":{"bar":"baz"}}, "__proto__": { "x": 7 } }',
|
||||
{ protoAction: 'remove', constructorAction: 'ignore' }
|
||||
),
|
||||
JSON.parse('{ "a": 5, "b": 6, "constructor":{"prototype":{"bar":"baz"}} }')
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('protoAction=ignore constructorAction=ignore', t => {
|
||||
t.deepEqual(
|
||||
j.parse(
|
||||
'{"a": 5, "b": 6, "constructor":{"prototype":{"bar":"baz"}}, "__proto__": { "x": 7 } }',
|
||||
{ protoAction: 'ignore', constructorAction: 'ignore' }
|
||||
),
|
||||
JSON.parse('{ "a": 5, "b": 6, "constructor":{"prototype":{"bar":"baz"}}, "__proto__": { "x": 7 } }')
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('protoAction=error constructorAction=ignore', t => {
|
||||
t.throws(() => j.parse(
|
||||
'{"a": 5, "b": 6, "constructor":{"prototype":{"bar":"baz"}}, "__proto__": { "x": 7 } }',
|
||||
{ protoAction: 'error', constructorAction: 'ignore' }
|
||||
), SyntaxError)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('protoAction=ignore constructorAction=error', t => {
|
||||
t.throws(() => j.parse(
|
||||
'{"a": 5, "b": 6, "constructor":{"prototype":{"bar":"baz"}}, "__proto__": { "x": 7 } }',
|
||||
{ protoAction: 'ignore', constructorAction: 'error' }
|
||||
), SyntaxError)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('protoAction=error constructorAction=error', t => {
|
||||
t.throws(() => j.parse(
|
||||
'{"a": 5, "b": 6, "constructor":{"prototype":{"bar":"baz"}}, "__proto__": { "x": 7 } }',
|
||||
{ protoAction: 'error', constructorAction: 'error' }
|
||||
), SyntaxError)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('sanitizes nested object string', t => {
|
||||
const text = '{ "a": 5, "b": 6, "__proto__": { "x": 7 }, "c": { "d": 0, "e": "text", "__proto__": { "y": 8 }, "f": { "g": 2 } } }'
|
||||
|
||||
const obj = j.parse(text, { protoAction: 'remove' })
|
||||
t.deepEqual(obj, { a: 5, b: 6, c: { d: 0, e: 'text', f: { g: 2 } } })
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('errors on constructor property', t => {
|
||||
const text = '{ "a": 5, "b": 6, "constructor": { "x": 7 }, "c": { "d": 0, "e": "text", "__proto__": { "y": 8 }, "f": { "g": 2 } } }'
|
||||
|
||||
t.throws(() => j.parse(text), SyntaxError)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('errors on proto property', t => {
|
||||
const text = '{ "a": 5, "b": 6, "__proto__": { "x": 7 }, "c": { "d": 0, "e": "text", "__proto__": { "y": 8 }, "f": { "g": 2 } } }'
|
||||
|
||||
t.throws(() => j.parse(text), SyntaxError)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('errors on constructor property', t => {
|
||||
const text = '{ "a": 5, "b": 6, "constructor": { "x": 7 }, "c": { "d": 0, "e": "text", "__proto__": { "y": 8 }, "f": { "g": 2 } } }'
|
||||
|
||||
t.throws(() => j.parse(text), SyntaxError)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('does not break when hasOwnProperty is overwritten', t => {
|
||||
const text = '{ "a": 5, "b": 6, "hasOwnProperty": "text", "__proto__": { "x": 7 } }'
|
||||
|
||||
const obj = j.parse(text, { protoAction: 'remove' })
|
||||
t.deepEqual(obj, { a: 5, b: 6, hasOwnProperty: 'text' })
|
||||
t.end()
|
||||
})
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('safeParse', t => {
|
||||
t.test('parses buffer', t => {
|
||||
t.strictEqual(
|
||||
j.safeParse(Buffer.from('"X"')),
|
||||
JSON.parse(Buffer.from('"X"'))
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('should reset stackTraceLimit', t => {
|
||||
const text = '{ "a": 5, "b": 6, "__proto__": { "x": 7 }, "c": { "d": 0, "e": "text", "__proto__": { "y": 8 }, "f": { "g": 2 } } }'
|
||||
Error.stackTraceLimit = 42
|
||||
t.same(j.safeParse(text), null)
|
||||
t.same(Error.stackTraceLimit, 42)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('sanitizes nested object string', t => {
|
||||
const text = '{ "a": 5, "b": 6, "__proto__": { "x": 7 }, "c": { "d": 0, "e": "text", "__proto__": { "y": 8 }, "f": { "g": 2 } } }'
|
||||
|
||||
t.same(j.safeParse(text), null)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('returns null on constructor property', t => {
|
||||
const text = '{ "a": 5, "b": 6, "constructor": { "x": 7 }, "c": { "d": 0, "e": "text", "__proto__": { "y": 8 }, "f": { "g": 2 } } }'
|
||||
|
||||
t.same(j.safeParse(text), null)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('returns null on proto property', t => {
|
||||
const text = '{ "a": 5, "b": 6, "__proto__": { "x": 7 }, "c": { "d": 0, "e": "text", "__proto__": { "y": 8 }, "f": { "g": 2 } } }'
|
||||
|
||||
t.same(j.safeParse(text), null)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('returns null on constructor property', t => {
|
||||
const text = '{ "a": 5, "b": 6, "constructor": { "x": 7 }, "c": { "d": 0, "e": "text", "__proto__": { "y": 8 }, "f": { "g": 2 } } }'
|
||||
|
||||
t.same(j.safeParse(text), null)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('parses object string', t => {
|
||||
t.deepEqual(
|
||||
j.safeParse('{"a": 5, "b": 6}'),
|
||||
{ a: 5, b: 6 }
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('returns null on proto object string', t => {
|
||||
t.strictEqual(
|
||||
j.safeParse('{ "a": 5, "b": 6, "__proto__": { "x": 7 } }'),
|
||||
null
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('returns undefined on invalid object string', t => {
|
||||
t.strictEqual(
|
||||
j.safeParse('{"a": 5, "b": 6'),
|
||||
undefined
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('sanitizes object string (options)', t => {
|
||||
t.deepEqual(
|
||||
j.safeParse('{"a": 5, "b": 6, "constructor":{"prototype":{"bar":"baz"}} }'),
|
||||
null
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('sanitizes object string (no prototype key)', t => {
|
||||
t.deepEqual(
|
||||
j.safeParse('{"a": 5, "b": 6,"constructor":{"bar":"baz"} }'),
|
||||
{ a: 5, b: 6, constructor: { bar: 'baz' } }
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('parse string with BOM', t => {
|
||||
const theJson = { hello: 'world' }
|
||||
const buffer = Buffer.concat([
|
||||
Buffer.from([239, 187, 191]), // the utf8 BOM
|
||||
Buffer.from(JSON.stringify(theJson))
|
||||
])
|
||||
t.deepEqual(j.parse(buffer.toString()), theJson)
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('parse buffer with BOM', t => {
|
||||
const theJson = { hello: 'world' }
|
||||
const buffer = Buffer.concat([
|
||||
Buffer.from([239, 187, 191]), // the utf8 BOM
|
||||
Buffer.from(JSON.stringify(theJson))
|
||||
])
|
||||
t.deepEqual(j.parse(buffer), theJson)
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('safeParse string with BOM', t => {
|
||||
const theJson = { hello: 'world' }
|
||||
const buffer = Buffer.concat([
|
||||
Buffer.from([239, 187, 191]), // the utf8 BOM
|
||||
Buffer.from(JSON.stringify(theJson))
|
||||
])
|
||||
t.deepEqual(j.safeParse(buffer.toString()), theJson)
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('safeParse buffer with BOM', t => {
|
||||
const theJson = { hello: 'world' }
|
||||
const buffer = Buffer.concat([
|
||||
Buffer.from([239, 187, 191]), // the utf8 BOM
|
||||
Buffer.from(JSON.stringify(theJson))
|
||||
])
|
||||
t.deepEqual(j.safeParse(buffer), theJson)
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('scan handles optional options', t => {
|
||||
t.doesNotThrow(() => j.scan({ a: 'b' }))
|
||||
t.end()
|
||||
})
|
||||
58
node_modules/secure-json-parse/types/index.d.ts
generated
vendored
Normal file
58
node_modules/secure-json-parse/types/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
type Parse = typeof parse
|
||||
|
||||
declare namespace parse {
|
||||
export type ParseOptions = {
|
||||
/**
|
||||
* What to do when a `__proto__` key is found.
|
||||
* - `'error'` - throw a `SyntaxError` when a `__proto__` key is found. This is the default value.
|
||||
* - `'remove'` - deletes any `__proto__` keys from the result object.
|
||||
* - `'ignore'` - skips all validation (same as calling `JSON.parse()` directly).
|
||||
*/
|
||||
protoAction?: 'error' | 'remove' | 'ignore';
|
||||
/**
|
||||
* What to do when a `constructor` key is found.
|
||||
* - `'error'` - throw a `SyntaxError` when a `constructor.prototype` key is found. This is the default value.
|
||||
* - `'remove'` - deletes any `constructor` keys from the result object.
|
||||
* - `'ignore'` - skips all validation (same as calling `JSON.parse()` directly).
|
||||
*/
|
||||
constructorAction?: 'error' | 'remove' | 'ignore';
|
||||
}
|
||||
|
||||
export type ScanOptions = ParseOptions
|
||||
|
||||
export type Reviver = (this: any, key: string, value: any) => any
|
||||
|
||||
/**
|
||||
* Parses a given JSON-formatted text into an object.
|
||||
*
|
||||
* @param text The JSON text string.
|
||||
* @param reviver The `JSON.parse()` optional `reviver` argument.
|
||||
* @param options Optional configuration object.
|
||||
* @returns The parsed object.
|
||||
*/
|
||||
export const parse: Parse
|
||||
|
||||
/**
|
||||
* Parses a given JSON-formatted text into an object.
|
||||
*
|
||||
* @param text The JSON text string.
|
||||
* @param reviver The `JSON.parse()` optional `reviver` argument.
|
||||
* @returns The parsed object, or `undefined` if there was an error or if the JSON contained possibly insecure properties.
|
||||
*/
|
||||
export function safeParse (text: string | Buffer, reviver?: Reviver | null): any
|
||||
|
||||
/**
|
||||
* Scans a given object for prototype properties.
|
||||
*
|
||||
* @param obj The object being scanned.
|
||||
* @param options Optional configuration object.
|
||||
* @returns The object, or `null` if onError is set to `nullify`
|
||||
*/
|
||||
export function scan (obj: { [key: string | number]: any }, options?: ParseOptions): any
|
||||
|
||||
export { parse as default }
|
||||
}
|
||||
|
||||
declare function parse (text: string | Buffer, options?: parse.ParseOptions): any
|
||||
declare function parse (text: string | Buffer, reviver?: parse.Reviver | null, options?: parse.ParseOptions): any
|
||||
export = parse
|
||||
35
node_modules/secure-json-parse/types/index.test-d.ts
generated
vendored
Normal file
35
node_modules/secure-json-parse/types/index.test-d.ts
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
import { expectType, expectError } from 'tsd'
|
||||
import sjson from '..'
|
||||
|
||||
expectError(sjson.parse(null))
|
||||
expectType<any>(sjson.parse('{"anything":0}'))
|
||||
|
||||
sjson.parse('"test"', null, { protoAction: 'remove' })
|
||||
expectError(sjson.parse('"test"', null, { protoAction: 'incorrect' }))
|
||||
sjson.parse('"test"', null, { constructorAction: 'ignore' })
|
||||
expectError(sjson.parse('"test"', null, { constructorAction: 'incorrect' }))
|
||||
expectError(sjson.parse('"test"', { constructorAction: 'incorrect' }))
|
||||
sjson.parse('test', { constructorAction: 'remove' })
|
||||
sjson.parse('test', { protoAction: 'ignore' })
|
||||
sjson.parse('test', () => {}, { protoAction: 'ignore', constructorAction: 'remove' })
|
||||
|
||||
sjson.safeParse('"test"', null)
|
||||
sjson.safeParse('"test"')
|
||||
expectError(sjson.safeParse(null))
|
||||
|
||||
sjson.scan({}, { protoAction: 'remove' })
|
||||
sjson.scan({}, { protoAction: 'ignore' })
|
||||
sjson.scan({}, { constructorAction: 'error' })
|
||||
sjson.scan({}, { constructorAction: 'ignore' })
|
||||
sjson.scan([], {})
|
||||
|
||||
declare const input: Buffer
|
||||
sjson.parse(input)
|
||||
sjson.safeParse(input)
|
||||
|
||||
sjson.parse('{"anything":0}', (key, value) => {
|
||||
expectType<string>(key)
|
||||
})
|
||||
sjson.safeParse('{"anything":0}', (key, value) => {
|
||||
expectType<string>(key)
|
||||
})
|
||||
Reference in New Issue
Block a user