16
16
limitations under the License.
17
17
-----------------------------------------------------------------
18
18
"""
19
- from typing import Optional
19
+ from typing import ClassVar , Optional
20
20
21
21
import yaml
22
22
23
23
from app .translator .const import DEFAULT_VALUE_TYPE
24
+ from app .translator .core .custom_types .values import ValueType
24
25
from app .translator .core .mapping import LogSourceSignature , SourceMapping
25
26
from app .translator .core .models .platform_details import PlatformDetails
26
27
from app .translator .core .models .query_container import MetaInfoContainer
28
+ from app .translator .core .models .query_tokens .field import Field
27
29
from app .translator .core .render import BaseFieldValueRender , PlatformQueryRender
28
30
from app .translator .core .str_value_manager import StrValueManager
29
31
from app .translator .managers import render_manager
30
32
from app .translator .platforms .falco .const import falco_rule_details
31
- from app .translator .platforms .falco .mapping import falco_rule_mappings , FalcoRuleMappings
33
+ from app .translator .platforms .falco .mapping import FalcoRuleMappings , falco_rule_mappings
34
+ from app .translator .platforms .falco .str_value_manager import falco_rule_str_value_manager
32
35
33
36
34
- class FalcoFieldValueRender (BaseFieldValueRender ):
37
+ class FalcoRuleFieldValueRender (BaseFieldValueRender ):
35
38
details = falco_rule_details
36
- str_value_manager : StrValueManager = None
37
- #
38
- # def equal_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: # noqa: ARG002
39
- # raise UnsupportedRenderMethod(platform_name=self.details.name, method=OperatorType.EQ.capitalize())
40
-
41
- # def not_equal_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: # noqa: ARG002
42
- # raise UnsupportedRenderMethod(platform_name=self.details.name, method=OperatorType.NOT_EQ.capitalize())
43
- #
44
- # def less_modifier(self, field: str, value: Union[int, str]) -> str: # noqa: ARG002
45
- # raise UnsupportedRenderMethod(platform_name=self.details.name, method=OperatorType.LT.capitalize())
46
- #
47
- # def less_or_equal_modifier(self, field: str, value: Union[int, str]) -> str: # noqa: ARG002
48
- # raise UnsupportedRenderMethod(platform_name=self.details.name, method=OperatorType.LTE.capitalize())
49
- #
50
- # def greater_modifier(self, field: str, value: Union[int, str]) -> str: # noqa: ARG002
51
- # raise UnsupportedRenderMethod(platform_name=self.details.name, method=OperatorType.GT.capitalize())
52
- #
53
- # def greater_or_equal_modifier(self, field: str, value: Union[int, str]) -> str: # noqa: ARG002
54
- # raise UnsupportedRenderMethod(platform_name=self.details.name, method=OperatorType.GTE.capitalize())
55
- #
56
- # def contains_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: # noqa: ARG002
57
- # raise UnsupportedRenderMethod(platform_name=self.details.name, method=OperatorType.CONTAINS.capitalize())
58
- #
59
- # def not_contains_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: # noqa: ARG002
60
- # raise UnsupportedRenderMethod(platform_name=self.details.name, method=OperatorType.NOT_CONTAINS.capitalize())
61
- #
62
- # def endswith_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: # noqa: ARG002
63
- # raise UnsupportedRenderMethod(platform_name=self.details.name, method=OperatorType.ENDSWITH.capitalize())
64
- #
65
- # def not_endswith_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: # noqa: ARG002
66
- # raise UnsupportedRenderMethod(platform_name=self.details.name, method=OperatorType.NOT_ENDSWITH.capitalize())
67
- #
68
- # def startswith_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: # noqa: ARG002
69
- # raise UnsupportedRenderMethod(platform_name=self.details.name, method=OperatorType.STARTSWITH.capitalize())
70
- #
71
- # def not_startswith_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: # noqa: ARG002
72
- # raise UnsupportedRenderMethod(platform_name=self.details.name, method=OperatorType.NOT_STARTSWITH.capitalize())
73
- #
74
- # def regex_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: # noqa: ARG002
75
- # raise UnsupportedRenderMethod(platform_name=self.details.name, method=OperatorType.REGEX.capitalize())
76
- #
77
- # def not_regex_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: # noqa: ARG002
78
- # raise UnsupportedRenderMethod(platform_name=self.details.name, method=OperatorType.NOT_REGEX.capitalize())
79
- #
80
- # def keywords(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: # noqa: ARG002
81
- # raise UnsupportedRenderMethod(platform_name=self.details.name, method=OperatorType.KEYWORD.capitalize())
82
- #
83
- # def is_none(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: # noqa: ARG002
84
- # raise UnsupportedRenderMethod(platform_name=self.details.name, method=OperatorType.IS_NONE.capitalize())
85
- #
86
- # def is_not_none(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: # noqa: ARG002
87
- # raise UnsupportedRenderMethod(platform_name=self.details.name, method=OperatorType.IS_NOT_NONE.capitalize())
39
+ str_value_manager : StrValueManager = falco_rule_str_value_manager
40
+
41
+ @staticmethod
42
+ def _wrap_str_value (value : str ) -> str :
43
+ return f'"{ value } "'
44
+
45
+ @staticmethod
46
+ def _wrap_int_value (value : str ) -> str :
47
+ return f'"{ value } "'
48
+
49
+ def equal_modifier (self , field : str , value : DEFAULT_VALUE_TYPE ) -> str :
50
+ if isinstance (value , list ):
51
+ return f"({ self .or_token .join ([self .equal_modifier (field = field , value = v ) for v in value ])} )"
52
+ return f"{ field } = { self ._pre_process_value (field , value , wrap_str = True )} "
53
+
54
+ def not_equal_modifier (self , field : str , value : DEFAULT_VALUE_TYPE ) -> str :
55
+ if isinstance (value , list ):
56
+ return f"({ self .or_token .join ([self .not_equal_modifier (field = field , value = v ) for v in value ])} )"
57
+ return f"{ field } != { self ._pre_process_value (field , value , wrap_str = True )} "
58
+
59
+ def less_modifier (self , field : str , value : int ) -> str :
60
+ return f"{ field } < { self ._pre_process_value (field , value )} "
61
+
62
+ def less_or_equal_modifier (self , field : str , value : int ) -> str :
63
+ return f"{ field } <= { self ._pre_process_value (field , value )} "
64
+
65
+ def greater_modifier (self , field : str , value : int ) -> str :
66
+ return f"{ field } > { self ._pre_process_value (field , value )} "
67
+
68
+ def greater_or_equal_modifier (self , field : str , value : int ) -> str :
69
+ return f"{ field } >= { self ._pre_process_value (field , value )} "
70
+
71
+ def contains_modifier (self , field : str , value : DEFAULT_VALUE_TYPE ) -> str :
72
+ if isinstance (value , list ):
73
+ return f"({ self .or_token .join ([self .contains_modifier (field = field , value = v ) for v in value ])} )"
74
+ value = self ._pre_process_value (field , value , wrap_str = True , wrap_int = True )
75
+ return f"{ field } contains { value } "
76
+
77
+ def endswith_modifier (self , field : str , value : DEFAULT_VALUE_TYPE ) -> str :
78
+ if isinstance (value , list ):
79
+ return f"({ self .or_token .join ([self .endswith_modifier (field = field , value = v ) for v in value ])} )"
80
+ value = self ._pre_process_value (field , value , wrap_str = True , wrap_int = True )
81
+ return f"{ field } endswith { value } "
82
+
83
+ def startswith_modifier (self , field : str , value : DEFAULT_VALUE_TYPE ) -> str :
84
+ if isinstance (value , list ):
85
+ return f"({ self .or_token .join ([self .startswith_modifier (field = field , value = v ) for v in value ])} )"
86
+ value = self ._pre_process_value (field , value , wrap_str = True , wrap_int = True )
87
+ return f"{ field } startswith { value } "
88
+
89
+ def regex_modifier (self , field : str , value : DEFAULT_VALUE_TYPE ) -> str :
90
+ if isinstance (value , list ):
91
+ return f"({ self .or_token .join (self .regex_modifier (field = field , value = v ) for v in value )} )"
92
+ regex_str = self ._pre_process_value (field , value , value_type = ValueType .regex_value )
93
+ return f"{ field } regex '{ regex_str } '"
94
+
95
+ def is_none (self , field : str , value : DEFAULT_VALUE_TYPE ) -> str :
96
+ if isinstance (value , list ):
97
+ return f"({ self .or_token .join (self .is_none (field = field , value = v ) for v in value )} )"
98
+ return f"{ field } exists"
88
99
89
100
90
101
@render_manager .register
@@ -98,32 +109,51 @@ class FalcoRuleRender(PlatformQueryRender):
98
109
99
110
comment_symbol = "//"
100
111
101
- field_value_render = FalcoFieldValueRender (or_token = or_token )
112
+ field_value_render = FalcoRuleFieldValueRender (or_token = or_token )
113
+
114
+ priority_map : ClassVar [dict [str , str ]] = {
115
+ "unspecified" : "NOTICE" ,
116
+ "info" : "INFORMATIONAL" ,
117
+ "low" : "WARNING" ,
118
+ "medium" : "ERROR" ,
119
+ "high" : "ERROR" ,
120
+ "critical" : "CRITICAL" ,
121
+ }
102
122
103
123
def generate_prefix (self , log_source_signature : Optional [LogSourceSignature ], functions_prefix : str = "" ) -> str : # noqa: ARG002
104
124
return ""
105
125
126
+ def generate_output (self , fields : list [Field ], unmapped_fields : list [str ], source_mapping : SourceMapping ) -> str :
127
+ extra_fields = [
128
+ field .source_name
129
+ if field .source_name in unmapped_fields
130
+ else source_mapping .fields_mapping .get_platform_field_name (generic_field_name = field .source_name )
131
+ for field in fields
132
+ ]
133
+ extra_fields = [f"{ field .replace ('.' , '_' )} =%{ field } " for field in extra_fields ]
134
+ return f"shell in a container (container_name=%container.name { ' ' .join (extra_fields )} )"
106
135
107
136
def finalize_query (
108
137
self ,
109
138
prefix : str ,
110
139
query : str ,
111
140
functions : str ,
112
141
meta_info : Optional [MetaInfoContainer ] = None ,
113
- source_mapping : Optional [SourceMapping ] = None , # noqa: ARG002
142
+ source_mapping : Optional [SourceMapping ] = None ,
114
143
not_supported_functions : Optional [list ] = None ,
115
144
unmapped_fields : Optional [list [str ]] = None ,
116
145
* args , # noqa: ARG002
117
146
** kwargs , # noqa: ARG002
118
147
) -> str :
119
- query = super ().finalize_query (prefix = prefix , query = query , functions = functions )
120
- default_output = "shell in a container (user=%user.name container_id=%container.id container_name=%container.name)"
148
+ query = self ._join_query_parts (prefix , query , functions )
121
149
rule = {
122
150
"rule" : meta_info .title or "Falco Rule" ,
123
151
"condition" : query ,
124
152
"desc" : meta_info .description or "Falco Rule" ,
125
- "output" : default_output ,
126
- "priority" : "alert" ,
153
+ "output" : self . generate_output ( meta_info . query_fields , unmapped_fields or [], source_mapping ) ,
154
+ "priority" : self . priority_map . get ( meta_info . severity or "medium" ) ,
127
155
}
128
- rule = yaml .dump (rule , default_flow_style = False , sort_keys = False )
129
- return rule
156
+ rule_str = yaml .dump (rule , default_flow_style = False , sort_keys = False )
157
+ rule_str = self .wrap_with_meta_info (rule_str , meta_info )
158
+ rule_str = self .wrap_with_unmapped_fields (rule_str , unmapped_fields )
159
+ return self .wrap_with_not_supported_functions (rule_str , not_supported_functions )
0 commit comments