T09 · Insecure Skill Coding Practices
- Location
scripts/open-runtime.mjs:10068- Finding
Untrusted project code can invoke a privileged CDP bridge to read project files
- Content
View full analysis
Vulnerability Details
File Location:
scripts/open-runtime.mjs:10068-10173
Related Locations:assets/vibepolish.html:29,assets/vibepolish-runtime.js:12400-12405,scripts/open-runtime.mjs:8219-8227,scripts/open-runtime.mjs:10414-10434
Vulnerability Type: Unauthenticated privileged browser-to-filesystem bridge
Risk Level: HighComplete Code Snippets
The project is loaded into an iframe that permits scripts and retains its origin:
html <iframe id="dt-frame" sandbox="allow-scripts allow-same-origin allow-forms allow-modals"></iframe>Project mode navigates that iframe to a page served from the same local origin as the trusted editor wrapper:
js frame.removeAttribute("srcdoc"); frame.src = runtimeSource.url;The launcher installs a CDP binding and accepts requests without validating the caller's frame, execution context, origin, or possession of an authorization capability:
js cdp.on("Runtime.bindingCalled", async (event) => { if (event.name !== "__dtCdpStyleProviderBinding") return; let payload; try { payload = JSON.parse(event.payload || "{}"); const request = payload.request || {}; const isExclusive = [ "applyStyleTexts", "captureReviewImage", "captureSourceState", "exportProject", "exportProjectSourceCopy", "exportSingleHtmlPackage", "applySource", "restoreSessionTransaction" ].includes(request.action); const readGeneration = isExclusive ? 0 : ++latestReadGeneration; providerQueue = providerQueue.catch(() => void 0).then(async () => { await styleProviderReady; return cdpWinningRulesForRequest(cdp, request, knownStyleSheetIds, { debug: !!meta.debug, projectRoot: meta.projectRoot || "", sourceFile: meta.sourceFile || "", projectOrigin: meta.projectOrigin || "", knownStyleSheetIds, styleSheetHeaders, liveStyleSheetTextBySourceKey, sourceStyleSheetBaseline ...[truncated 5626 chars]- Remediation
View remediation
Remediation Suggestions
-
Bind privileged operations only to the trusted editor context
- Record the editor wrapper's CDP execution-context ID and frame ID.
- Reject every
Runtime.bindingCalledevent not originating from that exact context. - Do not install the filesystem-capable binding globally across all frames.
-
Separate the editor and project into different origins
- Serve untrusted project content from a distinct origin or randomized port.
- Ensure the project iframe cannot access the editor's global objects.
- Remove
allow-same-originfrom the iframe sandbox if application functionality permits it.
-
Require an unguessable per-session capability
- Generate a cryptographically random capability in the Node.js launcher.
- Deliver it only to the trusted editor context.
- Authenticate every privileged request and bind the capability to the expected frame and session.
- Do not expose the capability in the project DOM, query string, or globals reachable from the iframe.
-
Restrict file reads
- Maintain an explicit allowlist of files loaded as editable documents or stylesheets.
- Reject
captureSourceStatepaths not already registered as part of the editing session. - Explicitly deny common sensitive files and directories such as
.env, credential files, private keys, and hidden configuration unless the user separately authorizes them.
-
Restrict mutation operations
- Require explicit trusted-editor user interaction before
applySourceor restoration operations. - Use a short-lived confirmation token tied to the exact paths and content hashes being modified.
- Reject write requests initiated from project frames, regardless of request payload validity.
- Require explicit trusted-editor user interaction before
-
Harden the response channel
- Return privileged results only to the authenticated trusted editor context.
- Avoid evaluating result callbacks in a context selected solely by attacker-provided request identifiers.
...[truncated 313 chars]
-
