diff --git a/integration_tests/test_program.py b/integration_tests/test_program.py index 3d14e931e..65aa0b0d3 100644 --- a/integration_tests/test_program.py +++ b/integration_tests/test_program.py @@ -1,4 +1,7 @@ import subprocess +from core_codemods.remove_assertion_in_pytest_raises import ( + RemoveAssertionInPytestRaises, +) class TestProgramFails: @@ -21,3 +24,21 @@ def test_codemods_include_exclude_conflict(self): check=False, ) assert completed_process.returncode == 3 + + def test_load_sast_only_by_flag(self, tmp_path): + tmp_file_path = tmp_path / "sonar.json" + tmp_file_path.touch() + completed_process = subprocess.run( + [ + "codemodder", + "tests/samples/", + "--sonar-issues-json", + f"{tmp_file_path}", + "--dry-run", + ], + check=False, + capture_output=True, + text=True, + ) + assert completed_process.returncode == 0 + assert RemoveAssertionInPytestRaises.id not in completed_process.stdout diff --git a/src/codemodder/codemodder.py b/src/codemodder/codemodder.py index 4ff92bb39..5a98c55c3 100644 --- a/src/codemodder/codemodder.py +++ b/src/codemodder/codemodder.py @@ -183,7 +183,9 @@ def run(original_args) -> int: # TODO: this should be a method of CodemodExecutionContext codemods_to_run = codemod_registry.match_codemods( - argv.codemod_include, argv.codemod_exclude + argv.codemod_include, + argv.codemod_exclude, + sast_only=argv.sonar_issues_json or argv.sarif, ) log_section("setup") diff --git a/src/codemodder/codemods/base_codemod.py b/src/codemodder/codemods/base_codemod.py index f2c5b2d21..cdd36a50c 100644 --- a/src/codemodder/codemods/base_codemod.py +++ b/src/codemodder/codemods/base_codemod.py @@ -108,10 +108,10 @@ def docs_module(self) -> Traversable: @cached_property def description(self) -> str: - if not self._metadata.description: + if self._metadata.description == None: doc_path = self.docs_module / f"{self.origin}_python_{self.name}.md" return doc_path.read_text() - return self._metadata.description + return self._metadata.description # type: ignore @property def review_guidance(self): diff --git a/src/codemodder/codemods/base_visitor.py b/src/codemodder/codemods/base_visitor.py index a84776342..e50c91784 100644 --- a/src/codemodder/codemods/base_visitor.py +++ b/src/codemodder/codemods/base_visitor.py @@ -35,7 +35,6 @@ def node_is_selected(self, node) -> bool: return False pos_to_match = self.node_position(node) - print(pos_to_match) return self.filter_by_result( pos_to_match ) and self.filter_by_path_includes_or_excludes(pos_to_match) diff --git a/src/codemodder/registry.py b/src/codemodder/registry.py index 1c678f355..15ba125b7 100644 --- a/src/codemodder/registry.py +++ b/src/codemodder/registry.py @@ -59,14 +59,24 @@ def match_codemods( self, codemod_include: Optional[list] = None, codemod_exclude: Optional[list] = None, + sast_only=False, ) -> list[BaseCodemod]: codemod_include = codemod_include or [] codemod_exclude = codemod_exclude or DEFAULT_EXCLUDED_CODEMODS + if sast_only: + base_list = [ + codemod for codemod in self.codemods if codemod.origin != "pixee" + ] + else: + base_list = [ + codemod for codemod in self.codemods if codemod.origin == "pixee" + ] + if codemod_exclude and not codemod_include: return [ codemod - for codemod in self.codemods + for codemod in base_list if codemod.name not in codemod_exclude and codemod.id not in codemod_exclude ] diff --git a/tests/codemods/test_include_exclude.py b/tests/codemods/test_include_exclude.py index 8fcd84673..131671f03 100644 --- a/tests/codemods/test_include_exclude.py +++ b/tests/codemods/test_include_exclude.py @@ -1,5 +1,6 @@ import pytest from codemodder.registry import DEFAULT_EXCLUDED_CODEMODS, load_registered_codemods +from core_codemods import registry class TestMatchCodemods: @@ -9,12 +10,31 @@ def setup_class(cls): cls.codemod_map = ( cls.registry._codemods_by_name # pylint: disable=protected-access ) + cls.default_ids = [ + c().id if isinstance(c, type) else c.id for c in registry.codemods + ] def test_no_include_exclude(self): - defaults = set( - x for x in self.registry.codemods if x.id not in DEFAULT_EXCLUDED_CODEMODS + default_codemods = set( + x + for x in self.registry.codemods + if x.id in self.default_ids and x.id not in DEFAULT_EXCLUDED_CODEMODS + ) + assert set(self.registry.match_codemods(None, None)) == default_codemods + + def test_load_sast_codemods(self): + sast_codemods = set( + c for c in self.registry.codemods if c.id not in self.default_ids ) - assert set(self.registry.match_codemods(None, None)) == defaults + assert ( + set(self.registry.match_codemods(None, None, sast_only=True)) + == sast_codemods + ) + + def test_include_non_sast_in_sast(self): + assert set( + self.registry.match_codemods(["secure-random"], None, sast_only=True) + ) == {self.codemod_map["secure-random"]} @pytest.mark.parametrize( "input_str", ["secure-random", "secure-random,url-sandbox"] @@ -44,5 +64,7 @@ def test_include_preserve_order(self, input_str): def test_exclude(self, input_str): excludes = input_str.split(",") assert self.registry.match_codemods(None, excludes) == [ - v for (k, v) in self.codemod_map.items() if k not in excludes + c + for c in self.registry.codemods + if c.name not in excludes and c.id in self.default_ids ] diff --git a/tests/test_cli.py b/tests/test_cli.py index e95c42804..e91240363 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -6,6 +6,7 @@ from codemodder.cli import parse_args from codemodder import __version__ from codemodder.registry import DEFAULT_EXCLUDED_CODEMODS, load_registered_codemods +from core_codemods import registry as core_registry class TestParseArgs: @@ -102,7 +103,7 @@ def test_describe_prints_codemod_metadata(self, mock_print): assert mock_print.call_count == 1 results = json.loads(mock_print.call_args_list[0][0][0]) - assert len(results["results"]) == len(self.registry.codemods) - len( + assert len(results["results"]) == len(core_registry.codemods) - len( DEFAULT_EXCLUDED_CODEMODS )