Permalink
Browse files

Added setup, restructured built-in checks

Added setup.py
Restructured package for sanity
Moved RobotsTest out of corechecks because it is lame.
Moved djangochecks out of the package (will be put into a separate repo)
Added WebTouch and Http200 to corechecks (WebTouch simply visits an URL
with a get, and follows a redirect if present, Http200 visits a URL and
passes if it returns 200, but doesn't follow redirects.
  • Loading branch information...
1 parent 68371f6 commit 14d47414132712057eff654f346646a96bab128b Yvan Boily committed Oct 14, 2011
Showing with 115 additions and 53 deletions.
  1. +3 −1 .gitignore
  2. 0 Garmr/__init__.py
  3. +20 −12 { → Garmr}/corechecks.py
  4. +12 −4 { → Garmr}/garmr.py
  5. +4 −5 { → Garmr}/reporter.py
  6. 0 { → Garmr}/scanner.py
  7. +0 −29 config.txt
  8. +2 −2 djangochecks.py
  9. +50 −0 setup.py
  10. +24 −0 webchecks.py
View
@@ -3,7 +3,9 @@ dist/
*.egg-info/
*.pyc
garmr-results.xml
-
+build/
+*.xml
+*.txt
.project
.pydevproject
View
No changes.
@@ -62,20 +62,27 @@ def analyze(self, response):
result = self.result("Pass", "X-Frame-Options header present.", response.headers[xfoheader])
return result
-class RobotsTest(ActiveTest):
+class Http200Check(ActiveTest):
run_passives = True
- description = "Check for the presence of a robots.txt file. If save_contents is true, the contents will be saved."
- config = {"save_contents" : "False"}
+ description = "Make a GET request to the specified URL, reporting success only on a 200 response without following redirects"
def do_test(self, url):
- u = urlparse(url)
- roboturl="%s://%s/robots.txt" % (u.scheme, u.netloc)
- response = requests.get(roboturl)
+ response = get_url(url, False)
if response.status_code == 200:
- result = self.result("Pass", "A robots.txt file is present on the server",
- response.content if self.config["save_contents"].lower() == "true" else None)
+ result = self.result("Pass", "The request returned an HTTP 200 response.", None)
else:
- result = self.result("Fail", "No robots.txt file was found.", None)
- return (result, response);
+ result = self.result("Fail", "The response code was %s" % response.status_code, None)
+ return (result, response)
+
+class WebTouch(ActiveTest):
+ run_passives = True
+ description = "Make a GET request to the specified URL, and check for a 200 response after resolving redirects."
+ def do_test(self, url):
+ response = requests.get(url)
+ if response.status_code == 200:
+ result = self.result("Pass", "The request returned an HTTP 200 response.", None)
+ else:
+ result = self.result("Fail", "The response code was %s" % response.status_code, None)
+ return (result, response)
class StsUpgradeCheck(ActiveTest):
insecure_only = True
@@ -117,9 +124,10 @@ def do_test(self, url):
def configure(scanner):
if isinstance(scanner, Scanner) == False:
raise Exception("Cannot configure a non-scanner object!")
+ scanner.register_check(Http200Check())
+ scanner.register_check(WebTouch())
scanner.register_check(StrictTransportSecurityPresent())
scanner.register_check(XFrameOptionsPresent())
- scanner.register_check(RobotsTest())
scanner.register_check(StsUpgradeCheck())
scanner.register_check(HttpOnlyAttributePresent())
- scanner.register_check(SecureAttributePresent())
+ scanner.register_check(SecureAttributePresent())
@@ -2,18 +2,22 @@
from scanner import ActiveTest, PassiveTest, Scanner
import corechecks
from reporter import Reporter
-import sys
+import os, sys
import traceback
def main():
+ # Include current path in case we attempt to load modules.
+ sys.path.append(os.getcwd())
+
parser = argparse.ArgumentParser("Runs a set of tests against the set of provided URLs")
parser.add_argument("-u", "--url", action="append", dest="targets", help="Add a target to test")
parser.add_argument("-f", "--target-file", action="append", dest="target_files", help="File with URLs to test")
- parser.add_argument("-m", "--module", action="append", default = ["corechecks"], dest="modules", help="Load an extension module")
+ parser.add_argument("-m", "--module", action="append", default = [], dest="modules", help="Load an extension module")
+ 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="reporter.AntXmlReporter", dest="report",help="Load a reporter e.g. -r reporter.AntXmlReporter")
+ 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("-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")
@@ -43,6 +47,10 @@ def main():
except:
Scanner.logger.error("Unable to process the target list in: %s", targets)
+ # Load built-in modules if required.
+ if args.disable_core == False:
+ corechecks.configure(scanner)
+
# Configure modules.
# TODO: change the module loading to scan the list of classes in a module and automagically
# detect any tests defined.
@@ -93,4 +101,4 @@ def main():
if __name__ == "__main__":
- main()
+ main()
@@ -40,8 +40,6 @@ class DetailReporter(Reporter):
def end_report(self):
return "This reporter should emit an XML report that includes all of the the details for each test, including captured data"
-Reporter.reporters['detail'] = DetailReporter()
-
class AntXmlReporter(Reporter):
def __init__(self):
@@ -74,7 +72,8 @@ def start_actives(self):
def write_active(self, test, result):
self.states[result["state"]] += 1
self.checks += 1
- module, check = ("%s" % test ).split('.')
+ print test
+ module, check = ("%s" % test ).split('.')[-2:]
self.lines += '\t\t<testcase classname="%s" name="%s" time="%s"' % (module, check, result["duration"])
if result["state"] == "Pass":
self.lines += " />\n"
@@ -88,7 +87,7 @@ def start_passives(self):
def write_passive(self, test, result):
self.states[result["state"]] += 1
self.checks += 1
- module, check = ("%s" % test ).split('.')
+ module, check = ("%s" % test ).split('.')[-2:]
self.lines += '\t\t<testcase classname="%s" name="%s" time="%s"' % (module, check, result["duration"])
if result["state"] == "Pass":
self.lines += " />\n"
@@ -113,4 +112,4 @@ def end_report(self):
return self.report
Reporter.reporters['xml'] = AntXmlReporter()
-
+
File renamed without changes.
View
@@ -1,29 +0,0 @@
-[Garmr]
-force-passives = False
-module = corechecks, djangochecks
-reporter = reporter.AntXmlReporter
-output = garmr-results.xml
-dns = True
-
-[corechecks.StsUpgradeCheck]
-enabled = True
-
-[djangochecks.AdminAvailable]
-enabled = True
-path = console
-
-[corechecks.RobotsTest]
-enabled = True
-
-[corechecks.StsHeaderPresent]
-enabled = True
-
-[corechecks.SecureAttributePresent]
-enabled = True
-
-[corechecks.HttpOnlyPresent]
-enabled = True
-
-[corechecks.XfoPresent]
-enabled = True
-
View
@@ -1,6 +1,6 @@
from urlparse import urlparse
import requests
-from scanner import ActiveTest, PassiveTest, Scanner, get_url
+from Garmr.scanner import ActiveTest, PassiveTest, Scanner, get_url
class AdminAvailable(ActiveTest):
@@ -22,4 +22,4 @@ def configure(scanner):
if isinstance(scanner, Scanner) == False:
raise Exception("Cannot configure a non-scanner object!")
scanner.register_check(AdminAvailable())
-
+
View
@@ -0,0 +1,50 @@
+import os, sys
+from setuptools import setup
+
+def read(fname):
+ return open(os.path.join(os.path.dirname(__file__), fname)).read()
+
+
+def main():
+ setup(
+ name = "Garmr",
+ version = "0.3",
+ author = "David Burns, Yvan Boily",
+ author_email = "dburns at mozilladotcom",
+ description='A tool for testing a web application for basic security holes',
+ keywords = "example documentation tutorial",
@AutomatedTester

AutomatedTester Oct 14, 2011

Owner

We should think of some keywords that may help find the project

+ url = "http://packages.python.org/an_example_pypi_project",
@AutomatedTester

AutomatedTester Oct 14, 2011

Owner

This should be changed to the Github URL so that people can find it quickly

+ packages=['Garmr'],
+ long_description=read('README.md'),
+ entry_points = make_entry_points(),
+ classifiers=[
+ "Development Status :: 3 - Alpha",
+ "Topic :: Utilities",
+ 'License :: OSI Approved :: Mozilla Public License 1.1 (MPL 1.1)',
+ ],
+ install_requires= ['requests>=0.6.2']
+ )
+
+
+def cmdline_entrypoints(versioninfo, platform, basename):
+ target = 'Garmr.garmr:main'
+ if platform.startswith('java'):
+ points = {'garmr': target}
+ else:
+ if basename.startswith("pypy"):
+ points = {'garmr-%s' % basename: target}
+ else: # cpython
+ points = {'garmr-%s.%s' % versioninfo[:2] : target,}
+ points['garmr'] = target
+ return points
+
+def make_entry_points():
+ basename = os.path.basename(sys.executable)
+ points = cmdline_entrypoints(sys.version_info, sys.platform, basename)
+ keys = list(points.keys())
+ keys.sort()
+ l = ["%s = %s" % (x, points[x]) for x in keys]
+ return {'console_scripts': l}
+
+if __name__ == '__main__':
+ main()
View
@@ -0,0 +1,24 @@
+from urlparse import urlparse
+import requests
+from Garmr.scanner import ActiveTest, PassiveTest, Scanner, get_url
+
+
+class RobotsTest(ActiveTest):
+ run_passives = True
+ description = "Check for the presence of a robots.txt file. If save_contents is true, the contents will be saved."
+ config = {"save_contents" : "False"}
+ def do_test(self, url):
+ u = urlparse(url)
+ roboturl="%s://%s/robots.txt" % (u.scheme, u.netloc)
+ response = requests.get(roboturl)
+ if response.status_code == 200:
+ result = self.result("Pass", "A robots.txt file is present on the server",
+ response.content if self.config["save_contents"].lower() == "true" else None)
+ else:
+ result = self.result("Fail", "No robots.txt file was found.", None)
+ return (result, response);
+
+def configure(scanner):
+ if isinstance(scanner, Scanner) == False:
+ raise Exception("Cannot configure a non-scanner object!")
+ scanner.register_check(RobotsTest())

0 comments on commit 14d4741

Please sign in to comment.