forked from pwntester/codeql.nvim
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsarif.lua
More file actions
283 lines (263 loc) · 8.93 KB
/
Copy pathsarif.lua
File metadata and controls
283 lines (263 loc) · 8.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
local util = require "codeql.util"
local config = require "codeql.config"
local vim = vim
local M = {}
-- process a SARIF file and returns a table of issues
-- each issue is a table with the following fields:
---- is_folded: true if the issue is folded in the panel
---- paths: list of paths
---- active_path: active path index
---- hidden: true if the issue is hidden
---- node: primary node of the actie path
---- query_id: the id of the query generating the issue
-- each path is a list of nodes where each node is a table with the following fields:
---- label: label of the node
---- mark: mark (bullet)
---- filename: filename of the node
---- line: line number of the node
---- visitable: whether the node points to a visitable location
---- url: location of the node
-- the url is a table with the following fields:
---- uri: path of the file
---- startLine: line number of the node
---- endLine: line number of the node
---- startColumn: start column number of the node
---- endColumn: end column number of the node
function M.process_sarif(opts)
if not util.is_file(opts.path) then
return
end
config.sarif.path = opts.path
local decoded = util.read_json_file(opts.path)
if not decoded then
return
end
-- TODO: handle multiple runs if thats even a thing
local results = decoded.runs[1].results
-- check if the SARIF file contains source code artifacts
local artifacts = decoded.runs[1].artifacts
if artifacts then
for _, artifact in ipairs(artifacts) do
if artifact.contents then
config.sarif.hasArtifacts = true
break
end
end
end
local versionControlProvenance
if decoded.runs[1].versionControlProvenance then
versionControlProvenance = decoded.runs[1].versionControlProvenance[1]
end
util.message("Sarif: " .. opts.path)
util.message("Results: " .. #results)
local issues = {}
for i, r in ipairs(results) do
local message = r.message.text
local query_id = r.ruleId
if r.codeFlows == nil then
-- results with NO codeFlows
local nodes = {}
local locations = {}
--- location relevant to understanding the result.
if r.relatedLocations then
for _, v in ipairs(r.relatedLocations) do
table.insert(locations, v)
end
end
--- location where the result occurred
if r.locations then
for _, v in ipairs(r.locations) do
table.insert(locations, v)
end
end
for j, l in ipairs(locations) do
local label, mark
if l.message then
label = l.message.text or message
else
label = message
end
if #r.locations == j then
mark = "⦿"
else
mark = "→"
end
local uri = l.physicalLocation.artifactLocation.uri
local uriBaseId = l.physicalLocation.artifactLocation.uriBaseId
if uriBaseId then
uri = string.format("file:%s/%s", uriBaseId, uri)
end
local region = l.physicalLocation.region
local node = {
label = label,
mark = mark,
filename = util.uri_to_fname(uri) or uri,
line = region and region.startLine or -1,
visitable = region or false,
versionControlProvenance = versionControlProvenance,
url = {
uri = uri,
startLine = region and region.startLine or -1,
endLine = region and region.endLine or region.startLine,
startColumn = region and region.startColumn or -1,
endColumn = region and region.endColumn or -1,
},
}
-- check if the SARIF file contains source code snippets
if l.physicalLocation.contextRegion and l.physicalLocation.contextRegion.snippet then
config.sarif.hasSnippets = true
node.contextRegion = l.physicalLocation.contextRegion
end
table.insert(nodes, node)
end
-- create issue
local primary_node = nodes[#nodes]
local issue = {
is_folded = true,
paths = { nodes },
active_path = 1,
hidden = false,
node = primary_node,
query_id = query_id,
}
table.insert(issues, issue)
else
-- each result contains a codeflow that groups a source
local paths = {}
for _, c in ipairs(r.codeFlows) do
for _, t in ipairs(c.threadFlows) do
-- each threadFlow contains all reached sinks for
-- codeFlow source
-- we can treat a threadFlow as a "regular" dataflow
-- first element is source, last one is sink
local nodes = {}
for j, l in ipairs(t.locations) do
local mark
if 1 == j then
mark = "⭃"
elseif #t.locations == i then
mark = "⦿"
else
mark = "→"
end
local uri = l.location.physicalLocation.artifactLocation.uri
local uriBaseId = l.location.physicalLocation.artifactLocation.uriBaseId
if uriBaseId then
uri = string.format("file:%s/%s", uriBaseId, uri)
end
local region = l.location.physicalLocation.region
if region and region.startLine and not region.endLine then
region.endLine = region.startLine
end
local node
if region then
node = {
label = l.location.message.text,
mark = mark,
filename = util.uri_to_fname(uri) or uri,
line = region.startLine,
visitable = true,
versionControlProvenance = versionControlProvenance,
url = {
uri = uri,
startLine = region.startLine,
endLine = region.endLine,
startColumn = region.startColumn,
endColumn = region.endColumn,
},
}
else
node = {
label = l.location.message.text,
mark = mark,
filename = util.uri_to_fname(uri) or uri,
line = -1,
visitable = false,
versionControlProvenance = versionControlProvenance,
url = {
uri = uri,
startLine = -1,
endLine = -1,
startColumn = -1,
endColumn = -1,
},
}
end
-- check if the SARIF file contains source code snippets
if l.location.physicalLocation.contextRegion and l.location.physicalLocation.contextRegion.snippet then
config.sarif.hasSnippets = true
node.contextRegion = l.location.physicalLocation.contextRegion
end
table.insert(nodes, node)
end
-- group code flows with same source and sink
-- into a single issue with different paths
if not opts.max_length or opts.max_length == -1 or #nodes <= opts.max_length then
local source = nodes[1]
local sink = nodes[#nodes]
local source_key = source.filename
.. "::"
.. source.url.startLine
.. "::"
.. source.url.endLine
.. "::"
.. source.url.startColumn
.. "::"
.. source.url.endColumn
local sink_key = sink.filename
.. "::"
.. sink.url.startLine
.. "::"
.. sink.url.endLine
.. "::"
.. sink.url.startColumn
.. "::"
.. sink.url.endColumn
local key = source_key .. "::" .. sink_key
if not paths[key] then
paths[key] = {}
end
local _path = paths[key]
local message_node = {
label = string.gsub(string.gsub(r.message.text, "\n", " "), "\\", ""),
mark = "≔",
filename = nil,
line = nil,
visitable = false,
url = nil,
}
table.insert(nodes, message_node)
table.insert(_path, nodes)
paths[key] = _path
end
end
end
-- create issue
--- issue label
for _, p in pairs(paths) do
local primary_node
if opts.group_by == "sink" then
-- last node is the message node, so sink is #nodes - 1
primary_node = p[1][#p[1] - 1]
elseif opts.panel.group_by == "source" then
-- first node is the message node, so source is 1
primary_node = p[1][1]
else
-- default to source
primary_node = p[1][1]
end
local issue = {
is_folded = true,
paths = p,
active_path = 1,
hidden = false,
node = primary_node,
query_id = query_id,
}
table.insert(issues, issue)
end
end
end
return issues
end
return M