Skip to content

Better e2e testing #333

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

Open
wants to merge 32 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7c2ca78
port code from java e2e PR
bitterpanda63 Mar 7, 2025
b774a50
Add django mysql test cases
bitterpanda63 Mar 7, 2025
f01e2f8
Add django mysql gunicorn and django postgres gunicorn
bitterpanda63 Mar 7, 2025
5a84bd5
Add quart_postgres_attack + starlette/quart e2e
bitterpanda63 Mar 7, 2025
5e7bd03
allow for 201 status codes
bitterpanda63 Mar 7, 2025
4890fa8
Fix end2end workflow
bitterpanda63 Mar 7, 2025
700d18f
Remove copied over space before unsafe_reuqest=
bitterpanda63 Mar 7, 2025
e6e535e
Also port the xml/lxml end2end tests
bitterpanda63 Mar 7, 2025
dba57be
Port flask_mysql_uwsgi end2end tests
bitterpanda63 Mar 7, 2025
35d9d1d
Remopve tests that are already ported
bitterpanda63 Mar 7, 2025
cb4275a
Add trailing slashes for django_mysql_gunicorn
bitterpanda63 Mar 7, 2025
8f24957
remove \\ from flask mysql (failed copy)
bitterpanda63 Mar 7, 2025
ce469c6
flask_mongo port test cases to new system
bitterpanda63 Mar 7, 2025
d9bc505
Convert flask_postgres to the new testing framework
bitterpanda63 Mar 7, 2025
9b73bb6
Update flask_postgres.py
bitterpanda63 Mar 8, 2025
0d53485
Create flask_mysql.py file that also tests IP Blocking and rate-limiting
bitterpanda63 Mar 10, 2025
8c3d095
Allow setting of user through user header
bitterpanda63 Mar 10, 2025
5cba5ec
Change the "dangerous" payload for flask mysql
bitterpanda63 Mar 10, 2025
1143a59
Only set user id when the user header is present
bitterpanda63 Mar 10, 2025
2a157ab
Change block message to the one used by python
bitterpanda63 Mar 10, 2025
681d9d7
More testing in quart e2e tests
bitterpanda63 Mar 10, 2025
3d6f6a4
Starlette expand testing e2e
bitterpanda63 Mar 10, 2025
c6dc209
remove standard user present in quart attack
bitterpanda63 Mar 10, 2025
b14a5c9
end2end delay make it longer
bitterpanda63 Mar 10, 2025
9992f81
Fix setting of user
bitterpanda63 Mar 10, 2025
1baeccc
Check headers for lowercase user header
bitterpanda63 Mar 10, 2025
de7b3f4
sleep 30 seconds
bitterpanda63 Mar 10, 2025
dc81d87
print the headers in quart app for SetUser
bitterpanda63 Mar 10, 2025
d5a71d4
set users dynamically in both quart and starlette apps
bitterpanda63 Mar 10, 2025
380b826
remove test_blocking from flask mysql
bitterpanda63 Mar 10, 2025
21c6aee
Remove test_blocking from quart and starlette apps as well
bitterpanda63 Mar 10, 2025
f07141e
Merge remote-tracking branch 'origin/main' into better-e2e-testing
bitterpanda63 Apr 10, 2025
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
27 changes: 13 additions & 14 deletions .github/workflows/end2end.yml
Original file line number Diff line number Diff line change
@@ -24,17 +24,16 @@ jobs:
strategy:
matrix:
app:
- { name: django-mysql, testfile: end2end/django_mysql_test.py }
- { name: django-mysql-gunicorn, testfile: end2end/django_mysql_gunicorn_test.py }
- { name: django-postgres-gunicorn, testfile: end2end/django_postgres_gunicorn_test.py }
- { name: flask-mongo, testfile: end2end/flask_mongo_test.py }
- { name: flask-mysql, testfile: end2end/flask_mysql_test.py }
- { name: flask-mysql-uwsgi, testfile: end2end/flask_mysql_uwsgi_test.py }
- { name: flask-postgres, testfile: end2end/flask_postgres_test.py }
- { name: flask-postgres-xml, testfile: end2end/flask_postgres_xml_test.py }
- { name: flask-postgres-xml, testfile: end2end/flask_postgres_xml_lxml_test.py }
- { name: quart-postgres-uvicorn, testfile: end2end/quart_postgres_uvicorn_test.py }
- { name: starlette-postgres-uvicorn, testfile: end2end/starlette_postgres_uvicorn_test.py }
- { name: django-mysql, testfile: end2end/django_mysql.py }
- { name: django-mysql-gunicorn, testfile: end2end/django_mysql_gunicorn.py }
- { name: django-postgres-gunicorn, testfile: end2end/django_postgres_gunicorn.py }
- { name: flask-mongo, testfile: end2end/flask_mongo.py }
- { name: flask-mysql, testfile: end2end/flask_mysql.py }
- { name: flask-mysql-uwsgi, testfile: end2end/flask_mysql_uwsgi.py }
- { name: flask-postgres, testfile: end2end/flask_postgres.py }
- { name: flask-postgres-xml, testfile: end2end/flask_postgres_xml.py }
- { name: quart-postgres-uvicorn, testfile: end2end/quart_postgres_uvicorn.py }
- { name: starlette-postgres-uvicorn, testfile: end2end/starlette_postgres_uvicorn.py }
python-version: ["3.10", "3.11", "3.12"]
steps:
- name: Install packages
@@ -64,7 +63,7 @@ jobs:
- name: Start application
working-directory: ./sample-apps/${{ matrix.app.name }}
run: |
nohup make run > output.log & tail -f output.log & sleep 20
nohup make runZenDisabled & sleep 20
nohup make run > output.log & tail -f output.log & sleep 30
nohup make runZenDisabled & sleep 30
- name: Run end2end tests for application
run: tail -f ./sample-apps/${{ matrix.app.name }}/output.log & poetry run pytest ./${{ matrix.app.testfile }}
run: tail -f ./sample-apps/${{ matrix.app.name }}/output.log & python ./${{ matrix.app.testfile }}
4 changes: 4 additions & 0 deletions end2end/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import json

with open('end2end/attack_events.json', 'r') as file:
events = json.load(file)
124 changes: 124 additions & 0 deletions end2end/attack_events.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
{
"django_mysql_attack_sql": {
"blocked": true,
"kind": "sql_injection",
"metadata": {
"sql": "INSERT INTO sample_app_dogs (dog_name, dog_boss) VALUES (\"Dangerous bobby\", 1); -- \", \"N/A\")"
},
"operation": "MySQLdb.Cursor.execute",
"pathToPayload": ".dog_name",
"payload": "\"Dangerous bobby\\\", 1); -- \"",
"source": "body"
},
"django_mysql_attack_shell": {
"blocked": true,
"kind": "shell_injection",
"metadata": {
"command": "ls -la"
},
"operation": "subprocess.Popen",
"pathToPayload": ".[0]",
"payload": "\"ls -la\"",
"source": "route_params"
},
"django_postgres_attack": {
"blocked": true,
"kind": "sql_injection",
"metadata": {
"sql": "INSERT INTO sample_app_Dogs (dog_name, is_admin) VALUES ('Dangerous bobby', TRUE); -- ', FALSE)"
},
"operation": "psycopg2.Connection.Cursor.execute",
"pathToPayload": ".dog_name",
"payload": "\"Dangerous bobby', TRUE); -- \"",
"source": "body"
},
"quart_postgres_attack": {
"blocked": true,
"kind": "sql_injection",
"metadata": {
"sql": "INSERT INTO dogs (dog_name, isAdmin) VALUES ('Dangerous Bobby', TRUE); -- ', FALSE)"
},
"operation": "asyncpg.connection.Connection.execute",
"pathToPayload": ".dog_name",
"payload": "\"Dangerous Bobby', TRUE); -- \"",
"source": "body"
},
"flask_xml_attack": {
"blocked": true,
"kind": "sql_injection",
"metadata": {
"sql": "INSERT INTO dogs (dog_name, isAdmin) VALUES ('Malicious dog', TRUE); -- ', FALSE)"
},
"operation": "psycopg2.Connection.Cursor.execute",
"pathToPayload": ".dog_name.[0]",
"payload": "\"Malicious dog', TRUE); -- \"",
"source": "xml"
},
"flask_mysql_attack": {
"blocked": true,
"kind": "sql_injection",
"metadata": {
"sql": "INSERT INTO dogs (dog_name, isAdmin) VALUES (\"Dangerous bobby\", 1); -- \", 0)"
},
"operation": "pymysql.Cursor.execute",
"pathToPayload": ".dog_name",
"payload": "\"Dangerous bobby\\\", 1); -- \"",
"source": "body"
},
"flask_mongo_attack": {
"blocked": true,
"kind": "nosql_injection",
"metadata": {
"filter": "{\"dog_name\": \"bobby_tables\", \"pswd\": {\"$ne\": \"\"}}"
},
"operation": "pymongo.collection.Collection.find",
"pathToPayload": ".pswd",
"payload": "{\"$ne\": \"\"}",
"source": "body"
},
"flask_postgres_attack_body": {
"blocked": true,
"kind": "sql_injection",
"metadata": {
"sql": "INSERT INTO dogs (dog_name, isAdmin) VALUES ('Dangerous Bobby', TRUE); -- ', FALSE)"
},
"operation": "psycopg2.Connection.Cursor.execute",
"pathToPayload": ".dog_name",
"payload": "\"Dangerous Bobby', TRUE); -- \"",
"source": "body"
},
"flask_postgres_attack_cookie": {
"blocked": true,
"kind": "sql_injection",
"metadata": {
"sql": "INSERT INTO dogs (dog_name, isAdmin) VALUES ('Bobby', TRUE) --', FALSE)"
},
"operation": "psycopg2.Connection.Cursor.execute",
"pathToPayload": ".dog_name",
"payload": "\"Bobby', TRUE) --\"",
"source": "cookies"
},
"flask_mysql_attack_sql": {
"blocked": true,
"kind": "sql_injection",
"metadata": {
"sql": "INSERT INTO dogs (dog_name, isAdmin) VALUES (\"Dangerous bobby\", 1); -- \", 0)"
},
"operation": "pymysql.Cursor.execute",
"pathToPayload": ".dog_name",
"payload": "\"Dangerous bobby\\\", 1); -- \"",
"source": "body"
},
"flask_mysql_attack_shell": {
"blocked": true,
"kind": "shell_injection",
"metadata": {
"command": "ls -la"
},
"operation": "subprocess.Popen",
"pathToPayload": ".command",
"payload": "\"ls -la\"",
"source": "route_params",
"user_id": "456"
}
}
17 changes: 17 additions & 0 deletions end2end/django_mysql.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from __init__ import events
from utils import App, Request

django_mysql_app = App(8080)

django_mysql_app.add_payload(
"sql", test_event=events["django_mysql_attack_sql"],
safe_request=Request(route="/app/create", body={'dog_name': 'Bobby'}, data_type="form"),
unsafe_request=Request(route="/app/create", body={'dog_name': 'Dangerous bobby", 1); -- '}, data_type="form")
)
django_mysql_app.add_payload(
"shell", test_event=events["django_mysql_attack_shell"],
safe_request=Request(route="/app/shell/bobby", method="GET"),
unsafe_request=Request(route="/app/shell/ls -la", method="GET")
)

django_mysql_app.test_all_payloads()
12 changes: 12 additions & 0 deletions end2end/django_mysql_gunicorn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from __init__ import events
from utils import App, Request

django_mysql_gunicorn_app = App(8082)

django_mysql_gunicorn_app.add_payload(
"sql", test_event=events["django_mysql_attack_sql"],
safe_request=Request(route="/app/create/", body={'dog_name': 'Bobby'}, data_type="form"),
unsafe_request=Request(route="/app/create/", body={'dog_name': 'Dangerous bobby", 1); -- '}, data_type="form")
)

django_mysql_gunicorn_app.test_all_payloads()
54 changes: 0 additions & 54 deletions end2end/django_mysql_gunicorn_test.py

This file was deleted.

89 changes: 0 additions & 89 deletions end2end/django_mysql_test.py

This file was deleted.

12 changes: 12 additions & 0 deletions end2end/django_postgres_gunicorn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from __init__ import events
from utils import App, Request

django_postgres_gunicorn_app = App(8100)

django_postgres_gunicorn_app.add_payload(
"sql", test_event=events["django_postgres_attack"],
safe_request=Request(route="/app/create", body={'dog_name': 'Bobby'}, data_type="form"),
unsafe_request=Request(route="/app/create", body={'dog_name': "Dangerous bobby', TRUE); -- "}, data_type="form")
)

django_postgres_gunicorn_app.test_all_payloads()
Loading