diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 70ae2d3..2026a0d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [6.x, 8.x, 10.x, 11.x, 12.x, 13.x, 14.x, 15.x] + node-version: [10.x, 11.x, 12.x, 13.x, 14.x, 15.x] steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} @@ -25,3 +25,18 @@ jobs: run: npm install --ignore-scripts - name: Test run: npm run test + test-legacy: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [6.x, 8.x] + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v2.1.5 + with: + node-version: ${{ matrix.node-version }} + - name: Install Dependencies + run: npm install --ignore-scripts + - name: Test + run: npm run test-legacy diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..0c15f9e --- /dev/null +++ b/index.d.ts @@ -0,0 +1,60 @@ +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 = { + /** + * 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 input `obj`. + */ + protoAction?: 'error' | 'remove', + /** + * 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 input `obj`. + */ + constructorAction?: 'error' | 'remove', +} + +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 function parse(text: string | Buffer, reviver?: Reviver | null, options?: ParseOptions): any + +/** + * 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 `null` 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. + */ +export function scan(obj: any, options?: ScanOptions): void diff --git a/index.test-d.ts b/index.test-d.ts new file mode 100644 index 0000000..002f2f9 --- /dev/null +++ b/index.test-d.ts @@ -0,0 +1,30 @@ +import { expectType, expectError } from 'tsd' +import sjson = require('.') + +expectError(sjson.parse(null)) +expectType(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' })) + +sjson.safeParse('"test"', null) +sjson.safeParse('"test"') +expectError(sjson.safeParse(null)) + +sjson.scan('"test"', { protoAction: 'remove' }) +expectError(sjson.scan('"test"', { protoAction: 'ignore' })) +sjson.scan('"test"', { constructorAction: 'error' }) +expectError(sjson.scan('"test"', { constructorAction: 'ignore' })) + +declare const input: Buffer +sjson.parse(input) +sjson.safeParse(input) + +sjson.parse('{"anything":0}', (key, value) => { + expectType(key) +}) +sjson.safeParse('{"anything":0}', (key, value) => { + expectType(key) +}) diff --git a/package.json b/package.json index d284b1a..4536142 100644 --- a/package.json +++ b/package.json @@ -3,9 +3,11 @@ "version": "2.3.2", "description": "JSON parse with prototype poisoning protection", "main": "index.js", + "types": "index.d.ts", "scripts": { "benchmark": "cd benchmarks && npm install && npm run all", - "test": "standard && nyc tape test.js", + "test-legacy": "nyc tape test.js", + "test": "standard && tsd && nyc tape test.js", "test-in-browsers": "airtap test.js" }, "repository": { @@ -31,6 +33,7 @@ "nyc": "^14.1.1", "playwright": "^1.7.1", "standard": "^16.0.0", - "tape": "^5.1.1" + "tape": "^5.1.1", + "tsd": "^0.14.0" } }