Added 'Parse TCP' operation

This commit is contained in:
n1474335 2022-05-30 18:06:15 +01:00
parent 477e4a7421
commit a895d1d82a
14 changed files with 430 additions and 81 deletions

View File

@ -217,7 +217,8 @@ module.exports = function (grunt) {
client: { client: {
logging: "error", logging: "error",
overlay: true overlay: true
} },
hot: "only"
}, },
plugins: [ plugins: [
new webpack.DefinePlugin(BUILD_CONSTANTS), new webpack.DefinePlugin(BUILD_CONSTANTS),

14
package-lock.json generated
View File

@ -98,7 +98,7 @@
"autoprefixer": "^10.4.4", "autoprefixer": "^10.4.4",
"babel-loader": "^8.2.4", "babel-loader": "^8.2.4",
"babel-plugin-dynamic-import-node": "^2.3.3", "babel-plugin-dynamic-import-node": "^2.3.3",
"chromedriver": "^99.0.0", "chromedriver": "^101.0.0",
"cli-progress": "^3.10.0", "cli-progress": "^3.10.0",
"colors": "^1.4.0", "colors": "^1.4.0",
"copy-webpack-plugin": "^10.2.4", "copy-webpack-plugin": "^10.2.4",
@ -4467,9 +4467,9 @@
} }
}, },
"node_modules/chromedriver": { "node_modules/chromedriver": {
"version": "99.0.0", "version": "101.0.0",
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-99.0.0.tgz", "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-101.0.0.tgz",
"integrity": "sha512-pyB+5LuyZdb7EBPL3i5D5yucZUD+SlkdiUtmpjaEnLd9zAXp+SvD/hP5xF4l/ZmWvUo/1ZLxAI1YBdhazGTpgA==", "integrity": "sha512-LkkWxy6KM/0YdJS8qBeg5vfkTZTRamhBfOttb4oic4echDgWvCU1E8QcBbUBOHqZpSrYMyi7WMKmKMhXFUaZ+w==",
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
@ -19171,9 +19171,9 @@
"dev": true "dev": true
}, },
"chromedriver": { "chromedriver": {
"version": "99.0.0", "version": "101.0.0",
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-99.0.0.tgz", "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-101.0.0.tgz",
"integrity": "sha512-pyB+5LuyZdb7EBPL3i5D5yucZUD+SlkdiUtmpjaEnLd9zAXp+SvD/hP5xF4l/ZmWvUo/1ZLxAI1YBdhazGTpgA==", "integrity": "sha512-LkkWxy6KM/0YdJS8qBeg5vfkTZTRamhBfOttb4oic4echDgWvCU1E8QcBbUBOHqZpSrYMyi7WMKmKMhXFUaZ+w==",
"dev": true, "dev": true,
"requires": { "requires": {
"@testim/chrome-version": "^1.1.2", "@testim/chrome-version": "^1.1.2",

View File

@ -48,7 +48,7 @@
"autoprefixer": "^10.4.4", "autoprefixer": "^10.4.4",
"babel-loader": "^8.2.4", "babel-loader": "^8.2.4",
"babel-plugin-dynamic-import-node": "^2.3.3", "babel-plugin-dynamic-import-node": "^2.3.3",
"chromedriver": "^99.0.0", "chromedriver": "^101.0.0",
"cli-progress": "^3.10.0", "cli-progress": "^3.10.0",
"colors": "^1.4.0", "colors": "^1.4.0",
"copy-webpack-plugin": "^10.2.4", "copy-webpack-plugin": "^10.2.4",

View File

@ -190,6 +190,7 @@
"Parse IP range", "Parse IP range",
"Parse IPv6 address", "Parse IPv6 address",
"Parse IPv4 header", "Parse IPv4 header",
"Parse TCP",
"Parse UDP", "Parse UDP",
"Parse SSH Host Key", "Parse SSH Host Key",
"Parse URI", "Parse URI",

View File

@ -13,7 +13,7 @@ import OperationError from "../errors/OperationError.mjs";
/** /**
* Convert a byte array into a binary string. * Convert a byte array into a binary string.
* *
* @param {Uint8Array|byteArray} data * @param {Uint8Array|byteArray|number} data
* @param {string} [delim="Space"] * @param {string} [delim="Space"]
* @param {number} [padding=8] * @param {number} [padding=8]
* @returns {string} * @returns {string}
@ -26,13 +26,17 @@ import OperationError from "../errors/OperationError.mjs";
* toBinary([10,20,30], ":"); * toBinary([10,20,30], ":");
*/ */
export function toBinary(data, delim="Space", padding=8) { export function toBinary(data, delim="Space", padding=8) {
if (!data) return "";
delim = Utils.charRep(delim); delim = Utils.charRep(delim);
let output = ""; let output = "";
for (let i = 0; i < data.length; i++) { if (data.length) { // array
output += data[i].toString(2).padStart(padding, "0") + delim; for (let i = 0; i < data.length; i++) {
output += data[i].toString(2).padStart(padding, "0") + delim;
}
} else if (typeof data === "number") { // Single value
return data.toString(2).padStart(padding, "0");
} else {
return "";
} }
if (delim.length) { if (delim.length) {

View File

@ -3778,8 +3778,8 @@ function parseDEFLATE(stream) {
while (!finalBlock) { while (!finalBlock) {
// Read header // Read header
finalBlock = stream.readBits(1); finalBlock = stream.readBits(1, "le");
const blockType = stream.readBits(2); const blockType = stream.readBits(2, "le");
if (blockType === 0) { if (blockType === 0) {
/* No compression */ /* No compression */
@ -3798,16 +3798,16 @@ function parseDEFLATE(stream) {
/* Dynamic Huffman */ /* Dynamic Huffman */
// Read the number of liternal and length codes // Read the number of liternal and length codes
const hlit = stream.readBits(5) + 257; const hlit = stream.readBits(5, "le") + 257;
// Read the number of distance codes // Read the number of distance codes
const hdist = stream.readBits(5) + 1; const hdist = stream.readBits(5, "le") + 1;
// Read the number of code lengths // Read the number of code lengths
const hclen = stream.readBits(4) + 4; const hclen = stream.readBits(4, "le") + 4;
// Parse code lengths // Parse code lengths
const codeLengths = new Uint8Array(huffmanOrder.length); const codeLengths = new Uint8Array(huffmanOrder.length);
for (let i = 0; i < hclen; i++) { for (let i = 0; i < hclen; i++) {
codeLengths[huffmanOrder[i]] = stream.readBits(3); codeLengths[huffmanOrder[i]] = stream.readBits(3, "le");
} }
// Parse length table // Parse length table
@ -3819,16 +3819,16 @@ function parseDEFLATE(stream) {
code = readHuffmanCode(stream, codeLengthsTable); code = readHuffmanCode(stream, codeLengthsTable);
switch (code) { switch (code) {
case 16: case 16:
repeat = 3 + stream.readBits(2); repeat = 3 + stream.readBits(2, "le");
while (repeat--) lengthTable[i++] = prev; while (repeat--) lengthTable[i++] = prev;
break; break;
case 17: case 17:
repeat = 3 + stream.readBits(3); repeat = 3 + stream.readBits(3, "le");
while (repeat--) lengthTable[i++] = 0; while (repeat--) lengthTable[i++] = 0;
prev = 0; prev = 0;
break; break;
case 18: case 18:
repeat = 11 + stream.readBits(7); repeat = 11 + stream.readBits(7, "le");
while (repeat--) lengthTable[i++] = 0; while (repeat--) lengthTable[i++] = 0;
prev = 0; prev = 0;
break; break;
@ -3886,11 +3886,11 @@ function parseHuffmanBlock(stream, litTab, distTab) {
if (code < 256) continue; if (code < 256) continue;
// Length code // Length code
stream.readBits(lengthExtraTable[code - 257]); stream.readBits(lengthExtraTable[code - 257], "le");
// Dist code // Dist code
code = readHuffmanCode(stream, distTab); code = readHuffmanCode(stream, distTab);
stream.readBits(distanceExtraTable[code]); stream.readBits(distanceExtraTable[code], "le");
} }
} }
@ -3948,7 +3948,7 @@ function readHuffmanCode(stream, table) {
const [codeTable, maxCodeLength] = table; const [codeTable, maxCodeLength] = table;
// Read max length // Read max length
const bitsBuf = stream.readBits(maxCodeLength); const bitsBuf = stream.readBits(maxCodeLength, "le");
const codeWithLength = codeTable[bitsBuf & ((1 << maxCodeLength) - 1)]; const codeWithLength = codeTable[bitsBuf & ((1 << maxCodeLength) - 1)];
const codeLength = codeWithLength >>> 16; const codeLength = codeWithLength >>> 16;

47
src/core/lib/Protocol.mjs Normal file
View File

@ -0,0 +1,47 @@
/**
* Protocol parsing functions.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2022
* @license Apache-2.0
*/
import BigNumber from "bignumber.js";
import {toHexFast} from "../lib/Hex.mjs";
/**
* Recursively displays a JSON object as an HTML table
*
* @param {Object} obj
* @returns string
*/
export function objToTable(obj, nested=false) {
let html = `<table
class='table table-sm table-nonfluid ${nested ? "mb-0 table-borderless" : "table-bordered"}'
style='table-layout: fixed; ${nested ? "margin: -1px !important;" : ""}'>`;
if (!nested)
html += `<tr>
<th>Field</th>
<th>Value</th>
</tr>`;
for (const key in obj) {
html += `<tr><td style='word-wrap: break-word'>${key}</td>`;
if (typeof obj[key] === "object")
html += `<td style='padding: 0'>${objToTable(obj[key], true)}</td>`;
else
html += `<td>${obj[key]}</td>`;
html += "</tr>";
}
html += "</table>";
return html;
}
/**
* Converts bytes into a BigNumber string
* @param {Uint8Array} bs
* @returns {string}
*/
export function bytesToLargeNumber(bs) {
return BigNumber(toHexFast(bs), 16).toString();
}

View File

@ -27,15 +27,17 @@ export default class Stream {
} }
/** /**
* Get a number of bytes from the current position. * Get a number of bytes from the current position, or all remaining bytes.
* *
* @param {number} numBytes * @param {number} [numBytes=null]
* @returns {Uint8Array} * @returns {Uint8Array}
*/ */
getBytes(numBytes) { getBytes(numBytes=null) {
if (this.position > this.length) return undefined; if (this.position > this.length) return undefined;
const newPosition = this.position + numBytes; const newPosition = numBytes !== null ?
this.position + numBytes :
this.length;
const bytes = this.bytes.slice(this.position, newPosition); const bytes = this.bytes.slice(this.position, newPosition);
this.position = newPosition; this.position = newPosition;
this.bitPos = 0; this.bitPos = 0;
@ -91,34 +93,40 @@ export default class Stream {
} }
/** /**
* Reads a number of bits from the buffer. * Reads a number of bits from the buffer in big or little endian.
*
* @TODO Add endianness
* *
* @param {number} numBits * @param {number} numBits
* @param {string} [endianness="be"]
* @returns {number} * @returns {number}
*/ */
readBits(numBits) { readBits(numBits, endianness="be") {
if (this.position > this.length) return undefined; if (this.position > this.length) return undefined;
let bitBuf = 0, let bitBuf = 0,
bitBufLen = 0; bitBufLen = 0;
// Add remaining bits from current byte // Add remaining bits from current byte
bitBuf = (this.bytes[this.position++] & bitMask(this.bitPos)) >>> this.bitPos; bitBuf = this.bytes[this.position++] & bitMask(this.bitPos);
if (endianness !== "be") bitBuf >>>= this.bitPos;
bitBufLen = 8 - this.bitPos; bitBufLen = 8 - this.bitPos;
this.bitPos = 0; this.bitPos = 0;
// Not enough bits yet // Not enough bits yet
while (bitBufLen < numBits) { while (bitBufLen < numBits) {
bitBuf |= this.bytes[this.position++] << bitBufLen; if (endianness === "be")
bitBuf = (bitBuf << bitBufLen) | this.bytes[this.position++];
else
bitBuf |= this.bytes[this.position++] << bitBufLen;
bitBufLen += 8; bitBufLen += 8;
} }
// Reverse back to numBits // Reverse back to numBits
if (bitBufLen > numBits) { if (bitBufLen > numBits) {
const excess = bitBufLen - numBits; const excess = bitBufLen - numBits;
bitBuf &= (1 << numBits) - 1; if (endianness === "be")
bitBuf >>>= excess;
else
bitBuf &= (1 << numBits) - 1;
bitBufLen -= excess; bitBufLen -= excess;
this.position--; this.position--;
this.bitPos = 8 - excess; this.bitPos = 8 - excess;
@ -133,7 +141,9 @@ export default class Stream {
* @returns {number} The bit mask * @returns {number} The bit mask
*/ */
function bitMask(bitPos) { function bitMask(bitPos) {
return 256 - (1 << bitPos); return endianness === "be" ?
(1 << (8 - bitPos)) - 1 :
256 - (1 << bitPos);
} }
} }

View File

@ -0,0 +1,245 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2022
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import Stream from "../lib/Stream.mjs";
import {toHexFast, fromHex} from "../lib/Hex.mjs";
import {toBinary} from "../lib/Binary.mjs";
import {objToTable, bytesToLargeNumber} from "../lib/Protocol.mjs";
import Utils from "../Utils.mjs";
import OperationError from "../errors/OperationError.mjs";
import BigNumber from "bignumber.js";
/**
* Parse TCP operation
*/
class ParseTCP extends Operation {
/**
* ParseTCP constructor
*/
constructor() {
super();
this.name = "Parse TCP";
this.module = "Default";
this.description = "Parses a TCP header and payload (if present).";
this.infoURL = "https://wikipedia.org/wiki/Transmission_Control_Protocol";
this.inputType = "string";
this.outputType = "json";
this.presentType = "html";
this.args = [
{
name: "Input format",
type: "option",
value: ["Hex", "Raw"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
run(input, args) {
const format = args[0];
if (format === "Hex") {
input = fromHex(input);
} else if (format === "Raw") {
input = Utils.strToArrayBuffer(input);
} else {
throw new OperationError("Unrecognised input format.");
}
const s = new Stream(new Uint8Array(input));
if (s.length < 20) {
throw new OperationError("Need at least 20 bytes for a TCP Header");
}
// Parse Header
const TCPPacket = {
"Source port": s.readInt(2),
"Destination port": s.readInt(2),
"Sequence number": bytesToLargeNumber(s.getBytes(4)),
"Acknowledgement number": s.readInt(4),
"Data offset": s.readBits(4),
"Flags": {
"Reserved": toBinary(s.readBits(3), "", 3),
"NS": s.readBits(1),
"CWR": s.readBits(1),
"ECE": s.readBits(1),
"URG": s.readBits(1),
"ACK": s.readBits(1),
"PSH": s.readBits(1),
"RST": s.readBits(1),
"SYN": s.readBits(1),
"FIN": s.readBits(1),
},
"Window size": s.readInt(2),
"Checksum": "0x" + toHexFast(s.getBytes(2)),
"Urgent pointer": "0x" + toHexFast(s.getBytes(2))
};
// Parse options if present
let windowScaleShift = 0;
if (TCPPacket["Data offset"] > 5) {
let remainingLength = TCPPacket["Data offset"] * 4 - 20;
const options = {};
while (remainingLength > 0) {
const option = {
"Kind": s.readInt(1)
};
let opt = { name: "Reserved", length: true };
if (Object.prototype.hasOwnProperty.call(TCP_OPTION_KIND_LOOKUP, option.Kind)) {
opt = TCP_OPTION_KIND_LOOKUP[option.Kind];
}
// Add Length and Value fields
if (opt.length) {
option.Length = s.readInt(1);
if (option.Length > 2) {
if (Object.prototype.hasOwnProperty.call(opt, "parser")) {
option.Value = opt.parser(s.getBytes(option.Length - 2));
} else {
option.Value = option.Length <= 6 ?
s.readInt(option.Length - 2):
"0x" + toHexFast(s.getBytes(option.Length - 2));
}
// Store Window Scale shift for later
if (option.Kind === 3 && option.Value) {
windowScaleShift = option.Value["Shift count"];
}
}
}
options[opt.name] = option;
const length = option.Length || 1;
remainingLength -= length;
}
TCPPacket.Options = options;
}
if (s.hasMore()) {
TCPPacket.Data = "0x" + toHexFast(s.getBytes());
}
// Improve values
TCPPacket["Data offset"] = `${TCPPacket["Data offset"]} (${TCPPacket["Data offset"] * 4} bytes)`;
const trueWndSize = BigNumber(TCPPacket["Window size"]).multipliedBy(BigNumber(2).pow(BigNumber(windowScaleShift)));
TCPPacket["Window size"] = `${TCPPacket["Window size"]} (Scaled: ${trueWndSize})`;
return TCPPacket;
}
/**
* Displays the TCP Packet in a tabular style
* @param {Object} data
* @returns {html}
*/
present(data) {
return objToTable(data);
}
}
// Taken from https://www.iana.org/assignments/tcp-parameters/tcp-parameters.xhtml
// on 2022-05-30
const TCP_OPTION_KIND_LOOKUP = {
0: { name: "End of Option List", length: false },
1: { name: "No-Operation", length: false },
2: { name: "Maximum Segment Size", length: true },
3: { name: "Window Scale", length: true, parser: windowScaleParser },
4: { name: "SACK Permitted", length: true },
5: { name: "SACK", length: true },
6: { name: "Echo (obsoleted by option 8)", length: true },
7: { name: "Echo Reply (obsoleted by option 8)", length: true },
8: { name: "Timestamps", length: true, parser: tcpTimestampParser },
9: { name: "Partial Order Connection Permitted (obsolete)", length: true },
10: { name: "Partial Order Service Profile (obsolete)", length: true },
11: { name: "CC (obsolete)", length: true },
12: { name: "CC.NEW (obsolete)", length: true },
13: { name: "CC.ECHO (obsolete)", length: true },
14: { name: "TCP Alternate Checksum Request (obsolete)", length: true, parser: tcpAlternateChecksumParser },
15: { name: "TCP Alternate Checksum Data (obsolete)", length: true },
16: { name: "Skeeter", length: true },
17: { name: "Bubba", length: true },
18: { name: "Trailer Checksum Option", length: true },
19: { name: "MD5 Signature Option (obsoleted by option 29)", length: true },
20: { name: "SCPS Capabilities", length: true },
21: { name: "Selective Negative Acknowledgements", length: true },
22: { name: "Record Boundaries", length: true },
23: { name: "Corruption experienced", length: true },
24: { name: "SNAP", length: true },
25: { name: "Unassigned (released 2000-12-18)", length: true },
26: { name: "TCP Compression Filter", length: true },
27: { name: "Quick-Start Response", length: true },
28: { name: "User Timeout Option (also, other known unauthorized use)", length: true },
29: { name: "TCP Authentication Option (TCP-AO)", length: true },
30: { name: "Multipath TCP (MPTCP)", length: true },
69: { name: "Encryption Negotiation (TCP-ENO)", length: true },
70: { name: "Reserved (known unauthorized use without proper IANA assignment)", length: true },
76: { name: "Reserved (known unauthorized use without proper IANA assignment)", length: true },
77: { name: "Reserved (known unauthorized use without proper IANA assignment)", length: true },
78: { name: "Reserved (known unauthorized use without proper IANA assignment)", length: true },
253: { name: "RFC3692-style Experiment 1 (also improperly used for shipping products) ", length: true },
254: { name: "RFC3692-style Experiment 2 (also improperly used for shipping products) ", length: true }
};
/**
* Parses the TCP Alternate Checksum Request field
* @param {Uint8Array} data
*/
function tcpAlternateChecksumParser(data) {
const lookup = {
0: "TCP Checksum",
1: "8-bit Fletchers's algorithm",
2: "16-bit Fletchers's algorithm",
3: "Redundant Checksum Avoidance"
}[data[0]];
return `${lookup} (0x${toHexFast(data)})`;
}
/**
* Parses the TCP Timestamp field
* @param {Uint8Array} data
*/
function tcpTimestampParser(data) {
const s = new Stream(data);
if (s.length !== 8)
return `Error: Timestamp field should be 8 bytes long (received 0x${toHexFast(data)})`;
const tsval = bytesToLargeNumber(s.getBytes(4)),
tsecr = bytesToLargeNumber(s.getBytes(4));
return {
"Current Timestamp": tsval,
"Echo Reply": tsecr
};
}
/**
* Parses the Window Scale field
* @param {Uint8Array} data
*/
function windowScaleParser(data) {
if (data.length !== 1)
return `Error: Window Scale should be one byte long (received 0x${toHexFast(data)})`;
return {
"Shift count": data[0],
"Multiplier": 1 << data[0]
};
}
export default ParseTCP;

View File

@ -6,7 +6,9 @@
import Operation from "../Operation.mjs"; import Operation from "../Operation.mjs";
import Stream from "../lib/Stream.mjs"; import Stream from "../lib/Stream.mjs";
import {toHex} from "../lib/Hex.mjs"; import {toHexFast, fromHex} from "../lib/Hex.mjs";
import {objToTable} from "../lib/Protocol.mjs";
import Utils from "../Utils.mjs";
import OperationError from "../errors/OperationError.mjs"; import OperationError from "../errors/OperationError.mjs";
/** /**
@ -24,58 +26,61 @@ class ParseUDP extends Operation {
this.module = "Default"; this.module = "Default";
this.description = "Parses a UDP header and payload (if present)."; this.description = "Parses a UDP header and payload (if present).";
this.infoURL = "https://wikipedia.org/wiki/User_Datagram_Protocol"; this.infoURL = "https://wikipedia.org/wiki/User_Datagram_Protocol";
this.inputType = "ArrayBuffer"; this.inputType = "string";
this.outputType = "json"; this.outputType = "json";
this.presentType = "html"; this.presentType = "html";
this.args = []; this.args = [
{
name: "Input format",
type: "option",
value: ["Hex", "Raw"]
}
];
} }
/** /**
* @param {ArrayBuffer} input * @param {string} input
* @param {Object[]} args
* @returns {Object} * @returns {Object}
*/ */
run(input, args) { run(input, args) {
if (input.byteLength < 8) { const format = args[0];
throw new OperationError("Need 8 bytes for a UDP Header");
if (format === "Hex") {
input = fromHex(input);
} else if (format === "Raw") {
input = Utils.strToArrayBuffer(input);
} else {
throw new OperationError("Unrecognised input format.");
} }
const s = new Stream(new Uint8Array(input)); const s = new Stream(new Uint8Array(input));
if (s.length < 8) {
throw new OperationError("Need 8 bytes for a UDP Header");
}
// Parse Header // Parse Header
const UDPPacket = { const UDPPacket = {
"Source port": s.readInt(2), "Source port": s.readInt(2),
"Destination port": s.readInt(2), "Destination port": s.readInt(2),
"Length": s.readInt(2), "Length": s.readInt(2),
"Checksum": toHex(s.getBytes(2), "") "Checksum": "0x" + toHexFast(s.getBytes(2))
}; };
// Parse data if present // Parse data if present
if (s.hasMore()) { if (s.hasMore()) {
UDPPacket.Data = toHex(s.getBytes(UDPPacket.Length - 8), ""); UDPPacket.Data = "0x" + toHexFast(s.getBytes(UDPPacket.Length - 8));
} }
return UDPPacket; return UDPPacket;
} }
/** /**
* Displays the UDP Packet in a table style * Displays the UDP Packet in a tabular style
* @param {Object} data * @param {Object} data
* @returns {html} * @returns {html}
*/ */
present(data) { present(data) {
const html = []; return objToTable(data);
html.push("<table class='table table-hover table-sm table-bordered table-nonfluid' style='table-layout: fixed'>");
html.push("<tr>");
html.push("<th>Field</th>");
html.push("<th>Value</th>");
html.push("</tr>");
for (const key in data) {
html.push("<tr>");
html.push("<td style=\"word-wrap:break-word\">" + key + "</td>");
html.push("<td>" + data[key] + "</td>");
html.push("</tr>");
}
html.push("</table>");
return html.join("");
} }
} }

View File

@ -109,11 +109,15 @@ class OperationsWaiter {
const matchedOps = []; const matchedOps = [];
const matchedDescs = []; const matchedDescs = [];
// Create version with no whitespace for the fuzzy match
// Helps avoid missing matches e.g. query "TCP " would not find "Parse TCP"
const inStrNWS = inStr.replace(/\s/g, "");
for (const opName in this.app.operations) { for (const opName in this.app.operations) {
const op = this.app.operations[opName]; const op = this.app.operations[opName];
// Match op name using fuzzy match // Match op name using fuzzy match
const [nameMatch, score, idxs] = fuzzyMatch(inStr, opName); const [nameMatch, score, idxs] = fuzzyMatch(inStrNWS, opName);
// Match description based on exact match // Match description based on exact match
const descPos = op.description.toLowerCase().indexOf(inStr.toLowerCase()); const descPos = op.description.toLowerCase().indexOf(inStr.toLowerCase());

View File

@ -96,6 +96,7 @@ import "./tests/Protobuf.mjs";
import "./tests/ParseSSHHostKey.mjs"; import "./tests/ParseSSHHostKey.mjs";
import "./tests/DefangIP.mjs"; import "./tests/DefangIP.mjs";
import "./tests/ParseUDP.mjs"; import "./tests/ParseUDP.mjs";
import "./tests/ParseTCP.mjs";
import "./tests/AvroToJSON.mjs"; import "./tests/AvroToJSON.mjs";
import "./tests/Lorenz.mjs"; import "./tests/Lorenz.mjs";
import "./tests/LuhnChecksum.mjs"; import "./tests/LuhnChecksum.mjs";

View File

@ -0,0 +1,44 @@
/**
* Parse TCP tests.
*
* @author n1474335
* @copyright Crown Copyright 2022
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "Parse TCP: No options",
input: "c2eb0050a138132e70dc9fb9501804025ea70000",
expectedMatch: /1026 \(Scaled: 1026\)/,
recipeConfig: [
{
op: "Parse TCP",
args: ["Hex"],
}
],
},
{
name: "Parse TCP: Options",
input: "c2eb0050a1380c1f000000008002faf080950000020405b40103030801010402",
expectedMatch: /1460/,
recipeConfig: [
{
op: "Parse TCP",
args: ["Hex"],
}
],
},
{
name: "Parse TCP: Timestamps",
input: "9e90e11574d57b2c00000000a002ffffe5740000020405b40402080aa4e8c8f50000000001030308",
expectedMatch: /2766719221/,
recipeConfig: [
{
op: "Parse TCP",
args: ["Hex"],
}
],
}
]);

View File

@ -2,7 +2,6 @@
* Parse UDP tests. * Parse UDP tests.
* *
* @author h345983745 * @author h345983745
*
* @copyright Crown Copyright 2019 * @copyright Crown Copyright 2019
* @license Apache-2.0 * @license Apache-2.0
*/ */
@ -12,15 +11,11 @@ TestRegister.addTests([
{ {
name: "Parse UDP: No Data - JSON", name: "Parse UDP: No Data - JSON",
input: "04 89 00 35 00 2c 01 01", input: "04 89 00 35 00 2c 01 01",
expectedOutput: "{\"Source port\":1161,\"Destination port\":53,\"Length\":44,\"Checksum\":\"0101\"}", expectedOutput: "{\"Source port\":1161,\"Destination port\":53,\"Length\":44,\"Checksum\":\"0x0101\"}",
recipeConfig: [ recipeConfig: [
{
op: "From Hex",
args: ["Auto"],
},
{ {
op: "Parse UDP", op: "Parse UDP",
args: [], args: ["Hex"],
}, },
{ {
op: "JSON Minify", op: "JSON Minify",
@ -30,15 +25,11 @@ TestRegister.addTests([
}, { }, {
name: "Parse UDP: With Data - JSON", name: "Parse UDP: With Data - JSON",
input: "04 89 00 35 00 2c 01 01 02 02", input: "04 89 00 35 00 2c 01 01 02 02",
expectedOutput: "{\"Source port\":1161,\"Destination port\":53,\"Length\":44,\"Checksum\":\"0101\",\"Data\":\"0202\"}", expectedOutput: "{\"Source port\":1161,\"Destination port\":53,\"Length\":44,\"Checksum\":\"0x0101\",\"Data\":\"0x0202\"}",
recipeConfig: [ recipeConfig: [
{
op: "From Hex",
args: ["Auto"],
},
{ {
op: "Parse UDP", op: "Parse UDP",
args: [], args: ["Hex"],
}, },
{ {
op: "JSON Minify", op: "JSON Minify",
@ -51,13 +42,9 @@ TestRegister.addTests([
input: "04 89 00", input: "04 89 00",
expectedOutput: "Need 8 bytes for a UDP Header", expectedOutput: "Need 8 bytes for a UDP Header",
recipeConfig: [ recipeConfig: [
{
op: "From Hex",
args: ["Auto"],
},
{ {
op: "Parse UDP", op: "Parse UDP",
args: [], args: ["Hex"],
}, },
{ {
op: "JSON Minify", op: "JSON Minify",