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'<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,
)
--
2.35.1
On Sun, Nov 13, 2022, at 8:39 AM, Thomas Sileo wrote:
> (and I think Firefox is doing "the right thing" :p).
Agreed - Chrome's behavior doesn't protect us from anything, and just makes things more complicated in cases like this. Hopefully they update the spec to match Firefox.
Follow-up commit looks good! Good catch on adding it to remote interactions, too.