From 5e4b955f4466bd9889ac7440cc16ff18763711d5 Mon Sep 17 00:00:00 2001 From: shivanraptor Date: Wed, 7 Apr 2021 13:29:40 +0800 Subject: [PATCH] Changed external JS to internal Fixed also some HTML minor problems --- src/static/optparse.hashtopolis.js | 340 +++++++++++++++++++++ src/static/optparse.js | 357 +++++++++++++++++++++++ src/templates/tasks/detail.template.html | 20 +- src/templates/tasks/new.template.html | 30 +- 4 files changed, 722 insertions(+), 25 deletions(-) create mode 100644 src/static/optparse.hashtopolis.js create mode 100755 src/static/optparse.js diff --git a/src/static/optparse.hashtopolis.js b/src/static/optparse.hashtopolis.js new file mode 100644 index 000000000..b63d56bee --- /dev/null +++ b/src/static/optparse.hashtopolis.js @@ -0,0 +1,340 @@ +/** + * Hashcat Command Validation (based on Hashcat v6.1.1) + * + * This plugin provides validation of Hashcat command + * require OptparseJS Library: https://github.com/shivanraptor/optparse-js + * + * @package optparse_hashtopolis + * @copyright 2020 Raptor Kwok + * @license MIT License + * @website https://github.com/shivanraptor/optparse-js + * @version 0.3 + */ + +// TODO: Change hardcoded values to ENUM + +let switches = [ + ['-m', '--hash-type NUMBER', "Hash-type"], + ['-a', '--attack-mode NUMBER', "Attack-mode"], + ['-V', '--version', "Print version"], + ['-h', '--help', "Print help"], + ['--quiet', "Suppress output"], + ['--hex-charset', "Assume charset is given in hex"], + ['--hex-salt', "Assume salt is given in hex"], + ['--hex-wordlist', "Assume words in wordlist are given in hex"], + ['--force', "Ignore warnings"], + ['--status', "Enable automatic update of the status screen"], + ['--status-json', "Enable JSON format for status output"], + ['--status-timer NUMBER', "Sets seconds between status screen updates to X"], + ['--stdin-timeout-abort NUMBER', "Abort if there is no input from stdin for X seconds"], + ['--machine-readable', "Display the status view in a machine-readable format"], + ['--keep-guessing', "Keep guessing the hash after it has been cracked"], + ['--self-test-disable', "Disable self-test functionality on startup"], + ['--loopback', "Add new plains to induct directory"], + ['--markov-hcstat2 FILE', "Specify hcstat2 file to use"], + ['--markov-disable', "Disables markov-chains, emulates classic brute-force"], + ['--markov-classic', "Enables classic markov-chains, no per-position"], + ['-t', '--markov-threshold NUMBER', "Threshold X when to stop accepting new markov-chains"], + ['--runtime NUMBER', "Abort session after X seconds of runtime"], + ['--session MESSAGE', "Define specific session name"], + ['--session', "Restore session from --session"], + ['--restore-disable', "Do not write restore file"], + ['--restore-file-path FILE', "Specific path to restore file"], + ['-o', '--outfile FILE', "Define outfile for recovered hash"], + ['--outfile-format CSV_INT', "Outfile format to use, separated with commas"], + ['--outfile-autohex-disable', "Disable the use of $HEX[] in output plains"], + ['--outfile-check-timer NUMBER', "Sets seconds between outfile checks to X"], + ['--wordlist-autohex-disable', "Disable the conversion of $HEX[] from the wordlist"], + ['-p', '--separator SINGLE_CHAR', "Separator char for hashlists and outfile"], + ['--stdout', "Do not crack a hash, instead print candidates only"], + ['--show', "Compare hashlist with potfile; show cracked hashes"], + ['--left', "Compare hashlist with potfile; show uncracked hashes"], + ['--username', "Enable ignoring of usernames in hashfile"], + ['--remove', "Enable removal of hashes once they are cracked"], + ['--remove-timer NUMBER', "Update input hash file each X seconds"], + ['--potfile-disable', "Do not write potfile"], + ['--potfile-path FILE', "Specific path to potfile"], + ['--encoding-from ENCODING', "Force internal wordlist encoding from X"], + ['--encoding-to ENCODING', "Force internal wordlist encoding to X"], + ['--debug-mode NUMBER', "Defines the debug mode (hybrid only by using rules)"], + ['--debug-file FILE', "Output file for debugging rules"], + ['--induction-dir FILE', "Specify the induction directory to use for loopback"], // TODO: Change to DIRECTORY filter + ['--outfile-check-dir FILE', "Specify the outfile directory to monitor for plains"], // TODO: Change to DIRECTORY filter + ['--logfile-disable', "Disable the logfile"], + ['--hccapx-message-pair NUMBER', "Load only message pairs from hccapx matching X"], + ['--nonce-error-corrections NUMBER', "The BF size range to replace AP's nonce last bytes"], + ['--keyboard-layout-mapping FILE', "Keyboard layout mapping table for special hash-modes"], + ['--truecrypt-keyfiles FILE', "TrueCrypt Keyfiles to use, separated with commas"], + ['--veracrypt-keyfiles FILE', "VeraCrypt Keyfiles to use, separated with commas"], + ['--veracrypt-pim-start NUMBER', "VeraCrypt personal iterations multiplier start"], + ['--veracrypt-pim-stop NUMBER', "VeraCrypt personal iterations multiplier stop"], + ['-b', '--benchmark', "Run benchmark of selected hash-modes"], + ['--benchmark-all', "Run benchmark of all hash-modes (requires -b)"], // TODO: Check existence of -b flag + ['--speed-only', "Return expected speed of the attack, then quit"], + ['--progress-only', "Return ideal progress step size and time to process"], + ['-c', '--segement-size NUMBER', "Sets size in MB to cache from the wordfile to X"], + ['--bitmap-min NUMBER', "Sets minimum bits allowed for bitmaps to X"], + ['--bitmap-max NUMBER', "Sets maximum bits allowed for bitmaps to X"], // TODO: Compare with --bitmap-min + ['--cpu-affinity CSV_INT', "Locks to CPU devices, separated with commas"], + ['--hook-threads NUMBER', "Sets number of threads for a hook (per compute unit)"], + ['--example-hashes', "Show an example hash for each hash-mode"], + ['--backend-ignore-cuda', "Do not try to open CUDA interface on startup"], + ['--backend-ignore-opencl', "Do not try to open OpenCL interface on startup"], + ['-I', '--backend-info', "Show info about detected backend API devices"], + ['-d', '--backend-devices CSV_INT', "Backend devices to use, separated with commas"], + ['-D', '--opencl-device-types CSV_INT', "OpenCL device-types to use, separated with commas"], + ['-O', '--optimized-kernel-enable', "Enable optimized kernels (limits password length)"], + ['-w', '--workload-profile NUMBER', "Enable a specific workload profile, see pool below"], // TODO: Check Workload Profile range + ['-n', '--kernel-accel NUMBER', "Manual workload tuning, set outerloop step size to X"], + ['-u', '--kernel-loops NUMBER', "Manual workload tuning, set innerloop step size to X"], + ['-T', '--kernel-threads NUMBER', "Manual workload tuning, set thread count to X"], + ['--backend-vector-width NUMBER', "Manually override backend vector-width to X"], + ['--spin-damp PERCENT', "Use CPU for device synchronization, in percent"], + ['--hwmon-disable', "Disable temperature and fanspeed reads and triggers"], + ['--hwmon-temp-abort NUMBER', "Abort if temperature reaches X degrees Celsius"], + ['--scrypt-tmto NUMBER', "Manually override TMTO value for scrypt to X"], + ['-s', '--skip NUMBER', "Skip X words from the start"], + ['-l', '--limit', "Limit X words from the start + skipped words"], + ['--keyspace', "Show keyspace base:mod values and quit"], + ['-j', '--rule-left FILE', "Single rule applied to each word from left wordlist"], + ['-k', '--rule-right FILE', "Single rule applied to each word from right wordlist"], + ['-r', '--rules-file FILE', "Multiple rules applied to each word from wordlists"], + ['-g', '--generate-rules NUMBER', "Generate X random rules"], + ['--generate-rules-func-min NUMBER', "Force min X functions per rule"], + ['--generate-rules-func-max NUMBER', "Force max X functions per rule"], // TODO: Compare MIN and MAX values + ['--generate-rules-seed NUMBER', "Force RNG seed set to X"], + ['-1', '--custom-charset1 MESSAGE', "User-defined charset ?1"], // TODO: Check Charset + ['-2', '--custom-charset2 MESSAGE', "User-defined charset ?2"], // TODO: Check Charset + ['-3', '--custom-charset3 MESSAGE', "User-defined charset ?3"], // TODO: Check Charset + ['-4', '--custom-charset4 MESSAGE', "User-defined charset ?4"], // TODO: Check Charset + ['-i', '--increment', "Enable mask increment mode"], + ['--increment-min NUMBER', "Start mask incrementing at X"], + ['--increment-max NUMBER', "Stop mask incrementing at X"], // TODO: Compare MIN and MAX values + ['-S', '--slow-candidates', "Enable slower (but advanced) candidate generators"], + ['--brain-server', "Enable brain server"], + ['--brain-server-timer NUMBER', "Update the brain server dump each X seconds (min:60)"], + ['-z', '--brain-client', "Enable brain client, activates -S"], + ['--brain-client-features NUMBER', "Define brain client features, see below"], // TODO: Check brain client features range + ['--brain-host MESSAGE', "Brain server host (IP or domain)"], // TODO: Check IP or Domain format + ['--brain-port NETWORK_PORT', "Brain server port"], + ['--brain-password MESSAGE', "Brain server authentication password"], + ['--brain-session HEX', "Overrides automatically calculated brain session"], + ['--brain-session-whitelist CSV_HEX', "Allow given sessions only, separated with commas"], +]; +let defaultOptions = { + debug: false, + ruleFiles: [], + attackType: -1, + hashMode: -1, + posArgs: [], // Positional Arguments + customCharset1: '', + customCharset2: '', + customCharset3: '', + customCharset4: '', + unrecognizedFlag: [] +}; +var options = defaultOptions; + +var parser = new optparse.OptionParser(switches); + +// ======================================================================================= +// Custom Hashtopolis-specific Filters +// ======================================================================================= +parser.filter('encoding', function(value) { + // TODO: Complete Encoding List: http://www.iana.org/assignments/character-sets + var encodings = ['us-ascii', 'iso-8859-1', 'iso-8859-2', 'iso-8859-3', 'iso-8859-4', 'iso-8859-5', 'iso-8859-6', 'iso-8859-7', 'iso-8859-8', 'iso-8859-9', 'iso-8859-10', 'iso_6937-2-add', 'jis_x0201', 'jis_encoding', 'shift_jis', 'bs_4730', 'sen_850200_c', 'it', 'es', 'din_66003', 'ns_4551-1', 'nf_z_62-010', 'iso-10646-utf-1', 'invariant', 'nats-sefi', 'nats-sefi-add', 'nats-dano', 'nats-dano-add', 'sen_850200_b', 'ks_c_5601-1987', 'euc-jp', 'iso-2022-kr', 'euc-kr', 'iso-2022-jp', 'iso-2022-jp-2', 'jis_c6220-1969-jp', 'jis_c6220-1969-ro', 'pt', 'greek7-old', 'latin-greek', 'latin-greek-1', 'iso-5427', 'jis_c6226-1978', 'inis', 'inis-8', 'inis-cyrillic', 'gb_1988-80', 'gb_2312-80', 'ns_4551-2', 'pt2', 'es2', 'jis_c6226-1983', 'greek7', 'asmo_449', 'iso-ir-90', 'jis_c6229-1984-a', 'jis_c6229-1984-b', 'jis_c6229-1984-b-add', 'iso_c6229-1984-hand', 'iso_c6229-1984-hand-add', 'jis_c6229-1984-kana', 'iso_2033-1983', 'ansx_x3.110-1983', 't.61-7bit', 't.61-8bit', 'ecma-cyrillic', 'csa_z243.4-1985-1', 'csa_z243.4-1985-2', 'csa_z243.4-1985-gr', 'iso-8859-6-e', 'iso-8859-6-i', 't.101-g2', 'iso-8859-8-e', 'iso-8859-8-i', 'csn_369103', 'jus_i.b1.002', 'iec_p27-1', 'greek-ccitt', 'iso_6937-2-25', 'gost_19768-74', 'iso_8859-supp', 'iso_10367-box', 'latin-lap', 'jis_x0212-1990', 'ds_2089', 'us-dk', 'dk-us', 'ksc5636', 'unicode-1-1-utf-7', 'iso-2022-cn', 'iso-2022-cn-ext', 'iso-8859-13', 'iso-8859-14', 'iso-8859-15', 'iso-8859-16', 'gbk', 'gb18030', 'gb2312', 'osd_ebcdic_df04_15', 'osd_edcdic_df03_irv', 'osd_ebcdic_df04_1', 'iso-11548-1', 'kz-1048', 'iso-10646-ucs-2', 'iso-10646-ucs-4', 'iso-10646-ucs-basic', 'iso-10646-unicode-latin1', 'iso-10646-j-1', 'iso-unicode-ibm-1261', 'iso-unicode-ibm-1268', 'iso-unicode-ibm-1276', 'iso-unicode-ibm-1264', 'iso-unicode-ibm-1265', 'unicode1-1', 'scsu', 'utf-7', 'big5', 'koi8-r', 'utf-8', 'utf-16', 'utf-16be', 'utf-16le', 'utf-32', 'utf-32be', 'utf-32le', 'cesu-8', 'bocu-1', 'hp-roman8', 'adobe-standard-encoding', 'ventura-us', 'ibm-symbols', 'adobe-symbol-encoding', 'macintosh', 'hz-gb-2312', 'windows-1258', 'tis-620', 'cp50220']; + if(encodings.indexOf(value) == -1) { + throw "Invalid encoding standards"; + } + return value; +}); + +parser.filter('network_port', function(value) { + if(parseInt(value) <= 0 || parseInt(value) > 65536) { + throw "Network port out of range (1 - 65536)"; + } + return parseInt(value); +}); + +// ======================================================================================= +// Option Parser (to be completed) +// ======================================================================================= +parser.on('hash-type', function(name, value) { + console.log('Hash Type: ' + value); + options.hashMode = parseInt(value); // TODO: Check Hash Mode +}); +parser.on('attack-mode', function(name, value) { + console.log('Attack Mode: ' + value); + if(parseInt(value) >= 0 && parseInt(value) <= 3) { + options.attackType = parseInt(value); + } else { + throw "Invalid Attack Type"; + } +}); +parser.on('rules-file', function(name, value) { + options.ruleFiles.push(value); +}); +parser.on('status-timer', function(name, value) { + console.log('Status Timer: ' + value); +}); +parser.on('separator', function(name, value) { + console.log('Separator: ' + value); +}); +parser.on('encoding-from', function(name, value) { + console.log('Encoding From: ' + value); +}); +parser.on('cpu-affinity', function(name, value) { + console.log('CPU Affinity: ' + value); +}); +parser.on('brain-session', function(name, value) { + console.log('Brain Session: ' + value); +}); +parser.on('brain-session-whitelist', function(name, value) { + console.log('Brain Session-whitelist: ' + value); +}); +parser.on('spin-damp', function(name, value) { + console.log('Spin Damp Percent: ' + value); +}); +parser.on('custom-charset1', function(name, value) { + console.log('Custom Charset 1: ' + value); + options.customCharset1 = value; +}); +parser.on('custom-charset2', function(name, value) { + console.log('Custom Charset 2: ' + value); + options.customCharset2 = value; +}); +parser.on('custom-charset3', function(name, value) { + console.log('Custom Charset 3: ' + value); + options.customCharset3 = value; +}); +parser.on('custom-charset4', function(name, value) { + console.log('Custom Charset 4: ' + value); + options.customCharset4 = value; +}); +parser.on('print', function(value) { + console.log('PRINT: ' + value); +}); +parser.on('debug', function() { + options.debug = true; +}); +parser.on(0, function(opt) { + console.log('The first non-switch option is: ' + opt); + options.posArgs[0] = opt; +}); +parser.on(1, function(opt) { + console.log('The second non-switch option is: ' + opt); + options.posArgs[1] = opt; +}); +parser.on(2, function(opt) { + console.log('The third non-switch option is: ' + opt); + options.posArgs[2] = opt; +}); +parser.on(3, function(opt) { + console.log('The fourth non-switch option is: ' + opt); + options.posArgs[3] = opt; +}); +parser.on(4, function(opt) { + console.log('The fifth non-switch option is: ' + opt); + options.posArgs[4] = opt; +}); +parser.on('*', function(opt, value) { + console.log('wild handler for ' + opt + ', value=' + value); +}); +parser.on(function(opt) { + console.log('Unrecognized flag: ' + opt); + options.unrecognizedFlag.push(opt); +}); + +// ======================================================================================= +// Functions +// ======================================================================================= +function startParse(cmd, isHashtopolis = true) { + // resetting the options + options = defaultOptions; + options.ruleFiles = []; + options.posArgs = []; + options.unrecognizedFlag = []; + + var result = false; + if(isHashtopolis) { + args = cmd.replace('hashcat', '').trim().split(/ |=/g); + try { + parser.parse(args); + result = validateHashtopolisCommand(options); + } catch (e) { + return {"result": false, "reason": "Value exception: " + e}; + } + } else { + parser.parse(args); + result = validateHashcatCommand(options); + } + return result; +} + +function validateHashtopolisCommand(opt) { + // Pre-case Check + if(opt.posArgs[0] != '#HL#') { + return {"result": false, "reason": "Hashlist is missing"}; + } + return validateHashcatCommand(opt); +} + +function validateHashcatCommand(opt) { + if(opt.unrecognizedFlag.length > 0) { + return {"result": false, "reason": "Unrecognized Flag: " + opt.unrecognizedFlag.join(', ')}; + } else if(opt.attackType == 0) { // 0: Word List Attack + // Required Dictionary + if(opt.posArgs.length == 2) { + console.log('Dictionary: ' + opt.posArgs[1]); + if(opt.ruleFiles.length == 0) { + return {"result": true, "reason": "Word List Attack"}; + } else { + return {"result": true, "reason": "Word List Attack with " + opt.ruleFiles.length + " rule(s)."}; + } + } else { + return {"result": false, "reason": "Missing wordlist"}; + } + } else if(opt.attackType == 1) { // 1: Combinator Attack + // Required Left and Right Wordlist + if(opt.posArgs.length == 3) { + console.log('Left Wordlist: ' + opt.posArgs[1] + ', Right Wordlist: ' + opt.posArgs[2]); + if(opt.ruleFiles.length == 0) { + return {"result": true, "reason": "Combinator Attack"}; + } else { + return {"result": false, "reason": "Combinator Attack cannot use with rules"}; + } + } else { + return {"result": false, "reason": "Required TWO Wordlist"}; + } + } else if(opt.attackType == 3) { // 3: Bruteforce Attack + if(opt.posArgs.length > 1) { + if(opt.customCharset1 != '') { + if(opt.posArgs[1].indexOf('?1') !== -1) { + return {"result": true, "reason": "Bruteforce Attack with Character Set 1"}; + } + } + if(opt.customCharset2 != '') { + if(opt.posArgs[1].indexOf('?2') !== -1) { + return {"result": true, "reason": "Bruteforce Attack with Character Set 2"}; + } + } + if(opt.customCharset3 != '') { + if(opt.posArgs[1].indexOf('?3') !== -1) { + return {"result": true, "reason": "Bruteforce Attack with Character Set 3"}; + } + } + if(opt.customCharset4 != '') { + if(opt.posArgs[1].indexOf('?4') !== -1) { + return {"result": true, "reason": "Bruteforce Attack with Character Set 4"}; + } + } + + // No custom character set + return {"result": true, "reason": "Bruteforce Attack with pattern: " + opt.posArgs[1]}; + } else { + return {"result": false, "reason": "Bruteforce Attack but missing pattern"}; + } + } else { + return {"result": false, "reason": "Missing / Unsupported Attack Type (Supported Types: 0, 1, 3)"}; + } +} \ No newline at end of file diff --git a/src/static/optparse.js b/src/static/optparse.js new file mode 100755 index 000000000..b277e1218 --- /dev/null +++ b/src/static/optparse.js @@ -0,0 +1,357 @@ +// Optparse.js 1.1.1 - Option Parser for Javascript +// +// Copyright (c) 2009 Johan Dahlberg +// Copyright (c) 2020 Raptor K +// +// See README.md for license. +// +var optparse = {}; +(function(self) { +var VERSION = '1.1.1'; +var LONG_SWITCH_RE = /^--\w/; +var SHORT_SWITCH_RE = /^-\w/; +var NUMBER_RE = /^(0x[A-Fa-f0-9]+)|([0-9]+\.[0-9]+)|(\d+)$/; +var DATE_RE = /^\d{4}-(0[0-9]|1[0,1,2])-([0,1,2][0-9]|3[0,1])$/; +var EMAIL_RE = /^([0-9a-zA-Z]+([_.-]?[0-9a-zA-Z]+)*@[0-9a-zA-Z]+[0-9,a-z,A-Z,.,-]*(.){1}[a-zA-Z]{2,4})+$/; +var EXT_RULE_RE = /(--[\w_-]+)\s+([\w[\]_-]+)|(--[\w_-]+)/; +var ARG_OPTIONAL_RE = /\[(.+)\]/; + +// The default switch argument filter to use, when argument name doesnt match +// any other names. +var DEFAULT_FILTER = '_DEFAULT'; +var PREDEFINED_FILTERS = {}; + +// The default switch argument filter. Parses the argument as text. +function filter_text(value) { + return value; +} + +// Switch argument filter that expects an integer, HEX or a decimal value. An +// exception is throwed if the criteria is not matched. +// Valid input formats are: 0xFFFFFFF, 12345 and 1234.1234 +function filter_number(value) { + var m = NUMBER_RE.exec(value); + if(m == null) throw OptError('Expected a number representative'); + if(m[1]) { + // The number is in HEX format. Convert into a number, then return it + return parseInt(m[1], 16); + } else { + // The number is in regular- or decimal form. Just run in through + // the float caster. + return parseFloat(m[2] || m[3]); + } +} + +// Switch argument filter that expects a Date expression. The date string MUST be +// formated as: "yyyy-mm-dd" An exception is throwed if the criteria is not +// matched. An DATE object is returned on success. +function filter_date(value) { + var m = DATE_RE.exec(value); + if(m == null) throw OptError('Expected a date representation in the "yyyy-mm-dd" format.'); + return new Date(parseInt(m[0]), parseInt(m[1]) - 1, parseInt(m[2])); +} + +// Switch argument filter that expects an email address. An exception is throwed +// if the criteria doesn't match. +function filter_email(value) { + var m = EMAIL_RE.exec(value); + if(m == null) throw OptError('Excpeted an email address.'); + return m[1]; +} + +// Switch argument filter that expects an comma-separated integer values. +// An exception is thrown if the criteria is not matched. +// Introduced in v1.0.9 +function filter_csv_int(value) { + var tokens = value.split(','); + for(var i = 0; i < tokens.length; i++) { + if(isNaN(parseInt(tokens[i]))) { + throw OptError('Invalid integer comma-separated value: ' + tokens[i]); + } + } + return value; +} + +// Switch argument filter that expects an comma-separated hex values. +// An exception is thrown if the criteria is not matched. +// Introduced in v1.0.9 +function filter_csv_hex(value) { + var tokens = value.split(','); + for(var i = 0; i < tokens.length; i++) { + if(isNaN(parseInt(tokens[i], 16))) { + throw OptError('Invalid hex comma-separated value: ' + tokens[i]); + } + } + return value; +} + +// Switch argument filter that expects a single character. +// An exception is thrown if the criteria is not matched. +// Introduced in v1.0.9 +function filter_singlechar(value) { + if(value.length != 1) { + throw OptError('Expected a single character'); + } + return value; +} + +// Switch argument filter that expects an integer value ranged from 0 - 100. +// An exception is thrown if the criteria is not matched. +// Introduced in v1.0.9 +function filter_percent(value) { + if(isNaN(parseInt(value))) { + throw OptError('Invalid percent value: ' + value); + } else if(parseInt(value) < 0 || parseInt(value) > 100) { + throw OptError('Percent out of range: ' + value); + } + return parseInt(value); +} + + +// Register all predefined filters. This dict is used by each OptionParser +// instance, when parsing arguments. Custom filters can be added to the parser +// instance by calling the "add_filter" -method. +PREDEFINED_FILTERS[DEFAULT_FILTER] = filter_text; +PREDEFINED_FILTERS['TEXT'] = filter_text; +PREDEFINED_FILTERS['NUMBER'] = filter_number; +PREDEFINED_FILTERS['DATE'] = filter_date; +PREDEFINED_FILTERS['EMAIL'] = filter_email; +PREDEFINED_FILTERS['SINGLE_CHAR'] = filter_singlechar; +PREDEFINED_FILTERS['CSV_INT'] = filter_csv_int; +PREDEFINED_FILTERS['CSV_HEX'] = filter_csv_hex; +PREDEFINED_FILTERS['PERCENT'] = filter_percent; + +// Buildes rules from a switches collection. The switches collection is defined +// when constructing a new OptionParser object. +function build_rules(filters, arr) { + var rules = []; + for(var i=0; i> value means that the switch does +// not take anargument. +function build_rule(filters, short, expr, desc) { + var optional, filter; + var m = expr.match(EXT_RULE_RE); + if(m == null) throw OptError('The switch is not well-formed.'); + var long = m[1] || m[3]; + if(m[2] != undefined) { + // A switch argument is expected. Check if the argument is optional, + // then find a filter that suites. + var optional = ARG_OPTIONAL_RE.test(m[2]); + var optional_match = m[2].match(ARG_OPTIONAL_RE); + var filter_name = optional ? optional_match[1] : m[2]; + filter = filters[filter_name]; + if(filter === undefined) filter = filters[DEFAULT_FILTER]; + } + return { + name: long.substr(2), + short: short, + long: long, + decl: expr, + desc: desc, + optional_arg: optional, + filter: filter + } +} + +// Loop's trough all elements of an array and check if there is valid +// options expression within. An valid option is a token that starts +// double dashes. E.G. --my_option +function contains_expr(arr) { + if(!arr || !arr.length) return false; + var l = arr.length; + while(l-- > 0) if(LONG_SWITCH_RE.test(arr[l])) return true; + return false; +} + +// Extends destination object with members of source object +function extend(dest, src) { + var result = dest; + for(var n in src) { + result[n] = src[n]; + } + return result; +} + +// Appends spaces to match specified number of chars +function spaces(arg1, arg2) { + var l, builder = []; + if(arg1.constructor === Number) { + l = arg1; + } else { + if(arg1.length == arg2) return arg1; + l = arg2 - arg1.length; + builder.push(arg1); + } + while(l-- > 0) builder.push(' '); + return builder.join(''); +} + +// Creates an error object with specified error message. +function OptError(msg) { + return new function() { + this.msg = msg; + this.toString = function() { + return this.msg; + } + } +} + +function OptionParser(rules) { + this.banner = 'Usage: [Options]'; + this.options_title = 'Available options:' + this._rules = rules; + this._halt = false; + this.filters = extend({}, PREDEFINED_FILTERS); + this.on_args = {}; + this.on_switches = {}; + this.on_halt = function() {}; + this.default_handler = function() {}; +} + +OptionParser.prototype = { + + // Adds args and switchs handler. + on: function(value, fn) { + if(value.constructor === Function ) { + this.default_handler = value; + } else if(value.constructor === Number) { + this.on_args[value] = fn; + } else { + this.on_switches[value] = fn; + } + }, + + // Adds a custom filter to the parser. It's possible to override the + // default filter by passing the value "_DEFAULT" to the ´´name´´ + // argument. The name of the filter is automatically transformed into + // upper case. + filter: function(name, fn) { + this.filters[name.toUpperCase()] = fn; + }, + + // Parses specified args. Returns remaining arguments. + parse: function(args) { + var result = [], callback; + var rules = build_rules(this.filters, this._rules); + var tokens = args.concat([]); + var token; + while(this._halt == false && (token = tokens.shift())) { + if(LONG_SWITCH_RE.test(token) || SHORT_SWITCH_RE.test(token)) { + var arg = undefined; + // The token is a long or a short switch. Get the corresponding + // rule, filter and handle it. Pass the switch to the default + // handler if no rule matched. + for(var i = 0; i < rules.length; i++) { + var rule = rules[i]; + if(rule.long == token || rule.short == token) { + if(rule.filter !== undefined) { + arg = tokens.shift(); + if(arg && (!LONG_SWITCH_RE.test(arg) && !SHORT_SWITCH_RE.test(arg))) { + try { + arg = rule.filter(arg, tokens); + } catch(e) { + throw OptError(token + ': ' + e.toString()); + } + } else if(rule.optional_arg) { + if(arg) { + tokens.unshift(arg); + } + } else { + throw OptError('Expected switch argument.'); + } + } + callback = this.on_switches[rule.name]; + if (!callback) callback = this.on_switches['*']; + if(callback) callback.apply(this, [rule.name, arg]); + break; + } + } + if(i == rules.length) this.default_handler.apply(this, [token]); + } else { + // Did not match long or short switch. Parse the token as a + // normal argument. + callback = this.on_args[result.length]; + result.push(token); + if(callback) callback.apply(this, [token]); + } + } + return this._halt ? this.on_halt.apply(this, [tokens]) : result; + }, + + // Returns an Array with all defined option rules + options: function() { + return build_rules(this.filters, this._rules); + }, + + // Add an on_halt callback if argument ´´fn´´ is specified. on_switch handlers can + // call instance.halt to abort the argument parsing. This can be useful when + // displaying help or version information. + halt: function(fn) { + this._halt = fn === undefined + if(fn) this.on_halt = fn; + }, + + // Returns a string representation of this OptionParser instance. + toString: function() { + var builder = [this.banner, '', this.options_title], + shorts = false, longest = 0, rule; + var rules = build_rules(this.filters, this._rules); + for(var i = 0; i < rules.length; i++) { + rule = rules[i]; + // Quick-analyze the options. + if(rule.short) shorts = true; + if(rule.decl.length > longest) longest = rule.decl.length; + } + for(var i = 0; i < rules.length; i++) { + var text = spaces(6); + rule = rules[i]; + if(shorts) { + if(rule.short) text = spaces(2) + rule.short + ', '; + } + text += spaces(rule.decl, longest) + spaces(3); + text += rule.desc; + builder.push(text); + } + return builder.join('\n'); + } +} + +self.VERSION = VERSION; +self.OptionParser = OptionParser; + +})(optparse); diff --git a/src/templates/tasks/detail.template.html b/src/templates/tasks/detail.template.html index de12a7f5c..f85c63e92 100755 --- a/src/templates/tasks/detail.template.html +++ b/src/templates/tasks/detail.template.html @@ -16,7 +16,7 @@

Task details{{IF [[task.getUsePreprocessor()]]}} (Preprocessor task){{ENDIF} [[task.getId()]] {{IF [[accessControl.hasPermission([[$DAccessControl::CREATE_TASK_ACCESS]])]]}} -
+ @@ -29,7 +29,7 @@

Task details{{IF [[task.getUsePreprocessor()]]}} (Preprocessor task){{ENDIF} Name: {{IF [[accessControl.hasPermission([[$DAccessControl::MANAGE_TASK_ACCESS]])]]}} - +    @@ -45,7 +45,7 @@

Task details{{IF [[task.getUsePreprocessor()]]}} (Preprocessor task){{ENDIF} Attack command: {{IF [[accessControl.hasPermission([[$DAccessControl::CREATE_TASK_ACCESS]])]]}} - + @@ -71,7 +71,7 @@

Task details{{IF [[task.getUsePreprocessor()]]}} (Preprocessor task){{ENDIF} {{IF [[accessControl.hasPermission([[$DAccessControl::MANAGE_TASK_ACCESS]])]]}} - + @@ -85,7 +85,7 @@

Task details{{IF [[task.getUsePreprocessor()]]}} (Preprocessor task){{ENDIF} Chunk size: {{IF [[accessControl.hasPermission([[$DAccessControl::MANAGE_TASK_ACCESS]])]]}} - + @@ -108,7 +108,7 @@

Task details{{IF [[task.getUsePreprocessor()]]}} (Preprocessor task){{ENDIF} Color: {{IF [[accessControl.hasPermission([[$DAccessControl::MANAGE_TASK_ACCESS]])]]}} - + @@ -134,7 +134,7 @@

Task details{{IF [[task.getUsePreprocessor()]]}} (Preprocessor task){{ENDIF} Status timer: {{IF [[accessControl.hasPermission([[$DAccessControl::MANAGE_TASK_ACCESS]])]]}} - + @@ -150,7 +150,7 @@

Task details{{IF [[task.getUsePreprocessor()]]}} (Preprocessor task){{ENDIF} Priority: {{IF [[accessControl.hasPermission([[$DAccessControl::MANAGE_TASK_ACCESS]])]]}} - + @@ -746,7 +746,7 @@

- + {{ENDIF}} {{IF [[activeChunks.getVal([[chunk.getId()]])]] && [[accessControl.hasPermission([[$DAccessControl::MANAGE_TASK_ACCESS]])]]}} @@ -754,7 +754,7 @@

- + {{ENDIF}} diff --git a/src/templates/tasks/new.template.html b/src/templates/tasks/new.template.html index 6cf449e46..a53c741dc 100755 --- a/src/templates/tasks/new.template.html +++ b/src/templates/tasks/new.template.html @@ -2,10 +2,10 @@ {%TEMPLATE->struct/menu%}

New task

{%TEMPLATE->struct/messages%} -
+
- + {{IF [[orig]] > 0 && [[origType]] == 2}} {{ENDIF}} @@ -19,13 +19,13 @@

New task

Name: - + Hashlist: - {{FOREACH list;[[lists]]}} {{ENDFOREACH}} @@ -35,7 +35,7 @@

New task

Command line: - +
Invalid command

Use [[config.getVal('hashlistAlias')]] for hash list and assume all files in current directory.
If you have Linux agents, please mind the filename case sensitivity!

@@ -44,7 +44,7 @@

New task

Priority: - + @@ -56,20 +56,20 @@

New task

Color: - # + # Chunk size: - + seconds Status timer: - + seconds @@ -197,7 +197,7 @@

New task

- + @@ -206,9 +206,9 @@

New task

- - - + + +
@@ -383,8 +383,8 @@

New task

- - + +