docxmlater 11.0.10 → 12.0.1
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/README.md +51 -13
- package/dist/core/Document.d.ts +28 -2
- package/dist/core/Document.d.ts.map +1 -1
- package/dist/core/Document.js +725 -170
- package/dist/core/Document.js.map +1 -1
- package/dist/core/DocumentGenerator.d.ts +1 -0
- package/dist/core/DocumentGenerator.d.ts.map +1 -1
- package/dist/core/DocumentGenerator.js +77 -25
- package/dist/core/DocumentGenerator.js.map +1 -1
- package/dist/core/DocumentParser.d.ts +7 -3
- package/dist/core/DocumentParser.d.ts.map +1 -1
- package/dist/core/DocumentParser.js +427 -81
- package/dist/core/DocumentParser.js.map +1 -1
- package/dist/core/RelationshipManager.d.ts.map +1 -1
- package/dist/core/RelationshipManager.js +2 -2
- package/dist/core/RelationshipManager.js.map +1 -1
- package/dist/elements/Comment.d.ts +15 -1
- package/dist/elements/Comment.d.ts.map +1 -1
- package/dist/elements/Comment.js +67 -12
- package/dist/elements/Comment.js.map +1 -1
- package/dist/elements/CommentManager.d.ts +4 -0
- package/dist/elements/CommentManager.d.ts.map +1 -1
- package/dist/elements/CommentManager.js +74 -16
- package/dist/elements/CommentManager.js.map +1 -1
- package/dist/elements/EndnoteManager.d.ts.map +1 -1
- package/dist/elements/EndnoteManager.js.map +1 -1
- package/dist/elements/Field.d.ts +10 -1
- package/dist/elements/Field.d.ts.map +1 -1
- package/dist/elements/Field.js +76 -34
- package/dist/elements/Field.js.map +1 -1
- package/dist/elements/FieldHelpers.d.ts +1 -0
- package/dist/elements/FieldHelpers.d.ts.map +1 -1
- package/dist/elements/FieldHelpers.js +8 -4
- package/dist/elements/FieldHelpers.js.map +1 -1
- package/dist/elements/Footer.d.ts +3 -1
- package/dist/elements/Footer.d.ts.map +1 -1
- package/dist/elements/Footer.js +39 -53
- package/dist/elements/Footer.js.map +1 -1
- package/dist/elements/FootnoteManager.d.ts.map +1 -1
- package/dist/elements/FootnoteManager.js.map +1 -1
- package/dist/elements/Header.d.ts +3 -1
- package/dist/elements/Header.d.ts.map +1 -1
- package/dist/elements/Header.js +39 -53
- package/dist/elements/Header.js.map +1 -1
- package/dist/elements/HeaderFooterManager.d.ts +4 -2
- package/dist/elements/HeaderFooterManager.d.ts.map +1 -1
- package/dist/elements/HeaderFooterManager.js +36 -6
- package/dist/elements/HeaderFooterManager.js.map +1 -1
- package/dist/elements/Hyperlink.d.ts +6 -1
- package/dist/elements/Hyperlink.d.ts.map +1 -1
- package/dist/elements/Hyperlink.js +69 -44
- package/dist/elements/Hyperlink.js.map +1 -1
- package/dist/elements/Image.d.ts.map +1 -1
- package/dist/elements/Image.js +33 -6
- package/dist/elements/Image.js.map +1 -1
- package/dist/elements/ImageManager.d.ts +1 -0
- package/dist/elements/ImageManager.d.ts.map +1 -1
- package/dist/elements/ImageManager.js +11 -0
- package/dist/elements/ImageManager.js.map +1 -1
- package/dist/elements/Paragraph.d.ts +3 -0
- package/dist/elements/Paragraph.d.ts.map +1 -1
- package/dist/elements/Paragraph.js +91 -26
- package/dist/elements/Paragraph.js.map +1 -1
- package/dist/elements/Revision.d.ts +4 -0
- package/dist/elements/Revision.d.ts.map +1 -1
- package/dist/elements/Revision.js +369 -29
- package/dist/elements/Revision.js.map +1 -1
- package/dist/elements/Run.d.ts +1 -0
- package/dist/elements/Run.d.ts.map +1 -1
- package/dist/elements/Run.js +24 -4
- package/dist/elements/Run.js.map +1 -1
- package/dist/elements/Section.d.ts.map +1 -1
- package/dist/elements/Section.js +17 -7
- package/dist/elements/Section.js.map +1 -1
- package/dist/elements/Shape.d.ts.map +1 -1
- package/dist/elements/Shape.js +50 -42
- package/dist/elements/Shape.js.map +1 -1
- package/dist/elements/StructuredDocumentTag.d.ts +9 -1
- package/dist/elements/StructuredDocumentTag.d.ts.map +1 -1
- package/dist/elements/StructuredDocumentTag.js +46 -6
- package/dist/elements/StructuredDocumentTag.js.map +1 -1
- package/dist/elements/Table.d.ts +2 -0
- package/dist/elements/Table.d.ts.map +1 -1
- package/dist/elements/Table.js +90 -42
- package/dist/elements/Table.js.map +1 -1
- package/dist/elements/TableCell.d.ts +1 -0
- package/dist/elements/TableCell.d.ts.map +1 -1
- package/dist/elements/TableCell.js +32 -13
- package/dist/elements/TableCell.js.map +1 -1
- package/dist/elements/TableOfContents.d.ts.map +1 -1
- package/dist/elements/TableOfContents.js +5 -7
- package/dist/elements/TableOfContents.js.map +1 -1
- package/dist/elements/TableRow.d.ts +1 -0
- package/dist/elements/TableRow.d.ts.map +1 -1
- package/dist/elements/TableRow.js +16 -1
- package/dist/elements/TableRow.js.map +1 -1
- package/dist/elements/TextBox.d.ts.map +1 -1
- package/dist/elements/TextBox.js +55 -42
- package/dist/elements/TextBox.js.map +1 -1
- package/dist/esm/core/Document.js +726 -171
- package/dist/esm/core/Document.js.map +1 -1
- package/dist/esm/core/DocumentGenerator.js +77 -25
- package/dist/esm/core/DocumentGenerator.js.map +1 -1
- package/dist/esm/core/DocumentParser.js +427 -81
- package/dist/esm/core/DocumentParser.js.map +1 -1
- package/dist/esm/core/RelationshipManager.js +2 -2
- package/dist/esm/core/RelationshipManager.js.map +1 -1
- package/dist/esm/elements/Comment.js +67 -12
- package/dist/esm/elements/Comment.js.map +1 -1
- package/dist/esm/elements/CommentManager.js +74 -16
- package/dist/esm/elements/CommentManager.js.map +1 -1
- package/dist/esm/elements/EndnoteManager.js.map +1 -1
- package/dist/esm/elements/Field.js +77 -35
- package/dist/esm/elements/Field.js.map +1 -1
- package/dist/esm/elements/FieldHelpers.js +7 -4
- package/dist/esm/elements/FieldHelpers.js.map +1 -1
- package/dist/esm/elements/Footer.js +39 -53
- package/dist/esm/elements/Footer.js.map +1 -1
- package/dist/esm/elements/FootnoteManager.js.map +1 -1
- package/dist/esm/elements/Header.js +39 -53
- package/dist/esm/elements/Header.js.map +1 -1
- package/dist/esm/elements/HeaderFooterManager.js +36 -6
- package/dist/esm/elements/HeaderFooterManager.js.map +1 -1
- package/dist/esm/elements/Hyperlink.js +69 -44
- package/dist/esm/elements/Hyperlink.js.map +1 -1
- package/dist/esm/elements/Image.js +33 -6
- package/dist/esm/elements/Image.js.map +1 -1
- package/dist/esm/elements/ImageManager.js +11 -0
- package/dist/esm/elements/ImageManager.js.map +1 -1
- package/dist/esm/elements/Paragraph.js +91 -26
- package/dist/esm/elements/Paragraph.js.map +1 -1
- package/dist/esm/elements/Revision.js +369 -29
- package/dist/esm/elements/Revision.js.map +1 -1
- package/dist/esm/elements/Run.js +24 -4
- package/dist/esm/elements/Run.js.map +1 -1
- package/dist/esm/elements/Section.js +17 -7
- package/dist/esm/elements/Section.js.map +1 -1
- package/dist/esm/elements/Shape.js +50 -42
- package/dist/esm/elements/Shape.js.map +1 -1
- package/dist/esm/elements/StructuredDocumentTag.js +46 -6
- package/dist/esm/elements/StructuredDocumentTag.js.map +1 -1
- package/dist/esm/elements/Table.js +90 -42
- package/dist/esm/elements/Table.js.map +1 -1
- package/dist/esm/elements/TableCell.js +32 -13
- package/dist/esm/elements/TableCell.js.map +1 -1
- package/dist/esm/elements/TableOfContents.js +5 -7
- package/dist/esm/elements/TableOfContents.js.map +1 -1
- package/dist/esm/elements/TableRow.js +16 -1
- package/dist/esm/elements/TableRow.js.map +1 -1
- package/dist/esm/elements/TextBox.js +55 -42
- package/dist/esm/elements/TextBox.js.map +1 -1
- package/dist/esm/formatting/AbstractNumbering.js +1 -1
- package/dist/esm/formatting/AbstractNumbering.js.map +1 -1
- package/dist/esm/formatting/NumberingLevel.js +8 -6
- package/dist/esm/formatting/NumberingLevel.js.map +1 -1
- package/dist/esm/formatting/NumberingManager.js +16 -1
- package/dist/esm/formatting/NumberingManager.js.map +1 -1
- package/dist/esm/formatting/Style.js +14 -0
- package/dist/esm/formatting/Style.js.map +1 -1
- package/dist/esm/formatting/StylesManager.js +14 -2
- package/dist/esm/formatting/StylesManager.js.map +1 -1
- package/dist/esm/helpers/CleanupHelper.js +103 -26
- package/dist/esm/helpers/CleanupHelper.js.map +1 -1
- package/dist/esm/images/ImageOptimizer.js +52 -4
- package/dist/esm/images/ImageOptimizer.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/internal.js +1 -1
- package/dist/esm/internal.js.map +1 -1
- package/dist/esm/processors/CompatibilityUpgrader.js +7 -1
- package/dist/esm/processors/CompatibilityUpgrader.js.map +1 -1
- package/dist/esm/processors/InMemoryRevisionAcceptor.js +134 -13
- package/dist/esm/processors/InMemoryRevisionAcceptor.js.map +1 -1
- package/dist/esm/processors/RevisionWalker.js +58 -8
- package/dist/esm/processors/RevisionWalker.js.map +1 -1
- package/dist/esm/processors/SelectiveRevisionAcceptor.js +116 -17
- package/dist/esm/processors/SelectiveRevisionAcceptor.js.map +1 -1
- package/dist/esm/processors/cnfStyleDecoder.js +7 -7
- package/dist/esm/processors/cnfStyleDecoder.js.map +1 -1
- package/dist/esm/processors/stripTrackedChanges.js +16 -5
- package/dist/esm/processors/stripTrackedChanges.js.map +1 -1
- package/dist/esm/utils/deepClone.js +24 -11
- package/dist/esm/utils/deepClone.js.map +1 -1
- package/dist/esm/utils/units.js +1 -1
- package/dist/esm/utils/units.js.map +1 -1
- package/dist/esm/utils/validation.js +11 -23
- package/dist/esm/utils/validation.js.map +1 -1
- package/dist/esm/utils/xmlSanitization.js +131 -7
- package/dist/esm/utils/xmlSanitization.js.map +1 -1
- package/dist/esm/validation/RevisionAutoFixer.js +69 -55
- package/dist/esm/validation/RevisionAutoFixer.js.map +1 -1
- package/dist/esm/validation/RevisionValidator.js +24 -20
- package/dist/esm/validation/RevisionValidator.js.map +1 -1
- package/dist/esm/xml/XMLBuilder.js +52 -20
- package/dist/esm/xml/XMLBuilder.js.map +1 -1
- package/dist/esm/xml/XMLParser.js +55 -13
- package/dist/esm/xml/XMLParser.js.map +1 -1
- package/dist/esm/zip/ZipHandler.js +37 -13
- package/dist/esm/zip/ZipHandler.js.map +1 -1
- package/dist/esm/zip/ZipReader.js +53 -6
- package/dist/esm/zip/ZipReader.js.map +1 -1
- package/dist/esm/zip/errors.js +7 -0
- package/dist/esm/zip/errors.js.map +1 -1
- package/dist/esm/zip/types.js +4 -0
- package/dist/esm/zip/types.js.map +1 -1
- package/dist/formatting/AbstractNumbering.d.ts.map +1 -1
- package/dist/formatting/AbstractNumbering.js +1 -1
- package/dist/formatting/AbstractNumbering.js.map +1 -1
- package/dist/formatting/NumberingLevel.d.ts +1 -1
- package/dist/formatting/NumberingLevel.d.ts.map +1 -1
- package/dist/formatting/NumberingLevel.js +8 -6
- package/dist/formatting/NumberingLevel.js.map +1 -1
- package/dist/formatting/NumberingManager.d.ts.map +1 -1
- package/dist/formatting/NumberingManager.js +16 -1
- package/dist/formatting/NumberingManager.js.map +1 -1
- package/dist/formatting/Style.d.ts.map +1 -1
- package/dist/formatting/Style.js +14 -0
- package/dist/formatting/Style.js.map +1 -1
- package/dist/formatting/StylesManager.d.ts +2 -0
- package/dist/formatting/StylesManager.d.ts.map +1 -1
- package/dist/formatting/StylesManager.js +14 -2
- package/dist/formatting/StylesManager.js.map +1 -1
- package/dist/helpers/CleanupHelper.d.ts.map +1 -1
- package/dist/helpers/CleanupHelper.js +103 -26
- package/dist/helpers/CleanupHelper.js.map +1 -1
- package/dist/images/ImageOptimizer.d.ts.map +1 -1
- package/dist/images/ImageOptimizer.js +52 -4
- package/dist/images/ImageOptimizer.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +2 -1
- package/dist/internal.js.map +1 -1
- package/dist/processors/CompatibilityUpgrader.d.ts.map +1 -1
- package/dist/processors/CompatibilityUpgrader.js +7 -1
- package/dist/processors/CompatibilityUpgrader.js.map +1 -1
- package/dist/processors/InMemoryRevisionAcceptor.d.ts +1 -0
- package/dist/processors/InMemoryRevisionAcceptor.d.ts.map +1 -1
- package/dist/processors/InMemoryRevisionAcceptor.js +135 -13
- package/dist/processors/InMemoryRevisionAcceptor.js.map +1 -1
- package/dist/processors/RevisionWalker.d.ts +2 -0
- package/dist/processors/RevisionWalker.d.ts.map +1 -1
- package/dist/processors/RevisionWalker.js +58 -8
- package/dist/processors/RevisionWalker.js.map +1 -1
- package/dist/processors/SelectiveRevisionAcceptor.d.ts.map +1 -1
- package/dist/processors/SelectiveRevisionAcceptor.js +116 -17
- package/dist/processors/SelectiveRevisionAcceptor.js.map +1 -1
- package/dist/processors/cnfStyleDecoder.d.ts.map +1 -1
- package/dist/processors/cnfStyleDecoder.js +7 -7
- package/dist/processors/cnfStyleDecoder.js.map +1 -1
- package/dist/processors/stripTrackedChanges.d.ts.map +1 -1
- package/dist/processors/stripTrackedChanges.js +16 -5
- package/dist/processors/stripTrackedChanges.js.map +1 -1
- package/dist/utils/deepClone.d.ts.map +1 -1
- package/dist/utils/deepClone.js +24 -11
- package/dist/utils/deepClone.js.map +1 -1
- package/dist/utils/units.d.ts.map +1 -1
- package/dist/utils/units.js +1 -1
- package/dist/utils/units.js.map +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +11 -23
- package/dist/utils/validation.js.map +1 -1
- package/dist/utils/xmlSanitization.d.ts +2 -0
- package/dist/utils/xmlSanitization.d.ts.map +1 -1
- package/dist/utils/xmlSanitization.js +133 -7
- package/dist/utils/xmlSanitization.js.map +1 -1
- package/dist/validation/RevisionAutoFixer.d.ts +6 -2
- package/dist/validation/RevisionAutoFixer.d.ts.map +1 -1
- package/dist/validation/RevisionAutoFixer.js +68 -54
- package/dist/validation/RevisionAutoFixer.js.map +1 -1
- package/dist/validation/RevisionValidator.d.ts +2 -0
- package/dist/validation/RevisionValidator.d.ts.map +1 -1
- package/dist/validation/RevisionValidator.js +26 -21
- package/dist/validation/RevisionValidator.js.map +1 -1
- package/dist/xml/XMLBuilder.d.ts +1 -0
- package/dist/xml/XMLBuilder.d.ts.map +1 -1
- package/dist/xml/XMLBuilder.js +52 -20
- package/dist/xml/XMLBuilder.js.map +1 -1
- package/dist/xml/XMLParser.d.ts +1 -0
- package/dist/xml/XMLParser.d.ts.map +1 -1
- package/dist/xml/XMLParser.js +55 -13
- package/dist/xml/XMLParser.js.map +1 -1
- package/dist/zip/ZipHandler.d.ts.map +1 -1
- package/dist/zip/ZipHandler.js +37 -13
- package/dist/zip/ZipHandler.js.map +1 -1
- package/dist/zip/ZipReader.d.ts.map +1 -1
- package/dist/zip/ZipReader.js +52 -5
- package/dist/zip/ZipReader.js.map +1 -1
- package/dist/zip/errors.d.ts +3 -0
- package/dist/zip/errors.d.ts.map +1 -1
- package/dist/zip/errors.js +9 -1
- package/dist/zip/errors.js.map +1 -1
- package/dist/zip/types.d.ts +4 -0
- package/dist/zip/types.d.ts.map +1 -1
- package/dist/zip/types.js +4 -0
- package/dist/zip/types.js.map +1 -1
- package/package.json +2 -2
- package/src/core/Document.ts +1159 -240
- package/src/core/DocumentGenerator.ts +106 -26
- package/src/core/DocumentParser.ts +681 -114
- package/src/core/RelationshipManager.ts +446 -442
- package/src/elements/Comment.ts +123 -18
- package/src/elements/CommentManager.ts +121 -22
- package/src/elements/EndnoteManager.ts +4 -1
- package/src/elements/Field.ts +134 -43
- package/src/elements/FieldHelpers.ts +20 -4
- package/src/elements/Footer.ts +62 -67
- package/src/elements/FootnoteManager.ts +4 -1
- package/src/elements/Header.ts +62 -67
- package/src/elements/HeaderFooterManager.ts +56 -8
- package/src/elements/Hyperlink.ts +150 -55
- package/src/elements/Image.ts +51 -7
- package/src/elements/ImageManager.ts +24 -0
- package/src/elements/Paragraph.ts +161 -40
- package/src/elements/Revision.ts +446 -47
- package/src/elements/Run.ts +44 -6
- package/src/elements/Section.ts +25 -9
- package/src/elements/Shape.ts +52 -44
- package/src/elements/StructuredDocumentTag.ts +83 -8
- package/src/elements/Table.ts +151 -50
- package/src/elements/TableCell.ts +57 -17
- package/src/elements/TableOfContents.ts +10 -9
- package/src/elements/TableRow.ts +35 -3
- package/src/elements/TextBox.ts +58 -43
- package/src/formatting/AbstractNumbering.ts +4 -2
- package/src/formatting/NumberingLevel.ts +50 -12
- package/src/formatting/NumberingManager.ts +32 -3
- package/src/formatting/Style.ts +14 -0
- package/src/formatting/StylesManager.ts +29 -2
- package/src/helpers/CleanupHelper.ts +122 -30
- package/src/images/ImageOptimizer.ts +64 -5
- package/src/index.ts +7 -0
- package/src/internal.ts +1 -0
- package/src/processors/CompatibilityUpgrader.ts +15 -2
- package/src/processors/InMemoryRevisionAcceptor.ts +276 -17
- package/src/processors/RevisionWalker.ts +83 -7
- package/src/processors/SelectiveRevisionAcceptor.ts +164 -28
- package/src/processors/cnfStyleDecoder.ts +18 -12
- package/src/processors/stripTrackedChanges.ts +47 -10
- package/src/utils/deepClone.ts +48 -14
- package/src/utils/units.ts +3 -1
- package/src/utils/validation.ts +30 -32
- package/src/utils/xmlSanitization.ts +218 -22
- package/src/validation/RevisionAutoFixer.ts +109 -65
- package/src/validation/RevisionValidator.ts +48 -23
- package/src/xml/XMLBuilder.ts +82 -22
- package/src/xml/XMLParser.ts +87 -20
- package/src/zip/ZipHandler.ts +61 -21
- package/src/zip/ZipReader.ts +135 -13
- package/src/zip/errors.ts +15 -0
- package/src/zip/types.ts +44 -2
package/src/zip/ZipReader.ts
CHANGED
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
import JSZip from 'jszip';
|
|
6
6
|
import { promises as fs } from 'fs';
|
|
7
|
-
import { ZipFile, FileMap, LoadOptions } from './types.js';
|
|
7
|
+
import { ZipFile, FileMap, LoadOptions, SizeLimitOptions, DEFAULT_SIZE_LIMITS } from './types.js';
|
|
8
8
|
import {
|
|
9
9
|
DocxNotFoundError,
|
|
10
10
|
InvalidDocxError,
|
|
11
11
|
CorruptedArchiveError,
|
|
12
12
|
FileOperationError,
|
|
13
|
+
ResourceLimitError,
|
|
13
14
|
} from './errors.js';
|
|
14
15
|
import {
|
|
15
16
|
validateDocxStructure,
|
|
@@ -18,6 +19,25 @@ import {
|
|
|
18
19
|
isValidZipBuffer,
|
|
19
20
|
} from '../utils/validation.js';
|
|
20
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Shape of JSZip's undocumented per-entry `_data` field, consulted only as an
|
|
24
|
+
* early-reject hint — never as the authoritative size guard (measured decompressed
|
|
25
|
+
* bytes are). Mirrors the commented-out `CompressedObject` interface in jszip's own
|
|
26
|
+
* `node_modules/jszip/index.d.ts`.
|
|
27
|
+
*
|
|
28
|
+
* [TS-5] Verified against jszip 3.10.x (package.json range `^3.10.1`). A minor jszip
|
|
29
|
+
* bump could rename or remove this field; if so the early-reject/ratio hints silently
|
|
30
|
+
* degrade to no-ops while the measured-byte accounting in {@link extractFiles} keeps
|
|
31
|
+
* enforcing the budgets. `tests/zip/JSZipInternalContract.test.ts` canaries the field
|
|
32
|
+
* so such a regression surfaces on upgrade.
|
|
33
|
+
*/
|
|
34
|
+
interface JSZipObjectPrivate {
|
|
35
|
+
_data?: {
|
|
36
|
+
compressedSize?: number;
|
|
37
|
+
uncompressedSize?: number;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
21
41
|
/**
|
|
22
42
|
* Handles reading operations on ZIP archives
|
|
23
43
|
*/
|
|
@@ -44,7 +64,14 @@ export class ZipReader {
|
|
|
44
64
|
const buffer = await fs.readFile(filePath);
|
|
45
65
|
await this.loadFromBuffer(buffer, options);
|
|
46
66
|
} catch (error: unknown) {
|
|
47
|
-
|
|
67
|
+
// Surface load-classification errors verbatim so callers can distinguish a
|
|
68
|
+
// missing/oversized/hostile archive from a generic read failure.
|
|
69
|
+
if (
|
|
70
|
+
error instanceof DocxNotFoundError ||
|
|
71
|
+
error instanceof ResourceLimitError ||
|
|
72
|
+
error instanceof InvalidDocxError ||
|
|
73
|
+
error instanceof CorruptedArchiveError
|
|
74
|
+
) {
|
|
48
75
|
throw error;
|
|
49
76
|
}
|
|
50
77
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -59,6 +86,7 @@ export class ZipReader {
|
|
|
59
86
|
*/
|
|
60
87
|
async loadFromBuffer(buffer: Buffer, options: LoadOptions = {}): Promise<void> {
|
|
61
88
|
const { validate = true } = options;
|
|
89
|
+
const limits: Required<SizeLimitOptions> = { ...DEFAULT_SIZE_LIMITS, ...options.sizeLimits };
|
|
62
90
|
|
|
63
91
|
try {
|
|
64
92
|
// Validate ZIP signature
|
|
@@ -69,8 +97,19 @@ export class ZipReader {
|
|
|
69
97
|
// Load ZIP archive
|
|
70
98
|
this.zip = await JSZip.loadAsync(buffer);
|
|
71
99
|
|
|
72
|
-
//
|
|
73
|
-
|
|
100
|
+
// Enumerate non-directory entries once; reused for the count guard below and
|
|
101
|
+
// for extraction, so the file list is not filtered twice.
|
|
102
|
+
const filePaths = Object.keys(this.zip.files).filter((path) => !this.zip!.files[path]!.dir);
|
|
103
|
+
|
|
104
|
+
// Reject entry-count amplification before decompressing anything.
|
|
105
|
+
if (limits.maxEntryCount > 0 && filePaths.length > limits.maxEntryCount) {
|
|
106
|
+
throw new ResourceLimitError(
|
|
107
|
+
`archive entry count (${filePaths.length}) exceeds maxEntryCount (${limits.maxEntryCount})`
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Extract all files (enforces uncompressed/ratio budgets while decompressing)
|
|
112
|
+
await this.extractFiles(limits, filePaths);
|
|
74
113
|
|
|
75
114
|
// Validate DOCX structure if requested
|
|
76
115
|
if (validate) {
|
|
@@ -79,7 +118,9 @@ export class ZipReader {
|
|
|
79
118
|
|
|
80
119
|
this.loaded = true;
|
|
81
120
|
} catch (error: unknown) {
|
|
82
|
-
|
|
121
|
+
// A resource-limit breach must not be masked as a generic corruption error;
|
|
122
|
+
// the cause (which budget was exceeded) is actionable for the caller.
|
|
123
|
+
if (error instanceof InvalidDocxError || error instanceof ResourceLimitError) {
|
|
83
124
|
throw error;
|
|
84
125
|
}
|
|
85
126
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -91,20 +132,41 @@ export class ZipReader {
|
|
|
91
132
|
* Extracts all files from the ZIP archive into memory
|
|
92
133
|
*
|
|
93
134
|
* **Encoding Note:**
|
|
94
|
-
* -
|
|
135
|
+
* - Known-text parts (.xml, .rels, etc.) are extracted as UTF-8 strings using `async('string')`
|
|
95
136
|
* - JSZip automatically decodes UTF-8 when extracting as 'string'
|
|
96
|
-
* -
|
|
137
|
+
* - Everything else is extracted as a Buffer to preserve exact bytes — a
|
|
138
|
+
* string decode is lossy (invalid UTF-8 becomes U+FFFD), which would
|
|
139
|
+
* corrupt embedded binary parts (OLE packages, fonts, metafiles)
|
|
97
140
|
* - All text content is guaranteed to be valid UTF-8
|
|
141
|
+
*
|
|
142
|
+
* **Resource limits:** the running total of *actual* decompressed bytes is the
|
|
143
|
+
* primary defense against high-ratio archives. JSZip's internal `_data` metadata
|
|
144
|
+
* is consulted only as an early-reject hint (it is undocumented and may be absent),
|
|
145
|
+
* never as the sole guard — every breach is re-checked against measured bytes.
|
|
146
|
+
*
|
|
147
|
+
* @param limits - Fully-resolved size limits to enforce while extracting
|
|
148
|
+
* @param filePaths - Pre-filtered non-directory entry paths (computed by the caller)
|
|
98
149
|
*/
|
|
99
|
-
private async extractFiles(
|
|
150
|
+
private async extractFiles(
|
|
151
|
+
limits: Required<SizeLimitOptions>,
|
|
152
|
+
filePaths: string[]
|
|
153
|
+
): Promise<void> {
|
|
100
154
|
if (!this.zip) {
|
|
101
155
|
throw new Error('ZIP archive not loaded');
|
|
102
156
|
}
|
|
103
157
|
|
|
104
158
|
this.files.clear();
|
|
105
159
|
|
|
106
|
-
|
|
107
|
-
|
|
160
|
+
const maxEntryBytes =
|
|
161
|
+
limits.maxEntryUncompressedMB > 0 ? limits.maxEntryUncompressedMB * 1024 * 1024 : 0;
|
|
162
|
+
const maxTotalBytes =
|
|
163
|
+
limits.maxTotalUncompressedMB > 0 ? limits.maxTotalUncompressedMB * 1024 * 1024 : 0;
|
|
164
|
+
// Only enforce the compression ratio on entries large enough for the ratio to be
|
|
165
|
+
// meaningful, so a small but highly-compressible part (e.g. tiny repetitive XML)
|
|
166
|
+
// cannot trip a false positive.
|
|
167
|
+
const ratioFloorBytes = 1024 * 1024;
|
|
168
|
+
|
|
169
|
+
let totalBytes = 0;
|
|
108
170
|
|
|
109
171
|
// Extract each file
|
|
110
172
|
for (const filePath of filePaths) {
|
|
@@ -115,20 +177,80 @@ export class ZipReader {
|
|
|
115
177
|
continue;
|
|
116
178
|
}
|
|
117
179
|
|
|
180
|
+
// Early-reject hint: JSZip exposes declared uncompressed/compressed sizes on a
|
|
181
|
+
// private `_data` field. Cast once per entry (see {@link JSZipObjectPrivate}) and
|
|
182
|
+
// use it only to avoid decompressing an obviously oversized entry; the
|
|
183
|
+
// authoritative check below uses the measured byte count.
|
|
184
|
+
const internalData = (zipObject as unknown as JSZipObjectPrivate)._data;
|
|
185
|
+
const declaredSize = internalData?.uncompressedSize;
|
|
186
|
+
if (typeof declaredSize === 'number' && declaredSize >= 0) {
|
|
187
|
+
if (maxEntryBytes > 0 && declaredSize > maxEntryBytes) {
|
|
188
|
+
throw new ResourceLimitError(
|
|
189
|
+
`entry "${normalizedPath}" uncompressed size (${declaredSize} bytes) exceeds ` +
|
|
190
|
+
`maxEntryUncompressedMB (${limits.maxEntryUncompressedMB}MB)`
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
if (maxTotalBytes > 0 && totalBytes + declaredSize > maxTotalBytes) {
|
|
194
|
+
throw new ResourceLimitError(
|
|
195
|
+
`total uncompressed size would exceed maxTotalUncompressedMB ` +
|
|
196
|
+
`(${limits.maxTotalUncompressedMB}MB)`
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
118
201
|
const isBinary = isBinaryFile(normalizedPath);
|
|
119
202
|
|
|
120
203
|
// Extract content based on type
|
|
121
|
-
// For text files: JSZip's async('string') automatically uses UTF-8 decoding
|
|
122
|
-
// For
|
|
204
|
+
// For known-text files: JSZip's async('string') automatically uses UTF-8 decoding
|
|
205
|
+
// For everything else: async('nodebuffer') preserves exact bytes
|
|
123
206
|
let content;
|
|
124
207
|
if (isBinary) {
|
|
125
208
|
content = await zipObject.async('nodebuffer');
|
|
126
209
|
} else {
|
|
127
|
-
//
|
|
210
|
+
// Known-text files are extracted as UTF-8 strings
|
|
128
211
|
// JSZip automatically handles UTF-8 decoding for 'string' type
|
|
129
212
|
content = await zipObject.async('string');
|
|
130
213
|
}
|
|
131
214
|
|
|
215
|
+
// Measure the *actual* decompressed byte count (UTF-8 for strings) — this is the
|
|
216
|
+
// primary, authoritative resource accounting that does not trust archive metadata.
|
|
217
|
+
const entryBytes = Buffer.isBuffer(content)
|
|
218
|
+
? content.length
|
|
219
|
+
: Buffer.byteLength(content, 'utf8');
|
|
220
|
+
|
|
221
|
+
if (maxEntryBytes > 0 && entryBytes > maxEntryBytes) {
|
|
222
|
+
throw new ResourceLimitError(
|
|
223
|
+
`entry "${normalizedPath}" uncompressed size (${entryBytes} bytes) exceeds ` +
|
|
224
|
+
`maxEntryUncompressedMB (${limits.maxEntryUncompressedMB}MB)`
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
totalBytes += entryBytes;
|
|
229
|
+
if (maxTotalBytes > 0 && totalBytes > maxTotalBytes) {
|
|
230
|
+
throw new ResourceLimitError(
|
|
231
|
+
`total uncompressed size (${totalBytes} bytes) exceeds maxTotalUncompressedMB ` +
|
|
232
|
+
`(${limits.maxTotalUncompressedMB}MB)`
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Compression-ratio guard for sizable entries, when the archive reports a
|
|
237
|
+
// compressed size we can divide by (reusing the single `_data` read above).
|
|
238
|
+
const compressedSize = internalData?.compressedSize;
|
|
239
|
+
if (
|
|
240
|
+
limits.maxCompressionRatio > 0 &&
|
|
241
|
+
entryBytes > ratioFloorBytes &&
|
|
242
|
+
typeof compressedSize === 'number' &&
|
|
243
|
+
compressedSize > 0
|
|
244
|
+
) {
|
|
245
|
+
const ratio = entryBytes / compressedSize;
|
|
246
|
+
if (ratio > limits.maxCompressionRatio) {
|
|
247
|
+
throw new ResourceLimitError(
|
|
248
|
+
`entry "${normalizedPath}" compression ratio (${ratio.toFixed(1)}:1) exceeds ` +
|
|
249
|
+
`maxCompressionRatio (${limits.maxCompressionRatio}:1)`
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
132
254
|
// Get file metadata
|
|
133
255
|
const date = zipObject.date;
|
|
134
256
|
|
package/src/zip/errors.ts
CHANGED
|
@@ -46,6 +46,21 @@ export class CorruptedArchiveError extends DocxError {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Error thrown when an archive breaches a configured resource limit.
|
|
51
|
+
*
|
|
52
|
+
* Distinct from {@link CorruptedArchiveError} so callers can tell a hostile/oversized
|
|
53
|
+
* input (a "zip bomb" style payload) apart from a genuinely malformed archive. The
|
|
54
|
+
* message names the specific limit that was exceeded so it is actionable.
|
|
55
|
+
*/
|
|
56
|
+
export class ResourceLimitError extends DocxError {
|
|
57
|
+
constructor(message: string) {
|
|
58
|
+
super(`Resource limit exceeded: ${message}`);
|
|
59
|
+
this.name = 'ResourceLimitError';
|
|
60
|
+
Object.setPrototypeOf(this, ResourceLimitError.prototype);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
49
64
|
/**
|
|
50
65
|
* Error thrown when a required DOCX file is missing
|
|
51
66
|
*/
|
package/src/zip/types.ts
CHANGED
|
@@ -28,18 +28,60 @@ export interface SizeLimitOptions {
|
|
|
28
28
|
*/
|
|
29
29
|
warningSizeMB?: number;
|
|
30
30
|
/**
|
|
31
|
-
* Maximum size in MB to load (default: 150 MB)
|
|
31
|
+
* Maximum *compressed* archive size in MB to load (default: 150 MB)
|
|
32
32
|
* Set to 0 to disable the limit (not recommended)
|
|
33
33
|
*/
|
|
34
34
|
maxSizeMB?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Maximum total *uncompressed* size in MB across all archive entries
|
|
37
|
+
* (default: 300 MB). This is the primary, authoritative defense against
|
|
38
|
+
* high-ratio "zip bomb" payloads whose compressed size is small: it is
|
|
39
|
+
* enforced against measured decompressed bytes, never archive-reported
|
|
40
|
+
* metadata. Set to 0 to disable.
|
|
41
|
+
*
|
|
42
|
+
* Note: decompression is non-streaming (JSZip buffers each entry fully), so
|
|
43
|
+
* peak transient heap can approach `maxTotalUncompressedMB + maxEntryUncompressedMB`
|
|
44
|
+
* (the already-accumulated total plus the entry being decoded) before a limit
|
|
45
|
+
* fires. Size the limits with that headroom in mind for memory-constrained hosts.
|
|
46
|
+
*/
|
|
47
|
+
maxTotalUncompressedMB?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Maximum *uncompressed* size in MB for any single archive entry
|
|
50
|
+
* (default: 150 MB). Set to 0 to disable.
|
|
51
|
+
*/
|
|
52
|
+
maxEntryUncompressedMB?: number;
|
|
53
|
+
/**
|
|
54
|
+
* Maximum number of entries (files) allowed in the archive
|
|
55
|
+
* (default: 2000). Guards against entry-count amplification. Set to 0 to disable.
|
|
56
|
+
*/
|
|
57
|
+
maxEntryCount?: number;
|
|
58
|
+
/**
|
|
59
|
+
* Maximum allowed per-entry compression ratio (uncompressed / compressed)
|
|
60
|
+
* (default: 200). Only enforced for sizable entries where the ratio is
|
|
61
|
+
* meaningful and the compressed size is reported by the archive.
|
|
62
|
+
*
|
|
63
|
+
* Best-effort: it relies on a compressed size that JSZip exposes only on an
|
|
64
|
+
* undocumented internal field, so it is silently skipped for any entry where
|
|
65
|
+
* that size is absent. `maxTotalUncompressedMB` is the authoritative guard
|
|
66
|
+
* against high-ratio payloads; treat this ratio limit as a defense-in-depth
|
|
67
|
+
* early reject, not a sole defense. Set to 0 to disable.
|
|
68
|
+
*/
|
|
69
|
+
maxCompressionRatio?: number;
|
|
35
70
|
}
|
|
36
71
|
|
|
37
72
|
/**
|
|
38
|
-
* Default size limits for document loading
|
|
73
|
+
* Default size limits for document loading.
|
|
74
|
+
*
|
|
75
|
+
* Defaults are intentionally generous so legitimate large documents load
|
|
76
|
+
* unchanged; tighten them via {@link LoadOptions.sizeLimits} for untrusted input.
|
|
39
77
|
*/
|
|
40
78
|
export const DEFAULT_SIZE_LIMITS: Required<SizeLimitOptions> = {
|
|
41
79
|
warningSizeMB: 50,
|
|
42
80
|
maxSizeMB: 150,
|
|
81
|
+
maxTotalUncompressedMB: 300,
|
|
82
|
+
maxEntryUncompressedMB: 150,
|
|
83
|
+
maxEntryCount: 2000,
|
|
84
|
+
maxCompressionRatio: 200,
|
|
43
85
|
};
|
|
44
86
|
|
|
45
87
|
/**
|