From cacefb81329181e1863fafc44bfe5e9cd30abd30 Mon Sep 17 00:00:00 2001 From: clavedeluna Date: Wed, 10 Apr 2024 08:57:07 -0300 Subject: [PATCH 1/3] clarify semgrep id must be same as codemod name id --- src/core_codemods/django_debug_flag_on.py | 4 ++-- src/core_codemods/django_session_cookie_secure_off.py | 4 ++-- src/core_codemods/limit_readline.py | 4 ++-- src/core_codemods/secure_flask_cookie.py | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/core_codemods/django_debug_flag_on.py b/src/core_codemods/django_debug_flag_on.py index c9192ee6..9b753bd8 100644 --- a/src/core_codemods/django_debug_flag_on.py +++ b/src/core_codemods/django_debug_flag_on.py @@ -19,9 +19,9 @@ class DjangoDebugFlagOn(SimpleCodemod): ], ) change_description = "Flip `Django` debug flag to off." - detector_pattern = """ + detector_pattern = f""" rules: - - id: django-debug-flag-on + - id: {metadata.name} pattern: DEBUG = True paths: include: diff --git a/src/core_codemods/django_session_cookie_secure_off.py b/src/core_codemods/django_session_cookie_secure_off.py index 34f086dd..11703d98 100644 --- a/src/core_codemods/django_session_cookie_secure_off.py +++ b/src/core_codemods/django_session_cookie_secure_off.py @@ -19,9 +19,9 @@ class DjangoSessionCookieSecureOff(SimpleCodemod): ], ) change_description = "Sets Django's `SESSION_COOKIE_SECURE` flag if off or missing." - detector_pattern = """ + detector_pattern = f""" rules: - - id: django-session-cookie-secure-off + - id: {metadata.name} # This pattern creates one finding with no text for settings.py file. pattern-regex: ^ paths: diff --git a/src/core_codemods/limit_readline.py b/src/core_codemods/limit_readline.py index 81c888f2..a131697f 100644 --- a/src/core_codemods/limit_readline.py +++ b/src/core_codemods/limit_readline.py @@ -15,9 +15,9 @@ class LimitReadline(SimpleCodemod): ], ) change_description = "Adds a size limit argument to readline() calls." - detector_pattern = """ + detector_pattern = f""" rules: - - id: limit-readline + - id: {metadata.name} mode: taint pattern-sources: - pattern-either: diff --git a/src/core_codemods/secure_flask_cookie.py b/src/core_codemods/secure_flask_cookie.py index 53bfe956..bb2ab666 100644 --- a/src/core_codemods/secure_flask_cookie.py +++ b/src/core_codemods/secure_flask_cookie.py @@ -17,9 +17,9 @@ class SecureFlaskCookie(SimpleCodemod, SecureCookieMixin): ], ) change_description = "Flask response `set_cookie` call should be called with `secure=True`, `httponly=True`, and `samesite='Lax'`." - detector_pattern = """ + detector_pattern = f""" rules: - - id: secure-flask-cookie + - id: {metadata.name} mode: taint pattern-sources: - pattern-either: From ad152bdfb0411715429d8337ee54a31b879967a4 Mon Sep 17 00:00:00 2001 From: clavedeluna Date: Wed, 10 Apr 2024 11:32:50 -0300 Subject: [PATCH 2/3] initial deprecate pkg_resources tests --- .../fix_deprecated_pkg_resources.py | 31 +++++ .../test_fix_deprecated_pkg_resources.py | 125 ++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 src/core_codemods/fix_deprecated_pkg_resources.py create mode 100644 tests/codemods/test_fix_deprecated_pkg_resources.py diff --git a/src/core_codemods/fix_deprecated_pkg_resources.py b/src/core_codemods/fix_deprecated_pkg_resources.py new file mode 100644 index 00000000..fa34617f --- /dev/null +++ b/src/core_codemods/fix_deprecated_pkg_resources.py @@ -0,0 +1,31 @@ +import libcst as cst + +from codemodder.codemods.utils_mixin import NameResolutionMixin +from core_codemods.api import Metadata, Reference, ReviewGuidance, SimpleCodemod + + +class FixDeprecatedPkgResources(SimpleCodemod, NameResolutionMixin): + metadata = Metadata( + name="fix-deprecated-pkg-resources", + summary="Replace Deprecated Use of `pkg_resources` Module`", + review_guidance=ReviewGuidance.MERGE_WITHOUT_REVIEW, + references=[ + Reference( + url="TODOOOOhttps://docs.python.org/3/library/logging.html#logging.Logger.warning" + ), + ], + ) + change_description = "Replace deprecated `logging.warn` with `logging.warning`" + detector_pattern = f""" + rules: + - id: {metadata.name} + pattern: pkg_resources.get_distribution(...) + pattern-inside: | + import pkg_resources + ... + """ + + def on_result_found(self, original_node, updated_node): + self.remove_unused_import(original_node) + self.add_needed_import("importlib.metadata", "distribution") + return updated_node.with_changes(func=cst.Name(value="distribution")) diff --git a/tests/codemods/test_fix_deprecated_pkg_resources.py b/tests/codemods/test_fix_deprecated_pkg_resources.py new file mode 100644 index 00000000..08097db9 --- /dev/null +++ b/tests/codemods/test_fix_deprecated_pkg_resources.py @@ -0,0 +1,125 @@ +import pytest + +from codemodder.codemods.test import BaseSemgrepCodemodTest +from core_codemods.fix_deprecated_pkg_resources import FixDeprecatedPkgResources + + +class TestFixDeprecatedPkgResources(BaseSemgrepCodemodTest): + codemod = FixDeprecatedPkgResources + + @pytest.mark.parametrize( + "input_code,expected_output", + [ + ( + """ + import pkg_resources + dist = pkg_resources.get_distribution("package_name") + """, + """ + from importlib.metadata import distribution + + dist = distribution("package_name") + """, + ), + ( + """ + import pkg_resources + version = pkg_resources.get_distribution("package_name").version + """, + """ + from importlib.metadata import distribution + + version = distribution("package_name").version + """, + ), + ], + ) + def test_import(self, tmpdir, input_code, expected_output): + self.run_and_assert(tmpdir, input_code, expected_output) + + @pytest.mark.parametrize( + "input_code,expected_output", + [ + ( + """ + from pkg_resources import get_distribution + dist = get_distribution("package_name") + """, + """ + from importlib.metadata import distribution + + dist = distribution("package_name") + """, + ), + ( + """ + from pkg_resources import get_distribution + version = get_distribution("package_name").version + """, + """ + from importlib.metadata import distribution + + version = distribution("package_name").version + """, + ), + ], + ) + def test_from_import(self, tmpdir, input_code, expected_output): + self.run_and_assert(tmpdir, input_code, expected_output) + + # + # @pytest.mark.parametrize( + # "input_code,expected_output", + # [ + # ( + # """ + # from logging import warn as warn_func + # warn_func('something')""", + # """ + # from logging import warning + # warning('something')""", + # ), + # ( + # """ + # from logging import getLogger as make_logger + # logger = make_logger('anything') + # logger.warn('something')""", + # """ + # from logging import getLogger as make_logger + # logger = make_logger('anything') + # logger.warning('something')""", + # ), + # ], + # ) + # def test_import_alias(self, tmpdir, input_code, expected_output): + # self.run_and_assert(tmpdir, input_code, expected_output) + # + # @pytest.mark.parametrize( + # "code", + # [ + # """ + # import xyz + # xyz.warn('something') + # """, + # """ + # import my_logging + # log = my_logging.getLogger('anything') + # log.warn('something') + # """, + # ], + # ) + # def test_different_warn(self, tmpdir, code): + # self.run_and_assert(tmpdir, code, code) + # + # @pytest.mark.xfail(reason="Not currently supported") + # def test_log_as_arg(self, tmpdir): + # code = """ + # import logging + # log = logging.getLogger('foo') + # def some_function(logger): + # logger.{}("hi") + # some_function(log) + # """ + # original_code = code.format("warn") + # new_code = code.format("warning") + # self.run_and_assert(tmpdir, original_code, new_code) From 57837185bf883289fa64724cfce2073f90345550 Mon Sep 17 00:00:00 2001 From: clavedeluna Date: Wed, 10 Apr 2024 11:45:09 -0300 Subject: [PATCH 3/3] store codemod --- .../test_fix_deprecated_pkg_resources.py | 20 ++++ src/core_codemods/__init__.py | 2 + ...xee_python_fix-deprecated-pkg-resources.md | 0 .../test_fix_deprecated_pkg_resources.py | 101 ++++++++---------- 4 files changed, 67 insertions(+), 56 deletions(-) create mode 100644 integration_tests/test_fix_deprecated_pkg_resources.py create mode 100644 src/core_codemods/docs/pixee_python_fix-deprecated-pkg-resources.md diff --git a/integration_tests/test_fix_deprecated_pkg_resources.py b/integration_tests/test_fix_deprecated_pkg_resources.py new file mode 100644 index 00000000..15d371e6 --- /dev/null +++ b/integration_tests/test_fix_deprecated_pkg_resources.py @@ -0,0 +1,20 @@ +from codemodder.codemods.test import BaseIntegrationTest +from core_codemods.fix_deprecated_pkg_resources import FixDeprecatedPkgResources + + +class TestFixDeprecatedPkgResources(BaseIntegrationTest): + codemod = FixDeprecatedPkgResources + original_code = """ + import pkg_resources + + dist = pkg_resources.get_distribution("Django") + dist.location + version = dist.version + """ + replacement_lines = [ + (1, "from importlib.metadata import distribution\n"), + (3, 'dist = distribution("Django")\n'), + ] + expected_diff = '--- \n+++ \n@@ -1,4 +1,4 @@\n import tempfile\n \n-tempfile.mktemp()\n+tempfile.mkstemp()\n var = "hello"\n' + expected_line_change = "3" + change_description = FixDeprecatedPkgResources.change_description diff --git a/src/core_codemods/__init__.py b/src/core_codemods/__init__.py index d260df3e..0b9b44d4 100644 --- a/src/core_codemods/__init__.py +++ b/src/core_codemods/__init__.py @@ -19,6 +19,7 @@ from .fix_dataclass_defaults import FixDataclassDefaults from .fix_deprecated_abstractproperty import FixDeprecatedAbstractproperty from .fix_deprecated_logging_warn import FixDeprecatedLoggingWarn +from .fix_deprecated_pkg_resources import FixDeprecatedPkgResources from .fix_empty_sequence_comparison import FixEmptySequenceComparison from .fix_float_equality import FixFloatEquality from .fix_hasattr_call import TransformFixHasattrCall @@ -141,6 +142,7 @@ TransformFixHasattrCall, FixDataclassDefaults, FixMissingSelfOrCls, + FixDeprecatedPkgResources, ], ) diff --git a/src/core_codemods/docs/pixee_python_fix-deprecated-pkg-resources.md b/src/core_codemods/docs/pixee_python_fix-deprecated-pkg-resources.md new file mode 100644 index 00000000..e69de29b diff --git a/tests/codemods/test_fix_deprecated_pkg_resources.py b/tests/codemods/test_fix_deprecated_pkg_resources.py index 08097db9..57ff9f23 100644 --- a/tests/codemods/test_fix_deprecated_pkg_resources.py +++ b/tests/codemods/test_fix_deprecated_pkg_resources.py @@ -67,59 +67,48 @@ def test_import(self, tmpdir, input_code, expected_output): def test_from_import(self, tmpdir, input_code, expected_output): self.run_and_assert(tmpdir, input_code, expected_output) - # - # @pytest.mark.parametrize( - # "input_code,expected_output", - # [ - # ( - # """ - # from logging import warn as warn_func - # warn_func('something')""", - # """ - # from logging import warning - # warning('something')""", - # ), - # ( - # """ - # from logging import getLogger as make_logger - # logger = make_logger('anything') - # logger.warn('something')""", - # """ - # from logging import getLogger as make_logger - # logger = make_logger('anything') - # logger.warning('something')""", - # ), - # ], - # ) - # def test_import_alias(self, tmpdir, input_code, expected_output): - # self.run_and_assert(tmpdir, input_code, expected_output) - # - # @pytest.mark.parametrize( - # "code", - # [ - # """ - # import xyz - # xyz.warn('something') - # """, - # """ - # import my_logging - # log = my_logging.getLogger('anything') - # log.warn('something') - # """, - # ], - # ) - # def test_different_warn(self, tmpdir, code): - # self.run_and_assert(tmpdir, code, code) - # - # @pytest.mark.xfail(reason="Not currently supported") - # def test_log_as_arg(self, tmpdir): - # code = """ - # import logging - # log = logging.getLogger('foo') - # def some_function(logger): - # logger.{}("hi") - # some_function(log) - # """ - # original_code = code.format("warn") - # new_code = code.format("warning") - # self.run_and_assert(tmpdir, original_code, new_code) + @pytest.mark.parametrize( + "input_code,expected_output", + [ + ( + """ + from pkg_resources import get_distribution as get + dist = get("package_name") + """, + """ + from importlib.metadata import distribution + + dist = distribution("package_name") + """, + ), + ( + """ + from pkg_resources import get_distribution as get + version = get("package_name").version + """, + """ + from importlib.metadata import distribution + + version = distribution("package_name").version + """, + ), + ], + ) + def test_import_alias(self, tmpdir, input_code, expected_output): + self.run_and_assert(tmpdir, input_code, expected_output) + + def test_multiple(self, tmpdir): + original_code = """ + import pkg_resources + dist = pkg_resources.get_distribution("Django") + dist.location + version = dist.version + """ + expected_output = """ + from importlib.metadata import distribution + + dist = distribution("Django") + dist.location + version = dist.version + """ + self.run_and_assert(tmpdir, original_code, expected_output)