@zola_do/docx 0.3.6 → 0.3.7

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 CHANGED
@@ -15,6 +15,27 @@ DOCX template processing for NestJS using docx-templates.
15
15
  - **Handlebars Syntax** — Supports loops, conditionals, images
16
16
  - **Buffer Output** — Returns generated DOCX as Buffer
17
17
 
18
+ ## Security
19
+
20
+ `docx-templates` evaluates template commands in a JavaScript VM. **Only use trusted, version-controlled templates** (stored in your repo or admin-only object storage). Do not accept user-uploaded `.docx` files for server-side rendering.
21
+
22
+ By default, `@zola_do/docx`:
23
+
24
+ - Rejects templates containing `EXEC` commands (`allowExec: true` only for fully trusted templates).
25
+ - Keeps VM sandbox enabled (`noSandbox: false`); `allowUnsafeJs` is required to disable it or supply custom `runJs`.
26
+ - Uses `secureCreateReport()` internally from `DocxService.generateDocx()`.
27
+
28
+ ```typescript
29
+ import { secureCreateReport } from '@zola_do/docx';
30
+
31
+ // Optional escape hatch for trusted internal templates only:
32
+ await secureCreateReport({
33
+ template,
34
+ data,
35
+ allowExec: true,
36
+ });
37
+ ```
38
+
18
39
  ## Installation
19
40
 
20
41
  ```bash
@@ -0,0 +1,13 @@
1
+ import { createReport } from 'docx-templates';
2
+ export type SecureDocxTemplateOptions = {
3
+ cmdDelimiter?: string | [string, string];
4
+ allowExec?: boolean;
5
+ allowUnsafeJs?: boolean;
6
+ };
7
+ type CreateReportOptions = Parameters<typeof createReport>[0];
8
+ export declare const DOCX_TRUSTED_TEMPLATES_ONLY = "DOCX templates must be trusted and version-controlled. User-uploaded templates are not supported. EXEC commands are rejected by default.";
9
+ export declare function resolveCmdDelimiter(delimiter?: string | [string, string]): [string, string];
10
+ export declare function assertTrustedDocxTemplate(template: Buffer | Uint8Array, options?: SecureDocxTemplateOptions): Promise<void>;
11
+ export declare function buildSecureCreateReportOptions(options?: SecureDocxTemplateOptions & Partial<Pick<CreateReportOptions, 'cmdDelimiter' | 'additionalJsContext' | 'failFast' | 'noSandbox' | 'runJs'>>): Partial<CreateReportOptions>;
12
+ export declare function secureCreateReport(options: CreateReportOptions & SecureDocxTemplateOptions): Promise<Uint8Array>;
13
+ export {};
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.DOCX_TRUSTED_TEMPLATES_ONLY = void 0;
15
+ exports.resolveCmdDelimiter = resolveCmdDelimiter;
16
+ exports.assertTrustedDocxTemplate = assertTrustedDocxTemplate;
17
+ exports.buildSecureCreateReportOptions = buildSecureCreateReportOptions;
18
+ exports.secureCreateReport = secureCreateReport;
19
+ const common_1 = require("@nestjs/common");
20
+ const docx_templates_1 = require("docx-templates");
21
+ exports.DOCX_TRUSTED_TEMPLATES_ONLY = 'DOCX templates must be trusted and version-controlled. User-uploaded templates are not supported. EXEC commands are rejected by default.';
22
+ const DEFAULT_CMD_DELIMITER = ['+++', '+++'];
23
+ function resolveCmdDelimiter(delimiter) {
24
+ if (!delimiter) {
25
+ return DEFAULT_CMD_DELIMITER;
26
+ }
27
+ if (Array.isArray(delimiter)) {
28
+ return delimiter;
29
+ }
30
+ return [delimiter, delimiter];
31
+ }
32
+ async function assertTrustedDocxTemplate(template, options = {}) {
33
+ if (options.allowExec) {
34
+ return;
35
+ }
36
+ const commands = await (0, docx_templates_1.listCommands)(new Uint8Array(template), resolveCmdDelimiter(options.cmdDelimiter));
37
+ const execCommands = commands.filter((command) => command.type === 'EXEC');
38
+ if (execCommands.length > 0) {
39
+ throw new common_1.BadRequestException(`${exports.DOCX_TRUSTED_TEMPLATES_ONLY} Rejected EXEC command(s): ${execCommands
40
+ .map((command) => command.raw)
41
+ .join(', ')}`);
42
+ }
43
+ }
44
+ function buildSecureCreateReportOptions(options = {}) {
45
+ var _a, _b;
46
+ if (options.noSandbox === true && !options.allowUnsafeJs) {
47
+ throw new common_1.BadRequestException(`${exports.DOCX_TRUSTED_TEMPLATES_ONLY} noSandbox is not allowed.`);
48
+ }
49
+ if (options.runJs && !options.allowUnsafeJs) {
50
+ throw new common_1.BadRequestException(`${exports.DOCX_TRUSTED_TEMPLATES_ONLY} Custom runJs is not allowed.`);
51
+ }
52
+ return {
53
+ cmdDelimiter: options.cmdDelimiter,
54
+ additionalJsContext: options.additionalJsContext,
55
+ failFast: (_a = options.failFast) !== null && _a !== void 0 ? _a : true,
56
+ noSandbox: options.allowUnsafeJs ? (_b = options.noSandbox) !== null && _b !== void 0 ? _b : false : false,
57
+ runJs: options.allowUnsafeJs ? options.runJs : undefined,
58
+ };
59
+ }
60
+ async function secureCreateReport(options) {
61
+ await assertTrustedDocxTemplate(options.template, options);
62
+ const { allowExec, allowUnsafeJs } = options, createOptions = __rest(options, ["allowExec", "allowUnsafeJs"]);
63
+ return (0, docx_templates_1.createReport)(Object.assign(Object.assign({}, createOptions), buildSecureCreateReportOptions({
64
+ allowExec,
65
+ allowUnsafeJs,
66
+ cmdDelimiter: createOptions.cmdDelimiter,
67
+ additionalJsContext: createOptions.additionalJsContext,
68
+ failFast: createOptions.failFast,
69
+ noSandbox: createOptions.noSandbox,
70
+ runJs: createOptions.runJs,
71
+ })));
72
+ }
73
+ //# sourceMappingURL=docx-template-security.helper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docx-template-security.helper.js","sourceRoot":"","sources":["../src/docx-template-security.helper.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,2CAAqD;AACrD,mDAA4D;AAY/C,QAAA,2BAA2B,GACtC,0IAA0I,CAAC;AAE7I,MAAM,qBAAqB,GAAqB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAE/D,6BACE,SAAqC;IAErC,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,qBAAqB,CAAC;IAC/B,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7B,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;AAChC,CAAC;AAEM,KAAK,oCACV,QAA6B,EAC7B,OAAO,GAA8B,EAAE;IAEvC,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;QACtB,OAAO;IACT,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,IAAA,6BAAY,EACjC,IAAI,UAAU,CAAC,QAAQ,CAAQ,EAC/B,mBAAmB,CAAC,OAAO,CAAC,YAAY,CAAC,CAC1C,CAAC;IAEF,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;IAC3E,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,4BAAmB,CAC3B,GAAG,QAAA,2BAA2B,8BAA8B,YAAY;aACrE,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC;aAC7B,IAAI,CAAC,IAAI,CAAC,EAAE,CAChB,CAAC;IACJ,CAAC;AACH,CAAC;AAED,wCACE,OAAO,GAUD,EAAE;;IAER,IAAI,OAAO,CAAC,SAAS,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;QACzD,MAAM,IAAI,4BAAmB,CAC3B,GAAG,QAAA,2BAA2B,4BAA4B,CAC3D,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;QAC5C,MAAM,IAAI,4BAAmB,CAC3B,GAAG,QAAA,2BAA2B,+BAA+B,CAC9D,CAAC;IACJ,CAAC;IAED,OAAO;QACL,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,mBAAmB,EAAE,OAAO,CAAC,mBAAmB;QAChD,QAAQ,QAAE,OAAO,CAAC,QAAQ,mCAAI,IAAI;QAClC,SAAS,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC,OAAC,OAAO,CAAC,SAAS,mCAAI,KAAK,CAAC,CAAC,CAAC,KAAK;QACrE,KAAK,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;KACzD,CAAC;AACJ,CAAC;AAEM,KAAK,6BACV,OAAwD;IAExD,MAAM,yBAAyB,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAE3D,MAAM,EAAE,SAAS,EAAE,aAAa,KAAuB,OAAO,EAAzB,aAAa,UAAK,OAAO,EAAxD,8BAA8C,CAAU,CAAC;IAE/D,OAAO,IAAA,6BAAY,kCACd,aAAa,GACb,8BAA8B,CAAC;QAChC,SAAS;QACT,aAAa;QACb,YAAY,EAAE,aAAa,CAAC,YAAY;QACxC,mBAAmB,EAAE,aAAa,CAAC,mBAAmB;QACtD,QAAQ,EAAE,aAAa,CAAC,QAAQ;QAChC,SAAS,EAAE,aAAa,CAAC,SAAS;QAClC,KAAK,EAAE,aAAa,CAAC,KAAK;KAC3B,CAAC,EACF,CAAC;AACL,CAAC"}
@@ -1,4 +1,5 @@
1
+ import { type SecureDocxTemplateOptions } from './docx-template-security.helper';
1
2
  export declare class DocxService {
2
- validateDocument(template: Buffer, requiredProperties: string[]): Promise<string[]>;
3
- generateDocx(template: Buffer, data: any): Promise<Buffer<ArrayBuffer>>;
3
+ validateDocument(template: Buffer, requiredProperties: string[], options?: SecureDocxTemplateOptions): Promise<string[]>;
4
+ generateDocx(template: Buffer, data: any, options?: SecureDocxTemplateOptions): Promise<Buffer<ArrayBuffer>>;
4
5
  }
@@ -9,10 +9,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.DocxService = void 0;
10
10
  const common_1 = require("@nestjs/common");
11
11
  const docx_templates_1 = require("docx-templates");
12
+ const docx_template_security_helper_1 = require("./docx-template-security.helper");
12
13
  let DocxService = class DocxService {
13
- async validateDocument(template, requiredProperties) {
14
+ async validateDocument(template, requiredProperties, options = {}) {
15
+ var _a;
14
16
  try {
15
- const providedValues = await (0, docx_templates_1.listCommands)(new Uint8Array(template), ['{', '}']);
17
+ const cmdDelimiter = (_a = options.cmdDelimiter) !== null && _a !== void 0 ? _a : ['{', '}'];
18
+ const providedValues = await (0, docx_templates_1.listCommands)(new Uint8Array(template), cmdDelimiter);
19
+ await (0, docx_template_security_helper_1.assertTrustedDocxTemplate)(template, Object.assign(Object.assign({}, options), { cmdDelimiter }));
16
20
  const result = requiredProperties.filter((r) => !providedValues.find((p) => p.code == r));
17
21
  return result;
18
22
  }
@@ -20,10 +24,13 @@ let DocxService = class DocxService {
20
24
  throw error;
21
25
  }
22
26
  }
23
- async generateDocx(template, data) {
24
- const buffer = await (0, docx_templates_1.createReport)({
27
+ async generateDocx(template, data, options = {}) {
28
+ const buffer = await (0, docx_template_security_helper_1.secureCreateReport)({
25
29
  template: template,
26
30
  data,
31
+ cmdDelimiter: options.cmdDelimiter,
32
+ allowExec: options.allowExec,
33
+ allowUnsafeJs: options.allowUnsafeJs,
27
34
  });
28
35
  return Buffer.from(buffer);
29
36
  }
@@ -1 +1 @@
1
- {"version":3,"file":"docx.service.js","sourceRoot":"","sources":["../src/docx.service.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4C;AAC5C,mDAA4D;AAGrD,IAAM,WAAW,GAAjB,MAAM,WAAW;IACtB,KAAK,CAAC,gBAAgB,CAAC,QAAgB,EAAE,kBAA4B;QACnE,IAAI,CAAC;YACH,MAAM,cAAc,GAAG,MAAM,IAAA,6BAAY,EACvC,IAAI,UAAU,CAAC,QAAQ,CAAQ,EAC/B,CAAC,GAAG,EAAE,GAAG,CAAC,CACX,CAAC;YACF,MAAM,MAAM,GAAG,kBAAkB,CAAC,MAAM,CACtC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAChD,CAAC;YACF,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,QAAgB,EAAE,IAAS;QAC5C,MAAM,MAAM,GAAG,MAAM,IAAA,6BAAY,EAAC;YAChC,QAAQ,EAAE,QAAe;YACzB,IAAI;SACL,CAAC,CAAC;QACH,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;CACF,CAAA;;sBAvBY,WAAW;IADvB,IAAA,mBAAU,GAAE;GACA,WAAW,CAuBvB"}
1
+ {"version":3,"file":"docx.service.js","sourceRoot":"","sources":["../src/docx.service.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4C;AAC5C,mDAA8C;AAC9C,mFAIyC;AAGlC,IAAM,WAAW,GAAjB,MAAM,WAAW;IACtB,KAAK,CAAC,gBAAgB,CACpB,QAAgB,EAChB,kBAA4B,EAC5B,OAAO,GAA8B,EAAE;;QAEvC,IAAI,CAAC;YACH,MAAM,YAAY,SAAG,OAAO,CAAC,YAAY,mCAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YACxD,MAAM,cAAc,GAAG,MAAM,IAAA,6BAAY,EACvC,IAAI,UAAU,CAAC,QAAQ,CAAQ,EAC/B,YAAY,CACb,CAAC;YACF,MAAM,IAAA,yDAAyB,EAAC,QAAQ,kCACnC,OAAO,KACV,YAAY,IACZ,CAAC;YACH,MAAM,MAAM,GAAG,kBAAkB,CAAC,MAAM,CACtC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAChD,CAAC;YACF,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,QAAgB,EAChB,IAAS,EACT,OAAO,GAA8B,EAAE;QAEvC,MAAM,MAAM,GAAG,MAAM,IAAA,kDAAkB,EAAC;YACtC,QAAQ,EAAE,QAAe;YACzB,IAAI;YACJ,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,aAAa,EAAE,OAAO,CAAC,aAAa;SACrC,CAAC,CAAC;QACH,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;CACF,CAAA;;sBAvCY,WAAW;IADvB,IAAA,mBAAU,GAAE;GACA,WAAW,CAuCvB"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from './docx.module';
2
2
  export * from './docx.service';
3
+ export * from './docx-template-security.helper';
package/dist/index.js CHANGED
@@ -16,4 +16,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./docx.module"), exports);
18
18
  __exportStar(require("./docx.service"), exports);
19
+ __exportStar(require("./docx-template-security.helper"), exports);
19
20
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,gDAA8B;AAC9B,iDAA+B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,gDAA8B;AAC9B,iDAA+B;AAC/B,kEAAgD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zola_do/docx",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "description": "DOCX template processing for NestJS",
5
5
  "author": "zolaDO",
6
6
  "license": "ISC",