ssg.rules.find_rule_dirs_in_paths()   A
last analyzed

Complexity

Conditions 4

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 5
nop 1
dl 0
loc 8
rs 10
c 0
b 0
f 0
ccs 0
cts 5
cp 0
crap 20
1
from __future__ import absolute_import
2
from __future__ import print_function
3
4
import os
5
from glob import glob
6
7
8
def get_rule_dir_yaml(dir_path):
9
    """
10
    Returns the path to the yaml metadata for a rule directory,
11
    regardless of if it exists.
12
    """
13
    return os.path.join(dir_path, "rule.yml")
14
15
16
def get_rule_dir_id(path):
17
    """
18
    Returns the ID of a rule directory; correctly handles being passed
19
    either the directory path or the yaml metadata path.
20
    """
21
    dir_path = path
22
23
    if path.endswith("rule.yml"):
24
        dir_path = os.path.dirname(path)
25
26
    return os.path.basename(dir_path)
27
28
29
def is_rule_dir(dir_path):
30
    """
31
    Returns True iff dir_path is a valid rule directory which exists
32
33
    To be valid, dir_path must exist and be a directory and the file
34
    returned by get_rule_dir_yaml(dir_path) must exist.
35
    """
36
    rule_yaml = get_rule_dir_yaml(dir_path)
37
38
    is_dir = os.path.isdir(dir_path)
39
    has_rule_yaml = os.path.exists(rule_yaml)
40
41
    return is_dir and has_rule_yaml
42
43
44
def applies_to_product(file_name, product):
45
    """
46
    A OVAL or fix is filtered by product iff product is Falsy, file_name is
47
    "shared", or file_name is product. Note that this does not filter by
48
    contents of the fix or check, only by the name of the file.
49
    """
50
51
    if not product:
52
        return True
53
54
    return file_name == "shared" or file_name == product or product.startswith(file_name)
55
56
57
def get_rule_dir_ovals(dir_path, product=None):
58
    """
59
    Gets a list of OVALs contained in a rule directory. If product is
60
    None, returns all OVALs. If product is not None, returns applicable
61
    OVALs in order of priority:
62
63
        {{{ product }}}.xml -> shared.xml
64
65
    Only returns OVALs which exist.
66
    """
67
68
    if not is_rule_dir(dir_path):
69
        return []
70
71
    oval_dir = os.path.join(dir_path, "oval")
72
    has_oval_dir = os.path.isdir(oval_dir)
73
    if not has_oval_dir:
74
        return []
75
76
    # Two categories of results: those for a product and those that are shared
77
    # to multiple products. Within common results, there's two types:
78
    # those shared to multiple versions of the same type (added up front) and
79
    # those shared across multiple product types (e.g., RHEL and Ubuntu).
80
    product_results = []
81
    common_results = []
82
    for oval_file in sorted(os.listdir(oval_dir)):
83
        file_name, ext = os.path.splitext(oval_file)
84
        oval_path = os.path.join(oval_dir, oval_file)
85
86
        if ext == ".xml" and applies_to_product(file_name, product):
87
            # applies_to_product ensures we only have three entries:
88
            # 1. shared
89
            # 2. <product>
90
            # 3. <product><version>
91
            if file_name == 'shared':
92
                # Shared are the lowest priority items, add them to the end of
93
                # the common results.
94
                common_results.append(oval_path)
95
            elif file_name != product:
96
                # Here, the filename is a subset of the product, but isn't
97
                # the full product. Product here is both the product name
98
                # (e.g., ubuntu) and its version (2004). Filename could be
99
                # either "ubuntu" or "ubuntu2004" so we want this branch
100
                # to trigger when it is the former, not the latter. It is
101
                # the highest priority of common results, so insert it
102
                # before any shared ones.
103
                common_results.insert(0, oval_path)
104
            else:
105
                # Finally, this must be a product-specific result.
106
                product_results.append(oval_path)
107
108
    # Combine the two sets in priority order.
109
    return product_results + common_results
110
111
112
def get_rule_dir_sces(dir_path, product=None):
113
    """
114
    Get a list of SCEs contained in a rule directory. If product is None,
115
    returns all SCEs. If product is not None, returns applicable SCEs in
116
    order of priority:
117
118
        {{{ product }}}.{{{ ext }}} -> shared.{{{ ext }}}
119
120
    Only returns SCEs which exist.
121
    """
122
123
    if not is_rule_dir(dir_path):
124
        return []
125
126
    sce_dir = os.path.join(dir_path, "sce")
127
    has_sce_dir = os.path.isdir(sce_dir)
128
    if not has_sce_dir:
129
        return []
130
131
    results = []
132
    common_results = []
133
    for sce_file in sorted(os.listdir(sce_dir)):
134
        file_name, ext = os.path.splitext(sce_file)
135
        sce_path = os.path.join(sce_dir, sce_file)
136
137
        if applies_to_product(file_name, product):
138
            if file_name == 'shared':
139
                common_results.append(sce_path)
140
            elif file_name != product:
141
                common_results.insert(0, sce_path)
142
            else:
143
                results.append(sce_path)
144
145
    return results + common_results
146
147
148
def find_rule_dirs(base_dir):
149
    """
150
    Generator which yields all rule directories within a given base_dir, recursively
151
    """
152
    for root, dirs, _ in os.walk(base_dir):
153
        dirs.sort()
154
        for dir_name in dirs:
155
            dir_path = os.path.join(root, dir_name)
156
            if is_rule_dir(dir_path):
157
                yield dir_path
158
159
160
def find_rule_dirs_in_paths(base_dirs):
161
    """
162
    Generator which yields all rule directories within a given directories list, recursively
163
    """
164
    if base_dirs:
165
        for cur_dir in base_dirs:
166
            for d in find_rule_dirs(cur_dir):
167
                yield d
168