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