From 9db7bdf0fb9537b7c9a6ebcd7b381b8fa8c653d2 Mon Sep 17 00:00:00 2001 From: Kevin Wallace Date: Fri, 11 Nov 2022 09:51:11 -0800 Subject: [PATCH] remote follow: use HTML redirect to work around CSP issue In Chrome, I get the following when trying to use the remote follow form: Refused to send form data to 'https://example.com/remote_follow' because it violates the following Content Security Policy directive: "form-action 'self'". It seems some browsers (but notably not Firefox) apply the form-action policy to the redirect target in addition to the initial form submission endpoint. See: https://github.com/w3c/webappsec-csp/issues/8 In that thread, this workaround is suggested. --- app/main.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/app/main.py b/app/main.py index 37e1ab8..e25cc9d 100644 --- a/app/main.py +++ b/app/main.py @@ -1,4 +1,5 @@ import base64 +import html import os import sys import time @@ -38,6 +39,7 @@ from starlette.background import BackgroundTask from starlette.datastructures import Headers from starlette.datastructures import MutableHeaders from starlette.exceptions import HTTPException as StarletteHTTPException +from starlette.responses import HTMLResponse from starlette.responses import JSONResponse from starlette.types import Message from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware # type: ignore @@ -254,6 +256,25 @@ class ActivityPubResponse(JSONResponse): media_type = "application/activity+json" +class HTMLRedirectResponse(HTMLResponse): + """ + Similar to RedirectResponse, but uses a 200 response with HTML. + + Needed for remote redirects on form submission endpoints, + since our CSP policy disallows remote form submission. + https://github.com/w3c/webappsec-csp/issues/8#issuecomment-810108984 + """ + + def __init__( + self, + url: str, + ) -> None: + super().__init__( + content=f'Continue to remote resource', + headers={"Refresh": "0;url=" + url}, + ) + + @app.get(config.NavBarItems.NOTES_PATH) async def index( request: Request, @@ -961,7 +982,7 @@ async def post_remote_follow( request: Request, csrf_check: None = Depends(verify_csrf_token), profile: str = Form(), -) -> RedirectResponse: +) -> HTMLRedirectResponse: if not profile.startswith("@"): profile = f"@{profile}" @@ -970,9 +991,8 @@ async def post_remote_follow( # TODO(ts): error message to user raise HTTPException(status_code=404) - return RedirectResponse( + return HTMLRedirectResponse( remote_follow_template.format(uri=ID), - status_code=302, )