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.
This commit is contained in:
Kevin Wallace 2022-11-11 09:51:11 -08:00 committed by Thomas Sileo
parent 793a939046
commit 9db7bdf0fb
1 changed files with 23 additions and 3 deletions

View File

@ -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'<a href="{html.escape(url)}">Continue to remote resource</a>',
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,
)