2018-05-03 10:20:13 +01:00
/* eslint no-console: 0 */
/**
* nodeApi.js
*
* Test node api utilities
*
* @author d98762625 [d98762625@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import assert from "assert" ;
import it from "../assertionHandler" ;
import chef from "../../../src/node/index" ;
2018-06-06 17:29:30 +01:00
import OperationError from "../../../src/core/errors/OperationError" ;
2019-02-15 15:20:05 +00:00
import NodeDish from "../../../src/node/NodeDish" ;
2018-10-05 16:32:12 +01:00
import fs from "fs" ;
2018-06-06 17:29:30 +01:00
2018-10-12 11:30:41 +01:00
import { toBase32 , Dish , SHA3 } from "../../../src/node/index" ;
2019-01-04 12:14:02 +00:00
import TestRegister from "../../lib/TestRegister" ;
2018-05-03 10:20:13 +01:00
TestRegister . addApiTests ([
it ( "should have some operations" , () => {
assert ( chef );
assert ( chef . toBase32 );
assert ( chef . setUnion );
assert ( ! chef . randomFunction );
}),
2018-06-06 17:29:30 +01:00
it ( "should export other functions at top level" , () => {
assert ( toBase32 );
}),
it ( "should be synchronous" , () => {
2018-05-03 10:20:13 +01:00
try {
2018-06-06 17:29:30 +01:00
const result = chef . toBase32 ( "input" );
2018-05-03 10:20:13 +01:00
assert . notEqual ( "something" , result );
} catch ( e ) {
// shouldnt reach here
assert ( false );
}
try {
const fail = chef . setUnion ( "1" );
// shouldnt get here
2018-05-03 13:58:15 +01:00
assert ( ! fail || false );
2018-05-03 10:20:13 +01:00
} catch ( e ) {
assert ( true );
}
}),
2018-06-06 17:29:30 +01:00
it ( "should not catch Errors" , () => {
try {
chef . setUnion ( "1" );
2018-05-03 10:20:13 +01:00
assert ( false );
2018-06-06 17:29:30 +01:00
} catch ( e ) {
assert ( e instanceof OperationError );
}
2018-05-11 19:25:14 +01:00
}),
2018-06-06 17:29:30 +01:00
it ( "should accept arguments in object format for operations" , () => {
const result = chef . setUnion ( "1 2 3 4:3 4 5 6" , {
2018-05-11 19:25:14 +01:00
itemDelimiter : " " ,
sampleDelimiter : ":"
});
2018-06-06 17:29:30 +01:00
assert . equal ( result . value , "1 2 3 4 5 6" );
2018-05-11 19:25:14 +01:00
}),
2018-06-06 17:29:30 +01:00
it ( "should accept just some of the optional arguments being overriden" , () => {
const result = chef . setIntersection ( "1 2 3 4 5\\n\\n3 4 5" , {
2018-05-11 19:25:14 +01:00
itemDelimiter : " " ,
});
2018-06-06 17:29:30 +01:00
assert . equal ( result . value , "3 4 5" );
2018-05-11 19:25:14 +01:00
}),
2018-06-06 17:29:30 +01:00
it ( "should accept no override arguments and just use the default values" , () => {
const result = chef . powerSet ( "1,2,3" );
assert . equal ( result . value , "\n3\n2\n1\n2,3\n1,3\n1,2\n1,2,3\n" );
}),
it ( "should return an object with a .to method" , () => {
const result = chef . toBase32 ( "input" );
assert ( result . to );
assert . equal ( result . to ( "string" ), "NFXHA5LU" );
}),
it ( "should return an object with a .get method" , () => {
const result = chef . toBase32 ( "input" );
assert ( result . get );
assert . equal ( result . get ( "string" ), "NFXHA5LU" );
}),
2019-02-15 15:20:05 +00:00
it ( "should return a NodeDish" , async () => {
2018-06-06 17:29:30 +01:00
const result = chef . toBase32 ( "input" );
2019-02-15 15:20:05 +00:00
assert ( result instanceof NodeDish );
2018-06-06 17:29:30 +01:00
}),
it ( "should coerce to a string as you expect" , () => {
const result = chef . fromBase32 ( chef . toBase32 ( "something" ));
assert . equal ( String ( result ), "something" );
// This kind of coercion uses toValue
assert . equal ( "" + result , "NaN" );
}),
it ( "should coerce to a number as you expect" , () => {
const result = chef . fromBase32 ( chef . toBase32 ( "32" ));
assert . equal ( 3 + result , 35 );
}),
2018-06-19 16:41:16 +01:00
it ( "chef.help: should exist" , () => {
assert ( chef . help );
}),
it ( "chef.help: should describe a operation" , () => {
const result = chef . help ( "tripleDESDecrypt" );
2018-09-04 09:33:49 +01:00
assert . strictEqual ( result [ 0 ]. name , "Triple DES Decrypt" );
assert . strictEqual ( result [ 0 ]. module , "Ciphers" );
assert . strictEqual ( result [ 0 ]. inputType , "string" );
assert . strictEqual ( result [ 0 ]. outputType , "string" );
assert . strictEqual ( result [ 0 ]. description , "Triple DES applies DES three times to each block to increase key size.<br><br><b>Key:</b> Triple DES uses a key length of 24 bytes (192 bits).<br>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." );
assert . strictEqual ( result [ 0 ]. args . length , 5 );
2018-06-19 16:41:16 +01:00
}),
it ( "chef.help: null for invalid operation" , () => {
const result = chef . help ( "some invalid function name" );
assert . strictEqual ( result , null );
}),
2018-06-25 17:58:57 +01:00
it ( "chef.help: takes a wrapped operation as input" , () => {
const result = chef . help ( chef . toBase32 );
2018-09-04 09:33:49 +01:00
assert . strictEqual ( result [ 0 ]. name , "To Base32" );
assert . strictEqual ( result [ 0 ]. module , "Default" );
}),
it ( "chef.help: returns multiple results" , () => {
const result = chef . help ( "base 64" );
2018-09-04 09:55:41 +01:00
assert . strictEqual ( result . length , 10 );
2018-09-04 09:33:49 +01:00
}),
it ( "chef.help: looks in description for matches too" , () => {
// string only in one operation's description.
const result = chef . help ( "Converts a unit of data to another format." );
assert . strictEqual ( result . length , 1 );
assert . strictEqual ( result [ 0 ]. name , "Convert data units" );
}),
it ( "chef.help: lists name matches before desc matches" , () => {
2019-03-20 11:57:47 +00:00
const result = chef . help ( "Checksum" );
assert . ok ( result [ 0 ]. name . includes ( "Checksum" ));
assert . ok ( result [ 1 ]. name . includes ( "Checksum" ));
assert . strictEqual ( result [ result . length - 1 ]. name . includes ( "Checksum" ), false );
assert . ok ( result [ result . length - 1 ]. description . includes ( "checksum" ));
}),
it ( "chef.help: exact name match only returns one result" , () => {
2018-09-04 09:33:49 +01:00
const result = chef . help ( "MD5" );
2019-03-20 11:57:47 +00:00
assert . strictEqual ( result . length , 1 );
assert . strictEqual ( result [ 0 ]. name , "MD5" );
}),
it ( "chef.help: exact match ignores whitespace" , () => {
const result = chef . help ( "tobase64" );
assert . strictEqual ( result . length , 1 );
assert . strictEqual ( result [ 0 ]. name , "To Base64" );
2018-07-06 12:54:19 +01:00
}),
it ( "chef.bake: should exist" , () => {
assert ( chef . bake );
}),
2019-02-15 16:11:13 +00:00
it ( "chef.bake: should return NodeDish" , () => {
2018-07-06 12:54:19 +01:00
const result = chef . bake ( "input" , "to base 64" );
2019-02-15 16:11:13 +00:00
assert ( result instanceof NodeDish );
2018-07-06 12:54:19 +01:00
}),
it ( "chef.bake: should take an input and an op name and perform it" , () => {
const result = chef . bake ( "some input" , "to base 32" );
assert . strictEqual ( result . toString (), "ONXW2ZJANFXHA5LU" );
}),
it ( "chef.bake: should complain if recipe isnt a valid object" , () => {
try {
chef . bake ( "some input" , 3264 );
} catch ( e ) {
assert . strictEqual ( e . name , "TypeError" );
assert . strictEqual ( e . message , "Recipe can only contain function names or functions" );
}
}),
it ( "chef.bake: Should complain if string op is invalid" , () => {
try {
chef . bake ( "some input" , "not a valid operation" );
assert . fail ( "Shouldn't be hit" );
} catch ( e ) {
assert . strictEqual ( e . name , "TypeError" );
assert . strictEqual ( e . message , "Couldn't find an operation with name 'not a valid operation'." );
}
}),
it ( "chef.bake: Should take an input and an operation and perform it" , () => {
const result = chef . bake ( "https://google.com/search?q=help" , chef . parseURI );
assert . strictEqual ( result . toString (), "Protocol:\thttps:\nHostname:\tgoogle.com\nPath name:\t/search\nArguments:\n\tq = help\n" );
}),
it ( "chef.bake: Should complain if an invalid operation is inputted" , () => {
try {
chef . bake ( "https://google.com/search?q=help" , () => {});
assert . fail ( "Shouldn't be hit" );
} catch ( e ) {
assert . strictEqual ( e . name , "TypeError" );
assert . strictEqual ( e . message , "Inputted function not a Chef operation." );
}
}),
it ( "chef.bake: accepts an array of operation names and performs them all in order" , () => {
const result = chef . bake ( "https://google.com/search?q=that's a complicated question" , [ "URL encode" , "URL decode" , "Parse URI" ]);
assert . strictEqual ( result . toString (), "Protocol:\thttps:\nHostname:\tgoogle.com\nPath name:\t/search\nArguments:\n\tq = that's a complicated question\n" );
}),
2018-08-31 15:03:53 +01:00
it ( "chef.bake: forgiving with operation names" , () =>{
const result = chef . bake ( "https://google.com/search?q=that's a complicated question" , [ "urlencode" , "url decode" , "parseURI" ]);
assert . strictEqual ( result . toString (), "Protocol:\thttps:\nHostname:\tgoogle.com\nPath name:\t/search\nArguments:\n\tq = that's a complicated question\n" );
}),
it ( "chef.bake: forgiving with operation names" , () =>{
const result = chef . bake ( "hello" , [ "to base 64" ]);
assert . strictEqual ( result . toString (), "aGVsbG8=" );
}),
2018-07-06 12:54:19 +01:00
it ( "chef.bake: if recipe is empty array, return input as dish" , () => {
const result = chef . bake ( "some input" , []);
assert . strictEqual ( result . toString (), "some input" );
2019-02-15 16:11:13 +00:00
assert ( result instanceof NodeDish , "Result is not instance of NodeDish" );
2018-07-06 12:54:19 +01:00
}),
it ( "chef.bake: accepts an array of operations as recipe" , () => {
const result = chef . bake ( "https://google.com/search?q=that's a complicated question" , [ chef . URLEncode , chef . URLDecode , chef . parseURI ]);
assert . strictEqual ( result . toString (), "Protocol:\thttps:\nHostname:\tgoogle.com\nPath name:\t/search\nArguments:\n\tq = that's a complicated question\n" );
}),
it ( "should complain if an invalid operation is inputted as part of array" , () => {
try {
chef . bake ( "something" , [() => {}]);
} catch ( e ) {
assert . strictEqual ( e . name , "TypeError" );
assert . strictEqual ( e . message , "Inputted function not a Chef operation." );
}
}),
it ( "chef.bake: should take single JSON object describing op and args OBJ" , () => {
const result = chef . bake ( "some input" , {
op : chef . toHex ,
args : {
Delimiter : "Colon"
}
2018-07-20 13:59:52 +01:00
});
2018-07-06 12:54:19 +01:00
assert . strictEqual ( result . toString (), "73:6f:6d:65:20:69:6e:70:75:74" );
}),
it ( "chef.bake: should take single JSON object describing op and args ARRAY" , () => {
const result = chef . bake ( "some input" , {
op : chef . toHex ,
args : [ "Colon" ]
2018-07-20 13:59:52 +01:00
});
2018-07-06 12:54:19 +01:00
assert . strictEqual ( result . toString (), "73:6f:6d:65:20:69:6e:70:75:74" );
}),
it ( "chef.bake: should error if op in JSON is not chef op" , () => {
try {
chef . bake ( "some input" , {
op : () => {},
args : [ "Colon" ],
});
} catch ( e ) {
assert . strictEqual ( e . name , "TypeError" );
assert . strictEqual ( e . message , "Inputted function not a Chef operation." );
}
}),
it ( "chef.bake: should take multiple ops in JSON object form, some ops by string" , () => {
const result = chef . bake ( "some input" , [
{
op : chef . toHex ,
args : [ "Colon" ]
},
{
op : "to octal" ,
args : {
delimiter : "Semi-colon" ,
}
}
]);
assert . strictEqual ( result . toString (), "67;63;72;66;146;72;66;144;72;66;65;72;62;60;72;66;71;72;66;145;72;67;60;72;67;65;72;67;64" );
}),
it ( "chef.bake: should handle op with multiple args" , () => {
const result = chef . bake ( "some input" , {
op : "to morse code" ,
args : {
formatOptions : "Dash/Dot" ,
wordDelimiter : "Comma" ,
letterDelimiter : "Backslash" ,
}
});
assert . strictEqual ( result . toString (), "DotDotDot\\DashDashDash\\DashDash\\Dot,DotDot\\DashDot\\DotDashDashDot\\DotDotDash\\Dash" );
}),
it ( "chef.bake: should take compact JSON format from Chef Website as recipe" , () => {
2018-07-20 13:59:52 +01:00
const result = chef . bake ( "some input" , [{ "op" : "To Morse Code" , "args" : [ "Dash/Dot" , "Backslash" , "Comma" ]}, { "op" : "Hex to PEM" , "args" : [ "SOMETHING" ]}, { "op" : "To Snake case" , "args" : [ false ]}]);
2018-07-06 12:54:19 +01:00
assert . strictEqual ( result . toString (), "begin_something_anananaaaaak_da_aaak_da_aaaaananaaaaaaan_da_aaaaaaanan_da_aaak_end_something" );
}),
it ( "chef.bake: should accept Clean JSON format from Chef website as recipe" , () => {
const result = chef . bake ( "some input" , [
{ "op" : "To Morse Code" ,
"args" : [ "Dash/Dot" , "Backslash" , "Comma" ] },
{ "op" : "Hex to PEM" ,
"args" : [ "SOMETHING" ] },
{ "op" : "To Snake case" ,
"args" : [ false ] }
]);
assert . strictEqual ( result . toString (), "begin_something_anananaaaaak_da_aaak_da_aaaaananaaaaaaan_da_aaaaaaanan_da_aaak_end_something" );
}),
2018-08-23 21:40:45 +01:00
it ( "Composable Dish: Should have top level Dish object" , () => {
assert . ok ( Dish );
}),
it ( "Composable Dish: Should construct empty dish object" , () => {
const dish = new Dish ();
2019-04-08 18:06:01 +01:00
assert . strictEqual ( dish . value . byteLength , new ArrayBuffer ( 0 ). byteLength );
assert . strictEqual ( dish . type , 4 );
2018-08-23 21:40:45 +01:00
}),
2018-10-12 11:30:41 +01:00
it ( "Composable Dish: constructed dish should have apply prototype functions" , () => {
2018-08-23 21:40:45 +01:00
const dish = new Dish ();
2018-10-12 11:30:41 +01:00
assert . ok ( dish . apply );
2018-08-23 21:40:45 +01:00
assert . throws (() => dish . someInvalidFunction ());
}),
it ( "Composable Dish: composed function returns another dish" , () => {
2018-10-12 11:30:41 +01:00
const result = new Dish ( "some input" ). apply ( toBase32 );
2019-02-15 16:11:13 +00:00
assert . ok ( result instanceof NodeDish );
2018-08-23 21:40:45 +01:00
}),
2018-10-12 11:30:41 +01:00
2018-08-23 21:40:45 +01:00
it ( "Composable dish: infers type from input if needed" , () => {
const dish = new Dish ( "string input" );
assert . strictEqual ( dish . type , 1 );
const numberDish = new Dish ( 333 );
assert . strictEqual ( numberDish . type , 2 );
const arrayBufferDish = new Dish ( Buffer . from ( "some buffer input" ). buffer );
assert . strictEqual ( arrayBufferDish . type , 4 );
2019-03-22 09:39:43 +00:00
const byteArrayDish = new Dish ( Buffer . from ( "some buffer input" ));
assert . strictEqual ( byteArrayDish . type , 0 );
2018-08-23 21:40:45 +01:00
const JSONDish = new Dish ({ key : "value" });
assert . strictEqual ( JSONDish . type , 6 );
}),
2018-10-05 16:32:12 +01:00
it ( "Composable dish: Buffer type dishes should be converted to strings" , () => {
fs . writeFileSync ( "test.txt" , "abc" );
const dish = new Dish ( fs . readFileSync ( "test.txt" ));
2019-03-22 09:39:43 +00:00
assert . strictEqual ( dish . type , 0 );
2018-10-05 16:32:12 +01:00
fs . unlinkSync ( "test.txt" );
}),
2018-10-12 11:30:41 +01:00
it ( "Composable Dish: apply should allow set of arguments for operation" , () => {
const result = new Dish ( "input" ). apply ( SHA3 , { size : "256" });
assert . strictEqual ( result . toString (), "7640cc9b7e3662b2250a43d1757e318bb29fb4860276ac4373b67b1650d6d3e3" );
}),
it ( "Composable Dish: apply functions can be chained" , () => {
const result = new Dish ( "input" ). apply ( toBase32 ). apply ( SHA3 , { size : "224" });
assert . strictEqual ( result . toString (), "493e8136b759370a415ef2cf2f7a69690441ff86592aba082bc2e2e0" );
}),
2018-08-31 14:43:14 +01:00
it ( "Excluded operations: throw a sensible error when you try and call one" , () => {
try {
chef . fork ();
} catch ( e ) {
assert . strictEqual ( e . type , "ExcludedOperationError" );
assert . strictEqual ( e . message , "Sorry, the Fork operation is not available in the Node.js version of CyberChef." );
}
}),
it ( "Excluded operations: throw a sensible error when you try and call one" , () => {
try {
chef . renderImage ();
} catch ( e ) {
assert . strictEqual ( e . type , "ExcludedOperationError" );
assert . strictEqual ( e . message , "Sorry, the RenderImage operation is not available in the Node.js version of CyberChef." );
}
2018-12-21 09:46:30 +00:00
}),
it ( "Operation arguments: should be accessible from operation object if op has array arg" , () => {
2019-04-04 15:21:52 +01:00
assert . ok ( chef . toCharcode . args );
assert . deepEqual ( chef . unzip . args , {
2019-03-20 16:27:35 +00:00
password : {
type : "binaryString" ,
value : "" ,
},
verifyResult : {
type : "boolean" ,
value : false ,
}
});
2018-12-21 09:46:30 +00:00
}),
2019-03-20 16:27:35 +00:00
it ( "Operation arguments: should have key for each argument in operation" , () => {
2019-04-04 15:21:52 +01:00
assert . ok ( chef . convertDistance . args . inputUnits );
assert . ok ( chef . convertDistance . args . outputUnits );
2018-12-21 09:46:30 +00:00
2019-04-04 15:21:52 +01:00
assert . strictEqual ( chef . bitShiftRight . args . amount . type , "number" );
assert . strictEqual ( chef . bitShiftRight . args . amount . value , 1 );
assert . strictEqual ( chef . bitShiftRight . args . type . type , "option" );
assert . ok ( Array . isArray ( chef . bitShiftRight . args . type . options ));
2019-03-20 16:27:35 +00:00
2018-12-21 09:46:30 +00:00
}),
it ( "Operation arguments: should list all options excluding subheadings" , () => {
// First element (subheading) removed
2019-04-04 15:21:52 +01:00
assert . equal ( chef . convertDistance . args . inputUnits . options [ 0 ], "Nanometres (nm)" );
assert . equal ( chef . defangURL . args . process . options [ 1 ], "Only full URLs" );
2018-08-31 14:43:14 +01:00
})
2018-05-03 10:20:13 +01:00
]);