-
Notifications
You must be signed in to change notification settings - Fork 9
/
GraphQLParser.py
201 lines (153 loc) · 6.76 KB
/
GraphQLParser.py
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
from burp import IBurpExtender
from burp import IMessageEditorTabFactory
from burp import IMessageEditorTab
from burp import IScannerInsertionPointProvider
from burp import IScannerInsertionPoint
from burp import IParameter
import json
import queryProcess
class BurpExtender(IBurpExtender, IScannerInsertionPointProvider, IMessageEditorTabFactory):
##
#Main Class for Burp Extenders
def registerExtenderCallbacks(self, callbacks):
self._callbacks = callbacks
self._helpers = callbacks.getHelpers()
callbacks.setExtensionName("GraphQLParser")
callbacks.issueAlert("GQL Parser Started")
print "Graph Query(GQL) Parser Started!"
#Registering Graph Query Tab
callbacks.registerMessageEditorTabFactory(self)
#Registering IScannerInsertionPointProvider class Object
callbacks.registerScannerInsertionPointProvider(self)
return
# Define function to fetch Insertion Points
def getInsertionPoints(self, baseRequestResponse):
# get the parameter for insertion
dataParameter = self._helpers.getRequestParameter(baseRequestResponse.getRequest(), "data")
if (dataParameter is None):
return None
else:
# ad one insertion point at a time
return [InsertionPoint(self._helpers, baseRequestResponse.getRequest(), dataParameter.getValue())]
def createNewInstance(self, controller, editable):
return listGQLParameters(self, controller, editable)
class listGQLParameters(IMessageEditorTab):
def __init__(self, extender, controller, editable):
self._extender = extender
self._helpers = extender._helpers
self._editable = editable
self._txtInput = extender._callbacks.createTextEditor()
self._txtInput.setEditable(editable)
#Define Query Indicators To Identify a GQL
self._GQLIndicator = [
'[{"operationName"',
'{"operationName":',
'[{"query":"query ',
'{"query":"mutation',
'{"query":"',
'{"data":',
'[{"data":']
self._variable = 'variables": {'
return
#define Message Editor Properties for GQL Editor
def getTabCaption(self):
return "GraphQLParser"
def getUiComponent(self):
return self._txtInput.getComponent()
def isEnabled(self, content, isRequest):
isgql = False
if isRequest:
rBody = self._helpers.analyzeRequest(content)
else:
rBody = self._helpers.analyzeResponse(content)
message = content[rBody.getBodyOffset():].tostring()
for indicator in self._GQLIndicator:
if message.startswith(indicator):
isgql = True
if len(message) > 2 and isgql:
#print "Starting GQL parsing and gql Indicator found: %s"%message[:17]
return True
else:
var_pos = message.find(self._variable)
if len(message) > 2 and var_pos > 0:
#print "GQL Indicator found: %s" % message[var_pos:var_pos+17]
return True
return False
def setMessage(self, content, isRequest):
if content is None:
#Display Nothing for NoContent
self._txtInput.setText(None)
self._txtInput.setEditable(False)
else:
if isRequest:
rBody = self._helpers.analyzeRequest(content)
else:
rBody = self._helpers.analyzeResponse(content)
message = content[rBody.getBodyOffset():].tostring()
try:
limit = min(
message.index('{') if '{' in message else len(message),
message.index('[') if '[' in message else len(message)
)
except ValueError:
print "Sorry, this doesnt look like a Graph Query!"
print ValueError
return
garbage = message[:limit]
clean = message[limit:]
try:
gql_msg = garbage.strip() + '\n' + json.dumps(json.loads(clean), indent=4)
#gql_msg = re.sub(r'\\n', '\n', gql_msg)
except Exception:
print("Problem parsing the setMessage")
print(Exception)
gql_msg = garbage + clean
self._txtInput.setText(gql_msg)
self._txtInput.setEditable(self._editable)
self._currentMessage = content
return
def getMessage(self):
if self._txtInput.isTextModified():
try:
#self._manual = True
data = self._txtInput.getText()
except Exception:
print "Problem Getting the Message After Modification"
print Exception
# Update Request After Modification
r = self._helpers.analyzeRequest(self._currentMessage)
#return self._helpers.buildHttpMessage(r.getHeaders(), self._helpers.stringToBytes(data))
return self._helpers.buildHttpMessage(r.getHeaders(), data)
def isModified(self):
return self._txtInput.isTextModified()
def getSeletedData(self):
return self._txtInput.getSelectedText()
class InsertionPoint(IScannerInsertionPoint):
def __init__(self, helpers, baseRequest, dataParameter):
self._helpers = helpers
self._baseRequest = baseRequest
self.final_positions = []
dataParameter = helpers.bytesToString(dataParameter)
##Implement Query Process to get Insertion Points
request = queryProcess(dataParameter)
request.findInsertionPoints()
self.final_positions = request.findFinalPositions()
#Loop through to Create prefix and suffix for insertion Points
for ins_point in self.final_positions:
start = ins_point[0]
end = ins_point[1]
self._insertionPointPrefix = dataParameter[:start]
if (end == -1):
end = dataParameter.length()
self._baseValue = dataParameter[start:end]
self._insertionPointSuffix = dataParameter[end:]
return
def getInsertionPointName(self):
return self._baseValue
def buildRequest(self, payload):
input(self._insertionPointPrefix + self._helpers.bytesToString(payload) + self._insertionPointSuffix)
return self._helpers.updateParameter(self._baseRequest, self._helpers.buildParameter("data"), input, IParameter.PARAM_BODY)
def getPayloadOffsets(self, payload):
return None
def getInsertionPointType(self):
return INS_EXTENSION_PROVIDED