From a31f57026ddbe9a352be4875f5385cd7a89c1813 Mon Sep 17 00:00:00 2001 From: David Bruant Date: Thu, 18 Sep 2025 16:15:24 +0200 Subject: [PATCH] Remove default call to lockdown (#22) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * restore test * suppression de l'appel à lockdown par défaut * improving readme * ses@1.14 * add section on securing fillOdtTemplate --- package-lock.json | 56 +++++++++++++++++------ package.json | 2 +- readme.md | 16 ++++++- scripts/odf/templating/fillOdtTemplate.js | 2 - tests/fill-odt-template/image.js | 2 +- 5 files changed, 57 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index ecf763a..293e759 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "@xmldom/xmldom": "^0.9.8", "@zip.js/zip.js": "^2.7.57", "image-size": "^2.0.2", - "ses": "^1.12.0" + "ses": "^1.14.0" }, "devDependencies": { "@rollup/plugin-commonjs": "^25.0.7", @@ -42,10 +42,22 @@ "node": ">=6.0.0" } }, + "node_modules/@endo/cache-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@endo/cache-map/-/cache-map-1.1.0.tgz", + "integrity": "sha512-owFGshs/97PDw9oguZqU/px8Lv1d0KjAUtDUiPwKHNXRVUE/jyettEbRoTbNJR1OaI8biMn6bHr9kVJsOh6dXw==", + "license": "Apache-2.0" + }, "node_modules/@endo/env-options": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@endo/env-options/-/env-options-1.1.8.tgz", - "integrity": "sha512-Xtxw9n33I4guo8q0sDyZiRuxlfaopM454AKiELgU7l3tqsylCut6IBZ0fPy4ltSHsBib7M3yF7OEMoIuLwzWVg==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@endo/env-options/-/env-options-1.1.11.tgz", + "integrity": "sha512-p9OnAPsdqoX4YJsE98e3NBVhIr2iW9gNZxHhAI2/Ul5TdRfoOViItzHzTqrgUVopw6XxA1u1uS6CykLMDUxarA==", + "license": "Apache-2.0" + }, + "node_modules/@endo/immutable-arraybuffer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@endo/immutable-arraybuffer/-/immutable-arraybuffer-1.1.2.tgz", + "integrity": "sha512-u+NaYB2aqEugQ3u7w3c5QNkPogf8q/xGgsPaqdY6pUiGWtYiTiFspKFcha6+oeZhWXWQ23rf0KrUq0kfuzqYyQ==", "license": "Apache-2.0" }, "node_modules/@jridgewell/gen-mapping": { @@ -3628,12 +3640,14 @@ } }, "node_modules/ses": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/ses/-/ses-1.12.0.tgz", - "integrity": "sha512-jvmwXE2lFxIIY1j76hFjewIIhYMR9Slo3ynWZGtGl5M7VUCw3EA0wetS+JCIbl2UcSQjAT0yGAHkyxPJreuC9w==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/ses/-/ses-1.14.0.tgz", + "integrity": "sha512-T07hNgOfVRTLZGwSS50RnhqrG3foWP+rM+Q5Du4KUQyMLFI3A8YA4RKl0jjZzhihC1ZvDGrWi/JMn4vqbgr/Jg==", "license": "Apache-2.0", "dependencies": { - "@endo/env-options": "^1.1.8" + "@endo/cache-map": "^1.1.0", + "@endo/env-options": "^1.1.11", + "@endo/immutable-arraybuffer": "^1.1.2" } }, "node_modules/set-blocking": { @@ -4584,10 +4598,20 @@ "@jridgewell/trace-mapping": "^0.3.9" } }, + "@endo/cache-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@endo/cache-map/-/cache-map-1.1.0.tgz", + "integrity": "sha512-owFGshs/97PDw9oguZqU/px8Lv1d0KjAUtDUiPwKHNXRVUE/jyettEbRoTbNJR1OaI8biMn6bHr9kVJsOh6dXw==" + }, "@endo/env-options": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@endo/env-options/-/env-options-1.1.8.tgz", - "integrity": "sha512-Xtxw9n33I4guo8q0sDyZiRuxlfaopM454AKiELgU7l3tqsylCut6IBZ0fPy4ltSHsBib7M3yF7OEMoIuLwzWVg==" + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@endo/env-options/-/env-options-1.1.11.tgz", + "integrity": "sha512-p9OnAPsdqoX4YJsE98e3NBVhIr2iW9gNZxHhAI2/Ul5TdRfoOViItzHzTqrgUVopw6XxA1u1uS6CykLMDUxarA==" + }, + "@endo/immutable-arraybuffer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@endo/immutable-arraybuffer/-/immutable-arraybuffer-1.1.2.tgz", + "integrity": "sha512-u+NaYB2aqEugQ3u7w3c5QNkPogf8q/xGgsPaqdY6pUiGWtYiTiFspKFcha6+oeZhWXWQ23rf0KrUq0kfuzqYyQ==" }, "@jridgewell/gen-mapping": { "version": "0.3.2", @@ -7156,11 +7180,13 @@ } }, "ses": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/ses/-/ses-1.12.0.tgz", - "integrity": "sha512-jvmwXE2lFxIIY1j76hFjewIIhYMR9Slo3ynWZGtGl5M7VUCw3EA0wetS+JCIbl2UcSQjAT0yGAHkyxPJreuC9w==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/ses/-/ses-1.14.0.tgz", + "integrity": "sha512-T07hNgOfVRTLZGwSS50RnhqrG3foWP+rM+Q5Du4KUQyMLFI3A8YA4RKl0jjZzhihC1ZvDGrWi/JMn4vqbgr/Jg==", "requires": { - "@endo/env-options": "^1.1.8" + "@endo/cache-map": "^1.1.0", + "@endo/env-options": "^1.1.11", + "@endo/immutable-arraybuffer": "^1.1.2" } }, "set-blocking": { diff --git a/package.json b/package.json index 496865a..06d638c 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,6 @@ "@xmldom/xmldom": "^0.9.8", "@zip.js/zip.js": "^2.7.57", "image-size": "^2.0.2", - "ses": "^1.12.0" + "ses": "^1.14.0" } } diff --git a/readme.md b/readme.md index 8a7d972..9eec352 100644 --- a/readme.md +++ b/readme.md @@ -99,8 +99,7 @@ And then run the code: ```js import {join} from 'node:path'; -import {getOdtTemplate} from '../scripts/odf/odtTemplate-forNode.js' -import {fillOdtTemplate} from '../scripts/node.js' +import {getOdtTemplate, fillOdtTemplate} from '@odfjs/odfjs' // replace with your template path const templatePath = join(import.meta.dirname, './tests/data/template-anniversaire.odt') @@ -126,6 +125,19 @@ There are also loops in the form: They can be used to generate lists or tables in .odt files from data and a template using this syntax +#### Securing calls to fillOdtTemplate + +`fillOdtTemplate` evaluate arbitrary JavaScript code in `{#each as élément}` and `{#if }` and in `{}` + +By default, `fillOdtTemplate` limits access to global functions to only ECMAScript defaults via the use of [ses' Compartment](https://www.npmjs.com/package/ses#compartment), this prevents naïve data exfiltration + +However, `fillOdtTemplate` is vulnerable to [prototype pollution](https://cheatsheetseries.owasp.org/cheatsheets/Prototype_Pollution_Prevention_Cheat_Sheet.html) inside template code. Two main ways to be secure are: +- control the set of possible templates +- call ses' `lockdown` which freezes Javascript intrinsics before calling `fillOdtTemplate` (this may lead to incompatibilities) + + + + ### Demo https://odfjs.github.io/odfjs/ diff --git a/scripts/odf/templating/fillOdtTemplate.js b/scripts/odf/templating/fillOdtTemplate.js index 63aea42..9ca80a0 100644 --- a/scripts/odf/templating/fillOdtTemplate.js +++ b/scripts/odf/templating/fillOdtTemplate.js @@ -7,8 +7,6 @@ import prepareTemplateDOMTree from './prepareTemplateDOMTree.js'; import 'ses' import fillOdtElementTemplate from './fillOdtElementTemplate.js'; -lockdown(); - /** @import {Reader, ZipWriterAddDataOptions} from '@zip.js/zip.js' */ /** @import {ODFManifest, ODFManifestFileEntry} from '../manifest.js' */ diff --git a/tests/fill-odt-template/image.js b/tests/fill-odt-template/image.js index 315c475..3fdc974 100644 --- a/tests/fill-odt-template/image.js +++ b/tests/fill-odt-template/image.js @@ -9,7 +9,7 @@ import { listZipEntries } from '../helpers/zip-analysis.js'; import { getContentDocument } from '../../scripts/odf/odt/getOdtTextContent.js'; -test.skip('template filling preserves images', async t => { +test('template filling preserves images', async t => { const templatePath = join(import.meta.dirname, '../fixtures/template-avec-image.odt') const data = {