1"""Wrapper around idstools.rule.Rule for future removal of idstools dependency."""
2
3from typing import Optional
4
5import idstools.rule
6
7
[docs]
8class Rule:
9 """Wrapper around an `suricata_check.rule.Rule` in preparation for dropping the idstools dependency."""
10
11 def __init__(self, inner: Optional[idstools.rule.Rule]) -> None:
12 """Create a wrapper around an existing `suricata_check.rule.Rule`.
13
14 Parameters
15 ----------
16 inner:
17 The parsed rule object from `suricata_check.rule.parse()` or None.
18 """
19 super().__init__()
20 self._inner = inner
21
22 @property
23 def inner(self) -> Optional[idstools.rule.Rule]:
24 """Return the underlying parsed rule object (or None)."""
25 return self._inner
26
[docs]
27 def __getitem__(self, key: str): # noqa: ANN204
28 """Forward mapping access to the underlying rule.
29
30 Raises KeyError when no underlying rule is present.
31 """
32 if self._inner is None:
33 raise KeyError(key)
34 return self._inner[key]
35
[docs]
36 def get(self, key: str, default: Optional[object] = None): # noqa: ANN201
37 """Return the value for *key* if present, otherwise *default*."""
38 if self._inner is None:
39 return default
40 return self._inner.get(key, default)
41
[docs]
42 def __contains__(self, key: str) -> bool:
43 """Return True when the underlying rule contains *key*."""
44 return self._inner is not None and key in self._inner
45
[docs]
46 def __repr__(self) -> str:
47 """Return representation of the wrapped rule (or 'None')."""
48 return repr(self._inner)
49
50
[docs]
51class ParsingError(RuntimeError):
52 """Raised when a rule cannot be parsed by suricata-check.
53
54 Most likely, such a rule is also an invalid Suricata rule.
55 """
56
57 def __init__(self: "ParsingError", message: str) -> None:
58 """Initializes the `ParsingError` with the raw rule as message."""
59 super().__init__(message)
60
61
[docs]
62def parse(text: Optional[str]) -> Optional["Rule"]:
63 """Parse a rule string using the underlying `idstools` parser.
64
65 Return a wrapped `Rule` instance.
66
67 Returns None when the text could not be parsed as a rule.
68 """
69 try:
70 inner = idstools.rule.parse(text)
71 except Exception as e: # noqa: BLE001
72 raise ParsingError(str(e))
73
74 if inner is None:
75 return None
76
77 return Rule(inner)