@patrickdk77/office-defuse 0.0.0-stage → 0.1.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.
Files changed (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +317 -2
  3. package/dist/biff.d.ts +62 -0
  4. package/dist/biff.js +95 -0
  5. package/dist/cfb.d.ts +58 -0
  6. package/dist/cfb.js +456 -0
  7. package/dist/context.d.ts +66 -0
  8. package/dist/context.js +121 -0
  9. package/dist/crc32.d.ts +8 -0
  10. package/dist/crc32.js +58 -0
  11. package/dist/engine.d.ts +5 -0
  12. package/dist/engine.js +200 -0
  13. package/dist/errors.d.ts +19 -0
  14. package/dist/errors.js +33 -0
  15. package/dist/fields.d.ts +29 -0
  16. package/dist/fields.js +97 -0
  17. package/dist/findings.d.ts +25 -0
  18. package/dist/findings.js +188 -0
  19. package/dist/formats.d.ts +10 -0
  20. package/dist/formats.js +46 -0
  21. package/dist/formulas.d.ts +22 -0
  22. package/dist/formulas.js +77 -0
  23. package/dist/index.d.ts +5 -0
  24. package/dist/index.js +30 -0
  25. package/dist/index.mjs +3 -0
  26. package/dist/io.d.ts +23 -0
  27. package/dist/io.js +139 -0
  28. package/dist/links.d.ts +26 -0
  29. package/dist/links.js +145 -0
  30. package/dist/msg.d.ts +7 -0
  31. package/dist/msg.js +97 -0
  32. package/dist/objects.d.ts +42 -0
  33. package/dist/objects.js +134 -0
  34. package/dist/odf.d.ts +3 -0
  35. package/dist/odf.js +297 -0
  36. package/dist/offcrypto.d.ts +31 -0
  37. package/dist/offcrypto.js +408 -0
  38. package/dist/ole.d.ts +12 -0
  39. package/dist/ole.js +127 -0
  40. package/dist/oleobj.d.ts +74 -0
  41. package/dist/oleobj.js +198 -0
  42. package/dist/ooxml.d.ts +8 -0
  43. package/dist/ooxml.js +468 -0
  44. package/dist/package.d.ts +3 -0
  45. package/dist/package.js +21 -0
  46. package/dist/ppt.d.ts +8 -0
  47. package/dist/ppt.js +241 -0
  48. package/dist/rtf.d.ts +8 -0
  49. package/dist/rtf.js +539 -0
  50. package/dist/score.d.ts +17 -0
  51. package/dist/score.js +63 -0
  52. package/dist/types.d.ts +274 -0
  53. package/dist/types.js +152 -0
  54. package/dist/word.d.ts +9 -0
  55. package/dist/word.js +160 -0
  56. package/dist/xls.d.ts +27 -0
  57. package/dist/xls.js +174 -0
  58. package/dist/xlsb.d.ts +19 -0
  59. package/dist/xlsb.js +101 -0
  60. package/dist/xml.d.ts +256 -0
  61. package/dist/xml.js +1105 -0
  62. package/dist/zip-writer.d.ts +38 -0
  63. package/dist/zip-writer.js +182 -0
  64. package/dist/zip.d.ts +55 -0
  65. package/dist/zip.js +535 -0
  66. package/package.json +96 -4
@@ -0,0 +1,274 @@
1
+ /** High-level kind of a finding. */
2
+ export declare enum OfficeCategory {
3
+ Container = "CONTAINER",
4
+ Xml = "XML",
5
+ Limit = "LIMIT",
6
+ Package = "PACKAGE",
7
+ Encrypted = "ENCRYPTED",
8
+ Macro = "MACRO",
9
+ Ui = "UI",
10
+ Action = "ACTION",
11
+ Dde = "DDE",
12
+ Field = "FIELD",
13
+ External = "EXTERNAL",
14
+ Link = "LINK",
15
+ Embedded = "EMBEDDED",
16
+ Control = "CONTROL"
17
+ }
18
+ /** Exact variant of a finding. Some details appear under more than one category. */
19
+ export declare enum OfficeDetail {
20
+ NotZip = "NOT_ZIP",
21
+ AmbiguousEnd = "AMBIGUOUS_END",
22
+ MultiDisk = "MULTI_DISK",
23
+ DirectoryMismatch = "DIRECTORY_MISMATCH",
24
+ OutOfRange = "OUT_OF_RANGE",
25
+ Overlap = "OVERLAP",
26
+ HeaderMismatch = "HEADER_MISMATCH",
27
+ DuplicateName = "DUPLICATE_NAME",
28
+ UnsafeName = "UNSAFE_NAME",
29
+ NameEncoding = "NAME_ENCODING",
30
+ EncryptedEntry = "ENCRYPTED_ENTRY",
31
+ UnsupportedMethod = "UNSUPPORTED_METHOD",
32
+ Zip64Mismatch = "ZIP64_MISMATCH",
33
+ BadExtraField = "BAD_EXTRA_FIELD",
34
+ DataError = "DATA_ERROR",
35
+ LeadingBytes = "LEADING_BYTES",
36
+ HiddenData = "HIDDEN_DATA",
37
+ TrailingBytes = "TRAILING_BYTES",
38
+ BadHeader = "BAD_HEADER",
39
+ BrokenChain = "BROKEN_CHAIN",
40
+ ChainLoop = "CHAIN_LOOP",
41
+ DirectoryLoop = "DIRECTORY_LOOP",
42
+ BadEntry = "BAD_ENTRY",
43
+ Doctype = "DOCTYPE",
44
+ InternalSubset = "INTERNAL_SUBSET",
45
+ ExternalEntity = "EXTERNAL_ENTITY",
46
+ Malformed = "MALFORMED",
47
+ Encoding = "ENCODING",
48
+ FileSize = "FILE_SIZE",
49
+ EntryCount = "ENTRY_COUNT",
50
+ DecompressedSize = "DECOMPRESSED_SIZE",
51
+ TotalDecompressedSize = "TOTAL_DECOMPRESSED_SIZE",
52
+ XmlDepth = "XML_DEPTH",
53
+ XmlTokenSize = "XML_TOKEN_SIZE",
54
+ XmlEntityDepth = "XML_ENTITY_DEPTH",
55
+ XmlEntityExpansions = "XML_ENTITY_EXPANSIONS",
56
+ XmlEntitySize = "XML_ENTITY_SIZE",
57
+ RtfDepth = "RTF_DEPTH",
58
+ Time = "TIME",
59
+ Unrecognized = "UNRECOGNIZED",
60
+ AmbiguousFormat = "AMBIGUOUS_FORMAT",
61
+ Unreadable = "UNREADABLE",
62
+ DuplicateEntry = "DUPLICATE_ENTRY",
63
+ UntypedPart = "UNTYPED_PART",
64
+ UnreferencedPart = "UNREFERENCED_PART",
65
+ OdfLayout = "ODF_LAYOUT",
66
+ PasswordRequired = "PASSWORD_REQUIRED",
67
+ DefaultPassword = "DEFAULT_PASSWORD",
68
+ UserPassword = "USER_PASSWORD",
69
+ Rc4_40 = "RC4_40",
70
+ Rc4_128 = "RC4_128",
71
+ Rc4Other = "RC4_OTHER",
72
+ Aes128 = "AES_128",
73
+ Aes192 = "AES_192",
74
+ Aes256 = "AES_256",
75
+ UnsupportedAlgorithm = "UNSUPPORTED_ALGORITHM",
76
+ VbaProject = "VBA_PROJECT",
77
+ XlmMacrosheet = "XLM_MACROSHEET",
78
+ WordBasic = "WORDBASIC",
79
+ OdfBasic = "ODF_BASIC",
80
+ OdfScripts = "ODF_SCRIPTS",
81
+ OdfDialogs = "ODF_DIALOGS",
82
+ EventOnOpen = "EVENT_ON_OPEN",
83
+ Event = "EVENT",
84
+ Link = "LINK",
85
+ Ribbon = "RIBBON",
86
+ Callback = "CALLBACK",
87
+ OdfConfiguration = "ODF_CONFIGURATION",
88
+ RunProgram = "RUN_PROGRAM",
89
+ OleVerb = "OLE_VERB",
90
+ ObjectUpdate = "OBJECT_UPDATE",
91
+ Field = "FIELD",
92
+ Formula = "FORMULA",
93
+ Macrobutton = "MACROBUTTON",
94
+ Computed = "COMPUTED",
95
+ Template = "TEMPLATE",
96
+ Object = "OBJECT",
97
+ Image = "IMAGE",
98
+ Frame = "FRAME",
99
+ Include = "INCLUDE",
100
+ Workbook = "WORKBOOK",
101
+ Reload = "RELOAD",
102
+ Other = "OTHER",
103
+ LocalFile = "LOCAL_FILE",
104
+ Safe = "SAFE",
105
+ FileUrl = "FILE_URL",
106
+ NetworkPath = "NETWORK_PATH",
107
+ DataUrl = "DATA_URL",
108
+ ScriptUrl = "SCRIPT_URL",
109
+ OtherScheme = "OTHER_SCHEME",
110
+ Credentials = "CREDENTIALS",
111
+ IpHost = "IP_HOST",
112
+ LookalikeHost = "LOOKALIKE_HOST",
113
+ EncodedHost = "ENCODED_HOST",
114
+ Relative = "RELATIVE",
115
+ OleObject = "OLE_OBJECT",
116
+ Package = "PACKAGE",
117
+ OleFile = "OLE_FILE",
118
+ Applet = "APPLET",
119
+ Plugin = "PLUGIN",
120
+ Equation = "EQUATION",
121
+ Attachment = "ATTACHMENT",
122
+ ActiveX = "ACTIVEX"
123
+ }
124
+ export type OfficeFindingAction = 'reject' | 'strip' | 'info';
125
+ /**
126
+ * A finding as any package of the defuse family reports it. Category and detail are plain strings, since the string
127
+ * enums of two packages never match each other.
128
+ */
129
+ export interface DefuseFinding {
130
+ category: string;
131
+ detail: string;
132
+ /** Plain sentence, safe to show the uploader. */
133
+ description: string;
134
+ /** Default action after any caller override. */
135
+ action: 'reject' | 'strip' | 'info';
136
+ /** Where in the file, such as a part name or a relationship. */
137
+ location?: string;
138
+ /** Extra values: the target, the field code, the content type, counts. */
139
+ data?: Record<string, string | number>;
140
+ /**
141
+ * Set when the finding comes from inside a contained file: the file's name, with " > " between nesting levels.
142
+ * `location` then starts with where that file sits.
143
+ */
144
+ attachment?: string;
145
+ /** How much the finding adds to a score, set by the package that made it. */
146
+ weight?: number;
147
+ }
148
+ /** A finding office-defuse makes itself. */
149
+ export interface OfficeFinding extends DefuseFinding {
150
+ category: OfficeCategory;
151
+ detail: OfficeDetail;
152
+ }
153
+ export declare enum OfficeRisk {
154
+ None = "NONE",
155
+ Low = "LOW",
156
+ Medium = "MEDIUM",
157
+ High = "HIGH",
158
+ Critical = "CRITICAL",
159
+ /** The file could not be inspected: a refusal of the container or XML layer, or a limit, stopped the run. */
160
+ Unknown = "UNKNOWN"
161
+ }
162
+ export interface OfficeInspection {
163
+ status: 'clean' | 'strippable' | 'rejected';
164
+ /** 0 to 100, null when risk is UNKNOWN. */
165
+ score: number | null;
166
+ risk: OfficeRisk;
167
+ findings: DefuseFinding[];
168
+ /** The format the content says, as its usual file extension without the dot, such as 'docx' or 'ods'. '' when not recognized. */
169
+ format: string;
170
+ /** The MIME type of a file in that format. '' when not recognized. */
171
+ mimeType: string;
172
+ }
173
+ /** Random-access input. A buffer, a local file and an S3 ranged GetObject all fit. */
174
+ export interface ByteSource {
175
+ size(): Promise<number>;
176
+ read(offset: number, length: number): Promise<Uint8Array>;
177
+ close?(): Promise<void>;
178
+ }
179
+ /** Append-only output. Never seeks. */
180
+ export interface ByteSink {
181
+ write(chunk: Uint8Array): Promise<void>;
182
+ close(): Promise<void>;
183
+ /** Called instead of close() when copying to the sink fails, so a partial output is not taken for a whole one. */
184
+ abort?(error: unknown): Promise<void>;
185
+ }
186
+ export interface OfficeLimits {
187
+ /** Bytes in the upload. Not enforced unless set. */
188
+ fileSize?: number;
189
+ /** Entries in the ZIP central directory. Default 10,000. */
190
+ entries?: number;
191
+ /** Declared decompressed size of any one entry, in bytes. Default 512 MiB. */
192
+ decompressedBytes?: number;
193
+ /** Declared decompressed sizes of all entries together, in bytes. Default 2 GiB. */
194
+ totalDecompressedBytes?: number;
195
+ /** Element nesting depth in one XML part. Default 256. */
196
+ xmlDepth?: number;
197
+ /** Characters in one XML tag, comment, processing instruction or CDATA section. Default 16 Mi. */
198
+ xmlTokenSize?: number;
199
+ /** Entity references nested inside entity text. Default 16. */
200
+ xmlEntityDepth?: number;
201
+ /** Entity references expanded in the whole run. Default 100,000. */
202
+ xmlEntityExpansions?: number;
203
+ /** Characters of entity text expanded in the whole run. Default 16 Mi. */
204
+ xmlEntitySize?: number;
205
+ /** Group nesting in an RTF file, and destinations open at once. Default 256. */
206
+ rtfDepth?: number;
207
+ /** Milliseconds for the whole run. Not enforced unless set. */
208
+ timeMs?: number;
209
+ }
210
+ /** What a contained file plugin learns of the run, with nothing format-specific, so any package of the family can make one. */
211
+ export interface PluginContext {
212
+ /** The depth of the file itself: 0 for the upload, 1 for a file inside it, and so on, whatever the formats. */
213
+ depth: number;
214
+ /** When a time limit is set, the Date.now() value the whole run must finish by. Nested runs share it. */
215
+ deadline?: number;
216
+ }
217
+ export interface ContainedFile {
218
+ name?: string;
219
+ /** The MIME type the container declares for the file, such as a part's content type. */
220
+ declaredType?: string;
221
+ /** From the first bytes. */
222
+ sniffedType?: string;
223
+ /** Decoded size. */
224
+ size: number;
225
+ location: string;
226
+ /** The depth of the file itself: 0 for the upload, 1 for a file inside it, and so on, whatever the formats. */
227
+ depth: number;
228
+ /** Decoded bytes. */
229
+ source: ByteSource;
230
+ }
231
+ /** What a file plugin did, plus anything it found inside the file. */
232
+ export interface ContainedFileResult {
233
+ result: 'scrubbed' | 'passed' | 'removed';
234
+ /** Findings inside the file as it arrived. They go into the report of the file that contains it. */
235
+ findings?: DefuseFinding[];
236
+ /** Findings inside the bytes the plugin kept. They go into the report of the output. */
237
+ outputFindings?: DefuseFinding[];
238
+ }
239
+ export interface ContainedFilePlugin {
240
+ kind: 'file';
241
+ name: string;
242
+ accepts(file: ContainedFile): boolean | Promise<boolean>;
243
+ /** Writes the bytes to keep. Returning 'removed' drops the file after all. */
244
+ process(file: ContainedFile, sink: ByteSink, context: PluginContext): Promise<'scrubbed' | 'passed' | 'removed' | ContainedFileResult>;
245
+ }
246
+ export interface OfficeActionOverride {
247
+ category: OfficeCategory;
248
+ detail?: OfficeDetail;
249
+ action: OfficeFindingAction;
250
+ }
251
+ export interface OfficeScoreWeight {
252
+ category: OfficeCategory;
253
+ detail?: OfficeDetail;
254
+ weight: number;
255
+ }
256
+ /** Lower bound of each band. */
257
+ export interface OfficeScoreBands {
258
+ low: number;
259
+ medium: number;
260
+ high: number;
261
+ critical: number;
262
+ }
263
+ export interface OfficeOptions {
264
+ /**
265
+ * Tried first on an encrypted file: an encrypted OOXML package, or an .xls workbook. If it does not open the file,
266
+ * the password Excel opens it with by default, VelvetSweatshop, is tried. Pass it in code, never on a command line,
267
+ * where other users can read it.
268
+ */
269
+ password?: string;
270
+ limits?: OfficeLimits;
271
+ actionOverrides?: OfficeActionOverride[];
272
+ scoreWeights?: OfficeScoreWeight[];
273
+ scoreBands?: OfficeScoreBands;
274
+ }
package/dist/types.js ADDED
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OfficeRisk = exports.OfficeDetail = exports.OfficeCategory = void 0;
4
+ /** High-level kind of a finding. */
5
+ var OfficeCategory;
6
+ (function (OfficeCategory) {
7
+ OfficeCategory["Container"] = "CONTAINER";
8
+ OfficeCategory["Xml"] = "XML";
9
+ OfficeCategory["Limit"] = "LIMIT";
10
+ OfficeCategory["Package"] = "PACKAGE";
11
+ OfficeCategory["Encrypted"] = "ENCRYPTED";
12
+ OfficeCategory["Macro"] = "MACRO";
13
+ OfficeCategory["Ui"] = "UI";
14
+ OfficeCategory["Action"] = "ACTION";
15
+ OfficeCategory["Dde"] = "DDE";
16
+ OfficeCategory["Field"] = "FIELD";
17
+ OfficeCategory["External"] = "EXTERNAL";
18
+ OfficeCategory["Link"] = "LINK";
19
+ OfficeCategory["Embedded"] = "EMBEDDED";
20
+ OfficeCategory["Control"] = "CONTROL";
21
+ })(OfficeCategory || (exports.OfficeCategory = OfficeCategory = {}));
22
+ /** Exact variant of a finding. Some details appear under more than one category. */
23
+ var OfficeDetail;
24
+ (function (OfficeDetail) {
25
+ // CONTAINER
26
+ OfficeDetail["NotZip"] = "NOT_ZIP";
27
+ OfficeDetail["AmbiguousEnd"] = "AMBIGUOUS_END";
28
+ OfficeDetail["MultiDisk"] = "MULTI_DISK";
29
+ OfficeDetail["DirectoryMismatch"] = "DIRECTORY_MISMATCH";
30
+ OfficeDetail["OutOfRange"] = "OUT_OF_RANGE";
31
+ OfficeDetail["Overlap"] = "OVERLAP";
32
+ OfficeDetail["HeaderMismatch"] = "HEADER_MISMATCH";
33
+ OfficeDetail["DuplicateName"] = "DUPLICATE_NAME";
34
+ OfficeDetail["UnsafeName"] = "UNSAFE_NAME";
35
+ OfficeDetail["NameEncoding"] = "NAME_ENCODING";
36
+ OfficeDetail["EncryptedEntry"] = "ENCRYPTED_ENTRY";
37
+ OfficeDetail["UnsupportedMethod"] = "UNSUPPORTED_METHOD";
38
+ OfficeDetail["Zip64Mismatch"] = "ZIP64_MISMATCH";
39
+ OfficeDetail["BadExtraField"] = "BAD_EXTRA_FIELD";
40
+ OfficeDetail["DataError"] = "DATA_ERROR";
41
+ OfficeDetail["LeadingBytes"] = "LEADING_BYTES";
42
+ OfficeDetail["HiddenData"] = "HIDDEN_DATA";
43
+ OfficeDetail["TrailingBytes"] = "TRAILING_BYTES";
44
+ OfficeDetail["BadHeader"] = "BAD_HEADER";
45
+ OfficeDetail["BrokenChain"] = "BROKEN_CHAIN";
46
+ OfficeDetail["ChainLoop"] = "CHAIN_LOOP";
47
+ OfficeDetail["DirectoryLoop"] = "DIRECTORY_LOOP";
48
+ OfficeDetail["BadEntry"] = "BAD_ENTRY";
49
+ // XML
50
+ OfficeDetail["Doctype"] = "DOCTYPE";
51
+ OfficeDetail["InternalSubset"] = "INTERNAL_SUBSET";
52
+ OfficeDetail["ExternalEntity"] = "EXTERNAL_ENTITY";
53
+ OfficeDetail["Malformed"] = "MALFORMED";
54
+ OfficeDetail["Encoding"] = "ENCODING";
55
+ // LIMIT
56
+ OfficeDetail["FileSize"] = "FILE_SIZE";
57
+ OfficeDetail["EntryCount"] = "ENTRY_COUNT";
58
+ OfficeDetail["DecompressedSize"] = "DECOMPRESSED_SIZE";
59
+ OfficeDetail["TotalDecompressedSize"] = "TOTAL_DECOMPRESSED_SIZE";
60
+ OfficeDetail["XmlDepth"] = "XML_DEPTH";
61
+ OfficeDetail["XmlTokenSize"] = "XML_TOKEN_SIZE";
62
+ OfficeDetail["XmlEntityDepth"] = "XML_ENTITY_DEPTH";
63
+ OfficeDetail["XmlEntityExpansions"] = "XML_ENTITY_EXPANSIONS";
64
+ OfficeDetail["XmlEntitySize"] = "XML_ENTITY_SIZE";
65
+ OfficeDetail["RtfDepth"] = "RTF_DEPTH";
66
+ OfficeDetail["Time"] = "TIME";
67
+ // PACKAGE
68
+ OfficeDetail["Unrecognized"] = "UNRECOGNIZED";
69
+ OfficeDetail["AmbiguousFormat"] = "AMBIGUOUS_FORMAT";
70
+ OfficeDetail["Unreadable"] = "UNREADABLE";
71
+ OfficeDetail["DuplicateEntry"] = "DUPLICATE_ENTRY";
72
+ OfficeDetail["UntypedPart"] = "UNTYPED_PART";
73
+ OfficeDetail["UnreferencedPart"] = "UNREFERENCED_PART";
74
+ OfficeDetail["OdfLayout"] = "ODF_LAYOUT";
75
+ // ENCRYPTED
76
+ OfficeDetail["PasswordRequired"] = "PASSWORD_REQUIRED";
77
+ OfficeDetail["DefaultPassword"] = "DEFAULT_PASSWORD";
78
+ OfficeDetail["UserPassword"] = "USER_PASSWORD";
79
+ OfficeDetail["Rc4_40"] = "RC4_40";
80
+ OfficeDetail["Rc4_128"] = "RC4_128";
81
+ OfficeDetail["Rc4Other"] = "RC4_OTHER";
82
+ OfficeDetail["Aes128"] = "AES_128";
83
+ OfficeDetail["Aes192"] = "AES_192";
84
+ OfficeDetail["Aes256"] = "AES_256";
85
+ OfficeDetail["UnsupportedAlgorithm"] = "UNSUPPORTED_ALGORITHM";
86
+ // MACRO
87
+ OfficeDetail["VbaProject"] = "VBA_PROJECT";
88
+ OfficeDetail["XlmMacrosheet"] = "XLM_MACROSHEET";
89
+ OfficeDetail["WordBasic"] = "WORDBASIC";
90
+ OfficeDetail["OdfBasic"] = "ODF_BASIC";
91
+ OfficeDetail["OdfScripts"] = "ODF_SCRIPTS";
92
+ OfficeDetail["OdfDialogs"] = "ODF_DIALOGS";
93
+ OfficeDetail["EventOnOpen"] = "EVENT_ON_OPEN";
94
+ OfficeDetail["Event"] = "EVENT";
95
+ OfficeDetail["Link"] = "LINK";
96
+ // UI
97
+ OfficeDetail["Ribbon"] = "RIBBON";
98
+ OfficeDetail["Callback"] = "CALLBACK";
99
+ OfficeDetail["OdfConfiguration"] = "ODF_CONFIGURATION";
100
+ // ACTION
101
+ OfficeDetail["RunProgram"] = "RUN_PROGRAM";
102
+ OfficeDetail["OleVerb"] = "OLE_VERB";
103
+ OfficeDetail["ObjectUpdate"] = "OBJECT_UPDATE";
104
+ // DDE
105
+ OfficeDetail["Field"] = "FIELD";
106
+ OfficeDetail["Formula"] = "FORMULA";
107
+ // FIELD
108
+ OfficeDetail["Macrobutton"] = "MACROBUTTON";
109
+ OfficeDetail["Computed"] = "COMPUTED";
110
+ // EXTERNAL
111
+ OfficeDetail["Template"] = "TEMPLATE";
112
+ OfficeDetail["Object"] = "OBJECT";
113
+ OfficeDetail["Image"] = "IMAGE";
114
+ OfficeDetail["Frame"] = "FRAME";
115
+ OfficeDetail["Include"] = "INCLUDE";
116
+ OfficeDetail["Workbook"] = "WORKBOOK";
117
+ OfficeDetail["Reload"] = "RELOAD";
118
+ OfficeDetail["Other"] = "OTHER";
119
+ OfficeDetail["LocalFile"] = "LOCAL_FILE";
120
+ // LINK
121
+ OfficeDetail["Safe"] = "SAFE";
122
+ OfficeDetail["FileUrl"] = "FILE_URL";
123
+ OfficeDetail["NetworkPath"] = "NETWORK_PATH";
124
+ OfficeDetail["DataUrl"] = "DATA_URL";
125
+ OfficeDetail["ScriptUrl"] = "SCRIPT_URL";
126
+ OfficeDetail["OtherScheme"] = "OTHER_SCHEME";
127
+ OfficeDetail["Credentials"] = "CREDENTIALS";
128
+ OfficeDetail["IpHost"] = "IP_HOST";
129
+ OfficeDetail["LookalikeHost"] = "LOOKALIKE_HOST";
130
+ OfficeDetail["EncodedHost"] = "ENCODED_HOST";
131
+ OfficeDetail["Relative"] = "RELATIVE";
132
+ // EMBEDDED
133
+ OfficeDetail["OleObject"] = "OLE_OBJECT";
134
+ OfficeDetail["Package"] = "PACKAGE";
135
+ OfficeDetail["OleFile"] = "OLE_FILE";
136
+ OfficeDetail["Applet"] = "APPLET";
137
+ OfficeDetail["Plugin"] = "PLUGIN";
138
+ OfficeDetail["Equation"] = "EQUATION";
139
+ OfficeDetail["Attachment"] = "ATTACHMENT";
140
+ // CONTROL
141
+ OfficeDetail["ActiveX"] = "ACTIVEX";
142
+ })(OfficeDetail || (exports.OfficeDetail = OfficeDetail = {}));
143
+ var OfficeRisk;
144
+ (function (OfficeRisk) {
145
+ OfficeRisk["None"] = "NONE";
146
+ OfficeRisk["Low"] = "LOW";
147
+ OfficeRisk["Medium"] = "MEDIUM";
148
+ OfficeRisk["High"] = "HIGH";
149
+ OfficeRisk["Critical"] = "CRITICAL";
150
+ /** The file could not be inspected: a refusal of the container or XML layer, or a limit, stopped the run. */
151
+ OfficeRisk["Unknown"] = "UNKNOWN";
152
+ })(OfficeRisk || (exports.OfficeRisk = OfficeRisk = {}));
package/dist/word.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ import type { CfbEntry, CompoundFile } from './cfb';
2
+ import type { RunContext } from './context';
3
+ /**
4
+ * Inspects a Word 6.0 to 2003 document: its FIB, whether it is encrypted, the fields in its text and the template it
5
+ * attaches. Returns whether it is a template, or undefined for an `optional` stream that holds no FIB.
6
+ */
7
+ export declare function wordRules(ctx: RunContext, cfb: CompoundFile, stream: CfbEntry, optional?: boolean): Promise<{
8
+ template: boolean;
9
+ } | undefined>;
package/dist/word.js ADDED
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.wordRules = wordRules;
4
+ const errors_1 = require("./errors");
5
+ const fields_1 = require("./fields");
6
+ const io_1 = require("./io");
7
+ const oleobj_1 = require("./oleobj");
8
+ const types_1 = require("./types");
9
+ /** wIdent of Word 97 and later, and of Word 6.0 and 95 (MS-DOC 2.5.2). */
10
+ const WORD8 = 0xa5ec;
11
+ const WORD6 = 0xa5dc;
12
+ /** FibRgFcLcb97 pairs (MS-DOC 2.5.6): the attached template's table and the piece table. */
13
+ const FC_STTBF_ASSOC = 32;
14
+ const FC_CLX = 33;
15
+ /** In a Word 6.0 or 95 FIB, the length of the macro table that holds WordBasic macros. Word writes 2 for an empty one. */
16
+ const WORD6_MACROS = 0x11c;
17
+ /** Text is read in pieces of at most this many characters. */
18
+ const PIECE = 65536;
19
+ const u16 = (b, at) => b[at] | (b[at + 1] << 8);
20
+ const u32 = (b, at) => (b[at] | (b[at + 1] << 8) | (b[at + 2] << 16) | (b[at + 3] << 24)) >>> 0;
21
+ /**
22
+ * Inspects a Word 6.0 to 2003 document: its FIB, whether it is encrypted, the fields in its text and the template it
23
+ * attaches. Returns whether it is a template, or undefined for an `optional` stream that holds no FIB.
24
+ */
25
+ async function wordRules(ctx, cfb, stream, optional = false) {
26
+ const source = cfb.source(stream);
27
+ const fib = await source.read(0, 1024);
28
+ const ident = fib.length >= 32 ? u16(fib, 0) : 0;
29
+ if (ident !== WORD8 && ident !== WORD6) {
30
+ if (optional)
31
+ return undefined;
32
+ throw new errors_1.StopError(types_1.OfficeCategory.Package, types_1.OfficeDetail.Unrecognized, 'The WordDocument stream does not start with a Word FIB', { reason: 'a WordDocument stream without a FIB' });
33
+ }
34
+ const flags = u16(fib, 10);
35
+ // fEncrypted, set for both RC4 encryption and XOR obfuscation.
36
+ if (flags & 0x0100)
37
+ throw new errors_1.StopError(types_1.OfficeCategory.Encrypted, types_1.OfficeDetail.PasswordRequired, 'The document is encrypted', { stream: stream.name });
38
+ const location = stream.name;
39
+ const fields = new fields_1.FieldStack(ctx, location);
40
+ // Word marks a field's start, separator and end with 0x13, 0x14 and 0x15 in the text. Other control characters in
41
+ // a field's code, such as the 0x01 of a hyperlink's data, are not part of what it says.
42
+ const text = (s) => {
43
+ for (const ch of s) {
44
+ if (ch === '\x13')
45
+ fields.begin();
46
+ else if (ch === '\x14')
47
+ fields.separate();
48
+ else if (ch === '\x15')
49
+ fields.end();
50
+ else if (fields.inCode && (ch >= ' ' || ch === '\t'))
51
+ fields.text(ch);
52
+ }
53
+ };
54
+ const reader = new io_1.Reader(source, stream.size, 65536, 8);
55
+ if (ident === WORD6) {
56
+ // Word 6.0 and 95 keep their text as 8-bit characters from fcMin to fcMac. A file saved fast adds pieces to the
57
+ // end, and this reads every byte between, as msodde does.
58
+ const from = u32(fib, 0x18);
59
+ const to = Math.min(u32(fib, 0x1c), stream.size);
60
+ for (let at = from; at < to; at += PIECE) {
61
+ (0, errors_1.checkTime)(ctx.settings.deadline);
62
+ text(latin(await reader.read(at, Math.min(PIECE, to - at))));
63
+ }
64
+ if (fib.length >= WORD6_MACROS + 4 && u32(fib, WORD6_MACROS) > 2)
65
+ ctx.report(types_1.OfficeCategory.Macro, types_1.OfficeDetail.WordBasic, location, { stream: location, bytes: u32(fib, WORD6_MACROS) });
66
+ }
67
+ else {
68
+ const pairs = fcLcb(fib);
69
+ const table = cfb.child(cfb.root, flags & 0x0200 ? '1Table' : '0Table');
70
+ if (table?.kind === 'stream') {
71
+ const tableSource = cfb.source(table);
72
+ const clx = pairs[FC_CLX];
73
+ if (clx && clx.lcb > 0 && clx.fc + clx.lcb <= table.size) {
74
+ for (const p of pieces(await tableSource.read(clx.fc, clx.lcb))) {
75
+ for (let cp = 0; cp < p.length; cp += PIECE) {
76
+ (0, errors_1.checkTime)(ctx.settings.deadline);
77
+ const n = Math.min(PIECE, p.length - cp);
78
+ if (p.compressed)
79
+ text(latin(await reader.read(p.fc + cp, n)));
80
+ else
81
+ text((0, oleobj_1.utf16)(await reader.read(p.fc + cp * 2, n * 2)));
82
+ }
83
+ }
84
+ }
85
+ const assoc = pairs[FC_STTBF_ASSOC];
86
+ if (assoc && assoc.lcb > 0 && assoc.fc + assoc.lcb <= table.size) {
87
+ // The attached template is string 1 of SttbfAssoc (MS-DOC 2.9.271).
88
+ const template = sttb(await tableSource.read(assoc.fc, Math.min(assoc.lcb, 65536)))[1] ?? '';
89
+ if (template.trim())
90
+ ctx.external(types_1.OfficeDetail.Template, template, location, { source: 'SttbfAssoc' }, 'template');
91
+ }
92
+ }
93
+ }
94
+ fields.finish();
95
+ return { template: (flags & 1) === 1 };
96
+ }
97
+ /** The fc and lcb pairs of a Word 97 FIB, after its variable-length arrays of 16- and 32-bit values. */
98
+ function fcLcb(fib) {
99
+ const r = new oleobj_1.ByteReader(fib);
100
+ const out = [];
101
+ return (0, oleobj_1.bounded)(() => {
102
+ r.skip(32);
103
+ r.skip(r.u16() * 2);
104
+ r.skip(r.u16() * 4);
105
+ const n = r.u16();
106
+ for (let i = 0; i < n; i++)
107
+ out.push({ fc: r.u32(), lcb: r.u32() });
108
+ return out;
109
+ }, () => out);
110
+ }
111
+ /** The pieces of a Clx (MS-DOC 2.9.38): where each run of text is, and whether it is stored 8 bits a character. */
112
+ function pieces(clx) {
113
+ const r = new oleobj_1.ByteReader(clx);
114
+ const out = [];
115
+ return (0, oleobj_1.bounded)(() => {
116
+ // Prc entries of formatting come first, each with its size.
117
+ while (r.left > 0 && clx[r.pos] === 0x01) {
118
+ r.skip(1);
119
+ r.skip(r.u16());
120
+ }
121
+ if (r.u8() !== 0x02)
122
+ return out;
123
+ const lcb = r.u32();
124
+ const n = Math.floor((lcb - 4) / 12);
125
+ const plc = new oleobj_1.ByteReader(r.bytes(Math.min(lcb, r.left)));
126
+ const cps = [];
127
+ for (let i = 0; i <= n; i++)
128
+ cps.push(plc.u32());
129
+ for (let i = 0; i < n; i++) {
130
+ plc.skip(2);
131
+ const fc = plc.u32();
132
+ plc.skip(2);
133
+ const length = cps[i + 1] - cps[i];
134
+ if (length <= 0)
135
+ continue;
136
+ const compressed = (fc & 0x40000000) !== 0;
137
+ out.push({ fc: compressed ? (fc & 0x3fffffff) / 2 : fc, length, compressed });
138
+ }
139
+ return out;
140
+ }, () => []);
141
+ }
142
+ /** The strings of an STTB (MS-DOC 2.2.4), in 16-bit characters when it starts with 0xFFFF, as Word 97 writes one. */
143
+ function sttb(b) {
144
+ const r = new oleobj_1.ByteReader(b);
145
+ const out = [];
146
+ return (0, oleobj_1.bounded)(() => {
147
+ const wide = r.u16() === 0xffff;
148
+ if (!wide)
149
+ r.pos = 0;
150
+ const count = r.u16();
151
+ const extra = r.u16();
152
+ for (let i = 0; i < count; i++) {
153
+ const n = wide ? r.u16() : r.u8();
154
+ out.push(wide ? (0, oleobj_1.utf16)(r.bytes(n * 2)) : latin(r.bytes(n)));
155
+ r.skip(extra);
156
+ }
157
+ return out;
158
+ }, () => out);
159
+ }
160
+ const latin = (b) => Buffer.from(b.buffer, b.byteOffset, b.byteLength).toString('latin1');
package/dist/xls.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ import { type BiffRecord } from './biff';
2
+ import type { CfbEntry, CompoundFile } from './cfb';
3
+ import { type RunContext } from './context';
4
+ /** Turns an encrypted record body into its plain bytes. Without encryption the body is as stored. */
5
+ export type Decrypt = (r: BiffRecord) => Uint8Array;
6
+ /**
7
+ * Opens a workbook encrypted by its FILEPASS record, or says it cannot. Set by the decryption module, so this module
8
+ * reads workbooks without knowing how they are encrypted.
9
+ */
10
+ export type Unlock = (ctx: RunContext, filePass: BiffRecord, stream: string) => Decrypt | Promise<Decrypt>;
11
+ /**
12
+ * Inspects an Excel 5.0 to 2003 workbook stream. Excel reads the globals substream from the start of the stream,
13
+ * then each sheet from where its BoundSheet record says the sheet starts. The globals give the macro sheets, the
14
+ * names Excel runs as macros, and the DDE, OLE and workbook links. The sheets give shapes that run macros and
15
+ * hyperlinks. An encrypted workbook goes through `unlock`.
16
+ */
17
+ export declare function excelRules(ctx: RunContext, cfb: CompoundFile, stream: CfbEntry, unlock: Unlock): Promise<{
18
+ template: boolean;
19
+ addIn: boolean;
20
+ }>;
21
+ /**
22
+ * A VirtualPath (MS-XLS 2.5.277) written out. One that starts with U+0001 is encoded: its control characters name a
23
+ * volume, the root, a folder, a step up, a URL's volume or Excel's startup folders.
24
+ */
25
+ export declare function virtualPath(p: string): string;
26
+ /** The target of an HLink record: its Hyperlink Object (MS-OSHARED 2.3.7.1) after the cell range and class id. */
27
+ export declare function hyperlink(b: Uint8Array): string;