From 9dcb67596365674c8c75b996aa5f2daddba412de Mon Sep 17 00:00:00 2001 From: Olivier Leduc Date: Thu, 8 Jun 2023 16:15:58 -0400 Subject: [PATCH] BST-5922: Fix imported default ignored (#29) Like regular rules, default rule can be imported and the latest imported one should take precedence over the others. In the previous version, any import default would just get ignored. --- .../registry_validator/upload_rules_db.py | 3 +- tests/unit/scanner/test_upload_rules_db.py | 164 ++++++++++++++++++ 2 files changed, 166 insertions(+), 1 deletion(-) diff --git a/boostsec/registry_validator/upload_rules_db.py b/boostsec/registry_validator/upload_rules_db.py index 7ce08b8..8e2333d 100644 --- a/boostsec/registry_validator/upload_rules_db.py +++ b/boostsec/registry_validator/upload_rules_db.py @@ -136,8 +136,9 @@ def _get_rules_and_default( default_rule = None if imports := rules_db_yaml.get("import"): for ns in imports: - import_rules, _ = _get_rules_and_default(ns, config) + import_rules, imported_default = _get_rules_and_default(ns, config) rules.update(import_rules) + default_rule = imported_default or default_rule if module_rules := rules_db_yaml.get("rules"): rules.update(module_rules) diff --git a/tests/unit/scanner/test_upload_rules_db.py b/tests/unit/scanner/test_upload_rules_db.py index d9d695e..d55ad1f 100644 --- a/tests/unit/scanner/test_upload_rules_db.py +++ b/tests/unit/scanner/test_upload_rules_db.py @@ -21,6 +21,7 @@ VALID_RULES_DB_STRING, VALID_RULES_DB_STRING_WITH_DEFAULT, VALID_RULES_DB_STRING_WITH_IMPORTS, + VALID_RULES_DB_STRING_WITH_ONLY_IMPORT, VALID_RULES_DB_STRING_WITH_PLACEHOLDER, ) @@ -394,6 +395,169 @@ def has_auth_token(request: Any) -> bool: } +def test_upload_rules_db_with_imported_default( + registry_config: RegistryConfig, requests_mock: Mocker +) -> None: + """Should include any imported default rule.""" + url = "https://my_endpoint/" + test_token = "my-random-key" # noqa: S105 + + def has_auth_token(request: Any) -> bool: + assert request.headers["Authorization"] == f"ApiKey {test_token}" + return True + + requests_mock.post( + urljoin(url, "/rules-management/graphql"), + additional_matcher=has_auth_token, + json={ + "data": {"setRules": {"__typename": "RuleSuccessSchema"}}, + }, + ) + + _create_rules_realm( + registry_config.rules_realm_path, + VALID_RULES_DB_STRING_WITH_DEFAULT, + "namespace/module-a", + ) + + namespace = "namespace-example" + module_path = _create_module_and_rules( + registry_config.scanners_path, VALID_RULES_DB_STRING_WITH_ONLY_IMPORT, namespace + ) + + upload_rules_db( + module_path.parent, + url, + test_token, + registry_config, + ) + + assert requests_mock.call_count == 1 + assert requests_mock.last_request is not None + req_json = requests_mock.last_request.json() + assert req_json == { + "query": "mutation setRules($rules: RuleInputSchemas!) {\n setRules(namespacedRules: $rules) {\n __typename\n ... on RuleSuccessSchema {\n successMessage\n }\n ... on RuleErrorSchema {\n errorMessage\n }\n }\n}", # noqa: E501 + "variables": { + "rules": { + "namespace": "namespace-example", + "defaultRule": "my-rule-2", + "ruleInputs": [ + { + "categories": ["ALL", "category-1"], + "description": "Lorem Ipsum", + "driver": "Example Scanner", + "group": "Test group 1", + "name": "my-rule-1", + "prettyName": "My rule 1", + "ref": "http://my.link.com", + }, + { + "categories": ["ALL", "category-2"], + "description": "Lorem Ipsum", + "driver": "Example Scanner", + "group": "Test group 2", + "name": "my-rule-2", + "prettyName": "My rule 2", + "ref": "http://my.link.com", + }, + ], + } + }, + } + + +def test_upload_rules_db_imported_default_precedence( + registry_config: RegistryConfig, requests_mock: Mocker +) -> None: + """Module default should take precedence over any imported one.""" + url = "https://my_endpoint/" + test_token = "my-random-key" # noqa: S105 + + def has_auth_token(request: Any) -> bool: + assert request.headers["Authorization"] == f"ApiKey {test_token}" + return True + + requests_mock.post( + urljoin(url, "/rules-management/graphql"), + additional_matcher=has_auth_token, + json={ + "data": {"setRules": {"__typename": "RuleSuccessSchema"}}, + }, + ) + + _create_rules_realm( + registry_config.rules_realm_path, + VALID_RULES_DB_STRING_WITH_DEFAULT, + "namespace/module-a", + ) + + namespace = "namespace-example" + rules = VALID_RULES_DB_STRING_WITH_ONLY_IMPORT + rules += """ +default: + my-default: + categories: + - ALL + description: Lorem Ipsum + group: Test default + name: my-default + pretty_name: My Default + ref: "http://my.link.com" + """ + module_path = _create_module_and_rules( + registry_config.scanners_path, rules, namespace + ) + + upload_rules_db( + module_path.parent, + url, + test_token, + registry_config, + ) + + assert requests_mock.call_count == 1 + assert requests_mock.last_request is not None + req_json = requests_mock.last_request.json() + assert req_json == { + "query": "mutation setRules($rules: RuleInputSchemas!) {\n setRules(namespacedRules: $rules) {\n __typename\n ... on RuleSuccessSchema {\n successMessage\n }\n ... on RuleErrorSchema {\n errorMessage\n }\n }\n}", # noqa: E501 + "variables": { + "rules": { + "namespace": "namespace-example", + "defaultRule": "my-default", + "ruleInputs": [ + { + "categories": ["ALL", "category-1"], + "description": "Lorem Ipsum", + "driver": "Example Scanner", + "group": "Test group 1", + "name": "my-rule-1", + "prettyName": "My rule 1", + "ref": "http://my.link.com", + }, + { + "categories": ["ALL", "category-2"], + "description": "Lorem Ipsum", + "driver": "Example Scanner", + "group": "Test group 2", + "name": "my-rule-2", + "prettyName": "My rule 2", + "ref": "http://my.link.com", + }, + { + "categories": ["ALL"], + "description": "Lorem Ipsum", + "driver": "Example Scanner", + "group": "Test default", + "name": "my-default", + "prettyName": "My Default", + "ref": "http://my.link.com", + }, + ], + } + }, + } + + def test_upload_rules_db_permission_denied( capfd: pytest.CaptureFixture[str], registry_config: RegistryConfig,