From effa97cd0e5a038cbd9c0ab0d208598bde219301 Mon Sep 17 00:00:00 2001 From: Leon Zandman Date: Mon, 25 May 2026 17:55:18 +0200 Subject: [PATCH 1/4] Handle invalid coordinate input to prevent TypeError. --- src/core/operations/ShowOnMap.mjs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/core/operations/ShowOnMap.mjs b/src/core/operations/ShowOnMap.mjs index d75c2aa6a9..be606d18dd 100644 --- a/src/core/operations/ShowOnMap.mjs +++ b/src/core/operations/ShowOnMap.mjs @@ -71,6 +71,16 @@ class ShowOnMap extends Operation { } latLong = latLong.replace(/[,]$/, ""); latLong = latLong.replace(/°/g, ""); + + // The map requires a latitude and longitude pair. If the conversion only produced a + // single value (e.g. because the chosen input delimiter didn't match the input), bail + // out with a helpful message rather than passing it on to the map, which would throw an + // uncaught TypeError in the browser. + const coords = latLong.split(",").map(v => v.trim()); + if (coords.length !== 2 || coords.some(v => v === "" || isNaN(Number(v)))) { + throw new OperationError(`Could not show co-ordinates '${latLong}' on the map. Expected a latitude and longitude pair - check that the input format and delimiter are correct.`); + } + return latLong; } return input; From b683f0239c82289fa4a1bec7717867bd37821ce5 Mon Sep 17 00:00:00 2001 From: Leon Zandman Date: Mon, 25 May 2026 17:55:34 +0200 Subject: [PATCH 2/4] Add tests for Show on map operation to validate coordinate input --- tests/operations/index.mjs | 1 + tests/operations/tests/ShowOnMap.mjs | 39 ++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 tests/operations/tests/ShowOnMap.mjs diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index c12c271098..ed6a26168f 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -159,6 +159,7 @@ import "./tests/SeqUtils.mjs"; import "./tests/SetDifference.mjs"; import "./tests/SetIntersection.mjs"; import "./tests/SetUnion.mjs"; +import "./tests/ShowOnMap.mjs"; import "./tests/Shuffle.mjs"; import "./tests/SIGABA.mjs"; import "./tests/SM2.mjs"; diff --git a/tests/operations/tests/ShowOnMap.mjs b/tests/operations/tests/ShowOnMap.mjs new file mode 100644 index 0000000000..f9af64effe --- /dev/null +++ b/tests/operations/tests/ShowOnMap.mjs @@ -0,0 +1,39 @@ +/** + * Show on map tests + * + * @author Leon Zandman [leon@wirwar.com] + * + * @copyright Crown Copyright 2026 + * @license Apache-2.0 + */ +import TestRegister from "../../lib/TestRegister.mjs"; + +TestRegister.addTests([ + { + name: "Show on map: valid co-ordinate pair", + input: "51.5007, -0.1246", + // The presented output is the Leaflet map HTML; just check the co-ordinates made it through. + expectedMatch: /51\.5007,-0\.1246/, + recipeConfig: [ + { + op: "Show on map", + args: [13, "Auto", "Auto"] + }, + ], + }, + { + // Regression test: a comma-separated input with the delimiter set to "\n" used to be + // mis-detected as a single Degrees Decimal Minutes value (1° 24' = 1.4°), producing a single + // co-ordinate. That single value was then passed to Leaflet's setView([1.4], ...), throwing + // an uncaught "Cannot read properties of null (reading 'lat')" TypeError in the browser. + name: "Show on map: single value is rejected with a helpful error", + input: "1, 24", + expectedOutput: "Could not show co-ordinates '1.4' on the map. Expected a latitude and longitude pair - check that the input format and delimiter are correct.", + recipeConfig: [ + { + op: "Show on map", + args: [13, "Auto", "\\n"] + }, + ], + }, +]); From aae2312a5a7ff51ceba7db9473c986412b1896d1 Mon Sep 17 00:00:00 2001 From: Leon Zandman Date: Mon, 25 May 2026 18:07:12 +0200 Subject: [PATCH 3/4] Spelling. --- src/core/operations/ShowOnMap.mjs | 2 +- tests/operations/tests/ShowOnMap.mjs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/operations/ShowOnMap.mjs b/src/core/operations/ShowOnMap.mjs index be606d18dd..2eab5140f6 100644 --- a/src/core/operations/ShowOnMap.mjs +++ b/src/core/operations/ShowOnMap.mjs @@ -78,7 +78,7 @@ class ShowOnMap extends Operation { // uncaught TypeError in the browser. const coords = latLong.split(",").map(v => v.trim()); if (coords.length !== 2 || coords.some(v => v === "" || isNaN(Number(v)))) { - throw new OperationError(`Could not show co-ordinates '${latLong}' on the map. Expected a latitude and longitude pair - check that the input format and delimiter are correct.`); + throw new OperationError(`Could not show coordinates '${latLong}' on the map. Expected a latitude and longitude pair - check that the input format and delimiter are correct.`); } return latLong; diff --git a/tests/operations/tests/ShowOnMap.mjs b/tests/operations/tests/ShowOnMap.mjs index f9af64effe..8605ae704e 100644 --- a/tests/operations/tests/ShowOnMap.mjs +++ b/tests/operations/tests/ShowOnMap.mjs @@ -10,9 +10,9 @@ import TestRegister from "../../lib/TestRegister.mjs"; TestRegister.addTests([ { - name: "Show on map: valid co-ordinate pair", + name: "Show on map: valid coordinate pair", input: "51.5007, -0.1246", - // The presented output is the Leaflet map HTML; just check the co-ordinates made it through. + // The presented output is the Leaflet map HTML; just check the coordinates made it through. expectedMatch: /51\.5007,-0\.1246/, recipeConfig: [ { @@ -24,11 +24,11 @@ TestRegister.addTests([ { // Regression test: a comma-separated input with the delimiter set to "\n" used to be // mis-detected as a single Degrees Decimal Minutes value (1° 24' = 1.4°), producing a single - // co-ordinate. That single value was then passed to Leaflet's setView([1.4], ...), throwing + // coordinate. That single value was then passed to Leaflet's setView([1.4], ...), throwing // an uncaught "Cannot read properties of null (reading 'lat')" TypeError in the browser. name: "Show on map: single value is rejected with a helpful error", input: "1, 24", - expectedOutput: "Could not show co-ordinates '1.4' on the map. Expected a latitude and longitude pair - check that the input format and delimiter are correct.", + expectedOutput: "Could not show coordinates '1.4' on the map. Expected a latitude and longitude pair - check that the input format and delimiter are correct.", recipeConfig: [ { op: "Show on map", From 1831c3d28d7a96bb70961f58bf12c10b29efa4f6 Mon Sep 17 00:00:00 2001 From: GCHQDeveloper581 <63102987+GCHQDeveloper581@users.noreply.github.com> Date: Sat, 20 Jun 2026 10:19:54 +0000 Subject: [PATCH 4/4] Remove file that is now autogenerated --- tests/operations/index.mjs | 218 ------------------------------------- 1 file changed, 218 deletions(-) delete mode 100644 tests/operations/index.mjs diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs deleted file mode 100644 index 8d7b6af8e4..0000000000 --- a/tests/operations/index.mjs +++ /dev/null @@ -1,218 +0,0 @@ -/* eslint no-console: 0 */ - -/** - * Test Runner - * - * For running the tests in the test register. - * - * @author tlwr [toby@toby.codes] - * @author n1474335 [n1474335@gmail.com] - * @copyright Crown Copyright 2017 - * @license Apache-2.0 - */ - -import "../lib/wasmFetchPolyfill.mjs"; -import { setLongTestFailure, logTestReport } from "../lib/utils.mjs"; - -import TestRegister from "../lib/TestRegister.mjs"; -import "./tests/A1Z26CipherDecode.mjs"; -import "./tests/AESKeyWrap.mjs"; -import "./tests/AnalyseUUID.mjs"; -import "./tests/AlternatingCaps.mjs"; -import "./tests/AvroToJSON.mjs"; -import "./tests/BaconCipher.mjs"; -import "./tests/Base32.mjs"; -import "./tests/Base45.mjs"; -import "./tests/Base58.mjs"; -import "./tests/Base62.mjs"; -import "./tests/Base64.mjs"; -import "./tests/Base85.mjs"; -import "./tests/Base92.mjs"; -import "./tests/BCD.mjs"; -import "./tests/Bech32.mjs"; -import "./tests/BitwiseOp.mjs"; -import "./tests/BLAKE2b.mjs"; -import "./tests/BLAKE2s.mjs"; -import "./tests/BLAKE3.mjs"; -import "./tests/Bombe.mjs"; -import "./tests/BSON.mjs"; -import "./tests/ByteRepr.mjs"; -import "./tests/CaesarBoxCipher.mjs"; -import "./tests/CaretMdecode.mjs"; -import "./tests/CartesianProduct.mjs"; -import "./tests/CBORDecode.mjs"; -import "./tests/CBOREncode.mjs"; -import "./tests/CetaceanCipherDecode.mjs"; -import "./tests/CetaceanCipherEncode.mjs"; -import "./tests/ChaCha.mjs"; -import "./tests/ChangeIPFormat.mjs"; -import "./tests/CharEnc.mjs"; -import "./tests/Charts.mjs"; -import "./tests/Ciphers.mjs"; -import "./tests/CipherSaber2.mjs"; -import "./tests/CMAC.mjs"; -import "./tests/Code.mjs"; -import "./tests/Colossus.mjs"; -import "./tests/Comment.mjs"; -import "./tests/Compress.mjs"; -import "./tests/ConditionalJump.mjs"; -import "./tests/ConvertCoordinateFormat.mjs"; -import "./tests/ConvertLeetSpeak.mjs"; -import "./tests/ConvertToNATOAlphabet.mjs"; -import "./tests/CRCChecksum.mjs"; -import "./tests/Crypt.mjs"; -import "./tests/CSV.mjs"; -import "./tests/DateTime.mjs"; -import "./tests/DefangIP.mjs"; -import "./tests/DisassembleARM.mjs"; -import "./tests/DropNthBytes.mjs"; -import "./tests/ECDSA.mjs"; -import "./tests/ELFInfo.mjs"; -import "./tests/Enigma.mjs"; -import "./tests/EscapeSmartCharacters.mjs"; -import "./tests/ExtractAudioMetadata.mjs"; -import "./tests/ExtractEmailAddresses.mjs"; -import "./tests/ExtractHashes.mjs"; -import "./tests/ExtractIPAddresses.mjs"; -import "./tests/Fernet.mjs"; -import "./tests/Float.mjs"; -import "./tests/FileTree.mjs"; -import "./tests/FletcherChecksum.mjs"; -import "./tests/Fork.mjs"; -import "./tests/FromDecimal.mjs"; -import "./tests/GenerateAllChecksums.mjs"; -import "./tests/GenerateAllHashes.mjs"; -import "./tests/GenerateDeBruijnSequence.mjs"; -import "./tests/GenerateQRCode.mjs"; -import "./tests/GetAllCasings.mjs"; -import "./tests/GOST.mjs"; -import "./tests/Gunzip.mjs"; -import "./tests/Gzip.mjs"; -import "./tests/Hash.mjs"; -import "./tests/HASSH.mjs"; -import "./tests/HaversineDistance.mjs"; -import "./tests/Hex.mjs"; -import "./tests/Hexdump.mjs"; -import "./tests/HKDF.mjs"; -import "./tests/Image.mjs"; -import "./tests/IndexOfCoincidence.mjs"; -import "./tests/JA3Fingerprint.mjs"; -import "./tests/JA4.mjs"; -import "./tests/JA3SFingerprint.mjs"; -import "./tests/Jsonata.mjs"; -import "./tests/JSONBeautify.mjs"; -import "./tests/JSONMinify.mjs"; -import "./tests/JSONtoCSV.mjs"; -import "./tests/Jump.mjs"; -import "./tests/JWK.mjs"; -import "./tests/JWTDecode.mjs"; -import "./tests/JWTSign.mjs"; -import "./tests/JWTVerify.mjs"; -import "./tests/LevenshteinDistance.mjs"; -import "./tests/Lorenz.mjs"; -import "./tests/LS47.mjs"; -import "./tests/LuhnChecksum.mjs"; -import "./tests/LZNT1Decompress.mjs"; -import "./tests/LZString.mjs"; -import "./tests/Magic.mjs"; -import "./tests/Media.mjs"; -import "./tests/MIMEDecoding.mjs"; -import "./tests/Modhex.mjs"; -import "./tests/MorseCode.mjs"; -import "./tests/MS.mjs"; -import "./tests/MultipleBombe.mjs"; -import "./tests/MurmurHash3.mjs"; -import "./tests/NetBIOS.mjs"; -import "./tests/NormaliseUnicode.mjs"; -import "./tests/NTLM.mjs"; -import "./tests/OTP.mjs"; -import "./tests/ParseEthernetFrame.mjs"; -import "./tests/ParseIPv4Header.mjs"; -import "./tests/ParseIPRange.mjs"; -import "./tests/ParseObjectIDTimestamp.mjs"; -import "./tests/ParseQRCode.mjs"; -import "./tests/ParseSSHHostKey.mjs"; -import "./tests/ParseTCP.mjs"; -import "./tests/ParseTLSRecord.mjs"; -import "./tests/ParseTLV.mjs"; -import "./tests/ParseUDP.mjs"; -import "./tests/PEMtoHex.mjs"; -import "./tests/PGP.mjs"; -import "./tests/PHP.mjs"; -import "./tests/ParityBit.mjs"; -import "./tests/PHPSerialize.mjs"; -import "./tests/PowerSet.mjs"; -import "./tests/Protobuf.mjs"; -import "./tests/PubKeyFromCert.mjs"; -import "./tests/PubKeyFromPrivKey.mjs"; -import "./tests/Rabbit.mjs"; -import "./tests/RAKE.mjs"; -import "./tests/Regex.mjs"; -import "./tests/Register.mjs"; -import "./tests/RemoveANSIEscapeCodes.mjs"; -import "./tests/RegularExpression.mjs"; -import "./tests/RenderMarkdown.mjs"; -import "./tests/RisonEncodeDecode.mjs"; -import "./tests/Rotate.mjs"; -import "./tests/RSA.mjs"; -import "./tests/Salsa20.mjs"; -import "./tests/XSalsa20.mjs"; -import "./tests/SeqUtils.mjs"; -import "./tests/SetDifference.mjs"; -import "./tests/SetIntersection.mjs"; -import "./tests/SetUnion.mjs"; -import "./tests/ShowOnMap.mjs"; -import "./tests/Shuffle.mjs"; -import "./tests/SIGABA.mjs"; -import "./tests/SM2.mjs"; -import "./tests/SM4.mjs"; -import "./tests/RC6.mjs"; -// import "./tests/SplitColourChannels.mjs"; // Cannot test operations that use the File type yet -import "./tests/SQLBeautify.mjs"; -import "./tests/StrUtils.mjs"; -import "./tests/StripIPv4Header.mjs"; -import "./tests/StripTCPHeader.mjs"; -import "./tests/StripUDPHeader.mjs"; -import "./tests/Subsection.mjs"; -import "./tests/SwapCase.mjs"; -import "./tests/SymmetricDifference.mjs"; -import "./tests/TakeNthBytes.mjs"; -import "./tests/Template.mjs"; -import "./tests/TextEncodingBruteForce.mjs"; -import "./tests/TextIntegerConverter.mjs"; -import "./tests/ToFromInsensitiveRegex.mjs"; -import "./tests/TranslateDateTimeFormat.mjs"; -import "./tests/Typex.mjs"; -import "./tests/UnescapeString.mjs"; -import "./tests/Unicode.mjs"; -import "./tests/Wrap.mjs"; -import "./tests/URLEncodeDecode.mjs"; -import "./tests/RSA.mjs"; -import "./tests/CBOREncode.mjs"; -import "./tests/CBORDecode.mjs"; -import "./tests/JA3Fingerprint.mjs"; -import "./tests/JA3SFingerprint.mjs"; -import "./tests/HASSH.mjs"; -import "./tests/JSONtoYAML.mjs"; - -// Cannot test operations that use the File type yet -// import "./tests/SplitColourChannels.mjs"; -import "./tests/YARA.mjs"; -import "./tests/ParseCSR.mjs"; -import "./tests/XXTEA.mjs"; - -const testStatus = { - allTestsPassing: true, - counts: { - total: 0, - }, -}; - -setLongTestFailure(); - -const logOpsTestReport = logTestReport.bind(null, testStatus); - -(async function () { - const results = await TestRegister.runTests(); - logOpsTestReport(results); -})();