diff --git a/.github/codeql-config.yml b/.github/codeql-config.yml new file mode 100644 index 0000000..cfedc46 --- /dev/null +++ b/.github/codeql-config.yml @@ -0,0 +1,28 @@ +name: BentoPDF CodeQL config + +paths-ignore: + - dist + - dist-test + - coverage + - node_modules + - vendor + - bentopdf-pymupdf-wasm + - libreoffice-wasm-package + - bentopdf-airgap-bundle + - public/pdfjs-viewer + - public/pdfjs-annotation-viewer + - public/libreoffice-wasm + - public/coherentpdf.browser.min.js + - public/workers + - public/embedpdf + - docs/.vitepress + - '**/*.min.js' + - '**/*.d.ts' + +query-filters: + - exclude: + id: js/log-injection + - exclude: + id: js/tainted-format-string + - exclude: + id: js/file-system-race diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index fa3f119..9a0ad2d 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -32,26 +32,8 @@ jobs: uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} - queries: security-extended,security-and-quality - config: | - paths-ignore: - - dist - - dist-test - - coverage - - node_modules - - vendor - - bentopdf-pymupdf-wasm - - libreoffice-wasm-package - - bentopdf-airgap-bundle - - public/pdfjs-viewer - - public/pdfjs-annotation-viewer - - public/libreoffice-wasm - - public/coherentpdf.browser.min.js - - public/workers - - public/embedpdf - - docs/.vitepress - - '**/*.min.js' - - '**/*.d.ts' + queries: security-extended + config-file: ./.github/codeql-config.yml - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 diff --git a/scripts/generate-i18n-pages.mjs b/scripts/generate-i18n-pages.mjs index 5a32db8..846cd0a 100644 --- a/scripts/generate-i18n-pages.mjs +++ b/scripts/generate-i18n-pages.mjs @@ -145,7 +145,9 @@ function processFileForLanguage( href.startsWith('#') || href.startsWith('mailto:') || href.startsWith('tel:') || - href.startsWith('javascript:') + href.startsWith('javascript:') || + href.startsWith('data:') || + href.startsWith('vbscript:') ) { return; } diff --git a/src/js/i18n/i18n.ts b/src/js/i18n/i18n.ts index 5513148..4bf6198 100644 --- a/src/js/i18n/i18n.ts +++ b/src/js/i18n/i18n.ts @@ -235,7 +235,9 @@ export const rewriteLinks = (): void => { href.startsWith('mailto:') || href.startsWith('tel:') || href.startsWith('#') || - href.startsWith('javascript:') + href.startsWith('javascript:') || + href.startsWith('data:') || + href.startsWith('vbscript:') ) { return; } diff --git a/src/js/logic/form-creator.ts b/src/js/logic/form-creator.ts index 36692a2..b6d7e09 100644 --- a/src/js/logic/form-creator.ts +++ b/src/js/logic/form-creator.ts @@ -754,7 +754,8 @@ function renderField(field: FormField): void { contentEl.appendChild(img); } catch (error) { console.warn( - `Failed to render barcode preview for field "${field.name}":`, + 'Failed to render barcode preview for field:', + String(field.name).replace(/[\r\n]+/g, ' '), error ); contentEl.innerHTML = `
Invalid data
`; @@ -2320,11 +2321,17 @@ downloadBtn.addEventListener('click', async () => { if (existingField) { radioGroup = existingField as PDFRadioGroup; radioGroups.set(groupName, radioGroup); - console.log(`Using existing radio group from PDF: ${groupName}`); + console.log( + 'Using existing radio group from PDF:', + String(groupName).replace(/[\r\n]+/g, ' ') + ); } else { radioGroup = form.createRadioGroup(groupName); radioGroups.set(groupName, radioGroup); - console.log(`Created new radio group: ${groupName}`); + console.log( + 'Created new radio group:', + String(groupName).replace(/[\r\n]+/g, ' ') + ); } } @@ -2690,7 +2697,8 @@ downloadBtn.addEventListener('click', async () => { pdfPage.drawImage(pngImage, { x, y, width, height }); } catch (e) { console.warn( - `Failed to generate barcode for field "${field.name}":`, + 'Failed to generate barcode for field:', + String(field.name).replace(/[\r\n]+/g, ' '), e ); } diff --git a/src/js/logic/pdf-workflow-page.ts b/src/js/logic/pdf-workflow-page.ts index 9de0984..a3ad6b6 100644 --- a/src/js/logic/pdf-workflow-page.ts +++ b/src/js/logic/pdf-workflow-page.ts @@ -554,7 +554,10 @@ async function addNodeToCanvas( try { const node = createNodeByType(type); if (!node) { - console.error('Node type not found in registry:', type); + console.error( + 'Node type not found in registry:', + String(type).replace(/[\r\n]+/g, ' ') + ); return; } await editor.addNode(node); diff --git a/src/js/workflow/nodes/encrypt-node.ts b/src/js/workflow/nodes/encrypt-node.ts index 0d50218..7d1bb57 100644 --- a/src/js/workflow/nodes/encrypt-node.ts +++ b/src/js/workflow/nodes/encrypt-node.ts @@ -45,7 +45,7 @@ export class EncryptNode extends BaseWorkflowNode { return { pdf: await processBatch(pdfInputs, async (input) => { const qpdf = await initializeQpdf(); - const uid = `${Date.now()}_${Math.random().toString(36).slice(2, 9)}`; + const uid = `${Date.now()}_${crypto.randomUUID().slice(0, 7)}`; const inputPath = `/tmp/input_encrypt_${uid}.pdf`; const outputPath = `/tmp/output_encrypt_${uid}.pdf`; diff --git a/src/js/workflow/nodes/linearize-node.ts b/src/js/workflow/nodes/linearize-node.ts index cadb9f3..b7d9680 100644 --- a/src/js/workflow/nodes/linearize-node.ts +++ b/src/js/workflow/nodes/linearize-node.ts @@ -28,7 +28,7 @@ export class LinearizeNode extends BaseWorkflowNode { return { pdf: await processBatch(pdfInputs, async (input) => { const qpdf = await initializeQpdf(); - const uid = `${Date.now()}_${Math.random().toString(36).slice(2, 9)}`; + const uid = `${Date.now()}_${crypto.randomUUID().slice(0, 7)}`; const inputPath = `/tmp/input_linearize_${uid}.pdf`; const outputPath = `/tmp/output_linearize_${uid}.pdf`; diff --git a/src/js/workflow/nodes/overlay-node.ts b/src/js/workflow/nodes/overlay-node.ts index e27b9a0..5c5a53c 100644 --- a/src/js/workflow/nodes/overlay-node.ts +++ b/src/js/workflow/nodes/overlay-node.ts @@ -40,7 +40,7 @@ export class OverlayNode extends BaseWorkflowNode { const mode = modeControl?.value === 'underlay' ? '--underlay' : '--overlay'; const qpdf = await initializeQpdf(); - const uid = `${Date.now()}_${Math.random().toString(36).slice(2, 9)}`; + const uid = `${Date.now()}_${crypto.randomUUID().slice(0, 7)}`; const inputPath = `/tmp/input_overlay_${uid}.pdf`; const overlayPath = `/tmp/overlay_${uid}.pdf`; const outputPath = `/tmp/output_overlay_${uid}.pdf`; diff --git a/src/js/workflow/nodes/repair-node.ts b/src/js/workflow/nodes/repair-node.ts index 05c882e..3cc0079 100644 --- a/src/js/workflow/nodes/repair-node.ts +++ b/src/js/workflow/nodes/repair-node.ts @@ -25,7 +25,7 @@ export class RepairNode extends BaseWorkflowNode { return { pdf: await processBatch(pdfInputs, async (input) => { const qpdf = await initializeQpdf(); - const uid = `${Date.now()}_${Math.random().toString(36).slice(2, 9)}`; + const uid = `${Date.now()}_${crypto.randomUUID().slice(0, 7)}`; const inputPath = `/tmp/input_repair_${uid}.pdf`; const outputPath = `/tmp/output_repair_${uid}.pdf`; diff --git a/vite.config.ts b/vite.config.ts index 4ffb79a..feecd73 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -328,9 +328,10 @@ function createCorsProxyMiddleware(): Connect.NextHandleFunction { ); proxyReq.on('error', (err) => { - console.error('[CORS Proxy] Error:', err.message); + const msg = String(err.message).replace(/[\r\n]+/g, ' '); + console.error('[CORS Proxy] Error:', msg); res.statusCode = 502; - res.end(`Proxy error: ${err.message}`); + res.end(`Proxy error: ${msg}`); }); if (body.length > 0) {