1
1
# -*- coding: utf_8 -*-
2
- """Sarif output format .
2
+ """SARIF output formatter for MobSF scan results .
3
3
4
- Based on https://github.com/microsoft/bandit-sarif-formatter/
5
- blob/master/bandit_sarif_formatter/formatter.py
4
+ Based on https://github.com/microsoft/
5
+ bandit-sarif-formatter/blob/master/
6
+ bandit_sarif_formatter/formatter.py
7
+ MIT License, Copyright (c) Microsoft Corporation.
6
8
7
- Copyright (c) Microsoft. All Rights Reserved.
8
- MIT License
9
-
10
- Copyright (c) Microsoft Corporation.
11
-
12
- Permission is hereby granted, free of charge, to any person obtaining a copy
13
- of this software and associated documentation files (the "Software"), to deal
14
- in the Software without restriction, including without limitation the rights
15
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
- copies of the Software, and to permit persons to whom the Software is
17
- furnished to do so, subject to the following conditions:
18
-
19
- The above copyright notice and this permission notice shall be included in all
20
- copies or substantial portions of the Software.
21
-
22
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
- SOFTWARE
29
9
"""
30
- from datetime import datetime
10
+ from datetime import datetime , timezone
31
11
from pathlib import PurePath
32
12
import urllib .parse as urlparse
33
13
34
14
import sarif_om as om
35
15
36
16
from jschema_to_python .to_json import to_json
37
17
38
-
39
18
TS_FORMAT = '%Y-%m-%dT%H:%M:%SZ'
40
19
41
20
42
21
def level_from_severity (severity ):
43
- if severity == 'ERROR' :
44
- return 'error'
45
- elif severity == 'WARNING' :
46
- return 'warning'
47
- elif severity == 'INFO' :
48
- return 'note'
49
- else :
50
- return 'none'
22
+ return {
23
+ 'ERROR' : 'error' ,
24
+ 'WARNING' : 'warning' ,
25
+ 'INFO' : 'note' ,
26
+ }.get (severity , 'none' )
51
27
52
28
53
29
def to_uri (file_path ):
54
30
pure_path = PurePath (file_path )
55
31
if pure_path .is_absolute ():
56
32
return pure_path .as_uri ()
57
33
else :
58
- posix_path = pure_path .as_posix () # Replace backslashes with slashes.
59
- return urlparse .quote (posix_path ) # %-encode special characters.
34
+ return urlparse .quote (pure_path .as_posix ())
60
35
61
36
62
- def get_rule_name (rule_id ):
63
- normalized = []
64
- noms = rule_id .split ('_' )
65
- for nom in noms :
66
- normalized .append (nom .capitalize ())
67
- return '' .join (normalized )
37
+ def format_rule_name (rule_id ):
38
+ return '' .join (word .capitalize () for word in rule_id .split ('_' ))
68
39
69
40
70
41
def add_results (path , scan_results , run ):
71
42
if run .results is None :
72
43
run .results = []
73
- res = {}
74
- res .update (scan_results .get ('results' , []))
44
+ res = scan_results .get ('results' , {})
75
45
rules = {}
76
46
rule_indices = {}
77
47
78
48
for rule_id , issue_dict in res .items ():
79
- result = create_result (path , rule_id , issue_dict , rules , rule_indices )
80
- run .results .append (result )
49
+ rule_results = create_rule_results (
50
+ path , rule_id , issue_dict , rules , rule_indices )
51
+ run .results .extend (rule_results )
81
52
82
- if len ( rules ) > 0 :
53
+ if rules :
83
54
run .tool .driver .rules = list (rules .values ())
84
55
85
56
86
- def create_result (path , rule_id , issue_dict , rules , rule_indices ):
87
- if rule_id in rules :
88
- rule = rules [rule_id ]
89
- rule_index = rule_indices [rule_id ]
90
- else :
91
- doc = issue_dict ['metadata' ].get ('reference' )
92
- if not doc :
93
- doc = ('https://mobile-security.gitbook.io/'
94
- 'mobile-security-testing-guide/' )
57
+ def create_rule_results (path , rule_id , issue_dict , rules , rule_indices ):
58
+ rule_results = []
59
+ rule , rule_index = rules .get (rule_id ), rule_indices .get (rule_id )
60
+ ref_url = ('https://mobile-security.gitbook.io/'
61
+ 'mobile-security-testing-guide/' )
62
+ if not rule :
63
+ doc = issue_dict ['metadata' ].get ('reference' ) or ref_url
95
64
cwe_id = issue_dict ['metadata' ]['cwe' ].split (':' )[0 ].lower ()
96
65
rule = om .ReportingDescriptor (
97
66
id = rule_id ,
98
- name = get_rule_name (rule_id ),
67
+ name = format_rule_name (rule_id ),
99
68
help_uri = doc ,
100
- properties = {
101
- 'tags' : ['security' , f'external/cwe/{ cwe_id } ' ],
102
- },
103
- )
69
+ properties = {'tags' : ['security' , f'external/cwe/{ cwe_id } ' ]})
104
70
rule_index = len (rules )
105
71
rules [rule_id ] = rule
106
72
rule_indices [rule_id ] = rule_index
107
73
108
- locations = []
109
74
for item in issue_dict .get ('files' , []):
110
- physical_location = om .PhysicalLocation (
111
- artifact_location = om .ArtifactLocation (
112
- uri = to_uri (item ['file_path' ])),
113
- )
114
- physical_location .region = om .Region (
115
- start_line = item ['match_lines' ][0 ],
116
- end_line = item ['match_lines' ][1 ],
117
- start_column = item ['match_position' ][0 ],
118
- end_column = item ['match_position' ][1 ],
119
- snippet = om .ArtifactContent (text = item ['match_string' ]),
120
- )
121
- locations .append (om .Location (physical_location = physical_location ))
122
- if not locations :
123
- artifact = om .PhysicalLocation (
124
- artifact_location = om .ArtifactLocation (
125
- uri = path [0 ]),
126
- )
127
- artifact .region = om .Region (
128
- start_line = 1 ,
129
- end_line = 1 ,
130
- start_column = 1 ,
131
- end_column = 1 ,
132
- snippet = om .ArtifactContent (text = 'Missing Best Practice' ),
133
- )
134
- locations .append (om .Location (physical_location = artifact ))
135
-
75
+ location = create_location (item )
76
+ rule_results .append (create_result (rule , rule_index , issue_dict , [location ]))
77
+
78
+ if not issue_dict .get ('files' ):
79
+ default_location = om .Location (
80
+ physical_location = om .PhysicalLocation (
81
+ artifact_location = om .ArtifactLocation (uri = path [0 ]),
82
+ region = om .Region (
83
+ start_line = 1 ,
84
+ end_line = 1 ,
85
+ start_column = 1 ,
86
+ end_column = 1 ,
87
+ snippet = om .ArtifactContent (text = 'Missing Best Practice' ))))
88
+ rule_results .append (create_result (
89
+ rule , rule_index , issue_dict , [default_location ]))
90
+
91
+ return rule_results
92
+
93
+
94
+ def create_location (item ):
95
+ return om .Location (
96
+ physical_location = om .PhysicalLocation (
97
+ artifact_location = om .ArtifactLocation (uri = to_uri (item ['file_path' ])),
98
+ region = om .Region (
99
+ start_line = item ['match_lines' ][0 ],
100
+ end_line = item ['match_lines' ][1 ],
101
+ start_column = item ['match_position' ][0 ],
102
+ end_column = item ['match_position' ][1 ],
103
+ snippet = om .ArtifactContent (text = item ['match_string' ]))))
104
+
105
+
106
+ def create_result (rule , rule_index , issue_dict , locations ):
136
107
return om .Result (
137
108
rule_id = rule .id ,
138
109
rule_index = rule_index ,
@@ -144,38 +115,34 @@ def create_result(path, rule_id, issue_dict, rules, rule_indices):
144
115
'masvs' : issue_dict ['metadata' ]['masvs' ],
145
116
'cwe' : issue_dict ['metadata' ]['cwe' ],
146
117
'reference' : issue_dict ['metadata' ]['reference' ],
147
- },
148
- )
118
+ })
149
119
150
120
151
121
def sarif_output (outfile , scan_results , mobsfscan_version , path ):
152
122
log = om .SarifLog (
153
- schema_uri = ('https://raw.githubusercontent.com/oasis-tcs/'
154
- 'sarif-spec/master/Schemata/sarif-schema-2.1.0.json' ),
123
+ schema_uri = ('https://raw.githubusercontent.com/'
124
+ 'oasis-tcs/sarif-spec/master/Schemata/'
125
+ 'sarif-schema-2.1.0.json' ),
155
126
version = '2.1.0' ,
156
- runs = [
157
- om .Run (
158
- tool = om .Tool (driver = om .ToolComponent (
159
- name = 'mobsfscan' ,
160
- information_uri = 'https://github.com/MobSF/mobsfscan' ,
161
- semantic_version = mobsfscan_version ,
162
- version = mobsfscan_version ),
163
- ),
164
- invocations = [
165
- om .Invocation (
166
- end_time_utc = datetime .utcnow ().strftime (TS_FORMAT ),
167
- execution_successful = True ,
168
- ),
169
- ],
170
- ),
171
- ],
172
- )
127
+ runs = [om .Run (
128
+ tool = om .Tool (driver = om .ToolComponent (
129
+ name = 'mobsfscan' ,
130
+ information_uri = 'https://github.com/MobSF/mobsfscan' ,
131
+ semantic_version = mobsfscan_version ,
132
+ version = mobsfscan_version ,
133
+ )),
134
+ invocations = [om .Invocation (
135
+ end_time_utc = datetime .now (timezone .utc ).strftime (TS_FORMAT ),
136
+ execution_successful = True ,
137
+ )])])
173
138
run = log .runs [0 ]
174
139
add_results (path , scan_results , run )
175
140
json_out = to_json (log )
141
+
176
142
if outfile :
177
143
with open (outfile , 'w' ) as of :
178
144
of .write (json_out )
179
145
else :
180
146
print (json_out )
147
+
181
148
return json_out
0 commit comments