pretext-pdf 1.1.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +687 -0
- package/README.md +83 -8
- package/dist/allowed-props.d.ts +76 -0
- package/dist/allowed-props.d.ts.map +1 -1
- package/dist/allowed-props.js.map +1 -1
- package/dist/assets/generators/barcode.d.ts +9 -0
- package/dist/assets/generators/barcode.d.ts.map +1 -0
- package/dist/assets/generators/barcode.js +24 -0
- package/dist/assets/generators/barcode.js.map +1 -0
- package/dist/assets/generators/chart.d.ts +13 -0
- package/dist/assets/generators/chart.d.ts.map +1 -0
- package/dist/assets/generators/chart.js +32 -0
- package/dist/assets/generators/chart.js.map +1 -0
- package/dist/assets/generators/qr.d.ts +9 -0
- package/dist/assets/generators/qr.d.ts.map +1 -0
- package/dist/assets/generators/qr.js +25 -0
- package/dist/assets/generators/qr.js.map +1 -0
- package/dist/assets/index.d.ts +19 -0
- package/dist/assets/index.d.ts.map +1 -0
- package/dist/assets/index.js +19 -0
- package/dist/assets/index.js.map +1 -0
- package/dist/assets/loaders/images.d.ts +20 -0
- package/dist/assets/loaders/images.d.ts.map +1 -0
- package/dist/assets/loaders/images.js +69 -0
- package/dist/assets/loaders/images.js.map +1 -0
- package/dist/assets/loaders/orchestrator.d.ts +24 -0
- package/dist/assets/loaders/orchestrator.d.ts.map +1 -0
- package/dist/assets/loaders/orchestrator.js +109 -0
- package/dist/assets/loaders/orchestrator.js.map +1 -0
- package/dist/assets/loaders/vectors.d.ts +25 -0
- package/dist/assets/loaders/vectors.d.ts.map +1 -0
- package/dist/assets/loaders/vectors.js +118 -0
- package/dist/assets/loaders/vectors.js.map +1 -0
- package/dist/assets/loaders/watermark.d.ts +12 -0
- package/dist/assets/loaders/watermark.d.ts.map +1 -0
- package/dist/assets/loaders/watermark.js +40 -0
- package/dist/assets/loaders/watermark.js.map +1 -0
- package/dist/assets/security/fetch.d.ts +14 -0
- package/dist/assets/security/fetch.d.ts.map +1 -0
- package/dist/assets/security/fetch.js +112 -0
- package/dist/assets/security/fetch.js.map +1 -0
- package/dist/assets/security/ipv4-normalize.d.ts +28 -0
- package/dist/assets/security/ipv4-normalize.d.ts.map +1 -0
- package/dist/assets/security/ipv4-normalize.js +116 -0
- package/dist/assets/security/ipv4-normalize.js.map +1 -0
- package/dist/assets/security/path-allowlist.d.ts +12 -0
- package/dist/assets/security/path-allowlist.d.ts.map +1 -0
- package/dist/assets/security/path-allowlist.js +26 -0
- package/dist/assets/security/path-allowlist.js.map +1 -0
- package/dist/assets/security/url-validation.d.ts +22 -0
- package/dist/assets/security/url-validation.d.ts.map +1 -0
- package/dist/assets/security/url-validation.js +164 -0
- package/dist/assets/security/url-validation.js.map +1 -0
- package/dist/assets/svg/dimensions.d.ts +19 -0
- package/dist/assets/svg/dimensions.d.ts.map +1 -0
- package/dist/assets/svg/dimensions.js +43 -0
- package/dist/assets/svg/dimensions.js.map +1 -0
- package/dist/assets/svg/rasterize.d.ts +6 -0
- package/dist/assets/svg/rasterize.d.ts.map +1 -0
- package/dist/assets/svg/rasterize.js +38 -0
- package/dist/assets/svg/rasterize.js.map +1 -0
- package/dist/assets/svg/resolve-content.d.ts +16 -0
- package/dist/assets/svg/resolve-content.d.ts.map +1 -0
- package/dist/assets/svg/resolve-content.js +38 -0
- package/dist/assets/svg/resolve-content.js.map +1 -0
- package/dist/assets/svg/sanitize.d.ts +22 -0
- package/dist/assets/svg/sanitize.d.ts.map +1 -0
- package/dist/assets/svg/sanitize.js +46 -0
- package/dist/assets/svg/sanitize.js.map +1 -0
- package/dist/assets/util/redact-path.d.ts +14 -0
- package/dist/assets/util/redact-path.d.ts.map +1 -0
- package/dist/assets/util/redact-path.js +16 -0
- package/dist/assets/util/redact-path.js.map +1 -0
- package/dist/assets.d.ts +10 -27
- package/dist/assets.d.ts.map +1 -1
- package/dist/assets.js +10 -549
- package/dist/assets.js.map +1 -1
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +2 -1
- package/dist/builder.js.map +1 -1
- package/dist/cli.js +11 -1
- package/dist/cli.js.map +1 -1
- package/dist/compat.d.ts +63 -1
- package/dist/compat.d.ts.map +1 -1
- package/dist/compat.js +42 -5
- package/dist/compat.js.map +1 -1
- package/dist/errors.d.ts +2 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +2 -2
- package/dist/errors.js.map +1 -1
- package/dist/fonts.d.ts.map +1 -1
- package/dist/fonts.js +8 -10
- package/dist/fonts.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -5
- package/dist/index.js.map +1 -1
- package/dist/layout-state.d.ts +1 -1
- package/dist/layout-state.d.ts.map +1 -1
- package/dist/layout-state.js +5 -0
- package/dist/layout-state.js.map +1 -1
- package/dist/measure-blocks/float-group.d.ts +9 -0
- package/dist/measure-blocks/float-group.d.ts.map +1 -0
- package/dist/measure-blocks/float-group.js +103 -0
- package/dist/measure-blocks/float-group.js.map +1 -0
- package/dist/measure-blocks/helpers.d.ts +44 -0
- package/dist/measure-blocks/helpers.d.ts.map +1 -0
- package/dist/measure-blocks/helpers.js +43 -0
- package/dist/measure-blocks/helpers.js.map +1 -0
- package/dist/measure-blocks/highlight.d.ts +26 -0
- package/dist/measure-blocks/highlight.d.ts.map +1 -0
- package/dist/measure-blocks/highlight.js +169 -0
- package/dist/measure-blocks/highlight.js.map +1 -0
- package/dist/measure-blocks/image.d.ts +9 -0
- package/dist/measure-blocks/image.d.ts.map +1 -0
- package/dist/measure-blocks/image.js +136 -0
- package/dist/measure-blocks/image.js.map +1 -0
- package/dist/measure-blocks/index.d.ts +24 -0
- package/dist/measure-blocks/index.d.ts.map +1 -0
- package/dist/measure-blocks/index.js +179 -0
- package/dist/measure-blocks/index.js.map +1 -0
- package/dist/measure-blocks/list.d.ts +8 -0
- package/dist/measure-blocks/list.d.ts.map +1 -0
- package/dist/measure-blocks/list.js +108 -0
- package/dist/measure-blocks/list.js.map +1 -0
- package/dist/measure-blocks/simple-blocks.d.ts +18 -0
- package/dist/measure-blocks/simple-blocks.d.ts.map +1 -0
- package/dist/measure-blocks/simple-blocks.js +121 -0
- package/dist/measure-blocks/simple-blocks.js.map +1 -0
- package/dist/measure-blocks/table/columns.d.ts +17 -0
- package/dist/measure-blocks/table/columns.d.ts.map +1 -0
- package/dist/measure-blocks/table/columns.js +83 -0
- package/dist/measure-blocks/table/columns.js.map +1 -0
- package/dist/measure-blocks/table/measure.d.ts +8 -0
- package/dist/measure-blocks/table/measure.d.ts.map +1 -0
- package/dist/measure-blocks/table/measure.js +231 -0
- package/dist/measure-blocks/table/measure.js.map +1 -0
- package/dist/measure-blocks/table/spans.d.ts +25 -0
- package/dist/measure-blocks/table/spans.d.ts.map +1 -0
- package/dist/measure-blocks/table/spans.js +55 -0
- package/dist/measure-blocks/table/spans.js.map +1 -0
- package/dist/measure-blocks/text-blocks.d.ts +17 -0
- package/dist/measure-blocks/text-blocks.d.ts.map +1 -0
- package/dist/measure-blocks/text-blocks.js +242 -0
- package/dist/measure-blocks/text-blocks.js.map +1 -0
- package/dist/measure-text.d.ts +21 -3
- package/dist/measure-text.d.ts.map +1 -1
- package/dist/measure-text.js +87 -36
- package/dist/measure-text.js.map +1 -1
- package/dist/measure.d.ts +1 -1
- package/dist/measure.d.ts.map +1 -1
- package/dist/measure.js +8 -6
- package/dist/measure.js.map +1 -1
- package/dist/node-polyfill.d.ts.map +1 -1
- package/dist/node-polyfill.js +9 -0
- package/dist/node-polyfill.js.map +1 -1
- package/dist/pipeline-footnotes.d.ts +1 -1
- package/dist/pipeline-footnotes.d.ts.map +1 -1
- package/dist/pipeline-toc.d.ts +1 -1
- package/dist/pipeline-toc.d.ts.map +1 -1
- package/dist/pipeline.d.ts +3 -3
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +4 -5
- package/dist/pipeline.js.map +1 -1
- package/dist/plugin-types.d.ts +1 -1
- package/dist/plugin-types.d.ts.map +1 -1
- package/dist/post-process.d.ts +2 -2
- package/dist/post-process.d.ts.map +1 -1
- package/dist/post-process.js +32 -9
- package/dist/post-process.js.map +1 -1
- package/dist/render-blocks/blockquote.d.ts +7 -0
- package/dist/render-blocks/blockquote.d.ts.map +1 -0
- package/dist/render-blocks/blockquote.js +87 -0
- package/dist/render-blocks/blockquote.js.map +1 -0
- package/dist/render-blocks/callout.d.ts +7 -0
- package/dist/render-blocks/callout.d.ts.map +1 -0
- package/dist/render-blocks/callout.js +84 -0
- package/dist/render-blocks/callout.js.map +1 -0
- package/dist/render-blocks/code.d.ts +7 -0
- package/dist/render-blocks/code.d.ts.map +1 -0
- package/dist/render-blocks/code.js +84 -0
- package/dist/render-blocks/code.js.map +1 -0
- package/dist/render-blocks/footnote.d.ts +11 -0
- package/dist/render-blocks/footnote.d.ts.map +1 -0
- package/dist/render-blocks/footnote.js +45 -0
- package/dist/render-blocks/footnote.js.map +1 -0
- package/dist/render-blocks/header-footer.d.ts +11 -0
- package/dist/render-blocks/header-footer.d.ts.map +1 -0
- package/dist/render-blocks/header-footer.js +56 -0
- package/dist/render-blocks/header-footer.js.map +1 -0
- package/dist/render-blocks/hr.d.ts +7 -0
- package/dist/render-blocks/hr.d.ts.map +1 -0
- package/dist/render-blocks/hr.js +24 -0
- package/dist/render-blocks/hr.js.map +1 -0
- package/dist/render-blocks/image.d.ts +9 -0
- package/dist/render-blocks/image.d.ts.map +1 -0
- package/dist/render-blocks/image.js +135 -0
- package/dist/render-blocks/image.js.map +1 -0
- package/dist/render-blocks/index.d.ts +17 -0
- package/dist/render-blocks/index.d.ts.map +1 -0
- package/dist/render-blocks/index.js +17 -0
- package/dist/render-blocks/index.js.map +1 -0
- package/dist/render-blocks/list-item.d.ts +7 -0
- package/dist/render-blocks/list-item.d.ts.map +1 -0
- package/dist/render-blocks/list-item.js +80 -0
- package/dist/render-blocks/list-item.js.map +1 -0
- package/dist/render-blocks/rich.d.ts +7 -0
- package/dist/render-blocks/rich.d.ts.map +1 -0
- package/dist/render-blocks/rich.js +160 -0
- package/dist/render-blocks/rich.js.map +1 -0
- package/dist/render-blocks/table.d.ts +7 -0
- package/dist/render-blocks/table.d.ts.map +1 -0
- package/dist/render-blocks/table.js +139 -0
- package/dist/render-blocks/table.js.map +1 -0
- package/dist/render-blocks/text.d.ts +7 -0
- package/dist/render-blocks/text.d.ts.map +1 -0
- package/dist/render-blocks/text.js +183 -0
- package/dist/render-blocks/text.js.map +1 -0
- package/dist/render-blocks/watermark.d.ts +8 -0
- package/dist/render-blocks/watermark.d.ts.map +1 -0
- package/dist/render-blocks/watermark.js +52 -0
- package/dist/render-blocks/watermark.js.map +1 -0
- package/dist/render-extras.d.ts.map +1 -1
- package/dist/render-extras.js +1 -2
- package/dist/render-extras.js.map +1 -1
- package/dist/render-utils.d.ts.map +1 -1
- package/dist/render-utils.js +10 -6
- package/dist/render-utils.js.map +1 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +9 -3
- package/dist/render.js.map +1 -1
- package/dist/rich-text.d.ts +2 -1
- package/dist/rich-text.d.ts.map +1 -1
- package/dist/rich-text.js +0 -1
- package/dist/rich-text.js.map +1 -1
- package/dist/types-internal.d.ts +19 -3
- package/dist/types-internal.d.ts.map +1 -1
- package/dist/types-public/document.d.ts +261 -0
- package/dist/types-public/document.d.ts.map +1 -0
- package/dist/types-public/document.js +2 -0
- package/dist/types-public/document.js.map +1 -0
- package/dist/types-public/elements-block.d.ts +246 -0
- package/dist/types-public/elements-block.d.ts.map +1 -0
- package/dist/types-public/elements-block.js +8 -0
- package/dist/types-public/elements-block.js.map +1 -0
- package/dist/types-public/elements-media.d.ts +199 -0
- package/dist/types-public/elements-media.d.ts.map +1 -0
- package/dist/types-public/elements-media.js +2 -0
- package/dist/types-public/elements-media.js.map +1 -0
- package/dist/types-public/elements-text.d.ts +327 -0
- package/dist/types-public/elements-text.d.ts.map +1 -0
- package/dist/types-public/elements-text.js +2 -0
- package/dist/types-public/elements-text.js.map +1 -0
- package/dist/types-public/index.d.ts +14 -0
- package/dist/types-public/index.d.ts.map +1 -0
- package/dist/types-public/index.js +2 -0
- package/dist/types-public/index.js.map +1 -0
- package/dist/types-public/render-options.d.ts +38 -0
- package/dist/types-public/render-options.d.ts.map +1 -0
- package/dist/types-public/render-options.js +2 -0
- package/dist/types-public/render-options.js.map +1 -0
- package/dist/types-public/union.d.ts +13 -0
- package/dist/types-public/union.d.ts.map +1 -0
- package/dist/types-public/union.js +2 -0
- package/dist/types-public/union.js.map +1 -0
- package/dist/types-public/validation.d.ts +64 -0
- package/dist/types-public/validation.d.ts.map +1 -0
- package/dist/types-public/validation.js +2 -0
- package/dist/types-public/validation.js.map +1 -0
- package/dist/types-public.d.ts +5 -1081
- package/dist/types-public.d.ts.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/validate/document.d.ts +28 -0
- package/dist/validate/document.d.ts.map +1 -0
- package/dist/validate/document.js +295 -0
- package/dist/validate/document.js.map +1 -0
- package/dist/validate/elements/forms-floats.d.ts +19 -0
- package/dist/validate/elements/forms-floats.d.ts.map +1 -0
- package/dist/validate/elements/forms-floats.js +96 -0
- package/dist/validate/elements/forms-floats.js.map +1 -0
- package/dist/validate/elements/list.d.ts +10 -0
- package/dist/validate/elements/list.d.ts.map +1 -0
- package/dist/validate/elements/list.js +66 -0
- package/dist/validate/elements/list.js.map +1 -0
- package/dist/validate/elements/media.d.ts +23 -0
- package/dist/validate/elements/media.d.ts.map +1 -0
- package/dist/validate/elements/media.js +179 -0
- package/dist/validate/elements/media.js.map +1 -0
- package/dist/validate/elements/structural-simple.d.ts +21 -0
- package/dist/validate/elements/structural-simple.d.ts.map +1 -0
- package/dist/validate/elements/structural-simple.js +63 -0
- package/dist/validate/elements/structural-simple.js.map +1 -0
- package/dist/validate/elements/structural.d.ts +12 -0
- package/dist/validate/elements/structural.d.ts.map +1 -0
- package/dist/validate/elements/structural.js +12 -0
- package/dist/validate/elements/structural.js.map +1 -0
- package/dist/validate/elements/table.d.ts +10 -0
- package/dist/validate/elements/table.d.ts.map +1 -0
- package/dist/validate/elements/table.js +165 -0
- package/dist/validate/elements/table.js.map +1 -0
- package/dist/validate/elements/text.d.ts +26 -0
- package/dist/validate/elements/text.d.ts.map +1 -0
- package/dist/validate/elements/text.js +331 -0
- package/dist/validate/elements/text.js.map +1 -0
- package/dist/validate/errors.d.ts +9 -0
- package/dist/validate/errors.d.ts.map +1 -0
- package/dist/validate/errors.js +43 -0
- package/dist/validate/errors.js.map +1 -0
- package/dist/validate/fonts.d.ts +11 -0
- package/dist/validate/fonts.d.ts.map +1 -0
- package/dist/validate/fonts.js +118 -0
- package/dist/validate/fonts.js.map +1 -0
- package/dist/validate/helpers.d.ts +76 -0
- package/dist/validate/helpers.d.ts.map +1 -0
- package/dist/validate/helpers.js +169 -0
- package/dist/validate/helpers.js.map +1 -0
- package/dist/validate/index.d.ts +37 -0
- package/dist/validate/index.d.ts.map +1 -0
- package/dist/validate/index.js +279 -0
- package/dist/validate/index.js.map +1 -0
- package/dist/validate.d.ts +6 -18
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +6 -1582
- package/dist/validate.js.map +1 -1
- package/dist/vendor/pretext/VERSION.d.ts +3 -0
- package/dist/vendor/pretext/VERSION.d.ts.map +1 -0
- package/dist/vendor/pretext/VERSION.js +12 -0
- package/dist/vendor/pretext/VERSION.js.map +1 -0
- package/dist/version-check.d.ts +47 -0
- package/dist/version-check.d.ts.map +1 -0
- package/dist/version-check.js +75 -0
- package/dist/version-check.js.map +1 -0
- package/package.json +27 -8
- package/dist/measure-blocks.d.ts +0 -26
- package/dist/measure-blocks.d.ts.map +0 -1
- package/dist/measure-blocks.js +0 -1317
- package/dist/measure-blocks.js.map +0 -1
- package/dist/render-blocks.d.ts +0 -28
- package/dist/render-blocks.d.ts.map +0 -1
- package/dist/render-blocks.js +0 -1059
- package/dist/render-blocks.js.map +0 -1
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image-loading orchestrator — extracted from src/assets.ts in v1.6.0 commit 15/16.
|
|
3
|
+
*
|
|
4
|
+
* Stage 2b of the render pipeline: loads + embeds every ImageElement,
|
|
5
|
+
* float-group image, vector asset (SVG / QR / barcode / chart), plugin
|
|
6
|
+
* asset, and watermark image into a single ImageMap that the layout
|
|
7
|
+
* engine consumes.
|
|
8
|
+
*
|
|
9
|
+
* Image keys are 'img-N' / 'float-group-N' / 'watermark' / per-vector keys
|
|
10
|
+
* assigned inside loadVectorAssets / plugin-supplied keys.
|
|
11
|
+
*
|
|
12
|
+
* IMPORTANT: @cantoo/pdf-lib image embedding is NOT thread-safe. Raw bytes
|
|
13
|
+
* load in parallel; embed calls run sequentially.
|
|
14
|
+
*
|
|
15
|
+
* Images that fail to load (network error, file not found, unreachable URL)
|
|
16
|
+
* are routed through doc.onImageLoadError (default = warn-and-skip) so a
|
|
17
|
+
* single broken image does not crash the document.
|
|
18
|
+
*/
|
|
19
|
+
import { PDFDocument } from '@cantoo/pdf-lib';
|
|
20
|
+
import { PretextPdfError } from '../../errors.js';
|
|
21
|
+
import { findPlugin, runPluginLoadAsset } from '../../plugin-registry.js';
|
|
22
|
+
import { loadImageBytes, resolveImageFormat } from './images.js';
|
|
23
|
+
import { loadVectorAssets } from './vectors.js';
|
|
24
|
+
import { loadWatermarkImage } from './watermark.js';
|
|
25
|
+
export async function loadImages(doc, pdfDoc, contentWidth, plugins, logger) {
|
|
26
|
+
const warn = logger ? logger.warn.bind(logger) : console.warn.bind(console);
|
|
27
|
+
const imageMap = new Map();
|
|
28
|
+
const allowedDirs = doc.allowedFileDirs;
|
|
29
|
+
// Collect all ImageElement entries with their content index for stable keys
|
|
30
|
+
const imageEntries = [];
|
|
31
|
+
for (let i = 0; i < doc.content.length; i++) {
|
|
32
|
+
const el = doc.content[i];
|
|
33
|
+
if (el.type === 'image') {
|
|
34
|
+
imageEntries.push({ el, key: `img-${i}` });
|
|
35
|
+
}
|
|
36
|
+
else if (el.type === 'float-group') {
|
|
37
|
+
const fgImage = {
|
|
38
|
+
type: 'image',
|
|
39
|
+
src: el.image.src,
|
|
40
|
+
...(el.image.format !== undefined ? { format: el.image.format } : {}),
|
|
41
|
+
...(el.image.height !== undefined ? { height: el.image.height } : {}),
|
|
42
|
+
align: 'left',
|
|
43
|
+
spaceAfter: 0,
|
|
44
|
+
spaceBefore: 0,
|
|
45
|
+
};
|
|
46
|
+
imageEntries.push({ el: fgImage, key: `float-group-${i}` });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Load all image bytes in parallel; capture both successes and failures
|
|
50
|
+
const loadResults = imageEntries.length > 0
|
|
51
|
+
? await Promise.allSettled(imageEntries.map(async ({ el, key }) => {
|
|
52
|
+
const bytes = await loadImageBytes(el, key, allowedDirs);
|
|
53
|
+
return { el, key, bytes };
|
|
54
|
+
}))
|
|
55
|
+
: [];
|
|
56
|
+
// Process results: embed successful images, warn on failures
|
|
57
|
+
for (let ri = 0; ri < loadResults.length; ri++) {
|
|
58
|
+
const result = loadResults[ri];
|
|
59
|
+
const entry = imageEntries[ri];
|
|
60
|
+
if (result.status === 'rejected') {
|
|
61
|
+
const err = result.reason instanceof Error ? result.reason : new Error(String(result.reason));
|
|
62
|
+
const action = doc.onImageLoadError ? doc.onImageLoadError(entry.el.src, err) : 'skip';
|
|
63
|
+
if (action === 'throw')
|
|
64
|
+
throw err;
|
|
65
|
+
warn(`[pretext-pdf] Image load skipped: ${err.message}`);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const { el, key, bytes } = result.value;
|
|
69
|
+
const resolvedFormat = resolveImageFormat(el, bytes, key);
|
|
70
|
+
try {
|
|
71
|
+
const pdfImage = resolvedFormat === 'png'
|
|
72
|
+
? await pdfDoc.embedPng(bytes)
|
|
73
|
+
: await pdfDoc.embedJpg(bytes);
|
|
74
|
+
imageMap.set(key, pdfImage);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
78
|
+
const action = doc.onImageLoadError ? doc.onImageLoadError(entry.el.src, error) : 'skip';
|
|
79
|
+
if (action === 'throw')
|
|
80
|
+
throw error;
|
|
81
|
+
warn(`[pretext-pdf] Image embed failed: key "${key}" (format: '${resolvedFormat}')`);
|
|
82
|
+
// Continue without this image rather than crashing
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
await loadVectorAssets(doc, pdfDoc, imageMap, contentWidth, allowedDirs, warn);
|
|
86
|
+
// Load plugin assets
|
|
87
|
+
if (plugins && plugins.length > 0) {
|
|
88
|
+
for (let i = 0; i < doc.content.length; i++) {
|
|
89
|
+
const el = doc.content[i];
|
|
90
|
+
const plugin = findPlugin(plugins, el.type);
|
|
91
|
+
if (!plugin)
|
|
92
|
+
continue;
|
|
93
|
+
try {
|
|
94
|
+
const assetResult = await runPluginLoadAsset(plugin, el, pdfDoc, contentWidth, i);
|
|
95
|
+
if (assetResult) {
|
|
96
|
+
imageMap.set(assetResult.key, assetResult.image);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
if (err instanceof PretextPdfError)
|
|
101
|
+
throw err;
|
|
102
|
+
warn(`[pretext-pdf] Plugin '${plugin.type}' loadAsset failed at index ${i}: ${err instanceof Error ? err.message : String(err)}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
await loadWatermarkImage(doc, pdfDoc, imageMap, allowedDirs, warn);
|
|
107
|
+
return imageMap;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=orchestrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../../../src/assets/loaders/orchestrator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vector asset loader — extracted from src/assets.ts in v1.6.0 commit 14/16.
|
|
3
|
+
*
|
|
4
|
+
* Two-phase load for SVG / QR / barcode / chart elements:
|
|
5
|
+
* Phase A — generate svg + rasterize to PNG in bounded-parallel (CPU/I/O,
|
|
6
|
+
* safe to fan out)
|
|
7
|
+
* Phase B — embed PNGs sequentially because pdf-lib xref mutation is not
|
|
8
|
+
* concurrency-safe
|
|
9
|
+
*
|
|
10
|
+
* One failed asset must not block other embeds (error isolation matches
|
|
11
|
+
* prior behavior). SVG failures propagate; QR/barcode/chart failures warn
|
|
12
|
+
* and leave the slot blank.
|
|
13
|
+
*/
|
|
14
|
+
import { PDFDocument } from '@cantoo/pdf-lib';
|
|
15
|
+
import type { PdfDocument } from '../../types.js';
|
|
16
|
+
import type { ImageMap } from '../../types-internal.js';
|
|
17
|
+
/** Maximum number of vector-asset rasterization tasks allowed to run in parallel. */
|
|
18
|
+
export declare const VECTOR_RASTER_CONCURRENCY = 4;
|
|
19
|
+
/**
|
|
20
|
+
* Bounded-parallel allSettled. Runs at most `limit` tasks concurrently and
|
|
21
|
+
* collects per-task fulfilled/rejected results in the original order.
|
|
22
|
+
*/
|
|
23
|
+
export declare function allSettledWithLimit<T>(items: ReadonlyArray<() => Promise<T>>, limit: number): Promise<PromiseSettledResult<T>[]>;
|
|
24
|
+
export declare function loadVectorAssets(doc: PdfDocument, pdfDoc: PDFDocument, imageMap: ImageMap, contentWidth: number, allowedDirs: string[] | undefined, warn: (msg: string) => void): Promise<void>;
|
|
25
|
+
//# sourceMappingURL=vectors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vectors.d.ts","sourceRoot":"","sources":["../../../src/assets/loaders/vectors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,gBAAgB,CAAA;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AASvD,qFAAqF;AACrF,eAAO,MAAM,yBAAyB,IAAI,CAAA;AAE1C;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,CAAC,EACzC,KAAK,EAAE,aAAa,CAAC,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC,EACtC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAAC,EAAE,CAAC,CAiBpC;AAED,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,EAAE,GAAG,SAAS,EACjC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAC1B,OAAO,CAAC,IAAI,CAAC,CAqEf"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vector asset loader — extracted from src/assets.ts in v1.6.0 commit 14/16.
|
|
3
|
+
*
|
|
4
|
+
* Two-phase load for SVG / QR / barcode / chart elements:
|
|
5
|
+
* Phase A — generate svg + rasterize to PNG in bounded-parallel (CPU/I/O,
|
|
6
|
+
* safe to fan out)
|
|
7
|
+
* Phase B — embed PNGs sequentially because pdf-lib xref mutation is not
|
|
8
|
+
* concurrency-safe
|
|
9
|
+
*
|
|
10
|
+
* One failed asset must not block other embeds (error isolation matches
|
|
11
|
+
* prior behavior). SVG failures propagate; QR/barcode/chart failures warn
|
|
12
|
+
* and leave the slot blank.
|
|
13
|
+
*/
|
|
14
|
+
import { PDFDocument } from '@cantoo/pdf-lib';
|
|
15
|
+
import { PretextPdfError } from '../../errors.js';
|
|
16
|
+
import { resolveSvgContent } from '../svg/resolve-content.js';
|
|
17
|
+
import { resolveSvgDimensions } from '../svg/dimensions.js';
|
|
18
|
+
import { rasterizeSvgToPng } from '../svg/rasterize.js';
|
|
19
|
+
import { generateQrSvg } from '../generators/qr.js';
|
|
20
|
+
import { generateBarcodeSvg } from '../generators/barcode.js';
|
|
21
|
+
import { generateChartSvg } from '../generators/chart.js';
|
|
22
|
+
/** Maximum number of vector-asset rasterization tasks allowed to run in parallel. */
|
|
23
|
+
export const VECTOR_RASTER_CONCURRENCY = 4;
|
|
24
|
+
/**
|
|
25
|
+
* Bounded-parallel allSettled. Runs at most `limit` tasks concurrently and
|
|
26
|
+
* collects per-task fulfilled/rejected results in the original order.
|
|
27
|
+
*/
|
|
28
|
+
export async function allSettledWithLimit(items, limit) {
|
|
29
|
+
const results = new Array(items.length);
|
|
30
|
+
let cursor = 0;
|
|
31
|
+
async function next() {
|
|
32
|
+
while (cursor < items.length) {
|
|
33
|
+
const idx = cursor++;
|
|
34
|
+
try {
|
|
35
|
+
const value = await items[idx]();
|
|
36
|
+
results[idx] = { status: 'fulfilled', value };
|
|
37
|
+
}
|
|
38
|
+
catch (reason) {
|
|
39
|
+
results[idx] = { status: 'rejected', reason };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const workerCount = Math.min(Math.max(1, limit), items.length);
|
|
44
|
+
await Promise.all(Array.from({ length: workerCount }, () => next()));
|
|
45
|
+
return results;
|
|
46
|
+
}
|
|
47
|
+
export async function loadVectorAssets(doc, pdfDoc, imageMap, contentWidth, allowedDirs, warn) {
|
|
48
|
+
const tasks = [];
|
|
49
|
+
for (let i = 0; i < doc.content.length; i++) {
|
|
50
|
+
const el = doc.content[i];
|
|
51
|
+
if (el.type === 'svg') {
|
|
52
|
+
tasks.push({ key: `svg-${i}`, index: i, kind: 'svg', run: async () => {
|
|
53
|
+
const svgContent = await resolveSvgContent(el, allowedDirs);
|
|
54
|
+
const svgWithContent = { type: 'svg', svg: svgContent, width: el.width, height: el.height, align: el.align, spaceBefore: el.spaceBefore, spaceAfter: el.spaceAfter };
|
|
55
|
+
const { widthPt, heightPt } = resolveSvgDimensions(svgWithContent, contentWidth);
|
|
56
|
+
return rasterizeSvgToPng(svgContent, widthPt, heightPt);
|
|
57
|
+
} });
|
|
58
|
+
}
|
|
59
|
+
else if (el.type === 'qr-code') {
|
|
60
|
+
tasks.push({ key: `qr-${i}`, index: i, kind: 'qr-code', run: async () => {
|
|
61
|
+
const svgString = await generateQrSvg(el);
|
|
62
|
+
const sizePt = el.size ?? 80;
|
|
63
|
+
return rasterizeSvgToPng(svgString, sizePt, sizePt);
|
|
64
|
+
} });
|
|
65
|
+
}
|
|
66
|
+
else if (el.type === 'barcode') {
|
|
67
|
+
tasks.push({ key: `barcode-${i}`, index: i, kind: 'barcode', run: async () => {
|
|
68
|
+
const svgString = await generateBarcodeSvg(el);
|
|
69
|
+
const widthPt = el.width ?? 200;
|
|
70
|
+
const heightPt = el.height ?? 60;
|
|
71
|
+
return rasterizeSvgToPng(svgString, widthPt, heightPt);
|
|
72
|
+
} });
|
|
73
|
+
}
|
|
74
|
+
else if (el.type === 'chart') {
|
|
75
|
+
tasks.push({ key: `chart-${i}`, index: i, kind: 'chart', run: async () => {
|
|
76
|
+
const svgString = await generateChartSvg(el, contentWidth);
|
|
77
|
+
const widthPt = el.width ?? contentWidth;
|
|
78
|
+
const heightPt = el.height ?? 300;
|
|
79
|
+
return rasterizeSvgToPng(svgString, widthPt, heightPt);
|
|
80
|
+
} });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (tasks.length === 0)
|
|
84
|
+
return;
|
|
85
|
+
// Phase A: bounded-parallel rasterization. allSettled isolates per-task failures.
|
|
86
|
+
// Cap concurrency to avoid resource exhaustion (sharp/svg2pdfkit workers, FDs)
|
|
87
|
+
// when a document carries many vector assets.
|
|
88
|
+
const results = await allSettledWithLimit(tasks.map(t => () => t.run()), VECTOR_RASTER_CONCURRENCY);
|
|
89
|
+
// Phase B: MUST remain sequential — pdf-lib xref mutation is not concurrency-safe
|
|
90
|
+
for (let ti = 0; ti < tasks.length; ti++) {
|
|
91
|
+
const task = tasks[ti];
|
|
92
|
+
const result = results[ti];
|
|
93
|
+
if (result.status === 'rejected') {
|
|
94
|
+
const err = result.reason;
|
|
95
|
+
// SVG failures historically propagated (file/url/render errors thrown).
|
|
96
|
+
if (task.kind === 'svg')
|
|
97
|
+
throw err;
|
|
98
|
+
if (err instanceof PretextPdfError)
|
|
99
|
+
throw err;
|
|
100
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
101
|
+
warn(`[pretext-pdf] ${task.kind} load failed at index ${task.index} (CHART_LOAD_FAILED): ${message}. Slot will be blank in PDF.`);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
const pdfImage = await pdfDoc.embedPng(result.value);
|
|
106
|
+
imageMap.set(task.key, pdfImage);
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
if (task.kind === 'svg')
|
|
110
|
+
throw err;
|
|
111
|
+
if (err instanceof PretextPdfError)
|
|
112
|
+
throw err;
|
|
113
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
114
|
+
warn(`[pretext-pdf] ${task.kind} embed failed at index ${task.index} (CHART_LOAD_FAILED): ${message}. Slot will be blank in PDF.`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=vectors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vectors.js","sourceRoot":"","sources":["../../../src/assets/loaders/vectors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Watermark image loader — extracted from src/assets.ts in v1.6.0 commit 15/16.
|
|
3
|
+
*
|
|
4
|
+
* Loads + embeds the optional `doc.watermark.image` (path or Uint8Array)
|
|
5
|
+
* into the imageMap under the 'watermark' key. PATH_TRAVERSAL errors
|
|
6
|
+
* propagate; other failures are warn-and-skip (matches prior behavior).
|
|
7
|
+
*/
|
|
8
|
+
import { PDFDocument } from '@cantoo/pdf-lib';
|
|
9
|
+
import type { PdfDocument } from '../../types.js';
|
|
10
|
+
import type { ImageMap } from '../../types-internal.js';
|
|
11
|
+
export declare function loadWatermarkImage(doc: PdfDocument, pdfDoc: PDFDocument, imageMap: ImageMap, allowedDirs: string[] | undefined, warn: (msg: string) => void): Promise<void>;
|
|
12
|
+
//# sourceMappingURL=watermark.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watermark.d.ts","sourceRoot":"","sources":["../../../src/assets/loaders/watermark.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAIvD,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,QAAQ,EAClB,WAAW,EAAE,MAAM,EAAE,GAAG,SAAS,EACjC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAC1B,OAAO,CAAC,IAAI,CAAC,CAwBf"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Watermark image loader — extracted from src/assets.ts in v1.6.0 commit 15/16.
|
|
3
|
+
*
|
|
4
|
+
* Loads + embeds the optional `doc.watermark.image` (path or Uint8Array)
|
|
5
|
+
* into the imageMap under the 'watermark' key. PATH_TRAVERSAL errors
|
|
6
|
+
* propagate; other failures are warn-and-skip (matches prior behavior).
|
|
7
|
+
*/
|
|
8
|
+
import { PDFDocument } from '@cantoo/pdf-lib';
|
|
9
|
+
import { PretextPdfError } from '../../errors.js';
|
|
10
|
+
import { assertPathAllowed } from '../security/path-allowlist.js';
|
|
11
|
+
export async function loadWatermarkImage(doc, pdfDoc, imageMap, allowedDirs, warn) {
|
|
12
|
+
if (!doc.watermark?.image)
|
|
13
|
+
return;
|
|
14
|
+
const src = doc.watermark.image;
|
|
15
|
+
try {
|
|
16
|
+
let bytes;
|
|
17
|
+
if (src instanceof Uint8Array) {
|
|
18
|
+
bytes = src;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
const [fs, pathMod] = await Promise.all([import('fs'), import('path')]);
|
|
22
|
+
const filePath = pathMod.resolve(src);
|
|
23
|
+
assertPathAllowed(filePath, allowedDirs, 'watermark image');
|
|
24
|
+
if (!fs.existsSync(filePath))
|
|
25
|
+
throw new Error(`Watermark image not found`);
|
|
26
|
+
bytes = new Uint8Array(fs.readFileSync(filePath));
|
|
27
|
+
}
|
|
28
|
+
const isPng = bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4E && bytes[3] === 0x47;
|
|
29
|
+
const pdfImage = isPng
|
|
30
|
+
? await pdfDoc.embedPng(bytes)
|
|
31
|
+
: await pdfDoc.embedJpg(bytes);
|
|
32
|
+
imageMap.set('watermark', pdfImage);
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
if (err instanceof PretextPdfError && err.code === 'PATH_TRAVERSAL')
|
|
36
|
+
throw err;
|
|
37
|
+
warn(`[pretext-pdf] Watermark image skipped: ${err instanceof Error ? err.message : String(err)}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=watermark.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watermark.js","sourceRoot":"","sources":["../../../src/assets/loaders/watermark.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAG7C,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAA;AAEjE,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,GAAgB,EAChB,MAAmB,EACnB,QAAkB,EAClB,WAAiC,EACjC,IAA2B;IAE3B,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK;QAAE,OAAM;IAEjC,MAAM,GAAG,GAAG,GAAG,CAAC,SAAS,CAAC,KAAK,CAAA;IAC/B,IAAI,CAAC;QACH,IAAI,KAAiB,CAAA;QACrB,IAAI,GAAG,YAAY,UAAU,EAAE,CAAC;YAC9B,KAAK,GAAG,GAAG,CAAA;QACb,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;YACvE,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,GAAa,CAAC,CAAA;YAC/C,iBAAiB,CAAC,QAAQ,EAAE,WAAW,EAAE,iBAAiB,CAAC,CAAA;YAC3D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAA;YAC1E,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAA;QACnD,CAAC;QACD,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;QAC9F,MAAM,QAAQ,GAAG,KAAK;YACpB,CAAC,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;YAC9B,CAAC,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;QAChC,QAAQ,CAAC,GAAG,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAA;IACrC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,eAAe,IAAI,GAAG,CAAC,IAAI,KAAK,gBAAgB;YAAE,MAAM,GAAG,CAAA;QAC9E,IAAI,CAAC,0CAA0C,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACpG,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch with a hard 10-second timeout AND a manual redirect chain that
|
|
3
|
+
* re-validates each hop against `resolveAndValidateUrl`. Without manual
|
|
4
|
+
* redirect handling, a public URL could 302 to `http://127.0.0.1:8080/internal`
|
|
5
|
+
* and bypass the upfront check — the connection would still be made to
|
|
6
|
+
* the private target.
|
|
7
|
+
*
|
|
8
|
+
* Each hop creates a fresh undici Agent that pins the socket to the IP
|
|
9
|
+
* that just passed validation. This defeats DNS-rebinding TOCTOU attacks
|
|
10
|
+
* where an attacker controls a TTL=0 DNS record and swaps the answer
|
|
11
|
+
* between our `dns.lookup()` and the actual TCP connect.
|
|
12
|
+
*/
|
|
13
|
+
export declare function fetchWithTimeout(url: string, errorCode: 'IMAGE_LOAD_FAILED' | 'SVG_LOAD_FAILED', label: string): Promise<Response>;
|
|
14
|
+
//# sourceMappingURL=fetch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../../src/assets/security/fetch.ts"],"names":[],"mappings":"AA0DA;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,mBAAmB,GAAG,iBAAiB,EAClD,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,QAAQ,CAAC,CAgDnB"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { Agent, fetch as undiciFetch } from 'undici';
|
|
2
|
+
import { PretextPdfError } from '../../errors.js';
|
|
3
|
+
import { resolveAndValidateUrl } from './url-validation.js';
|
|
4
|
+
/**
|
|
5
|
+
* Hardened remote fetch primitives for image / SVG loading.
|
|
6
|
+
*
|
|
7
|
+
* Extracted from `src/assets.ts` in v1.6.0 commit 8/16 — the architect-flagged
|
|
8
|
+
* highest-risk commit of the split. Key invariants preserved here:
|
|
9
|
+
*
|
|
10
|
+
* 1. NO module-level side effects. In particular, `createPinnedAgent`
|
|
11
|
+
* must remain lazy — every call constructs a fresh undici Agent that
|
|
12
|
+
* lives only for the duration of one HTTP request. There is NO module-
|
|
13
|
+
* level `new Agent({...})` allocation. This preserves the G7 cold-start
|
|
14
|
+
* perf baseline (the previous home in `src/assets.ts` had the same
|
|
15
|
+
* lazy behavior).
|
|
16
|
+
* 2. The undici imports are top-level bindings only; no Agent is
|
|
17
|
+
* constructed at module evaluation time.
|
|
18
|
+
* 3. `fetchWithTimeout` re-validates every redirect hop. Public targets
|
|
19
|
+
* that 302 to private addresses are rejected at the next hop.
|
|
20
|
+
* 4. `Agent.close()` is fire-and-forget inside `finally` so callers never
|
|
21
|
+
* block on shutdown.
|
|
22
|
+
*
|
|
23
|
+
* `src/assets.ts` re-exports `fetchWithTimeout` so existing consumers
|
|
24
|
+
* (notably `test/security-ssrf.test.ts` and `test/assets-dns-dedup.test.ts`
|
|
25
|
+
* which import via `'../src/assets.js'` / `'../dist/assets.js'`) keep working.
|
|
26
|
+
* `createPinnedAgent` is intentionally NOT re-exported — it is a private
|
|
27
|
+
* implementation detail of the fetch primitive.
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* Build an undici Agent whose every TCP connection is pinned to the supplied
|
|
31
|
+
* pre-validated IP. Closes the DNS-rebinding TOCTOU window: even if DNS is
|
|
32
|
+
* re-resolved by the runtime mid-flight, the socket targets the IP we already
|
|
33
|
+
* confirmed is public.
|
|
34
|
+
*
|
|
35
|
+
* Caller MUST `close()` the agent after the fetch completes.
|
|
36
|
+
*
|
|
37
|
+
* LAZY: called only inside `fetchWithTimeout`, never at module load.
|
|
38
|
+
*/
|
|
39
|
+
function createPinnedAgent(ip, family) {
|
|
40
|
+
return new Agent({
|
|
41
|
+
connect: {
|
|
42
|
+
// Undici accepts a Node `dns.lookup`-compatible function here.
|
|
43
|
+
// We unconditionally return the pre-validated IP so a malicious DNS
|
|
44
|
+
// server cannot rebind to a private address between validation and
|
|
45
|
+
// connect.
|
|
46
|
+
lookup: (_hostname, _options, cb) => {
|
|
47
|
+
cb(null, ip, family);
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Fetch with a hard 10-second timeout AND a manual redirect chain that
|
|
54
|
+
* re-validates each hop against `resolveAndValidateUrl`. Without manual
|
|
55
|
+
* redirect handling, a public URL could 302 to `http://127.0.0.1:8080/internal`
|
|
56
|
+
* and bypass the upfront check — the connection would still be made to
|
|
57
|
+
* the private target.
|
|
58
|
+
*
|
|
59
|
+
* Each hop creates a fresh undici Agent that pins the socket to the IP
|
|
60
|
+
* that just passed validation. This defeats DNS-rebinding TOCTOU attacks
|
|
61
|
+
* where an attacker controls a TTL=0 DNS record and swaps the answer
|
|
62
|
+
* between our `dns.lookup()` and the actual TCP connect.
|
|
63
|
+
*/
|
|
64
|
+
export async function fetchWithTimeout(url, errorCode, label) {
|
|
65
|
+
const MAX_REDIRECTS = 3;
|
|
66
|
+
let currentUrl = url;
|
|
67
|
+
for (let hop = 0; hop <= MAX_REDIRECTS; hop++) {
|
|
68
|
+
const { url: parsed, ip, family } = await resolveAndValidateUrl(currentUrl, errorCode, label);
|
|
69
|
+
const controller = new AbortController();
|
|
70
|
+
const timer = setTimeout(() => controller.abort(), 10_000);
|
|
71
|
+
// Only create a pinned dispatcher when we have a resolved IP. For
|
|
72
|
+
// IP-literal hosts or DNS-unavailable cases we let undici handle
|
|
73
|
+
// resolution itself (a DNS failure there is already safe).
|
|
74
|
+
const pinnedAgent = ip && family ? createPinnedAgent(ip, family) : null;
|
|
75
|
+
let res;
|
|
76
|
+
try {
|
|
77
|
+
const fetchOpts = {
|
|
78
|
+
signal: controller.signal,
|
|
79
|
+
redirect: 'manual',
|
|
80
|
+
};
|
|
81
|
+
if (pinnedAgent) {
|
|
82
|
+
// `dispatcher` is an undici-specific extension; cast keeps fetch
|
|
83
|
+
// typings happy without leaking undici types into the public API.
|
|
84
|
+
;
|
|
85
|
+
fetchOpts.dispatcher = pinnedAgent;
|
|
86
|
+
}
|
|
87
|
+
res = (await undiciFetch(parsed.toString(), fetchOpts));
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
clearTimeout(timer);
|
|
91
|
+
if (pinnedAgent) {
|
|
92
|
+
// Don't block the caller on agent shutdown; swallow close errors.
|
|
93
|
+
void pinnedAgent.close().catch(() => undefined);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Undici fetch does not produce `opaqueredirect`, but keep parity with
|
|
97
|
+
// the browser-fetch contract: if we somehow get one, refuse.
|
|
98
|
+
if (res.type === 'opaqueredirect') {
|
|
99
|
+
throw new PretextPdfError(errorCode, `${label}: cannot follow opaque redirect. Pre-resolve the URL.`);
|
|
100
|
+
}
|
|
101
|
+
if (res.status >= 300 && res.status < 400) {
|
|
102
|
+
const loc = res.headers.get('Location');
|
|
103
|
+
if (!loc)
|
|
104
|
+
throw new PretextPdfError(errorCode, `${label}: redirect (${res.status}) with no Location header`);
|
|
105
|
+
currentUrl = new URL(loc, parsed).toString();
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
return res;
|
|
109
|
+
}
|
|
110
|
+
throw new PretextPdfError(errorCode, `${label}: too many redirects (max ${MAX_REDIRECTS})`);
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../../src/assets/security/fetch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,IAAI,WAAW,EAAE,MAAM,QAAQ,CAAA;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAE3D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH;;;;;;;;;GASG;AACH,SAAS,iBAAiB,CAAC,EAAU,EAAE,MAAa;IAClD,OAAO,IAAI,KAAK,CAAC;QACf,OAAO,EAAE;YACP,+DAA+D;YAC/D,oEAAoE;YACpE,mEAAmE;YACnE,WAAW;YACX,MAAM,EAAE,CACN,SAAiB,EACjB,QAAiB,EACjB,EAAgF,EAC1E,EAAE;gBACR,EAAE,CAAC,IAAI,EAAE,EAAE,EAAE,MAAM,CAAC,CAAA;YACtB,CAAC;SACF;KACF,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,GAAW,EACX,SAAkD,EAClD,KAAa;IAEb,MAAM,aAAa,GAAG,CAAC,CAAA;IACvB,IAAI,UAAU,GAAG,GAAG,CAAA;IACpB,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,IAAI,aAAa,EAAE,GAAG,EAAE,EAAE,CAAC;QAC9C,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,MAAM,qBAAqB,CAAC,UAAU,EAAE,SAAS,EAAE,KAAK,CAAC,CAAA;QAE7F,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;QACxC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,MAAM,CAAC,CAAA;QAE1D,kEAAkE;QAClE,iEAAiE;QACjE,2DAA2D;QAC3D,MAAM,WAAW,GAAG,EAAE,IAAI,MAAM,CAAC,CAAC,CAAC,iBAAiB,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;QAEvE,IAAI,GAAa,CAAA;QACjB,IAAI,CAAC;YACH,MAAM,SAAS,GAAsC;gBACnD,MAAM,EAAE,UAAU,CAAC,MAAM;gBACzB,QAAQ,EAAE,QAAQ;aACnB,CAAA;YACD,IAAI,WAAW,EAAE,CAAC;gBAChB,iEAAiE;gBACjE,kEAAkE;gBAClE,CAAC;gBAAC,SAA8C,CAAC,UAAU,GAAG,WAAW,CAAA;YAC3E,CAAC;YACD,GAAG,GAAG,CAAC,MAAM,WAAW,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,SAAS,CAAC,CAAwB,CAAA;QAChF,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAA;YACnB,IAAI,WAAW,EAAE,CAAC;gBAChB,kEAAkE;gBAClE,KAAK,WAAW,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;YACjD,CAAC;QACH,CAAC;QAED,uEAAuE;QACvE,6DAA6D;QAC7D,IAAI,GAAG,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;YAClC,MAAM,IAAI,eAAe,CAAC,SAAS,EAAE,GAAG,KAAK,uDAAuD,CAAC,CAAA;QACvG,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;YAC1C,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;YACvC,IAAI,CAAC,GAAG;gBAAE,MAAM,IAAI,eAAe,CAAC,SAAS,EAAE,GAAG,KAAK,eAAe,GAAG,CAAC,MAAM,2BAA2B,CAAC,CAAA;YAC5G,UAAU,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAA;YAC5C,SAAQ;QACV,CAAC;QACD,OAAO,GAAG,CAAA;IACZ,CAAC;IACD,MAAM,IAAI,eAAe,CAAC,SAAS,EAAE,GAAG,KAAK,6BAA6B,aAAa,GAAG,CAAC,CAAA;AAC7F,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize alternative IPv4 notations to dotted-decimal form so the
|
|
3
|
+
* private-range regexes catch them. WHATWG URL does NOT normalize these,
|
|
4
|
+
* so an attacker can use any of these forms to bypass `/^127\./`-style
|
|
5
|
+
* checks:
|
|
6
|
+
* - Pure decimal: `2130706433` → 127.0.0.1
|
|
7
|
+
* - Pure hex: `0x7f000001` → 127.0.0.1
|
|
8
|
+
* - Octal octet: `0177.0.0.1` → 127.0.0.1
|
|
9
|
+
* - Hex octet: `0x7f.0.0.1` → 127.0.0.1
|
|
10
|
+
* - Short form: `127.1`, `127.0.1` → 127.0.0.1
|
|
11
|
+
*
|
|
12
|
+
* Returns the dotted-decimal form when `hostname` is any valid IPv4
|
|
13
|
+
* representation, otherwise `null` (leaving the original hostname for
|
|
14
|
+
* DNS resolution / regex matching). Never throws.
|
|
15
|
+
*
|
|
16
|
+
* NOTE: parts with leading zeros (e.g. `008`) are parsed as octal per the
|
|
17
|
+
* traditional inet_aton rules — `008` is INVALID (8 is not an octal digit)
|
|
18
|
+
* so we return null. `010` parses as octal 8. This matches how Linux's
|
|
19
|
+
* getaddrinfo / inet_aton historically resolved these forms, which is what
|
|
20
|
+
* we need to defend against.
|
|
21
|
+
*
|
|
22
|
+
* Extracted from `src/assets.ts` in v1.6.0 commit 6/16 as part of the
|
|
23
|
+
* post-v1.5.2 assets.ts file-size sprint. Pure function — no module-level
|
|
24
|
+
* side effects. Re-exported from `src/assets.ts` so existing direct importers
|
|
25
|
+
* (`test/security-ipv4-bypass.test.ts`) keep working.
|
|
26
|
+
*/
|
|
27
|
+
export declare function normalizeIpv4Hostname(hostname: string): string | null;
|
|
28
|
+
//# sourceMappingURL=ipv4-normalize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ipv4-normalize.d.ts","sourceRoot":"","sources":["../../../src/assets/security/ipv4-normalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAuErE"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize alternative IPv4 notations to dotted-decimal form so the
|
|
3
|
+
* private-range regexes catch them. WHATWG URL does NOT normalize these,
|
|
4
|
+
* so an attacker can use any of these forms to bypass `/^127\./`-style
|
|
5
|
+
* checks:
|
|
6
|
+
* - Pure decimal: `2130706433` → 127.0.0.1
|
|
7
|
+
* - Pure hex: `0x7f000001` → 127.0.0.1
|
|
8
|
+
* - Octal octet: `0177.0.0.1` → 127.0.0.1
|
|
9
|
+
* - Hex octet: `0x7f.0.0.1` → 127.0.0.1
|
|
10
|
+
* - Short form: `127.1`, `127.0.1` → 127.0.0.1
|
|
11
|
+
*
|
|
12
|
+
* Returns the dotted-decimal form when `hostname` is any valid IPv4
|
|
13
|
+
* representation, otherwise `null` (leaving the original hostname for
|
|
14
|
+
* DNS resolution / regex matching). Never throws.
|
|
15
|
+
*
|
|
16
|
+
* NOTE: parts with leading zeros (e.g. `008`) are parsed as octal per the
|
|
17
|
+
* traditional inet_aton rules — `008` is INVALID (8 is not an octal digit)
|
|
18
|
+
* so we return null. `010` parses as octal 8. This matches how Linux's
|
|
19
|
+
* getaddrinfo / inet_aton historically resolved these forms, which is what
|
|
20
|
+
* we need to defend against.
|
|
21
|
+
*
|
|
22
|
+
* Extracted from `src/assets.ts` in v1.6.0 commit 6/16 as part of the
|
|
23
|
+
* post-v1.5.2 assets.ts file-size sprint. Pure function — no module-level
|
|
24
|
+
* side effects. Re-exported from `src/assets.ts` so existing direct importers
|
|
25
|
+
* (`test/security-ipv4-bypass.test.ts`) keep working.
|
|
26
|
+
*/
|
|
27
|
+
export function normalizeIpv4Hostname(hostname) {
|
|
28
|
+
if (!hostname || hostname.includes(':') || hostname.includes('['))
|
|
29
|
+
return null;
|
|
30
|
+
// Parse a single part as decimal / octal / hex per inet_aton semantics.
|
|
31
|
+
// Returns null on any malformed input (non-digit chars, out-of-range, etc.)
|
|
32
|
+
const parsePart = (part) => {
|
|
33
|
+
if (part.length === 0)
|
|
34
|
+
return null;
|
|
35
|
+
let radix = 10;
|
|
36
|
+
let body = part;
|
|
37
|
+
if (part.length > 1 && (part[0] === '0' || part[0] === '-')) {
|
|
38
|
+
// Reject negative parts outright
|
|
39
|
+
if (part[0] === '-')
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
if (part === '0')
|
|
43
|
+
return 0;
|
|
44
|
+
if (part.length >= 2 && (part[0] === '0') && (part[1] === 'x' || part[1] === 'X')) {
|
|
45
|
+
radix = 16;
|
|
46
|
+
body = part.slice(2);
|
|
47
|
+
if (body.length === 0)
|
|
48
|
+
return null;
|
|
49
|
+
if (!/^[0-9a-fA-F]+$/.test(body))
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
else if (part[0] === '0') {
|
|
53
|
+
radix = 8;
|
|
54
|
+
body = part.slice(1);
|
|
55
|
+
if (!/^[0-7]+$/.test(body))
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
if (!/^[0-9]+$/.test(part))
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const n = parseInt(body, radix);
|
|
63
|
+
if (!Number.isFinite(n) || n < 0)
|
|
64
|
+
return null;
|
|
65
|
+
return n;
|
|
66
|
+
};
|
|
67
|
+
const parts = hostname.split('.');
|
|
68
|
+
if (parts.length === 0 || parts.length > 4)
|
|
69
|
+
return null;
|
|
70
|
+
const parsed = [];
|
|
71
|
+
for (const p of parts) {
|
|
72
|
+
const n = parsePart(p);
|
|
73
|
+
if (n === null)
|
|
74
|
+
return null;
|
|
75
|
+
parsed.push(n);
|
|
76
|
+
}
|
|
77
|
+
// Apply inet_aton-style packing for short forms:
|
|
78
|
+
// 1 part: a → a (32-bit, all octets)
|
|
79
|
+
// 2 parts: a.b → a.0.0.0 | b (last covers low 24 bits)
|
|
80
|
+
// 3 parts: a.b.c → a.b.0.0 | c (last covers low 16 bits)
|
|
81
|
+
// 4 parts: a.b.c.d → each octet 0–255
|
|
82
|
+
let value;
|
|
83
|
+
if (parsed.length === 1) {
|
|
84
|
+
value = parsed[0];
|
|
85
|
+
// Single-int forms (decimal/hex) must fit in 32 bits; reject before the
|
|
86
|
+
// `>>> 0` truncation below would silently wrap.
|
|
87
|
+
if (value < 0 || value > 0xffffffff)
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
else if (parsed.length === 2) {
|
|
91
|
+
const [a, b] = parsed;
|
|
92
|
+
if (a > 0xff || b > 0xffffff)
|
|
93
|
+
return null;
|
|
94
|
+
value = (a << 24) >>> 0 | b;
|
|
95
|
+
}
|
|
96
|
+
else if (parsed.length === 3) {
|
|
97
|
+
const [a, b, c] = parsed;
|
|
98
|
+
if (a > 0xff || b > 0xff || c > 0xffff)
|
|
99
|
+
return null;
|
|
100
|
+
value = ((a << 24) >>> 0) | (b << 16) | c;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
const [a, b, c, d] = parsed;
|
|
104
|
+
if (a > 0xff || b > 0xff || c > 0xff || d > 0xff)
|
|
105
|
+
return null;
|
|
106
|
+
value = ((a << 24) >>> 0) | (b << 16) | (c << 8) | d;
|
|
107
|
+
}
|
|
108
|
+
// `|` in JS is a signed 32-bit op — coerce to unsigned for the final value
|
|
109
|
+
// so 0xFFFFFFFF reads as 4294967295 rather than -1.
|
|
110
|
+
value = value >>> 0;
|
|
111
|
+
// Final 32-bit range guard (covers single-int forms like `2130706433`)
|
|
112
|
+
if (value > 0xffffffff)
|
|
113
|
+
return null;
|
|
114
|
+
return `${(value >>> 24) & 0xff}.${(value >>> 16) & 0xff}.${(value >>> 8) & 0xff}.${value & 0xff}`;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=ipv4-normalize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ipv4-normalize.js","sourceRoot":"","sources":["../../../src/assets/security/ipv4-normalize.ts"],"names":[],"mappings":"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"}
|