/** * Builds the iframe CSP from the img-src / media-src lists. * * SECURITY: both lists are validated again here, at the point they are * interpolated, rather than trusted because they came from app config. The * resolved config is a cached, mutable object, and the client renderers pass * lists fetched from the server, so neither is guaranteed to still be what the * schema accepted. A list that fails validation throws instead of producing a * policy with an injected directive. */ export declare function extensionIframeCspBase(imageSources: readonly string[], mediaSources: readonly string[]): string; export declare const EXTENSION_FRAME_ANCESTORS: string; export declare const EXTENSION_IFRAME_CSP: string; export declare const EXTENSION_IFRAME_META_CSP: string; /** * Authenticated endpoint that returns the deployment's validated * `{ imageSources, mediaSources }`, so the client-rendered `srcDoc` frames * (`ExtensionViewer`, `InlineExtensionFrame`) can apply the same img-src / * media-src policy as the server render route without importing app config. */ export declare const EXTENSION_IFRAME_DISPLAY_SOURCES_PATH = "/_agent-native/extensions/iframe/display-sources"; export declare const EXTENSION_LINK_CHANNEL_HELLO_MESSAGE_TYPE = "agent-native-extension.hello-link-channel"; export declare const EXTENSION_LINK_CHANNEL_HOST_PORT_MESSAGE_TYPE = "agent-native-extension.host-bridge-port"; export declare const EXTENSION_LINK_CHANNEL_ACK_MESSAGE_TYPE = "agent-native-extension.link-channel-ready-ack"; export declare const EXTENSION_LINK_CHANNEL_READY_MESSAGE_TYPE = "agent-native-extension.link-channel-ready"; export declare const EXTENSION_OPEN_EXTERNAL_LINK_MESSAGE_TYPE = "agent-native-extension.open-external-link"; export declare const EXTENSION_NAVIGATE_SAME_ORIGIN_MESSAGE_TYPE = "agent-native-extension.navigate-same-origin"; /** * Builds the meta CSP for a client-rendered frame from the * `EXTENSION_IFRAME_DISPLAY_SOURCES_PATH` response. Anything that is not a * valid pair of source lists falls back to the default policy, which blocks * every remote image and media source (fail closed). */ export declare function extensionIframeMetaCspFromDisplaySources(value: unknown): string; /** * SECURITY — EXTENSION CONTENT IS UNTRUSTED. * * `${content}` (line ~Body) interpolates raw HTML/JS authored by a user. This * file is the boundary between framework-controlled HTML and user-controlled * HTML. Two non-negotiable invariants for every change here: * * 1. The iframe MUST be rendered with a `sandbox` attribute that does NOT * include `allow-same-origin`. The viewer (`ExtensionViewer.tsx`, * `EmbeddedExtension.tsx`) normalizes the sandbox through * `normalizeAgentNativeExtensionSandbox()`, which permits scripts, forms, * and downloads. External anchors ask the parent to open a fresh tab; * extensions never receive top-level navigation or popup permission. Adding * `allow-same-origin` would give the extension full DOM access to the * parent window via cross-frame script. * * 2. Every reachable parent action must treat the postMessage payload as * hostile. The bridge in `iframe-bridge.ts` enforces a path allowlist, * header sanitization, and method allowlist; do not relax those gates * for "convenience" in this file or any caller. * * 3. `connect-src` stays `'self'` and stays out of app config. Remote * images and media are `extensions.iframeImageSources` / * `iframeMediaSources`, which reach only `img-src` / `media-src`. * Each allowed remote origin is an explicit egress permission: the * browser sends the URL, and extension script can encode data into * it. API calls stay on the permission-gated host bridge. Token * validation still stops a value from appending another directive. * * For the trust model rationale, see audit 05-tools-sandbox.md (C1) and the * `extensions` skill. When in doubt, fail closed. * * BACKWARDS COMPAT — the iframe injects helpers under both their canonical * `extension*` names (`extensionFetch`, `extensionData`, `extensionId`, * `extensionBinding`) AND legacy `tool*` aliases (`toolFetch`, `toolData`, * `toolId`, `toolBinding`) so existing user-authored extension bodies that * pre-date the rename keep working. Same for layout opt-ins: * `data-extension-layout="full-bleed"` / `data-extension-padding="none"` / * class `agent-native-extension-bleed` / CSS var * `--agent-native-extension-padding` are canonical; the `data-tool-*`, * `agent-native-tool-bleed`, and `--agent-native-tool-padding` variants are * accepted as aliases. */ export interface ExtensionRenderBinding { authorEmail: string; viewerEmail: string; isAuthor: boolean; /** * Resolved role for the viewer ("owner" | "admin" | "editor" | "commenter" | "viewer"). * * TODO(security, audit H4): the host-side bridge does not yet gate any * helper based on this value — every viewer gets the same powers as the * author. The role is plumbed through so a follow-up PR can constrain * `appAction` / `dbExec` / `extensionFetch` for non-author viewers (and * eventually require an explicit consent step before running a shared * extension, audit C1). For now this is metadata only. */ role: "owner" | "admin" | "editor" | "commenter" | "viewer"; source?: "database" | "local-files"; permissions?: { appActions?: string[]; extensionData?: boolean; sql?: boolean; externalFetch?: boolean; }; } export declare function buildExtensionExternalLinkTargetBootstrap(linkNonce?: string): string; export declare function buildExtensionHtml(content: string, themeVars: string, isDark: boolean, extensionId?: string, binding?: ExtensionRenderBinding, metaCsp?: string, linkNonce?: string, enableExternalLinkBridge?: boolean): string;