Added 'Strict mode' to 'From Base64' operation

This commit is contained in:
n1474335 2022-06-03 21:41:37 +01:00
parent f9a6402825
commit b78bb2d3d6
4 changed files with 78 additions and 68 deletions

View File

@ -82,72 +82,74 @@ export function toBase64(data, alphabet="A-Za-z0-9+/=") {
* // returns [72, 101, 108, 108, 111] * // returns [72, 101, 108, 108, 111]
* fromBase64("SGVsbG8=", null, "byteArray"); * fromBase64("SGVsbG8=", null, "byteArray");
*/ */
export function fromBase64(data, alphabet="A-Za-z0-9+/=", returnType="string", removeNonAlphChars=true) { export function fromBase64(data, alphabet="A-Za-z0-9+/=", returnType="string", removeNonAlphChars=true, strictMode=false) {
if (!data) { if (!data) {
return returnType === "string" ? "" : []; return returnType === "string" ? "" : [];
} }
alphabet = alphabet || "A-Za-z0-9+/="; alphabet = alphabet || "A-Za-z0-9+/=";
alphabet = Utils.expandAlphRange(alphabet).join(""); alphabet = Utils.expandAlphRange(alphabet).join("");
// Confirm alphabet is a valid length
if (alphabet.length !== 64 && alphabet.length !== 65) { // Allow for padding if (alphabet.length !== 64 && alphabet.length !== 65) { // Allow for padding
throw new OperationError(`Invalid Base64 alphabet length (${alphabet.length}): ${alphabet}`); throw new OperationError(`Error: Base64 alphabet should be 64 characters long, or 65 with a padding character. Found ${alphabet.length}: ${alphabet}`);
} }
const output = []; // Remove non-alphabet characters
let chr1, chr2, chr3,
encChr1, encChr2, encChr3, encChr4,
enc1, enc2, enc3, enc4,
i = 0;
if (removeNonAlphChars) { if (removeNonAlphChars) {
const re = new RegExp("[^" + alphabet.replace(/[[\]\\\-^$]/g, "\\$&") + "]", "g"); const re = new RegExp("[^" + alphabet.replace(/[[\]\\\-^$]/g, "\\$&") + "]", "g");
data = data.replace(re, ""); data = data.replace(re, "");
} }
if (data.length % 4 === 1) { if (strictMode) {
throw new OperationError(`Invalid Base64 input length (${data.length}): it won't be 4n+1`); // Check for incorrect lengths (even without padding)
} if (data.length % 4 === 1) {
throw new OperationError(`Error: Invalid Base64 input length (${data.length}). Cannot be 4n+1, even without padding chars.`);
}
if (alphabet.length === 65) { if (alphabet.length === 65) { // Padding character included
const pad = alphabet.charAt(64); const pad = alphabet.charAt(64);
const padPos = data.indexOf(pad); const padPos = data.indexOf(pad);
if (padPos >= 0) { if (padPos >= 0) {
// padding character should appear only at the end of the input // Check that the padding character is only used at the end and maximum of twice
// there should be only one or two padding character(s) if it exists if (padPos < data.length - 2 || data.charAt(data.length - 1) !== pad) {
if (padPos < data.length - 2 || data.charAt(data.length - 1) !== pad) { throw new OperationError(`Error: Base64 padding character (${pad}) not used in the correct place.`);
throw new OperationError("Invalid Base64 input: padding character misused"); }
}
if (data.length % 4 !== 0) { // Check that input is padded to the correct length
throw new OperationError("Invalid Base64 input: padded not to multiple of 4"); if (data.length % 4 !== 0) {
throw new OperationError("Error: Base64 not padded to a multiple of 4.");
}
} }
} }
} }
const output = [];
let chr1, chr2, chr3,
enc1, enc2, enc3, enc4,
i = 0;
while (i < data.length) { while (i < data.length) {
encChr1 = data.charAt(i++); enc1 = alphabet.indexOf(data.charAt(i++));
encChr2 = data.charAt(i++); enc2 = alphabet.indexOf(data.charAt(i++));
encChr3 = data.charAt(i++); enc3 = alphabet.indexOf(data.charAt(i++));
encChr4 = data.charAt(i++); enc4 = alphabet.indexOf(data.charAt(i++));
enc1 = alphabet.indexOf(encChr1); if (strictMode && (enc1 < 0 || enc2 < 0 || enc3 < 0 || enc4 < 0)) {
enc2 = alphabet.indexOf(encChr2); throw new OperationError("Error: Base64 input contains non-alphabet char(s)");
enc3 = alphabet.indexOf(encChr3);
enc4 = alphabet.indexOf(encChr4);
if (enc1 < 0 || enc2 < 0 || enc3 < 0 || enc4 < 0) {
throw new OperationError("Invalid Base64 input: contains non-alphabet char(s)");
} }
chr1 = (enc1 << 2) | (enc2 >> 4); chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4; chr3 = ((enc3 & 3) << 6) | enc4;
output.push(chr1); if (chr1 < 256) {
output.push(chr1);
if (encChr3 !== "" && enc3 !== 64) { }
if (chr2 < 256 && enc3 !== 64) {
output.push(chr2); output.push(chr2);
} }
if (encChr4 !== "" && enc4 !== 64) { if (chr3 < 256 && enc4 !== 64) {
output.push(chr3); output.push(chr3);
} }
} }

View File

@ -34,93 +34,98 @@ class FromBase64 extends Operation {
name: "Remove non-alphabet chars", name: "Remove non-alphabet chars",
type: "boolean", type: "boolean",
value: true value: true
},
{
name: "Strict mode",
type: "boolean",
value: false
} }
]; ];
this.checks = [ this.checks = [
{ {
pattern: "^\\s*(?:[A-Z\\d+/]{4})+(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$", pattern: "^\\s*(?:[A-Z\\d+/]{4})+(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$",
flags: "i", flags: "i",
args: ["A-Za-z0-9+/=", true] args: ["A-Za-z0-9+/=", true, false]
}, },
{ {
pattern: "^\\s*[A-Z\\d\\-_]{20,}\\s*$", pattern: "^\\s*[A-Z\\d\\-_]{20,}\\s*$",
flags: "i", flags: "i",
args: ["A-Za-z0-9-_", true] args: ["A-Za-z0-9-_", true, false]
}, },
{ {
pattern: "^\\s*(?:[A-Z\\d+\\-]{4}){5,}(?:[A-Z\\d+\\-]{2}==|[A-Z\\d+\\-]{3}=)?\\s*$", pattern: "^\\s*(?:[A-Z\\d+\\-]{4}){5,}(?:[A-Z\\d+\\-]{2}==|[A-Z\\d+\\-]{3}=)?\\s*$",
flags: "i", flags: "i",
args: ["A-Za-z0-9+\\-=", true] args: ["A-Za-z0-9+\\-=", true, false]
}, },
{ {
pattern: "^\\s*(?:[A-Z\\d./]{4}){5,}(?:[A-Z\\d./]{2}==|[A-Z\\d./]{3}=)?\\s*$", pattern: "^\\s*(?:[A-Z\\d./]{4}){5,}(?:[A-Z\\d./]{2}==|[A-Z\\d./]{3}=)?\\s*$",
flags: "i", flags: "i",
args: ["./0-9A-Za-z=", true] args: ["./0-9A-Za-z=", true, false]
}, },
{ {
pattern: "^\\s*[A-Z\\d_.]{20,}\\s*$", pattern: "^\\s*[A-Z\\d_.]{20,}\\s*$",
flags: "i", flags: "i",
args: ["A-Za-z0-9_.", true] args: ["A-Za-z0-9_.", true, false]
}, },
{ {
pattern: "^\\s*(?:[A-Z\\d._]{4}){5,}(?:[A-Z\\d._]{2}--|[A-Z\\d._]{3}-)?\\s*$", pattern: "^\\s*(?:[A-Z\\d._]{4}){5,}(?:[A-Z\\d._]{2}--|[A-Z\\d._]{3}-)?\\s*$",
flags: "i", flags: "i",
args: ["A-Za-z0-9._-", true] args: ["A-Za-z0-9._-", true, false]
}, },
{ {
pattern: "^\\s*(?:[A-Z\\d+/]{4}){5,}(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$", pattern: "^\\s*(?:[A-Z\\d+/]{4}){5,}(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$",
flags: "i", flags: "i",
args: ["0-9a-zA-Z+/=", true] args: ["0-9a-zA-Z+/=", true, false]
}, },
{ {
pattern: "^\\s*(?:[A-Z\\d+/]{4}){5,}(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$", pattern: "^\\s*(?:[A-Z\\d+/]{4}){5,}(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$",
flags: "i", flags: "i",
args: ["0-9A-Za-z+/=", true] args: ["0-9A-Za-z+/=", true, false]
}, },
{ {
pattern: "^[ !\"#$%&'()*+,\\-./\\d:;<=>?@A-Z[\\\\\\]^_]{20,}$", pattern: "^[ !\"#$%&'()*+,\\-./\\d:;<=>?@A-Z[\\\\\\]^_]{20,}$",
flags: "", flags: "",
args: [" -_", false] args: [" -_", false, false]
}, },
{ {
pattern: "^\\s*[A-Z\\d+\\-]{20,}\\s*$", pattern: "^\\s*[A-Z\\d+\\-]{20,}\\s*$",
flags: "i", flags: "i",
args: ["+\\-0-9A-Za-z", true] args: ["+\\-0-9A-Za-z", true, false]
}, },
{ {
pattern: "^\\s*[!\"#$%&'()*+,\\-0-689@A-NP-VX-Z[`a-fh-mp-r]{20,}\\s*$", pattern: "^\\s*[!\"#$%&'()*+,\\-0-689@A-NP-VX-Z[`a-fh-mp-r]{20,}\\s*$",
flags: "", flags: "",
args: ["!-,-0-689@A-NP-VX-Z[`a-fh-mp-r", true] args: ["!-,-0-689@A-NP-VX-Z[`a-fh-mp-r", true, false]
}, },
{ {
pattern: "^\\s*(?:[N-ZA-M\\d+/]{4}){5,}(?:[N-ZA-M\\d+/]{2}==|[N-ZA-M\\d+/]{3}=)?\\s*$", pattern: "^\\s*(?:[N-ZA-M\\d+/]{4}){5,}(?:[N-ZA-M\\d+/]{2}==|[N-ZA-M\\d+/]{3}=)?\\s*$",
flags: "i", flags: "i",
args: ["N-ZA-Mn-za-m0-9+/=", true] args: ["N-ZA-Mn-za-m0-9+/=", true, false]
}, },
{ {
pattern: "^\\s*[A-Z\\d./]{20,}\\s*$", pattern: "^\\s*[A-Z\\d./]{20,}\\s*$",
flags: "i", flags: "i",
args: ["./0-9A-Za-z", true] args: ["./0-9A-Za-z", true, false]
}, },
{ {
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}CC|[A-Z=\\d\\+/]{3}C)?\\s*$", pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}CC|[A-Z=\\d\\+/]{3}C)?\\s*$",
flags: "i", flags: "i",
args: ["/128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC", true] args: ["/128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC", true, false]
}, },
{ {
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}55|[A-Z=\\d\\+/]{3}5)?\\s*$", pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}55|[A-Z=\\d\\+/]{3}5)?\\s*$",
flags: "i", flags: "i",
args: ["3GHIJKLMNOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5", true] args: ["3GHIJKLMNOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5", true, false]
}, },
{ {
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}22|[A-Z=\\d\\+/]{3}2)?\\s*$", pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}22|[A-Z=\\d\\+/]{3}2)?\\s*$",
flags: "i", flags: "i",
args: ["ZKj9n+yf0wDVX1s/5YbdxSo=ILaUpPBCHg8uvNO4klm6iJGhQ7eFrWczAMEq3RTt2", true] args: ["ZKj9n+yf0wDVX1s/5YbdxSo=ILaUpPBCHg8uvNO4klm6iJGhQ7eFrWczAMEq3RTt2", true, false]
}, },
{ {
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}55|[A-Z=\\d\\+/]{3}5)?\\s*$", pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}55|[A-Z=\\d\\+/]{3}5)?\\s*$",
flags: "i", flags: "i",
args: ["HNO4klm6ij9n+J2hyf0gzA8uvwDEq3X1Q7ZKeFrWcVTts/MRGYbdxSo=ILaUpPBC5", true] args: ["HNO4klm6ij9n+J2hyf0gzA8uvwDEq3X1Q7ZKeFrWcVTts/MRGYbdxSo=ILaUpPBC5", true, false]
} }
]; ];
} }
@ -131,9 +136,9 @@ class FromBase64 extends Operation {
* @returns {byteArray} * @returns {byteArray}
*/ */
run(input, args) { run(input, args) {
const [alphabet, removeNonAlphChars] = args; const [alphabet, removeNonAlphChars, strictMode] = args;
return fromBase64(input, alphabet, "byteArray", removeNonAlphChars); return fromBase64(input, alphabet, "byteArray", removeNonAlphChars, strictMode);
} }
/** /**

View File

@ -5,6 +5,7 @@
*/ */
import Operation from "../Operation.mjs"; import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
/** /**
* To Upper case operation * To Upper case operation
@ -38,27 +39,29 @@ class ToUpperCase extends Operation {
*/ */
run(input, args) { run(input, args) {
if (!args || args.length === 0) { if (!args || args.length === 0) {
throw new OperationException("No capitalization scope was provided."); throw new OperationError("No capitalization scope was provided.");
} }
const scope = args[0]; const scope = args[0];
if (scope === "All") { if (scope === "All") {
return input.toUpperCase(); return input.toUpperCase();
} }
const scopeRegex = { const scopeRegex = {
"Word": /(\b\w)/gi, "Word": /(\b\w)/gi,
"Sentence": /(?:\.|^)\s*(\b\w)/gi, "Sentence": /(?:\.|^)\s*(\b\w)/gi,
"Paragraph": /(?:\n|^)\s*(\b\w)/gi "Paragraph": /(?:\n|^)\s*(\b\w)/gi
}[ scope ]; }[scope];
if (scopeRegex !== undefined) {
// Use the regexes to capitalize the input. if (scopeRegex === undefined) {
return input.replace(scopeRegex, function(m) {
return m.toUpperCase();
});
}
else {
// The selected scope was invalid.
throw new OperationError("Unrecognized capitalization scope"); throw new OperationError("Unrecognized capitalization scope");
} }
// Use the regex to capitalize the input
return input.replace(scopeRegex, function(m) {
return m.toUpperCase();
});
} }
/** /**

View File

@ -68,7 +68,7 @@ TestRegister.addTests([
{ {
name: "Magic Chain: Base64", name: "Magic Chain: Base64",
input: "WkVkV2VtUkRRbnBrU0Vwd1ltMWpQUT09", input: "WkVkV2VtUkRRbnBrU0Vwd1ltMWpQUT09",
expectedMatch: /From_Base64\('A-Za-z0-9\+\/=',true\)\nFrom_Base64\('A-Za-z0-9\+\/=',true\)\nFrom_Base64\('A-Za-z0-9\+\/=',true\)/, expectedMatch: /From_Base64\('A-Za-z0-9\+\/=',true,false\)\nFrom_Base64\('A-Za-z0-9\+\/=',true,false\)\nFrom_Base64\('A-Za-z0-9\+\/=',true,false\)/,
recipeConfig: [ recipeConfig: [
{ {
op: "Magic", op: "Magic",
@ -79,7 +79,7 @@ TestRegister.addTests([
{ {
name: "Magic Chain: Hex -> Hexdump -> Base64", name: "Magic Chain: Hex -> Hexdump -> Base64",
input: "MDAwMDAwMDAgIDM3IDM0IDIwIDM2IDM1IDIwIDM3IDMzIDIwIDM3IDM0IDIwIDMyIDMwIDIwIDM3ICB8NzQgNjUgNzMgNzQgMjAgN3wKMDAwMDAwMTAgIDMzIDIwIDM3IDM0IDIwIDM3IDMyIDIwIDM2IDM5IDIwIDM2IDY1IDIwIDM2IDM3ICB8MyA3NCA3MiA2OSA2ZSA2N3w=", input: "MDAwMDAwMDAgIDM3IDM0IDIwIDM2IDM1IDIwIDM3IDMzIDIwIDM3IDM0IDIwIDMyIDMwIDIwIDM3ICB8NzQgNjUgNzMgNzQgMjAgN3wKMDAwMDAwMTAgIDMzIDIwIDM3IDM0IDIwIDM3IDMyIDIwIDM2IDM5IDIwIDM2IDY1IDIwIDM2IDM3ICB8MyA3NCA3MiA2OSA2ZSA2N3w=",
expectedMatch: /From_Base64\('A-Za-z0-9\+\/=',true\)\nFrom_Hexdump\(\)\nFrom_Hex\('Space'\)/, expectedMatch: /From_Base64\('A-Za-z0-9\+\/=',true,false\)\nFrom_Hexdump\(\)\nFrom_Hex\('Space'\)/,
recipeConfig: [ recipeConfig: [
{ {
op: "Magic", op: "Magic",