Skip to content

Commit d6c64ed

Browse files
authored
semgrep url sandbox codemod (#754)
new semgrep url sandbox codemod
1 parent 1231122 commit d6c64ed

File tree

4 files changed

+135
-0
lines changed

4 files changed

+135
-0
lines changed

src/codemodder/scripts/generate_docs.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ class DocMetadata:
321321
}
322322

323323
SEMGREP_CODEMOD_NAMES = [
324+
"url-sandbox",
324325
"enable-jinja2-autoescape",
325326
"jwt-decode-verify",
326327
"use-defusedxml",

src/core_codemods/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
from .semgrep.semgrep_rsa_key_size import SemgrepRsaKeySize
6262
from .semgrep.semgrep_sql_parametrization import SemgrepSQLParameterization
6363
from .semgrep.semgrep_subprocess_shell_false import SemgrepSubprocessShellFalse
64+
from .semgrep.semgrep_url_sandbox import SemgrepUrlSandbox
6465
from .semgrep.semgrep_use_defused_xml import SemgrepUseDefusedXml
6566
from .sonar.sonar_break_or_continue_out_of_loop import SonarBreakOrContinueOutOfLoop
6667
from .sonar.sonar_disable_graphql_introspection import SonarDisableGraphQLIntrospection
@@ -204,6 +205,7 @@
204205
semgrep_registry = CodemodCollection(
205206
origin="semgrep",
206207
codemods=[
208+
SemgrepUrlSandbox,
207209
SemgrepEnableJinja2Autoescape,
208210
SemgrepJwtDecodeVerify,
209211
SemgrepUseDefusedXml,
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from core_codemods.semgrep.api import SemgrepCodemod, ToolRule, semgrep_url_from_id
2+
from core_codemods.url_sandbox import UrlSandbox
3+
4+
SemgrepUrlSandbox = SemgrepCodemod.from_core_codemod(
5+
name="url-sandbox",
6+
other=UrlSandbox,
7+
rules=[
8+
ToolRule(
9+
id=(
10+
rule_id := "python.django.security.injection.ssrf.ssrf-injection-requests.ssrf-injection-requests"
11+
),
12+
name="ssrf-injection-requests",
13+
url=semgrep_url_from_id(rule_id),
14+
),
15+
ToolRule(
16+
id=(
17+
rule_id := "python.flask.security.injection.ssrf-requests.ssrf-requests"
18+
),
19+
name="ssrf-requests",
20+
url=semgrep_url_from_id(rule_id),
21+
),
22+
],
23+
)
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import json
2+
3+
from codemodder.codemods.test import BaseSASTCodemodTest
4+
from core_codemods.semgrep.semgrep_url_sandbox import SemgrepUrlSandbox
5+
6+
7+
class TestSemgrepUrlSandbox(BaseSASTCodemodTest):
8+
codemod = SemgrepUrlSandbox
9+
tool = "semgrep"
10+
11+
def test_name(self):
12+
assert self.codemod.name == "url-sandbox"
13+
14+
def test_url_sandbox(self, tmpdir):
15+
original_code = """\
16+
import requests
17+
from flask import Flask, request
18+
19+
app = Flask(__name__)
20+
21+
22+
@app.route("/example")
23+
def example():
24+
url = request.args["url"]
25+
requests.get(url)
26+
"""
27+
28+
new_code = """\
29+
from flask import Flask, request
30+
from security import safe_requests
31+
32+
app = Flask(__name__)
33+
34+
35+
@app.route("/example")
36+
def example():
37+
url = request.args["url"]
38+
safe_requests.get(url)
39+
"""
40+
41+
results = {
42+
"runs": [
43+
{
44+
"results": [
45+
{
46+
"fingerprints": {"matchBasedId/v1": "370059975f"},
47+
"locations": [
48+
{
49+
"physicalLocation": {
50+
"artifactLocation": {
51+
"uri": "code.py",
52+
"uriBaseId": "%SRCROOT%",
53+
},
54+
"region": {
55+
"endColumn": 22,
56+
"endLine": 10,
57+
"snippet": {
58+
"text": ' url = request.args["url"]\n requests.get(url)'
59+
},
60+
"startColumn": 5,
61+
"startLine": 9,
62+
},
63+
}
64+
}
65+
],
66+
"message": {
67+
"text": "Data from request object is passed to a new server-side request. This could lead to a server-side request forgery (SSRF). To mitigate, ensure that schemes and hosts are validated against an allowlist, do not forward the response to the user, and ensure proper authentication and transport-layer security in the proxied request. See https://owasp.org/www-community/attacks/Server_Side_Request_Forgery to learn more about SSRF vulnerabilities."
68+
},
69+
"properties": {},
70+
"ruleId": "python.django.security.injection.ssrf.ssrf-injection-requests.ssrf-injection-requests",
71+
},
72+
{
73+
"fingerprints": {"matchBasedId/v1": "cd899"},
74+
"locations": [
75+
{
76+
"physicalLocation": {
77+
"artifactLocation": {
78+
"uri": "code.py",
79+
"uriBaseId": "%SRCROOT%",
80+
},
81+
"region": {
82+
"endColumn": 22,
83+
"endLine": 10,
84+
"snippet": {
85+
"text": " requests.get(url)"
86+
},
87+
"startColumn": 5,
88+
"startLine": 10,
89+
},
90+
}
91+
}
92+
],
93+
"message": {
94+
"text": "Data from request object is passed to a new server-side request. This could lead to a server-side request forgery (SSRF). To mitigate, ensure that schemes and hosts are validated against an allowlist, do not forward the response to the user, and ensure proper authentication and transport-layer security in the proxied request."
95+
},
96+
"properties": {},
97+
"ruleId": "python.flask.security.injection.ssrf-requests.ssrf-requests",
98+
},
99+
],
100+
}
101+
]
102+
}
103+
104+
self.run_and_assert(
105+
tmpdir,
106+
original_code,
107+
new_code,
108+
results=json.dumps(results),
109+
)

0 commit comments

Comments
 (0)