From f09c917208d1d1cccecd665072fab6140b5f7d40 Mon Sep 17 00:00:00 2001 From: Thibault Date: Thu, 6 Aug 2026 12:35:55 +0200 Subject: [PATCH] fix(security): disable Liquid file access --- src/infrastructure/template/template-engine.ts | 9 +++++++++ .../unit/infrastructure/template-engine.test.ts | 17 +++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/infrastructure/template/template-engine.ts b/src/infrastructure/template/template-engine.ts index da40c4d..5c01069 100644 --- a/src/infrastructure/template/template-engine.ts +++ b/src/infrastructure/template/template-engine.ts @@ -87,6 +87,15 @@ export class LiquidTemplateEngine implements ITemplateEngine { this.engine = new Liquid({ strictFilters: false, strictVariables: false, + fs: { + exists: async () => false, + readFile: async () => { throw new Error('Liquid file includes are disabled'); }, + existsSync: () => false, + readFileSync: () => { throw new Error('Liquid file includes are disabled'); }, + resolve: (_root: string, file: string) => file, + dirname: (file: string) => file, + sep: '/', + }, }); } return this.engine; diff --git a/test/unit/infrastructure/template-engine.test.ts b/test/unit/infrastructure/template-engine.test.ts index add22ad..afb5d27 100644 --- a/test/unit/infrastructure/template-engine.test.ts +++ b/test/unit/infrastructure/template-engine.test.ts @@ -240,6 +240,23 @@ describe('LiquidTemplateEngine timeout', () => { }); }); +describe('LiquidTemplateEngine file access', () => { + const engine = new LiquidTemplateEngine(); + const ctx = buildPromptContext(makeTask(), makeAgent(), 1, '/workspace', DEFAULT_CONFIG); + + it.each([ + '{% include "/etc/passwd" %}', + '{% include "../secret.txt" %}', + '{% render "/etc/passwd" %}', + ])('rejects file-backed tag: %s', async (template) => { + await expect(engine.render(template, ctx)).rejects.toThrow(); + }); + + it('rejects dynamic include paths', async () => { + await expect(engine.render('{% assign path = "/etc/passwd" %}{% include path %}', ctx)).rejects.toThrow(); + }); +}); + describe('LiquidTemplateEngine with retry context', () => { const engine = new LiquidTemplateEngine();