-
Notifications
You must be signed in to change notification settings - Fork 0
/
qualysguard_was_update_app.py
185 lines (178 loc) · 6.89 KB
/
qualysguard_was_update_app.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
#!/usr/bin/env python
'''Automate updating of multiple QualysGuard web apps.
Author: Parag Baxi <parag.baxi@gmail.com>
License: Apache License, Version 2.0
'''
'''To do:
Add ability to import CSV.
'''
import argparse
import base64, urllib2
import datetime, time
import logging
import os
import qualysapi
import random
import unicodedata
from collections import defaultdict
from lxml import objectify
from texttable import Texttable
def list_apps(apps):
"""Print applications into a pretty table.
"""
table = Texttable()
table.set_cols_align(["r", "l", "l"])
table.set_cols_valign(["m", "m", "m"])
table.add_rows([ ['App #', 'App name', 'App ID #'], ], header = True)
c=0
for webapp in apps:
c+=1
table.add_row([c, webapp['name'], webapp['id']])
# Print table.
print (table.draw() + '\n')
return True
# Start of script.
# Declare the command line flags/options we want to allow.
parser = argparse.ArgumentParser(description = 'Automate updating of multiple QualysGuard webapps.')
parser.add_argument('-a', '--all_apps', action = 'store_true',
help = 'Select all web applications. Overwrites any tag filters.')
parser.add_argument('--config',
help = 'Configuration for Qualys connector.')
parser.add_argument('--debug', action = 'store_true',
help = 'Outputs additional information to log.')
parser.add_argument('-l', '--no_list', action = 'store_true', default = False,
help = 'Do not list all selected web applications. (Default = False)')
parser.add_argument('-t', '--tag',
help = 'Filter selection of web applications to those with TAG.')
#parser.add_argument('-T', '--Tags_file',
# help = 'Filter selection of web applications to those with all tags from TAGS_FILE (tags combined using a logical AND). Tags from file should be separated by line break.')
parser.add_argument('-u', '--update', default = 'post.xml',
help = 'Input XML file to POST update to webapps. (Default = post.xml)')
parser.add_argument('-x', '--no_update', action = 'store_true', default = False,
help = 'Do not update selected web applications. (Default = False)')
# Parse arguements.
c_args = parser.parse_args()
# Check requirements.
try:
with open(c_args.update) as myfile:
post = myfile.read().replace('\n', '')
except IOError:
print 'No XML file found to update apps.'
parser.print_help()
exit(1)
# Create log directory.
PATH_LOG = 'log'
if not os.path.exists(PATH_LOG):
os.makedirs(PATH_LOG)
# Set log options.
now = datetime.datetime.now()
BASE_FILENAME = '%s-%s' % (__file__,
datetime.datetime.now().strftime('%Y-%m-%d.%H-%M-%S'))
LOG_FILENAME = '%s/%s.log' % (PATH_LOG,
BASE_FILENAME)
# Set logging level.
if c_args.debug:
# Enable debug level of logging.
print "Logging level set to debug."
logging.basicConfig(filename = LOG_FILENAME, format = '%(asctime)s %(message)s',
level = logging.DEBUG)
else:
logging.basicConfig(filename = LOG_FILENAME, format = '%(asctime)s %(message)s',
level = logging.INFO)
# Validate arguments.
if not ((c_args.all_apps or c_args.tag or c_args.Tags_file)):
parser.print_help()
logging.error('Invalid run parameters.')
exit(1)
# Configure Qualys API connector.
if c_args.config:
qgc = qualysapi.connect(c_args.config)
else:
qgc = qualysapi.connect()
# There may be more than 1000 apps so start with first possible record, # 0.
last_record = '0'
apps_to_update = []
print 'Downloading list of applications.'
while True:
# Get list of web apps.
query_uri = '/search/was/webapp'
if c_args.all_apps:
data = '''
<ServiceRequest>
<filters>
<Criteria field="createdDate" operator="GREATER">2000-02-21T00:00:00Z</Criteria>
<Criteria field="id" operator="GREATER">%s</Criteria>
</filters>
<preferences>
<limitResults>1000</limitResults>
</preferences>
</ServiceRequest>''' % (last_record)
elif c_args.tag:
data = '''
<ServiceRequest>
<filters>
<Criteria field="tags.name" operator="EQUALS">%s</Criteria>
<Criteria field="id" operator="GREATER">%s</Criteria>
</filters>
<preferences>
<limitResults>1000</limitResults>
</preferences>
</ServiceRequest>''' % (c_args.tag, last_record)
search_apps = qgc.request(query_uri, data)
logging.debug(search_apps)
# Parse list of web apps to associate each web app id with web app name.
tree = objectify.fromstring(search_apps)
for webapp in tree.data.WebApp:
app = defaultdict(str)
app_name = webapp.name.text
# App name may be in unicode.
if isinstance(app_name, unicode):
# Decode to string.
app_name = unicodedata.normalize('NFKD', app_name).encode('ascii','ignore')
app['name'] = app_name
app['id'] = webapp.id.text
apps_to_update.append(app)
if tree.hasMoreRecords.text == 'true':
last_record = tree.lastId.text
else:
break
print '\n'
logging.info('apps_to_update = %s' % (apps_to_update))
if not c_args.no_list:
list_apps(apps_to_update)
if c_args.no_update:
print '\nNot updating the apps.\n'
exit()
# Start updating.
apps_updated=[]
apps_not_updated=[]
for app in apps_to_update:
logging.debug('Attempting to update %s, %s' % (app['name'], app['id']))
# Limit updates to concurrency limit.
# Update web app.
query_uri = '/update/was/webapp/%s' % (app['id'])
# Setup request to update web app.
# Make request
logging.info('Updating %s (web app ID %s)...' % (app['name'], app['id']))
print 'Updating %s (web app ID %s)...' % (app['name'], app['id'])
response = qgc.request(query_uri, post)
print 'Done:'
logging.info(response)
print response
app['response'] = response
if '<errorMessage>' in app['response']:
logging.warning('Could not update app %s, id %s.' % (str(app['name']), str(app['id'])))
print 'Could not update app %s, id %s.\n' % (str(app['name']), str(app['id']))
apps_not_updated.append(app)
else:
print 'Successfully updated app %s, id %s.\n' % (str(app['name']), str(app['id']))
apps_updated.append(app)
# Write update status to file.
logging.debug('Writing results of initiating app updates to file.')
with open('%s-updated.txt' % (BASE_FILENAME), 'wb') as f:
for app in apps_updated:
f.write('%s, id %s, response: \n%s' % (str(app['name']), str(app['id']), str(app['response'])))
with open('%s-not_updated.txt' % (BASE_FILENAME), 'wb') as f:
for app in apps_not_updated:
f.write('%s, id %s, response: \n%s' % (str(app['name']), str(app['id']), str(app['response'])))
exit()