243 lines
8.5 KiB
CoffeeScript
243 lines
8.5 KiB
CoffeeScript
# 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 <repo> <target> - Builds a .pdf file from <target> in <repo>
|
|
# hubot convert <repo> <target> - Builds a .xml file from <target> in <repo>
|
|
# hubot invoice <repo> <target> - Builds pdf invoice from quote
|
|
# hubot quickscope <repo> <namespace> [branch=MASTER] - Converts quickscope into quotation
|
|
# hubot startpentest <name> - Bootstraps a pentest
|
|
# hubot startquote <name> - Bootstraps a quotation
|
|
# hubot validate <parms..> - 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) <command>. The will let the function know it's dealing with a command identifiers.
|
|
# (5 spaces) <infoline> 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 <command> 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 |