Skip to content
Browse files

Added support for Json results

Added support for JSON result files using --report json, and added
support to have results written to stdout via the --output option.
  • Loading branch information...
1 parent 86668d9 commit 1b7eb5953532c9a4af93828f74dfe99d7788e6f3 @ygjb ygjb committed Sep 6, 2012
Showing with 40 additions and 30 deletions.
  1. +2 −2 Garmr/garmr.py
  2. +11 −7 Garmr/reporter.py
  3. +27 −21 Garmr/scanner.py
View
4 Garmr/garmr.py
@@ -18,8 +18,8 @@ def main():
parser.add_argument("-D", "--disable-core", action="store_true", default = False, dest="disable_core", help="Disable corechecks")
parser.add_argument("-p", "--force-passive", action="store_true", default=False, dest="force_passives", help ="Force passives to be run for each active test")
parser.add_argument("-d", "--dns", action="store_false", default=True, dest="resolve_target", help ="Skip DNS resolution when registering a target")
- parser.add_argument("-r", "--report", action="store", default="xml", dest="report",help="Load a reporter e.g. -r reporter.AntXmlReporter")
- parser.add_argument("-o", "--output", action="store", default="garmr-results.xml", dest="output", help="Default output is garmr-results.xml")
+ parser.add_argument("-r", "--report", action="store", default="xml", dest="report",help="Load a reporter e.g. -r reporter.AntXmlReporter, reporter.JsonReporter, xml, or json")
+ parser.add_argument("-o", "--output", action="store", default="garmr-results.xml", dest="output", help="Default output is garmr-results.xml, specify - to write to stdout")
parser.add_argument("-c", "--check", action="append", dest="opts", help="Set a parameter for a check (check:opt=value)" )
parser.add_argument("-e", "--exclude", action="append", dest="exclusions", help="Prevent a check from being run/processed")
parser.add_argument("--save", action="store", dest="dump_path", help="Write out a configuration file based on parameters (won't run scan)")
View
18 Garmr/reporter.py
@@ -1,4 +1,4 @@
-
+import json
class Reporter():
reporters = {}
@@ -14,13 +14,13 @@ def write_target(self, target):
def start_actives(self):
return None
- def write_active(self, test):
+ def write_active(self, test, result):
return None
def start_passives(self):
return None
- def write_passive(self, target):
+ def write_passive(self, test, result):
return None
def end_passives(self):
@@ -32,14 +32,18 @@ def end_actives(self):
def end_targets(self):
return None
- def end_report(self):
+ def end_report(self, results):
return "This reporter is unimplemented!"
class DetailReporter(Reporter):
# TODO Implement detailed reporter
- def end_report(self):
+ def end_report(self, results):
return "This reporter should emit an XML report that includes all of the the details for each test, including captured data"
+class JsonReporter(Reporter):
+ def end_report(self, results):
+ return json.dumps(results, indent = 2)
+
class AntXmlReporter(Reporter):
def __init__(self):
@@ -107,8 +111,8 @@ def end_targets(self):
self.report += "</testsuites>\n"
return None
- def end_report(self):
+ def end_report(self, results):
return self.report
Reporter.reporters['xml'] = AntXmlReporter()
-
+Reporter.reporters['json'] = JsonReporter()
View
48 Garmr/scanner.py
@@ -7,7 +7,7 @@
import requests
import socket
import traceback
-from inspect import getargspec
+from inspect import getargspec, getmodule
import subprocess
import json
@@ -44,7 +44,7 @@ def analyze(self, response, results):
return None
def result(self, state, message, data):
- return {'state' : state, 'message' : message, 'data' : data }
+ return {'state' : state, 'message' : message, 'data' : str(data) }
class ActiveTest():
@@ -87,7 +87,7 @@ def execute(self, url, predecessor=None):
return resulttuple
def result(self, state, message, data):
- return { 'state' : state, 'message' : message, 'data' : data, 'passive' : {}}
+ return { 'state' : state, 'message' : message, 'data' : str(data), 'passive' : {}}
class HtmlTest(PassiveTest):
description = 'allow easy analysis of html source code'
@@ -126,17 +126,17 @@ def do_passive_scan(self, passiveclass, is_ssl, response):
Scanner.logger.debug("\t\t[%s] Skip Test invalid for http scheme" % passiveclass)
passive_result = PassiveTest().result("Skip", "This check is only applicable to SSL requests.", None)
start = datetime.now()
- passive_result['start'] = start
- passive_result['end'] = start
+ passive_result['start'] = str(start)
+ passive_result['end'] = str(start)
passive_result["duration"] = 0
else:
start = datetime.now()
passive = passiveclass()
passive_result = passive.analyze(response)
end = datetime.now()
td = end - start
- passive_result['start'] = start
- passive_result['end'] = end
+ passive_result['start'] = str(start)
+ passive_result['end'] = str(end)
passive_result['duration'] = float((td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6)) / 10**6
Scanner.logger.info("\t\t[%s] %s %s" % (passiveclass, passive_result['state'], passive_result['message']))
return passive_result
@@ -146,21 +146,21 @@ def do_active_scan(self, testclass, is_ssl, target):
if (testclass.secure_only and not is_ssl):
Scanner.logger.info("\t[Skip] [%s] (reason: secure_only)" % testclass)
result = ActiveTest().result("Skip", "This check is only applicable to SSL requests", None)
- result['start'] = datetime.now()
+ result['start'] = str(datetime.now())
result['end'] = result['start']
result['duration'] = 0
return result
elif (testclass.insecure_only and is_ssl):
Scanner.logger.info("\t[Skip] [%s] (reason: insecure_only)" % testclass)
result = ActiveTest().result("Skip", "This check is only applicable to SSL requests", None)
- result['start'] = datetime.now()
+ result['start'] = str(datetime.now())
result['end'] = result['start']
result['duration'] = 0
return result
elif str(testclass).split('.')[-1] in self._disabled_tests_:
Scanner.logger.info("\t[Skip] [%s] (reason: disabled)" % testclass)
result = ActiveTest().result("Skip", "This check was marked as disabled.", None)
- result['start'] = datetime.now()
+ result['start'] = str(datetime.now())
result['end'] = result['start']
result['duration'] = 0
return result
@@ -174,9 +174,9 @@ def do_active_scan(self, testclass, is_ssl, target):
result, response = test.execute(target)
end = datetime.now()
td = end - start
- result['response'] = response
- result['start'] = start
- result['end'] = end
+ result['response'] = str(response)
+ result['start'] = str(start)
+ result['end'] = str(end)
result['duration'] = float((td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6)) / 10**6
Scanner.logger.info("\t[%s] %s %s" % (testclass, result['state'], result['message']))
self.reporter.write_active(testclass, result)
@@ -186,8 +186,9 @@ def do_active_scan(self, testclass, is_ssl, target):
result['passive'] = {}
self.reporter.start_passives()
for passive_testclass in self._passive_tests_:
- result["passive"][passive_testclass] = self.do_passive_scan(passive_testclass, is_ssl, response)
- self.reporter.write_passive(passive_testclass, result["passive"][passive_testclass])
+ passive_testclass_key = "%s.%s" % (getmodule(passive_testclass).__name__, passive_testclass.__name__)
+ result["passive"][passive_testclass_key] = self.do_passive_scan(passive_testclass, is_ssl, response)
+ self.reporter.write_passive(passive_testclass, result["passive"][passive_testclass_key])
self.reporter.end_passives()
return result
@@ -203,11 +204,12 @@ def scan_target(self, target):
while len(self.active_tests_stack) > 0:
testclass = self.active_tests_stack[0]
self.active_tests_stack = self.active_tests_stack[1:]
- self.results[testclass] = self.do_active_scan(testclass, is_ssl, target)
+ testclass_key = "%s.%s" % (getmodule(testclass).__name__, testclass.__name__)
+ self.results[testclass_key] = self.do_active_scan(testclass, is_ssl, target)
if hasattr(testclass, 'events'): #TODO enforce every test to have event dict present?
events_lower = dict([(k.lower(),v) for k,v in testclass.events.items()])
- if self.results[testclass]['state'].lower() in events_lower and events_lower[self.results[testclass]['state'].lower()] != None:
- nexttest = events_lower[self.results[testclass]['state'].lower()]
+ if self.results[testclass_key]['state'].lower() in events_lower and events_lower[self.results[testclass_key]['state'].lower()] != None:
+ nexttest = events_lower[self.results[testclass_key]['state'].lower()]
Scanner.logger.info("\t[%s] Instantiated because %s declares it as its successor (the event was '%s')" % (nexttest, testclass, self.results[testclass]['state']))
self.active_tests_stack.append(nexttest) # we have to hand over the response!!1, # important: we hand over an instance, not the class
self._finished_active_tests_.append(testclass)
@@ -228,9 +230,13 @@ def run_scan(self):
except:
Scanner.logger.error(traceback.format_exc())
self.reporter.end_targets()
- file = open(self.output, "w")
- file.write(self.reporter.end_report())
- file.close()
+ result = self.reporter.end_report(results)
+ if (self.output == "-"):
+ print result
+ else:
+ file = open(self.output, "w")
+ file.write(self.reporter.end_report(results))
+ file.close()
def register_target(self, url):

0 comments on commit 1b7eb59

Please sign in to comment.
Something went wrong with that request. Please try again.