Release v0.51.278 — Release IT (stage-p3g — repair inline PDF preview #3652) (#3684)
Some checks failed
Release & Docker / release (push) Has been cancelled

* fix(ui): repair inline PDF preview (blob module loader + CSP worker-src) (#3652, #3649)

Co-authored-by: sky <example@email.com>

* docs(changelog): v0.51.278 — Release IT (stage-p3g, #3652 only); widen CSP test window

---------

Co-authored-by: nesquena-hermes <[email protected]>
Co-authored-by: sky <example@email.com>
This commit is contained in:
nesquena-hermes
2026-06-05 14:27:38 -07:00
committed by GitHub
parent 8ef698ea05
commit 8f89b4f825
4 changed files with 19 additions and 12 deletions

View File

@@ -3,6 +3,11 @@
## [Unreleased]
## [v0.51.278] — 2026-06-05 — Release IT (stage-p3g — repair inline PDF preview)
### Fixed
- **Inline PDF preview in chat now renders again.** The PDF.js loader previously created a `<script>` with both `src` and `textContent` set (the latter is ignored when `src` is present), so PDF.js never initialized and the preview hung on the spinner before degrading to a download link. It now loads PDF.js via a blob module script that sets the worker source, passes `isEvalSupported:false` to harden the parser, and revokes the blob URL on load. CSP gains `blob:` in `script-src` and a scoped `worker-src blob: 'self' https://cdn.jsdelivr.net` to permit the worker. (#3652, @xx77yy; closes #3649)
## [v0.51.277] — 2026-06-05 — Release IS (stage-p3f — preserve context-window in usage indicator)
### Fixed

View File

@@ -57,7 +57,8 @@ def _security_headers(handler):
handler.send_header(
'Content-Security-Policy',
"default-src 'self' https://*.cloudflareaccess.com; "
"script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://static.cloudflareinsights.com; "
"script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://static.cloudflareinsights.com blob:; "
"worker-src blob: 'self' https://cdn.jsdelivr.net; "
"style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com; "
"img-src 'self' data: https: blob:; font-src 'self' data: https://fonts.gstatic.com; connect-src 'self' https://cdn.jsdelivr.net; "
"manifest-src 'self' https://*.cloudflareaccess.com; "

View File

@@ -8515,7 +8515,7 @@ function loadPdfInline(container){
el.outerHTML=`<div class="pdf-preview-fallback"><a class="msg-media-link" href="api/media?path=${encodeURIComponent(path)}&download=1" download="${esc(fname)}">📎 ${esc(fname)}</a><br><span style="color:var(--muted);font-size:12px">${t('pdf_too_large')}</span></div>`;
return;
}
return pdfjsLib.getDocument({data:buf}).promise;
return pdfjsLib.getDocument({data:buf, isEvalSupported:false}).promise;
})
.then(pdf=>{
if(!pdf) return;
@@ -8548,16 +8548,14 @@ function loadPdfInline(container){
loadPdf(window._pdfjsLib);
} else if(!_pdfjsLoading){
_pdfjsLoading=true;
const _pdfSrc='https://cdn.jsdelivr.net/npm/pdfjs-dist@4.9.155/build/pdf.min.mjs';
const _pdfWorker='https://cdn.jsdelivr.net/npm/pdfjs-dist@4.9.155/build/pdf.worker.min.mjs';
const _pdfBlob=new Blob([`import*as p from'${_pdfSrc}';p.GlobalWorkerOptions.workerSrc='${_pdfWorker}';window._pdfjsLib=p;window._pdfjsReady=true;window.dispatchEvent(new Event('pdfjs-ready'));`],{type:'application/javascript'});
const s=document.createElement('script');
s.src='https://cdn.jsdelivr.net/npm/pdfjs-dist@4.9.155/build/pdf.min.mjs';
s.type='module';
s.textContent=`
import * as pdfjsLib from '${s.src}';
pdfjsLib.GlobalWorkerOptions.workerSrc='https://cdn.jsdelivr.net/npm/pdfjs-dist@4.9.155/build/pdf.worker.min.mjs';
window._pdfjsLib=pdfjsLib;
window._pdfjsReady=true;
window.dispatchEvent(new Event('pdfjs-ready'));
`;
const _pdfBlobUrl=URL.createObjectURL(_pdfBlob);
s.src=_pdfBlobUrl;
s.onload=()=>URL.revokeObjectURL(_pdfBlobUrl);
document.head.appendChild(s);
window.addEventListener('pdfjs-ready',()=>{ _pdfjsReady=true; loadPdf(window._pdfjsLib); },{once:true});
setTimeout(()=>{

View File

@@ -18,8 +18,11 @@ class TestManifestSrcCSP:
text = (ROOT / "api" / "helpers.py").read_text(encoding="utf-8")
start = text.find("Content-Security-Policy")
assert start != -1, "Content-Security-Policy not found in helpers.py"
# Grab the full CSP string (up to the closing paren of send_header)
chunk = text[start:start + 600]
# Grab the full CSP string (up to the closing paren of send_header).
# Widened from 600 -> 1000 after the PDF-preview fix (#3652) added
# `blob:` to script-src + a `worker-src` directive, pushing later
# directives (form-action) past the old window.
chunk = text[start:start + 1000]
return chunk
def test_manifest_src_self_present(self):