This commit is contained in:
engin0223 2026-03-07 14:52:50 +03:00
commit 8402c1cafb
10 changed files with 588 additions and 19 deletions

12
package-lock.json generated
View File

@ -52,7 +52,7 @@
"highlight.js": "^11.11.1",
"ieee754": "^1.2.1",
"jimp": "^1.6.0",
"jq-web": "^0.6.2",
"jq-wasm": "^1.1.0-jq-1.8.1",
"jquery": "3.7.1",
"js-sha3": "^0.9.3",
"jsesc": "^3.1.0",
@ -12060,11 +12060,11 @@
"integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==",
"license": "BSD-3-Clause"
},
"node_modules/jq-web": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/jq-web/-/jq-web-0.6.2.tgz",
"integrity": "sha512-+7XvjBYwTx4vP5PYkf6Q6orubO/v+UgMU6By1GritrmShr9QpT3UKa4ANzXWQfhdqtBnQYXsm7ZNbdIHT6tYpQ==",
"license": "ISC"
"node_modules/jq-wasm": {
"version": "1.1.0-jq-1.8.1",
"resolved": "https://registry.npmjs.org/jq-wasm/-/jq-wasm-1.1.0-jq-1.8.1.tgz",
"integrity": "sha512-lWfu34lpDFIygOYcL5TzxhZIApDR9iR5XywcVoyUAZ6jlQrj8HKHOKeCcHgUm2dE9RVdbP3eqNAKGLuj+k4seQ==",
"license": "MIT"
},
"node_modules/jquery": {
"version": "3.7.1",

View File

@ -135,7 +135,7 @@
"highlight.js": "^11.11.1",
"ieee754": "^1.2.1",
"jimp": "^1.6.0",
"jq-web": "^0.6.2",
"jq-wasm": "^1.1.0-jq-1.8.1",
"jquery": "3.7.1",
"js-sha3": "^0.9.3",
"jsesc": "^3.1.0",

View File

@ -164,7 +164,10 @@
"Typex",
"Lorenz",
"Colossus",
"SIGABA"
"SIGABA",
"Flask Session Decode",
"Flask Session Sign",
"Flask Session Verify"
]
},
{

View File

@ -0,0 +1,80 @@
/**
* @author ThePlayer372-FR []
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import { fromBase64 } from "../lib/Base64.mjs";
/**
* Flask Session Decode operation
*/
class FlaskSessionDecode extends Operation {
/**
* FlaskSessionDecode constructor
*/
constructor() {
super();
this.name = "Flask Session Decode";
this.module = "Crypto";
this.description = "Decodes the payload of a Flask session cookie (itsdangerous) into JSON.";
this.inputType = "string";
this.outputType = "JSON";
this.args = [
{
name: "View TimeStamp",
type: "boolean",
value: false
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {Object[]}
*/
run(input, args) {
input = input.trim();
const parts = input.split(".");
if (parts.length !== 3) {
throw new OperationError("Invalid Flask token format. Expected payload.timestamp.signature");
}
const payloadB64 = parts[0];
const time = parts[1];
const timeB64 = time.replace(/-/g, "+").replace(/_/g, "/");
const binary = fromBase64(timeB64);
const bytes = new Uint8Array(4);
for (let i = 0; i < 4; i++) {
bytes[i] = binary.charCodeAt(i);
}
const view = new DataView(bytes.buffer);
const timestamp = view.getInt32(0, false);
const base64 = payloadB64.replace(/-/g, "+").replace(/_/g, "/");
const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, "=");
let payloadJson;
try {
payloadJson = fromBase64(padded);
} catch (e) {
throw new OperationError("Invalid Base64 payload");
}
try {
let data = JSON.parse(payloadJson);
if (args[0]) {
data = {payload: data, timestamp: timestamp};
}
return data;
} catch (e) {
throw new OperationError("Unable to decode JSON payload: " + e.message);
}
}
}
export default FlaskSessionDecode;

View File

@ -0,0 +1,89 @@
/**
* @author ThePlayer372-FR []
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import CryptoApi from "crypto-api/src/crypto-api.mjs";
import Utils from "../Utils.mjs";
import { toBase64 } from "../lib/Base64.mjs";
import OperationError from "../errors/OperationError.mjs";
/**
* Flask Session Sign operation
*/
class FlaskSessionSign extends Operation {
/**
* FlaskSessionSign constructor
*/
constructor() {
super();
this.name = "Flask Session Sign";
this.module = "Crypto";
this.description = "Signs a JSON payload to produce a Flask session cookie (itsdangerous HMAC).";
this.inputType = "JSON";
this.outputType = "string";
this.args = [
{
name: "Key",
type: "toggleString",
value: "",
toggleValues: ["Hex", "Decimal", "Binary", "Base64", "UTF8", "Latin1"]
},
{
name: "Salt",
type: "toggleString",
value: "cookie-session",
toggleValues: ["UTF8", "Hex", "Decimal", "Binary", "Base64", "Latin1"]
},
{
name: "Algorithm",
type: "option",
value: ["sha1", "sha256"],
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
if (!args[0].string) {
throw new OperationError("Secret key required");
}
const key = Utils.convertToByteString(args[0].string, args[0].option);
const salt = Utils.convertToByteString(args[1].string || "cookie-session", args[1].option);
const algorithm = args[2] || "sha1";
const payloadB64 = toBase64(Utils.strToByteArray(JSON.stringify(input)));
const payload = payloadB64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
const derivedKey = CryptoApi.getHmac(key, CryptoApi.getHasher(algorithm));
derivedKey.update(salt);
const currentTimeStamp = Math.ceil(Date.now() / 1000);
const buffer = new ArrayBuffer(4);
const view = new DataView(buffer);
view.setInt32(0, currentTimeStamp, false);
const bytes = new Uint8Array(buffer);
let binary = "";
bytes.forEach(b => binary += String.fromCharCode(b));
const timeB64 = toBase64(Utils.strToByteArray(binary));
const time = timeB64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
const data = Utils.convertToByteString(payload + "." + time, "utf8");
const sign = CryptoApi.getHmac(derivedKey.finalize(), CryptoApi.getHasher(algorithm));
sign.update(data);
const signB64 = toBase64(sign.finalize());
const sign64 = signB64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
return payload + "." + time + "." + sign64;
}
}
export default FlaskSessionSign;

View File

@ -0,0 +1,136 @@
/**
* @author ThePlayer372-FR []
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import CryptoApi from "crypto-api/src/crypto-api.mjs";
import Utils from "../Utils.mjs";
import { toBase64, fromBase64 } from "../lib/Base64.mjs";
/**
* Flask Session Verify operation
*/
class FlaskSessionVerify extends Operation {
/**
* FlaskSessionVerify constructor
*/
constructor() {
super();
this.name = "Flask Session Verify";
this.module = "Crypto";
this.description = "Verifies the HMAC signature of a Flask session cookie (itsdangerous) generated.";
this.inputType = "string";
this.outputType = "JSON";
this.args = [
{
name: "Key",
type: "toggleString",
value: "",
toggleValues: ["Hex", "Decimal", "Binary", "Base64", "UTF8", "Latin1"]
},
{
name: "Salt",
type: "toggleString",
value: "cookie-session",
toggleValues: ["UTF8", "Hex", "Decimal", "Binary", "Base64", "Latin1"]
},
{
name: "Algorithm",
type: "option",
value: ["sha1", "sha256"],
},
{
name: "View TimeStamp",
type: "boolean",
value: true
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
if (!args[0].string) {
throw new OperationError("Secret key required");
}
const key = Utils.convertToByteString(args[0].string, args[0].option);
const salt = Utils.convertToByteString(args[1].string || "cookie-session", args[1].option);
const algorithm = args[2] || "sha1";
input = input.trim();
const parts = input.split(".");
if (parts.length !== 3) {
throw new OperationError("Invalid Flask token format. Expected payload.timestamp.signature");
}
const data = Utils.convertToByteString(parts[0] + "." + parts[1], "utf8");
const derivedKey = CryptoApi.getHmac(key, CryptoApi.getHasher(algorithm));
derivedKey.update(salt);
const sign = CryptoApi.getHmac(derivedKey.finalize(), CryptoApi.getHasher(algorithm));
sign.update(data);
const payloadB64 = parts[0];
const base64 = payloadB64.replace(/-/g, "+").replace(/_/g, "/");
const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, "=");
const time = parts[1];
const timeB64 = time.replace(/-/g, "+").replace(/_/g, "/");
const binary = fromBase64(timeB64);
const bytes = new Uint8Array(4);
for (let i = 0; i < 4; i++) {
bytes[i] = binary.charCodeAt(i);
}
const view = new DataView(bytes.buffer);
const timestamp = view.getInt32(0, false);
let payloadJson;
try {
payloadJson = fromBase64(padded);
} catch (e) {
throw new OperationError("Invalid Base64 payload");
}
const signB64 = toBase64(sign.finalize());
const sign64 = signB64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
if (sign64 !== parts[2]) {
throw new OperationError("Invalid signature!");
}
try {
const decoded = JSON.parse(payloadJson);
if (!args[3]) {
return {
valid: true,
payload: decoded,
};
} else {
return {
valid: true,
payload: decoded,
timestamp: timestamp
};
}
} catch (e) {
throw new OperationError("Unable to decode JSON payload: " + e.message);
}
}
}
export default FlaskSessionVerify;

View File

@ -6,7 +6,7 @@
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import jq from "jq-web";
import * as jq from "jq-wasm";
/**
* jq operation
@ -40,16 +40,15 @@ class Jq extends Operation {
* @returns {string}
*/
run(input, args) {
const [query] = args;
let result;
try {
result = jq.json(input, query);
} catch (err) {
throw new OperationError(`Invalid jq expression: ${err.message}`);
}
return JSON.stringify(result);
return (async () => {
const [query] = args;
try {
const result = await jq.json(input, query);
return JSON.stringify(result);
} catch (err) {
throw new OperationError(`Invalid jq expression: ${err.message}`);
}
})();
}
}

View File

@ -249,6 +249,13 @@ optgroup {
}
/* Bootstrap form inside CodeMirror editor */
.cm-panel > .bmd-form-group {
padding-top: 0;
}
/* CodeMirror */
.ͼ2 .cm-specialChar,

View File

@ -50,6 +50,14 @@ module.exports = {
testOp(browser, "Analyse hash", "0123456789abcdef", /CRC-64/);
testOp(browser, "Atbash Cipher", "test input", "gvhg rmkfg");
// testOp(browser, "Avro to JSON", "test input", "test_output");
testOp(browser,
[
"From Hex", "Avro to JSON"
],
"4f626a0104166176726f2e736368656d6196017b2274797065223a227265636f7264222c226e616d65223a22736d616c6c222c226669656c6473223a5b7b226e616d65223a226e616d65222c2274797065223a22737472696e67227d5d7d146176726f2e636f646563086e756c6c004e0247632e3702e5b75cdab9a62f1541020e0c6d796e616d654e0247632e3702e5b75cdab9a62f1541",
'{"name":"myname"}\n',
[[], [false]]
);
testOp(browser, "BLAKE2b", "test input", "33ebdc8f38177f3f3f334eeb117a84e11f061bbca4db6b8923e5cec85103f59f415551a5d5a933fdb6305dc7bf84671c2540b463dbfa08ee1895cfaa5bd780b5", ["512", "Hex", { "option": "UTF8", "string": "pass" }]);
testOp(browser, "BLAKE2s", "test input", "defe73d61dfa6e5807e4f9643e159a09ccda6be3c26dcd65f8a9bb38bfc973a7", ["256", "Hex", { "option": "UTF8", "string": "pass" }]);
testOp(browser, "BSON deserialise", "\u0011\u0000\u0000\u0000\u0002a\u0000\u0005\u0000\u0000\u0000test\u0000\u0000", '{\u000A "a": "test"\u000A}');
@ -206,6 +214,7 @@ module.exports = {
testOpHtml(browser, "Index of Coincidence", "test input", "", /Index of Coincidence: 0.08333333333333333/);
testOpImage(browser, "Invert Image", "files/Hitchhikers_Guide.jpeg");
// testOp(browser, "JPath expression", "test input", "test_output");
testOp(browser, "Jq", '{"a":{"b":1}}', '{"b":1}', [".a"]);
testOpHtml(browser, "JSON Beautify", "{a:1}", ".json-dict .json-literal", "1");
// testOp(browser, "JSON Minify", "test input", "test_output");
// testOp(browser, "JSON to CSV", "test input", "test_output");

View File

@ -0,0 +1,246 @@
/**
* Flask Session tests
*
* @author ThePlayer372-FR []
*
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
const validTokenSha1 = "eyJyb2xlIjoic3VwZXJ1c2VyIiwidXNlciI6ImFkbWluIn0.aZ-KEw.E_x6bOhA4GU9t72pMinJUjN-O3I";
const validTokenSha256 = "eyJyb2xlIjoic3VwZXJ1c2VyIiwidXNlciI6ImFkbWluIn0.aab3Ew.Jsx2DOx_H9anZg0YcvhsASxQ11897EFHeQfS2oja4y8";
const validKey = "mysecretkey";
const wrongKey = "notTheKey";
const outputObject = {
user: "admin",
role: "superuser",
};
const outputVerify = {
valid: true,
payload: outputObject,
};
TestRegister.addTests([
{
name: "Flask Session: Decode",
input: validTokenSha1,
expectedOutput: outputObject,
recipeConfig: [
{
op: "Flask Session Decode",
args: [
false
],
}
]
},
{
name: "Flask Session: Verify Sha1",
input: validTokenSha1,
expectedOutput: outputVerify,
recipeConfig: [
{
op: "Flask Session Verify",
args: [
{
string: validKey,
option: "UTF8"
},
{
string: "cookie-session",
option: "UTF8"
},
"sha1",
false,
],
}
]
},
{
name: "Flask Session: Verify Sha256",
input: validTokenSha256,
expectedOutput: outputVerify,
recipeConfig: [
{
op: "Flask Session Verify",
args: [
{
string: validKey,
option: "UTF8"
},
{
string: "cookie-session",
option: "UTF8"
},
"sha256",
false,
],
}
]
},
{
name: "Flask Session: Sign Sha1",
input: outputObject,
expectedOutput: outputVerify,
recipeConfig: [
{
op: "Flask Session Sign",
args: [
{
string: validKey,
option: "UTF8"
},
{
string: "cookie-session",
option: "UTF8"
},
"sha1"
]
},
{
op: "Flask Session Verify",
args: [
{
string: validKey,
option: "UTF8"
},
{
string: "cookie-session",
option: "UTF8"
},
"sha1",
false,
],
}
]
},
{
name: "Flask Session: Sign Sha256",
input: outputObject,
expectedOutput: outputVerify,
recipeConfig: [
{
op: "Flask Session Sign",
args: [
{
string: validKey,
option: "UTF8"
},
{
string: "cookie-session",
option: "UTF8"
},
"sha256"
]
},
{
op: "Flask Session Verify",
args: [
{
string: validKey,
option: "UTF8"
},
{
string: "cookie-session",
option: "UTF8"
},
"sha256",
false,
],
}
]
},
{
name: "Flask Session: Verify Sha1 Wrong Key",
input: validTokenSha1,
expectedOutput: "Invalid signature!",
recipeConfig: [
{
op: "Flask Session Verify",
args: [
{
string: wrongKey,
option: "UTF8"
},
{
string: "cookie-session",
option: "UTF8"
},
"sha1",
false,
],
}
]
},
{
name: "Flask Session: Verify Sha256 Wrong Key",
input: validTokenSha256,
expectedOutput: "Invalid signature!",
recipeConfig: [
{
op: "Flask Session Verify",
args: [
{
string: wrongKey,
option: "UTF8"
},
{
string: "cookie-session",
option: "UTF8"
},
"sha256",
false,
],
}
]
},
{
name: "Flask Session: Verify Sha1 Wrong Salt",
input: validTokenSha1,
expectedOutput: "Invalid signature!",
recipeConfig: [
{
op: "Flask Session Verify",
args: [
{
string: validKey,
option: "UTF8"
},
{
string: "notTheSalt",
option: "UTF8"
},
"sha1",
false,
],
}
]
},
{
name: "Flask Session: Verify Sha256 Wrong Salt",
input: validTokenSha256,
expectedOutput: "Invalid signature!",
recipeConfig: [
{
op: "Flask Session Verify",
args: [
{
string: validKey,
option: "UTF8"
},
{
string: "notTheSalt",
option: "UTF8"
},
"sha256",
false,
],
}
]
},
]);