From a69e9f203e8912411ae64de3c139124b33af06b6 Mon Sep 17 00:00:00 2001 From: emayuri-godaddy Date: Wed, 29 Mar 2023 13:00:30 -0700 Subject: [PATCH] Processing rule-patterns from default configuration files --- CHANGELOG.md | 1 + tartufo/scanner.py | 24 ++++++++++++++++++++++- tests/test_base_scanner.py | 39 +++++++++++++++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 597a4813..dd91e986 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Features: * [#479](https://github.com/godaddy/tartufo/pull/479) - Remove upward traversal logic for config discovery Bug fixes: +* [#482](https://github.com/godaddy/tartufo/pull/482) - Code updates to process rule-patterns set up in the target's default config file i.e. tartufo.toml or pyproject.toml * [#467](https://github.com/godaddy/tartufo/issues/467) - Multiple fixes to configuration file processing: - If multiple configuration files were specified, only the last was processed diff --git a/tartufo/scanner.py b/tartufo/scanner.py index cfd31cb0..a6f2ac1c 100755 --- a/tartufo/scanner.py +++ b/tartufo/scanner.py @@ -146,6 +146,7 @@ class ScannerBase(abc.ABC): # pylint: disable=too-many-instance-attributes logger: logging.Logger _scan_lock: threading.Lock = threading.Lock() _excluded_signatures: Optional[Tuple[str, ...]] = None + _rule_patterns: Optional[Iterable[Dict[str, str]]] = None _config_data: MutableMapping[str, Any] = {} _issue_list: List[Issue] = [] _issue_file: Optional[IO] = None @@ -333,7 +334,7 @@ def rules_regexes(self) -> Set[Rule]: try: self._rules_regexes = config.configure_regexes( include_default=self.global_options.default_regexes, - rule_patterns=self.global_options.rule_patterns, + rule_patterns=self.rule_patterns, rules_repo=self.global_options.git_rules_repo, rules_repo_files=self.global_options.git_rules_files, ) @@ -372,6 +373,27 @@ def should_scan(self, file_path: str) -> bool: return False return True + @cached_property + def rule_patterns(self) -> Optional[Iterable[Dict[str, str]]]: + """Get a list of patterns to the to search in the target repository or folder being scanned + + :returns: The regular expression patterns to searched in the target + @return: + """ + if self._rule_patterns is None: + rules: List[Dict[str, str]] = [] + for rule in tuple(self.global_options.rule_patterns or []) + tuple( + self.config_data.get("rule_patterns", []) + ): + if isinstance(rule, dict): + rules.append(rule) + else: + raise types.ConfigException( + f"{type(rule).__name__} pattern is illegal in rule-patterns" + ) + self._rule_patterns = rules + return self._rule_patterns + @cached_property def excluded_signatures(self) -> Tuple[str, ...]: """Get a list of the signatures of findings to be excluded from the scan results. diff --git a/tests/test_base_scanner.py b/tests/test_base_scanner.py index 08ca66bf..4b15cb1e 100644 --- a/tests/test_base_scanner.py +++ b/tests/test_base_scanner.py @@ -271,10 +271,10 @@ def test_populated_regex_list_does_not_recompute(self): def test_regex_rules_are_computed_when_first_accessed(self): self.options.default_regexes = True - self.options.rule_patterns = "oof" # type: ignore self.options.git_rules_repo = "bar" self.options.git_rules_files = "baz" # type: ignore test_scanner = TestScanner(self.options) + test_scanner._rule_patterns = "oof" # pylint: disable=protected-access test_scanner.rules_regexes # pylint: disable=pointless-statement self.mock_configure.assert_called_once_with( include_default=True, @@ -283,6 +283,43 @@ def test_regex_rules_are_computed_when_first_accessed(self): rules_repo_files="baz", ) + def test_rule_patterns_with_rules_in_default_config(self): + rule_patterns = [ + { + "reason": "RSA private key 2", + "pattern": "-----BEGIN default PRIVATE KEY-----", + } + ] + self.options.rule_patterns = [] + test_scanner = TestScanner(self.options) + test_scanner.config_data = {"rule_patterns": rule_patterns} + self.assertEqual(test_scanner.rule_patterns, rule_patterns) + + def test_rule_patterns_with_rules_in_custom_config(self): + rule_patterns = [ + { + "reason": "RSA private key 2", + "pattern": "-----BEGIN default PRIVATE KEY-----", + } + ] + self.options.rule_patterns = rule_patterns + test_scanner = TestScanner(self.options) + test_scanner.config_data = {} + self.assertEqual(test_scanner.rule_patterns, rule_patterns) + + def test_rule_patterns_with_rule_patterns_syntax_issue(self): + rule_patterns = { + "reason": "RSA private key 2", + "pattern": "-----BEGIN default PRIVATE KEY-----", + } + self.options.rule_patterns = rule_patterns + test_scanner = TestScanner(self.options) + test_scanner.config_data = {} + with self.assertRaisesRegex( + types.ConfigException, "str pattern is illegal in rule-patterns" + ): + test_scanner.rule_patterns # pylint: disable=pointless-statement + class SignatureTests(ScannerTestCase): @mock.patch("tartufo.util.generate_signature")