Issues (69)

scripts/random-report-gen.gmp.py (3 issues)

1
# -*- coding: utf-8 -*-
2
# Copyright (C) 2017-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
# pylint: disable=too-many-lines
20
21
import time
22
import textwrap
23
import json
24
25
from random import randrange, choice, gauss, seed
26
from argparse import ArgumentParser, RawTextHelpFormatter
27
from pathlib import Path
28
29
from lxml import etree as e
30
31
from gvmtools.helper import (
32
    generate_uuid,
33
    generate_id,
34
    generate_random_ips,
35
)
36
37
__version__ = "0.1.0"
38
39
HELP_TEXT = """
40
    Random Report Generation Script {version} (C) 2017-2021 Greenbone Networks GmbH
41
42
    This program is free software: you can redistribute it and/or modify
43
    it under the terms of the GNU General Public License as published by
44
    the Free Software Foundation, either version 3 of the License, or
45
    (at your option) any later version.
46
47
    This program is distributed in the hope that it will be useful,
48
    but WITHOUT ANY WARRANTY; without even the implied warranty of
49
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
50
    GNU General Public License for more details.
51
52
    You should have received a copy of the GNU General Public License
53
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
54
55
    This script generates randomized report data.
56
    """.format(
57
    version=__version__
58
)
59
60
61
def generate_ports(n_ports):
62
    protocol = ['/tcp', '/udp']
63
    return [str(randrange(0, 65536)) + choice(protocol) for i in range(n_ports)]
64
65
66
def generate_report_elem(task, **kwargs):
67
    rep_format_id = 'a994b278-1f62-11e1-96ac-406186ea4fc5'
68
    rep_id = generate_uuid()
69
    outer_report_elem = e.Element(
70
        'report',
71
        attrib={
72
            'extension': 'xml',
73
            'id': rep_id,
74
            'format_id': rep_format_id,
75
            'content_type': 'text/xml',
76
        },
77
    )
78
    owner_elem = e.SubElement(outer_report_elem, 'owner')
79
    e.SubElement(owner_elem, 'name').text = 'testowner'
80
    e.SubElement(outer_report_elem, 'name').text = 'testname'
81
    e.SubElement(outer_report_elem, 'writeable').text = str(0)
82
    e.SubElement(outer_report_elem, 'in_use').text = str(0)
83
    task_elem = e.SubElement(outer_report_elem, 'task', attrib={'id': task[0]})
84
    e.SubElement(task_elem, 'name').text = task[1]
85
    repform_elem = e.SubElement(
86
        outer_report_elem, 'report_format', attrib={'id': rep_format_id}
87
    )
88
    e.SubElement(repform_elem, 'name').text = 'XML'
89
90
    # Generating inner <report> tag
91
    outer_report_elem.append(generate_inner_report(rep_id, **kwargs))
92
93
    return outer_report_elem
94
95
96
def generate_inner_report(rep_id, n_results, n_hosts, data, **kwargs):
97
    report_elem = e.Element('report', attrib={'id': rep_id})
98
    results_elem = e.SubElement(
99
        report_elem, 'results', {'max': str(n_results), 'start': '1'}
100
    )
101
102
    # Create Hosts, Ports, Data
103
    hosts = generate_random_ips(n_hosts)  # Host IPs
104
    ports = generate_ports(n_hosts)
105
    oid_dict = {host: [] for host in hosts}
106
    asset_dict = {host: generate_uuid() for host in hosts}
107
    host_names = {host: generate_id() for host in hosts}
108
    max_sev = 0.0
109
110
    # Create <result> tags with random data
111
    for _ in range(n_results):
112
        host_ip = choice(hosts)
113
        host_port = choice(ports)
114
        result_elem, oid, severity = generate_result_elem(
115
            data["vulns"],
116
            host_ip,
117
            host_port,
118
            asset_dict[host_ip],
119
            host_names[host_ip],
120
        )
121
        if float(severity) > max_sev:
122
            max_sev = float(severity)
123
124
        oid_dict[host_ip].append(oid)
125
        results_elem.append(result_elem)
126
127
    e.SubElement(report_elem, "result_count").text = str(n_results)
128
129
    sev_elem = e.Element("severity")
130
    e.SubElement(sev_elem, "full").text = str(max_sev)
131
    e.SubElement(sev_elem, "filtered").text = str(max_sev)
132
133
    report_elem.append(sev_elem)
134
135
    # Create <host> tags with random data
136
    for host in hosts:
137
        if len(oid_dict[host]) > 0:
138
            report_elem.append(
139
                generate_host_elem(
140
                    host,
141
                    oid_dict[host][0],
142
                    asset_dict[host],
143
                    host_names[host],
144
                    data=data,
145
                    **kwargs,
146
                )
147
            )
148
149
    return report_elem
150
151
152
def generate_result_elem(vulns, host_ip, host_port, host_asset, host_name):
153
    result_elem = e.Element('result', {'id': generate_uuid()})
154
155
    e.SubElement(result_elem, 'name').text = "a_result" + generate_id()
156
    own = e.SubElement(result_elem, 'owner')
157
    e.SubElement(own, 'name').text = generate_id()
158
159
    elem = e.Element('modification_time')
160
    e.SubElement(result_elem, 'modification_time').text = (
161
        time.strftime("%Y-%m-%dT%H:%M:%S%z", time.localtime(time.time()))[:-2]
162
        + ':00'
163
    )  # Hell of a Timeformat :D
164
    e.SubElement(result_elem, 'comment').text = ''
165
    e.SubElement(result_elem, 'creation_time').text = (
166
        time.strftime("%Y-%m-%dT%H:%M:%S%z", time.localtime(time.time() - 20))[
167
            :-2
168
        ]
169
        + ':00'
170
    )
171
172
    host_elem = e.Element('host')
173
    host_elem.text = host_ip
174
    e.SubElement(host_elem, 'asset', {'asset_id': host_asset}).text = ''
175
    e.SubElement(host_elem, 'hostname').text = host_name
176
    result_elem.append(host_elem)
177
178
    port_elem = e.Element('port')
179
    port_elem.text = host_port
180
    result_elem.append(port_elem)
181
182
    nvt = vulns[randrange(len(vulns))]
183
    e.SubElement(result_elem, 'severity').text = nvt['severity']
184
    nvt_elem = e.Element('nvt', {'oid': nvt['oid']})
185
    result_elem.append(nvt_elem)
186
187
    e.SubElement(result_elem, 'notes').text = 'TestNotes'
188
189
    result_elem.append(elem)
190
191
    return result_elem, nvt['oid'], nvt['severity']
192
193
194
def generate_host_detail_elem(
195
    name, value, source_name=None, source_description=None
196
):
197
    host_detail_elem = e.Element('detail')
198
    e.SubElement(host_detail_elem, 'name').text = name
199
    e.SubElement(host_detail_elem, 'value').text = value
200
201
    if source_name:
202
        source_elem = e.SubElement(host_detail_elem, 'source')
203
        e.SubElement(source_elem, 'name').text = source_name
204
205
        if source_description:
206
            e.SubElement(source_elem, 'description').text = source_description
207
208
    return host_detail_elem
209
210
211
def generate_additional_host_details(
212
    n_details, host_details, *, not_vuln=False
213
):
214
    host_detail_elems = []
215
216
    for _ in range(n_details):
217
        details = None
218
219
        if not_vuln:
220
            details = host_details.copy()
221
            details["source_name"] += str(randrange(14259, 103585))
222
        else:
223
            details = choice(host_details)
224
225
        host_detail_elems.append(
226
            generate_host_detail_elem(
227
                details['name'],
228
                details['value'],
229
                source_name=details.get('source_name'),
230
                source_description=details.get('source_description'),
231
            )
232
        )
233
234
    return host_detail_elems
235
236
237
def generate_host_elem(
238
    host_ip, oid, host_asset, host_name, n_host_details, n_not_vuln, data
239
):
240
    host_elem = e.Element('host')
241
    e.SubElement(host_elem, 'ip').text = host_ip
242
    e.SubElement(host_elem, 'asset', {'asset_id': host_asset}).text = ''
243
244
    e.SubElement(host_elem, 'start').text = (
245
        time.strftime(
246
            "%Y-%m-%dT%H:%M:%S%z", time.localtime(time.time() - 1000)
247
        )[:-2]
248
        + ':00'
249
    )
250
    e.SubElement(host_elem, 'end').text = (
251
        time.strftime("%Y-%m-%dT%H:%M:%S%z", time.localtime(time.time() - 30))[
252
            :-2
253
        ]
254
        + ':00'
255
    )
256
257
    app = choice(list(data["apps"]))
258
    os = choice(list(data["oss"]))
259
260
    host_elem.append(
261
        generate_host_detail_elem('App', data["apps"].get(app), source_name=oid)
262
    )
263
    host_elem.append(
264
        generate_host_detail_elem(
265
            data["apps"].get(app), '/usr/bin/foo', source_name=oid
266
        )
267
    )
268
    host_elem.append(
269
        generate_host_detail_elem(
270
            'hostname',
271
            host_name,
272
            source_name=oid,
273
            source_description="Host Details",
274
        )
275
    )
276
    host_elem.append(
277
        generate_host_detail_elem(
278
            'best_os_txt',
279
            list(os)[0],
280
            source_name=oid,
281
            source_description="Host Details",
282
        )
283
    )
284
    host_elem.append(
285
        generate_host_detail_elem(
286
            'best_os_cpe',
287
            data["oss"].get(os),
288
            source_name=oid,
289
            source_description="Host Details",
290
        )
291
    )
292
293
    if n_host_details:
294
        host_elem.extend(
295
            generate_additional_host_details(
296
                n_host_details, data["host_details"]
297
            )
298
        )
299
300
    dev = n_not_vuln / 10
301
    if n_not_vuln:
302
        host_elem.extend(
303
            generate_additional_host_details(
304
                n_not_vuln + randrange(-dev, dev),
305
                data["not_vuln"],
306
                not_vuln=True,
307
            )
308
        )
309
310
    return host_elem
311
312
313
def generate_reports(task, n_reports, with_gauss, **kwargs):
314
    reports = []
315
316
    if with_gauss:
317
        n_reports = abs(int(gauss(n_reports, 1)))
318
        if n_reports == 0:
319
            n_reports += 1
320
321
    for _ in range(n_reports):
322
        if with_gauss:
323
            n_results = abs(int(gauss(n_results, 2)))
0 ignored issues
show
The variable n_results does not seem to be defined for all execution paths.
Loading history...
324
325
        report_elem = generate_report_elem(task, **kwargs)
326
        report_elem = e.tostring(report_elem)
327
        reports.append(report_elem)
328
329
    return reports
330
331
332
def generate_data(gmp, n_tasks, **kwargs):
333
    for i in range(n_tasks):
334
        index = '{{0:0>{}}}'.format(len(str(n_tasks)))
335
        task_name = 'Task_for_GenReport:_{}'.format(index.format(i + 1))
336
337
        gmp.create_container_task(task_name)
338
339
        task_id = gmp.get_tasks(filter='name={}'.format(task_name)).xpath(
340
            '//@id'
341
        )[0]
342
343
        reports = generate_reports(task=(task_id, task_name), **kwargs)
344
345
        for report in reports[0:]:
346
            gmp.import_report(report, task_id=task_id, in_assets=True)
347
348
349
def main(gmp, args):
350
    # pylint: disable=undefined-variable, line-too-long
351
352
    parser = ArgumentParser(
353
        prog="random-report-gen",
354
        prefix_chars="-",
355
        description=HELP_TEXT,
356
        formatter_class=RawTextHelpFormatter,
357
        add_help=False,
358
        epilog=textwrap.dedent(
359
            """
360
        Example:
361
            $ gvm-script --gmp-username name --gmp-password pass
362
            ssh --hostname <gsm> scripts/gen-random-reports.gmp.py -T 5 -r 4 -R 3 --hosts 10
363
        """
364
        ),
365
    )
366
367
    parser.add_argument(
368
        "-H", action="help", help="Show this help message and exit."
369
    )
370
371
    parser.add_argument(
372
        "--datafile",
373
        default=Path(args.script[0]).parent / "default_report_data.json",
374
        help="A json file containing the following information: "
375
        "vulnerabilities, operating systems, applications and host details. "
376
        "Take the default json file as an example.",
377
    )
378
379
    parser.add_argument(
380
        "--tasks",
381
        "-T",
382
        type=int,
383
        default="1",
384
        help="Number of Tasks to be generated.",
385
    )
386
387
    parser.add_argument(
388
        "--reports",
389
        "-r",
390
        type=int,
391
        default="5",
392
        help="Number of Reports per Task.",
393
    )
394
395
    parser.add_argument(
396
        "--results",
397
        "-R",
398
        type=int,
399
        default="5",
400
        help="Number of Results per Report.",
401
    )
402
403
    parser.add_argument(
404
        "--hosts",
405
        type=int,
406
        default="5",
407
        help="Number of randomized hosts to select from.",
408
    )
409
410
    parser.add_argument(
411
        "--host-details",
412
        dest="host_details",
413
        type=int,
414
        default="2",
415
        help="Number of additional host details per host.",
416
    )
417
418
    parser.add_argument(
419
        "--not-vuln-details",
420
        dest="not_vuln",
421
        type=int,
422
        default="10",
423
        help="Number of 'NOT_VULN' host details per host.",
424
    )
425
426
    parser.add_argument(
427
        "--with-gauss",
428
        dest="with_gauss",
429
        action="store_true",
430
        help="if you would like for the number of reports/task and "
431
        "results/report to be randomized along a Gaussian distribution.",
432
    )
433
434
    parser.add_argument(
435
        "--seed", help="RNG Seed, in case the same data should be generated."
436
    )
437
438
    script_args = parser.parse_args(args.script_args)
439
440
    if not script_args.seed:
441
        seed()
442
    else:
443
        seed(script_args.seed)
444
445
    with open(str(script_args.datafile)) as file:
446
        data = json.load(file)
447
448
    print('\n  Generating randomized data(s)...\n')
449
450
    generate_data(
451
        gmp,
452
        n_tasks=script_args.tasks,
453
        n_reports=script_args.reports,
454
        n_results=script_args.results,
455
        n_hosts=script_args.hosts,
456
        n_host_details=script_args.host_details,
457
        n_not_vuln=script_args.not_vuln,
458
        data=data,
459
        with_gauss=script_args.with_gauss,
460
    )
461
462
    print('\n  Generation done.\n')
463
464
465
if __name__ == '__gmp__':
466
    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...
467