mirror of
https://github.com/gchq/CyberChef.git
synced 2025-05-08 07:21:02 -04:00
Add decode and encode operations for URL query strings
These allow converting between URL query strings and JSON. They are based on the qs NPM package.
This commit is contained in:
parent
2efd075803
commit
cd8b2ee53a
7 changed files with 314 additions and 8 deletions
60
package-lock.json
generated
60
package-lock.json
generated
|
@ -76,6 +76,7 @@
|
|||
"process": "^0.11.10",
|
||||
"protobufjs": "^6.11.3",
|
||||
"qr-image": "^3.2.0",
|
||||
"qs": "^6.10.3",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"scryptsy": "^2.1.0",
|
||||
"snackbarjs": "^1.1.0",
|
||||
|
@ -10587,6 +10588,14 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
|
||||
"integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/object-is": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
|
||||
|
@ -11794,12 +11803,17 @@
|
|||
"integrity": "sha1-n6gpW+rlDEoUnPn5CaHbRkqGcug="
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.5.3",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
|
||||
"integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
|
||||
"dev": true,
|
||||
"version": "6.11.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
||||
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/querystring": {
|
||||
|
@ -12540,6 +12554,19 @@
|
|||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
|
||||
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.0",
|
||||
"get-intrinsic": "^1.0.2",
|
||||
"object-inspect": "^1.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/signal-exit": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||
|
@ -22380,6 +22407,11 @@
|
|||
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
|
||||
"dev": true
|
||||
},
|
||||
"object-inspect": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
|
||||
"integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ=="
|
||||
},
|
||||
"object-is": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
|
||||
|
@ -23295,10 +23327,12 @@
|
|||
"integrity": "sha1-n6gpW+rlDEoUnPn5CaHbRkqGcug="
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.5.3",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
|
||||
"integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
|
||||
"dev": true
|
||||
"version": "6.11.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
||||
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
|
||||
"requires": {
|
||||
"side-channel": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"querystring": {
|
||||
"version": "0.2.0",
|
||||
|
@ -23889,6 +23923,16 @@
|
|||
"integrity": "sha512-C2FisSSW8S6TIYHHiMHN0NqzdjWfTekdMpA2FJTbRWnQMLO1RRIXEB9eVZYOlofYmjZA7fY3ChoFu09MeI3wlQ==",
|
||||
"dev": true
|
||||
},
|
||||
"side-channel": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
|
||||
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
|
||||
"requires": {
|
||||
"call-bind": "^1.0.0",
|
||||
"get-intrinsic": "^1.0.2",
|
||||
"object-inspect": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"signal-exit": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||
|
|
|
@ -152,6 +152,7 @@
|
|||
"process": "^0.11.10",
|
||||
"protobufjs": "^6.11.3",
|
||||
"qr-image": "^3.2.0",
|
||||
"qs": "^6.10.3",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"scryptsy": "^2.1.0",
|
||||
"snackbarjs": "^1.1.0",
|
||||
|
|
|
@ -39,6 +39,8 @@
|
|||
"From HTML Entity",
|
||||
"URL Encode",
|
||||
"URL Decode",
|
||||
"Query String Encode",
|
||||
"Query String Decode",
|
||||
"Escape Unicode Characters",
|
||||
"Unescape Unicode Characters",
|
||||
"Normalise Unicode",
|
||||
|
|
74
src/core/operations/QueryStringDecode.mjs
Normal file
74
src/core/operations/QueryStringDecode.mjs
Normal file
|
@ -0,0 +1,74 @@
|
|||
/**
|
||||
* @author Benjamin Altpeter [hi@bn.al]
|
||||
* @copyright Crown Copyright 2022
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import qs from "qs";
|
||||
import Operation from "../Operation.mjs";
|
||||
|
||||
/**
|
||||
* Query String Decode operation
|
||||
*/
|
||||
class QueryStringDecode extends Operation {
|
||||
/**
|
||||
* QueryStringDecode constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Query String Decode";
|
||||
this.module = "URL";
|
||||
this.description =
|
||||
"Converts URL query strings into a JSON representation.<br><br>e.g. <code>a=b&c=1</code> becomes <code>{"a": "b", "c": "1"}</code>";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Query_string";
|
||||
this.inputType = "string";
|
||||
this.outputType = "JSON";
|
||||
this.args = [
|
||||
{
|
||||
name: "Depth",
|
||||
type: "number",
|
||||
value: 5,
|
||||
},
|
||||
{
|
||||
name: "Parameter limit",
|
||||
type: "number",
|
||||
value: 1000,
|
||||
},
|
||||
{
|
||||
name: "Delimiter",
|
||||
type: "string",
|
||||
value: "&",
|
||||
},
|
||||
{
|
||||
name: "Allow dot notation (<code>a.b=c</code>)?",
|
||||
type: "boolean",
|
||||
value: false,
|
||||
},
|
||||
{
|
||||
name: "Allow comma arrays (<code>a=b,c</code>)?",
|
||||
type: "boolean",
|
||||
value: false,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {JSON}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [depth, parameterLimit, delimiter, allowDots, comma] = args;
|
||||
return qs.parse(input, {
|
||||
depth,
|
||||
delimiter,
|
||||
parameterLimit,
|
||||
allowDots,
|
||||
comma,
|
||||
ignoreQueryPrefix: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default QueryStringDecode;
|
64
src/core/operations/QueryStringEncode.mjs
Normal file
64
src/core/operations/QueryStringEncode.mjs
Normal file
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
* @author Benjamin Altpeter [hi@bn.al]
|
||||
* @copyright Crown Copyright 2022
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import qs from "qs";
|
||||
import Operation from "../Operation.mjs";
|
||||
|
||||
/**
|
||||
* Query String Encode operation
|
||||
*/
|
||||
class QueryStringEncode extends Operation {
|
||||
/**
|
||||
* QueryStringEncode constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Query String Encode";
|
||||
this.module = "URL";
|
||||
this.description =
|
||||
"Converts JSON objects into a URL query string representation.<br><br>e.g. <code>{"a": "b", "c": 1}</code> becomes <code>a=b&c=1</code>";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Query_string";
|
||||
this.inputType = "JSON";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
name: "Array format",
|
||||
type: "option",
|
||||
value: ["brackets", "indices", "repeat", "comma"],
|
||||
defaultIndex: 0,
|
||||
},
|
||||
{
|
||||
name: "Object format",
|
||||
type: "option",
|
||||
value: ["brackets", "dots"],
|
||||
defaultIndex: 0,
|
||||
},
|
||||
{
|
||||
name: "Delimiter",
|
||||
type: "string",
|
||||
value: "&",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {JSON} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [arrayFormat, objectFormat, delimiter] = args;
|
||||
return qs.stringify(input, {
|
||||
arrayFormat,
|
||||
delimiter,
|
||||
allowDots: objectFormat === "dots",
|
||||
encode: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default QueryStringEncode;
|
|
@ -130,6 +130,7 @@ import "./tests/FletcherChecksum.mjs";
|
|||
import "./tests/CMAC.mjs";
|
||||
import "./tests/AESKeyWrap.mjs";
|
||||
import "./tests/Rabbit.mjs";
|
||||
import "./tests/QueryString.mjs";
|
||||
|
||||
// Cannot test operations that use the File type yet
|
||||
// import "./tests/SplitColourChannels.mjs";
|
||||
|
|
120
tests/operations/tests/QueryString.mjs
Normal file
120
tests/operations/tests/QueryString.mjs
Normal file
|
@ -0,0 +1,120 @@
|
|||
/**
|
||||
* Query String tests.
|
||||
*
|
||||
* @author Benjamin Altpeter [hi@bn.al]
|
||||
* @copyright Crown Copyright 2022
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import TestRegister from "../../lib/TestRegister.mjs";
|
||||
|
||||
/**
|
||||
* Small helper for JSON.stringify() with the correct settings.
|
||||
* @param {any} obj An object to stringify
|
||||
* @returns A stringified version of the object, indented by four spaces.
|
||||
*/
|
||||
const json = (obj) => JSON.stringify(obj, null, 4);
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "Query String Decode simple example (defaults)",
|
||||
input: "?a=b&c=1&d=e;f=g",
|
||||
expectedOutput: json({
|
||||
a: "b",
|
||||
c: "1",
|
||||
d: "e;f=g",
|
||||
}),
|
||||
recipeConfig: [
|
||||
{ op: "Query String Decode", args: [5, 1000, "&", false, false] },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Query String Decode arrays and objects (defaults)",
|
||||
input: "a[]=b&a[2]=b&c[d]=e&f=g,h&i.j=k",
|
||||
expectedOutput: json({
|
||||
a: ["b", "b"],
|
||||
c: {
|
||||
d: "e",
|
||||
},
|
||||
f: "g,h",
|
||||
"i.j": "k",
|
||||
}),
|
||||
recipeConfig: [
|
||||
{ op: "Query String Decode", args: [5, 1000, "&", false, false] },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Query String Decode arrays and objects (extended)",
|
||||
input: "a[]=b&a[2]=b&c[d]=e&f=g,h&i.j=k",
|
||||
expectedOutput: json({
|
||||
a: ["b", "b"],
|
||||
c: {
|
||||
d: "e",
|
||||
},
|
||||
f: ["g", "h"],
|
||||
i: {
|
||||
j: "k",
|
||||
},
|
||||
}),
|
||||
recipeConfig: [
|
||||
{ op: "Query String Decode", args: [5, 1000, "&", true, true] },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Query String Decode delimiter",
|
||||
input: "a=b;c=d",
|
||||
expectedOutput: json({
|
||||
a: "b",
|
||||
c: "d",
|
||||
}),
|
||||
recipeConfig: [
|
||||
{ op: "Query String Decode", args: [5, 1000, ";", false, false] },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Query String Decode depth (default)",
|
||||
input: "a[b][c][d][e][f][g][h]=5",
|
||||
expectedOutput: json({
|
||||
a: {
|
||||
b: {
|
||||
c: {
|
||||
d: {
|
||||
e: {
|
||||
f: {
|
||||
"[g][h]": "5",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
recipeConfig: [
|
||||
{ op: "Query String Decode", args: [5, 1000, "&", false, false] },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Query String Decode depth (higher)",
|
||||
input: "a[b][c][d][e][f][g][h]=5",
|
||||
expectedOutput: json({
|
||||
a: {
|
||||
b: {
|
||||
c: {
|
||||
d: {
|
||||
e: {
|
||||
f: {
|
||||
g: {
|
||||
h: "5",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
recipeConfig: [
|
||||
{ op: "Query String Decode", args: [7, 1000, "&", false, false] },
|
||||
],
|
||||
},
|
||||
]);
|
Loading…
Add table
Add a link
Reference in a new issue