# Description: # Allows hubot to execute PenText framework commands # # Dependencies: # The PenText framework # # Configuration: # See the various handlers in bash/ # # Usage: # build # Detailed line 1 # Detailed line 2 # Detailed line 3 # # respond # Detailed line 11 # Detailed line 22 # Detailed line 33 # # # Commands: # hubot build - Builds a .pdf file from in # hubot convert - Builds a .xml file from in # hubot invoice - Builds pdf invoice from quote # hubot quickscope [branch=MASTER] - Converts quickscope into quotation # hubot startpentest - Bootstraps a pentest # hubot startquote - Bootstraps a quotation # hubot validate - Validates a report/quotation # hubot usage [command] - Displays usage information for command. If no command is specified, supported commands are displayed. # # Author: # Peter Mosmans # John Sinteur # Daniel Attevelt # # This is part of the PenText framework # USAGE_LABEL = '# Usage:' Fs = require 'fs' Path = require 'path' admins = ['peter'] ### This will initialise the usage information for the chatops command used in the pentext framework It will parse the file comment sections in search of usage information. See above for an example ! This section should always be above the commands section and should always be terminated by two consecutive #s ! ! Only single word commands are supported ! robot gets a new property .usages which can be used later on ### init_usage = (robot) -> robot.usages = [] load_scripts(robot) ### This will load all the scripts in the same folder as this one. It will then proceed to parsing those scripts in order to extract the rosbot command usage information ### load_scripts = (robot) -> if Fs.existsSync(__dirname) for file in Fs.readdirSync(__dirname).sort() fullPath = Path.join __dirname, file robot.logger.info "File: " + fullPath body = Fs.readFileSync fullPath, 'utf-8' parse_scripts(robot, body) ### This function will parse the script for usage information. It will do so by looping through every comment line until it hits the # Usage: label Then it will start to look for a command tag that is identified by: # (3 spaces) . The will let the function know it's dealing with a command identifiers. # (5 spaces) lets the function know it's dealing with info line that should go together with the previously encountered command. The sequence is broken by two sequential empty comment lines. e.g.: # # ### parse_scripts = (robot, body) -> parsing_usage = false current_command = null # https://regex101.com cmd_regex = /^#[ ]{3}[\S]+/i info_regex = /^#[ ]{5}[\S]+/i # Loop through each comment line until there is none. for line in body.split "\n" break unless line[0] is '#' or line.substr(0, 2) is '//' # Determine if we've reached the Usage: tag if line.indexOf(USAGE_LABEL) != -1 parsing_usage = true if parsing_usage # Pre-cleaning ... cleanedLine = line.replace(/^(#|\/\/)\s?/, "").trim() # Are we dealing with a command? if line.match cmd_regex current_command = cleanedLine robot.usages[current_command] = [] # Are we dealing with an info line ? if line.match info_regex if not robot.usages[current_command] robot.logger.debug "Error parsing chatpos usage info: found info line, but no command line" else robot.usages[current_command].push cleanedLine # Detects two sequential #'s and quits parsing usage when it does if cleanedLine.length == 0 if current_command == null parsing_usage = false else current_command = null # Sanity check robot.logger.info "--- Following usage information parsed ----" for command, lines of robot.usages for index, line of lines robot.logger.info "* #{command} : #{line}" module.exports = (robot) -> run_cmd = (cmd, args, cb ) -> spawn = require("child_process").spawn child = spawn(cmd, args) child.stdout.on "data", (buffer) -> cb buffer.toString() child.stderr.on "data", (buffer) -> cb buffer.toString() init_usage(robot) # *************************************************************************** # ChatOps command handlers # *************************************************************************** robot.respond /build (.*)/i, id:'chatops.build', (msg) -> msg.match[0] = msg.match[0].replace(/^[a-z0-9]+$/i); msg.match.shift(); args = msg.match[0].split(" "); cmd = "bash/handler_build"; run_cmd cmd, args, (text) -> msg.send text.replace("\n",""); robot.respond /convert (.*)/i, id:'chatops.convert', (msg) -> msg.match[0] = msg.match[0].replace(/^[a-z0-9]+$/i); msg.match.shift(); args = msg.match[0].split(" "); cmd = "bash/handler_convert"; run_cmd cmd, args, (text) -> msg.send text.replace("\n",""); robot.respond /invoice (.*)/i, id:'chatops.invoice', (msg) -> msg.match[0] = msg.match[0].replace(/^[a-z0-9]+$/i); msg.match.shift(); args = msg.match[0].split(" "); cmd = "bash/handler_invoice"; run_cmd cmd, args, (text) -> msg.send text.replace("\n",""); robot.respond /quickscope (.*)/i, id:'chatops.quickscope', (msg) -> msg.match[0] = msg.match[0].replace(/^[a-z0-9]+$/i); msg.match.shift(); args = msg.match[0].split(" "); cmd = "bash/handler_quickscope"; run_cmd cmd, args, (text) -> msg.send text.replace("\n",""); robot.respond /startpentest (.*)/i, id:'chatops.startpentest', (msg) -> msg.match[0] = msg.match[0].replace(/^[a-z0-9]+$/i); msg.match.shift(); args = msg.match[0].split(" "); if args[0].substring(0, 4) == "off-" msg.send "[-] Please do not start pen names with off-"; return; if args[0].substring(0, 4) == "pen-" msg.send "[-] Please do not start pen names with pen-"; return; roomName = "pen-" + args[0]; newroom = robot.adapter.callMethod('createPrivateGroup', roomName, admins) msg.send "[+] new channel created - Added " + admins + " to the new room " + roomName newroom.then (roomId) => robot.messageRoom roomId.rid, "@all hello!" args[1] = roomId.rid cmd = "bash/handler_pentest"; run_cmd cmd, args, (text) -> msg.send text.replace("\n",""); robot.respond /startquote (.*)/i, id:'chatops.startquote',(msg) -> msg.match[0] = msg.match[0].replace(/^[a-z0-9]+$/i); msg.match.shift(); args = msg.match[0].split(" "); if args[0].substring(0, 4) == "pen-" msg.send "[-] Please do not start quote names with pen-"; return; if args[0].substring(0, 4) == "off-" msg.send "[-] Please do not start quote names with off-"; return; roomName = "off-" + args[0] newroom = robot.adapter.callMethod('createPrivateGroup', roomName, admins) msg.send "[+] new channel created - Added " + admins + " to the new room " + roomName newroom.then (roomId) => robot.messageRoom roomId.rid, "@all hello!" cmd = "bash/handler_quote"; run_cmd cmd, args, (text) -> msg.send text.replace("\n",""); robot.respond /validate (.*)/i, id:'chatops.validate', (msg) -> msg.match[0] = msg.match[0].replace(/^[a-z0-9]+$/i); msg.match.shift(); args = msg.match[0].split(" "); cmd = "bash/handler_validate"; run_cmd cmd, args, (text) -> msg.send text.replace("\n",""); # Handler for the usage command. # Note that the regex option group (.*) also captures the space after the command # This allows for the case when there is no command specified, supported commands can be displayed robot.respond /usage(.*)/i, id:'chatops.usage', (msg) -> msg.match[0] = msg.match[0].replace(/^[a-z0-9]+$/i); msg.match.shift(); args = msg.match[0].trim().split(" "); command = args[0] # If not command is provided, return information on which commands can be handled if command.trim().length == 0 msg.send "I can provide usage information for the following commands:" for command of robot.usages msg.send command msg.send "Issue the command: usage for usage information for this command" return # This deals when unsupported commands if not robot.usages[command] msg.send "I have no usage information for: " + command return # All odd case have been dealt with, let's get down to business for index, line of robot.usages[command] msg.send line