This commit is contained in:
Benjamin Altpeter 2022-12-23 21:16:47 +00:00 committed by GitHub
commit f91afd366d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 314 additions and 8 deletions

60
package-lock.json generated
View file

@ -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",

View file

@ -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",

View file

@ -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",

View 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>{&quot;a&quot;: &quot;b&quot;, &quot;c&quot;: &quot;1&quot;}</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;

View 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>{&quot;a&quot;: &quot;b&quot;, &quot;c&quot;: 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;

View file

@ -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";

View 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] },
],
},
]);