@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/README.md +15 -2
- package/dist/detect.d.ts +13 -0
- package/dist/detect.js +83 -0
- package/dist/engine.d.ts +55 -1
- package/dist/engine.js +22 -12
- package/dist/flat.d.ts +5 -5
- package/dist/flat.js +110 -41
- package/dist/formats.d.ts +2 -0
- package/dist/formats.js +3 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -1
- package/dist/index.mjs +1 -1
- package/dist/odf.d.ts +22 -0
- package/dist/odf.js +23 -12
- package/dist/ole.d.ts +19 -2
- package/dist/ole.js +81 -53
- package/dist/ooxml.d.ts +27 -2
- package/dist/ooxml.js +60 -38
- package/dist/package.d.ts +14 -0
- package/dist/package.js +23 -7
- package/dist/plugins.js +3 -28
- package/dist/rtf.js +2 -1
- package/dist/types.d.ts +23 -0
- package/dist/word.d.ts +4 -4
- package/dist/word.js +19 -6
- package/dist/xls.d.ts +10 -4
- package/dist/xls.js +20 -6
- package/dist/zip.d.ts +2 -0
- package/dist/zip.js +6 -2
- package/package.json +1 -1
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.
|
|
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
|
|
37
|
-
if (
|
|
50
|
+
const base = fibBase(fib);
|
|
51
|
+
if (!base) {
|
|
38
52
|
if (optional)
|
|
39
|
-
return
|
|
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 =
|
|
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.
|
|
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",
|