Test Failed
Push — master ( ad184d...ba033d )
by Milan
01:39 queued 15s
created

utils.fix_rules.add_to_the_section()   A

Complexity

Conditions 3

Size

Total Lines 18
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 14
nop 4
dl 0
loc 18
ccs 0
cts 13
cp 0
crap 12
rs 9.7
c 0
b 0
f 0
1
#!/usr/bin/env python
2
3
from __future__ import print_function
4
5
import sys
6
import os
7
import jinja2
8
import argparse
9
import json
10
import re
11
import random
12
13
from ssg import yaml, cce, products
14
from ssg.shims import input_func
15
from ssg.utils import read_file_list
16
import ssg
17
import ssg.products
18
import ssg.rules
19
import ssg.rule_yaml
20
21
22
SSG_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
23
TO_SORT = ['identifiers', 'references']
24
25
26
_COMMANDS = dict()
27
28
29
def command(name, description):
30
    def wrapper(wrapped):
31
        _COMMANDS[name] = wrapped
32
        wrapped.description = description
33
        return wrapped
34
    return wrapper
35
36
37 View Code Duplication
def has_empty_identifier(yaml_file, product_yaml=None):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
38
    rule = yaml.open_and_macro_expand(yaml_file, product_yaml)
39
    if 'identifiers' in rule and rule['identifiers'] is None:
40
        return True
41
42
    if 'identifiers' in rule and rule['identifiers'] is not None:
43
        for _, value in rule['identifiers'].items():
44
            if str(value).strip() == "":
45
                return True
46
    return False
47
48
49
def has_no_cce(yaml_file, product_yaml=None):
50
    rule = yaml.open_and_macro_expand(yaml_file, product_yaml)
51
    product = product_yaml["product"]
52
    if "prodtype" in rule and product not in rule["prodtype"]:
53
        return False
54
    if 'identifiers' in rule and rule['identifiers'] is None:
55
        return True
56
57
    if 'identifiers' in rule and rule['identifiers'] is not None:
58
        for ident in rule['identifiers']:
59
            if ident == "cce@" + product:
60
                return False
61
    return True
62
63
64 View Code Duplication
def has_empty_references(yaml_file, product_yaml=None):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
65
    rule = yaml.open_and_macro_expand(yaml_file, product_yaml)
66
    if 'references' in rule and rule['references'] is None:
67
        return True
68
69
    if 'references' in rule and rule['references'] is not None:
70
        for _, value in rule['references'].items():
71
            if str(value).strip() == "":
72
                return True
73
    return False
74
75
76
def has_prefix_cce(yaml_file, product_yaml=None):
77
    rule = yaml.open_and_macro_expand(yaml_file, product_yaml)
78
    if 'identifiers' in rule and rule['identifiers'] is not None:
79
        for i_type, i_value in rule['identifiers'].items():
80
            if i_type[0:3] == 'cce':
81
                has_prefix = i_value[0:3].upper() == 'CCE'
82
                remainder_valid = cce.is_cce_format_valid("CCE-" + i_value[3:])
83
                remainder_valid |= cce.is_cce_format_valid("CCE-" + i_value[4:])
84
                return has_prefix and remainder_valid
85
    return False
86
87
88
def has_invalid_cce(yaml_file, product_yaml=None):
89
    rule = yaml.open_and_macro_expand(yaml_file, product_yaml)
90
    if 'identifiers' in rule and rule['identifiers'] is not None:
91
        for i_type, i_value in rule['identifiers'].items():
92
            if i_type[0:3] == 'cce':
93
                if not cce.is_cce_value_valid("CCE-" + str(i_value)):
94
                    return True
95
    return False
96
97
98
def has_int_identifier(yaml_file, product_yaml=None):
99
    rule = yaml.open_and_macro_expand(yaml_file, product_yaml)
100
    if 'identifiers' in rule and rule['identifiers'] is not None:
101
        for _, value in rule['identifiers'].items():
102
            if type(value) != str:
103
                return True
104
    return False
105
106
107
def has_int_reference(yaml_file, product_yaml=None):
108
    rule = yaml.open_and_macro_expand(yaml_file, product_yaml)
109
    if 'references' in rule and rule['references'] is not None:
110
        for _, value in rule['references'].items():
111
            if type(value) != str:
112
                return True
113
    return False
114
115
116
def has_duplicated_subkeys(yaml_file, product_yaml=None):
117
    rule_lines = read_file_list(yaml_file)
118
    return ssg.rule_yaml.has_duplicated_subkeys(yaml_file, rule_lines, TO_SORT)
119
120
121
def has_unordered_sections(yaml_file, product_yaml=None):
122
    rule = yaml.open_and_macro_expand(yaml_file, product_yaml)
123
    if 'references' in rule or 'identifiers' in rule:
124
        rule_lines = read_file_list(yaml_file)
125
        new_lines = ssg.rule_yaml.sort_section_keys(yaml_file, rule_lines, TO_SORT)
126
127
        # Compare string representations to avoid issues with references being
128
        # different.
129
        return "\n".join(rule_lines) != "\n".join(new_lines)
130
131
    return False
132
133
134
def find_rules_generator(args, func):
135
    # Iterates over all know rules in the build system (according to
136
    # rule_dir_json.py) and attempts to load the resulting YAML files.
137
    # If they parse correctly, yield them as a result.
138
    #
139
    # Note: this has become a generator rather than returning a list of
140
    # results.
141
142
    product_yamls = dict()
143
144
    rule_dirs = json.load(open(args.json))
145
    for rule_id in rule_dirs:
146
        rule_obj = rule_dirs[rule_id]
147
148
        if 'products' not in rule_obj or not rule_obj['products']:
149
            print(rule_id, rule_obj)
150
        assert rule_obj['products']
151
        product = rule_obj['products'][0]
152
153
        if product not in product_yamls:
154
            product_path = os.path.join(args.root, "products", product, 'product.yml')
155
            product_yaml = ssg.products.load_product_yaml(product_path)
156
            product_yaml['cmake_build_type'] = 'Debug'
157
            product_yamls[product] = product_yaml
158
159
        local_env_yaml = dict()
160
        local_env_yaml.update(product_yamls[product])
161
        local_env_yaml['rule_id'] = rule_id
162
163
        rule_path = ssg.rules.get_rule_dir_yaml(rule_obj['dir'])
164
        try:
165
            if func(rule_path, local_env_yaml):
166
                yield (rule_path, product_path, local_env_yaml)
0 ignored issues
show
introduced by
The variable product_path does not seem to be defined for all execution paths.
Loading history...
167
        except jinja2.exceptions.UndefinedError as ue:
168
            msg = "Failed to parse file {0} (with product.yml: {1}). Skipping. {2}"
169
            msg = msg.format(rule_path, product_path, ue)
170
            print(msg, file=sys.stderr)
171
172
173
def find_rules(args, func):
174
    # Returns find_rules_generator as a list
175
    return list(find_rules_generator(args, func))
176
177
178
def print_file(file_contents):
179
    for line_num in range(0, len(file_contents)):
180
        print("%d: %s" % (line_num, file_contents[line_num]))
181
182
183 View Code Duplication
def find_section_lines(file_contents, sec):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
184
    # Hack to find a global key ("section"/sec) in a YAML-like file.
185
    # All indented lines until the next global key are included in the range.
186
    # For example:
187
    #
188
    # 0: not_it:
189
    # 1:     - value
190
    # 2: this_one:
191
    # 3:      - 2
192
    # 4:      - 5
193
    # 5:
194
    # 6: nor_this:
195
    #
196
    # for the section "this_one", the result [(2, 5)] will be returned.
197
    # Note that multiple sections may exist in a file and each will be
198
    # identified and returned.
199
    sec_ranges = []
200
201
    sec_id = sec + ":"
202
    sec_len = len(sec_id)
203
    end_num = len(file_contents)
204
    line_num = 0
205
206
    while line_num < end_num:
207
        if len(file_contents[line_num]) >= sec_len:
208
            if file_contents[line_num][0:sec_len] == sec_id:
209
                begin = line_num
210
                line_num += 1
211
                while line_num < end_num:
212
                    if len(file_contents[line_num]) > 0 and file_contents[line_num][0] != ' ':
213
                        break
214
                    line_num += 1
215
216
                end = line_num - 1
217
                sec_ranges.append((begin, end))
218
        line_num += 1
219
    return sec_ranges
220
221
222
def remove_lines(file_contents, lines):
223
    # Returns a series of lines and returns a new copy
224
    new_file = []
225
    for line_num in range(0, len(file_contents)):
226
        if line_num not in lines:
227
            new_file.append(file_contents[line_num])
228
229
    return new_file
230
231
232
def remove_section_keys(file_contents, yaml_contents, section, removed_keys):
233
    # Remove a series of keys from a section. Refuses to operate if there is more
234
    # than one instance of the section. If the section is empty (because all keys
235
    # are removed), then the section is also removed. Otherwise, only matching keys
236
    # are removed. Note that all instances of the keys will be removed, if it appears
237
    # more than once.
238
    sec_ranges = find_section_lines(file_contents, section)
239
    if len(sec_ranges) != 1:
240
        raise RuntimeError("Refusing to fix file: %s -- could not find one section: %d"
241
                           % (path, sec_ranges))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable path does not seem to be defined.
Loading history...
242
243
    begin, end = sec_ranges[0]
244
    r_lines = set()
245
246
    if (yaml_contents[section] is None or len(yaml_contents[section].keys()) == len(removed_keys)):
247
        r_lines = set(range(begin, end+1))
248
        print("Removing entire section since all keys are empty")
249
    else:
250
        # Don't include section header
251
        for line_num in range(begin+1, end+1):
252
            line = file_contents[line_num].strip()
253
            len_line = len(line)
254
255
            for key in removed_keys:
256
                k_l = len(key)+1
257
                k_i = key + ":"
258
                if len_line >= k_l and line[0:k_l] == k_i:
259
                    r_lines.add(line_num)
260
                    break
261
262
    return remove_lines(file_contents, r_lines)
263
264
265
def rewrite_value_int_str(line):
266
    # Rewrites a key's value to explicitly be a string. Assumes it starts
267
    # as an integer. Takes a line.
268
    key_end = line.index(':')
269
    key = line[0:key_end]
270
    value = line[key_end+1:].strip()
271
    str_value = '"' + value + '"'
272
    return key + ": " + str_value
273
274
275
def rewrite_keyless_section(file_contents, yaml_contents, section, content):
276
    new_contents = file_contents[:]
277
278
    sec_ranges = find_section_lines(file_contents, section)
279
    if len(sec_ranges) != 1:
280
        raise RuntimeError("Refusing to fix file: %s -- could not find one section: %d"
281
                           % (path, sec_ranges))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable path does not seem to be defined.
Loading history...
282
283
    if len(sec_ranges[0]) != 2:
284
        raise RuntimeError("Section has more than one line")
285
286
    new_contents[sec_ranges[0][0]] = "{section}: {content}".format(section=section, content=content)
287
288
    return new_contents
289
290
291
def rewrite_value_remove_prefix(line):
292
    # Rewrites a key's value to remove a "CCE" prefix.
293
    key_end = line.index(':')
294
    key = line[0:key_end]
295
    value = line[key_end+1:].strip()
296
    new_value = value
297
    if cce.is_cce_format_valid("CCE-" + value[3:]):
298
        new_value = value[3:]
299
    elif cce.is_cce_format_valid("CCE-" + value[4:]):
300
        new_value = value[4:]
301
    return key + ": " + new_value
302
303
304
def add_to_the_section(file_contents, yaml_contents, section, new_keys):
305
    to_insert = []
306
307
    sec_ranges = find_section_lines(file_contents, section)
308
    if len(sec_ranges) != 1:
309
        raise RuntimeError("could not find one section: %s"
310
                           % section)
311
312
    begin, end = sec_ranges[0]
313
314
    assert end > begin, "We need at least one identifier there already"
315
    template_line = str(file_contents[end - 1])
316
    leading_whitespace = re.match(r"^\s*", template_line).group()
317
    for key, value in new_keys.items():
318
        to_insert.append(leading_whitespace + key + ": " + value)
319
320
    new_contents = file_contents[:end] + to_insert + file_contents[end:]
321
    return new_contents
322
323
324
def sort_section(file_contents, yaml_contents, section):
325
    new_contents = ssg.rule_yaml.sort_section_keys(yaml_contents, file_contents, section)
326
    return new_contents
327
328
329
def rewrite_section_value(file_contents, yaml_contents, section, keys, transform):
330
    # For a given section, rewrite the keys in int_keys to be strings. Refuses to
331
    # operate if the given section appears more than once in the file. Assumes all
332
    # instances of key are an integer; all will get updated.
333
    new_contents = file_contents[:]
334
335
    sec_ranges = find_section_lines(file_contents, section)
336
    if len(sec_ranges) != 1:
337
        raise RuntimeError("Refusing to fix file: %s -- could not find one section: %d"
338
                           % (path, sec_ranges))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable path does not seem to be defined.
Loading history...
339
340
    begin, end = sec_ranges[0]
341
    r_lines = set()
342
343
    # Don't include section header
344
    for line_num in range(begin+1, end+1):
345
        line = file_contents[line_num].strip()
346
        len_line = len(line)
347
348
        for key in keys:
349
            k_l = len(key)+1
350
            k_i = key + ":"
351
352
            if len_line >= k_l and line[0:k_l] == k_i:
353
                new_contents[line_num] = transform(file_contents[line_num])
354
                break
355
356
    return new_contents
357
358
359
def rewrite_section_value_int_str(file_contents, yaml_contents, section, int_keys):
360
    return rewrite_section_value(file_contents, yaml_contents, section, int_keys,
361
                                 rewrite_value_int_str)
362
363
364 View Code Duplication
def fix_empty_identifier(file_contents, yaml_contents):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
365
    section = 'identifiers'
366
367
    empty_identifiers = []
368
    if yaml_contents[section] is not None:
369
        for i_type, i_value in yaml_contents[section].items():
370
            if str(i_value).strip() == "":
371
                empty_identifiers.append(i_type)
372
373
    return remove_section_keys(file_contents, yaml_contents, section, empty_identifiers)
374
375
376 View Code Duplication
def fix_empty_reference(file_contents, yaml_contents):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
377
    section = 'references'
378
379
    empty_identifiers = []
380
381
    if yaml_contents[section] is not None:
382
        for i_type, i_value in yaml_contents[section].items():
383
            if str(i_value).strip() == "":
384
                empty_identifiers.append(i_type)
385
386
    return remove_section_keys(file_contents, yaml_contents, section, empty_identifiers)
387
388
389
def fix_prefix_cce(file_contents, yaml_contents):
390
    section = 'identifiers'
391
392
    prefixed_identifiers = []
393
394
    if yaml_contents[section] is not None:
395
        for i_type, i_value in yaml_contents[section].items():
396
            if i_type[0:3] == 'cce':
397
                has_prefix = i_value[0:3].upper() == 'CCE'
398
                remainder_valid = cce.is_cce_format_valid("CCE-" + str(i_value[3:]))
399
                remainder_valid |= cce.is_cce_format_valid("CCE-" + str(i_value[4:]))
400
                if has_prefix and remainder_valid:
401
                    prefixed_identifiers.append(i_type)
402
403
    return rewrite_section_value(file_contents, yaml_contents, section, prefixed_identifiers,
404
                                 rewrite_value_remove_prefix)
405
406
407
def fix_invalid_cce(file_contents, yaml_contents):
408
    section = 'identifiers'
409
410
    invalid_identifiers = []
411
412
    if yaml_contents[section] is not None:
413
        for i_type, i_value in yaml_contents[section].items():
414
            if i_type[0:3] == 'cce':
415
                if not cce.is_cce_value_valid("CCE-" + str(i_value)):
416
                    invalid_identifiers.append(i_type)
417
418
    return remove_section_keys(file_contents, yaml_contents, section, invalid_identifiers)
419
420
421
def fix_prodtypes(file_contents, yaml_contents):
422
    section = 'prodtype'
423
    sorted_prodtypes = yaml_contents[section].split(",")
424
    sorted_prodtypes.sort()
425
    out = ",".join(sorted_prodtypes)
426
427
    return rewrite_keyless_section(file_contents, yaml_contents, section, out)
428
429
430
def has_product_cce(yaml_contents, product):
431
    section = 'identifiers'
432
433
    invalid_identifiers = []
434
435
    if not yaml_contents[section]:
436
        return False
437
438
    for i_type, i_value in yaml_contents[section].items():
439
        if i_type[0:3] != 'cce' or "@" not in i_type:
440
            continue
441
442
        _, cce_product = i_type.split("@", 1)
443
        if product == cce_product:
444
            return True
445
446
    return False
447
448
449
def add_product_cce(file_contents, yaml_contents, product, cce):
450
    section = 'identifiers'
451
452
    if section not in yaml_contents:
453
        return file_contents
454
455
    new_contents = add_to_the_section(
456
        file_contents, yaml_contents, section, {"cce@{product}".format(product=product): cce})
457
    new_contents = sort_section(new_contents, yaml_contents, section)
458
    return new_contents
459
460
461
def fix_int_identifier(file_contents, yaml_contents):
462
    section = 'identifiers'
463
464
    int_identifiers = []
465
    for i_type, i_value in yaml_contents[section].items():
466
        if type(i_value) != str:
467
            int_identifiers.append(i_type)
468
469
    return rewrite_section_value_int_str(file_contents, yaml_contents, section, int_identifiers)
470
471
472
def fix_int_reference(file_contents, yaml_contents):
473
    section = 'references'
474
475
    int_identifiers = []
476
    for i_type, i_value in yaml_contents[section].items():
477
        if type(i_value) != str:
478
            int_identifiers.append(i_type)
479
480
    return rewrite_section_value_int_str(file_contents, yaml_contents, section, int_identifiers)
481
482
483
def sort_rule_subkeys(file_contents, yaml_contents):
484
    return ssg.rule_yaml.sort_section_keys(None, file_contents, TO_SORT)
485
486
487
def _fixed_file_contents(path, file_contents, product_yaml, func):
488
    if file_contents[-1] == '':
489
        file_contents = file_contents[:-1]
490
491
    subst_dict = product_yaml
492
    yaml_contents = yaml.open_and_macro_expand(path, subst_dict)
493
494
    try:
495
        new_file_contents = func(file_contents, yaml_contents)
496
    except Exception as exc:
497
        msg = "Refusing to fix file: {path}: {error}".format(path=path, error=str(exc))
498
        raise RuntimeError(msg)
499
500
    return new_file_contents
501
502
503
def fix_file(path, product_yaml, func):
504
    file_contents = open(path, 'r').read().split("\n")
505
506
    new_file_contents = _fixed_file_contents(path, file_contents, product_yaml, func)
507
    if file_contents == new_file_contents:
508
        return False
509
510
    with open(path, 'w') as f:
511
        for line in new_file_contents:
512
            print(line, file=f)
513
    return True
514
515
516
def fix_file_prompt(path, product_yaml, func, args):
517
    file_contents = open(path, 'r').read().split("\n")
518
519
    new_file_contents = _fixed_file_contents(path, file_contents, product_yaml, func)
520
    changes = file_contents != new_file_contents
521
522
    if not changes:
523
        return changes
524
525
    need_input = not args.assume_yes and not args.dry_run
526
527
    if need_input:
528
        print("====BEGIN BEFORE====")
529
        print_file(file_contents)
530
        print("====END BEFORE====")
531
532
    if need_input:
533
        print("====BEGIN AFTER====")
534
        print_file(new_file_contents)
535
        print("====END AFTER====")
536
537
    response = 'n'
538
    if need_input:
539
        response = input_func("Confirm writing output to %s: (y/n): " % path)
540
541
    if args.assume_yes or response.strip().lower() == 'y':
542
        changes = True
543
        with open(path, 'w') as f:
544
            for line in new_file_contents:
545
                print(line, file=f)
546
    else:
547
        changes = False
548
    return changes
549
550
551
def add_cce(args, product_yaml):
552
    directory = os.path.join(args.root, args.subdirectory)
553
    cce_pool = cce.CCE_POOLS[args.cce_pool]()
554
    return _add_cce(directory, cce_pool, args.rule, product_yaml, args)
555
556
557
def _add_cce(directory, cce_pool, rules, product_yaml, args):
558
    product = product_yaml["product"]
559
560
    def is_relevant_rule(fname, _=None):
561
        for r in rules:
562
            if (
563
                    fname.endswith("/{r}/rule.yml".format(r=r))
564
                    and has_no_cce(fname, product_yaml)):
565
                return True
566
        return False
567
568
    results = find_rules(args, is_relevant_rule)
569
570
    for result in results:
571
        rule_path = result[0]
572
573
        cce = cce_pool.random_cce()
574
575
        def fix_callback(file_contents, yaml_contents):
576
            return add_product_cce(file_contents, yaml_contents, product_yaml["product"], cce)
0 ignored issues
show
introduced by
The variable cce does not seem to be defined in case the for loop on line 570 is not entered. Are you sure this can never be the case?
Loading history...
577
578
        try:
579
            changes = fix_file(rule_path, product_yaml, fix_callback)
580
        except RuntimeError as exc:
581
            msg = (
582
                "Error adding CCE into {rule_path}: {exc}"
583
                .format(rule_path=rule_path, exc=str(exc)))
584
            raise RuntimeError(exc)
585
586
        if changes:
587
            cce_pool.remove_cce_from_file(cce)
588
589
590
def has_unsorted_prodtype(yaml_file, product_yaml=None):
591
    rule = yaml.open_and_macro_expand(yaml_file, product_yaml)
592
    if 'prodtype' in rule:
593
        prodtypes = rule['prodtype'].split(',')
594
        return prodtypes != sorted(prodtypes)
595
    return False
596
597
598
@command("empty_identifiers", "check and fix rules with empty identifiers")
599
def fix_empty_identifiers(args, product_yaml):
600
    results = find_rules(args, has_empty_identifier)
601
    print("Number of rules with empty identifiers: %d" % len(results))
602
603
    for result in results:
604
        rule_path = result[0]
605
606
        product_yaml_path = result[2]
607
608
        if product_yaml_path is not None:
609
            product_yaml = yaml.open_raw(product_yaml_path)
610
611
        if args.dry_run:
612
            print(rule_path + " has one or more empty identifiers")
613
            continue
614
615
        fix_file_prompt(rule_path, product_yaml, fix_empty_identifier, args)
616
617
    return int(len(results) > 0)
618
619
620
@command("empty_references", "check and fix rules with empty references")
621
def fix_empty_references(args, product_yaml):
622
    results = find_rules(args, has_empty_references)
623
    print("Number of rules with empty references: %d" % len(results))
624
625
    for result in results:
626
        rule_path = result[0]
627
        product_yaml = result[2]
628
629
        if args.dry_run:
630
            print(rule_path + " has one or more empty references")
631
            continue
632
633
        fix_file_prompt(rule_path, product_yaml, fix_empty_reference)
634
635
    return int(len(results) > 0)
636
637
638
@command("prefixed_identifiers", "check and fix rules with prefixed (CCE-) identifiers")
639
def find_prefix_cce(args):
640
    results = find_rules(args, has_prefix_cce)
641
    print("Number of rules with prefixed CCEs: %d" % len(results))
642
643
    for result in results:
644
        rule_path = result[0]
645
        product_yaml = result[2]
646
647
        if args.dry_run:
648
            print(rule_path + " has one or more CCE with CCE- prefix")
649
            continue
650
651
        fix_file_prompt(rule_path, product_yaml, fix_prefix_cce)
652
653
    return int(len(results) > 0)
654
655
656
@command("invalid_identifiers", "check and fix rules with invalid identifiers")
657
def find_invalid_cce(args, product_yamls):
658
    results = find_rules(args, has_invalid_cce)
659
    print("Number of rules with invalid CCEs: %d" % len(results))
660
661
    for result in results:
662
        rule_path = result[0]
663
        product_yaml = result[2]
664
665
        if args.dry_run:
666
            print(rule_path + " has one or more invalid CCEs")
667
            continue
668
669
        fix_file_prompt(rule_path, product_yaml, fix_invalid_cce)
670
    return int(len(results) > 0)
671
672
673
@command("int_identifiers", "check and fix rules with pseudo-integer identifiers")
674
def find_int_identifiers(args, product_yaml):
675
    results = find_rules(args, has_int_identifier)
676
    print("Number of rules with integer identifiers: %d" % len(results))
677
678
    for result in results:
679
        rule_path = result[0]
680
        product_yaml = result[2]
681
682
        if args.dry_run:
683
            print(rule_path + " has one or more integer references")
684
            continue
685
686
        fix_file_prompt(rule_path, product_yaml, fix_int_identifier)
687
688
    return int(len(results) > 0)
689
690
691
@command("int_references", "check and fix rules with pseudo-integer references")
692
def find_int_references(args, product_yaml):
693
    results = find_rules(args, has_int_reference)
694
    print("Number of rules with integer references: %d" % len(results))
695
696
    for result in results:
697
        rule_path = result[0]
698
        product_yaml = result[2]
699
700
        if args.dry_run:
701
            print(rule_path + " has one or more unsorted references")
702
            continue
703
704
        fix_file_prompt(rule_path, product_yaml, fix_int_reference)
705
706
    return int(len(results) > 0)
707
708
709
@command("duplicate_subkeys", "check for duplicated references and identifiers")
710
def duplicate_subkeys(args, product_yaml):
711
    results = find_rules(args, has_duplicated_subkeys)
712
    print("Number of rules with duplicated subkeys: %d" % len(results))
713
714
    for result in results:
715
        print(result[0] + " has one or more duplicated subkeys")
716
717
    return int(len(results) > 0)
718
719
720
@command("sort_subkeys", "sort references and identifiers")
721
def sort_subkeys(args, product_yaml):
722
    results = find_rules(args, has_unordered_sections)
723
    print("Number of modified rules: %d" % len(results))
724
725
    for result in results:
726
        rule_path = result[0]
727
        product_yaml = result[2]
728
729
        if args.dry_run:
730
            print(rule_path + " has one or more unsorted references")
731
            continue
732
733
        fix_file_prompt(rule_path, product_yaml, sort_rule_subkeys, args)
734
735
    return int(len(results) > 0)
736
737
738
@command("sort_prodtypes", "sorts the products in the prodtype")
739
def sort_prodtypes(args, product_yaml):
740
    results = find_rules(args, has_unsorted_prodtype)
741
    for result in results:
742
        rule_path = result[0]
743
        product_yaml = result[2]
744
745
        if args.dry_run:
746
            print(rule_path + " prodtype is unsorted")
747
            continue
748
749
        fix_file(rule_path, product_yaml, fix_prodtypes)
750
751
    return int(len(results) > 0)
752
753
754
def create_parser_from_functions(subparsers):
755
    for name, function in _COMMANDS.items():
756
        subparser = subparsers.add_parser(name, description=function.description)
757
        subparser.set_defaults(func=function)
758
759
760
def create_other_parsers(subparsers):
761
    subparser = subparsers.add_parser("add-cce", description="Add CCE to rule files")
762
    subparser.add_argument("rule", nargs="+")
763
    subparser.add_argument("--subdirectory", default="linux_os")
764
    subparser.add_argument(
765
        "--cce-pool", "-p", default="redhat", choices=list(cce.CCE_POOLS.keys()),
766
    )
767
    subparser.set_defaults(func=add_cce)
768
769
770
def parse_args():
771
    parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
772
                                     description="Utility for fixing mistakes in rule files")
773
    parser.add_argument(
774
        "-y", "--assume-yes", default=False, action="store_true",
775
        help="Assume yes and overwrite all files (no prompt)")
776
    parser.add_argument(
777
        "-d", "--dry-run", default=False, action="store_true",
778
        help="Assume no and don't overwrite any files")
779
    parser.add_argument(
780
        "-j", "--json", type=str, action="store",
781
        default="build/rule_dirs.json", help="File to read json "
782
        "output of rule_dir_json.py from (defaults to "
783
        "build/rule_dirs.json")
784
    parser.add_argument(
785
        "-r", "--root", default=SSG_ROOT,
786
        help="Path to root of the project directory")
787
    parser.add_argument("--product", "-p", help="Path to the main product.yml")
788
    subparsers = parser.add_subparsers(title="command", help="What to perform.")
789
    create_parser_from_functions(subparsers)
790
    create_other_parsers(subparsers)
791
    return parser.parse_args()
792
793
794
def __main__():
795
    args = parse_args()
796
    project_root = args.root
797
    if not project_root:
798
        project_root = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.path.pardir)
799
800
    subst_dict = dict()
801
    if args.product:
802
        subst_dict = products.load_product_yaml(args.product)
803
804
    args.func(args, subst_dict)
805
806
807
if __name__ == "__main__":
808
    __main__()
809