8
8
import click
9
9
import jsonschema
10
10
import sys
11
+ import os
11
12
import validators
12
13
from tabulate import tabulate
13
14
14
15
from drheader import Drheader
15
16
from 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
17
22
18
23
19
24
@click .group ()
@@ -31,8 +36,9 @@ def scan():
31
36
@click .option ('--json' , 'json_output' , help = 'Output report as json' , is_flag = True )
32
37
@click .option ('--debug' , help = 'Show error messages' , is_flag = True )
33
38
@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' )
34
40
@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 ):
36
42
"""
37
43
If you have headers you would like to test with drheader, you can "compare" them with your ruleset this command.
38
44
@@ -60,7 +66,7 @@ def compare(file, json_output, debug, rule_file, merge):
60
66
...
61
67
]
62
68
"""
63
-
69
+ exit_code = EXIT_CODE_NO_ERROR
64
70
audit = []
65
71
schema = {
66
72
"type" : "array" ,
@@ -90,38 +96,64 @@ def compare(file, json_output, debug, rule_file, merge):
90
96
except Exception as e :
91
97
raise click .ClickException (e )
92
98
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
+
93
110
rules = load_rules (rule_file , merge )
94
111
95
112
for i in data :
96
113
logging .debug ('Analysing : {}' .format (i ['url' ]))
97
114
drheader_instance = Drheader (url = i ['url' ], headers = i ['headers' ])
98
115
drheader_instance .analyze (rules )
99
116
audit .append ({'url' : i ['url' ], 'report' : drheader_instance .report })
117
+ if drheader_instance .report :
118
+ exit_code = EXIT_CODE_FAILURE
100
119
101
120
echo_bulk_report (audit , json_output )
121
+ sys .exit (exit_code )
102
122
103
123
104
124
@scan .command ()
105
125
@click .argument ('target_url' , required = True )
106
126
@click .option ('--json' , 'json_output' , help = 'Output report as json' , is_flag = True )
107
127
@click .option ('--debug' , help = 'Show error messages' , is_flag = True )
108
128
@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' )
109
130
@click .option ('--merge' , help = 'Merge custom file rules, on top of default rules' , is_flag = True )
110
131
@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 ):
112
133
"""
113
134
Scan a single http(s) endpoint with drheader.
114
135
115
136
NOTE: URL parameters are currently only supported on bulk scans.
116
137
"""
117
-
138
+ exit_code = EXIT_CODE_NO_ERROR
118
139
if debug :
119
140
logging .basicConfig (level = logging .DEBUG )
120
141
121
142
logging .debug ('Validating: {}' .format (target_url ))
122
143
if not validators .url (target_url ):
123
144
raise click .ClickException (message = '"{}" is not a valid URL.' .format (target_url ))
124
145
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
+
125
157
rules = load_rules (rule_file , merge )
126
158
127
159
try :
@@ -142,6 +174,9 @@ def single(target_url, json_output, debug, rule_file, merge, junit):
142
174
else :
143
175
raise click .ClickException ('Failed to analyze headers.' )
144
176
177
+ if drheader_instance .report :
178
+ exit_code = EXIT_CODE_FAILURE
179
+
145
180
if json_output :
146
181
click .echo (json .dumps (drheader_instance .report ))
147
182
else :
@@ -158,7 +193,7 @@ def single(target_url, json_output, debug, rule_file, merge, junit):
158
193
click .echo (tabulate (values , tablefmt = "presto" ))
159
194
if junit :
160
195
file_junit_report (rules , drheader_instance .report )
161
- return 0
196
+ sys . exit ( exit_code )
162
197
163
198
164
199
@scan .command ()
@@ -169,8 +204,9 @@ def single(target_url, json_output, debug, rule_file, merge, junit):
169
204
@click .option ('--json' , 'json_output' , help = 'Output report as json' , is_flag = True )
170
205
@click .option ('--debug' , help = 'Show error messages' , is_flag = True )
171
206
@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' )
172
208
@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 ):
174
210
"""
175
211
Scan multiple http(s) endpoints with drheader.
176
212
@@ -195,7 +231,7 @@ def bulk(file, json_output, post, input_format, debug, rule_file, merge):
195
231
196
232
NOTE: URL parameters are currently only supported on bulk scans.
197
233
"""
198
-
234
+ exit_code = EXIT_CODE_NO_ERROR
199
235
audit = []
200
236
urls = []
201
237
schema = {
@@ -235,6 +271,17 @@ def bulk(file, json_output, post, input_format, debug, rule_file, merge):
235
271
236
272
logging .debug ('Found {} URLs' .format (len (urls )))
237
273
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
+
238
285
rules = load_rules (rule_file , merge )
239
286
240
287
for i , v in enumerate (urls ):
@@ -243,9 +290,11 @@ def bulk(file, json_output, post, input_format, debug, rule_file, merge):
243
290
logging .debug ('Analysing: {}...' .format (v ))
244
291
drheader_instance .analyze (rules )
245
292
audit .append ({'url' : v ['url' ], 'report' : drheader_instance .report })
293
+ if drheader_instance .report :
294
+ exit_code = EXIT_CODE_FAILURE
246
295
247
296
echo_bulk_report (audit , json_output )
248
- return 0
297
+ sys . exit ( exit_code )
249
298
250
299
251
300
if __name__ == "__main__" :
0 commit comments