Passed
Pull Request — master (#370)
by Jaspar
01:40
created

create-monthly-report.gmp.combine_reports()   B

Complexity

Conditions 5

Size

Total Lines 40
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 27
nop 3
dl 0
loc 40
rs 8.7653
c 0
b 0
f 0
1
# -*- coding: utf-8 -*-
2
# Copyright (C) 2021 Greenbone Networks GmbH
3
#
4
# SPDX-License-Identifier: GPL-3.0-or-later
5
#
6
# This program is free software: you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation, either version 3 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19
from uuid import UUID
20
from typing import List
21
from datetime import date, timedelta
22
from argparse import ArgumentParser, RawTextHelpFormatter
23
24
from gvmtools.helper import generate_uuid, error_and_exit
25
26
from lxml import etree as e
27
28
from gvm.xml import pretty_print
29
30
HELP_TEXT = 'This script creates a consolidated report and imports it to the GSM. Usable with gvm-script (gvm-tools)'
31
32
33
def get_last_reports_from_tasks(gmp, period_start, period_end, tags: List):
34
    """Get the last reports from the tasks in the given time period
35
36
    gmp: the GMP object
37
    period_start: the start date
38
    period_end: the end date
39
    tags: list of tags for the filter
40
41
    """
42
43
    task_filter = 'rows=-1 '
44
    period_filter = 'created>{0} and created<{1}'.format(
45
        period_start.isoformat(), period_end.isoformat()
46
    )
47
    filter_parts = []
48
    if tags:
49
        for tag in tags:
50
            filter_parts.append('{} and {}'.format(period_filter, tag))
51
52
        tags_filter = ' or '.join(filter_parts)
53
        task_filter += tags_filter
54
    else:
55
        task_filter += period_filter
56
57
    print('Filtering the task with the filter term [{}]'.format(task_filter))
58
59
    tasks_xml = gmp.get_tasks(filter=task_filter)
60
    reports = []
61
    for report in tasks_xml.xpath('task/last_report/report/@id'):
62
        reports.append(str(report))
63
64
    # remove duplicates ... just in case
65
    reports = list(dict.fromkeys(reports))
66
67
    return reports
68
69
70
def combine_reports(gmp, reports: List, filter_term: str):
71
    """Combining the filtered ports, results and hosts of the given
72
    report ids into one new report.
73
74
    gmp: the GMP object
75
    reports (List): List of report_ids
76
    filter_term (str): the result filter string
77
    """
78
79
    new_uuid = generate_uuid()
80
    combined_report = e.Element(
81
        'report',
82
        {
83
            'id': new_uuid,
84
            'format_id': 'd5da9f67-8551-4e51-807b-b6a873d70e34',
85
            'extension': 'xml',
86
            'content_type': 'text/xml',
87
        },
88
    )
89
    report_elem = e.Element('report', {'id': new_uuid})
90
91
    ports_elem = e.Element('ports', {'start': '1', 'max': '-1'})
92
    results_elem = e.Element('results', {'start': '1', 'max': '-1'})
93
    combined_report.append(report_elem)
94
    report_elem.append(results_elem)
95
96
    hosts = []
97
    for report in reports:
98
        current_report = gmp.get_report(
99
            report, filter=filter_term, details=True
100
        )[0]
101
        pretty_print(current_report.find('report').find('result_count'))
102
        for port in current_report.xpath('report/ports/port'):
103
            ports_elem.append(port)
104
        for result in current_report.xpath('report/results/result'):
105
            results_elem.append(result)
106
        for host in current_report.xpath('report/host'):
107
            report_elem.append(host)
108
109
    return combined_report
110
111
112
def send_report(gmp, combined_report, period_start, period_end):
113
    """Creating a container task and sending the combined report to the GSM
114
115
    gmp: the GMP object
116
    combined_report: the combined report xml object
117
    period_start: the start date
118
    period_end: the end date
119
    """
120
121
    task_name = 'Consolidated Report [{} - {}]'.format(period_start, period_end)
122
123
    res = gmp.create_container_task(
124
        name=task_name, comment='Created with gvm-tools.'
125
    )
126
127
    task_id = res.xpath('//@id')[0]
128
129
    combined_report = e.tostring(combined_report)
130
131
    res = gmp.import_report(combined_report, task_id=task_id)
132
133
    return res.xpath('//@id')[0]
134
135
136
def parse_tags(tags: List):
137
    """Parsing and validating the given tags
138
139
    tags (List): A list containing tags:
140
                 name, tag-id, name=value
141
142
    Returns a list containing tag="name", tag_id="id" ...
143
    """
144
    filter_tags = []
145
    for tag in tags:
146
        try:
147
            UUID(tag, version=4)
148
            filter_tags.append('tag_id="{}"'.format(tag))
149
        except ValueError:
150
            filter_tags.append('tag="{}"'.format(tag))
151
152
    return filter_tags
153
154
155
def parse_period(period: List):
156
    """Parsing and validating the given time period
157
158
    period (List): A list with two entries containing
159
                   dates in the format yyyy/mm/dd
160
161
    Returns two date-objects containing the passed dates
162
    """
163
    try:
164
        s_year, s_month, s_day = map(int, period[0].split('/'))
165
    except ValueError as e:
166
        error_and_exit(
167
            'Start date [{}] is not a correct date format:\n{}'.format(
168
                period[0], e.args[0]
169
            )
170
        )
171
    try:
172
        e_year, e_month, e_day = map(int, period[1].split('/'))
173
    except ValueError as e:
174
        error_and_exit(
175
            'End date [{}] is not a correct date format:\n{}'.format(
176
                period[1], e.args[0]
177
            )
178
        )
179
180
    try:
181
        period_start = date(s_year, s_month, s_day)
182
    except ValueError as e:
183
        error_and_exit('Start date: {}'.format(e.args[0]))
184
185
    try:
186
        period_end = date(e_year, e_month, e_day)
187
    except ValueError as e:
188
        error_and_exit('End date: {}'.format(e.args[0]))
189
190
    if period_end < period_start:
191
        error_and_exit('The start date seems to after the end date.')
192
193
    return period_start, period_end
194
195
196
def parse_args(args):  # pylint: disable=unused-argument
197
    """ Parsing args ... """
198
199
    parser = ArgumentParser(
200
        prefix_chars='+',
201
        add_help=False,
202
        formatter_class=RawTextHelpFormatter,
203
        description=HELP_TEXT,
204
    )
205
206
    parser.add_argument(
207
        '+h',
208
        '++help',
209
        action='help',
210
        help='Show this help message and exit.',
211
    )
212
213
    parser.add_argument(
214
        '+p',
215
        '++period',
216
        nargs=2,
217
        type=str,
218
        required=True,
219
        dest='period',
220
        help='Choose a time period that is filtering the tasks. Use the date format YYYY/MM/DD.',
221
    )
222
223
    parser.add_argument(
224
        '+t',
225
        '++tags',
226
        nargs='+',
227
        type=str,
228
        dest='tags',
229
        help=(
230
            'Filter the tasks by given tag(s).\n'
231
            'If you pass more than on tag, they will be concatenated with '
232
            or '\n'
233
            'You can pass tag names, tag ids or tag name=value to this argument'
234
        ),
235
    )
236
237
    parser.add_argument(
238
        '+f',
239
        '++filter',
240
        nargs='+',
241
        type=str,
242
        dest='filter',
243
        help='Filter the results by given filter(s).',
244
    )
245
246
    script_args, _ = parser.parse_known_args()
247
    return script_args
248
249
250
def main(gmp, args):
251
    # pylint: disable=undefined-variable
252
253
    parsed_args = parse_args(args=args)
254
255
    period_start, period_end = parse_period(period=parsed_args.period)
256
257
    print(
258
        'Combining reports from tasks within the time period [{}, {}]'.format(
259
            period_start, period_end
260
        )
261
    )
262
263
    filter_tags = None
264
    if parsed_args.tags:
265
        filter_tags = parse_tags(tags=parsed_args.tags)
266
267
    reports = get_last_reports_from_tasks(
268
        gmp=gmp,
269
        period_start=period_start,
270
        period_end=period_end,
271
        tags=filter_tags,
272
    )
273
274
    print("Combining {} found reports.".format(len(reports)))
275
276
    filter_term = ''
277
    if parsed_args.filter:
278
        filter_term = ' '.join(parsed_args.filter)
279
        print(
280
            'Filtering the results by the following filter term [{}]'.format(
281
                filter_term
282
            )
283
        )
284
    else:
285
        print('No result filter given.')
286
287
    combined_report = combine_reports(
288
        gmp=gmp, reports=reports, filter_term=filter_term
289
    )
290
291
    send_report(
292
        gmp=gmp,
293
        combined_report=combined_report,
294
        period_start=period_start,
295
        period_end=period_end,
296
    )
297
298
299
if __name__ == '__gmp__':
300
    main(gmp, args)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable args does not seem to be defined.
Loading history...
Comprehensibility Best Practice introduced by
The variable gmp does not seem to be defined.
Loading history...
301