88import click
99import jsonschema
1010import sys
11+ import os
1112import validators
1213from tabulate import tabulate
1314
1415from drheader import Drheader
1516from drheader .cli_utils import echo_bulk_report , file_junit_report
16- from drheader .utils import load_rules
17+ from drheader .utils import load_rules , get_rules_from_uri
18+
19+
20+ EXIT_CODE_NO_ERROR = os .EX_OK
21+ EXIT_CODE_FAILURE = os .EX_SOFTWARE
1722
1823
1924@click .group ()
@@ -31,8 +36,9 @@ def scan():
3136@click .option ('--json' , 'json_output' , help = 'Output report as json' , is_flag = True )
3237@click .option ('--debug' , help = 'Show error messages' , is_flag = True )
3338@click .option ('--rules' , 'rule_file' , help = 'Use custom rule set' , type = click .File ())
39+ @click .option ('--rules-uri' , 'rule_uri' , help = 'Use custom rule set, downloaded from URI' )
3440@click .option ('--merge' , help = 'Merge custom file rules, on top of default rules' , is_flag = True )
35- def compare (file , json_output , debug , rule_file , merge ):
41+ def compare (file , json_output , debug , rule_file , rule_uri , merge ):
3642 """
3743 If you have headers you would like to test with drheader, you can "compare" them with your ruleset this command.
3844
@@ -60,7 +66,7 @@ def compare(file, json_output, debug, rule_file, merge):
6066 ...
6167 ]
6268 """
63-
69+ exit_code = EXIT_CODE_NO_ERROR
6470 audit = []
6571 schema = {
6672 "type" : "array" ,
@@ -90,38 +96,64 @@ def compare(file, json_output, debug, rule_file, merge):
9096 except Exception as e :
9197 raise click .ClickException (e )
9298
99+ if rule_uri and not rule_file :
100+ if not validators .url (rule_uri ):
101+ raise click .ClickException (message = '"{}" is not a valid URL.' .format (rule_uri ))
102+ try :
103+ rule_file = get_rules_from_uri (rule_uri )
104+ except Exception as e :
105+ if debug :
106+ raise click .ClickException (e )
107+ else :
108+ raise click .ClickException ('No content retrieved from rules-uri.' )
109+
93110 rules = load_rules (rule_file , merge )
94111
95112 for i in data :
96113 logging .debug ('Analysing : {}' .format (i ['url' ]))
97114 drheader_instance = Drheader (url = i ['url' ], headers = i ['headers' ])
98115 drheader_instance .analyze (rules )
99116 audit .append ({'url' : i ['url' ], 'report' : drheader_instance .report })
117+ if drheader_instance .report :
118+ exit_code = EXIT_CODE_FAILURE
100119
101120 echo_bulk_report (audit , json_output )
121+ sys .exit (exit_code )
102122
103123
104124@scan .command ()
105125@click .argument ('target_url' , required = True )
106126@click .option ('--json' , 'json_output' , help = 'Output report as json' , is_flag = True )
107127@click .option ('--debug' , help = 'Show error messages' , is_flag = True )
108128@click .option ('--rules' , 'rule_file' , help = 'Use custom rule set' , type = click .File ())
129+ @click .option ('--rules-uri' , 'rule_uri' , help = 'Use custom rule set, downloaded from URI' )
109130@click .option ('--merge' , help = 'Merge custom file rules, on top of default rules' , is_flag = True )
110131@click .option ('--junit' , help = 'Produces a junit report with the result of the scan' , is_flag = True )
111- def single (target_url , json_output , debug , rule_file , merge , junit ):
132+ def single (target_url , json_output , debug , rule_file , rule_uri , merge , junit ):
112133 """
113134 Scan a single http(s) endpoint with drheader.
114135
115136 NOTE: URL parameters are currently only supported on bulk scans.
116137 """
117-
138+ exit_code = EXIT_CODE_NO_ERROR
118139 if debug :
119140 logging .basicConfig (level = logging .DEBUG )
120141
121142 logging .debug ('Validating: {}' .format (target_url ))
122143 if not validators .url (target_url ):
123144 raise click .ClickException (message = '"{}" is not a valid URL.' .format (target_url ))
124145
146+ if rule_uri and not rule_file :
147+ if not validators .url (rule_uri ):
148+ raise click .ClickException (message = '"{}" is not a valid URL.' .format (rule_uri ))
149+ try :
150+ rule_file = get_rules_from_uri (rule_uri )
151+ except Exception as e :
152+ if debug :
153+ raise click .ClickException (e )
154+ else :
155+ raise click .ClickException ('No content retrieved from rules-uri.' )
156+
125157 rules = load_rules (rule_file , merge )
126158
127159 try :
@@ -142,6 +174,9 @@ def single(target_url, json_output, debug, rule_file, merge, junit):
142174 else :
143175 raise click .ClickException ('Failed to analyze headers.' )
144176
177+ if drheader_instance .report :
178+ exit_code = EXIT_CODE_FAILURE
179+
145180 if json_output :
146181 click .echo (json .dumps (drheader_instance .report ))
147182 else :
@@ -158,7 +193,7 @@ def single(target_url, json_output, debug, rule_file, merge, junit):
158193 click .echo (tabulate (values , tablefmt = "presto" ))
159194 if junit :
160195 file_junit_report (rules , drheader_instance .report )
161- return 0
196+ sys . exit ( exit_code )
162197
163198
164199@scan .command ()
@@ -169,8 +204,9 @@ def single(target_url, json_output, debug, rule_file, merge, junit):
169204@click .option ('--json' , 'json_output' , help = 'Output report as json' , is_flag = True )
170205@click .option ('--debug' , help = 'Show error messages' , is_flag = True )
171206@click .option ('--rules' , 'rule_file' , help = 'Use custom rule set' , type = click .File ())
207+ @click .option ('--rules-uri' , 'rule_uri' , help = 'Use custom rule set, downloaded from URI' )
172208@click .option ('--merge' , help = 'Merge custom file rules, on top of default rules' , is_flag = True )
173- def bulk (file , json_output , post , input_format , debug , rule_file , merge ):
209+ def bulk (file , json_output , post , input_format , debug , rule_file , rule_uri , merge ):
174210 """
175211 Scan multiple http(s) endpoints with drheader.
176212
@@ -195,7 +231,7 @@ def bulk(file, json_output, post, input_format, debug, rule_file, merge):
195231
196232 NOTE: URL parameters are currently only supported on bulk scans.
197233 """
198-
234+ exit_code = EXIT_CODE_NO_ERROR
199235 audit = []
200236 urls = []
201237 schema = {
@@ -235,6 +271,17 @@ def bulk(file, json_output, post, input_format, debug, rule_file, merge):
235271
236272 logging .debug ('Found {} URLs' .format (len (urls )))
237273
274+ if rule_uri and not rule_file :
275+ if not validators .url (rule_uri ):
276+ raise click .ClickException (message = '"{}" is not a valid URL.' .format (rule_uri ))
277+ try :
278+ rule_file = get_rules_from_uri (rule_uri )
279+ except Exception as e :
280+ if debug :
281+ raise click .ClickException (e )
282+ else :
283+ raise click .ClickException ('No content retrieved from rules-uri.' )
284+
238285 rules = load_rules (rule_file , merge )
239286
240287 for i , v in enumerate (urls ):
@@ -243,9 +290,11 @@ def bulk(file, json_output, post, input_format, debug, rule_file, merge):
243290 logging .debug ('Analysing: {}...' .format (v ))
244291 drheader_instance .analyze (rules )
245292 audit .append ({'url' : v ['url' ], 'report' : drheader_instance .report })
293+ if drheader_instance .report :
294+ exit_code = EXIT_CODE_FAILURE
246295
247296 echo_bulk_report (audit , json_output )
248- return 0
297+ sys . exit ( exit_code )
249298
250299
251300if __name__ == "__main__" :
0 commit comments