fatsify核心功能示例测试!!!

This commit is contained in:
2025-09-21 14:50:41 +08:00
commit 9145aea047
1958 changed files with 230098 additions and 0 deletions

7
node_modules/secure-json-parse/.airtap.yml generated vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,126 @@
# secure-json-parse
[![CI](https://github.com/fastify/secure-json-parse/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/fastify/secure-json-parse/actions/workflows/ci.yml)
[![NPM version](https://img.shields.io/npm/v/secure-json-parse.svg?style=flat)](https://www.npmjs.com/package/secure-json-parse)
[![neostandard javascript style](https://img.shields.io/badge/code_style-neostandard-brightgreen?style=flat)](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
View 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
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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)
})