Compare commits

...

5 Commits

Author SHA1 Message Date
David Bruant
5e1b0b3da4 add section on securing fillOdtTemplate 2025-09-18 16:13:07 +02:00
David Bruant
44221b4255 ses@1.14 2025-09-18 15:29:12 +02:00
David Bruant
b9eadf8834 improving readme 2025-09-18 15:26:31 +02:00
David Bruant
d18c16ee68 suppression de l'appel à lockdown par défaut 2025-09-18 15:26:11 +02:00
David Bruant
5aaa4640ca restore test 2025-09-18 15:04:49 +02:00
5 changed files with 57 additions and 21 deletions

56
package-lock.json generated
View File

@ -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": {

View File

@ -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"
}
}

View File

@ -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 <collection> as élément}` and `{#if <condition>}` and in `{<expression>}`
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/

View File

@ -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' */

View File

@ -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 = {