File: variables.py

package info (click to toggle)
scap-security-guide 0.1.76-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 110,644 kB
  • sloc: xml: 241,883; sh: 73,777; python: 32,527; makefile: 27
file content (235 lines) | stat: -rw-r--r-- 8,675 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
from __future__ import absolute_import
from __future__ import print_function

import glob
import os
import sys

from collections import defaultdict
from .constants import BENCHMARKS
from .profiles import get_profiles_from_products
from .yaml import open_and_macro_expand_from_dir


if sys.version_info >= (3, 9):
    list_type = list  # Python 3.9+ supports built-in generics
    dict_type = dict
else:
    from typing import List as list_type  # Fallback for older versions
    from typing import Dict as dict_type


# Cache variable files and respective content to avoid multiple reads
_var_files_cache = {}
_vars_content_cache = {}


def get_variable_files_in_folder(content_dir: str, subfolder: str) -> list_type[str]:
    """
    Retrieve a list of variable files within a specified folder in the project.

    Args:
        content_dir (str): The root directory of the content.
        subfolder (str): The folder within the project to search for variable files.

    Returns:
        list: A list of paths to variable files found within the specified folder.
    """
    rules_base_dir = os.path.join(content_dir, subfolder)
    search_dir = os.path.normpath(rules_base_dir)
    pattern = os.path.join(search_dir, '**', '*.var')
    return glob.glob(pattern, recursive=True)


def get_variable_files(content_dir: str) -> list_type[str]:
    """
    Retrieves all variable files from the specified content root directory.

    This function iterates through a predefined list of benchmark directories and collects all
    variable files within those directories.

    Args:
        content_dir (str): The root directory containing benchmark directories.

    Returns:
        list: A list of paths to all variable files found within the benchmark directories.
    """
    if content_dir in _var_files_cache:
        return _var_files_cache[content_dir]

    variable_files = []
    for benchmark_directory in BENCHMARKS:
        variable_files.extend(get_variable_files_in_folder(content_dir, benchmark_directory))
    _var_files_cache[content_dir] = variable_files
    return variable_files


def _get_variables_content(content_dir: str) -> dict_type:
    """
    Retrieve the content of all variable files from the specified content root directory.

    Args:
        content_dir (str): The root directory containing benchmark directories.

    Returns:
        dict: A dictionary where keys are variable IDs and values are the content of the variable
              files.
    """
    if content_dir in _vars_content_cache:
        return _vars_content_cache[content_dir]

    variables_content = {}

    for var_file in get_variable_files(content_dir):
        try:
            yaml_content = open_and_macro_expand_from_dir(var_file, content_dir)
        except Exception as e:
            print(f"Error processing file {var_file}: {e}")
            continue

        var_id = os.path.basename(var_file).split('.var')[0]
        variables_content[var_id] = yaml_content

    _vars_content_cache[content_dir] = variables_content
    return variables_content


def get_variable_property(content_dir: str, variable_id: str, property_name: str) -> str:
    """
    Retrieve a specific property of a variable from the content root directory.

    Args:
        content_dir (str): The root directory containing benchmark directories.
        variable_id (str): The ID of the variable to retrieve the property for.
        property_name (str): The name of the property to retrieve.

    Returns:
        str: The value of the specified property for the variable.
    """
    variables_content = _get_variables_content(content_dir)
    variable_content = variables_content.get(variable_id, {})
    return variable_content.get(property_name, '')


def get_variable_options(content_dir: str, variable_id: str = None) -> dict_type:
    """
    Retrieve the options for specific or all variables from the content root directory.

    If `variable_id` is provided, returns options for that variable only.
    If `variable_id` is not provided, returns a dictionary of all variables with their options.

    Args:
        content_dir (str): The root directory containing benchmark directories.
        variable_id (str, optional): The ID of the variable to retrieve options for.
                                     Defaults to None.

    Returns:
        dict: If `variable_id` is None, a dictionary where keys are variable IDs and values are
              their options. Otherwise, a dictionary of options for the specified variable.
    """
    variables_content = _get_variables_content(content_dir)
    all_options = {}

    for var_id, var_yaml in variables_content.items():
        options = var_yaml.get("options", {})

        if variable_id:
            if var_id == variable_id:
                return options
        else:
            all_options[var_id] = options

    if variable_id:
        print(f"Variable {variable_id} not found")
        return {}

    return all_options


def get_variables_from_profiles(profiles: list) -> dict_type:
    """
    Extracts variables from a list of profiles and organizes them into a nested dictionary.

    Args:
        profiles (list): A list of profile objects, each containing selections and id attributes.

    Returns:
        dict: A nested dictionary where the first level keys are variable names, the second level
              keys are product names, and the third level keys are profile IDs, with the
              corresponding values being the variable values.
    """
    variables = defaultdict(lambda: defaultdict(dict))
    for profile in profiles:
        for variable, value in profile.variables.items():
            variables[variable][profile.product_id][profile.profile_id] = value
    return _convert_defaultdict_to_dict(variables)


def _convert_defaultdict_to_dict(dictionary: defaultdict) -> dict_type:
    """
    Recursively converts a defaultdict to a regular dictionary.

    Args:
        dictionary (defaultdict): The defaultdict to convert.

    Returns:
        dict: The converted dictionary.
    """
    if isinstance(dictionary, defaultdict):
        dictionary = {k: _convert_defaultdict_to_dict(v) for k, v in dictionary.items()}
    return dictionary


def get_variables_by_products(content_dir: str, products: list) -> dict_type[str, dict]:
    """
    Retrieve variables by products from the specified content root directory.

    This function collects profiles for the given products and extracts variables from these
    profiles. If you already have a list of Profiles obtained by get_profiles_from_products()
    defined in profiles.py, consider to use get_variables_from_profiles() instead.

    Args:
        content_dir (str): The root directory of the content.
        products (list): A list of products to retrieve variables for.

    Returns:
        dict: A dictionary where keys are variable names and values are dictionaries of
              product-profile pairs.
    """
    profiles = get_profiles_from_products(content_dir, products)
    profiles_variables = get_variables_from_profiles(profiles)
    return _convert_defaultdict_to_dict(profiles_variables)


def get_variable_values(content_dir: str, profiles_variables: dict) -> dict_type:
    """
    Update the variables dictionary with actual values for each variable option.

    Given a content root directory and a dictionary of variables, this function retrieves the
    respective options values from variable files and updates the variables dictionary with
    these values.

    Args:
        content_dir (str): The root directory of the content.
        variables (dict): A dictionary where keys are variable names and values are dictionaries
                          of product-profile pairs.

    Returns:
        dict: The updated variables dictionary with possible values for each variable.
    """
    all_variables_options = get_variable_options(content_dir)

    for variable in profiles_variables:
        variable_options = all_variables_options.get(variable, {})
        for product, profiles in profiles_variables[variable].items():
            for profile in profiles:
                profile_option = profiles.get(profile, None)
                profiles_variables[variable][product][profile] = variable_options.get(
                    profile_option, 'default'
                )

        if 'any' not in profiles_variables[variable] or not isinstance(
                profiles_variables[variable]['any'], dict):
            profiles_variables[variable]['any'] = {}
        profiles_variables[variable]['any']['default'] = variable_options.get('default', None)
    return profiles_variables