Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions datasette_apps/agent_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@
"loads invisibly in the user's open browser tab, so this requires an "
"interactive conversation and edit permission for that app. The "
"javascript argument is the body of an async function executed in "
"the app's global scope; its awaited return value is the result and "
"the app's global scope once the page has parsed and its own scripts "
"have run; its awaited return value is the result and "
"must be JSON-serializable - return .textContent or measurements, "
"not DOM elements. Apps render asynchronously, so wait for readiness "
"with await debug.waitFor(fn, {timeout, interval}), which polls fn "
Expand All @@ -85,7 +86,10 @@
"await debug.waitFor(() => window.innerWidth). Captured "
"events include JavaScript errors, unhandled rejections, CSP "
"violations, failed fetches, console output and datasette.query() "
"calls, from page load through script completion. Caveat: synthetic "
"calls, from page load through script completion. Some browsers "
"report errors against app-script-N - the app's Nth <script> "
"element - with line numbers within that script and the failing "
"line as sourceLine. Caveat: synthetic "
"events dispatched by the debug script have isTrusted: false, so a "
"few native browser behaviors (external-link interception, popup "
"allowances) will not trigger."
Expand Down
19 changes: 17 additions & 2 deletions datasette_apps/csp.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,29 @@ async def resolve_csp_origins(datasette, actor, requested_origins, existing_orig
return normalized


def build_csp(connect_origins):
def build_csp(connect_origins, debug=False):
"""debug=True builds the debug frame's policy, which also allows
blob: scripts: on engines that withhold error details for inline
scripts in sandboxed frames (WebKit) the debug bridge re-runs each
inline script as a same-text blob: script, whose errors keep their
details. That grants nothing 'unsafe-inline' does not - page code can
already run any script text it builds - and worker-src 'none' keeps
workers blocked, as they are in production."""
origins = [normalize_connect_origin(origin) for origin in connect_origins]
directives = [*BASE_DIRECTIVES]
if debug:
directives = [
f"{directive} blob:" if directive.startswith("script-src ") else directive
for directive in directives
]
if origins:
element_sources = ["'unsafe-inline'", *origins]
directives.append(f"script-src-elem {' '.join(element_sources)}")
script_element_sources = [*element_sources, *(["blob:"] if debug else [])]
directives.append(f"script-src-elem {' '.join(script_element_sources)}")
directives.append(f"style-src-elem {' '.join(element_sources)}")
directives.append(f"img-src {' '.join(['data:', 'blob:', *origins])}")
if origins:
directives.append(f"connect-src {' '.join(origins)}")
if debug:
directives.append("worker-src 'none'")
return "; ".join(directives) + ";"
Loading
Loading