1    	import re
2    	from textwrap import dedent
3    	from unittest import TestCase
4    	
5    	from pcs.cli import nvset
6    	from pcs.common.pacemaker.nvset import CibNvpairDto, CibNvsetDto
7    	from pcs.common.pacemaker.rule import CibRuleExpressionDto
8    	from pcs.common.types import CibRuleExpressionType, CibRuleInEffectStatus
9    	
10   	
11   	def fixture_dto(in_effect):
12   	    return CibNvsetDto(
13   	        f"id-{in_effect}",
14   	        {"score": "150"},
15   	        CibRuleExpressionDto(
16   	            f"id-{in_effect}-rule",
17   	            CibRuleExpressionType.RULE,
18   	            in_effect,
19   	            {"boolean-op": "or"},
20   	            None,
21   	            None,
22   	            [
23   	                CibRuleExpressionDto(
24   	                    f"id-{in_effect}-rule-op",
25   	                    CibRuleExpressionType.OP_EXPRESSION,
26   	                    CibRuleInEffectStatus.UNKNOWN,
27   	                    {"name": "monitor"},
28   	                    None,
29   	                    None,
30   	                    [],
31   	                    "op monitor",
32   	                ),
33   	            ],
34   	            "op monitor",
35   	        ),
36   	        [CibNvpairDto(f"id-{in_effect}-pair1", "name1", "value1")],
37   	    )
38   	
39   	
40   	def fixture_dto_list():
41   	    return [fixture_dto(in_effect.value) for in_effect in CibRuleInEffectStatus]
42   	
43   	
44   	class FilterOutExpiredNvset(TestCase):
45   	    def test_filter(self):
46   	        self.maxDiff = None
47   	        list_with_expired_nvsets = [
48   	            CibNvsetDto(id="nvset-no-rule", options={}, rule=None, nvpairs=[]),
49   	            CibNvsetDto(
50   	                id="nvset-2",
51   	                options={},
52   	                rule=CibRuleExpressionDto(
53   	                    id="rule-another-expired",
54   	                    type=CibRuleExpressionType.DATE_EXPRESSION,
55   	                    in_effect=CibRuleInEffectStatus.EXPIRED,
56   	                    options={},
57   	                    date_spec=None,
58   	                    duration=None,
59   	                    expressions=[],
60   	                    as_string="",
61   	                ),
62   	                nvpairs=[],
63   	            ),
64   	        ] + fixture_dto_list()
65   	        expected_list = [
66   	            item
67   	            for idx, item in enumerate(list_with_expired_nvsets[:])
68   	            if idx not in [1, 4]
69   	        ]
70   	        self.assertEqual(
71   	            nvset.filter_out_expired_nvset(list_with_expired_nvsets),
72   	            expected_list,
73   	        )
74   	
75   	    def test_empty_list(self):
76   	        self.assertEqual(nvset.filter_out_expired_nvset([]), [])
77   	
78   	
79   	class FilterNvpairsByNames(TestCase):
80   	    test_nvsets = [
81   	        CibNvsetDto(
82   	            id="1",
83   	            options={},
84   	            rule=None,
85   	            nvpairs=[
86   	                CibNvpairDto(id="1a", name="a", value="1"),
87   	                CibNvpairDto(id="1b", name="b", value="2"),
88   	                CibNvpairDto(id="1c", name="c", value="3"),
89   	            ],
90   	        ),
91   	        CibNvsetDto(
92   	            id="2",
93   	            options={},
94   	            rule=None,
95   	            nvpairs=[
96   	                CibNvpairDto(id="2a", name="a", value="1"),
97   	                CibNvpairDto(id="2b", name="b", value="2"),
98   	            ],
99   	        ),
100  	    ]
101  	
102  	    def test_filter_no_match(self):
103  	        self.assertEqual(
104  	            nvset.filter_nvpairs_by_names(self.test_nvsets, ["x"]),
105  	            [
106  	                CibNvsetDto(id="1", options={}, rule=None, nvpairs=[]),
107  	                CibNvsetDto(id="2", options={}, rule=None, nvpairs=[]),
108  	            ],
109  	        )
110  	
111  	    def test_filter_match_in_one_set(self):
112  	        self.assertEqual(
113  	            nvset.filter_nvpairs_by_names(self.test_nvsets, ["c"]),
114  	            [
115  	                CibNvsetDto(
116  	                    id="1",
117  	                    options={},
118  	                    rule=None,
119  	                    nvpairs=[
120  	                        CibNvpairDto(id="1c", name="c", value="3"),
121  	                    ],
122  	                ),
123  	                CibNvsetDto(id="2", options={}, rule=None, nvpairs=[]),
124  	            ],
125  	        )
126  	
127  	    def test_filter_match_in_more_set(self):
128  	        self.assertEqual(
129  	            nvset.filter_nvpairs_by_names(self.test_nvsets, ["a"]),
130  	            [
131  	                CibNvsetDto(
132  	                    id="1",
133  	                    options={},
134  	                    rule=None,
135  	                    nvpairs=[
136  	                        CibNvpairDto(id="1a", name="a", value="1"),
137  	                    ],
138  	                ),
139  	                CibNvsetDto(
140  	                    id="2",
141  	                    options={},
142  	                    rule=None,
143  	                    nvpairs=[
144  	                        CibNvpairDto(id="2a", name="a", value="1"),
145  	                    ],
146  	                ),
147  	            ],
148  	        )
149  	
150  	    def test_filter_multiple_filter(self):
151  	        self.assertEqual(
152  	            nvset.filter_nvpairs_by_names(self.test_nvsets, ["a", "c", "x"]),
153  	            [
154  	                CibNvsetDto(
155  	                    id="1",
156  	                    options={},
157  	                    rule=None,
158  	                    nvpairs=[
159  	                        CibNvpairDto(id="1a", name="a", value="1"),
160  	                        CibNvpairDto(id="1c", name="c", value="3"),
161  	                    ],
162  	                ),
163  	                CibNvsetDto(
164  	                    id="2",
165  	                    options={},
166  	                    rule=None,
167  	                    nvpairs=[
168  	                        CibNvpairDto(id="2a", name="a", value="1"),
169  	                    ],
170  	                ),
171  	            ],
172  	        )
173  	
174  	
175  	class NvsetDtoToLines(TestCase):
176  	    def setUp(self):
177  	        self.label = "Meta Attributes"
178  	        self.full_dto = CibNvsetDto(
179  	            "my-id",
180  	            {"score": "150"},
181  	            CibRuleExpressionDto(
182  	                "my-id-rule",
183  	                CibRuleExpressionType.RULE,
184  	                CibRuleInEffectStatus.UNKNOWN,
185  	                {"boolean-op": "or"},
186  	                None,
187  	                None,
188  	                [
189  	                    CibRuleExpressionDto(
190  	                        "my-id-rule-op",
191  	                        CibRuleExpressionType.OP_EXPRESSION,
192  	                        CibRuleInEffectStatus.UNKNOWN,
193  	                        {"name": "monitor"},
194  	                        None,
195  	                        None,
196  	                        [],
197  	                        "op monitor",
198  	                    ),
199  	                ],
200  	                "op monitor",
201  	            ),
202  	            [
203  	                CibNvpairDto("my-id-pair1", "name1", "value1"),
204  	                CibNvpairDto("my-id-pair2", "name 2", "value 2"),
205  	                CibNvpairDto("my-id-pair3", "name=3", "value=3"),
206  	                CibNvpairDto("my-id-pair4", "secret1", "lrm://"),
207  	                CibNvpairDto("my-id-pair5", "secret 2", "lrm://"),
208  	                CibNvpairDto("my-id-pair6", "secret=3", "lrm://"),
209  	            ],
210  	        )
211  	
212  	    def _export(self, dto, with_ids, secrets_map):
213  	        return (
214  	            "\n".join(
215  	                nvset.nvset_dto_to_lines(
216  	                    dto,
217  	                    nvset_label=self.label,
218  	                    with_ids=with_ids,
219  	                    secrets_map=secrets_map,
220  	                )
221  	            )
222  	            + "\n"
223  	        )
224  	
225  	    def assert_lines(self, dto, lines, secrets_map=None):
226  	        self.assertEqual(
227  	            self._export(dto, True, secrets_map),
228  	            lines,
229  	        )
230  	        self.assertEqual(
231  	            self._export(dto, False, secrets_map),
232  	            re.sub(r" +\(id:.*\)", "", lines),
233  	        )
234  	
235  	    def test_minimal(self):
236  	        dto = CibNvsetDto("my-id", {}, None, [])
237  	        output = dedent(
238  	            f"""\
239  	              {self.label}: my-id
240  	            """
241  	        )
242  	        self.assert_lines(dto, output)
243  	
244  	    def test_full_without_secrets_map(self):
245  	        output = dedent(
246  	            f"""\
247  	            {self.label}: my-id score=150
248  	              "name 2"="value 2" (id: my-id-pair2)
249  	              name1=value1 (id: my-id-pair1)
250  	              "name=3"="value=3" (id: my-id-pair3)
251  	              "secret 2"=lrm:// (id: my-id-pair5)
252  	              secret1=lrm:// (id: my-id-pair4)
253  	              "secret=3"=lrm:// (id: my-id-pair6)
254  	              Rule: boolean-op=or (id: my-id-rule)
255  	                Expression: op monitor (id: my-id-rule-op)
256  	            """
257  	        )
258  	        self.assert_lines(self.full_dto, output)
259  	
260  	    def test_full_with_secrets_map(self):
261  	        output = dedent(
262  	            f"""\
263  	            {self.label}: my-id score=150
264  	              "name 2"="value 2" (id: my-id-pair2)
265  	              name1=value1 (id: my-id-pair1)
266  	              "name=3"="value=3" (id: my-id-pair3)
267  	              "secret=3"=lrm:// (id: my-id-pair6)
268  	              Secret {self.label}:
269  	                "secret 2" (id: my-id-pair5)
270  	                secret1=secret_value1 (id: my-id-pair4)
271  	              Rule: boolean-op=or (id: my-id-rule)
272  	                Expression: op monitor (id: my-id-rule-op)
273  	            """
274  	        )
275  	        secrets_map = {
CID (unavailable; MK=e68700c72a7aa768563f0c88c662bbba) (#1 of 1): Hard-coded secret (SIGMA.hardcoded_secret):
(1) Event Sigma main event: A secret, such as a password, cryptographic key, or token is stored in plaintext directly in the source code, in an application's properties, or configuration file. Users with access to the secret may then use the secret to access resources that they otherwise would not have access to. Secret type: `Secret (generic)`.
(2) Event remediation: Avoid setting sensitive configuration values as string literals. Instead, these values should be set using variables with the sensitive data loaded from an encrypted file or a secret store.
276  	            "secret1": "secret_value1",
277  	            "secret 2": None,
278  	        }
279  	        self.assert_lines(self.full_dto, output, secrets_map)
280  	
281  	
282  	class NvsetDtoListToLines(TestCase):
283  	    def setUp(self):
284  	        self.label = "Meta Attributes"
285  	
286  	    def _export(self, dto, with_ids):
287  	        return (
288  	            "\n".join(
289  	                nvset.nvset_dto_list_to_lines(
290  	                    dto,
291  	                    nvset_label=self.label,
292  	                    with_ids=with_ids,
293  	                )
294  	            )
295  	            + "\n"
296  	        )
297  	
298  	    def assert_lines(self, dto, lines):
299  	        self.assertEqual(
300  	            self._export(dto, True),
301  	            lines,
302  	        )
303  	        self.assertEqual(
304  	            self._export(dto, False),
305  	            re.sub(r" +\(id:.*\)", "", lines),
306  	        )
307  	
308  	    def test_lines(self):
309  	        self.maxDiff = None
310  	        output = dedent(
311  	            f"""\
312  	            {self.label} (not yet in effect): id-NOT_YET_IN_EFFECT score=150
313  	              name1=value1 (id: id-NOT_YET_IN_EFFECT-pair1)
314  	              Rule (not yet in effect): boolean-op=or (id: id-NOT_YET_IN_EFFECT-rule)
315  	                Expression: op monitor (id: id-NOT_YET_IN_EFFECT-rule-op)
316  	            {self.label}: id-IN_EFFECT score=150
317  	              name1=value1 (id: id-IN_EFFECT-pair1)
318  	              Rule: boolean-op=or (id: id-IN_EFFECT-rule)
319  	                Expression: op monitor (id: id-IN_EFFECT-rule-op)
320  	            {self.label} (expired): id-EXPIRED score=150
321  	              name1=value1 (id: id-EXPIRED-pair1)
322  	              Rule (expired): boolean-op=or (id: id-EXPIRED-rule)
323  	                Expression: op monitor (id: id-EXPIRED-rule-op)
324  	            {self.label}: id-UNKNOWN score=150
325  	              name1=value1 (id: id-UNKNOWN-pair1)
326  	              Rule: boolean-op=or (id: id-UNKNOWN-rule)
327  	                Expression: op monitor (id: id-UNKNOWN-rule-op)
328  	        """
329  	        )
330  	        self.assert_lines(fixture_dto_list(), output)
331