fire-marshal-ebay 0.0.1-security.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fire-marshal-ebay might be problematic. Click here for more details.
- package/PadBuster/LICENSE +202 -0
- package/PadBuster/README +16 -0
- package/PadBuster/padBuster.pl +889 -0
- package/confused/.github/workflows/codeql-analysis.yml +67 -0
- package/confused/.github/workflows/golangci-lint.yml +28 -0
- package/confused/.goreleaser.yml +40 -0
- package/confused/CHANGELOG.md +31 -0
- package/confused/LICENSE +21 -0
- package/confused/README.md +93 -0
- package/confused/composer.go +105 -0
- package/confused/confused +0 -0
- package/confused/interfaces.go +11 -0
- package/confused/main.go +104 -0
- package/confused/mvn.go +120 -0
- package/confused/mvnparser.go +139 -0
- package/confused/npm.go +210 -0
- package/confused/packages.json +86 -0
- package/confused/pip.go +99 -0
- package/confused/util.go +11 -0
- package/index.js +47 -0
- package/package.json +9 -4
- package/synackAPI/Dockerfile +36 -0
- package/synackAPI/README.md +238 -0
- package/synackAPI/RHINOSPIDER/burpOOS.txt +25 -0
- package/synackAPI/RHINOSPIDER/burpScope.txt +1 -0
- package/synackAPI/RHINOSPIDER/scope.txt +1 -0
- package/synackAPI/bot.py +72 -0
- package/synackAPI/checkCerts.py +67 -0
- package/synackAPI/connect.py +9 -0
- package/synackAPI/currentTarget +24 -0
- package/synackAPI/getAnalytics.py +40 -0
- package/synackAPI/getHydra.py +46 -0
- package/synackAPI/getPayouts.py +11 -0
- package/synackAPI/getscope.py +123 -0
- package/synackAPI/polling.py +27 -0
- package/synackAPI/register.py +7 -0
- package/synackAPI/requirements.txt +7 -0
- package/synackAPI/synack.py +1046 -0
- package/synackAPI/synstats.py +54 -0
- package/synackAPI/target.py +17 -0
- package/README.md +0 -5
@@ -0,0 +1,1046 @@
|
|
1
|
+
import ipaddress
|
2
|
+
from netaddr import IPNetwork
|
3
|
+
import requests
|
4
|
+
import re
|
5
|
+
import os
|
6
|
+
import json
|
7
|
+
import base64
|
8
|
+
from pathlib import Path
|
9
|
+
import warnings
|
10
|
+
import operator
|
11
|
+
import sys
|
12
|
+
import datetime
|
13
|
+
from selenium import webdriver
|
14
|
+
from selenium.webdriver.common.by import By
|
15
|
+
from selenium.webdriver.common.keys import Keys
|
16
|
+
from selenium.webdriver.support.ui import WebDriverWait
|
17
|
+
from selenium.webdriver.support import expected_conditions as EC
|
18
|
+
from selenium.webdriver.firefox.options import Options
|
19
|
+
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
20
|
+
import configparser
|
21
|
+
import time
|
22
|
+
import pyotp
|
23
|
+
from urllib.parse import urlparse
|
24
|
+
|
25
|
+
warnings.filterwarnings("ignore")
|
26
|
+
|
27
|
+
class synack:
|
28
|
+
codename = None
|
29
|
+
def __init__(self):
|
30
|
+
self.session = requests.Session()
|
31
|
+
self.jsonResponse = []
|
32
|
+
self.assessments = []
|
33
|
+
self.token = ""
|
34
|
+
self.notificationToken = ""
|
35
|
+
self.url_registered_summary = "https://platform.synack.com/api/targets/registered_summary"
|
36
|
+
self.url_scope_summary = "https://platform.synack.com/api/targets/"
|
37
|
+
self.url_activate_target = "https://platform.synack.com/api/launchpoint"
|
38
|
+
self.url_assessments = "https://platform.synack.com/api/assessments"
|
39
|
+
self.url_vulnerabilities = "https://platform.synack.com/api/vulnerabilities"
|
40
|
+
self.url_drafts = "https://platform.synack.com/api/drafts"
|
41
|
+
self.url_unregistered_slugs = "https://platform.synack.com/api/targets?filter%5Bprimary%5D=unregistered&filter%5Bsecondary%5D=all&filter%5Bcategory%5D=all&sorting%5Bfield%5D=dateUpdated&sorting%5Bdirection%5D=desc&pagination%5Bpage%5D="
|
42
|
+
self.url_profile = "https://platform.synack.com/api/profiles/me"
|
43
|
+
self.url_analytics = "https://platform.synack.com/api/listing_analytics/categories?listing_id="
|
44
|
+
self.url_hydra = "https://platform.synack.com/api/hydra_search/search/"
|
45
|
+
self.url_published_missions = "https://platform.synack.com/api/tasks/v1/tasks?status=PUBLISHED"
|
46
|
+
self.url_logout = "https://platform.synack.com/api/logout"
|
47
|
+
self.url_notification_token = "https://platform.synack.com/api/users/notifications_token"
|
48
|
+
self.url_notification_api = "https://notifications.synack.com/api/v2/"
|
49
|
+
self.url_transactions = "https://platform.synack.com/api/transactions"
|
50
|
+
self.url_lp_credentials = "https://platform.synack.com/api/launchpoint/credentials"
|
51
|
+
self.webheaders = {}
|
52
|
+
self.configFile = str(Path.home())+"/.synack/synack.conf"
|
53
|
+
self.firefoxProfile = str(Path.home())+"/.synack/selenium.profile"
|
54
|
+
self.config = configparser.ConfigParser()
|
55
|
+
self.config.read(self.configFile)
|
56
|
+
self.email = self.config['DEFAULT']['email']
|
57
|
+
self.password = self.config['DEFAULT']['password']
|
58
|
+
self.login_wait = int(self.config['DEFAULT']['login_wait'])
|
59
|
+
self.login_url = self.config['DEFAULT']['login_url']
|
60
|
+
self.authySecret = self.config['DEFAULT']['authy_secret']
|
61
|
+
self.sessionTokenPath = self.config['DEFAULT'].get('session_token_path',"/tmp/synacktoken")
|
62
|
+
self.notificationTokenPath = self.config['DEFAULT'].get('notification_token_path',"/tmp/notificationtoken")
|
63
|
+
self.connector = False
|
64
|
+
self.webdriver = None
|
65
|
+
self.headless = False
|
66
|
+
# set to false to use the requests-based login
|
67
|
+
self.gecko = self.config['DEFAULT'].getboolean('gecko',True)
|
68
|
+
self.proxyport = self.config['DEFAULT'].getint('proxyport',8080)
|
69
|
+
|
70
|
+
## Set to 'True' for troubleshooting with Burp Suite ##
|
71
|
+
self.Proxy = self.config['DEFAULT'].getboolean('proxy',False)
|
72
|
+
|
73
|
+
#########
|
74
|
+
def getAuthy(self):
|
75
|
+
totp = pyotp.TOTP(self.authySecret)
|
76
|
+
totp.digits = 7
|
77
|
+
totp.interval = 10
|
78
|
+
totp.issuer = "synack"
|
79
|
+
return(totp.now())
|
80
|
+
|
81
|
+
## Get Synack platform session token ##
|
82
|
+
def getSessionToken(self):
|
83
|
+
if Path(self.sessionTokenPath).exists():
|
84
|
+
with open(self.sessionTokenPath, "r") as f:
|
85
|
+
self.token = f.readline()
|
86
|
+
f.close()
|
87
|
+
else:
|
88
|
+
self.connectToPlatform()
|
89
|
+
self.webheaders = {"Authorization": "Bearer " + self.token}
|
90
|
+
response = self.try_requests("GET", self.url_profile, 10)
|
91
|
+
profile = response.json()
|
92
|
+
self.webheaders['user_id'] = profile['user_id']
|
93
|
+
|
94
|
+
#################################################
|
95
|
+
## Function to attempt requests multiple times ##
|
96
|
+
#################################################
|
97
|
+
|
98
|
+
def try_requests(self, func, URL, times, extra=None):
|
99
|
+
http_proxy = "http://127.0.0.1:%d" % self.proxyport
|
100
|
+
https_proxy = "http://127.0.0.1:%d" % self.proxyport
|
101
|
+
proxyDict = {
|
102
|
+
"http" : http_proxy,
|
103
|
+
"https" : https_proxy
|
104
|
+
}
|
105
|
+
|
106
|
+
url = urlparse(URL)
|
107
|
+
scheme = url.scheme
|
108
|
+
netloc = url.netloc
|
109
|
+
path = url.path
|
110
|
+
port = url.port
|
111
|
+
platform = "platform.synack"
|
112
|
+
|
113
|
+
if self.Proxy == True:
|
114
|
+
for attempt in range(times):
|
115
|
+
try:
|
116
|
+
if func == "PUT":
|
117
|
+
putData = json.dumps({"listing_id": extra})
|
118
|
+
newHeaders = dict(self.webheaders)
|
119
|
+
newHeaders['Content-Type'] = "application/json"
|
120
|
+
response = self.session.put(URL, headers=newHeaders, data=putData, proxies=proxyDict, verify=False)
|
121
|
+
if response.status_code == 401 and platform in netloc:
|
122
|
+
self.connectToPlatform()
|
123
|
+
self.getSessionToken()
|
124
|
+
else:
|
125
|
+
return response
|
126
|
+
elif func == "GET":
|
127
|
+
if extra == None:
|
128
|
+
response = self.session.get(URL, headers=self.webheaders, proxies=proxyDict, verify=False)
|
129
|
+
if response.status_code == 401 and platform in netloc:
|
130
|
+
self.connectToPlatform()
|
131
|
+
self.getSessionToken()
|
132
|
+
else:
|
133
|
+
return response
|
134
|
+
else:
|
135
|
+
parameters = {'page': extra}
|
136
|
+
response = self.session.get(URL, headers=self.webheaders, params=parameters, proxies=proxyDict, verify=False)
|
137
|
+
if response.status_code == 401 and platform in netloc:
|
138
|
+
self.connectToPlatform()
|
139
|
+
self.getSessionToken()
|
140
|
+
else:
|
141
|
+
return response
|
142
|
+
elif func == "POST":
|
143
|
+
response = self.session.post(URL, headers=self.webheaders, proxies=proxyDict, json=extra, verify=False)
|
144
|
+
if response.status_code == 401 and platform in netloc:
|
145
|
+
self.connectToPlatform()
|
146
|
+
self.getSessionToken()
|
147
|
+
else:
|
148
|
+
return response
|
149
|
+
elif func == "PATCH":
|
150
|
+
newHeaders = dict(self.webheaders)
|
151
|
+
newHeaders['Content-Type'] = "application/json"
|
152
|
+
# PATCH request does not support `json=` parameter
|
153
|
+
response = self.session.patch(URL, headers=newHeaders, proxies=proxyDict, data=extra, verify=False)
|
154
|
+
if response.status_code == 401 and platform in netloc:
|
155
|
+
self.connectToPlatform()
|
156
|
+
self.getSessionToken()
|
157
|
+
else:
|
158
|
+
return response
|
159
|
+
elif func == "DELETE":
|
160
|
+
response = self.session.delete(URL, headers=self.webheaders, proxies=proxyDict, json=extra, verify=False)
|
161
|
+
if response.status_code == 401 and platform in netloc:
|
162
|
+
self.connectToPlatform()
|
163
|
+
self.getSessionToken()
|
164
|
+
else:
|
165
|
+
return response
|
166
|
+
except Exception as err:
|
167
|
+
last_err = err
|
168
|
+
raise last_err
|
169
|
+
else:
|
170
|
+
for attempt in range(times):
|
171
|
+
try:
|
172
|
+
if func == "PUT":
|
173
|
+
putData = json.dumps({"listing_id": extra})
|
174
|
+
newHeaders = dict(self.webheaders)
|
175
|
+
newHeaders['Content-Type'] = "application/json"
|
176
|
+
response =self.session.put(URL, headers=newHeaders, data=putData, verify=False)
|
177
|
+
if response.status_code == 401 and platform in netloc:
|
178
|
+
self.connectToPlatform()
|
179
|
+
self.getSessionToken()
|
180
|
+
else:
|
181
|
+
return response
|
182
|
+
elif func == "GET":
|
183
|
+
if extra == None:
|
184
|
+
response =self.session.get(URL, headers=self.webheaders, verify=False)
|
185
|
+
if response.status_code == 401 and platform in netloc:
|
186
|
+
self.connectToPlatform()
|
187
|
+
self.getSessionToken()
|
188
|
+
else:
|
189
|
+
return response
|
190
|
+
else:
|
191
|
+
parameters = {'page': extra}
|
192
|
+
response = self.session.get(URL, headers=self.webheaders, params=parameters, verify=False)
|
193
|
+
if response.status_code == 401 and platform in netloc:
|
194
|
+
self.connectToPlatform()
|
195
|
+
self.getSessionToken()
|
196
|
+
else:
|
197
|
+
return response
|
198
|
+
elif func == "POST":
|
199
|
+
response = self.session.post(URL, headers=self.webheaders, json=extra, verify=False)
|
200
|
+
if response.status_code == 401 and platform in netloc:
|
201
|
+
self.connectToPlatform()
|
202
|
+
self.getSessionToken()
|
203
|
+
else:
|
204
|
+
return response
|
205
|
+
elif func == "PATCH":
|
206
|
+
# PATCH request does not support `json=` parameter
|
207
|
+
newHeaders = dict(self.webheaders)
|
208
|
+
newHeaders['Content-Type'] = "application/json"
|
209
|
+
response = self.session.request("PATCH", URL, headers=newHeaders, data=extra, verify=False)
|
210
|
+
if response.status_code == 401 and platform in netloc:
|
211
|
+
self.connectToPlatform()
|
212
|
+
self.getSessionToken()
|
213
|
+
else:
|
214
|
+
return response
|
215
|
+
elif func == "DELETE":
|
216
|
+
response = self.session.delete(URL, headers=self.webheaders, json=extra, verify=False)
|
217
|
+
if response.status_code == 401 and platform in netloc:
|
218
|
+
self.connectToPlatform()
|
219
|
+
self.getSessionToken()
|
220
|
+
else:
|
221
|
+
return response
|
222
|
+
else:
|
223
|
+
raise ValueError("Choose a real HTTP method.")
|
224
|
+
except Exception as err:
|
225
|
+
last_err = err
|
226
|
+
raise last_err
|
227
|
+
|
228
|
+
|
229
|
+
####################################################
|
230
|
+
## Function to find all occurrences of nested key ##
|
231
|
+
####################################################
|
232
|
+
|
233
|
+
def findkeys(self, node, kv):
|
234
|
+
if isinstance(node, list):
|
235
|
+
for i in node:
|
236
|
+
for x in self.findkeys(i, kv):
|
237
|
+
yield x
|
238
|
+
elif isinstance(node, dict):
|
239
|
+
if kv in node:
|
240
|
+
yield node[kv]
|
241
|
+
for j in node.values():
|
242
|
+
for x in self.findkeys(j, kv):
|
243
|
+
yield x
|
244
|
+
|
245
|
+
##############################################
|
246
|
+
## Returns a JSON of all registered targets ##
|
247
|
+
## This must be the first call after object ##
|
248
|
+
## instantiation - it populates the json ##
|
249
|
+
##############################################
|
250
|
+
def getAllTargets(self):
|
251
|
+
self.jsonResponse.clear()
|
252
|
+
response = self.try_requests("GET", self.url_registered_summary, 10)
|
253
|
+
self.jsonResponse[:] = response.json()
|
254
|
+
return(response.status_code)
|
255
|
+
|
256
|
+
|
257
|
+
########################################
|
258
|
+
## Returns a list of web or host target codenames
|
259
|
+
## that are (mission only / not mission only)
|
260
|
+
## category: web || host || RE || mobile || sourceCode || hardware
|
261
|
+
## mission_only: True || False
|
262
|
+
########################################
|
263
|
+
def getCodenames(self, category, mission_only=False):
|
264
|
+
categories = ("web application", "re", "mobile", "host", "source code","hardware")
|
265
|
+
category = category.lower()
|
266
|
+
if category == "web":
|
267
|
+
category = "web application"
|
268
|
+
if category == "re":
|
269
|
+
category = "reverse engineering"
|
270
|
+
if category == "sourcecode":
|
271
|
+
category = "source code"
|
272
|
+
if category not in categories:
|
273
|
+
raise Exception("Invalid category.")
|
274
|
+
targets = []
|
275
|
+
for i in range (len(self.jsonResponse)):
|
276
|
+
if mission_only == True:
|
277
|
+
if self.jsonResponse[i]['vulnerability_discovery'] == False:
|
278
|
+
if self.jsonResponse[i]['category']['name'].lower() == category.lower():
|
279
|
+
targets.append(self.jsonResponse[i]['codename'])
|
280
|
+
else:
|
281
|
+
continue
|
282
|
+
else:
|
283
|
+
continue
|
284
|
+
elif mission_only == False:
|
285
|
+
if self.jsonResponse[i]['vulnerability_discovery'] == True:
|
286
|
+
if self.jsonResponse[i]['category']['name'].lower() == category.lower():
|
287
|
+
targets.append(self.jsonResponse[i]['codename'])
|
288
|
+
else:
|
289
|
+
continue
|
290
|
+
else:
|
291
|
+
continue
|
292
|
+
return(targets)
|
293
|
+
|
294
|
+
#########################################
|
295
|
+
## This returns the "slug" of a target ##
|
296
|
+
## based on the codename ##
|
297
|
+
#########################################
|
298
|
+
|
299
|
+
def getTargetID(self, codename):
|
300
|
+
for i in range (len(self.jsonResponse)):
|
301
|
+
if self.jsonResponse[i]['codename'].lower() == codename.lower():
|
302
|
+
return(self.jsonResponse[i]['id'])
|
303
|
+
|
304
|
+
##################################
|
305
|
+
## This retuens the codemane of ##
|
306
|
+
## a target based on the slug ##
|
307
|
+
##################################
|
308
|
+
def getCodenameFromSlug(self, slug):
|
309
|
+
for i in range(len(self.jsonResponse)):
|
310
|
+
if self.jsonResponse[i]['id'].lower() == slug.lower():
|
311
|
+
return(self.jsonResponse[i]['codename'])
|
312
|
+
|
313
|
+
#################################
|
314
|
+
## This private method returns ##
|
315
|
+
## the organization ID ##
|
316
|
+
#################################
|
317
|
+
def __getOrgID(self, codename):
|
318
|
+
for i in range (len(self.jsonResponse)):
|
319
|
+
if self.jsonResponse[i]['codename'].lower() == codename.lower():
|
320
|
+
return(self.jsonResponse[i]['organization_id'])
|
321
|
+
|
322
|
+
######################################
|
323
|
+
## This returns the target category ##
|
324
|
+
######################################
|
325
|
+
def getCategory(self, codename):
|
326
|
+
for i in range (len(self.jsonResponse)):
|
327
|
+
if self.jsonResponse[i]['codename'].lower() == codename.lower():
|
328
|
+
return(self.jsonResponse[i]['category']['name'])
|
329
|
+
|
330
|
+
#####################################################
|
331
|
+
## This will connect you to the target by codename ##
|
332
|
+
#####################################################
|
333
|
+
def connectToTarget(self, codename):
|
334
|
+
slug = self.getTargetID(codename)
|
335
|
+
response = self.try_requests("PUT", self.url_activate_target, 10, slug)
|
336
|
+
time.sleep(5)
|
337
|
+
return response.status_code
|
338
|
+
|
339
|
+
########################################################
|
340
|
+
## This just returns the "real" client name sometimes ##
|
341
|
+
########################################################
|
342
|
+
def clientName(self, codename):
|
343
|
+
for i in range (len(self.jsonResponse)):
|
344
|
+
if self.jsonResponse[i]['codename'].lower() == codename.lower():
|
345
|
+
return(self.jsonResponse[i]['name'])
|
346
|
+
|
347
|
+
|
348
|
+
################################
|
349
|
+
## This gets the target scope ##
|
350
|
+
################################
|
351
|
+
|
352
|
+
def getScope(self, codename):
|
353
|
+
category = self.getCategory(codename)
|
354
|
+
orgID = self.__getOrgID(codename)
|
355
|
+
slug = self.getTargetID(codename)
|
356
|
+
if category.lower() == "web application":
|
357
|
+
scopeURL = "https://platform.synack.com/api/asset/v1/organizations/"+orgID+"/owners/listings/"+slug+"/webapps"
|
358
|
+
allRules = []
|
359
|
+
oosRules = []
|
360
|
+
response = self.try_requests("GET", scopeURL, 10)
|
361
|
+
jsonResponse = response.json()
|
362
|
+
j = 0
|
363
|
+
while j < len(jsonResponse):
|
364
|
+
if jsonResponse[j]['status'] in ["out","tbd"]:
|
365
|
+
tmpOOS = set()
|
366
|
+
for thisRule in range(len(jsonResponse[j]['rules'])):
|
367
|
+
url = urlparse(jsonResponse[j]['rules'][thisRule]['rule'])
|
368
|
+
scheme = url.scheme
|
369
|
+
netloc = url.netloc
|
370
|
+
path = url.path
|
371
|
+
port = url.port
|
372
|
+
wildcard = False
|
373
|
+
if len(netloc) != 0:
|
374
|
+
subdomain = netloc.split('.')[0]
|
375
|
+
if subdomain == "*":
|
376
|
+
wildcard = True
|
377
|
+
netloc = ".".join(netloc.split('.')[1:])
|
378
|
+
else:
|
379
|
+
if len(path) != 0:
|
380
|
+
netloc = path.split('/')[0]
|
381
|
+
checkWildcard = netloc.split('.')[0]
|
382
|
+
if checkWildcard == "*":
|
383
|
+
wildcard = True
|
384
|
+
if ":" in netloc:
|
385
|
+
port = netloc.split(':')[1]
|
386
|
+
thisURL = netloc.split(':')[0]
|
387
|
+
netloc = ".".join(thisURL.split('.')[1:])
|
388
|
+
else:
|
389
|
+
port = 443
|
390
|
+
netloc = ".".join(netloc.split('.')[1:])
|
391
|
+
else:
|
392
|
+
if ":" in netloc:
|
393
|
+
port = netloc.split(':')[1]
|
394
|
+
thisURL = netloc.split(':')[0]
|
395
|
+
netloc = ".".join(thisURL.split('.')[0:])
|
396
|
+
else:
|
397
|
+
port = 443
|
398
|
+
netloc = ".".join(netloc.split('.')[0:])
|
399
|
+
path = "/" + "/".join(path.split('/')[1:])
|
400
|
+
else:
|
401
|
+
continue
|
402
|
+
oosDict = {
|
403
|
+
'scheme' : scheme,
|
404
|
+
'netloc': netloc,
|
405
|
+
'path': path,
|
406
|
+
'port': port,
|
407
|
+
'wildcard': wildcard,
|
408
|
+
'fullURI' : scheme+netloc
|
409
|
+
|
410
|
+
}
|
411
|
+
oosRules.append(oosDict)
|
412
|
+
j+=1
|
413
|
+
else:
|
414
|
+
for thisRule in range(len(jsonResponse[j]['rules'])):
|
415
|
+
url = urlparse(jsonResponse[j]['rules'][thisRule]['rule'])
|
416
|
+
scheme = url.scheme
|
417
|
+
netloc = url.netloc
|
418
|
+
path = url.path
|
419
|
+
port = url.port
|
420
|
+
wildcard = False
|
421
|
+
|
422
|
+
if len(netloc) != 0:
|
423
|
+
subdomain = netloc.split('.')[0]
|
424
|
+
if subdomain == "*":
|
425
|
+
wildcard = True
|
426
|
+
netloc = ".".join(netloc.split('.')[1:])
|
427
|
+
else:
|
428
|
+
if len(path) != 0:
|
429
|
+
netloc = path.split('/')[0]
|
430
|
+
checkWildcard = netloc.split('.')[0]
|
431
|
+
if checkWildcard == "*":
|
432
|
+
wildcard = True
|
433
|
+
if ":" in netloc:
|
434
|
+
port = netloc.split(':')[1]
|
435
|
+
thisURL = netloc.split(':')[0]
|
436
|
+
netloc = ".".join(thisURL.split('.')[1:])
|
437
|
+
else:
|
438
|
+
port = 443
|
439
|
+
netloc = ".".join(netloc.split('.')[1:])
|
440
|
+
else:
|
441
|
+
if ":" in netloc:
|
442
|
+
port = netloc.split(':')[1]
|
443
|
+
thisURL = netloc.split(':')[0]
|
444
|
+
netloc = ".".join(thisURL.split('.')[0:])
|
445
|
+
else:
|
446
|
+
port = 443
|
447
|
+
netloc = ".".join(netloc.split('.')[0:])
|
448
|
+
path = "/" + "/".join(path.split('/')[1:])
|
449
|
+
else:
|
450
|
+
continue
|
451
|
+
if jsonResponse[j]['rules'][thisRule]['status'] in ["out","tbd"]:
|
452
|
+
oosDict = {
|
453
|
+
'scheme' : scheme,
|
454
|
+
'netloc': netloc,
|
455
|
+
'path': path,
|
456
|
+
'port': port,
|
457
|
+
'wildcard': wildcard,
|
458
|
+
'fullURI' : scheme+netloc
|
459
|
+
|
460
|
+
}
|
461
|
+
oosRules.append(oosDict)
|
462
|
+
continue
|
463
|
+
else:
|
464
|
+
pass
|
465
|
+
|
466
|
+
scopeDict = {
|
467
|
+
'scheme' : scheme,
|
468
|
+
'netloc': netloc,
|
469
|
+
'path': path,
|
470
|
+
'port': port,
|
471
|
+
'wildcard': wildcard,
|
472
|
+
'fullURI' : scheme+netloc
|
473
|
+
}
|
474
|
+
allRules.append(scopeDict)
|
475
|
+
j+=1
|
476
|
+
return(list(allRules),list(oosRules))
|
477
|
+
if category.lower() == "host":
|
478
|
+
scopeURL = "https://platform.synack.com/api/targets/"+slug+"/cidrs?page=all"
|
479
|
+
cidrs = []
|
480
|
+
try:
|
481
|
+
response = self.try_requests("GET", scopeURL, 10)
|
482
|
+
except requests.exceptions.RequestException as e:
|
483
|
+
raise SystemExit(e)
|
484
|
+
temp = json.dumps(response.json()['cidrs']).replace("[","").replace("]","").replace("\"","").replace(", ","\n").split("\n")
|
485
|
+
cidrs.extend(temp)
|
486
|
+
cidrs = list(set(cidrs))
|
487
|
+
return(cidrs)
|
488
|
+
|
489
|
+
########################################
|
490
|
+
## This converts CIDR list to IP list ##
|
491
|
+
## This is a much faster method, previous method was causing problems on large hosts ##
|
492
|
+
########################################
|
493
|
+
def getIPs(self, cidrs):
|
494
|
+
IPs = []
|
495
|
+
for i in range(len(cidrs)):
|
496
|
+
if cidrs[i] != "":
|
497
|
+
for ip in IPNetwork(cidrs[i]):
|
498
|
+
IPs.append(str(ip))
|
499
|
+
return(IPs)
|
500
|
+
|
501
|
+
##############################################
|
502
|
+
## This gets all of your passed assessments ##
|
503
|
+
##############################################
|
504
|
+
def getAssessments(self):
|
505
|
+
self.assessments.clear()
|
506
|
+
response = self.try_requests("GET", self.url_assessments, 10)
|
507
|
+
jsonResponse = response.json()
|
508
|
+
for i in range(len(jsonResponse)):
|
509
|
+
if jsonResponse[i]['written_assessment']['passed'] == True:
|
510
|
+
self.assessments.append(jsonResponse[i]['category_name'])
|
511
|
+
i+=1
|
512
|
+
|
513
|
+
##########################################################
|
514
|
+
## This gets endpoints from Web Application "Analytics" ##
|
515
|
+
##########################################################
|
516
|
+
def getAnalytics(self, codename, status="all"):
|
517
|
+
slug = self.getTargetID(codename)
|
518
|
+
if status.lower() == "accepted":
|
519
|
+
url_analytics = self.url_analytics + slug + "&status=accepted"
|
520
|
+
elif status.lower() == "in_queue":
|
521
|
+
url_analytics = self.url_analytics + slug + "&status=in_queue"
|
522
|
+
elif status.lower() == "rejected":
|
523
|
+
url_analytics = self.url_analytics + slug + "&status=rejected"
|
524
|
+
else:
|
525
|
+
url_analytics = self.url_analytics + slug
|
526
|
+
response = self.try_requests("GET", url_analytics, 10)
|
527
|
+
jsonResponse = response.json()
|
528
|
+
analytics = []
|
529
|
+
targetType = self.getCategory(codename)
|
530
|
+
if targetType == "Web Application":
|
531
|
+
if "value" in jsonResponse:
|
532
|
+
for value in range(len(jsonResponse['value'])):
|
533
|
+
for exploitable_location in range(len(jsonResponse['value'][value]['exploitable_locations'])):
|
534
|
+
analyticsDict = {}
|
535
|
+
if jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['type'] == "url":
|
536
|
+
try:
|
537
|
+
URI = urlparse(str(jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['value']))
|
538
|
+
scheme = URI.scheme
|
539
|
+
except:
|
540
|
+
scheme = "https"
|
541
|
+
## URL
|
542
|
+
# analyticsDict['uri'] = urlparse(str(jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['value']))
|
543
|
+
port = str(URI.port)
|
544
|
+
if port != "None":
|
545
|
+
analyticsDict['port'] = port
|
546
|
+
else:
|
547
|
+
if scheme == "http":
|
548
|
+
analyticsDict['port'] = "80"
|
549
|
+
elif scheme == "https":
|
550
|
+
analyticsDict['port'] = "443"
|
551
|
+
analyticsDict['codename'] = codename
|
552
|
+
analyticsDict['vuln_category'] = str(jsonResponse['value'][value]['categories'][0])
|
553
|
+
if len(jsonResponse['value'][value]['categories']) == 2:
|
554
|
+
analyticsDict['vuln_subcategory'] = str(jsonResponse['value'][value]['categories'][1])
|
555
|
+
else:
|
556
|
+
pass
|
557
|
+
analyticsDict['scheme'] = "URL"
|
558
|
+
try:
|
559
|
+
analyticsDict['protocol'] = URI.scheme
|
560
|
+
except:
|
561
|
+
analyticsDict['protocol'] = "http"
|
562
|
+
pass
|
563
|
+
try:
|
564
|
+
analyticsDict['vuln_location'] = analyticsDict['protocol'] + "://" + URI.netloc + URI.path
|
565
|
+
except:
|
566
|
+
analyticsDict['vuln_location'] = ""
|
567
|
+
pass
|
568
|
+
if status.lower() == "rejected":
|
569
|
+
analyticsDict['status'] = "rejected"
|
570
|
+
else:
|
571
|
+
analyticsDict['status'] = str(jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['status'])
|
572
|
+
analytics.append(analyticsDict)
|
573
|
+
return analytics
|
574
|
+
elif targetType == "Host":
|
575
|
+
#'codename', 'vuln_category', 'vuln_subcategory', 'vuln_location', 'type', 'protocol', 'port', 'path', 'status'
|
576
|
+
if "value" in jsonResponse:
|
577
|
+
for value in range(len(jsonResponse['value'])):
|
578
|
+
for exploitable_location in range(len(jsonResponse['value'][value]['exploitable_locations'])):
|
579
|
+
analyticsDict = {}
|
580
|
+
if jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['type'] == "url":
|
581
|
+
try:
|
582
|
+
URI = urlparse(str(jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['value']))
|
583
|
+
scheme = URI.scheme
|
584
|
+
except:
|
585
|
+
scheme = "https"
|
586
|
+
## URL
|
587
|
+
# analyticsDict['uri'] = urlparse(str(jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['value']))
|
588
|
+
port = str(URI.port)
|
589
|
+
if port != "None":
|
590
|
+
analyticsDict['port'] = port
|
591
|
+
else:
|
592
|
+
if scheme == "http":
|
593
|
+
analyticsDict['port'] = "80"
|
594
|
+
elif scheme == "https":
|
595
|
+
analyticsDict['port'] = "443"
|
596
|
+
analyticsDict['codename'] = codename
|
597
|
+
analyticsDict['vuln_category'] = str(jsonResponse['value'][value]['categories'][0])
|
598
|
+
if len(jsonResponse['value'][value]['categories']) == 2:
|
599
|
+
analyticsDict['vuln_subcategory'] = str(jsonResponse['value'][value]['categories'][1])
|
600
|
+
else:
|
601
|
+
pass
|
602
|
+
analyticsDict['scheme'] = "URL"
|
603
|
+
try:
|
604
|
+
analyticsDict['protocol'] = URI.scheme
|
605
|
+
except:
|
606
|
+
analyticsDict['protocol'] = "http"
|
607
|
+
pass
|
608
|
+
try:
|
609
|
+
analyticsDict['vuln_location'] = analyticsDict['protocol'] + "://" + URI.netloc + URI.path
|
610
|
+
except:
|
611
|
+
analyticsDict['vuln_location'] = ""
|
612
|
+
pass
|
613
|
+
if status.lower() == "rejected":
|
614
|
+
analyticsDict['status'] = "rejected"
|
615
|
+
else:
|
616
|
+
analyticsDict['status'] = str(jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['status'])
|
617
|
+
analytics.append(analyticsDict)
|
618
|
+
elif jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['type'] == "ip":
|
619
|
+
## IP
|
620
|
+
analyticsDict['codename'] = codename
|
621
|
+
analyticsDict['vuln_category'] = str(jsonResponse['value'][value]['categories'][0])
|
622
|
+
if len(jsonResponse['value'][value]['categories']) == 2:
|
623
|
+
analyticsDict['vuln_subcategory'] = str(jsonResponse['value'][value]['categories'][1])
|
624
|
+
else:
|
625
|
+
pass
|
626
|
+
analyticsDict['scheme'] = "HOST"
|
627
|
+
analyticsDict['protocol'] = str(jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['protocol'])
|
628
|
+
analyticsDict['vuln_location'] = str(jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['address'])
|
629
|
+
analyticsDict['port'] = str(jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['port'])
|
630
|
+
if status.lower() == "rejected":
|
631
|
+
analyticsDict['status'] = "rejected"
|
632
|
+
else:
|
633
|
+
analyticsDict['status'] = str(jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['status'])
|
634
|
+
analytics.append(analyticsDict)
|
635
|
+
return analytics
|
636
|
+
|
637
|
+
#############################################
|
638
|
+
## This registers all unregistered targets ##
|
639
|
+
#############################################
|
640
|
+
|
641
|
+
def registerAll(self):
|
642
|
+
self.getAssessments()
|
643
|
+
pageNum = 1
|
644
|
+
next_page = True
|
645
|
+
unregistered_slugs = []
|
646
|
+
while next_page:
|
647
|
+
url_slugs = self.url_unregistered_slugs + str(pageNum)
|
648
|
+
response = self.try_requests("GET", url_slugs, 10)
|
649
|
+
jsonResponse = response.json()
|
650
|
+
if (len(jsonResponse)!=0):
|
651
|
+
for i in range (len(jsonResponse)):
|
652
|
+
if jsonResponse[i]["category"]["name"] in self.assessments:
|
653
|
+
unregistered_slugs.append(str(jsonResponse[i]["slug"]))
|
654
|
+
pageNum += 1
|
655
|
+
else:
|
656
|
+
next_page = False
|
657
|
+
pageNum += 1
|
658
|
+
for i in range (len(unregistered_slugs)):
|
659
|
+
url_register_slug = "https://platform.synack.com/api/targets/"+unregistered_slugs[i]+"/signup"
|
660
|
+
data='{"ResearcherListing":{"terms":1}}'
|
661
|
+
response = self.try_requests("POST", url_register_slug, 10, data)
|
662
|
+
slug = unregistered_slugs[i]
|
663
|
+
self.getAllTargets()
|
664
|
+
for i in range(len(unregistered_slugs)):
|
665
|
+
codename = self.getCodenameFromSlug(unregistered_slugs[i])
|
666
|
+
if codename == None:
|
667
|
+
print("Error registerring "+unregistered_slugs[i]+"!")
|
668
|
+
else:
|
669
|
+
print("Successfully registered "+str(codename))
|
670
|
+
|
671
|
+
###############
|
672
|
+
## Keepalive ##
|
673
|
+
###############
|
674
|
+
|
675
|
+
def connectToPlatform(self):
|
676
|
+
if self.gecko:
|
677
|
+
return self.connectToPlatformGecko()
|
678
|
+
else:
|
679
|
+
return self.connectToPlatformRequests()
|
680
|
+
|
681
|
+
def connectToPlatformRequests(self):
|
682
|
+
# Pull a valid CSRF token for requests in login flow
|
683
|
+
response = self.try_requests("GET", "https://login.synack.com/", 10)
|
684
|
+
# <meta name="csrf-token" content="..."/>
|
685
|
+
m = re.search('<meta name="csrf-token" content="([^"]*)"', response.text)
|
686
|
+
csrf_token = m.group(1)
|
687
|
+
self.webheaders['X-CSRF-Token'] = csrf_token
|
688
|
+
|
689
|
+
# fix broken Incapsula cookies - regression removed
|
690
|
+
for cookie_name in self.session.cookies.iterkeys():
|
691
|
+
cookie_value = self.session.cookies.get(cookie_name)
|
692
|
+
if cookie_value.find("\r") > -1 or cookie_value.find("\n") > -1:
|
693
|
+
print("Fixing cookie %s" % cookie_name)
|
694
|
+
cookie_value = re.sub("\r\n *","", cookie_value)
|
695
|
+
cookie_obj = requests.cookies.create_cookie(name=cookie_name,value=cookie_value,path="/")
|
696
|
+
self.session.cookies.clear(domain="login.synack.com",path="/",name=cookie_name)
|
697
|
+
self.session.cookies.set_cookie(cookie_obj)
|
698
|
+
|
699
|
+
data={"email":self.email,"password":self.password}
|
700
|
+
response = self.try_requests("POST", "https://login.synack.com/api/authenticate", 1, data)
|
701
|
+
jsonResponse = response.json()
|
702
|
+
if not jsonResponse['success']:
|
703
|
+
print("Error logging in: "+jsonResponse)
|
704
|
+
return False
|
705
|
+
|
706
|
+
progress_token = jsonResponse['progress_token']
|
707
|
+
|
708
|
+
data={"authy_token":self.getAuthy(),"progress_token":progress_token}
|
709
|
+
response = self.try_requests("POST", "https://login.synack.com/api/authenticate", 1, data)
|
710
|
+
jsonResponse = response.json()
|
711
|
+
|
712
|
+
grant_token = jsonResponse['grant_token']
|
713
|
+
|
714
|
+
# 2 requests required here to confirm the grant token - once to the HTML page and once to the API
|
715
|
+
response = self.try_requests("GET", "https://platform.synack.com/?grant_token="+grant_token, 1)
|
716
|
+
self.webheaders['X-Requested-With'] = "XMLHttpRequest"
|
717
|
+
response = self.try_requests("GET", "https://platform.synack.com/token?grant_token="+grant_token, 1)
|
718
|
+
jsonResponse = response.json()
|
719
|
+
access_token = jsonResponse['access_token']
|
720
|
+
|
721
|
+
self.token = access_token
|
722
|
+
with open(self.sessionTokenPath,"w") as f:
|
723
|
+
f.write(self.token)
|
724
|
+
f.close()
|
725
|
+
|
726
|
+
# Remove these headers so they don't affect other requests
|
727
|
+
del self.webheaders['X-Requested-With']
|
728
|
+
del self.webheaders['X-CSRF-Token']
|
729
|
+
|
730
|
+
|
731
|
+
def connectToPlatformGecko(self):
|
732
|
+
isExist = os.path.exists(self.firefoxProfile)
|
733
|
+
if not isExist:
|
734
|
+
os.makedirs(self.firefoxProfile)
|
735
|
+
options = Options()
|
736
|
+
options.add_argument("-profile")
|
737
|
+
options.add_argument(self.firefoxProfile)
|
738
|
+
firefox_capabilities = DesiredCapabilities.FIREFOX
|
739
|
+
firefox_capabilities['marionette'] = True
|
740
|
+
|
741
|
+
if self.headless == True:
|
742
|
+
options.headless = True
|
743
|
+
else:
|
744
|
+
options.headless = False
|
745
|
+
# driver = webdriver.Firefox(options=options)
|
746
|
+
driver = webdriver.Firefox(capabilities=firefox_capabilities, options=options)
|
747
|
+
driver.get(self.login_url)
|
748
|
+
assert "Synack" in driver.title
|
749
|
+
## Fill in the email address ##
|
750
|
+
email_path = '/html/body/div[2]/div/div/div[2]/form/fieldset/input'
|
751
|
+
driver.find_element_by_xpath(email_path).click()
|
752
|
+
driver.find_element_by_xpath(email_path).send_keys(self.email)
|
753
|
+
## Fill in the password ##
|
754
|
+
password_path = '/html/body/div[2]/div/div/div[2]/form/fieldset/div[1]/input'
|
755
|
+
driver.find_element_by_xpath(password_path).click()
|
756
|
+
driver.find_element_by_xpath(password_path).send_keys(self.password)
|
757
|
+
## Click the login button ##
|
758
|
+
login_path = '/html/body/div[2]/div/div/div[2]/form/fieldset/div[2]/button'
|
759
|
+
driver.find_element_by_xpath(login_path).click()
|
760
|
+
time.sleep(5)
|
761
|
+
## Hope the authy works! ##
|
762
|
+
authy_path = '/html/body/div[2]/div/div/div[2]/form/fieldset/input'
|
763
|
+
driver.find_element_by_xpath(authy_path).click()
|
764
|
+
driver.find_element_by_xpath(authy_path).send_keys(self.getAuthy())
|
765
|
+
authy_submit_path = '/html/body/div[2]/div/div/div[2]/form/fieldset/div[1]/button'
|
766
|
+
driver.find_element_by_xpath(authy_submit_path).click()
|
767
|
+
while True:
|
768
|
+
self.token = driver.execute_script("return sessionStorage.getItem('shared-session-com.synack.accessToken')")
|
769
|
+
if isinstance(self.token, str):
|
770
|
+
break
|
771
|
+
## Write the session token to /tmp/synacktoken ##
|
772
|
+
with open(self.sessionTokenPath,"w") as f:
|
773
|
+
f.write(self.token)
|
774
|
+
f.close()
|
775
|
+
print("Connected to platform.")
|
776
|
+
if self.headless == True:
|
777
|
+
driver.quit()
|
778
|
+
if self.connector == True:
|
779
|
+
self.webdriver = driver
|
780
|
+
return(0)
|
781
|
+
|
782
|
+
###########
|
783
|
+
## Vulns ##
|
784
|
+
###########
|
785
|
+
|
786
|
+
def getVulns(self, status="accepted"):
|
787
|
+
pageNum = 1
|
788
|
+
results = []
|
789
|
+
while True:
|
790
|
+
url_vulnerabilities = self.url_vulnerabilities +"?filters%5Bstatus%5D=" + status + "&page=" +str(pageNum)+"&per_page=5"
|
791
|
+
response = self.try_requests("GET", url_vulnerabilities, 10)
|
792
|
+
vulnsResponse = response.json()
|
793
|
+
if len(vulnsResponse) == 0:
|
794
|
+
break
|
795
|
+
else:
|
796
|
+
results = results + vulnsResponse
|
797
|
+
pageNum += 1
|
798
|
+
return results
|
799
|
+
|
800
|
+
def getVuln(self, identifier): # e.g. optimusant-4
|
801
|
+
url_vuln = self.url_vulnerabilities + "/" + identifier
|
802
|
+
response = self.try_requests("GET", url_vuln, 10)
|
803
|
+
vuln_response = response.json()
|
804
|
+
return vuln_response
|
805
|
+
|
806
|
+
###########
|
807
|
+
## Drafts ##
|
808
|
+
###########
|
809
|
+
|
810
|
+
def getDrafts(self):
|
811
|
+
pageNum = 1
|
812
|
+
results = []
|
813
|
+
while True:
|
814
|
+
url_drafts = self.url_drafts +"?page=" +str(pageNum)+"&per_page=5"
|
815
|
+
response = self.try_requests("GET", url_drafts, 10)
|
816
|
+
draftsResponse = response.json()
|
817
|
+
if len(draftsResponse) == 0:
|
818
|
+
break
|
819
|
+
else:
|
820
|
+
results = results + draftsResponse
|
821
|
+
pageNum += 1
|
822
|
+
return results
|
823
|
+
|
824
|
+
def deleteDraft(self, id):
|
825
|
+
# careful!!
|
826
|
+
url_delete = self.url_drafts + "/" + str(id)
|
827
|
+
response = self.try_requests("DELETE", url_delete, 1)
|
828
|
+
if response.status_code == 200:
|
829
|
+
return True
|
830
|
+
else:
|
831
|
+
return False
|
832
|
+
|
833
|
+
###########
|
834
|
+
## Hydra ##
|
835
|
+
###########
|
836
|
+
|
837
|
+
def getHydra(self, codename):
|
838
|
+
slug = self.getTargetID(codename)
|
839
|
+
pageNum = 1
|
840
|
+
hydraResults = []
|
841
|
+
while True:
|
842
|
+
url_hydra = self.url_hydra +"?page=" +str(pageNum)+"&listing_uids="+slug+"&q=%2Bport_is_open%3Atrue"
|
843
|
+
response = self.try_requests("GET", url_hydra, 10)
|
844
|
+
hydraResponse = response.json()
|
845
|
+
if len(hydraResponse) == 0:
|
846
|
+
break
|
847
|
+
else:
|
848
|
+
hydraResults = hydraResults + hydraResponse
|
849
|
+
pageNum += 1
|
850
|
+
return hydraResults
|
851
|
+
|
852
|
+
###################
|
853
|
+
## Mission stuff ##
|
854
|
+
###################
|
855
|
+
|
856
|
+
## Poll for missions ##
|
857
|
+
|
858
|
+
def pollMissions(self):
|
859
|
+
response = self.try_requests("GET", self.url_published_missions, 10)
|
860
|
+
try:
|
861
|
+
jsonResponse = response.json()
|
862
|
+
except:
|
863
|
+
jsonResponse = {}
|
864
|
+
try:
|
865
|
+
return jsonResponse
|
866
|
+
except NameError:
|
867
|
+
jsonResponse = {}
|
868
|
+
return jsonResponse
|
869
|
+
|
870
|
+
####################
|
871
|
+
## CLAIM MISSIONS ##
|
872
|
+
####################
|
873
|
+
def claimMission(self, missionJson, dontclaim, assetType):
|
874
|
+
dollarValue = {}
|
875
|
+
claim = {'type': 'CLAIM'}
|
876
|
+
################
|
877
|
+
## Sort missions by dollar amount high to low
|
878
|
+
################
|
879
|
+
for i in range(len(missionJson)):
|
880
|
+
dollarValue[i] = missionJson[i]["payout"]["amount"]
|
881
|
+
sorted_tuples = sorted(dollarValue.items(), key=operator.itemgetter(1), reverse=True)
|
882
|
+
sorted_dict = {k: v for k, v in sorted_tuples}
|
883
|
+
################
|
884
|
+
i = len(sorted_dict.keys())
|
885
|
+
missionList = []
|
886
|
+
for key in sorted_dict.keys():
|
887
|
+
target = self.getCodenameFromSlug(missionJson[key]["listing"]["id"])
|
888
|
+
category = self.getCategory(target)
|
889
|
+
if(category not in assetType):
|
890
|
+
continue
|
891
|
+
if(target in dontclaim):
|
892
|
+
continue
|
893
|
+
i-= 1
|
894
|
+
campaign = missionJson[key]["campaign"]["title"]
|
895
|
+
campaignID = missionJson[key]["campaign"]["id"]
|
896
|
+
orgID = missionJson[key]["organization"]["id"]
|
897
|
+
slug = missionJson[key]["listing"]["id"]
|
898
|
+
taskID = missionJson[key]["id"]
|
899
|
+
payout = str(missionJson[key]["payout"]["amount"])
|
900
|
+
url_claimPath = "https://platform.synack.com/api/tasks/v1/organizations/" + orgID + "/listings/" + slug + "/campaigns/" + campaignID + "/tasks/" + taskID + "/transitions"
|
901
|
+
claimResponse = self.try_requests("POST", url_claimPath, 10, claim)
|
902
|
+
if claimResponse.status_code == 201:
|
903
|
+
claimed = True
|
904
|
+
else:
|
905
|
+
claimed = False
|
906
|
+
missionDict = {"target": target, "payout": payout, "claimed": claimed}
|
907
|
+
missionList.append(missionDict)
|
908
|
+
return(missionList)
|
909
|
+
|
910
|
+
########################
|
911
|
+
## Notification Token ##
|
912
|
+
########################
|
913
|
+
|
914
|
+
def getNotificationToken(self):
|
915
|
+
response = self.try_requests("GET", self.url_notification_token, 10)
|
916
|
+
try:
|
917
|
+
jsonResponse = response.json()
|
918
|
+
except:
|
919
|
+
jsonResponse = {}
|
920
|
+
return(1)
|
921
|
+
self.notificationToken = jsonResponse['token']
|
922
|
+
with open(self.notificationTokenPath,"w") as f:
|
923
|
+
f.write(self.notificationToken)
|
924
|
+
return(0)
|
925
|
+
|
926
|
+
############################
|
927
|
+
## Read All Notifications ##
|
928
|
+
############################
|
929
|
+
|
930
|
+
def markNotificationsRead(self):
|
931
|
+
if not self.notificationToken:
|
932
|
+
self.getNotificationToken()
|
933
|
+
readNotifications = self.url_notification_api+"read_all?authorization_token="+self.notificationToken
|
934
|
+
del self.webheaders['Authorization']
|
935
|
+
response = self.try_requests("POST", readNotifications, 10)
|
936
|
+
self.webheaders['Authorization'] = "Bearer " + self.token
|
937
|
+
try:
|
938
|
+
textResponse = str(response.content)
|
939
|
+
except:
|
940
|
+
return(1)
|
941
|
+
return(0)
|
942
|
+
|
943
|
+
########################
|
944
|
+
## Read Notifications ##
|
945
|
+
########################
|
946
|
+
|
947
|
+
def pollNotifications(self):
|
948
|
+
pageIterator=1
|
949
|
+
breakOuterLoop = 0
|
950
|
+
notifications = []
|
951
|
+
if not self.notificationToken:
|
952
|
+
self.getNotificationToken()
|
953
|
+
while True:
|
954
|
+
notificationsUrl = self.url_notification_api+"notifications?pagination%5Bpage%5D="+str(pageIterator)+"&pagination%5Bper_page%5D=15&meta=1"
|
955
|
+
self.webheaders['Authorization'] = "Bearer " + self.notificationToken
|
956
|
+
response = self.try_requests("GET", notificationsUrl, 10)
|
957
|
+
self.webheaders['Authorization'] = "Bearer " + self.token
|
958
|
+
try:
|
959
|
+
jsonResponse = response.json()
|
960
|
+
except:
|
961
|
+
return(1)
|
962
|
+
if not jsonResponse:
|
963
|
+
break
|
964
|
+
for i in range(len(jsonResponse)):
|
965
|
+
if jsonResponse[i]["read"] == False:
|
966
|
+
notifications.append(jsonResponse[i])
|
967
|
+
else:
|
968
|
+
breakOuterLoop=1
|
969
|
+
break
|
970
|
+
if breakOuterLoop == 1:
|
971
|
+
break
|
972
|
+
else:
|
973
|
+
pageIterator=pageIterator+1
|
974
|
+
return(notifications)
|
975
|
+
|
976
|
+
#############################
|
977
|
+
## Get Current Target Slug ##
|
978
|
+
#############################
|
979
|
+
|
980
|
+
def getCurrentTargetSlug(self):
|
981
|
+
response = self.try_requests("GET", self.url_activate_target, 10)
|
982
|
+
try:
|
983
|
+
jsonResponse = response.json()
|
984
|
+
except:
|
985
|
+
return(1)
|
986
|
+
if jsonResponse['slug']:
|
987
|
+
return(jsonResponse['slug'])
|
988
|
+
|
989
|
+
##############
|
990
|
+
## Get ROEs ##
|
991
|
+
##############
|
992
|
+
|
993
|
+
def getRoes(self, slug):
|
994
|
+
requestURL = self.url_scope_summary + str(slug)
|
995
|
+
response = self.try_requests("GET", requestURL, 10)
|
996
|
+
roes = list()
|
997
|
+
try:
|
998
|
+
jsonResponse = response.json()
|
999
|
+
except:
|
1000
|
+
return(1)
|
1001
|
+
if not jsonResponse['roes']:
|
1002
|
+
return(roes)
|
1003
|
+
else:
|
1004
|
+
for i in range(len(jsonResponse['roes'])):
|
1005
|
+
roes.append(jsonResponse['roes'][i])
|
1006
|
+
return(roes)
|
1007
|
+
|
1008
|
+
######################
|
1009
|
+
## Get Transactions ##
|
1010
|
+
######################
|
1011
|
+
def getTransactions(self):
|
1012
|
+
pageIterator=1
|
1013
|
+
breakOuterLoop = 0
|
1014
|
+
transactions = []
|
1015
|
+
while True:
|
1016
|
+
transactionUrl = self.url_transactions+"?page="+str(pageIterator)+"&per_page=15"
|
1017
|
+
response = self.try_requests("GET", transactionUrl, 10)
|
1018
|
+
try:
|
1019
|
+
jsonResponse = response.json()
|
1020
|
+
except:
|
1021
|
+
return(1)
|
1022
|
+
if not jsonResponse:
|
1023
|
+
break
|
1024
|
+
for i in range(len(jsonResponse)):
|
1025
|
+
if jsonResponse[i]["title"] == "CashOut":
|
1026
|
+
if float(jsonResponse[i]['amount']) < 0:
|
1027
|
+
amount = float(jsonResponse[i]['amount']) * -1
|
1028
|
+
else:
|
1029
|
+
amount = float(jsonResponse[i]['amount'])
|
1030
|
+
ts = datetime.datetime.fromtimestamp(jsonResponse[i]['created_at'])
|
1031
|
+
transactions.append(ts.strftime('%Y-%m-%d')+","+str(amount))
|
1032
|
+
pageIterator=pageIterator+1
|
1033
|
+
return(transactions)
|
1034
|
+
|
1035
|
+
########################
|
1036
|
+
## Get LP Credentials ##
|
1037
|
+
########################
|
1038
|
+
def getLPCredentials(self):
|
1039
|
+
response = self.try_requests("GET", self.url_lp_credentials, 10)
|
1040
|
+
try:
|
1041
|
+
creds = response.json()
|
1042
|
+
creds["openvpn_file"] = base64.b64decode(creds["openvpn_file"])
|
1043
|
+
return creds
|
1044
|
+
except:
|
1045
|
+
return
|
1046
|
+
|