Test Failed
Push — master ( 00964b...f887a2 )
by Jan
02:30 queued 12s
created

ssg.build_profile.RuleStats.__init__()   A

Complexity

Conditions 1

Size

Total Lines 22
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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