From 13d6282f82d17a5334abe7ea3a103c8e0f0069cb Mon Sep 17 00:00:00 2001 From: Peter Mosmans Date: Tue, 1 Nov 2016 13:38:32 +0100 Subject: [PATCH 01/14] Extended list of non-capitalized words --- chatops/python/validate_report.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chatops/python/validate_report.py b/chatops/python/validate_report.py index fe9ae61..eb8c856 100644 --- a/chatops/python/validate_report.py +++ b/chatops/python/validate_report.py @@ -46,7 +46,8 @@ VOCABULARY = 'project-vocabulary.pws' # Snippets may contain XML fragments without the proper entities EXAMPLEDIR = 'examples/' NOT_CAPITALIZED = ['a', 'an', 'and', 'as', 'at', 'but', 'by', 'for', 'in', - 'nor', 'of', 'on', 'or', 'the', 'to', 'up'] + 'jQuery', 'jQuery-UI', 'nor', 'of', 'on', 'or', 'the', 'to', + 'up'] SNIPPETDIR = 'snippets/' TEMPLATEDIR = 'templates/' OFFERTE = '/offerte.xml' From 5caa5aaed8ec1c9c95bf78b5ab97cfde21c6d1f5 Mon Sep 17 00:00:00 2001 From: Peter Mosmans Date: Tue, 1 Nov 2016 13:44:04 +0100 Subject: [PATCH 02/14] Retrieve list of maximum 99 issues Workaround for some pagination issues. --- chatops/python/gitlab-to-pentext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chatops/python/gitlab-to-pentext.py b/chatops/python/gitlab-to-pentext.py index 6200c2a..2e31eca 100644 --- a/chatops/python/gitlab-to-pentext.py +++ b/chatops/python/gitlab-to-pentext.py @@ -146,7 +146,7 @@ def list_issues(gitserver, options): Lists all issues for options['issues'] """ try: - for issue in gitserver.project_issues.list(project_id=options['issues']): + for issue in gitserver.project_issues.list(project_id=options['issues'], per_page=99): if issue.state == 'closed' and not options['closed']: continue if 'finding' in issue.labels: From 0fd5171ebab96762f69cfa78d8595b3050baea4b Mon Sep 17 00:00:00 2001 From: Peter Mosmans Date: Thu, 10 Nov 2016 20:39:18 +1000 Subject: [PATCH 03/14] Added template for future work --- xml/source/futurework.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/xml/source/futurework.xml b/xml/source/futurework.xml index f7ce843..1240426 100644 --- a/xml/source/futurework.xml +++ b/xml/source/futurework.xml @@ -1,4 +1,12 @@
Future Work +

+

    +
  • + Title
    + Description +
  • +
+

From d07b44058eba8b16a3ed7faa9d12a0424b4df18b Mon Sep 17 00:00:00 2001 From: Peter Mosmans Date: Thu, 10 Nov 2016 20:43:05 +1000 Subject: [PATCH 04/14] Added boilerplate fingerprinting template From 34adf23dd1dd94d5e6a12a694bca02c78b7a38e6 Mon Sep 17 00:00:00 2001 From: Peter Mosmans Date: Thu, 10 Nov 2016 20:46:32 +1000 Subject: [PATCH 05/14] Filter another illegal filename character --- chatops/python/gitlab-to-pentext.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/chatops/python/gitlab-to-pentext.py b/chatops/python/gitlab-to-pentext.py index 2e31eca..9fd6e14 100644 --- a/chatops/python/gitlab-to-pentext.py +++ b/chatops/python/gitlab-to-pentext.py @@ -146,7 +146,8 @@ def list_issues(gitserver, options): Lists all issues for options['issues'] """ try: - for issue in gitserver.project_issues.list(project_id=options['issues'], per_page=99): + for issue in gitserver.project_issues.list(project_id=options['issues'], + per_page=99): if issue.state == 'closed' and not options['closed']: continue if 'finding' in issue.labels: @@ -251,7 +252,7 @@ def valid_filename(filename): """ result = '' for char in filename.strip(): - if char in [':', '/', '.', '\\', ' ', '[', ']', '(', ')', '\'']: + if char in ['*', ':', '/', '.', '\\', ' ', '[', ']', '(', ')', '\'']: if len(char) and not result.endswith('-'): result += '-' else: From a75c949bc934c002bd2be7c9cd32b30636bf8237 Mon Sep 17 00:00:00 2001 From: Peter Mosmans Date: Thu, 10 Nov 2016 20:47:43 +1000 Subject: [PATCH 06/14] Remove debug notification --- scripts/gitlab-to-pentext.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/gitlab-to-pentext.py b/scripts/gitlab-to-pentext.py index ce71e3d..a02fd6a 100644 --- a/scripts/gitlab-to-pentext.py +++ b/scripts/gitlab-to-pentext.py @@ -119,12 +119,11 @@ def convert_markdown(text): Replace markdown monospace with monospace tags """ result = text - return result + return result # currently not implemented print('EXAMINING ' + text + ' END') monospace = re.findall("\`\`\`(.*?)\`\`\`", text, re.DOTALL) print(monospace) if len(monospace): - print('YESSS ' + monospace) result = {} result['monospace'] = ''.join(monospace) @@ -158,7 +157,7 @@ def parse_arguments(): parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description=textwrap.dedent('''\ -gitlab-to-pentext - imports and updates gitlab issues into PetText (XML) format +gitlab-to-pentext - imports and updates gitlab issues into PenText (XML) format Copyright (C) 2016 Peter Mosmans [Radically Open Security]] This program is free software: you can redistribute it and/or modify @@ -184,7 +183,7 @@ the Free Software Foundation, either version 3 of the License, or return vars(parser.parse_args()) -def preflight_checks(options): +def preflight_checks(): """ Checks if all tools are there. Exits with 0 if everything went okilydokily. @@ -248,7 +247,7 @@ def main(): The main program. """ options = parse_arguments() - gitserver = preflight_checks(options) + gitserver = preflight_checks() if options['projects']: list_projects(gitserver, options) if options['issues']: From 181d9d57cf1abff020b9bb45f8c1b92ad40f9867 Mon Sep 17 00:00:00 2001 From: Peter Mosmans Date: Mon, 21 Nov 2016 11:28:58 +1000 Subject: [PATCH 07/14] Use logging module --- chatops/python/validate_report.py | 124 ++++++++++++++++++------------ 1 file changed, 76 insertions(+), 48 deletions(-) diff --git a/chatops/python/validate_report.py b/chatops/python/validate_report.py index eb8c856..0525db5 100644 --- a/chatops/python/validate_report.py +++ b/chatops/python/validate_report.py @@ -23,6 +23,7 @@ from __future__ import print_function from __future__ import unicode_literals import argparse +import logging import mmap import os import re @@ -107,24 +108,41 @@ the Free Software Foundation, either version 3 of the License, or return vars(parser.parse_args()) -def validate_spelling(tree, filename, options): +def initialize_speller(): """ - Checks spelling of text within tags. - If options['learn'], then unknown words will be added to the dictionary. + Initialize and return speller module. """ - result = True + speller = None try: speller = aspell.Speller(('lang', 'en'), ('personal-dir', '.'), ('personal', VOCABULARY)) - except: # some versions of aspell use a different path - speller = aspell.Speller(('lang', 'en'), - ('personal-path', './' + VOCABULARY)) - if options['debug']: - [print(i[0] + ' ' + str(i[2]) + '\n') for i in speller.ConfigKeys()] + except aspell.AspellConfigError as exception: # some versions of aspell use a different path + logging.debug('Encountered exception when trying to intialize spelling: %s', + exception) + try: + speller = aspell.Speller(('lang', 'en'), + ('personal-path', './' + VOCABULARY)) + except aspell.AspellSpellerError as exception: + logging.error('Could not initialize speller: %s', exception) + if speller: + [logging.debug(i[0] + ' ' + str(i[2]) + '\n') for i in speller.ConfigKeys()] + return speller + + +def validate_spelling(tree, filename, options): + """ + Check spelling of text within tags. + If options['learn'], then unknown words will be added to the dictionary. + """ + result = True + speller = initialize_speller() + if not speller: + options['spelling'] = False + return result try: root = tree.getroot() - for section in root.iter(): + for section in root.iter(): if section.text and isinstance(section.tag, basestring) and \ section.tag not in ('a', 'code', 'monospace', 'pre'): for word in re.findall('([a-zA-Z]+\'?[a-zA-Z]+)', section.text): @@ -133,12 +151,13 @@ def validate_spelling(tree, filename, options): speller.addtoPersonal(word) else: result = False - print('[-] Misspelled (unknown) word {0} in {1}'. - format(word.encode('utf-8'), filename)) + logging.warning('Misspelled (unknown) word %s in %s', + word.encode('utf-8'), filename) if options['learn']: speller.saveAllwords() except aspell.AspellSpellerError as exception: - print('[-] Spelling disabled ({0})'.format(exception)) + logging.error('Disabled spelling (%s)', exception) + options['spelling'] = False return result @@ -201,16 +220,6 @@ def validate_files(filenames, options): return result -def print_output(options, stdout, stderr=None): - """ - Prints out standard out and standard err using the verboseprint function. - """ - if stdout and options['verbose']: - print('[+] {0}'.format(stdout)) - if stderr and options['verbose']: - print('[-] {0}'.format(stderr)) - - def validate_report(): """ Validates XML report file by trying to build it. @@ -231,7 +240,7 @@ def validate_xml(filename, options): # crude check whether the file is outside the pentext framework if 'notes' in filename: return result, xml_type - print_output(options, 'Validating XML file: {0}'.format(filename)) + logging.info('Validating XML file: %s', filename) try: with open(filename, 'rb') as xml_file: xml.sax.parse(xml_file, xml.sax.ContentHandler()) @@ -332,11 +341,11 @@ def validate_type(tree, filename, options): fix = True for tag in tags: if root.find(tag) is None: - print('[-] Missing tag in {0}: {1}'.format(filename, tag)) + logging.warning('Missing tag in %s: %s', filename, tag) result = False continue if not get_all_text(root.find(tag)): - print('[-] Empty tag in {0}: {1}'.format(filename, tag)) + logging.warning('Empty tag in %s: %s', filename, tag) result = False continue if tag == 'title' and (options['capitalization'] and \ @@ -405,14 +414,14 @@ def validate_master(filename, findings, non_findings, scans, options): result = True include_findings = [] include_nonfindings = [] - print_output(options, 'Validating master file {0}'.format(filename)) + logging.info('Validating master file %s', filename) try: xmltree = ElementTree.parse(filename, ElementTree.XMLParser(strip_cdata=False)) if not find_keyword(xmltree, 'TODO', filename): print('[-] Keyword checks failed for {0}'.format(filename)) result = False - print_output(options, 'Performing cross check on findings, non-findings and scans...') + logging.info('Performing cross check on findings, non-findings and scans...') for finding in findings: if not cross_check_file(filename, finding): print('[A] Cross check failed for finding {0}'. @@ -421,23 +430,22 @@ def validate_master(filename, findings, non_findings, scans, options): result = False for non_finding in non_findings: if not cross_check_file(filename, non_finding): - print('[A] Cross check failed for non-finding {0}'. - format(non_finding)) + logging.warning('Cross check failed for non-finding %s', non_finding) include_nonfindings.append(non_finding) result = False if result: - print_output(options, 'Cross checks successful') + logging.info('Cross checks successful') except (ElementTree.ParseError, IOError) as exception: - print('[-] validating {0} failed ({1})'.format(filename, exception)) + logging.warning('Validating %s failed: %s', filename, exception) result = False if not result: if options['auto_fix']: add_include(filename, 'findings', include_findings) add_include(filename, 'nonFindings', include_nonfindings) close_file(filename) - print('[+] Automatically fixed {0}'.format(filename)) + logging.info('Automatically fixed %s', filename) else: - print('[+] NOTE: Items with [A] can be fixed automatically, use --auto-fix') + logging.warning('Item can be fixed automatically, use --auto-fix') return result @@ -449,7 +457,7 @@ def report_string(report_file): report = open(report_file) return mmap.mmap(report.fileno(), 0, access=mmap.ACCESS_READ) except IOError as exception: - print('[-] Could not open {0} ({1})'.format(report_file, exception)) + logging.critical('Could not open %s: %s', report_file, exception) sys.exit(-1) @@ -460,7 +468,7 @@ def cross_check_file(filename, external): result = True report_text = report_string(filename) if report_text.find(external) == -1: - print('[-] could not find a reference in {0} to {1}'.format(filename, external)) + logging.warning('Could not find a reference in %s to %s', filename, external) result = False return result @@ -508,11 +516,35 @@ def find_keyword(xmltree, keyword, filename): section = 'in {0}'.format(tag.attrib['id']) if tag.text: if keyword in tag.text: - print('[-] {0} found in {1} {2}'.format(keyword, filename, section)) + logging.warning('%s found in %s %s', keyword, filename, section) result = False return result +def setup_logging(options): + """ + Set up loghandlers according to options. + """ + # DEBUG = (10) debug status messages + # INFO = (20) verbose status messages + # WARNING = (30) warning messages (= errors in validation) + # ERROR = (40) error messages (= program errors) + logger = logging.getLogger() + logger.setLevel(0) + console = logging.StreamHandler(stream=sys.stdout) + console.setFormatter(logging.Formatter('%(levelname)s %(message)s', + datefmt='%H:%M:%S')) + if options['debug']: + console.setLevel(logging.DEBUG) + else: + if options['verbose']: + print('hi') + console.setLevel(logging.INFO) + else: + logger.setLevel(logging.WARNING) + logger.addHandler(console) + + def main(): """ The main program. Cross-checks, validates XML files and report. @@ -522,27 +554,23 @@ def main(): reload(sys) sys.setdefaultencoding('utf-8') options = parse_arguments() + setup_logging(options) if options['all']: options['capitalization'] = True options['long'] = True if options['learn']: - print_output(options, 'Adding unknown words to {0}'.format(VOCABULARY)) - # if options['spelling']: - # if not os.path.exists(VOCABULARY): - # print_output(options, 'Creating project-specific vocabulary file {0}'. - # format(VOCABULARY)) - # options['learn'] = True - print_output(options, 'Validating all XML files...') + logging.debug('Adding unknown words to %s', VOCABULARY) + logging.info('Validating all XML files...') result = validate_files(all_files(), options) if result: - print_output(options, 'Validation checks successful') + logging.info('Validation checks successful') if DOCBUILDER: - print_output(options, 'Validating report build...') + logging.info('Validating report build...') result = validate_report() and result if result: - print('[+] Succesfully validated everything. Good to go') + logging.info('Validation successful. Good to go') else: - print('[-] Errors occurred') + logging.warning('Validation failed') if options['spelling'] and options['learn']: print('[*] Don\'t forget to check the vocabulary file {0}'. format(VOCABULARY)) From a7253519f346b8ca2e55073378d43e8bcf6ba613 Mon Sep 17 00:00:00 2001 From: Peter Mosmans Date: Mon, 21 Nov 2016 20:39:09 +1000 Subject: [PATCH 08/14] Replace zip with 7zip --- chatops/bash/releaser.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chatops/bash/releaser.sh b/chatops/bash/releaser.sh index e483420..0cdcdf6 100644 --- a/chatops/bash/releaser.sh +++ b/chatops/bash/releaser.sh @@ -42,7 +42,7 @@ if [ -f ${source} ]; then else cp -v ${source} ${fullname} PASS=$(head -c 25 /dev/random | base64 | head -c 25) - zip --password ${PASS} "${fullname}.zip" ${fullname} 2>/dev/null && echo "Zip file encrypted with password '${PASS}'" + 7z a -p${PASS} "${fullname}.zip" ${fullname} 2>/dev/null && echo "Zip file encrypted with password '${PASS}'" fi else echo "Could not find source ${source}" From b9226a1ddbf79747d0d1fe8615eace2ad0c66010 Mon Sep 17 00:00:00 2001 From: Peter Mosmans Date: Wed, 23 Nov 2016 22:12:02 +1000 Subject: [PATCH 09/14] Added more explanation for notes directory (source code) --- xml/notes/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xml/notes/README.md b/xml/notes/README.md index ac04a43..9e4b845 100644 --- a/xml/notes/README.md +++ b/xml/notes/README.md @@ -1,2 +1,2 @@ # notes -This folder holds all email correspondence and other notes +This folder holds all email correspondence, notes and received source code From f9eabe34bc9dfb0c66dee30ff3b496efe5d8026f Mon Sep 17 00:00:00 2001 From: Peter Mosmans Date: Thu, 24 Nov 2016 10:22:24 +1000 Subject: [PATCH 10/14] Use standard image for invoices --- xml/xslt/styles_inv.xslt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xml/xslt/styles_inv.xslt b/xml/xslt/styles_inv.xslt index 31e4c06..cf66bfc 100644 --- a/xml/xslt/styles_inv.xslt +++ b/xml/xslt/styles_inv.xslt @@ -67,7 +67,7 @@ 0cm 0cm - url(../graphics/logo_alt.png) + url(../graphics/logo.png) 30mm scale-to-fit scale-to-fit @@ -104,4 +104,4 @@ #EEEEEE - \ No newline at end of file + From 13a5a095b69b3608ed238e40d50d50a86ed3544a Mon Sep 17 00:00:00 2001 From: Peter Mosmans Date: Thu, 24 Nov 2016 10:56:13 +1000 Subject: [PATCH 11/14] Beautified logging based on message type Initialize speller only once per run --- chatops/python/validate_report.py | 63 ++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/chatops/python/validate_report.py b/chatops/python/validate_report.py index 0525db5..720acd5 100644 --- a/chatops/python/validate_report.py +++ b/chatops/python/validate_report.py @@ -50,6 +50,7 @@ NOT_CAPITALIZED = ['a', 'an', 'and', 'as', 'at', 'but', 'by', 'for', 'in', 'jQuery', 'jQuery-UI', 'nor', 'of', 'on', 'or', 'the', 'to', 'up'] SNIPPETDIR = 'snippets/' +STATUS = 25 # loglevel for 'generic' status messages TEMPLATEDIR = 'templates/' OFFERTE = '/offerte.xml' REPORT = '/report.xml' @@ -62,8 +63,29 @@ if DOCBUILDER: import proxy_vagrant try: import aspell -except: - print('[-] aspell not installed: spelling not available') +except ImportError: + print('[-] aspell not installed: spelling not available',) + + +class LogFormatter(logging.Formatter): + """ + Format log messages according to their type. + """ + # DEBUG = (10) debug status messages + # INFO = (20) verbose status messages + # STATUS = (25) generic status messages + # WARNING = (30) warning messages (= errors in validation) + # ERROR = (40) error messages (= program errors) + FORMATS = {logging.DEBUG :"DEBUG: %(module)s: %(lineno)d: %(message)s", + logging.INFO : "[*] %(message)s", + STATUS : "[+] %(message)s", + logging.WARN : "[-] %(message)s", + logging.ERROR : "ERROR: %(message)s", + 'DEFAULT' : "%(message)s"} + + def format(self, record): + self._fmt = self.FORMATS.get(record.levelno, self.FORMATS['DEFAULT']) + return logging.Formatter.format(self, record) def parse_arguments(): @@ -126,23 +148,22 @@ def initialize_speller(): except aspell.AspellSpellerError as exception: logging.error('Could not initialize speller: %s', exception) if speller: - [logging.debug(i[0] + ' ' + str(i[2]) + '\n') for i in speller.ConfigKeys()] + [logging.debug('%s %s', i[0], i[2]) for i in speller.ConfigKeys()] return speller -def validate_spelling(tree, filename, options): +def validate_spelling(tree, filename, options, speller): """ Check spelling of text within tags. If options['learn'], then unknown words will be added to the dictionary. """ result = True - speller = initialize_speller() if not speller: options['spelling'] = False return result try: root = tree.getroot() - for section in root.iter(): + for section in root.iter(): if section.text and isinstance(section.tag, basestring) and \ section.tag not in ('a', 'code', 'monospace', 'pre'): for word in re.findall('([a-zA-Z]+\'?[a-zA-Z]+)', section.text): @@ -172,6 +193,9 @@ def all_files(): def open_editor(filename): + """ + Open editor with file to edit. + """ if sys.platform in ('linux', 'linux2'): editor = os.getenv('EDITOR') if editor: @@ -196,6 +220,7 @@ def validate_files(filenames, options): findings = [] non_findings = [] scans = [] + speller = initialize_speller() for filename in filenames: if (filename.lower().endswith('.xml') or filename.lower().endswith('xml"')): @@ -204,7 +229,7 @@ def validate_files(filenames, options): (REPORT in filename and not options['no_report']): masters.append(filename) # try: - type_result, xml_type = validate_xml(filename, options) + type_result, xml_type = validate_xml(filename, options, speller) result = result and type_result if 'non-finding' in xml_type: non_findings.append(filename) @@ -230,7 +255,7 @@ def validate_report(): return proxy_vagrant.execute_command(host, command) -def validate_xml(filename, options): +def validate_xml(filename, options, speller): """ Validates XML file by trying to parse it. Returns True if the file validated successfully. @@ -245,7 +270,7 @@ def validate_xml(filename, options): with open(filename, 'rb') as xml_file: xml.sax.parse(xml_file, xml.sax.ContentHandler()) tree = ElementTree.parse(filename, ElementTree.XMLParser(strip_cdata=False)) - type_result, xml_type = validate_type(tree, filename, options) + type_result, xml_type = validate_type(tree, filename, options, speller) result = validate_long_lines(tree, filename, options) and result and type_result if options['edit'] and not result: open_editor(filename) @@ -292,7 +317,7 @@ def capitalize(line): return capitalized.strip() -def validate_type(tree, filename, options): +def validate_type(tree, filename, options, speller): """ Performs specific checks based on type. Currently only finding and non-finding are supported. @@ -304,7 +329,7 @@ def validate_type(tree, filename, options): attributes = [] tags = [] if options['spelling']: - result = validate_spelling(tree, filename, options) + result = validate_spelling(tree, filename, options, speller) if xml_type == 'pentest_report': attributes = ['findingCode'] if xml_type == 'finding': @@ -525,23 +550,17 @@ def setup_logging(options): """ Set up loghandlers according to options. """ - # DEBUG = (10) debug status messages - # INFO = (20) verbose status messages - # WARNING = (30) warning messages (= errors in validation) - # ERROR = (40) error messages (= program errors) logger = logging.getLogger() logger.setLevel(0) console = logging.StreamHandler(stream=sys.stdout) - console.setFormatter(logging.Formatter('%(levelname)s %(message)s', - datefmt='%H:%M:%S')) + console.setFormatter(LogFormatter()) if options['debug']: console.setLevel(logging.DEBUG) else: if options['verbose']: - print('hi') console.setLevel(logging.INFO) else: - logger.setLevel(logging.WARNING) + logger.setLevel(STATUS) logger.addHandler(console) @@ -563,17 +582,15 @@ def main(): logging.info('Validating all XML files...') result = validate_files(all_files(), options) if result: - logging.info('Validation checks successful') if DOCBUILDER: logging.info('Validating report build...') result = validate_report() and result if result: - logging.info('Validation successful. Good to go') + logging.log(STATUS, 'Validation checks successful. Good to go') else: logging.warning('Validation failed') if options['spelling'] and options['learn']: - print('[*] Don\'t forget to check the vocabulary file {0}'. - format(VOCABULARY)) + logging.log(STATUS('Don\'t forget to check the vocabulary file %s', VOCABULARY)) if __name__ == "__main__": From 7b84dfaa25d01f7e4effe484a492a76d528a9ce3 Mon Sep 17 00:00:00 2001 From: skyanth Date: Tue, 29 Nov 2016 16:31:17 +0100 Subject: [PATCH 12/14] Added execsummary option to report builder --- chatops/python/docbuilder.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/chatops/python/docbuilder.py b/chatops/python/docbuilder.py index 5da7c57..d96bab0 100644 --- a/chatops/python/docbuilder.py +++ b/chatops/python/docbuilder.py @@ -55,6 +55,9 @@ the Free Software Foundation, either version 3 of the License, or help='overwrite output file if it already exists') parser.add_argument('-date', action='store', help='the invoice date') + parser.add_argument('-execsummary', action='store', + help="""create an executive summary as well as a report. + Default: no """) parser.add_argument('--fop-config', action='store', default='/etc/docbuilder/rosfop.xconf', help="""fop configuration file (default @@ -141,6 +144,8 @@ def to_fo(options): cmd.append('INVOICE_NO=' + options['invoice']) if options['date']: cmd.append('DATE=' + options['date']) + if options['execsummary']: + cmd.append('EXEC_SUMMARY=' + options['execsummary']) process = subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE) stdout, stderr = process.communicate() print_output(stdout, stderr) From 69a2c827f834e3e0706bde004f13243d5c9ca512 Mon Sep 17 00:00:00 2001 From: skyanth Date: Tue, 29 Nov 2016 16:33:01 +0100 Subject: [PATCH 13/14] Clarified execsummary documentation --- chatops/python/docbuilder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chatops/python/docbuilder.py b/chatops/python/docbuilder.py index d96bab0..af4530a 100644 --- a/chatops/python/docbuilder.py +++ b/chatops/python/docbuilder.py @@ -56,8 +56,8 @@ the Free Software Foundation, either version 3 of the License, or parser.add_argument('-date', action='store', help='the invoice date') parser.add_argument('-execsummary', action='store', - help="""create an executive summary as well as a report. - Default: no """) + help="""create an executive summary as well as a report (true/false). + Default: false """) parser.add_argument('--fop-config', action='store', default='/etc/docbuilder/rosfop.xconf', help="""fop configuration file (default From aad0e9be44e612fa1399d38e1971f8aef2996560 Mon Sep 17 00:00:00 2001 From: skyanth Date: Tue, 29 Nov 2016 16:58:20 +0100 Subject: [PATCH 14/14] execsummary=true now also still builds report instead of just summary --- chatops/bash/handler_build | 1 + chatops/python/docbuilder.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/chatops/bash/handler_build b/chatops/bash/handler_build index 01cce2a..49452ec 100644 --- a/chatops/bash/handler_build +++ b/chatops/bash/handler_build @@ -113,6 +113,7 @@ build() { add_to_repo() { git add target/$TARGET-latest.pdf git add target/waiver_?*.pdf &>/dev/null + git add target/execsummary.pdf &>/dev/null git commit -q -m "$targetpdf proudly manufactured using ChatOps" &>/dev/null git push -q >/dev/null } diff --git a/chatops/python/docbuilder.py b/chatops/python/docbuilder.py index af4530a..a1c85d6 100644 --- a/chatops/python/docbuilder.py +++ b/chatops/python/docbuilder.py @@ -32,6 +32,7 @@ import textwrap GITREV = 'GITREV' # Magic tag which gets replaced by the git short commit hash OFFERTE = 'generate_offerte.xsl' # XSL for generating waivers WAIVER = 'waiver_' # prefix for waivers +EXECSUMMARY = 'execsummary' # generating an executive summary instead of a report def parse_arguments(): @@ -227,6 +228,23 @@ def main(): except OSError as exception: print_exit('[-] ERR: {0}'.format(exception.strerror), exception.errno) + if options['execsummary'] == 'true': # we're generating a summary as well as a report + report_output = options['output'] + verboseprint('generating additional executive summary') + output_dir = os.path.dirname(options['output']) + fop_dir = os.path.dirname(options['fop']) + try: + for fop in [os.path.splitext(x)[0] for x in + os.listdir(fop_dir) if x.endswith('fo')]: + if EXECSUMMARY in fop: + options['output'] = output_dir + os.sep + fop + '.pdf' + else: + options['output'] = report_output + options['fop'] = fop_dir + os.sep + fop + '.fo' + result = to_pdf(options) and result + except OSError as exception: + print_exit('[-] ERR: {0}'.format(exception.strerror), + exception.errno) else: result = to_pdf(options)