@patrickdk77/office-defuse 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +230 -2
- package/dist/context.d.ts +38 -0
- package/dist/context.js +100 -0
- package/dist/crc32.d.ts +2 -0
- package/dist/crc32.js +18 -0
- package/dist/engine.d.ts +5 -0
- package/dist/engine.js +151 -0
- package/dist/errors.d.ts +19 -0
- package/dist/errors.js +33 -0
- package/dist/findings.d.ts +25 -0
- package/dist/findings.js +169 -0
- package/dist/formats.d.ts +10 -0
- package/dist/formats.js +46 -0
- package/dist/formulas.d.ts +22 -0
- package/dist/formulas.js +77 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +30 -0
- package/dist/index.mjs +3 -0
- package/dist/io.d.ts +23 -0
- package/dist/io.js +139 -0
- package/dist/links.d.ts +26 -0
- package/dist/links.js +145 -0
- package/dist/odf.d.ts +3 -0
- package/dist/odf.js +297 -0
- package/dist/ooxml.d.ts +8 -0
- package/dist/ooxml.js +516 -0
- package/dist/score.d.ts +17 -0
- package/dist/score.js +63 -0
- package/dist/types.d.ts +247 -0
- package/dist/types.js +133 -0
- package/dist/xlsb.d.ts +19 -0
- package/dist/xlsb.js +101 -0
- package/dist/xml.d.ts +256 -0
- package/dist/xml.js +1105 -0
- package/dist/zip-writer.d.ts +38 -0
- package/dist/zip-writer.js +182 -0
- package/dist/zip.d.ts +55 -0
- package/dist/zip.js +535 -0
- package/package.json +96 -4
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { OfficeCategory as C, OfficeDetail as D, type OfficeActionOverride, type OfficeFinding, type OfficeFindingAction } from './types';
|
|
2
|
+
interface Spec {
|
|
3
|
+
action: OfficeFindingAction;
|
|
4
|
+
/**
|
|
5
|
+
* May contain <first|second> choices, which take the first alternative whose {key} values from the data are all
|
|
6
|
+
* set and not empty, or that names none. A value has control and format characters replaced by "?" and is cut to
|
|
7
|
+
* 60 characters.
|
|
8
|
+
*/
|
|
9
|
+
description: string;
|
|
10
|
+
weight: number;
|
|
11
|
+
}
|
|
12
|
+
/** The built-in spec for a kind of finding, or undefined for a kind office-defuse does not make, such as one from another package. */
|
|
13
|
+
export declare function findingSpec(category: string, detail: string): Spec | undefined;
|
|
14
|
+
/** Every category and detail pair office-defuse makes, with its default action, description and score weight. */
|
|
15
|
+
export declare function allFindingSpecs(): Array<{
|
|
16
|
+
category: C;
|
|
17
|
+
detail: D;
|
|
18
|
+
} & Spec>;
|
|
19
|
+
export declare class FindingFactory {
|
|
20
|
+
private readonly overrides;
|
|
21
|
+
constructor(overrides?: OfficeActionOverride[]);
|
|
22
|
+
private actionFor;
|
|
23
|
+
make(category: C, detail: D, location?: string, data?: Record<string, string | number>): OfficeFinding;
|
|
24
|
+
}
|
|
25
|
+
export {};
|
package/dist/findings.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FindingFactory = void 0;
|
|
4
|
+
exports.findingSpec = findingSpec;
|
|
5
|
+
exports.allFindingSpecs = allFindingSpecs;
|
|
6
|
+
const types_1 = require("./types");
|
|
7
|
+
const specs = new Map();
|
|
8
|
+
function def(c, d, action, weight, description) {
|
|
9
|
+
specs.set(`${c}/${d}`, { action, weight, description });
|
|
10
|
+
}
|
|
11
|
+
// CONTAINER: the ZIP layer. Every reject stops the run.
|
|
12
|
+
def(types_1.OfficeCategory.Container, types_1.OfficeDetail.NotZip, 'reject', 0, 'The file is not a ZIP archive, or it is cut short');
|
|
13
|
+
def(types_1.OfficeCategory.Container, types_1.OfficeDetail.AmbiguousEnd, 'reject', 0, 'The archive has more than one end record a reader could pick');
|
|
14
|
+
def(types_1.OfficeCategory.Container, types_1.OfficeDetail.MultiDisk, 'reject', 0, 'The archive claims to span several disks');
|
|
15
|
+
def(types_1.OfficeCategory.Container, types_1.OfficeDetail.DirectoryMismatch, 'reject', 0, "The archive's end record disagrees with its central directory");
|
|
16
|
+
def(types_1.OfficeCategory.Container, types_1.OfficeDetail.OutOfRange, 'reject', 0, 'An entry or the central directory lies outside the file');
|
|
17
|
+
def(types_1.OfficeCategory.Container, types_1.OfficeDetail.Overlap, 'reject', 0, 'Two entries in the archive share bytes');
|
|
18
|
+
def(types_1.OfficeCategory.Container, types_1.OfficeDetail.HeaderMismatch, 'reject', 0, 'A local header contradicts the central directory<: {name}|>');
|
|
19
|
+
def(types_1.OfficeCategory.Container, types_1.OfficeDetail.DuplicateName, 'reject', 0, 'Two entries have the same name, ignoring case<: {name}|>');
|
|
20
|
+
def(types_1.OfficeCategory.Container, types_1.OfficeDetail.UnsafeName, 'reject', 0, 'An entry name is unsafe to use as a path<: {name}|>');
|
|
21
|
+
def(types_1.OfficeCategory.Container, types_1.OfficeDetail.NameEncoding, 'reject', 0, 'An entry name can be read in more than one way');
|
|
22
|
+
def(types_1.OfficeCategory.Container, types_1.OfficeDetail.EncryptedEntry, 'reject', 0, 'An entry is encrypted<: {name}|>');
|
|
23
|
+
def(types_1.OfficeCategory.Container, types_1.OfficeDetail.UnsupportedMethod, 'reject', 0, 'An entry uses a compression method other than stored or deflate<: {name}|>');
|
|
24
|
+
def(types_1.OfficeCategory.Container, types_1.OfficeDetail.Zip64Mismatch, 'reject', 0, 'ZIP64 fields disagree with the fields they extend');
|
|
25
|
+
def(types_1.OfficeCategory.Container, types_1.OfficeDetail.BadExtraField, 'reject', 0, 'An extra field block does not parse<: {name}|>');
|
|
26
|
+
def(types_1.OfficeCategory.Container, types_1.OfficeDetail.DataError, 'reject', 0, "An entry's data does not match its declared size and checksum<: {name}|>");
|
|
27
|
+
def(types_1.OfficeCategory.Container, types_1.OfficeDetail.LeadingBytes, 'strip', 10, 'Data before the first entry of the archive');
|
|
28
|
+
def(types_1.OfficeCategory.Container, types_1.OfficeDetail.HiddenData, 'strip', 20, 'Data between entries of the archive that no entry covers');
|
|
29
|
+
def(types_1.OfficeCategory.Container, types_1.OfficeDetail.TrailingBytes, 'strip', 10, 'Data after the end of the archive');
|
|
30
|
+
// XML. MALFORMED and ENCODING stop the run.
|
|
31
|
+
def(types_1.OfficeCategory.Xml, types_1.OfficeDetail.Doctype, 'info', 0, 'An XML part declares a document type<: {part}|>');
|
|
32
|
+
def(types_1.OfficeCategory.Xml, types_1.OfficeDetail.InternalSubset, 'strip', 20, 'An XML part declares entities or other markup in its document type<: {part}|>');
|
|
33
|
+
def(types_1.OfficeCategory.Xml, types_1.OfficeDetail.ExternalEntity, 'strip', 40, 'An XML part takes content from <{target}|outside the file>');
|
|
34
|
+
def(types_1.OfficeCategory.Xml, types_1.OfficeDetail.Malformed, 'reject', 0, 'An XML part is not well-formed<: {part}|>');
|
|
35
|
+
def(types_1.OfficeCategory.Xml, types_1.OfficeDetail.Encoding, 'reject', 0, 'An XML part is not in valid UTF-8 or UTF-16<: {part}|>');
|
|
36
|
+
// LIMIT
|
|
37
|
+
def(types_1.OfficeCategory.Limit, types_1.OfficeDetail.FileSize, 'reject', 0, 'The file exceeds the file size limit');
|
|
38
|
+
def(types_1.OfficeCategory.Limit, types_1.OfficeDetail.EntryCount, 'reject', 0, 'The archive exceeds the entry count limit');
|
|
39
|
+
def(types_1.OfficeCategory.Limit, types_1.OfficeDetail.DecompressedSize, 'reject', 0, 'An entry exceeds the decompressed size limit');
|
|
40
|
+
def(types_1.OfficeCategory.Limit, types_1.OfficeDetail.TotalDecompressedSize, 'reject', 0, 'The archive exceeds the total decompressed size limit');
|
|
41
|
+
def(types_1.OfficeCategory.Limit, types_1.OfficeDetail.XmlDepth, 'reject', 0, 'An XML part exceeds the element depth limit');
|
|
42
|
+
def(types_1.OfficeCategory.Limit, types_1.OfficeDetail.XmlTokenSize, 'reject', 0, 'An XML part exceeds the token size limit');
|
|
43
|
+
def(types_1.OfficeCategory.Limit, types_1.OfficeDetail.XmlEntityDepth, 'reject', 0, 'An XML part exceeds the entity nesting limit');
|
|
44
|
+
def(types_1.OfficeCategory.Limit, types_1.OfficeDetail.XmlEntityExpansions, 'reject', 0, 'The file exceeds the entity expansion limit');
|
|
45
|
+
def(types_1.OfficeCategory.Limit, types_1.OfficeDetail.XmlEntitySize, 'reject', 0, 'The file exceeds the expanded entity size limit');
|
|
46
|
+
def(types_1.OfficeCategory.Limit, types_1.OfficeDetail.Time, 'reject', 0, 'The file exceeds the time limit');
|
|
47
|
+
// PACKAGE
|
|
48
|
+
def(types_1.OfficeCategory.Package, types_1.OfficeDetail.Unrecognized, 'reject', 0, 'The file is not a recognized Office or OpenDocument package');
|
|
49
|
+
def(types_1.OfficeCategory.Package, types_1.OfficeDetail.AmbiguousFormat, 'reject', 0, 'The file can be read as more than one format');
|
|
50
|
+
def(types_1.OfficeCategory.Package, types_1.OfficeDetail.Unreadable, 'reject', 0, 'The file could not be read');
|
|
51
|
+
def(types_1.OfficeCategory.Package, types_1.OfficeDetail.DuplicateEntry, 'reject', 0, 'The package names one thing twice, and readers may pick either');
|
|
52
|
+
def(types_1.OfficeCategory.Package, types_1.OfficeDetail.UntypedPart, 'strip', 10, 'A part has no content type<: {part}|>');
|
|
53
|
+
def(types_1.OfficeCategory.Package, types_1.OfficeDetail.UnreferencedPart, 'strip', 0, 'A part nothing in the package refers to<: {part}|>');
|
|
54
|
+
def(types_1.OfficeCategory.Package, types_1.OfficeDetail.OdfLayout, 'strip', 10, 'The OpenDocument package layout is irregular<: {reason}|>');
|
|
55
|
+
// ENCRYPTED
|
|
56
|
+
def(types_1.OfficeCategory.Encrypted, types_1.OfficeDetail.PasswordRequired, 'reject', 0, 'The document is encrypted with a password');
|
|
57
|
+
// MACRO
|
|
58
|
+
def(types_1.OfficeCategory.Macro, types_1.OfficeDetail.VbaProject, 'strip', 70, 'The document holds VBA macros');
|
|
59
|
+
def(types_1.OfficeCategory.Macro, types_1.OfficeDetail.XlmMacrosheet, 'strip', 70, 'The workbook holds an Excel 4.0 macro sheet');
|
|
60
|
+
def(types_1.OfficeCategory.Macro, types_1.OfficeDetail.OdfBasic, 'strip', 70, 'The document holds Basic macros');
|
|
61
|
+
def(types_1.OfficeCategory.Macro, types_1.OfficeDetail.OdfScripts, 'strip', 70, 'The document holds scripts');
|
|
62
|
+
def(types_1.OfficeCategory.Macro, types_1.OfficeDetail.OdfDialogs, 'strip', 30, 'The document holds Basic dialogs, which call macros');
|
|
63
|
+
def(types_1.OfficeCategory.Macro, types_1.OfficeDetail.EventOnOpen, 'strip', 70, 'A macro or script runs when the document opens');
|
|
64
|
+
def(types_1.OfficeCategory.Macro, types_1.OfficeDetail.Event, 'strip', 50, 'A macro or script runs on <the {event}|an> event');
|
|
65
|
+
def(types_1.OfficeCategory.Macro, types_1.OfficeDetail.Link, 'strip', 50, 'A link or action runs a macro or script');
|
|
66
|
+
// UI
|
|
67
|
+
def(types_1.OfficeCategory.Ui, types_1.OfficeDetail.Ribbon, 'info', 0, 'The document customizes the ribbon');
|
|
68
|
+
def(types_1.OfficeCategory.Ui, types_1.OfficeDetail.Callback, 'strip', 30, 'The custom ribbon calls a macro<: {callback}|>');
|
|
69
|
+
def(types_1.OfficeCategory.Ui, types_1.OfficeDetail.OdfConfiguration, 'strip', 10, 'The document carries its own menu, toolbar or key settings, which can call macros');
|
|
70
|
+
// ACTION
|
|
71
|
+
def(types_1.OfficeCategory.Action, types_1.OfficeDetail.RunProgram, 'strip', 80, 'A click or hover runs a program or opens a file');
|
|
72
|
+
def(types_1.OfficeCategory.Action, types_1.OfficeDetail.OleVerb, 'strip', 40, 'A click or hover activates an embedded object');
|
|
73
|
+
// DDE
|
|
74
|
+
def(types_1.OfficeCategory.Dde, types_1.OfficeDetail.Field, 'strip', 70, 'A field runs a command through DDE');
|
|
75
|
+
def(types_1.OfficeCategory.Dde, types_1.OfficeDetail.Link, 'strip', 70, 'A DDE link asks another program for data<: {application}|>');
|
|
76
|
+
def(types_1.OfficeCategory.Dde, types_1.OfficeDetail.Formula, 'strip', 70, 'A formula <takes text from external entity {entity}, so it may call DDE|asks another program for data through DDE>');
|
|
77
|
+
// FIELD
|
|
78
|
+
def(types_1.OfficeCategory.Field, types_1.OfficeDetail.Macrobutton, 'strip', 40, 'A field runs a macro when double-clicked');
|
|
79
|
+
def(types_1.OfficeCategory.Field, types_1.OfficeDetail.Computed, 'strip', 50, "A field's type <comes from external entity {entity}|is built by another field when it updates>");
|
|
80
|
+
// EXTERNAL
|
|
81
|
+
def(types_1.OfficeCategory.External, types_1.OfficeDetail.Template, 'strip', 60, 'The document loads its template from <{target}|outside the file>');
|
|
82
|
+
def(types_1.OfficeCategory.External, types_1.OfficeDetail.Object, 'strip', 60, 'A linked object loads from <{target}|outside the file>');
|
|
83
|
+
def(types_1.OfficeCategory.External, types_1.OfficeDetail.Image, 'strip', 30, 'An image loads from <{target}|outside the file>');
|
|
84
|
+
def(types_1.OfficeCategory.External, types_1.OfficeDetail.Frame, 'strip', 40, 'A frame shows <{target}|a page from outside the file>');
|
|
85
|
+
def(types_1.OfficeCategory.External, types_1.OfficeDetail.Include, 'strip', 40, 'Content is included from <{target}|another file>');
|
|
86
|
+
def(types_1.OfficeCategory.External, types_1.OfficeDetail.Workbook, 'strip', 30, 'Formulas read another workbook at <{target}|another location>');
|
|
87
|
+
def(types_1.OfficeCategory.External, types_1.OfficeDetail.Reload, 'strip', 40, 'The document loads <{target}|another address> on its own after a delay');
|
|
88
|
+
def(types_1.OfficeCategory.External, types_1.OfficeDetail.Other, 'strip', 30, 'The document refers to <{target}|something outside the file>');
|
|
89
|
+
def(types_1.OfficeCategory.External, types_1.OfficeDetail.LocalFile, 'strip', 5, 'The document refers to a local file<: {target}|>');
|
|
90
|
+
// LINK
|
|
91
|
+
def(types_1.OfficeCategory.Link, types_1.OfficeDetail.Safe, 'info', 0, 'Web or email link');
|
|
92
|
+
def(types_1.OfficeCategory.Link, types_1.OfficeDetail.FileUrl, 'strip', 60, 'A link opens a local file');
|
|
93
|
+
def(types_1.OfficeCategory.Link, types_1.OfficeDetail.NetworkPath, 'strip', 60, 'A link opens a network share');
|
|
94
|
+
def(types_1.OfficeCategory.Link, types_1.OfficeDetail.DataUrl, 'strip', 60, 'A link carries embedded data');
|
|
95
|
+
def(types_1.OfficeCategory.Link, types_1.OfficeDetail.ScriptUrl, 'strip', 50, 'A link uses a javascript: or vbscript: URL');
|
|
96
|
+
def(types_1.OfficeCategory.Link, types_1.OfficeDetail.OtherScheme, 'strip', 30, 'A link uses a scheme other than http, https or mailto');
|
|
97
|
+
def(types_1.OfficeCategory.Link, types_1.OfficeDetail.Credentials, 'strip', 40, 'A link hides its real host behind a user name');
|
|
98
|
+
def(types_1.OfficeCategory.Link, types_1.OfficeDetail.IpHost, 'strip', 40, 'A link points at a raw IP address');
|
|
99
|
+
def(types_1.OfficeCategory.Link, types_1.OfficeDetail.LookalikeHost, 'strip', 40, "A link's host uses punycode or mixed scripts");
|
|
100
|
+
def(types_1.OfficeCategory.Link, types_1.OfficeDetail.EncodedHost, 'strip', 40, "A link's host is percent-encoded");
|
|
101
|
+
def(types_1.OfficeCategory.Link, types_1.OfficeDetail.Relative, 'strip', 30, "A relative link, which resolves against the file's own location");
|
|
102
|
+
// EMBEDDED
|
|
103
|
+
def(types_1.OfficeCategory.Embedded, types_1.OfficeDetail.OleObject, 'strip', 40, 'An embedded OLE object');
|
|
104
|
+
def(types_1.OfficeCategory.Embedded, types_1.OfficeDetail.Package, 'strip', 30, 'An embedded document<: {contentType}|>');
|
|
105
|
+
def(types_1.OfficeCategory.Embedded, types_1.OfficeDetail.OleFile, 'strip', 40, 'A part holds an OLE file that nothing declares as an object');
|
|
106
|
+
def(types_1.OfficeCategory.Embedded, types_1.OfficeDetail.Applet, 'strip', 60, 'An embedded Java applet');
|
|
107
|
+
def(types_1.OfficeCategory.Embedded, types_1.OfficeDetail.Plugin, 'strip', 50, 'An embedded browser plug-in object');
|
|
108
|
+
// CONTROL
|
|
109
|
+
def(types_1.OfficeCategory.Control, types_1.OfficeDetail.ActiveX, 'strip', 50, 'An ActiveX control');
|
|
110
|
+
/** The built-in spec for a kind of finding, or undefined for a kind office-defuse does not make, such as one from another package. */
|
|
111
|
+
function findingSpec(category, detail) {
|
|
112
|
+
return specs.get(`${category}/${detail}`);
|
|
113
|
+
}
|
|
114
|
+
/** The spec of a kind office-defuse makes itself, which always has one. */
|
|
115
|
+
function ownSpec(category, detail) {
|
|
116
|
+
const s = findingSpec(category, detail);
|
|
117
|
+
if (!s)
|
|
118
|
+
throw new Error(`No finding spec for ${category}/${detail}`);
|
|
119
|
+
return s;
|
|
120
|
+
}
|
|
121
|
+
/** Every category and detail pair office-defuse makes, with its default action, description and score weight. */
|
|
122
|
+
function allFindingSpecs() {
|
|
123
|
+
return [...specs.entries()].map(([k, s]) => {
|
|
124
|
+
const [category, detail] = k.split('/');
|
|
125
|
+
return { category, detail, ...s };
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
/** Values from the file can hold control or direction characters, so the description shows none of them. */
|
|
129
|
+
const shown = (v) => String(v).replace(/[\p{Cc}\p{Cf}]/gu, '?');
|
|
130
|
+
class FindingFactory {
|
|
131
|
+
overrides;
|
|
132
|
+
constructor(overrides = []) {
|
|
133
|
+
this.overrides = overrides;
|
|
134
|
+
}
|
|
135
|
+
actionFor(category, detail) {
|
|
136
|
+
const exact = this.overrides.find(o => o.category === category && o.detail === detail);
|
|
137
|
+
if (exact)
|
|
138
|
+
return exact.action;
|
|
139
|
+
const byCategory = this.overrides.find(o => o.category === category && o.detail === undefined);
|
|
140
|
+
if (byCategory)
|
|
141
|
+
return byCategory.action;
|
|
142
|
+
return ownSpec(category, detail).action;
|
|
143
|
+
}
|
|
144
|
+
make(category, detail, location, data) {
|
|
145
|
+
const spec = ownSpec(category, detail);
|
|
146
|
+
const description = spec.description.replace(/<([^<>]*)>/g, (_match, body) => {
|
|
147
|
+
const fills = body.split('|').map(alt => {
|
|
148
|
+
let missing = false;
|
|
149
|
+
const text = alt.replace(/\{([a-zA-Z]+)\}/g, (_, key) => {
|
|
150
|
+
const v = data?.[key];
|
|
151
|
+
if (v === undefined || v === '')
|
|
152
|
+
missing = true;
|
|
153
|
+
const s = shown(v ?? '');
|
|
154
|
+
return s.length > 60 ? `${s.slice(0, 57)}...` : s;
|
|
155
|
+
});
|
|
156
|
+
return { text, missing };
|
|
157
|
+
});
|
|
158
|
+
// Every choice ends with an alternative that names no value, so one always fits.
|
|
159
|
+
return (fills.find(x => !x.missing) ?? fills[fills.length - 1]).text;
|
|
160
|
+
});
|
|
161
|
+
const f = { category, detail, description, action: this.actionFor(category, detail) };
|
|
162
|
+
if (location !== undefined)
|
|
163
|
+
f.location = location;
|
|
164
|
+
if (data !== undefined && Object.keys(data).length)
|
|
165
|
+
f.data = data;
|
|
166
|
+
return f;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
exports.FindingFactory = FindingFactory;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface Format {
|
|
2
|
+
/** The usual file extension, without the dot. */
|
|
3
|
+
format: string;
|
|
4
|
+
/** The MIME type of a file in this format. */
|
|
5
|
+
mimeType: string;
|
|
6
|
+
}
|
|
7
|
+
/** OOXML formats by the content type of the main part, lowercased. */
|
|
8
|
+
export declare const OOXML_FORMATS: Map<string, Format>;
|
|
9
|
+
/** OpenDocument formats by the media type in the mimetype entry or the manifest, exactly as written. */
|
|
10
|
+
export declare const ODF_FORMATS: Map<string, Format>;
|
package/dist/formats.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ODF_FORMATS = exports.OOXML_FORMATS = void 0;
|
|
4
|
+
const f = (format, mimeType) => ({ format, mimeType });
|
|
5
|
+
/** OOXML formats by the content type of the main part, lowercased. */
|
|
6
|
+
exports.OOXML_FORMATS = new Map([
|
|
7
|
+
['application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml', f('docx', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document')],
|
|
8
|
+
['application/vnd.ms-word.document.macroenabled.main+xml', f('docm', 'application/vnd.ms-word.document.macroEnabled.12')],
|
|
9
|
+
['application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml', f('dotx', 'application/vnd.openxmlformats-officedocument.wordprocessingml.template')],
|
|
10
|
+
['application/vnd.ms-word.template.macroenabledtemplate.main+xml', f('dotm', 'application/vnd.ms-word.template.macroEnabled.12')],
|
|
11
|
+
['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml', f('xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')],
|
|
12
|
+
['application/vnd.ms-excel.sheet.macroenabled.main+xml', f('xlsm', 'application/vnd.ms-excel.sheet.macroEnabled.12')],
|
|
13
|
+
['application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml', f('xltx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.template')],
|
|
14
|
+
['application/vnd.ms-excel.template.macroenabled.main+xml', f('xltm', 'application/vnd.ms-excel.template.macroEnabled.12')],
|
|
15
|
+
['application/vnd.ms-excel.addin.macroenabled.main+xml', f('xlam', 'application/vnd.ms-excel.addin.macroEnabled.12')],
|
|
16
|
+
['application/vnd.ms-excel.sheet.binary.macroenabled.main', f('xlsb', 'application/vnd.ms-excel.sheet.binary.macroEnabled.12')],
|
|
17
|
+
['application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml', f('pptx', 'application/vnd.openxmlformats-officedocument.presentationml.presentation')],
|
|
18
|
+
['application/vnd.ms-powerpoint.presentation.macroenabled.main+xml', f('pptm', 'application/vnd.ms-powerpoint.presentation.macroEnabled.12')],
|
|
19
|
+
['application/vnd.openxmlformats-officedocument.presentationml.template.main+xml', f('potx', 'application/vnd.openxmlformats-officedocument.presentationml.template')],
|
|
20
|
+
['application/vnd.ms-powerpoint.template.macroenabled.main+xml', f('potm', 'application/vnd.ms-powerpoint.template.macroEnabled.12')],
|
|
21
|
+
['application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml', f('ppsx', 'application/vnd.openxmlformats-officedocument.presentationml.slideshow')],
|
|
22
|
+
['application/vnd.ms-powerpoint.slideshow.macroenabled.main+xml', f('ppsm', 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12')],
|
|
23
|
+
['application/vnd.ms-powerpoint.addin.macroenabled.main+xml', f('ppam', 'application/vnd.ms-powerpoint.addin.macroEnabled.12')],
|
|
24
|
+
['application/vnd.openxmlformats-officedocument.presentationml.slide+xml', f('sldx', 'application/vnd.openxmlformats-officedocument.presentationml.slide')],
|
|
25
|
+
['application/vnd.ms-powerpoint.slide.macroenabled.main+xml', f('sldm', 'application/vnd.ms-powerpoint.slide.macroEnabled.12')],
|
|
26
|
+
['application/vnd.ms-visio.drawing.main+xml', f('vsdx', 'application/vnd.ms-visio.drawing')],
|
|
27
|
+
['application/vnd.ms-visio.drawing.macroenabled.main+xml', f('vsdm', 'application/vnd.ms-visio.drawing.macroEnabled.12')],
|
|
28
|
+
['application/vnd.ms-visio.template.main+xml', f('vstx', 'application/vnd.ms-visio.template')],
|
|
29
|
+
['application/vnd.ms-visio.template.macroenabled.main+xml', f('vstm', 'application/vnd.ms-visio.template.macroEnabled.12')],
|
|
30
|
+
['application/vnd.ms-visio.stencil.main+xml', f('vssx', 'application/vnd.ms-visio.stencil')],
|
|
31
|
+
['application/vnd.ms-visio.stencil.macroenabled.main+xml', f('vssm', 'application/vnd.ms-visio.stencil.macroEnabled.12')],
|
|
32
|
+
]);
|
|
33
|
+
/** OpenDocument formats by the media type in the mimetype entry or the manifest, exactly as written. */
|
|
34
|
+
exports.ODF_FORMATS = new Map([
|
|
35
|
+
['text', 'odt'],
|
|
36
|
+
['text-template', 'ott'],
|
|
37
|
+
['spreadsheet', 'ods'],
|
|
38
|
+
['spreadsheet-template', 'ots'],
|
|
39
|
+
['presentation', 'odp'],
|
|
40
|
+
['presentation-template', 'otp'],
|
|
41
|
+
['graphics', 'odg'],
|
|
42
|
+
['graphics-template', 'otg'],
|
|
43
|
+
['formula', 'odf'],
|
|
44
|
+
['text-master', 'odm'],
|
|
45
|
+
['base', 'odb'],
|
|
46
|
+
].map(([kind, ext]) => [`application/vnd.oasis.opendocument.${kind}`, f(ext, `application/vnd.oasis.opendocument.${kind}`)]));
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Follows the quotes of a formula one character at a time, as its text arrives: double-quoted strings and
|
|
3
|
+
* single-quoted names, where doubling the quote escapes it.
|
|
4
|
+
*/
|
|
5
|
+
export declare class Quotes {
|
|
6
|
+
private quote;
|
|
7
|
+
private closing;
|
|
8
|
+
/** Takes the next character and tells whether it stands outside quotes. */
|
|
9
|
+
outside(c: string): boolean;
|
|
10
|
+
}
|
|
11
|
+
/** Whether an OpenDocument formula calls the DDE function. */
|
|
12
|
+
export declare function callsDde(formula: string): boolean;
|
|
13
|
+
/** One word of a Word field code. A switch is an unquoted word that starts with a backslash. */
|
|
14
|
+
export interface FieldWord {
|
|
15
|
+
text: string;
|
|
16
|
+
quoted: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Splits a Word field code into words: quoted arguments, switches and bare words. Inside quotes a backslash escapes
|
|
20
|
+
* the next character; outside them a doubled backslash stands for one, as Word writes paths.
|
|
21
|
+
*/
|
|
22
|
+
export declare function fieldWords(code: string): FieldWord[];
|
package/dist/formulas.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Quotes = void 0;
|
|
4
|
+
exports.callsDde = callsDde;
|
|
5
|
+
exports.fieldWords = fieldWords;
|
|
6
|
+
/**
|
|
7
|
+
* Follows the quotes of a formula one character at a time, as its text arrives: double-quoted strings and
|
|
8
|
+
* single-quoted names, where doubling the quote escapes it.
|
|
9
|
+
*/
|
|
10
|
+
class Quotes {
|
|
11
|
+
quote = '';
|
|
12
|
+
closing = false;
|
|
13
|
+
/** Takes the next character and tells whether it stands outside quotes. */
|
|
14
|
+
outside(c) {
|
|
15
|
+
if (this.closing) {
|
|
16
|
+
this.closing = false;
|
|
17
|
+
if (c === this.quote)
|
|
18
|
+
return false;
|
|
19
|
+
this.quote = '';
|
|
20
|
+
}
|
|
21
|
+
if (this.quote) {
|
|
22
|
+
if (c === this.quote)
|
|
23
|
+
this.closing = true;
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
if (c === '"' || c === "'") {
|
|
27
|
+
this.quote = c;
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.Quotes = Quotes;
|
|
34
|
+
/** Whether an OpenDocument formula calls the DDE function. */
|
|
35
|
+
function callsDde(formula) {
|
|
36
|
+
const q = new Quotes();
|
|
37
|
+
for (let i = 0; i < formula.length; i++) {
|
|
38
|
+
if (q.outside(formula[i]) && /^DDE\s*\(/i.test(formula.slice(i, i + 16)) && !/[\w.]/.test(formula[i - 1] ?? ''))
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Splits a Word field code into words: quoted arguments, switches and bare words. Inside quotes a backslash escapes
|
|
45
|
+
* the next character; outside them a doubled backslash stands for one, as Word writes paths.
|
|
46
|
+
*/
|
|
47
|
+
function fieldWords(code) {
|
|
48
|
+
const out = [];
|
|
49
|
+
const space = (c) => c === ' ' || c === '\t' || c === '\n' || c === '\r' || c === '\xa0';
|
|
50
|
+
let i = 0;
|
|
51
|
+
while (i < code.length) {
|
|
52
|
+
while (i < code.length && space(code[i]))
|
|
53
|
+
i++;
|
|
54
|
+
if (i >= code.length)
|
|
55
|
+
break;
|
|
56
|
+
let text = '';
|
|
57
|
+
const quoted = code[i] === '"';
|
|
58
|
+
if (quoted) {
|
|
59
|
+
i++;
|
|
60
|
+
while (i < code.length && code[i] !== '"') {
|
|
61
|
+
if (code[i] === '\\' && i + 1 < code.length)
|
|
62
|
+
i++;
|
|
63
|
+
text += code[i++];
|
|
64
|
+
}
|
|
65
|
+
i++;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
while (i < code.length && !space(code[i])) {
|
|
69
|
+
if (code[i] === '\\' && code[i + 1] === '\\')
|
|
70
|
+
i++;
|
|
71
|
+
text += code[i++];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
out.push({ text, quoted });
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.scoreFindings = exports.DEFAULT_BANDS = exports.COMBINATIONS = exports.fileSource = exports.bufferSource = exports.allFindingSpecs = exports.inspectOfficeSource = exports.inspectOffice = void 0;
|
|
18
|
+
var engine_1 = require("./engine");
|
|
19
|
+
Object.defineProperty(exports, "inspectOffice", { enumerable: true, get: function () { return engine_1.inspectOffice; } });
|
|
20
|
+
Object.defineProperty(exports, "inspectOfficeSource", { enumerable: true, get: function () { return engine_1.inspectOfficeSource; } });
|
|
21
|
+
var findings_1 = require("./findings");
|
|
22
|
+
Object.defineProperty(exports, "allFindingSpecs", { enumerable: true, get: function () { return findings_1.allFindingSpecs; } });
|
|
23
|
+
var io_1 = require("./io");
|
|
24
|
+
Object.defineProperty(exports, "bufferSource", { enumerable: true, get: function () { return io_1.bufferSource; } });
|
|
25
|
+
Object.defineProperty(exports, "fileSource", { enumerable: true, get: function () { return io_1.fileSource; } });
|
|
26
|
+
var score_1 = require("./score");
|
|
27
|
+
Object.defineProperty(exports, "COMBINATIONS", { enumerable: true, get: function () { return score_1.COMBINATIONS; } });
|
|
28
|
+
Object.defineProperty(exports, "DEFAULT_BANDS", { enumerable: true, get: function () { return score_1.DEFAULT_BANDS; } });
|
|
29
|
+
Object.defineProperty(exports, "scoreFindings", { enumerable: true, get: function () { return score_1.scoreFindings; } });
|
|
30
|
+
__exportStar(require("./types"), exports);
|
package/dist/index.mjs
ADDED
package/dist/io.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ByteSource } from './types';
|
|
2
|
+
/** A source over bytes already in memory. Reads return views, not copies. */
|
|
3
|
+
export declare function bufferSource(bytes: Uint8Array): ByteSource;
|
|
4
|
+
/** A source over a local file, read at offsets through one file handle. */
|
|
5
|
+
export declare function fileSource(filePath: string): ByteSource;
|
|
6
|
+
/**
|
|
7
|
+
* Random-access reader with a small block cache, so the ZIP reader can ask for small overlapping windows cheaply. Its
|
|
8
|
+
* reads span at most a few blocks: a header, a name and its extra field, or the end of the file.
|
|
9
|
+
*/
|
|
10
|
+
export declare class Reader {
|
|
11
|
+
readonly source: ByteSource;
|
|
12
|
+
readonly size: number;
|
|
13
|
+
private readonly blockSize;
|
|
14
|
+
private readonly maxBlocks;
|
|
15
|
+
/** Blocks by index, oldest first. */
|
|
16
|
+
private readonly cache;
|
|
17
|
+
constructor(source: ByteSource, size: number, blockSize?: number, maxBlocks?: number);
|
|
18
|
+
private block;
|
|
19
|
+
/** Reads up to `length` bytes at `offset`; shorter only at the end of the source. */
|
|
20
|
+
read(offset: number, length: number): Promise<Uint8Array>;
|
|
21
|
+
/** Yields the range in chunks without caching them. */
|
|
22
|
+
chunks(offset: number, length: number, chunkSize?: number): AsyncGenerator<Uint8Array>;
|
|
23
|
+
}
|
package/dist/io.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.Reader = void 0;
|
|
37
|
+
exports.bufferSource = bufferSource;
|
|
38
|
+
exports.fileSource = fileSource;
|
|
39
|
+
const fsp = __importStar(require("node:fs/promises"));
|
|
40
|
+
/** A source over bytes already in memory. Reads return views, not copies. */
|
|
41
|
+
function bufferSource(bytes) {
|
|
42
|
+
return {
|
|
43
|
+
size: async () => bytes.length,
|
|
44
|
+
read: async (offset, length) => bytes.subarray(Math.max(0, offset), Math.min(bytes.length, offset + length)),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** A source over a local file, read at offsets through one file handle. */
|
|
48
|
+
function fileSource(filePath) {
|
|
49
|
+
// The pending open is shared, so concurrent first calls get one handle and close() closes it.
|
|
50
|
+
let opening;
|
|
51
|
+
let size;
|
|
52
|
+
const open = () => {
|
|
53
|
+
opening ??= fsp.open(filePath, 'r');
|
|
54
|
+
return opening;
|
|
55
|
+
};
|
|
56
|
+
const getSize = async () => {
|
|
57
|
+
size ??= (await (await open()).stat()).size;
|
|
58
|
+
return size;
|
|
59
|
+
};
|
|
60
|
+
return {
|
|
61
|
+
size: getSize,
|
|
62
|
+
async read(offset, length) {
|
|
63
|
+
const h = await open();
|
|
64
|
+
const total = await getSize();
|
|
65
|
+
const n = Math.max(0, Math.min(length, total - offset));
|
|
66
|
+
const buf = Buffer.allocUnsafe(n);
|
|
67
|
+
let done = 0;
|
|
68
|
+
while (done < n) {
|
|
69
|
+
const { bytesRead } = await h.read(buf, done, n - done, offset + done);
|
|
70
|
+
if (bytesRead === 0)
|
|
71
|
+
break;
|
|
72
|
+
done += bytesRead;
|
|
73
|
+
}
|
|
74
|
+
return buf.subarray(0, done);
|
|
75
|
+
},
|
|
76
|
+
async close() {
|
|
77
|
+
const p = opening;
|
|
78
|
+
opening = undefined;
|
|
79
|
+
await p?.then(h => h.close(), () => {
|
|
80
|
+
// An open that failed left nothing to close.
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Random-access reader with a small block cache, so the ZIP reader can ask for small overlapping windows cheaply. Its
|
|
87
|
+
* reads span at most a few blocks: a header, a name and its extra field, or the end of the file.
|
|
88
|
+
*/
|
|
89
|
+
class Reader {
|
|
90
|
+
source;
|
|
91
|
+
size;
|
|
92
|
+
blockSize;
|
|
93
|
+
maxBlocks;
|
|
94
|
+
/** Blocks by index, oldest first. */
|
|
95
|
+
cache = new Map();
|
|
96
|
+
constructor(source, size, blockSize = 65536, maxBlocks = 16) {
|
|
97
|
+
this.source = source;
|
|
98
|
+
this.size = size;
|
|
99
|
+
this.blockSize = blockSize;
|
|
100
|
+
this.maxBlocks = maxBlocks;
|
|
101
|
+
}
|
|
102
|
+
async block(index) {
|
|
103
|
+
const hit = this.cache.get(index);
|
|
104
|
+
if (hit)
|
|
105
|
+
return hit;
|
|
106
|
+
const data = await this.source.read(index * this.blockSize, Math.min(this.blockSize, this.size - index * this.blockSize));
|
|
107
|
+
if (this.cache.size >= this.maxBlocks) {
|
|
108
|
+
for (const oldest of this.cache.keys()) {
|
|
109
|
+
this.cache.delete(oldest);
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
this.cache.set(index, data);
|
|
114
|
+
return data;
|
|
115
|
+
}
|
|
116
|
+
/** Reads up to `length` bytes at `offset`; shorter only at the end of the source. */
|
|
117
|
+
async read(offset, length) {
|
|
118
|
+
const end = Math.min(this.size, offset + length);
|
|
119
|
+
if (end <= offset)
|
|
120
|
+
return new Uint8Array(0);
|
|
121
|
+
const first = Math.floor(offset / this.blockSize);
|
|
122
|
+
const last = Math.floor((end - 1) / this.blockSize);
|
|
123
|
+
const out = new Uint8Array(end - offset);
|
|
124
|
+
for (let i = first; i <= last; i++) {
|
|
125
|
+
const b = await this.block(i);
|
|
126
|
+
const from = i === first ? offset - first * this.blockSize : 0;
|
|
127
|
+
const to = i === last ? end - i * this.blockSize : b.length;
|
|
128
|
+
out.set(b.subarray(from, to), i === first ? 0 : i * this.blockSize - offset);
|
|
129
|
+
}
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
/** Yields the range in chunks without caching them. */
|
|
133
|
+
async *chunks(offset, length, chunkSize = 262144) {
|
|
134
|
+
const end = Math.min(this.size, offset + length);
|
|
135
|
+
for (let pos = offset; pos < end; pos += chunkSize)
|
|
136
|
+
yield this.source.read(pos, Math.min(chunkSize, end - pos));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
exports.Reader = Reader;
|
package/dist/links.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { OfficeCategory as C, OfficeDetail as D } from './types';
|
|
2
|
+
export interface LinkVerdict {
|
|
3
|
+
/** LINK, or MACRO for a link that runs a macro or script. */
|
|
4
|
+
category: C.Link | C.Macro;
|
|
5
|
+
detail: D;
|
|
6
|
+
host?: string;
|
|
7
|
+
}
|
|
8
|
+
/** A target that names the same document or a place in it. RFC 3986 reads an empty reference as the document itself. */
|
|
9
|
+
export declare const isFragment: (target: string) => boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Classifies a hyperlink target with pdf-defuse's checks, without the comparison of the target with the link's text.
|
|
12
|
+
* Call it only for targets that are more than a fragment. A target that holds the content of an external entity
|
|
13
|
+
* cannot be read, and reads as the empty string: a relative link, which is what pdf-defuse makes of a URI it cannot
|
|
14
|
+
* resolve.
|
|
15
|
+
*/
|
|
16
|
+
export declare function checkLink(rawTarget: string): LinkVerdict;
|
|
17
|
+
/**
|
|
18
|
+
* Whether a target outside the file is on this machine: a drive path, a file: URL without a host, an absolute path or
|
|
19
|
+
* a relative one. A UNC path, a file: URL with a host and every other scheme reach elsewhere, and so can a target that
|
|
20
|
+
* holds the content of an external entity.
|
|
21
|
+
*/
|
|
22
|
+
export declare const isLocalTarget: (rawTarget: string) => boolean;
|
|
23
|
+
/** Whether an ODF reference names an entry inside the package: a relative path that does not climb out of it. */
|
|
24
|
+
export declare const isInsidePackage: (rawHref: string) => boolean;
|
|
25
|
+
/** Whether an OOXML relationship target is not a part name, whatever TargetMode says: a scheme, a drive or a network path. */
|
|
26
|
+
export declare const isAbsoluteTarget: (rawTarget: string) => boolean;
|