Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ on:
branches: [main]

jobs:
lint:
name: Lint + format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
- run: npm install --no-audit --no-fund
- run: npm run lint
- run: npm run format:check

test:
name: Node ${{ matrix.node }}
runs-on: ubuntu-latest
Expand All @@ -19,8 +31,8 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm install --legacy-peer-deps --no-audit --no-fund
- run: node ./node_modules/mocha/bin/_mocha test
- run: npm install --no-audit --no-fund
- run: npx mocha test
- run: node test/smoke-floor.js

test-qs-latest:
Expand All @@ -31,9 +43,9 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: '22'
- run: npm install --legacy-peer-deps --no-audit --no-fund
- run: npm install --no-audit --no-fund
- run: npm install --no-save qs@latest
- run: node ./node_modules/mocha/bin/_mocha test/conformance.spec.js
- run: npx mocha test/conformance.spec.js

floor:
name: Engines floor (Node ${{ matrix.node }} via Docker)
Expand All @@ -60,4 +72,4 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: '22'
- run: npm audit --omit=dev || echo "audit reports issues; review required"
- run: npm audit --omit=dev
29 changes: 0 additions & 29 deletions .jscs.json

This file was deleted.

31 changes: 0 additions & 31 deletions .jshintrc

This file was deleted.

5 changes: 5 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
coverage
dist
.nyc_output
package-lock.json
7 changes: 7 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"singleQuote": true,
"tabWidth": 2,
"printWidth": 100,
"trailingComma": "none",
"endOfLine": "lf"
}
35 changes: 0 additions & 35 deletions Gruntfile.js

This file was deleted.

38 changes: 19 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
node-qs-serialization
===================
# node-qs-serialization

Serialization and deserialization of Javascript objects for use in the querystring part of an url.
Slightly modified from jQuery's $.param function [$.param method](http://api.jquery.com/jQuery.param/) and Ben Alman's [jquery-bbq](https://github.com/cowboy/jquery-bbq/) with license info for both included.

Expand All @@ -12,32 +12,32 @@ deparam deserializes a provided querystring.
var param = require('node-qs-serialization').param;
var deparam = require('node-qs-serialization').deparam;

var paramStr = 'a[]=4&a[]=5&a[]=6&b[x][]=7&b[y]=8&b[z][]=9&b[z][]=0&b[z][]=true&b[z][]=false&b[z][]=undefined&b[z][]=&c=1';
var paramStr =
'a[]=4&a[]=5&a[]=6&b[x][]=7&b[y]=8&b[z][]=9&b[z][]=0&b[z][]=true&b[z][]=false&b[z][]=undefined&b[z][]=&c=1';
var paramsObj = {
a: [4,5,6],
b:{
x:[7],
y:8,
z:[9,0,true,false,undefined,'']
},
c:1
a: [4, 5, 6],
b: {
x: [7],
y: 8,
z: [9, 0, true, false, undefined, '']
},
c: 1
};

param(paramsObj).should.equal(paramStr);
deparam(paramStr).should.deep.equal(paramsObj);

```

Install
==============
# Install

```
npm install node-qs-serialization
```

(Or from source: `npm install github:edwardsmit/node-qs-serialization`.)

Usage
===============
# Usage

```
var param = require('node-qs-serialization').param;
var deparam = require('node-qs-serialization').deparam;
Expand All @@ -50,10 +50,10 @@ var querystring = param(paramsObj);
- `coerce` — when `true` (default), strings `"true"`, `"false"`, `"null"`, `"undefined"` and numeric values are converted to their JS equivalents.
- `maxDepth` — caps nested bracket depth (default `5`). Parameters whose key path exceeds this depth are silently dropped. Keys equal to `__proto__`, `constructor`, or `prototype` are always rejected to prevent prototype pollution.

Security
===============
# Security

See [SECURITY.md](./SECURITY.md) for disclosure policy.

License
===============
# License

MIT — see [LICENSE](./LICENSE) for full text including the attributions to jQuery and jquery-bbq from which `param` and `deparam` are adapted.
6 changes: 3 additions & 3 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

## Supported Versions

| Version | Supported |
| ------- | --------- |
| 1.x | yes |
| Version | Supported |
| ------- | --------------------------- |
| 1.x | yes |
| 0.0.x | no — legacy, please upgrade |

## Reporting a Vulnerability
Expand Down
61 changes: 61 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
'use strict';

const js = require('@eslint/js');

module.exports = [
{
ignores: ['node_modules/**', 'coverage/**', 'dist/**', '.nyc_output/**']
},
js.configs.recommended,
{
languageOptions: {
ecmaVersion: 5,
sourceType: 'script',
globals: {
require: 'readonly',
module: 'readonly',
exports: 'readonly',
process: 'readonly',
console: 'readonly',
Buffer: 'readonly',
__dirname: 'readonly',
__filename: 'readonly'
}
},
rules: {
'no-trailing-spaces': 'error',
'eol-last': 'error',
'no-unused-vars': [
'error',
{ args: 'none', caughtErrors: 'all', caughtErrorsIgnorePattern: '^_' }
]
}
},
{
files: ['lib/**/*.js'],
languageOptions: {
globals: {
unescape: 'readonly'
}
}
},
{
files: ['test/**/*.js'],
languageOptions: {
globals: {
describe: 'readonly',
it: 'readonly',
before: 'readonly',
after: 'readonly',
beforeEach: 'readonly',
afterEach: 'readonly'
}
}
},
{
files: ['eslint.config.js'],
languageOptions: {
ecmaVersion: 2022
}
}
];
37 changes: 22 additions & 15 deletions lib/deparam.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* global unescape */
'use strict';

var qs = require('qs');
Expand All @@ -8,8 +7,11 @@ var DEFAULT_MAX_DEPTH = 5;

function safeDecodeURIComponent(str) {
var withSpaces = str.replace(/\+/g, ' ');
try { return decodeURIComponent(withSpaces); }
catch (e) { return unescape(withSpaces); }
try {
return decodeURIComponent(withSpaces);
} catch (_e) {
return unescape(withSpaces);
}
}

function safeDecoder(str, defaultDecoder, charset, type) {
Expand All @@ -33,17 +35,22 @@ function keySegments(rawKey) {

function preFilter(qsString, maxDepth) {
if (!qsString) return qsString;
return qsString.split('&').filter(function(pair) {
var rawKey = pair.split('=')[0];
if (!rawKey) return false;
var segments = keySegments(rawKey);
var nonEmpty = segments.filter(function(s) { return s !== ''; });
if (nonEmpty.length > maxDepth) return false;
for (var i = 0; i < segments.length; i++) {
if (DANGEROUS_KEYS.indexOf(segments[i]) !== -1) return false;
}
return true;
}).join('&');
return qsString
.split('&')
.filter(function (pair) {
var rawKey = pair.split('=')[0];
if (!rawKey) return false;
var segments = keySegments(rawKey);
var nonEmpty = segments.filter(function (s) {
return s !== '';
});
if (nonEmpty.length > maxDepth) return false;
for (var i = 0; i < segments.length; i++) {
if (DANGEROUS_KEYS.indexOf(segments[i]) !== -1) return false;
}
return true;
})
.join('&');
}

function coerceScalar(v) {
Expand Down Expand Up @@ -71,7 +78,7 @@ function coerceWalk(o) {
return coerceScalar(o);
}

exports.deparam = function(params, coerce, maxDepth) {
exports.deparam = function (params, coerce, maxDepth) {
if (typeof params !== 'string') return {};
if (typeof coerce === 'undefined') coerce = true;
if (typeof maxDepth !== 'number' || maxDepth < 1) maxDepth = DEFAULT_MAX_DEPTH;
Expand Down
Loading
Loading