Test Failed
Push — master ( 45848e...931d35 )
by Jan
03:00 queued 11s
created

ssg.build_profile.RuleStats.__init__()   C

Complexity

Conditions 9

Size

Total Lines 44
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 90

Importance

Changes 0
Metric Value
cc 9
eloc 41
nop 17
dl 0
loc 44
ccs 0
cts 18
cp 0
crap 90
rs 6.5626
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
from __future__ import absolute_import
2
from __future__ import print_function
3
4
import os
5
import sys
6
7
from .build_yaml import ProfileWithInlinePolicies
8
from .xml import ElementTree
9
from .constants import XCCDF11_NS as xccdf_ns
10
from .constants import oval_namespace as oval_ns
11
from .constants import SCE_SYSTEM as sce_ns
12
from .constants import bash_system as bash_rem_system
13
from .constants import ansible_system as ansible_rem_system
14
from .constants import ignition_system as ignition_rem_system
15
from .constants import kubernetes_system as kubernetes_rem_system
16
from .constants import puppet_system as puppet_rem_system
17
from .constants import anaconda_system as anaconda_rem_system
18
from .constants import cce_uri
19
from .constants import ssg_version_uri
20
from .constants import stig_ns, cis_ns, generic_stig_ns, hipaa_ns, anssi_ns
21
from .constants import ospp_ns, cui_ns, xslt_ns
22
console_width = 80
23
24
25
def make_name_to_profile_mapping(profile_files, env_yaml):
26
    name_to_profile = {}
27
    for f in profile_files:
28
        try:
29
            p = ProfileWithInlinePolicies.from_yaml(f, env_yaml)
30
            name_to_profile[p.id_] = p
31
        except Exception as exc:
32
            # The profile is probably doc-incomplete
33
            msg = "Not building profile from {fname}: {err}".format(
34
                fname=f, err=str(exc))
35
            print(msg, file=sys.stderr)
36
    return name_to_profile
37
38
39
class RuleStats(object):
40
    """
41
    Class representing the content of a rule for statistics generation
42
    purposes.
43
    """
44
    def __init__(self, rid=None, roval=None, rsce=None,
45
                 rbash_fix=None, ransible_fix=None,
46
                 rignition_fix=None, rkubernetes_fix=None,
47
                 rpuppet_fix=None, ranaconda_fix=None, rcce=None,
48
                 stig_id=None, cis_ref=None, hipaa_ref=None,
49
                 anssi_ref=None, ospp_ref=None, cui_ref=None):
50
        self.dict = {
51
            'id': rid,
52
            'oval': roval,
53
            'sce': rsce,
54
            'check': None,
55
            'bash_fix': rbash_fix,
56
            'ansible_fix': ransible_fix,
57
            'ignition_fix': rignition_fix,
58
            'kubernetes_fix': rkubernetes_fix,
59
            'puppet_fix': rpuppet_fix,
60
            'anaconda_fix': ranaconda_fix,
61
            'fix': None,
62
            'cce': rcce,
63
            'stig_id': stig_id,
64
            'cis_ref': cis_ref,
65
            'hipaa_ref': hipaa_ref,
66
            'anssi_ref': anssi_ref,
67
            'ospp_ref': ospp_ref,
68
            'cui_ref': cui_ref,
69
        }
70
71
        if roval is not None:
72
            self.dict['check'] = roval
73
        elif rsce is not None:
74
            self.dict['check'] = rsce
75
76
        if rbash_fix is not None:
77
            self.dict['fix'] = rbash_fix
78
        elif ransible_fix is not None:
79
            self.dict['fix'] = ransible_fix
80
        elif rignition_fix is not None:
81
            self.dict['fix'] = rignition_fix
82
        elif rkubernetes_fix is not None:
83
            self.dict['fix'] = rkubernetes_fix
84
        elif rpuppet_fix is not None:
85
            self.dict['fix'] = rpuppet_fix
86
        elif ranaconda_fix is not None:
87
            self.dict['fix'] = ranaconda_fix
88
89
90
class XCCDFBenchmark(object):
91
    """
92
    Class for processing an XCCDF benchmark to generate
93
    statistics about the profiles contained within it.
94
    """
95
96
    def __init__(self, filepath, product=""):
97
        self.tree = None
98
        try:
99
            with open(filepath, 'r') as xccdf_file:
100
                file_string = xccdf_file.read()
101
                tree = ElementTree.fromstring(file_string)
102
                self.tree = tree
103
        except IOError as ioerr:
104
            print("%s" % ioerr)
105
            sys.exit(1)
106
107
        self.indexed_rules = {}
108
        for rule in self.tree.findall(".//{%s}Rule" % (xccdf_ns)):
109
            rule_id = rule.get("id")
110
            if rule_id is None:
111
                raise RuntimeError("Can't index a rule with no id attribute!")
112
113
            if rule_id in self.indexed_rules:
114
                raise RuntimeError("Multiple rules exist with same id attribute: %s!" % rule_id)
115
116
            self.indexed_rules[rule_id] = rule
117
118
        self.cis_ns = cis_ns
119
        self.stig_ns = stig_ns
120
        if product:
121
            constants_path = os.path.join(product, "transforms/constants.xslt")
122
            if os.path.exists(constants_path):
123
                root = ElementTree.parse(constants_path)
124
                cis_var = root.find('./{%s}variable[@name="cisuri"]' % (xslt_ns))
125
                if cis_var is not None and cis_var.text:
126
                    self.cis_ns = cis_var.text
127
128
                stig_var = root.find('./{%s}variable[@name="disa-stigs-uri"]' % (xslt_ns))
129
                if stig_var is not None and stig_var.text:
130
                    self.stig_ns = stig_var.text
131
                elif (stig_var and 'select' in stig_var.attrib and
132
                      stig_var.attrib['select'] == '$disa-stigs-os-unix-linux-uri'):
133
                    self.stig_ns = generic_stig_ns
134
135
136
    def get_profile_stats(self, profile):
137
        """Obtain statistics for the profile"""
138
139
        # Holds the intermediary statistics for profile
140
        profile_stats = {
141
            'profile_id': "",
142
            'ssg_version': 0,
143
            'rules': [],
144
            'rules_count': 0,
145
            'implemented_ovals': [],
146
            'implemented_ovals_pct': 0,
147
            'missing_ovals': [],
148
            'implemented_sces': [],
149
            'implemented_sces_pct': 0,
150
            'missing_sces': [],
151
            'implemented_checks': [],
152
            'implemented_checks_pct': 0,
153
            'missing_checks': [],
154
            'implemented_bash_fixes': [],
155
            'implemented_bash_fixes_pct': 0,
156
            'implemented_ansible_fixes': [],
157
            'implemented_ansible_fixes_pct': 0,
158
            'implemented_ignition_fixes': [],
159
            'implemented_ignition_fixes_pct': 0,
160
            'implemented_kubernetes_fixes': [],
161
            'implemented_kubernetes_fixes_pct': 0,
162
            'implemented_puppet_fixes': [],
163
            'implemented_puppet_fixes_pct': 0,
164
            'implemented_anaconda_fixes': [],
165
            'implemented_anaconda_fixes_pct': 0,
166
            'missing_bash_fixes': [],
167
            'missing_ansible_fixes': [],
168
            'missing_ignition_fixes': [],
169
            'missing_kubernetes_fixes': [],
170
            'missing_puppet_fixes': [],
171
            'missing_anaconda_fixes': [],
172
            'implemented_fixes': [],
173
            'implemented_fixes_pct': 0,
174
            'missing_fixes': [],
175
            'assigned_cces': [],
176
            'assigned_cces_pct': 0,
177
            'missing_cces': [],
178
            'missing_stig_ids': [],
179
            'missing_cis_refs': [],
180
            'missing_hipaa_refs': [],
181
            'missing_anssi_refs': [],
182
            'missing_ospp_refs': [],
183
            'missing_cui_refs': [],
184
            'ansible_parity': [],
185
        }
186
187
        rule_stats = []
188
        ssg_version_elem = self.tree.find("./{%s}version[@update=\"%s\"]" %
189
                                          (xccdf_ns, ssg_version_uri))
190
191
        rules = []
192
193
        if profile == "all":
194
            # "all" is a virtual profile that selects all rules
195
            rules = self.indexed_rules.values()
196
        else:
197
            xccdf_profile = self.tree.find("./{%s}Profile[@id=\"%s\"]" %
198
                                           (xccdf_ns, profile))
199
            if xccdf_profile is None:
200
                print("No such profile \"%s\" found in the benchmark!"
201
                      % profile)
202
                print("* Available profiles:")
203
                profiles_avail = self.tree.findall("./{%s}Profile" % (xccdf_ns))
204
                for _profile in profiles_avail:
205
                    print("** %s" % _profile.get('id'))
206
                sys.exit(1)
207
208
            # This will only work with SSG where the (default) profile has zero
209
            # selected rule. If you want to reuse this for custom content, you
210
            # need to change this to look into Rule/@selected
211
            selects = xccdf_profile.findall("./{%s}select[@selected=\"true\"]" %
212
                                            xccdf_ns)
213
214
            for select in selects:
215
                rule_id = select.get('idref')
216
                xccdf_rule = self.indexed_rules.get(rule_id)
217
                if xccdf_rule is not None:
218
                    # it could also be a Group
219
                    rules.append(xccdf_rule)
220
221
        for rule in rules:
222
            if rule is not None:
223
                oval = rule.find("./{%s}check[@system=\"%s\"]" %
224
                                 (xccdf_ns, oval_ns))
225
                sce = rule.find("./{%s}check[@system=\"%s\"]" %
226
                                (xccdf_ns, sce_ns))
227
                bash_fix = rule.find("./{%s}fix[@system=\"%s\"]" %
228
                                     (xccdf_ns, bash_rem_system))
229
                ansible_fix = rule.find("./{%s}fix[@system=\"%s\"]" %
230
                                        (xccdf_ns, ansible_rem_system))
231
                ignition_fix = rule.find("./{%s}fix[@system=\"%s\"]" %
232
                                        (xccdf_ns, ignition_rem_system))
233
                kubernetes_fix = rule.find("./{%s}fix[@system=\"%s\"]" %
234
                                           (xccdf_ns, kubernetes_rem_system))
235
                puppet_fix = rule.find("./{%s}fix[@system=\"%s\"]" %
236
                                       (xccdf_ns, puppet_rem_system))
237
                anaconda_fix = rule.find("./{%s}fix[@system=\"%s\"]" %
238
                                         (xccdf_ns, anaconda_rem_system))
239
                cce = rule.find("./{%s}ident[@system=\"%s\"]" %
240
                                (xccdf_ns, cce_uri))
241
                stig_id = rule.find("./{%s}reference[@href=\"%s\"]" %
242
                                    (xccdf_ns, self.stig_ns))
243
                cis_ref = rule.find("./{%s}reference[@href=\"%s\"]" %
244
                                    (xccdf_ns, self.cis_ns))
245
                hipaa_ref = rule.find("./{%s}reference[@href=\"%s\"]" %
246
                                    (xccdf_ns, hipaa_ns))
247
                anssi_ref = rule.find("./{%s}reference[@href=\"%s\"]" %
248
                                    (xccdf_ns, anssi_ns))
249
                ospp_ref = rule.find("./{%s}reference[@href=\"%s\"]" %
250
                                     (xccdf_ns, ospp_ns))
251
                cui_ref = rule.find("./{%s}reference[@href=\"%s\"]" %
252
                                    (xccdf_ns, cui_ns))
253
254
                rule_stats.append(
255
                    RuleStats(rule.get("id"), oval, sce,
256
                              bash_fix, ansible_fix, ignition_fix,
257
                              kubernetes_fix, puppet_fix, anaconda_fix,
258
                              cce, stig_id, cis_ref, hipaa_ref, anssi_ref,
259
                              ospp_ref, cui_ref)
260
                )
261
262
        if not rule_stats:
263
            print('Unable to retrieve statistics for %s profile' % profile)
264
            sys.exit(1)
265
266
        rule_stats.sort(key=lambda r: r.dict['id'])
267
268
        for rule in rule_stats:
269
            profile_stats['rules'].append(rule.dict['id'])
270
271
        profile_stats['profile_id'] = profile
272
        if ssg_version_elem is not None:
273
            profile_stats['ssg_version'] = \
274
                'SCAP Security Guide %s' % ssg_version_elem.text
275
        profile_stats['rules_count'] = len(rule_stats)
276
        profile_stats['implemented_ovals'] = \
277
            [x.dict['id'] for x in rule_stats if x.dict['oval'] is not None]
278
        profile_stats['implemented_ovals_pct'] = \
279
            float(len(profile_stats['implemented_ovals'])) / \
280
            profile_stats['rules_count'] * 100
281
        profile_stats['missing_ovals'] = \
282
            [x.dict['id'] for x in rule_stats if x.dict['oval'] is None]
283
284
        profile_stats['implemented_sces'] = \
285
            [x.dict['id'] for x in rule_stats if x.dict['sce'] is not None]
286
        profile_stats['implemented_sces_pct'] = \
287
            float(len(profile_stats['implemented_sces'])) / \
288
            profile_stats['rules_count'] * 100
289
        profile_stats['missing_sces'] = \
290
            [x.dict['id'] for x in rule_stats if x.dict['sce'] is None]
291
292
        profile_stats['implemented_checks'] = \
293
            [x.dict['id'] for x in rule_stats if x.dict['check'] is not None]
294
        profile_stats['implemented_checks_pct'] = \
295
            float(len(profile_stats['implemented_checks'])) / \
296
            profile_stats['rules_count'] * 100
297
        profile_stats['missing_checks'] = \
298
            [x.dict['id'] for x in rule_stats if x.dict['check'] is None]
299
300
        profile_stats['implemented_bash_fixes'] = \
301
            [x.dict['id'] for x in rule_stats if x.dict['bash_fix'] is not None]
302
        profile_stats['implemented_bash_fixes_pct'] = \
303
            float(len(profile_stats['implemented_bash_fixes'])) / \
304
            profile_stats['rules_count'] * 100
305
        profile_stats['missing_bash_fixes'] = \
306
            [x.dict['id'] for x in rule_stats if x.dict['bash_fix'] is None]
307
308
        profile_stats['implemented_ansible_fixes'] = \
309
            [x.dict['id'] for x in rule_stats if x.dict['ansible_fix'] is not None]
310
        profile_stats['implemented_ansible_fixes_pct'] = \
311
            float(len(profile_stats['implemented_ansible_fixes'])) / \
312
            profile_stats['rules_count'] * 100
313
        profile_stats['missing_ansible_fixes'] = \
314
            [x.dict['id'] for x in rule_stats if x.dict['ansible_fix'] is None]
315
316
        profile_stats['implemented_ignition_fixes'] = \
317
            [x.dict['id'] for x in rule_stats if x.dict['ignition_fix'] is not None]
318
        profile_stats['implemented_ignition_fixes_pct'] = \
319
            float(len(profile_stats['implemented_ignition_fixes'])) / \
320
            profile_stats['rules_count'] * 100
321
        profile_stats['missing_ignition_fixes'] = \
322
            [x.dict['id'] for x in rule_stats if x.dict['ignition_fix'] is None]
323
324
        profile_stats['implemented_kubernetes_fixes'] = \
325
            [x.dict['id'] for x in rule_stats if x.dict['kubernetes_fix'] is not None]
326
        profile_stats['implemented_kubernetes_fixes_pct'] = \
327
            float(len(profile_stats['implemented_kubernetes_fixes'])) / \
328
            profile_stats['rules_count'] * 100
329
        profile_stats['missing_kubernetes_fixes'] = \
330
            [x.dict['id'] for x in rule_stats if x.dict['kubernetes_fix'] is None]
331
332
        profile_stats['implemented_puppet_fixes'] = \
333
            [x.dict['id'] for x in rule_stats if x.dict['puppet_fix'] is not None]
334
        profile_stats['implemented_puppet_fixes_pct'] = \
335
            float(len(profile_stats['implemented_puppet_fixes'])) / \
336
            profile_stats['rules_count'] * 100
337
        profile_stats['missing_puppet_fixes'] = \
338
            [x.dict['id'] for x in rule_stats if x.dict['puppet_fix'] is None]
339
340
        profile_stats['implemented_anaconda_fixes'] = \
341
            [x.dict['id'] for x in rule_stats if x.dict['anaconda_fix'] is not None]
342
343
        profile_stats['implemented_fixes'] = \
344
            [x.dict['id'] for x in rule_stats if x.dict['fix'] is not None]
345
        profile_stats['implemented_fixes_pct'] = \
346
            float(len(profile_stats['implemented_fixes'])) / \
347
            profile_stats['rules_count'] * 100
348
        profile_stats['missing_fixes'] = \
349
            [x.dict['id'] for x in rule_stats if x.dict['fix'] is None]
350
351
        profile_stats['missing_stig_ids'] = []
352
        if 'stig' in profile_stats['profile_id']:
353
            profile_stats['missing_stig_ids'] = \
354
                [x.dict['id'] for x in rule_stats if x.dict['stig_id'] is None]
355
356
        profile_stats['missing_cis_refs'] = []
357
        if 'cis' in profile_stats['profile_id']:
358
            profile_stats['missing_cis_refs'] = \
359
                [x.dict['id'] for x in rule_stats if x.dict['cis_ref'] is None]
360
361
        profile_stats['missing_hipaa_refs'] = []
362
        if 'hipaa' in profile_stats['profile_id']:
363
            profile_stats['missing_hipaa_refs'] = \
364
                [x.dict['id'] for x in rule_stats if x.dict['hipaa_ref'] is None]
365
366
        profile_stats['missing_anssi_refs'] = []
367
        if 'anssi' in profile_stats['profile_id']:
368
            profile_stats['missing_anssi_refs'] = \
369
                [x.dict['id'] for x in rule_stats if x.dict['anssi_ref'] is None]
370
371
        profile_stats['missing_ospp_refs'] = []
372
        if 'ospp' in profile_stats['profile_id']:
373
            profile_stats['missing_ospp_refs'] = \
374
                [x.dict['id'] for x in rule_stats if x.dict['ospp_ref'] is None]
375
376
        profile_stats['missing_cui_refs'] = []
377
        if 'cui' in profile_stats['profile_id']:
378
            profile_stats['missing_cui_refs'] = \
379
                [x.dict['id'] for x in rule_stats if x.dict['cui_ref'] is None]
380
381
        profile_stats['implemented_anaconda_fixes_pct'] = \
382
            float(len(profile_stats['implemented_anaconda_fixes'])) / \
383
            profile_stats['rules_count'] * 100
384
        profile_stats['missing_anaconda_fixes'] = \
385
            [x.dict['id'] for x in rule_stats if x.dict['anaconda_fix'] is None]
386
387
        profile_stats['assigned_cces'] = \
388
            [x.dict['id'] for x in rule_stats if x.dict['cce'] is not None]
389
        profile_stats['assigned_cces_pct'] = \
390
            float(len(profile_stats['assigned_cces'])) / \
391
            profile_stats['rules_count'] * 100
392
        profile_stats['missing_cces'] = \
393
            [x.dict['id'] for x in rule_stats if x.dict['cce'] is None]
394
395
        profile_stats['ansible_parity'] = \
396
            [rule_id for rule_id in profile_stats["missing_ansible_fixes"] if rule_id not in profile_stats["missing_bash_fixes"]]
397
        profile_stats['ansible_parity_pct'] = 0
398
        if len(profile_stats['implemented_bash_fixes']):
399
            profile_stats['ansible_parity_pct'] = \
400
                float(len(profile_stats['implemented_bash_fixes']) -
401
                      len(profile_stats['ansible_parity'])) / \
402
                len(profile_stats['implemented_bash_fixes']) * 100
403
404
        return profile_stats
405
406
    def show_profile_stats(self, profile, options):
407
        """Displays statistics for specific profile"""
408
409
        profile_stats = self.get_profile_stats(profile)
410
        rules_count = profile_stats['rules_count']
411
        impl_ovals_count = len(profile_stats['implemented_ovals'])
412
        impl_sces_count = len(profile_stats['implemented_sces'])
413
        impl_checks_count = len(profile_stats['implemented_checks'])
414
        impl_bash_fixes_count = len(profile_stats['implemented_bash_fixes'])
415
        impl_ansible_fixes_count = len(profile_stats['implemented_ansible_fixes'])
416
        impl_ignition_fixes_count = len(profile_stats['implemented_ignition_fixes'])
417
        impl_kubernetes_fixes_count = len(profile_stats['implemented_kubernetes_fixes'])
418
        impl_puppet_fixes_count = len(profile_stats['implemented_puppet_fixes'])
419
        impl_anaconda_fixes_count = len(profile_stats['implemented_anaconda_fixes'])
420
        impl_fixes_count = len(profile_stats['implemented_fixes'])
421
        missing_stig_ids_count = len(profile_stats['missing_stig_ids'])
422
        missing_cis_refs_count = len(profile_stats['missing_cis_refs'])
423
        missing_hipaa_refs_count = len(profile_stats['missing_hipaa_refs'])
424
        missing_anssi_refs_count = len(profile_stats['missing_anssi_refs'])
425
        missing_ospp_refs_count = len(profile_stats['missing_ospp_refs'])
426
        missing_cui_refs_count = len(profile_stats['missing_cui_refs'])
427
        impl_cces_count = len(profile_stats['assigned_cces'])
428
429
        if options.format == "plain":
430
            if not options.skip_overall_stats:
431
                print("\nProfile %s:" % profile)
432
                print("* rules:              %d" % rules_count)
433
                print("* checks (OVAL):      %d\t[%d%% complete]" %
434
                      (impl_ovals_count,
435
                       profile_stats['implemented_ovals_pct']))
436
                print("* checks (SCE):       %d\t[%d%% complete]" %
437
                      (impl_sces_count,
438
                       profile_stats['implemented_sces_pct']))
439
                print("* checks (any):       %d\t[%d%% complete]" %
440
                      (impl_checks_count,
441
                       profile_stats['implemented_checks_pct']))
442
443
                print("* fixes (bash):       %d\t[%d%% complete]" %
444
                      (impl_bash_fixes_count,
445
                       profile_stats['implemented_bash_fixes_pct']))
446
                print("* fixes (ansible):    %d\t[%d%% complete]" %
447
                      (impl_ansible_fixes_count,
448
                       profile_stats['implemented_ansible_fixes_pct']))
449
                print("* fixes (ignition):   %d\t[%d%% complete]" %
450
                      (impl_ignition_fixes_count,
451
                       profile_stats['implemented_ignition_fixes_pct']))
452
                print("* fixes (kubernetes): %d\t[%d%% complete]" %
453
                      (impl_kubernetes_fixes_count,
454
                       profile_stats['implemented_kubernetes_fixes_pct']))
455
                print("* fixes (puppet):     %d\t[%d%% complete]" %
456
                      (impl_puppet_fixes_count,
457
                       profile_stats['implemented_puppet_fixes_pct']))
458
                print("* fixes (anaconda):   %d\t[%d%% complete]" %
459
                      (impl_anaconda_fixes_count,
460
                       profile_stats['implemented_anaconda_fixes_pct']))
461
                print("* fixes (any):        %d\t[%d%% complete]" %
462
                      (impl_fixes_count,
463
                       profile_stats['implemented_fixes_pct']))
464
465
                print("* CCEs:               %d\t[%d%% complete]" %
466
                      (impl_cces_count,
467
                       profile_stats['assigned_cces_pct']))
468
469
            if options.implemented_ovals and \
470
               profile_stats['implemented_ovals']:
471
                print("** Rules of '%s' " % profile +
472
                      "profile having OVAL check: %d of %d [%d%% complete]" %
473
                      (impl_ovals_count, rules_count,
474
                       profile_stats['implemented_ovals_pct']))
475
                self.console_print(profile_stats['implemented_ovals'],
476
                                   console_width)
477
478
            if options.implemented_sces and \
479
               profile_stats['implemented_sces']:
480
                print("** Rules of '%s' " % profile +
481
                      "profile having SCE check: %d of %d [%d%% complete]" %
482
                      (impl_sces_count, rules_count,
483
                       profile_stats['implemented_sces_pct']))
484
                self.console_print(profile_stats['implemented_sces'],
485
                                   console_width)
486
487
            if options.implemented_fixes:
488
                if profile_stats['implemented_bash_fixes']:
489
                    print("*** Rules of '%s' profile having "
490
                          "a bash fix script: %d of %d [%d%% complete]"
491
                          % (profile, impl_bash_fixes_count, rules_count,
492
                             profile_stats['implemented_bash_fixes_pct']))
493
                    self.console_print(profile_stats['implemented_bash_fixes'],
494
                                       console_width)
495
496
                if profile_stats['implemented_ansible_fixes']:
497
                    print("*** Rules of '%s' profile having "
498
                          "a ansible fix script: %d of %d [%d%% complete]"
499
                          % (profile, impl_ansible_fixes_count, rules_count,
500
                             profile_stats['implemented_ansible_fixes_pct']))
501
                    self.console_print(
502
                        profile_stats['implemented_ansible_fixes'],
503
                        console_width)
504
505
                if profile_stats['implemented_ignition_fixes']:
506
                    print("*** Rules of '%s' profile having "
507
                          "a ignition fix script: %d of %d [%d%% complete]"
508
                          % (profile, impl_ignition_fixes_count, rules_count,
509
                             profile_stats['implemented_ignition_fixes_pct']))
510
                    self.console_print(
511
                        profile_stats['implemented_ignition_fixes'],
512
                        console_width)
513
514
                if profile_stats['implemented_kubernetes_fixes']:
515
                    print("*** Rules of '%s' profile having "
516
                          "a kubernetes fix script: %d of %d [%d%% complete]"
517
                          % (profile, impl_kubernetes_fixes_count, rules_count,
518
                             profile_stats['implemented_kubernetes_fixes_pct']))
519
                    self.console_print(
520
                        profile_stats['implemented_kubernetes_fixes'],
521
                        console_width)
522
523
                if profile_stats['implemented_puppet_fixes']:
524
                    print("*** Rules of '%s' profile having "
525
                          "a puppet fix script: %d of %d [%d%% complete]"
526
                          % (profile, impl_puppet_fixes_count, rules_count,
527
                             profile_stats['implemented_puppet_fixes_pct']))
528
                    self.console_print(
529
                        profile_stats['implemented_puppet_fixes'],
530
                        console_width)
531
532
                if profile_stats['implemented_anaconda_fixes']:
533
                    print("*** Rules of '%s' profile having "
534
                          "a anaconda fix script: %d of %d [%d%% complete]"
535
                          % (profile, impl_anaconda_fixes_count, rules_count,
536
                             profile_stats['implemented_anaconda_fixes_pct']))
537
                    self.console_print(
538
                        profile_stats['implemented_anaconda_fixes'],
539
                        console_width)
540
541
            if options.assigned_cces and \
542
               profile_stats['assigned_cces']:
543
                print("*** Rules of '%s' " % profile +
544
                      "profile having CCE assigned: %d of %d [%d%% complete]" %
545
                      (impl_cces_count, rules_count,
546
                       profile_stats['assigned_cces_pct']))
547
                self.console_print(profile_stats['assigned_cces'],
548
                                   console_width)
549
550
            if options.missing_ovals and profile_stats['missing_ovals']:
551
                print("*** Rules of '%s' " % profile + "profile missing " +
552
                      "OVAL: %d of %d [%d%% complete]" %
553
                      (rules_count - impl_ovals_count, rules_count,
554
                       profile_stats['implemented_ovals_pct']))
555
                self.console_print(profile_stats['missing_ovals'],
556
                                   console_width)
557
558
            if options.missing_sces and profile_stats['missing_sces']:
559
                print("*** Rules of '%s' " % profile + "profile missing " +
560
                      "SCE: %d of %d [%d%% complete]" %
561
                      (rules_count - impl_sces_count, rules_count,
562
                       profile_stats['implemented_sces_pct']))
563
                self.console_print(profile_stats['missing_sces'],
564
                                   console_width)
565
566
            if options.missing_fixes:
567
                if profile_stats['missing_bash_fixes']:
568
                    print("*** rules of '%s' profile missing "
569
                          "a bash fix script: %d of %d [%d%% complete]"
570
                          % (profile, rules_count - impl_bash_fixes_count,
571
                             rules_count,
572
                             profile_stats['implemented_bash_fixes_pct']))
573
                    self.console_print(profile_stats['missing_bash_fixes'],
574
                                       console_width)
575
576
                if profile_stats['missing_ansible_fixes']:
577
                    print("*** rules of '%s' profile missing "
578
                          "a ansible fix script: %d of %d [%d%% complete]"
579
                          % (profile, rules_count - impl_ansible_fixes_count,
580
                             rules_count,
581
                             profile_stats['implemented_ansible_fixes_pct']))
582
                    self.console_print(profile_stats['missing_ansible_fixes'],
583
                                       console_width)
584
585
                if profile_stats['missing_ignition_fixes']:
586
                    print("*** rules of '%s' profile missing "
587
                          "a ignition fix script: %d of %d [%d%% complete]"
588
                          % (profile, rules_count - impl_ignition_fixes_count,
589
                             rules_count,
590
                             profile_stats['implemented_ignition_fixes_pct']))
591
                    self.console_print(profile_stats['missing_ignition_fixes'],
592
                                       console_width)
593
594
                if profile_stats['missing_kubernetes_fixes']:
595
                    print("*** rules of '%s' profile missing "
596
                          "a kubernetes fix script: %d of %d [%d%% complete]"
597
                          % (profile, rules_count - impl_kubernetes_fixes_count,
598
                             rules_count,
599
                             profile_stats['implemented_kubernetes_fixes_pct']))
600
                    self.console_print(profile_stats['missing_kubernetes_fixes'],
601
                                       console_width)
602
603
                if profile_stats['missing_puppet_fixes']:
604
                    print("*** rules of '%s' profile missing "
605
                          "a puppet fix script: %d of %d [%d%% complete]"
606
                          % (profile, rules_count - impl_puppet_fixes_count,
607
                             rules_count,
608
                             profile_stats['implemented_puppet_fixes_pct']))
609
                    self.console_print(profile_stats['missing_puppet_fixes'],
610
                                       console_width)
611
612
                if profile_stats['missing_anaconda_fixes']:
613
                    print("*** rules of '%s' profile missing "
614
                          "a anaconda fix script: %d of %d [%d%% complete]"
615
                          % (profile, rules_count - impl_anaconda_fixes_count,
616
                             rules_count,
617
                             profile_stats['implemented_anaconda_fixes_pct']))
618
                    self.console_print(profile_stats['missing_anaconda_fixes'],
619
                                       console_width)
620
621
            if options.missing_stig_ids and profile_stats['missing_stig_ids']:
622
                print("*** rules of '%s' profile missing "
623
                      "STIG IDs: %d of %d have them [%d%% missing]"
624
                      % (profile, rules_count - missing_stig_ids_count,
625
                         rules_count,
626
                         (100.0 * missing_stig_ids_count / rules_count)))
627
                self.console_print(profile_stats['missing_stig_ids'],
628
                                   console_width)
629
630
            if options.missing_cis_refs and profile_stats['missing_cis_refs']:
631
                print("*** rules of '%s' profile missing "
632
                      "CIS Refs: %d of %d have them [%d%% missing]"
633
                      % (profile, rules_count - missing_cis_refs_count,
634
                         rules_count,
635
                         (100.0 * missing_cis_refs_count / rules_count)))
636
                self.console_print(profile_stats['missing_cis_refs'],
637
                                   console_width)
638
639
            if options.missing_hipaa_refs and profile_stats['missing_hipaa_refs']:
640
                print("*** rules of '%s' profile missing "
641
                      "HIPAA Refs: %d of %d have them [%d%% missing]"
642
                      % (profile, rules_count - missing_hipaa_refs_count,
643
                         rules_count,
644
                         (100.0 * missing_hipaa_refs_count / rules_count)))
645
                self.console_print(profile_stats['missing_hipaa_refs'],
646
                                   console_width)
647
648
            if options.missing_anssi_refs and profile_stats['missing_anssi_refs']:
649
                print("*** rules of '%s' profile missing "
650
                      "ANSSI Refs: %d of %d have them [%d%% missing]"
651
                      % (profile, rules_count - missing_anssi_refs_count,
652
                         rules_count,
653
                         (100.0 * missing_anssi_refs_count / rules_count)))
654
                self.console_print(profile_stats['missing_anssi_refs'],
655
                                   console_width)
656
657
            if options.missing_ospp_refs and profile_stats['missing_ospp_refs']:
658
                print("*** rules of '%s' profile missing "
659
                      "OSPP Refs: %d of %d have them [%d%% missing]"
660
                      % (profile, rules_count - missing_ospp_refs_count,
661
                         rules_count,
662
                         (100.0 * missing_ospp_refs_count / rules_count)))
663
                self.console_print(profile_stats['missing_ospp_refs'],
664
                                   console_width)
665
666
            if options.missing_cui_refs and profile_stats['missing_cui_refs']:
667
                print("*** rules of '%s' profile missing "
668
                      "CUI Refs: %d of %d have them [%d%% missing]"
669
                      % (profile, rules_count - missing_cui_refs_count,
670
                         rules_count,
671
                         (100.0 * missing_cui_refs_count / rules_count)))
672
                self.console_print(profile_stats['missing_cui_refs'],
673
                                   console_width)
674
675
            if options.missing_cces and profile_stats['missing_cces']:
676
                print("***Rules of '%s' " % profile + "profile missing " +
677
                      "CCE identifier: %d of %d [%d%% complete]" %
678
                      (rules_count - impl_cces_count, rules_count,
679
                       profile_stats['assigned_cces_pct']))
680
                self.console_print(profile_stats['missing_cces'],
681
                                   console_width)
682
683
            if options.ansible_parity:
684
                print("*** rules of '%s' profile with bash fix that implement "
685
                      "ansible fix scripts: %d out of %d [%d%% complete]"
686
                      % (profile, impl_bash_fixes_count - len(profile_stats['ansible_parity']),
687
                         impl_bash_fixes_count,
688
                         profile_stats['ansible_parity_pct']))
689
                self.console_print(profile_stats['ansible_parity'],
690
                                   console_width)
691
692
        elif options.format == "html":
693
            del profile_stats['implemented_ovals']
694
            del profile_stats['implemented_sces']
695
            del profile_stats['implemented_bash_fixes']
696
            del profile_stats['implemented_ansible_fixes']
697
            del profile_stats['implemented_ignition_fixes']
698
            del profile_stats['implemented_kubernetes_fixes']
699
            del profile_stats['implemented_puppet_fixes']
700
            del profile_stats['implemented_anaconda_fixes']
701
            del profile_stats['assigned_cces']
702
703
            profile_stats['missing_stig_ids_count'] = missing_stig_ids_count
704
            profile_stats['missing_cis_refs_count'] = missing_cis_refs_count
705
            profile_stats['missing_hipaa_refs_count'] = missing_hipaa_refs_count
706
            profile_stats['missing_anssi_refs_count'] = missing_anssi_refs_count
707
            profile_stats['missing_ospp_refs_count'] = missing_ospp_refs_count
708
            profile_stats['missing_cui_refs_count'] = missing_cui_refs_count
709
            profile_stats['missing_ovals_count'] = len(profile_stats['missing_ovals'])
710
            profile_stats['missing_sces_count'] = len(profile_stats['missing_sces'])
711
            profile_stats['missing_bash_fixes_count'] = len(profile_stats['missing_bash_fixes'])
712
            profile_stats['missing_ansible_fixes_count'] = len(profile_stats['missing_ansible_fixes'])
713
            profile_stats['missing_ignition_fixes_count'] = len(profile_stats['missing_ignition_fixes'])
714
            profile_stats['missing_kubernetes_fixes_count'] = \
715
                    len(profile_stats['missing_kubernetes_fixes'])
716
            profile_stats['missing_puppet_fixes_count'] = len(profile_stats['missing_puppet_fixes'])
717
            profile_stats['missing_anaconda_fixes_count'] = len(profile_stats['missing_anaconda_fixes'])
718
            profile_stats['missing_cces_count'] = len(profile_stats['missing_cces'])
719
720
            del profile_stats['implemented_ovals_pct']
721
            del profile_stats['implemented_sces_pct']
722
            del profile_stats['implemented_bash_fixes_pct']
723
            del profile_stats['implemented_ansible_fixes_pct']
724
            del profile_stats['implemented_ignition_fixes_pct']
725
            del profile_stats['implemented_kubernetes_fixes_pct']
726
            del profile_stats['implemented_puppet_fixes_pct']
727
            del profile_stats['implemented_anaconda_fixes_pct']
728
            del profile_stats['assigned_cces_pct']
729
            del profile_stats['ssg_version']
730
            del profile_stats['ansible_parity_pct']
731
732
            return profile_stats
733
        else:
734
            # First delete the not requested information
735
            if not options.missing_ovals:
736
                del profile_stats['missing_ovals']
737
            if not options.missing_sces:
738
                del profile_stats['missing_sces']
739
            if not options.missing_fixes:
740
                del profile_stats['missing_bash_fixes']
741
                del profile_stats['missing_ansible_fixes']
742
                del profile_stats['missing_ignition_fixes']
743
                del profile_stats['missing_kubernetes_fixes']
744
                del profile_stats['missing_puppet_fixes']
745
                del profile_stats['missing_anaconda_fixes']
746
                del profile_stats['missing_stig_ids']
747
                del profile_stats['missing_cis_refs']
748
                del profile_stats['missing_hipaa_refs']
749
                del profile_stats['missing_anssi_refs']
750
                del profile_stats['missing_ospp_refs']
751
                del profile_stats['missing_cui_refs']
752
            if not options.missing_cces:
753
                del profile_stats['missing_cces']
754
            if not options.implemented_ovals:
755
                del profile_stats['implemented_ovals']
756
            if not options.implemented_sces:
757
                del profile_stats['implemented_sces']
758
            if not options.implemented_fixes:
759
                del profile_stats['implemented_bash_fixes']
760
                del profile_stats['implemented_ansible_fixes']
761
                del profile_stats['implemented_ignition_fixes']
762
                del profile_stats['implemented_kubernetes_fixes']
763
                del profile_stats['implemented_puppet_fixes']
764
                del profile_stats['implemented_anaconda_fixes']
765
            if not options.assigned_cces:
766
                del profile_stats['assigned_cces']
767
768
            del profile_stats['rules']
769
770
            return profile_stats
771
772
    def console_print(self, content, width):
773
        """Prints the 'content' array left aligned, each time 45 characters
774
           long, each row 'width' characters wide"""
775
776
        msg = ''
777
        for item in content:
778
            if len(msg) + len(item) < width - 6:
779
                msg += '   ' + "%-45s" % item
780
            else:
781
                print("%s" % msg)
782
                msg = '   ' + "%-45s" % item
783
        if msg != '':
784
            print("%s" % msg)
785