JSON.parse() drop-in replacement with prototype poisoning protection
Installations
npm install secure-json-parse
Developer
fastify
Developer Guide
Module System
CommonJS
Min. Node Version
Typescript Support
Yes
Node Version
20.18.0
NPM Version
10.8.2
Statistics
225 Stars
172 Commits
17 Forks
13 Watching
2 Branches
32 Contributors
Updated on 18 Nov 2024
Languages
JavaScript (95.5%)
TypeScript (4.5%)
Total Downloads
Cumulative downloads
Total Downloads
485,570,654
Last day
-9.9%
1,010,263
Compared to previous day
Last week
1.4%
5,944,416
Compared to previous week
Last month
10.4%
24,266,320
Compared to previous month
Last year
55.9%
227,309,375
Compared to previous year
Daily Downloads
Weekly Downloads
Monthly Downloads
Yearly Downloads
secure-json-parse
JSON.parse()
drop-in replacement with prototype poisoning protection.
Introduction
Consider this:
1> const a = '{"__proto__":{ "b":5}}'; 2'{"__proto__":{ "b":5}}' 3 4> const b = JSON.parse(a); 5{ __proto__: { b: 5 } } 6 7> b.b; 8undefined 9 10> const c = Object.assign({}, b); 11{} 12 13> c.b 145
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
.
You can choose which action to perform in case __proto__
is present, and in case constructor.prototype
is present.
1const sjson = require('secure-json-parse') 2 3const goodJson = '{ "a": 5, "b": 6 }' 4const badJson = '{ "a": 5, "b": 6, "__proto__": { "x": 7 }, "constructor": {"prototype": {"bar": "baz"} } }' 5 6console.log(JSON.parse(goodJson), sjson.parse(goodJson, undefined, { protoAction: 'remove', constructorAction: 'remove' })) 7console.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
- theJSON.parse()
optionalreviver
argument.options
- optional configuration object where:protoAction
- optional string with one of:'error'
- throw aSyntaxError
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 callingJSON.parse()
directly).
constructorAction
- optional string with one of:'error'
- throw aSyntaxError
when aconstructor.prototype
key is found. This is the default value.'remove'
- deletes anyconstructor
keys from the result object.'ignore'
- skips all validation (same as callingJSON.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 aSyntaxError
when a__proto__
key is found. This is the default value.'remove'
- deletes any__proto__
keys from the inputobj
.
constructorAction
- optional string with one of:'error'
- throw aSyntaxError
when aconstructor.prototype
key is found. This is the default value.'remove'
- deletes anyconstructor
keys from the inputobj
.
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
Acknowledgements
This project has been forked from hapijs/bourne. All the credits before the commit 4690682 goes to the hapijs/bourne project contributors. After, the project will be maintained by the Fastify team.
License
Licensed under BSD-3-Clause.
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
no dangerous workflow patterns detected
Reason
0 existing vulnerabilities detected
Reason
security policy file detected
Details
- Info: security policy file detected: github.com/fastify/.github/SECURITY.md:1
- Info: Found linked content: github.com/fastify/.github/SECURITY.md:1
- Info: Found disclosure, vulnerability, and/or timelines in security policy: github.com/fastify/.github/SECURITY.md:1
- Info: Found text in security policy: github.com/fastify/.github/SECURITY.md:1
Reason
detected GitHub workflow tokens with excessive permissions
Details
- Info: jobLevel 'contents' permission set to 'read': .github/workflows/ci.yml:114
- Warn: jobLevel 'contents' permission set to 'write': .github/workflows/ci.yml:140
- Info: jobLevel 'contents' permission set to 'read': .github/workflows/ci.yml:24
- Info: jobLevel 'contents' permission set to 'read': .github/workflows/ci.yml:38
- Info: jobLevel 'contents' permission set to 'read': .github/workflows/ci.yml:60
- Info: jobLevel 'contents' permission set to 'read': .github/workflows/ci.yml:85
- Warn: no topLevel permission defined: .github/workflows/ci.yml:1
Reason
license file detected
Details
- Info: project has a license file: LICENSE.md:0
- Warn: project license file does not contain an FSF or OSI license.
Reason
SAST tool is not run on all commits -- score normalized to 5
Details
- Warn: 14 commits out of 26 are checked with a SAST tool
Reason
4 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 4
Reason
Found 6/18 approved changesets -- score normalized to 3
Reason
dependency not pinned by hash detected -- score normalized to 0
Details
- Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:143: update your workflow using https://app.stepsecurity.io/secureworkflow/fastify/secure-json-parse/ci.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:27: update your workflow using https://app.stepsecurity.io/secureworkflow/fastify/secure-json-parse/ci.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:32: update your workflow using https://app.stepsecurity.io/secureworkflow/fastify/secure-json-parse/ci.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:41: update your workflow using https://app.stepsecurity.io/secureworkflow/fastify/secure-json-parse/ci.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:46: update your workflow using https://app.stepsecurity.io/secureworkflow/fastify/secure-json-parse/ci.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:63: update your workflow using https://app.stepsecurity.io/secureworkflow/fastify/secure-json-parse/ci.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:68: update your workflow using https://app.stepsecurity.io/secureworkflow/fastify/secure-json-parse/ci.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:91: update your workflow using https://app.stepsecurity.io/secureworkflow/fastify/secure-json-parse/ci.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:96: update your workflow using https://app.stepsecurity.io/secureworkflow/fastify/secure-json-parse/ci.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:117: update your workflow using https://app.stepsecurity.io/secureworkflow/fastify/secure-json-parse/ci.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:122: update your workflow using https://app.stepsecurity.io/secureworkflow/fastify/secure-json-parse/ci.yml/master?enable=pin
- Warn: npmCommand not pinned by hash: .github/workflows/ci.yml:52
- Warn: npmCommand not pinned by hash: .github/workflows/ci.yml:74
- Warn: npmCommand not pinned by hash: .github/workflows/ci.yml:103
- Warn: npmCommand not pinned by hash: .github/workflows/ci.yml:106
- Warn: npmCommand not pinned by hash: .github/workflows/ci.yml:128
- Info: 0 out of 10 GitHub-owned GitHubAction dependencies pinned
- Info: 0 out of 1 third-party GitHubAction dependencies pinned
- Info: 0 out of 5 npmCommand dependencies pinned
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
project is not fuzzed
Details
- Warn: no fuzzer integrations found
Reason
branch protection not enabled on development/release branches
Details
- Warn: branch protection not enabled for branch 'master'
Score
5.8
/10
Last Scanned on 2024-11-25
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More