-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8504790
commit f14491c
Showing
7 changed files
with
183 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import libcst as cst | ||
from libcst.codemod import CodemodContext | ||
from libcst import matchers | ||
from codemodder.codemods.base_codemod import ( | ||
SemgrepCodemod, | ||
CodemodMetadata, | ||
ReviewGuidance, | ||
) | ||
from codemodder.codemods.base_visitor import BaseTransformer | ||
from codemodder.codemods.change import Change | ||
from codemodder.file_context import FileContext | ||
|
||
|
||
class HardenRuamel(SemgrepCodemod, BaseTransformer): | ||
METADATA = CodemodMetadata( | ||
DESCRIPTION=("Ensures all unsafe calls to ruamel.yaml.YAML use `typ='safe'`."), | ||
NAME="harden-ruamel", | ||
REVIEW_GUIDANCE=ReviewGuidance.MERGE_WITHOUT_REVIEW, | ||
) | ||
CHANGE_DESCRIPTION = METADATA.DESCRIPTION | ||
YAML_FILES = [ | ||
"harden-ruamel.yaml", | ||
] | ||
|
||
def __init__(self, codemod_context: CodemodContext, file_context: FileContext): | ||
SemgrepCodemod.__init__(self, file_context) | ||
BaseTransformer.__init__( | ||
self, | ||
codemod_context, | ||
self._results, | ||
file_context.line_exclude, | ||
file_context.line_include, | ||
) | ||
|
||
def leave_Call(self, original_node: cst.Call, updated_node: cst.Call): | ||
pos_to_match = self.node_position(original_node) | ||
if self.filter_by_result( | ||
pos_to_match | ||
) and self.filter_by_path_includes_or_excludes(pos_to_match): | ||
line_number = pos_to_match.start.line | ||
self.CHANGES_IN_FILE.append( | ||
Change(str(line_number), self.CHANGE_DESCRIPTION).to_json() | ||
) | ||
new_args = update_arg_target(original_node.args, target_arg="typ") | ||
return updated_node.with_changes(args=new_args) | ||
return updated_node | ||
|
||
|
||
def update_arg_target(original_args, target_arg): | ||
new_args = [] | ||
for arg in original_args: | ||
if matchers.matches(arg.keyword, matchers.Name(target_arg)): | ||
new = cst.Arg( | ||
keyword=cst.parse_expression("typ"), | ||
value=cst.parse_expression('"safe"'), | ||
equal=arg.equal, | ||
) | ||
else: | ||
new = arg | ||
new_args.append(new) | ||
return new_args |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
rules: | ||
- id: harden-ruamel | ||
message: Unsafe call to ruamel.yaml.YAML | ||
severity: WARNING | ||
languages: | ||
- python | ||
pattern-either: | ||
- patterns: | ||
- pattern: ruamel.yaml.YAML(typ="unsafe", ...) | ||
- pattern-inside: | | ||
import ruamel | ||
... | ||
- patterns: | ||
- pattern: ruamel.yaml.YAML(typ="base", ...) | ||
- pattern-inside: | | ||
import ruamel | ||
... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
from codemodder.codemods.harden_ruamel import HardenRuamel | ||
from integration_tests.base_test import ( | ||
BaseIntegrationTest, | ||
original_and_expected_from_code_path, | ||
) | ||
|
||
|
||
class TestHardenRuamel(BaseIntegrationTest): | ||
codemod = HardenRuamel | ||
code_path = "tests/samples/unsafe_ruamel.py" | ||
original_code, expected_new_code = original_and_expected_from_code_path( | ||
code_path, | ||
[ | ||
(2, 'serializer = YAML(typ="safe")\n'), | ||
(3, 'serializer = YAML(typ="safe")\n'), | ||
], | ||
) | ||
expected_diff = '--- \n+++ \n@@ -1,4 +1,4 @@\n from ruamel.yaml import YAML\n \n-serializer = YAML(typ="unsafe")\n-serializer = YAML(typ="base")\n+serializer = YAML(typ="safe")\n+serializer = YAML(typ="safe")\n' | ||
expected_line_change = "3" | ||
num_changes = 2 | ||
change_description = HardenRuamel.CHANGE_DESCRIPTION |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import pytest | ||
from codemodder.codemods.harden_ruamel import HardenRuamel | ||
from tests.codemods.base_codemod_test import BaseSemgrepCodemodTest | ||
|
||
|
||
class TestHardenRuamel(BaseSemgrepCodemodTest): | ||
codemod = HardenRuamel | ||
|
||
def test_rule_ids(self): | ||
assert self.codemod.RULE_IDS == ["harden-ruamel"] | ||
|
||
@pytest.mark.parametrize("loader", ["YAML()", "YAML(typ='rt')", "YAML(typ='safe')"]) | ||
def test_safe(self, tmpdir, loader): | ||
input_code = f"""from ruamel.yaml import YAML | ||
serializer = {loader} | ||
""" | ||
self.run_and_assert(tmpdir, input_code, input_code) | ||
|
||
@pytest.mark.parametrize("loader", ["YAML(typ='base')", "YAML(typ='unsafe')"]) | ||
def test_unsafe(self, tmpdir, loader): | ||
input_code = f"""from ruamel.yaml import YAML | ||
serializer = {loader} | ||
""" | ||
|
||
expected = """from ruamel.yaml import YAML | ||
serializer = YAML(typ="safe") | ||
""" | ||
self.run_and_assert(tmpdir, input_code, expected) | ||
|
||
@pytest.mark.parametrize( | ||
"loader", ["YAML(typ='base', pure=True)", "YAML(typ='unsafe', pure=True)"] | ||
) | ||
def test_unsafe_more_args(self, tmpdir, loader): | ||
input_code = f"""from ruamel.yaml import YAML | ||
serializer = {loader} | ||
""" | ||
|
||
expected = """from ruamel.yaml import YAML | ||
serializer = YAML(typ="safe", pure=True) | ||
""" | ||
self.run_and_assert(tmpdir, input_code, expected) | ||
|
||
@pytest.mark.parametrize("loader", ["YAML(typ='base')", "YAML(typ='unsafe')"]) | ||
def test_unsafe_import(self, tmpdir, loader): | ||
input_code = f"""import ruamel | ||
serializer = ruamel.yaml.{loader} | ||
""" | ||
|
||
expected = """import ruamel | ||
serializer = ruamel.yaml.YAML(typ="safe") | ||
""" | ||
self.run_and_assert(tmpdir, input_code, expected) | ||
|
||
@pytest.mark.skip() | ||
@pytest.mark.parametrize("loader", ["YAML(typ='base')", "YAML(typ='unsafe')"]) | ||
def test_import_alias(self, tmpdir, loader): | ||
input_code = f"""from ruamel import yaml as yam | ||
serializer = yam.{loader} | ||
""" | ||
|
||
expected = """import ruamel | ||
serializer = yam.YAML(typ="safe") | ||
""" | ||
|
||
self.run_and_assert(tmpdir, input_code, expected) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from ruamel.yaml import YAML | ||
|
||
serializer = YAML(typ="unsafe") | ||
serializer = YAML(typ="base") |