@patrickdk77/office-defuse 0.2.0 → 0.2.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/dist/word.js CHANGED
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isWordTemplate = isWordTemplate;
3
4
  exports.wordRules = wordRules;
4
5
  const errors_1 = require("./errors");
5
6
  const fields_1 = require("./fields");
@@ -26,20 +27,33 @@ const decrypted = (src, decrypt) => ({
26
27
  read: async (offset, length) => decrypt(offset, await src.read(offset, length)),
27
28
  });
28
29
  const u32 = (b, at) => (b[at] | (b[at + 1] << 8) | (b[at + 2] << 16) | (b[at + 3] << 24)) >>> 0;
30
+ /**
31
+ * The identifier and flags of the FIB a WordDocument stream starts with (MS-DOC 2.5.2), or undefined when it does not
32
+ * start with the FIB of Word 6.0 to 2003. The FIB's base is never encrypted.
33
+ */
34
+ function fibBase(fib) {
35
+ const ident = fib.length >= 32 ? u16(fib, 0) : 0;
36
+ return ident === WORD8 || ident === WORD6 ? { ident, flags: u16(fib, 10) } : undefined;
37
+ }
38
+ /** Whether a WordDocument stream holds a template: its FIB has fDot set. */
39
+ async function isWordTemplate(cfb, stream) {
40
+ const base = fibBase(await cfb.source(stream).read(0, 32));
41
+ return base !== undefined && (base.flags & 1) === 1;
42
+ }
29
43
  /**
30
44
  * Inspects a Word 6.0 to 2003 document: its FIB, whether it is encrypted, the fields in its text and the template it
31
- * attaches. Returns whether it is a template, or undefined for an `optional` stream that holds no FIB.
45
+ * attaches. An `optional` stream that holds no FIB is not read.
32
46
  */
33
47
  async function wordRules(ctx, cfb, stream, optional = false) {
34
48
  let source = cfb.source(stream);
35
49
  let fib = await source.read(0, 1024);
36
- const ident = fib.length >= 32 ? u16(fib, 0) : 0;
37
- if (ident !== WORD8 && ident !== WORD6) {
50
+ const base = fibBase(fib);
51
+ if (!base) {
38
52
  if (optional)
39
- return undefined;
53
+ return;
40
54
  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' });
41
55
  }
42
- const flags = u16(fib, 10);
56
+ const { ident, flags } = base;
43
57
  const tableEntry = cfb.child(cfb.root, flags & 0x0200 ? '1Table' : '0Table');
44
58
  let tableSource = tableEntry?.kind === 'stream' ? cfb.source(tableEntry) : undefined;
45
59
  // fEncrypted, set for RC4, RC4 CryptoAPI and XOR obfuscation alike. Word 97 to 2003 documents are read through the
@@ -126,7 +140,6 @@ async function wordRules(ctx, cfb, stream, optional = false) {
126
140
  }
127
141
  }
128
142
  fields.finish();
129
- return { template: (flags & 1) === 1 };
130
143
  }
131
144
  /** The fc and lcb pairs of a Word 97 FIB, after its variable-length arrays of 16- and 32-bit values. */
132
145
  function fcLcb(fib) {
package/dist/xls.d.ts CHANGED
@@ -8,16 +8,22 @@ export type Decrypt = (r: BiffRecord) => Uint8Array;
8
8
  * reads workbooks without knowing how they are encrypted.
9
9
  */
10
10
  export type Unlock = (ctx: RunContext, filePass: BiffRecord, stream: string) => Decrypt | Promise<Decrypt>;
11
+ /**
12
+ * Whether a workbook stream holds a template or an add-in: its globals substream, from the BOF that starts the stream
13
+ * to its EOF, holds a Template or an AddIn record. Record headers are never encrypted, so an encrypted workbook tells
14
+ * this without its password. A stream that does not start with a BOF record is neither.
15
+ */
16
+ export declare function workbookKind(cfb: CompoundFile, stream: CfbEntry, deadline?: number): Promise<{
17
+ template: boolean;
18
+ addIn: boolean;
19
+ }>;
11
20
  /**
12
21
  * Inspects an Excel 5.0 to 2003 workbook stream. Excel reads the globals substream from the start of the stream,
13
22
  * then each sheet from where its BoundSheet record says the sheet starts. The globals give the macro sheets, the
14
23
  * names Excel runs as macros, and the DDE, OLE and workbook links. The sheets give shapes that run macros and
15
24
  * hyperlinks. An encrypted workbook goes through `unlock`.
16
25
  */
17
- export declare function excelRules(ctx: RunContext, cfb: CompoundFile, stream: CfbEntry, unlock: Unlock): Promise<{
18
- template: boolean;
19
- addIn: boolean;
20
- }>;
26
+ export declare function excelRules(ctx: RunContext, cfb: CompoundFile, stream: CfbEntry, unlock: Unlock): Promise<void>;
21
27
  /**
22
28
  * A VirtualPath (MS-XLS 2.5.277) written out. One that starts with U+0001 is encoded: its control characters name a
23
29
  * volume, the root, a folder, a step up, a URL's volume or Excel's startup folders.
package/dist/xls.js CHANGED
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.workbookKind = workbookKind;
3
4
  exports.excelRules = excelRules;
4
5
  exports.virtualPath = virtualPath;
5
6
  exports.hyperlink = hyperlink;
@@ -21,6 +22,25 @@ const FORMULA_AT = new Map([
21
22
  [SHRFMLA, 8],
22
23
  [ARRAY, 12],
23
24
  ]);
25
+ /**
26
+ * Whether a workbook stream holds a template or an add-in: its globals substream, from the BOF that starts the stream
27
+ * to its EOF, holds a Template or an AddIn record. Record headers are never encrypted, so an encrypted workbook tells
28
+ * this without its password. A stream that does not start with a BOF record is neither.
29
+ */
30
+ async function workbookKind(cfb, stream, deadline) {
31
+ const kind = { template: false, addIn: false };
32
+ const records = new biff_1.RecordReader(cfb, stream, 0, deadline);
33
+ const first = await records.next();
34
+ if (first?.type !== biff_1.R.bof || first.body.length < 4)
35
+ return kind;
36
+ for (let r = await records.next(); r && r.type !== biff_1.R.eof; r = await records.next()) {
37
+ if (r.type === biff_1.R.template)
38
+ kind.template = true;
39
+ else if (r.type === biff_1.R.addIn)
40
+ kind.addIn = true;
41
+ }
42
+ return kind;
43
+ }
24
44
  /**
25
45
  * Inspects an Excel 5.0 to 2003 workbook stream. Excel reads the globals substream from the start of the stream,
26
46
  * then each sheet from where its BoundSheet record says the sheet starts. The globals give the macro sheets, the
@@ -40,7 +60,6 @@ async function excelRules(ctx, cfb, stream, unlock) {
40
60
  const names = [];
41
61
  /** The SupBook each XTI of the ExternSheet record names. */
42
62
  const xti = [];
43
- const flags = { template: false, addIn: false };
44
63
  for (let r = await records.next(); r && r.type !== biff_1.R.eof; r = await records.next()) {
45
64
  if (r.type === biff_1.R.filePass) {
46
65
  decrypt = await unlock(ctx, r, location);
@@ -49,10 +68,6 @@ async function excelRules(ctx, cfb, stream, unlock) {
49
68
  const body = decrypt(r);
50
69
  if (r.type === biff_1.R.boundSheet)
51
70
  sheets.push((0, biff_1.parseBoundSheet)(body, biff8));
52
- else if (r.type === biff_1.R.template)
53
- flags.template = true;
54
- else if (r.type === biff_1.R.addIn)
55
- flags.addIn = true;
56
71
  else if (r.type === biff_1.R.lbl) {
57
72
  const name = (0, biff_1.parseLbl)(body, biff8);
58
73
  names.push(name.name);
@@ -85,7 +100,6 @@ async function excelRules(ctx, cfb, stream, unlock) {
85
100
  const nameOf = (ref) => (ref.kind === 'name' ? names[ref.index - 1] : books[xti[ref.ixti] ?? -1]?.names[ref.index - 1]);
86
101
  for (const s of sheets)
87
102
  await sheetRules(ctx, cfb, stream, s, decrypt, location, biff8 ? nameOf : undefined);
88
- return flags;
89
103
  }
90
104
  /** A SupBook record, read as MS-XLS 2.4.271 and oletools tell its kinds apart. */
91
105
  function linkRules(ctx, b, location) {
package/dist/zip.d.ts CHANGED
@@ -48,6 +48,8 @@ export declare class ZipArchive {
48
48
  readonly entries: ZipEntry[];
49
49
  readonly notes: ZipNote[];
50
50
  private readonly deadline;
51
+ /** Where the archive's data starts: the first local header, or the central directory of an archive without entries. */
52
+ readonly start: number;
51
53
  private readonly byKey;
52
54
  private constructor();
53
55
  /** The entry with this name, compared as entry names collide: ignoring case. */
package/dist/zip.js CHANGED
@@ -241,12 +241,16 @@ class ZipArchive {
241
241
  entries;
242
242
  notes;
243
243
  deadline;
244
+ start;
244
245
  byKey = new Map();
245
- constructor(reader, entries, notes, deadline) {
246
+ constructor(reader, entries, notes, deadline,
247
+ /** Where the archive's data starts: the first local header, or the central directory of an archive without entries. */
248
+ start) {
246
249
  this.reader = reader;
247
250
  this.entries = entries;
248
251
  this.notes = notes;
249
252
  this.deadline = deadline;
253
+ this.start = start;
250
254
  for (const e of entries)
251
255
  this.byKey.set(nameKey(e.name), e);
252
256
  }
@@ -447,7 +451,7 @@ class ZipArchive {
447
451
  if (at !== undefined)
448
452
  notes.push({ detail: types_1.OfficeDetail.HiddenEntry, bytes: to - from, offset: at });
449
453
  }
450
- return new ZipArchive(reader, entries, notes, deadline);
454
+ return new ZipArchive(reader, entries, notes, deadline, sorted.length ? sorted[0].localOffset : dirOffset);
451
455
  }
452
456
  /** Checks a local header and any data descriptor against the central directory, and fills in where the entry's data and the entry end. */
453
457
  static async checkLocal(reader, e, rawName, dirOffset) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patrickdk77/office-defuse",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Inspect Office and OpenDocument files for VBA and Excel 4.0 macros, LibreOffice Basic and scripts, DDE, external templates and links, embedded OLE objects and ActiveX controls, and score the risk. Strict ZIP and XML readers, bounded memory, no runtime dependencies.",
5
5
  "license": "MIT",
6
6
  "author": "patrickdk77",