Fuzzy match the source structure onto the target If something is defined in the target (such as dict key) it must also be present in the source Additional data (keys, list items etc...) that are present in source but not target are ignored This fuzzy match works recursively for nest
(source, target)
| 164 | |
| 165 | |
| 166 | def match_rule(source, target) -> bool: |
| 167 | """ |
| 168 | Fuzzy match the source structure onto the target |
| 169 | If something is defined in the target (such as dict key) it must also be present in the source |
| 170 | Additional data (keys, list items etc...) that are present in source but not target are ignored |
| 171 | This fuzzy match works recursively for nested structures |
| 172 | |
| 173 | :param source: input structure that we want to match on |
| 174 | :param target: pattern that we match against, all structure items/keys from target must be present in source |
| 175 | :return: bool |
| 176 | """ |
| 177 | # We want to avoid importing the Rule in top level |
| 178 | # as the tests can change the aura configuration which is influenced by import |
| 179 | global Detection |
| 180 | if Detection is None: |
| 181 | from aura.analyzers.detections import Detection |
| 182 | |
| 183 | if isinstance(source, Detection): |
| 184 | source = source._asdict() |
| 185 | |
| 186 | # Check if target is a regex and apply it to source string |
| 187 | if (isinstance(target, Pattern) or isinstance(target, REGEX_TYPE)) and type(source) == str: |
| 188 | return bool(target.match(source)) |
| 189 | |
| 190 | # Check if target is a function (that should return bool) |
| 191 | if inspect.isfunction(target): |
| 192 | return target(source) |
| 193 | |
| 194 | if type(target) != type(source): |
| 195 | return False |
| 196 | elif isinstance(target, list): |
| 197 | for t in target: |
| 198 | if not any(match_rule(s, t) for s in source): |
| 199 | return False |
| 200 | return True |
| 201 | # Fallback to direct comparison if target is dict |
| 202 | elif not isinstance(target, dict): |
| 203 | return target == source |
| 204 | |
| 205 | # Check that all the keys from a target are present in the source using recursive fuzzy match |
| 206 | for x in target.keys(): |
| 207 | # Fail if the key is not present in a source or is of a different type then target |
| 208 | if type(target[x]) == REGEX_TYPE and type(source[x]) == str: |
| 209 | return bool(target[x].match(source[x])) |
| 210 | |
| 211 | if type(target[x]) != type(source.get(x)): |
| 212 | return False |
| 213 | # Recurse if target key value is a dict |
| 214 | if isinstance(target[x], dict): |
| 215 | if not match_rule(source[x], target[x]): |
| 216 | return False |
| 217 | # Recurse if a target key value is a list |
| 218 | elif isinstance(target[x], list): |
| 219 | # We don't care about the ordering so any source list item can match |
| 220 | for t in target[x]: |
| 221 | if any(match_rule(s, t) for s in source[x]): |
| 222 | return True |
| 223 | # Fall back to direct comparison |
no test coverage detected