Skip to content

Commit b2981d3

Browse files
authored
Merge branch 'master' into feature/docker-multiplatform-build
2 parents 324560a + d3357d2 commit b2981d3

27 files changed

+2562
-182
lines changed

.cspell.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"version": "0.2",
3+
"language": "en,en-gb",
4+
"words": [],
5+
"dictionaries": [
6+
"npm",
7+
"softwareTerms",
8+
"node",
9+
"html",
10+
"css",
11+
"bash",
12+
"en-gb",
13+
"misc"
14+
],
15+
"ignorePaths": ["package.json", "package-lock.json", "node_modules"]
16+
}
17+

package-lock.json

Lines changed: 1244 additions & 108 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
"compression-webpack-plugin": "^11.1.0",
6262
"copy-webpack-plugin": "^12.0.2",
6363
"core-js": "^3.37.1",
64+
"cspell": "^8.17.3",
6465
"css-loader": "7.1.2",
6566
"eslint": "^9.4.0",
6667
"eslint-plugin-jsdoc": "^48.2.9",
@@ -193,6 +194,7 @@
193194
"testui": "npx grunt testui",
194195
"testuidev": "npx nightwatch --env=dev",
195196
"lint": "npx grunt lint",
197+
"lint:grammar": "cspell ./src",
196198
"postinstall": "npx grunt exec:fixCryptoApiImports && npx grunt exec:fixSnackbarMarkup && npx grunt exec:fixJimpModule",
197199
"newop": "node --experimental-modules --experimental-json-modules src/core/config/scripts/newOperation.mjs",
198200
"minor": "node --experimental-modules --experimental-json-modules src/core/config/scripts/newMinorVersion.mjs",

src/core/config/Categories.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,10 @@
7474
"CBOR Decode",
7575
"Caret/M-decode",
7676
"Rison Encode",
77-
"Rison Decode"
77+
"Rison Decode",
78+
"To Modhex",
79+
"From Modhex",
80+
"MIME Decoding"
7881
]
7982
},
8083
{

src/core/lib/Extract.mjs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,9 @@ export const URL_REGEX = new RegExp(protocol + hostname + "(?:" + port + ")?(?:"
6262
* Domain name regular expression
6363
*/
6464
export const DOMAIN_REGEX = /\b((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}\b/ig;
65+
66+
67+
/**
68+
* DMARC Domain name regular expression
69+
*/
70+
export const DMARC_DOMAIN_REGEX = /\b((?=[a-z0-9_-]{1,63}\.)(xn--)?[a-z0-9_]+(-[a-z0-9_]+)*\.)+[a-z]{2,63}\b/ig;

src/core/lib/Modhex.mjs

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/**
2+
* @author linuxgemini [ilteris@asenkron.com.tr]
3+
* @copyright Crown Copyright 2024
4+
* @license Apache-2.0
5+
*/
6+
7+
import Utils from "../Utils.mjs";
8+
import OperationError from "../errors/OperationError.mjs";
9+
import { fromHex, toHex } from "./Hex.mjs";
10+
11+
/**
12+
* Modhex alphabet.
13+
*/
14+
const MODHEX_ALPHABET = "cbdefghijklnrtuv";
15+
16+
17+
/**
18+
* Modhex alphabet map.
19+
*/
20+
const MODHEX_ALPHABET_MAP = MODHEX_ALPHABET.split("");
21+
22+
23+
/**
24+
* Hex alphabet to substitute Modhex.
25+
*/
26+
const HEX_ALPHABET = "0123456789abcdef";
27+
28+
29+
/**
30+
* Hex alphabet map to substitute Modhex.
31+
*/
32+
const HEX_ALPHABET_MAP = HEX_ALPHABET.split("");
33+
34+
35+
/**
36+
* Convert a byte array into a modhex string.
37+
*
38+
* @param {byteArray|Uint8Array|ArrayBuffer} data
39+
* @param {string} [delim=" "]
40+
* @param {number} [padding=2]
41+
* @returns {string}
42+
*
43+
* @example
44+
* // returns "cl bf bu"
45+
* toModhex([10,20,30]);
46+
*
47+
* // returns "cl:bf:bu"
48+
* toModhex([10,20,30], ":");
49+
*/
50+
export function toModhex(data, delim=" ", padding=2, extraDelim="", lineSize=0) {
51+
if (!data) return "";
52+
if (data instanceof ArrayBuffer) data = new Uint8Array(data);
53+
54+
const regularHexString = toHex(data, "", padding, "", 0);
55+
56+
let modhexString = "";
57+
for (const letter of regularHexString.split("")) {
58+
modhexString += MODHEX_ALPHABET_MAP[HEX_ALPHABET_MAP.indexOf(letter)];
59+
}
60+
61+
let output = "";
62+
const groupingRegexp = new RegExp(`.{1,${padding}}`, "g");
63+
const groupedModhex = modhexString.match(groupingRegexp);
64+
65+
for (let i = 0; i < groupedModhex.length; i++) {
66+
const group = groupedModhex[i];
67+
output += group + delim;
68+
69+
if (extraDelim) {
70+
output += extraDelim;
71+
}
72+
// Add LF after each lineSize amount of bytes but not at the end
73+
if ((i !== groupedModhex.length - 1) && ((i + 1) % lineSize === 0)) {
74+
output += "\n";
75+
}
76+
}
77+
78+
// Remove the extraDelim at the end (if there is one)
79+
// and remove the delim at the end, but if it's prepended there's nothing to remove
80+
const rTruncLen = extraDelim.length + delim.length;
81+
if (rTruncLen) {
82+
// If rTruncLen === 0 then output.slice(0,0) will be returned, which is nothing
83+
return output.slice(0, -rTruncLen);
84+
} else {
85+
return output;
86+
}
87+
}
88+
89+
90+
/**
91+
* Convert a byte array into a modhex string as efficiently as possible with no options.
92+
*
93+
* @param {byteArray|Uint8Array|ArrayBuffer} data
94+
* @returns {string}
95+
*
96+
* @example
97+
* // returns "clbfbu"
98+
* toModhexFast([10,20,30]);
99+
*/
100+
export function toModhexFast(data) {
101+
if (!data) return "";
102+
if (data instanceof ArrayBuffer) data = new Uint8Array(data);
103+
104+
const output = [];
105+
106+
for (let i = 0; i < data.length; i++) {
107+
output.push(MODHEX_ALPHABET_MAP[(data[i] >> 4) & 0xf]);
108+
output.push(MODHEX_ALPHABET_MAP[data[i] & 0xf]);
109+
}
110+
return output.join("");
111+
}
112+
113+
114+
/**
115+
* Convert a modhex string into a byte array.
116+
*
117+
* @param {string} data
118+
* @param {string} [delim]
119+
* @param {number} [byteLen=2]
120+
* @returns {byteArray}
121+
*
122+
* @example
123+
* // returns [10,20,30]
124+
* fromModhex("cl bf bu");
125+
*
126+
* // returns [10,20,30]
127+
* fromModhex("cl:bf:bu", "Colon");
128+
*/
129+
export function fromModhex(data, delim="Auto", byteLen=2) {
130+
if (byteLen < 1 || Math.round(byteLen) !== byteLen)
131+
throw new OperationError("Byte length must be a positive integer");
132+
133+
// The `.replace(/\s/g, "")` an interesting workaround: Hex "multiline" tests aren't actually
134+
// multiline. Tests for Modhex fixes that, thus exposing the issue.
135+
data = data.toLowerCase().replace(/\s/g, "");
136+
137+
if (delim !== "None") {
138+
const delimRegex = delim === "Auto" ? /[^cbdefghijklnrtuv]/gi : Utils.regexRep(delim);
139+
data = data.split(delimRegex);
140+
} else {
141+
data = [data];
142+
}
143+
144+
let regularHexString = "";
145+
for (let i = 0; i < data.length; i++) {
146+
for (const letter of data[i].split("")) {
147+
regularHexString += HEX_ALPHABET_MAP[MODHEX_ALPHABET_MAP.indexOf(letter)];
148+
}
149+
}
150+
151+
const output = fromHex(regularHexString, "None", byteLen);
152+
return output;
153+
}
154+
155+
156+
/**
157+
* To Modhex delimiters.
158+
*/
159+
export const TO_MODHEX_DELIM_OPTIONS = ["Space", "Percent", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF", "None"];
160+
161+
162+
/**
163+
* From Modhex delimiters.
164+
*/
165+
export const FROM_MODHEX_DELIM_OPTIONS = ["Auto"].concat(TO_MODHEX_DELIM_OPTIONS);

src/core/operations/ConvertLeetSpeak.mjs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class ConvertLeetSpeak extends Operation {
1818

1919
this.name = "Convert Leet Speak";
2020
this.module = "Default";
21-
this.description = "Converts to and from Leet Speak";
21+
this.description = "Converts to and from Leet Speak.";
2222
this.infoURL = "https://wikipedia.org/wiki/Leet";
2323
this.inputType = "string";
2424
this.outputType = "string";
@@ -39,13 +39,16 @@ class ConvertLeetSpeak extends Operation {
3939
*/
4040
run(input, args) {
4141
const direction = args[0];
42+
4243
if (direction === "To Leet Speak") {
43-
return input.replace(/[abcdefghijklmnopqrstuvwxyz]/gi, char => {
44-
return toLeetMap[char.toLowerCase()] || char;
44+
return input.replace(/[a-z]/gi, char => {
45+
const leetChar = toLeetMap[char.toLowerCase()] || char;
46+
return char === char.toUpperCase() ? leetChar.toUpperCase() : leetChar;
4547
});
4648
} else if (direction === "From Leet Speak") {
47-
return input.replace(/[48cd3f6h1jklmn0pqr57uvwxyz]/g, char => {
48-
return fromLeetMap[char] || char;
49+
return input.replace(/[48cd3f6h1jklmn0pqr57uvwxyz]/gi, char => {
50+
const normalChar = fromLeetMap[char] || char;
51+
return normalChar;
4952
});
5053
}
5154
}

src/core/operations/DESDecrypt.mjs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class DESDecrypt extends Operation {
2222

2323
this.name = "DES Decrypt";
2424
this.module = "Ciphers";
25-
this.description = "DES is a previously dominant algorithm for encryption, and was published as an official U.S. Federal Information Processing Standard (FIPS). It is now considered to be insecure due to its small key size.<br><br><b>Key:</b> DES uses a key length of 8 bytes (64 bits).<br>Triple DES uses a key length of 24 bytes (192 bits).<br><br><b>IV:</b> The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used as a default.";
25+
this.description = "DES is a previously dominant algorithm for encryption, and was published as an official U.S. Federal Information Processing Standard (FIPS). It is now considered to be insecure due to its small key size.<br><br><b>Key:</b> DES uses a key length of 8 bytes (64 bits).<br><br><b>IV:</b> The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used as a default.";
2626
this.infoURL = "https://wikipedia.org/wiki/Data_Encryption_Standard";
2727
this.inputType = "string";
2828
this.outputType = "string";
@@ -72,8 +72,7 @@ class DESDecrypt extends Operation {
7272
if (key.length !== 8) {
7373
throw new OperationError(`Invalid key length: ${key.length} bytes
7474
75-
DES uses a key length of 8 bytes (64 bits).
76-
Triple DES uses a key length of 24 bytes (192 bits).`);
75+
DES uses a key length of 8 bytes (64 bits).`);
7776
}
7877
if (iv.length !== 8 && mode !== "ECB") {
7978
throw new OperationError(`Invalid IV length: ${iv.length} bytes

src/core/operations/DESEncrypt.mjs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class DESEncrypt extends Operation {
2222

2323
this.name = "DES Encrypt";
2424
this.module = "Ciphers";
25-
this.description = "DES is a previously dominant algorithm for encryption, and was published as an official U.S. Federal Information Processing Standard (FIPS). It is now considered to be insecure due to its small key size.<br><br><b>Key:</b> DES uses a key length of 8 bytes (64 bits).<br>Triple DES uses a key length of 24 bytes (192 bits).<br><br>You can generate a password-based key using one of the KDF operations.<br><br><b>IV:</b> The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used.";
25+
this.description = "DES is a previously dominant algorithm for encryption, and was published as an official U.S. Federal Information Processing Standard (FIPS). It is now considered to be insecure due to its small key size.<br><br><b>Key:</b> DES uses a key length of 8 bytes (64 bits).<br><br>You can generate a password-based key using one of the KDF operations.<br><br><b>IV:</b> The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used.";
2626
this.infoURL = "https://wikipedia.org/wiki/Data_Encryption_Standard";
2727
this.inputType = "string";
2828
this.outputType = "string";
@@ -70,8 +70,7 @@ class DESEncrypt extends Operation {
7070
if (key.length !== 8) {
7171
throw new OperationError(`Invalid key length: ${key.length} bytes
7272
73-
DES uses a key length of 8 bytes (64 bits).
74-
Triple DES uses a key length of 24 bytes (192 bits).`);
73+
DES uses a key length of 8 bytes (64 bits).`);
7574
}
7675
if (iv.length !== 8 && mode !== "ECB") {
7776
throw new OperationError(`Invalid IV length: ${iv.length} bytes

src/core/operations/ExtractDomains.mjs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66

77
import Operation from "../Operation.mjs";
8-
import { search, DOMAIN_REGEX } from "../lib/Extract.mjs";
8+
import { search, DOMAIN_REGEX, DMARC_DOMAIN_REGEX } from "../lib/Extract.mjs";
99
import { caseInsensitiveSort } from "../lib/Sort.mjs";
1010

1111
/**
@@ -39,6 +39,11 @@ class ExtractDomains extends Operation {
3939
name: "Unique",
4040
type: "boolean",
4141
value: false
42+
},
43+
{
44+
name: "Underscore (DMARC, DKIM, etc)",
45+
type: "boolean",
46+
value: false
4247
}
4348
];
4449
}
@@ -49,11 +54,11 @@ class ExtractDomains extends Operation {
4954
* @returns {string}
5055
*/
5156
run(input, args) {
52-
const [displayTotal, sort, unique] = args;
57+
const [displayTotal, sort, unique, dmarc] = args;
5358

5459
const results = search(
5560
input,
56-
DOMAIN_REGEX,
61+
dmarc ? DMARC_DOMAIN_REGEX : DOMAIN_REGEX,
5762
null,
5863
sort ? caseInsensitiveSort : null,
5964
unique

0 commit comments

Comments
 (0)