XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet
公开
关注 0 Fork 0 Star 1
返回提交历史

XFEstudio/gpt4free

Serve /pa/files HTML with CSP sandbox+request origin for true localStorage isolation

Agent-Logs-Url: https://github.com/xtekky/gpt4free/sessions/d964c73c-85bc-432d-b14b-20f8ea2de94e Co-authored-by: hlohaus <983577+hlohaus@users.noreply.github.com>

dafc1d93
copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
提交于

代码差异

2 个文件 +80 -31
Modified etc/unittest/mcp.py +22 -0
@@ -761,3 +761,25 @@ class TestWorkspaceFileServing(unittest.TestCase):
761 761 self.assertIn("Permissions-Policy", src)
762 762 self.assertIn("connect-src 'none'", src, "CSP should block outbound connections")
763 763 self.assertIn("object-src 'none'", src, "CSP should block object/embed elements")
764
765 def test_html_served_with_csp_sandbox(self):
766 """HTML files must be served with CSP sandbox to isolate their origin."""
767 import g4f.api as api_mod
768 import inspect
769 src = inspect.getsource(api_mod.Api.register_routes)
770 # sandbox without allow-same-origin forces a null origin on the page,
771 # which prevents localStorage / sessionStorage / cookie access.
772 self.assertIn("sandbox allow-scripts", src,
773 "HTML files must be served with CSP sandbox directive")
774
775 def test_request_origin_used_in_csp(self):
776 """CSP source directives must use the request origin, not 'self'."""
777 import g4f.api as api_mod
778 import inspect
779 src = inspect.getsource(api_mod.Api.register_routes)
780 # The route must derive the origin from the incoming Request object.
781 self.assertIn("request_origin", src,
782 "CSP must use the actual request origin, not static 'self'")
783 self.assertIn("request.url.scheme", src,
784 "Route must extract scheme from the Request for the origin")
785
Modified g4f/api/__init__.py +58 -31
@@ -888,7 +888,7 @@ class Api:
888 888 HTTP_403_FORBIDDEN: {"model": ErrorResponseModel},
889 889 HTTP_404_NOT_FOUND: {"model": ErrorResponseModel},
890 890 })
891 async def pa_serve_workspace_file(file_path: str):
891 async def pa_serve_workspace_file(file_path: str, request: Request):
892 892 """Securely serve a workspace file for browser rendering.
893 893
894 894 Only files within ``~/.g4f/workspace`` can be served. Path
@@ -897,23 +897,20 @@ class Api:
897 897 refused with **403 Forbidden** so that sensitive file types (e.g.
898 898 ``.env``, ``.pa.py``, ``.py``) can never be read via this route.
899 899
900 HTML files may freely reference co-located CSS and JS files; the
901 browser will fetch those via additional ``GET /pa/files/…`` calls
902 which are also subject to the same security checks.
903
904 .. note:: **localStorage / sessionStorage / cookies**
905
906 Files served here share the browser origin with the g4f server
907 (e.g. ``http://localhost:8080``), so JavaScript inside them
908 **can** read ``localStorage`` and ``sessionStorage`` stored by
909 the main g4f web UI (e.g. saved API keys). The HTTP
910 ``Permissions-Policy`` header cannot restrict storage access.
911 The protection boundary is therefore at the *workspace* level:
912 only files that the operator or sandboxed PA code has
913 explicitly placed in ``~/.g4f/workspace`` are ever served.
914 Operators who expose this server to untrusted users should
915 keep their API key out of ``localStorage`` or serve the g4f
916 API on a separate origin/port.
900 HTML files are served with a ``Content-Security-Policy: sandbox``
901 directive (without ``allow-same-origin``), which forces the page
902 into a unique *null* browser origin. As a result the page cannot
903 access ``localStorage``, ``sessionStorage``, ``IndexedDB``, or
904 cookies belonging to the g4f server origin — the browser rejects
905 all such calls with a ``SecurityError``. The actual request
906 origin (``scheme://host``) is used in every source directive (e.g.
907 ``default-src``) instead of ``'self'``, so that co-located CSS,
908 JS, images, and fonts still load correctly despite the document
909 having a null origin.
910
911 Non-HTML sub-resources (CSS, JS, images, fonts) are served without
912 the ``sandbox`` directive; they are leaf resources and do not run
913 in their own browsing context.
917 914 """
918 915 from g4f.mcp.pa_provider import get_workspace_dir
919 916 workspace = get_workspace_dir()
@@ -940,6 +937,48 @@ class Api:
940 937 HTTP_403_FORBIDDEN,
941 938 )
942 939
940 # Derive the actual request origin (scheme + host) so that CSP
941 # source directives reference the real server address rather than
942 # the generic 'self' keyword. This is important because HTML
943 # files are sandboxed into a null origin (see below), at which
944 # point 'self' would resolve to null and block all sub-resources.
945 request_origin = f"{request.url.scheme}://{request.headers.get('host', 'localhost')}"
946
947 is_html = ext in ("html", "htm")
948 if is_html:
949 # HTML documents are served with the CSP sandbox directive
950 # (without allow-same-origin). This forces the page into a
951 # unique null browsing-context origin so that it cannot access
952 # the g4f server's localStorage, sessionStorage, IndexedDB, or
953 # cookies. The page can still load sub-resources (CSS, JS,
954 # images) because they are referenced by the explicit
955 # request_origin in the source directives.
956 csp = (
957 "sandbox allow-scripts allow-forms allow-downloads allow-popups; "
958 f"default-src {request_origin}; "
959 f"script-src {request_origin} 'unsafe-inline'; "
960 f"style-src {request_origin} 'unsafe-inline'; "
961 f"img-src {request_origin} data:; "
962 f"font-src {request_origin} data:; "
963 "connect-src 'none'; "
964 "object-src 'none'; "
965 "base-uri 'none';"
966 )
967 else:
968 # Non-HTML sub-resources (CSS, JS, images, fonts) don't need
969 # sandboxing — they are leaf assets without their own browsing
970 # context. Use the request origin for source directives.
971 csp = (
972 f"default-src {request_origin}; "
973 f"script-src {request_origin} 'unsafe-inline'; "
974 f"style-src {request_origin} 'unsafe-inline'; "
975 f"img-src {request_origin} data:; "
976 f"font-src {request_origin} data:; "
977 "connect-src 'none'; "
978 "object-src 'none'; "
979 "base-uri 'none';"
980 )
981
943 982 headers = {
944 983 # Prevent the browser from sniffing a different content-type
945 984 "X-Content-Type-Options": "nosniff",
@@ -947,19 +986,7 @@ class Api:
947 986 "X-Frame-Options": "SAMEORIGIN",
948 987 # Basic XSS filter (belt-and-suspenders; CSP is more important)
949 988 "X-XSS-Protection": "1; mode=block",
950 # Restrict what the page itself can load/execute.
951 # Note: localStorage / sessionStorage are NOT controllable via
952 # CSP or Permissions-Policy; isolation requires a distinct origin.
953 "Content-Security-Policy": (
954 "default-src 'self'; "
955 "script-src 'self' 'unsafe-inline'; "
956 "style-src 'self' 'unsafe-inline'; "
957 "img-src 'self' data:; "
958 "font-src 'self' data:; "
959 "connect-src 'none'; "
960 "object-src 'none'; "
961 "base-uri 'none';"
962 ),
989 "Content-Security-Policy": csp,
963 990 # Restrict powerful browser APIs that workspace pages don't need
964 991 "Permissions-Policy": (
965 992 "geolocation=(), camera=(), microphone=(), "