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

ssg.build_profile.make_name_to_profile_mapping()   A

Complexity

Conditions 3

Size

Total Lines 12
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 11
nop 2
dl 0
loc 12
ccs 0
cts 10
cp 0
crap 12
rs 9.85
c 0
b 0
f 0
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