mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-10-23 10:36:09 +00:00
🩹 fix: relax connect-src to allow sourcemap fetches (silence CSP noise)
Manual e2e on PR #12934: every time DevTools is open while viewing a DOCX or PPTX preview, the console fills with CSP violations like: Connecting to 'https://cdn.jsdelivr.net/npm/docx-preview@0.3.7/ dist/docx-preview.min.js.map' violates the following Content Security Policy directive: "connect-src 'none'". The request has been blocked. The actual rendering isn't affected (sourcemap fetches happen AFTER the script has already loaded and executed via `script-src`), but the noise is enough to make people suspect a real problem and distracts from useful console output. Fix: relax `connect-src` from `'none'` to `'self' https://cdn. jsdelivr.net` in both DOCX and PPTX CDN docs. This allows: - Same-origin fetches (sandpack-static-server) — covers any bundler-embedded sourcemaps + same-origin runtime fetches the renderer might make - jsdelivr fetches — covers sourcemaps from the CDN where we loaded the script Exfiltration risk stays minimal: the iframe is cross-origin to LibreChat so an attacker can't read application data anyway, and neither 'self' (sandpack-static-server) nor jsdelivr is a useful target for exfiltrating slide content to a host the attacker controls. Tests updated: assertions for `connect-src 'none'` swapped to `connect-src 'self' https://cdn.jsdelivr.net` for both DOCX + PPTX CDN docs. Added negative assertion for wildcard `*` in connect-src so a future "let's allow everything" rewrite can't widen the exfiltration surface.
This commit is contained in:
parent
78642bc6bb
commit
41cc527211
2 changed files with 45 additions and 8 deletions
|
|
@ -123,10 +123,15 @@ describe('Office HTML producers', () => {
|
|||
);
|
||||
expect(cspMatch).not.toBeNull();
|
||||
const csp = cspMatch![1];
|
||||
// The bomb defense: no outbound HTTP requests from the rendered
|
||||
// iframe — a parser bug in docx-preview can't be turned into
|
||||
// exfiltration of the embedded document content.
|
||||
expect(csp).toMatch(/connect-src 'none'/);
|
||||
/* `connect-src` is restricted to self + jsdelivr (where the
|
||||
* renderer script came from). Allowing these is necessary for
|
||||
* DevTools sourcemap fetches; broader exfiltration paths
|
||||
* (arbitrary HTTPS, websockets, etc.) stay blocked. Manual
|
||||
* e2e on PR #12934 — strict `'none'` filled DevTools console
|
||||
* with `.min.js.map` violations every time the iframe was
|
||||
* inspected. */
|
||||
expect(csp).toMatch(/connect-src 'self' https:\/\/cdn\.jsdelivr\.net/);
|
||||
expect(csp).not.toMatch(/connect-src[^;]*\*/); // no wildcard
|
||||
// No `<base>` tampering, no form submission either.
|
||||
expect(csp).toMatch(/base-uri 'none'/);
|
||||
expect(csp).toMatch(/form-action 'none'/);
|
||||
|
|
@ -438,7 +443,7 @@ describe('Office HTML producers', () => {
|
|||
expect(html).toContain('crossorigin="anonymous"');
|
||||
});
|
||||
|
||||
test('CSP locks the iframe down: connect-src none, no eval, no base/form tampering', async () => {
|
||||
test('CSP locks the iframe down: connect-src restricted, no eval, no base/form tampering', async () => {
|
||||
const pptx = await buildPptx([{ title: 'X' }]);
|
||||
const html = await _internal.pptxToHtmlViaCdn(pptx);
|
||||
const cspMatch = html.match(
|
||||
|
|
@ -446,7 +451,14 @@ describe('Office HTML producers', () => {
|
|||
);
|
||||
expect(cspMatch).not.toBeNull();
|
||||
const csp = cspMatch![1];
|
||||
expect(csp).toMatch(/connect-src 'none'/);
|
||||
/* `connect-src` allows self + jsdelivr (the script's origin)
|
||||
* so DevTools sourcemap fetches succeed and any same-origin
|
||||
* fetches the renderer makes at runtime work. Wildcard
|
||||
* exfiltration paths stay blocked. Manual e2e on PR #12934 —
|
||||
* strict `'none'` filled DevTools console with `.min.js.map`
|
||||
* violations every time the iframe was inspected. */
|
||||
expect(csp).toMatch(/connect-src 'self' https:\/\/cdn\.jsdelivr\.net/);
|
||||
expect(csp).not.toMatch(/connect-src[^;]*\*/); // no wildcard
|
||||
expect(csp).toMatch(/base-uri 'none'/);
|
||||
expect(csp).toMatch(/form-action 'none'/);
|
||||
expect(csp).toMatch(/script-src https:\/\/cdn\.jsdelivr\.net 'unsafe-inline'/);
|
||||
|
|
|
|||
|
|
@ -378,13 +378,25 @@ const OFFICE_HTML_OUTPUT_CAP = 512 * 1024;
|
|||
* styles into `<head>` at render time).
|
||||
*/
|
||||
function buildDocxCdnDocument(base64: string, mammothFallbackHtml: string): string {
|
||||
/* `connect-src` allows fetches to:
|
||||
* - `'self'`: the sandpack-static-server origin the iframe runs in
|
||||
* (covers any same-origin sourcemap fetches the bundler embedded)
|
||||
* - `https://cdn.jsdelivr.net`: where the renderer script came from
|
||||
* (DevTools fetches `.min.js.map` from the same host as the script
|
||||
* to map minified line numbers; with `'none'` the console fills
|
||||
* with CSP violations every time DevTools is open)
|
||||
*
|
||||
* Exfiltration risk is minimal: the iframe is cross-origin to the
|
||||
* LibreChat host so an attacker can't read application data from it,
|
||||
* and the only meaningful target ('self' or jsdelivr) isn't useful
|
||||
* for exfiltrating slide content to a host the attacker controls. */
|
||||
const csp = [
|
||||
"default-src 'none'",
|
||||
"script-src https://cdn.jsdelivr.net 'unsafe-inline'",
|
||||
"style-src 'unsafe-inline'",
|
||||
"img-src 'self' data: blob:",
|
||||
'font-src data:',
|
||||
"connect-src 'none'",
|
||||
"connect-src 'self' https://cdn.jsdelivr.net",
|
||||
"base-uri 'none'",
|
||||
"form-action 'none'",
|
||||
].join('; ');
|
||||
|
|
@ -1061,6 +1073,19 @@ function buildPptxCdnDocument(base64: string): string {
|
|||
* visible symptom is a black iframe with the renderer half-
|
||||
* started. Allowing blob:-only workers is the minimum surface to
|
||||
* unblock rendering without permitting arbitrary worker URLs. */
|
||||
/* `connect-src` allows fetches to:
|
||||
* - `'self'`: the sandpack-static-server origin the iframe runs in
|
||||
* (covers any same-origin fetches pptx-preview or its bundled
|
||||
* echarts make at render time, plus same-origin sourcemap loads)
|
||||
* - `https://cdn.jsdelivr.net`: where the renderer script came from
|
||||
* (DevTools fetches `.min.js.map` from there to map minified
|
||||
* line numbers; with `'none'` the console fills with CSP
|
||||
* violations every time DevTools is open)
|
||||
*
|
||||
* Exfiltration risk is minimal: the iframe is cross-origin to the
|
||||
* LibreChat host so an attacker can't read application data, and
|
||||
* the only meaningful targets ('self' or jsdelivr) aren't useful
|
||||
* for exfiltrating slide content to a host the attacker controls. */
|
||||
const csp = [
|
||||
"default-src 'none'",
|
||||
"script-src https://cdn.jsdelivr.net 'unsafe-inline'",
|
||||
|
|
@ -1068,7 +1093,7 @@ function buildPptxCdnDocument(base64: string): string {
|
|||
'worker-src blob:',
|
||||
"img-src 'self' data: blob:",
|
||||
'font-src data:',
|
||||
"connect-src 'none'",
|
||||
"connect-src 'self' https://cdn.jsdelivr.net",
|
||||
"base-uri 'none'",
|
||||
"form-action 'none'",
|
||||
].join('; ');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue