Skip to content

Commit 2748d5c

Browse files
committed
Remediate security issues
- Upgrade to jinja2~=3.1.0 - Remove hardcoded secrets using os.getenv - Add timeout handler to requests - Security hardening of Dockerfile - Fix SSTI vulnerability on jinja2 template rendering - Turn on coraza waf
1 parent 7b8f313 commit 2748d5c

File tree

7 files changed

+14
-10
lines changed

7 files changed

+14
-10
lines changed

.env_temp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
APP_IMAGE=python-insecure-app:latest
1+
APP_IMAGE=python-insecure-app:wolfi-distroless
22
COMPOSE_FILE=docker-compose.yaml
33
DEBUG=True
44

Dockerfile.alpine

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
FROM python:3.13-alpine@sha256:e5fa639e49b85986c4481e28faa2564b45aa8021413f31026c3856e5911618b1 AS alpine
44

55
LABEL project="Python Insecure App" service="FastAPI" stage="alpine"
6-
# RUN python3 -m pip install --upgrade pip~=25.3
6+
RUN python3 -m pip install --upgrade pip~=25.3
77
ENV NONROOT=nonroot \
88
LANG=C.UTF-8 \
99
LC_ALL=C.UTF-8 \

app/config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@
1010

1111
PUBLIC_IP_SERVICE_URL = os.getenv("PUBLIC_IP_SERVICE_URL")
1212

13-
SUPER_SECRET_NAME = "John Ripper" # FIXME: os.getenv("SUPER_SECRET_NAME")
13+
SUPER_SECRET_NAME = os.getenv("SUPER_SECRET_NAME")
1414

15-
SUPER_SECRET_TOKEN = "5u93R53Cr3tT0k3n" # FIXME: os.getenv("SUPER_SECRET_TOKEN")
15+
SUPER_SECRET_TOKEN = os.getenv("SUPER_SECRET_TOKEN")

app/main.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,13 @@ async def try_hack_me(name: str = config.SUPER_SECRET_NAME):
2828
"""
2929
try:
3030
# Get the public IP address from an external service
31-
public_ip_response = requests.get(config.PUBLIC_IP_SERVICE_URL)
31+
public_ip_response = requests.get(config.PUBLIC_IP_SERVICE_URL, timeout=5)
3232
public_ip_response.raise_for_status()
3333
except (requests.HTTPError, requests.exceptions.InvalidSchema):
3434
public_ip = "Unknown"
3535
else:
3636
public_ip = public_ip_response.text
3737
name = name or config.SUPER_SECRET_NAME
38-
content = f"<h1>Hello, {name}!</h1><h2>Public IP: <code>{public_ip}</code></h2>"
38+
content = "<h1>Hello, {{name}}!</h1><h2>Public IP: <code>{{public_ip}}</code></h2>"
3939
# FIXME: https://fastapi.tiangolo.com/advanced/custom-response/#return-a-response
40-
return Template(content).render()
40+
return Template(content).render(name=name, public_ip=public_ip)

caddy/Caddyfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
(waf_rules) {
1111
coraza_waf {
1212
directives `
13-
SecRuleEngine Off
13+
SecRuleEngine On
1414
SecRequestBodyAccess On
1515
SecRequestBodyLimitAction Reject
1616
SecDebugLogLevel 9

requirements/common.in

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
-r base.in
2-
fastapi[standard]~=0.115.0
3-
jinja2~=3.0.0
2+
fastapi[standard]~=0.120.0
3+
jinja2~=3.1.0
44
requests~=2.32.0

tests/test_main.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,7 @@ def test_root(requests_mock):
3535
response.content.decode()
3636
== "<h1>Hello, Bob!</h1><h2>Public IP: <code>123.45.67.89</code></h2>"
3737
)
38+
response = client.get("/?name={{7*6}}")
39+
assert response.status_code == 200
40+
assert "42" not in response.content.decode()
41+
assert "{{7*6}}" in response.content.decode()

0 commit comments

Comments
 (0)