import pytest from urllib.parse import quote from sanic.response import text, redirect @pytest.fixture def redirect_app(app): @app.route('/redirect_init') async def redirect_init(request): return redirect("/redirect_target") @app.route('/redirect_init_with_301') async def redirect_init_with_301(request): return redirect("/redirect_target", status=301) @app.route('/redirect_target') async def redirect_target(request): return text('OK') @app.route('/1') def handler1(request): return redirect('/2') @app.route('/2') def handler2(request): return redirect('/3') @app.route('/3') def handler3(request): return text('OK') @app.route('/redirect_with_header_injection') async def redirect_with_header_injection(request): return redirect("/unsafe\ntest-header: test-value\n\ntest-body") return app def test_redirect_default_302(redirect_app): """ We expect a 302 default status code and the headers to be set. """ request, response = redirect_app.test_client.get( '/redirect_init', allow_redirects=False) assert response.status == 302 assert response.headers["Location"] == "/redirect_target" assert response.headers["Content-Type"] == 'text/html; charset=utf-8' def test_redirect_headers_none(redirect_app): request, response = redirect_app.test_client.get( uri="/redirect_init", headers=None, allow_redirects=False) assert response.status == 302 assert response.headers["Location"] == "/redirect_target" def test_redirect_with_301(redirect_app): """ Test redirection with a different status code. """ request, response = redirect_app.test_client.get( "/redirect_init_with_301", allow_redirects=False) assert response.status == 301 assert response.headers["Location"] == "/redirect_target" def test_get_then_redirect_follow_redirect(redirect_app): """ With `allow_redirects` we expect a 200. """ request, response = redirect_app.test_client.get( "/redirect_init", allow_redirects=True) assert response.status == 200 assert response.text == 'OK' def test_chained_redirect(redirect_app): """Test test_client is working for redirection""" request, response = redirect_app.test_client.get('/1') assert request.url.endswith('/1') assert response.status == 200 assert response.text == 'OK' try: assert response.url.endswith('/3') except AttributeError: assert response.url.path.endswith('/3') def test_redirect_with_header_injection(redirect_app): """ Test redirection to a URL with header and body injections. """ request, response = redirect_app.test_client.get( "/redirect_with_header_injection", allow_redirects=False) assert response.status == 302 assert "test-header" not in response.headers assert not response.text.startswith('test-body') @pytest.mark.parametrize("test_str", ["sanic-test", "sanictest", "sanic test"]) async def test_redirect_with_params(app, test_client, test_str): @app.route("/api/v1/test//") async def init_handler(request, test): assert test == test_str return redirect("/api/v2/test/{}/".format(quote(test))) @app.route("/api/v2/test//") async def target_handler(request, test): assert test == test_str return text("OK") test_cli = await test_client(app) response = await test_cli.get("/api/v1/test/{}/".format(quote(test_str))) assert response.status == 200 txt = await response.text() assert txt == "OK"