-
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.
Docs and tests for flask-send-file-path-parameterization
- Loading branch information
1 parent
10cb5cf
commit 6afe541
Showing
7 changed files
with
259 additions
and
4 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
53 changes: 53 additions & 0 deletions
53
integration_tests/test_flask_send_file_path_parameterization.py
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,53 @@ | ||
from core_codemods.flask_send_file_path_parameterization import ( | ||
FlaskSendFilePathParameterization, | ||
) | ||
from integration_tests.base_test import ( | ||
BaseIntegrationTest, | ||
original_and_expected_from_code_path, | ||
) | ||
|
||
|
||
class TestFlaskSendFilePathParameterization(BaseIntegrationTest): | ||
codemod = FlaskSendFilePathParameterization | ||
code_path = "tests/samples/flask_send_file_path_parameterization.py" | ||
original_code, expected_new_code = original_and_expected_from_code_path( | ||
code_path, | ||
[ | ||
(0, """from flask import Flask\n"""), | ||
(1, """import flask\n"""), | ||
(2, """from pathlib import Path\n"""), | ||
(3, """\n"""), | ||
(4, """app = Flask(__name__)\n"""), | ||
(5, """\n"""), | ||
(6, """@app.route("/uploads/<path:name>")\n"""), | ||
(7, """def download_file(name):\n"""), | ||
( | ||
8, | ||
""" return flask.send_from_directory((p := Path(f'path/to/{name}.txt')).parent, p.name)\n""", | ||
), | ||
], | ||
) | ||
|
||
# fmt: off | ||
expected_diff =( | ||
"""--- \n""" | ||
"""+++ \n""" | ||
"""@@ -1,7 +1,9 @@\n""" | ||
"""-from flask import Flask, send_file\n""" | ||
"""+from flask import Flask\n""" | ||
"""+import flask\n""" | ||
"""+from pathlib import Path\n""" | ||
""" \n""" | ||
""" app = Flask(__name__)\n""" | ||
""" \n""" | ||
""" @app.route("/uploads/<path:name>")\n""" | ||
""" def download_file(name):\n""" | ||
"""- return send_file(f'path/to/{name}.txt')\n""" | ||
"""+ return flask.send_from_directory((p := Path(f'path/to/{name}.txt')).parent, p.name)\n""" | ||
|
||
) | ||
# fmt: on | ||
|
||
expected_line_change = "7" | ||
change_description = FlaskSendFilePathParameterization.CHANGE_DESCRIPTION | ||
num_changed_files = 1 |
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
19 changes: 19 additions & 0 deletions
19
src/core_codemods/docs/pixee_python_flask-send-file-path-parameterization.md
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,19 @@ | ||
The `send_file` function from Flask is susceptible to a path injection attack if its input is not proper validated. | ||
In a path injection attack, the malicious agent can craft a path containing special paths like `./` or `../` to resolve an unintended file. This allows the agent to overwrite, delete or read arbitrary files. | ||
Flask offers a native solution with the `flask.send_from_directory` function that validates the given path. | ||
|
||
Our changes look something like this: | ||
|
||
```diff | ||
-from flask import Flask, send_file | ||
+from flask import Flask | ||
+import flask | ||
+from pathlib import Path | ||
|
||
app = Flask(__name__) | ||
|
||
@app.route("/uploads/<path:name>") | ||
def download_file(name): | ||
- return send_file(f'path/to/{name}.txt') | ||
+ return flask.send_from_directory((p := Path(f'path/to/{name}.txt')).parent, p.name) | ||
``` |
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
178 changes: 178 additions & 0 deletions
178
tests/codemods/test_flask_send_file_path_parameterization.py
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,178 @@ | ||
from core_codemods.flask_send_file_path_parameterization import ( | ||
FlaskSendFilePathParameterization, | ||
) | ||
from tests.codemods.base_codemod_test import BaseCodemodTest | ||
from textwrap import dedent | ||
|
||
|
||
class TestFlaskSendFilePathParameterization(BaseCodemodTest): | ||
codemod = FlaskSendFilePathParameterization | ||
|
||
def test_name(self): | ||
assert self.codemod.name() == "flask-send-file-path-parameterization" | ||
|
||
def test_direct_string(self, tmpdir): | ||
input_code = """\ | ||
from flask import Flask, send_file | ||
app = Flask(__name__) | ||
@app.route("/uploads/<path:name>") | ||
def download_file(name): | ||
return send_file(f'path/to/{name}.txt') | ||
""" | ||
expected = """\ | ||
from flask import Flask | ||
import flask | ||
from pathlib import Path | ||
app = Flask(__name__) | ||
@app.route("/uploads/<path:name>") | ||
def download_file(name): | ||
return flask.send_from_directory((p := Path(f'path/to/{name}.txt')).parent, p.name) | ||
""" | ||
self.run_and_assert(tmpdir, dedent(input_code), dedent(expected)) | ||
assert len(self.file_context.codemod_changes) == 1 | ||
|
||
def test_direct_string_convert_arguments(self, tmpdir): | ||
input_code = """\ | ||
from flask import Flask, send_file | ||
app = Flask(__name__) | ||
@app.route("/uploads/<path:name>") | ||
def download_file(name): | ||
return send_file(f'path/to/{name}.txt', None, False, download_name = True) | ||
""" | ||
expected = """\ | ||
from flask import Flask | ||
import flask | ||
from pathlib import Path | ||
app = Flask(__name__) | ||
@app.route("/uploads/<path:name>") | ||
def download_file(name): | ||
return flask.send_from_directory((p := Path(f'path/to/{name}.txt')).parent, p.name, mimetype = None, as_attachment = False, download_name = True) | ||
""" | ||
self.run_and_assert(tmpdir, dedent(input_code), dedent(expected)) | ||
assert len(self.file_context.codemod_changes) == 1 | ||
|
||
def test_direct_path(self, tmpdir): | ||
input_code = """\ | ||
from flask import Flask, send_file | ||
from pathlib import Path | ||
app = Flask(__name__) | ||
@app.route("/uploads/<path:name>") | ||
def download_file(name): | ||
return send_file(Path(f'path/to/{name}.txt')) | ||
""" | ||
expected = """\ | ||
from flask import Flask | ||
from pathlib import Path | ||
import flask | ||
app = Flask(__name__) | ||
@app.route("/uploads/<path:name>") | ||
def download_file(name): | ||
return flask.send_from_directory((p := Path(f'path/to/{name}.txt')).parent, p.name) | ||
""" | ||
self.run_and_assert(tmpdir, dedent(input_code), dedent(expected)) | ||
assert len(self.file_context.codemod_changes) == 1 | ||
|
||
def test_indirect_path(self, tmpdir): | ||
input_code = """\ | ||
from flask import Flask, send_file | ||
from pathlib import Path | ||
app = Flask(__name__) | ||
@app.route("/uploads/<path:name>") | ||
def download_file(name): | ||
path = Path(f'path/to/{name}.txt') | ||
return send_file(path) | ||
""" | ||
expected = """\ | ||
from flask import Flask | ||
from pathlib import Path | ||
import flask | ||
app = Flask(__name__) | ||
@app.route("/uploads/<path:name>") | ||
def download_file(name): | ||
path = Path(f'path/to/{name}.txt') | ||
return flask.send_from_directory(path.parent, path.name) | ||
""" | ||
self.run_and_assert(tmpdir, dedent(input_code), dedent(expected)) | ||
assert len(self.file_context.codemod_changes) == 1 | ||
|
||
def test_indirect_path_alias(self, tmpdir): | ||
input_code = """\ | ||
from flask import Flask, send_file as send | ||
from pathlib import Path | ||
app = Flask(__name__) | ||
@app.route("/uploads/<path:name>") | ||
def download_file(name): | ||
path = Path(f'path/to/{name}.txt') | ||
return send(path) | ||
""" | ||
expected = """\ | ||
from flask import Flask | ||
from pathlib import Path | ||
import flask | ||
app = Flask(__name__) | ||
@app.route("/uploads/<path:name>") | ||
def download_file(name): | ||
path = Path(f'path/to/{name}.txt') | ||
return flask.send_from_directory(path.parent, path.name) | ||
""" | ||
self.run_and_assert(tmpdir, dedent(input_code), dedent(expected)) | ||
assert len(self.file_context.codemod_changes) == 1 | ||
|
||
def test_indirect_string(self, tmpdir): | ||
input_code = """\ | ||
from flask import Flask, send_file | ||
app = Flask(__name__) | ||
@app.route("/uploads/<path:name>") | ||
def download_file(name): | ||
path = f'path/to/{name}.txt' | ||
return send_file(path) | ||
""" | ||
expected = """\ | ||
from flask import Flask | ||
import flask | ||
from pathlib import Path | ||
app = Flask(__name__) | ||
@app.route("/uploads/<path:name>") | ||
def download_file(name): | ||
path = f'path/to/{name}.txt' | ||
return flask.send_from_directory((p := Path(path)).parent, p.name) | ||
""" | ||
self.run_and_assert(tmpdir, dedent(input_code), dedent(expected)) | ||
assert len(self.file_context.codemod_changes) == 1 | ||
|
||
def test_unknown_type(self, tmpdir): | ||
input_code = """\ | ||
from flask import Flask, send_file | ||
app = Flask(__name__) | ||
@app.route("/uploads/<path:name>") | ||
def download_file(name): | ||
return send_file(name) | ||
""" | ||
self.run_and_assert(tmpdir, dedent(input_code), dedent(input_code)) | ||
assert len(self.file_context.codemod_changes) == 0 |
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 |
---|---|---|
@@ -1,9 +1,7 @@ | ||
from flask import Flask, send_file | ||
from pathlib import Path | ||
|
||
app = Flask(__name__) | ||
|
||
@app.route("/uploads/<path:name>") | ||
def download_file(name): | ||
return send_file(f'path/to/{name}.txt', None, True, max_age=10) | ||
|
||
return send_file(f'path/to/{name}.txt') |