Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[python] xss vulnerability for Django and Jinja2 #3923

Merged
merged 16 commits into from
Feb 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion manifests/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,8 @@ tests/:
TestXPathInjection_ExtendedLocation: missing_feature
TestXPathInjection_StackTrace: missing_feature
test_xss.py:
TestXSS: missing_feature
TestXSS:
'*': v3.0.0.dev
TestXSS_ExtendedLocation: missing_feature
TestXSS_StackTrace: missing_feature
source/:
Expand Down
5 changes: 4 additions & 1 deletion tests/appsec/iast/sink/test_xss.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ class TestXSS(BaseSinkTestWithoutTelemetry):
insecure_endpoint = "/iast/xss/test_insecure"
secure_endpoint = "/iast/xss/test_secure"
data = {"param": "param"}
location_map = {"java": "com.datadoghq.system_tests.iast.utils.XSSExamples"}
location_map = {
"java": "com.datadoghq.system_tests.iast.utils.XSSExamples",
"python": {"django-poc": "app/urls.py"},
}


@rfc(
Expand Down
18 changes: 18 additions & 0 deletions utils/build/docker/python/django/app/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
from django.http import HttpResponse, HttpResponseBadRequest, JsonResponse
from django.urls import path
from django.views.decorators.csrf import csrf_exempt
from django.shortcuts import render
from django.utils.safestring import mark_safe
from moto import mock_aws
import urllib3
from iast import (
Expand Down Expand Up @@ -530,6 +532,20 @@ def view_iast_ssrf_secure(request):
return HttpResponse("OK")


@csrf_exempt
def view_iast_xss_insecure(request):
param = request.POST.get("param", "")
# Validate the URL and enforce whitelist
return render(request, "index.html", {"param": mark_safe(param)})


@csrf_exempt
def view_iast_xss_secure(request):
param = request.POST.get("param", "")
# Validate the URL and enforce whitelist
return render(request, "index.html", {"param": param})


@csrf_exempt
def view_iast_stacktraceleak_insecure(request):
return HttpResponse("""
Expand Down Expand Up @@ -980,6 +996,8 @@ def s3_multipart_upload(request):
path("iast/path_traversal/test_secure", view_iast_path_traversal_secure),
path("iast/ssrf/test_insecure", view_iast_ssrf_insecure),
path("iast/ssrf/test_secure", view_iast_ssrf_secure),
path("iast/xss/test_insecure", view_iast_xss_insecure),
path("iast/xss/test_secure", view_iast_xss_secure),
path("iast/stack_trace_leak/test_insecure", view_iast_stacktraceleak_insecure),
path("iast/stack_trace_leak/test_secure", view_iast_stacktraceleak_secure),
path("iast/source/body/test", view_iast_source_body),
Expand Down
4 changes: 3 additions & 1 deletion utils/build/docker/python/django/django_app/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [],
"DIRS": [
os.path.join(BASE_DIR, "django_app", "templates"),
],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<html>
<body>
<p>Input: {{ user_input }}</p>
</body>
</html>
2 changes: 1 addition & 1 deletion utils/build/docker/python/fastapi.base.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ RUN apt-get update && apt-get install -y curl git gcc g++ make cmake
RUN python --version && curl --version

# install python deps
RUN pip install fastapi uvicorn cryptography==42.0.8 pycryptodome python-multipart
RUN pip install fastapi uvicorn cryptography==42.0.8 pycryptodome python-multipart jinja2

# Install Rust toolchain
RUN curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain stable -y
Expand Down
16 changes: 16 additions & 0 deletions utils/build/docker/python/fastapi/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@
from fastapi import Header
from fastapi import Request
from fastapi.responses import JSONResponse
from fastapi.responses import HTMLResponse
from fastapi.responses import PlainTextResponse
from iast import weak_cipher
from iast import weak_cipher_secure_algorithm
from iast import weak_hash
from iast import weak_hash_duplicates
from iast import weak_hash_multiple
from iast import weak_hash_secure_algorithm
from jinja2 import Template
import psycopg2
from pydantic import BaseModel
import requests
Expand Down Expand Up @@ -913,6 +915,20 @@ def safe_eval(expr):
return "OK"


@app.post("/iast/xss/test_insecure", response_class=PlainTextResponse)
async def view_iast_xss_insecure(param: typing.Annotated[str, Form()]):
template = Template("<p>{{ param|safe }}</p>")
html = template.render(param=param)
return HTMLResponse(html)


@app.post("/iast/xss/test_secure", response_class=PlainTextResponse)
async def view_iast_xss_secure(param: typing.Annotated[str, Form()]):
template = Template("<p>{{ param }}</p>")
html = template.render(param=param)
return HTMLResponse(html)


@app.get("/createextraservice", response_class=PlainTextResponse)
def create_extra_service(serviceName: str = ""):
if serviceName:
Expand Down
16 changes: 16 additions & 0 deletions utils/build/docker/python/flask/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@
from flask import Flask
from flask import Response
from flask import jsonify
from flask import render_template_string
from flask import request
from flask import request as flask_request
from flask_login import LoginManager
from flask_login import login_user

from iast import weak_cipher
from iast import weak_cipher_secure_algorithm
from iast import weak_hash
Expand Down Expand Up @@ -1235,6 +1237,20 @@ def safe_eval(expr):
return resp


@app.route("/iast/xss/test_insecure", methods=["POST"])
def view_iast_xss_insecure():
param = flask_request.form["param"]

return render_template_string("<p>XSS: {{ param|safe }}</p>", param=param)


@app.route("/iast/xss/test_secure", methods=["POST"])
def view_iast_xss_secure():
param = flask_request.form["param"]

return render_template_string("<p>XSS: {{ param }}</p>", param=param)


_TRACK_METADATA = {
"metadata0": "value0",
"metadata1": "value1",
Expand Down
Loading