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

XCCDFBenchmark.get_profile_stats()   F

Complexity

Conditions 19

Size

Total Lines 269
Code Lines 228

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 380

Importance

Changes 0
Metric Value
cc 19
eloc 228
nop 2
dl 0
loc 269
ccs 0
cts 105
cp 0
crap 380
rs 0.4199
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like ssg.build_profile.XCCDFBenchmark.get_profile_stats() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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