Skip to content

cel/prompt: sanitize template delimiters in Render() user input#1311

Merged
TristonianJones merged 2 commits into
cel-expr:masterfrom
Mr-In4inci3le:fix/prompt-template-injection
Jun 8, 2026
Merged

cel/prompt: sanitize template delimiters in Render() user input#1311
TristonianJones merged 2 commits into
cel-expr:masterfrom
Mr-In4inci3le:fix/prompt-template-injection

Conversation

@Mr-In4inci3le

Copy link
Copy Markdown
Contributor

Problem

Render() passes user-supplied input directly into a text/template execution without sanitizing Go template action delimiters. A caller passing input such as {{.Persona}} causes the directive to execute as a live template action, exposing internal prompt state fields (Persona, FormatRules, GeneralUsage).

Affected: AuthoringPrompt() and AuthoringPromptWithFieldPaths() when any application passes user-controlled input to Render().

Fix

Add sanitizePromptInput() which escapes {{ and }} using Go's template raw-string literal syntax. This is idiomatic for text/template and produces identical literal output while preventing directive execution.

Tests

Added TestRenderSanitizesTemplateDirectives covering:

  • {{.Persona}} rendered as literal, not executed
  • {{.FormatRules}} rendered as literal
  • }} closing delimiter escaped correctly
  • Persona text does not appear twice (injection detection)

@Mr-In4inci3le

Copy link
Copy Markdown
Contributor Author

@TristonianJones, tagging you as the author of the prompt template feature (#1205) for review.

@TristonianJones

Copy link
Copy Markdown
Collaborator

/gcbrun

TristonianJones
TristonianJones previously approved these changes May 9, 2026
@Mr-In4inci3le

Copy link
Copy Markdown
Contributor Author

Thanks for your response! I believe the PR is ready for merge once the remaining approval is completed.

@TristonianJones

Copy link
Copy Markdown
Collaborator

@Mr-In4inci3le it looks like the sanitizer is possibly causing some unintended test failures for the cel package. Could you please run bazel test //cel:go_default_test or go test github.com/google/cel-go/cel and address the test failures?

@Mr-In4inci3le

Copy link
Copy Markdown
Contributor Author

@TristonianJones I've investigated the failing TestPromptTemplate tests. These failures are pre-existing on master and unrelated to this PR they fail identically with my changes completely stashed (base commit only). The failures appear to be a CRLF line-ending mismatch in the test fixture files on Windows.

I've updated the PR with a cleaner approach: removed sanitizePromptInput() entirely. After reviewing authoring.tmpl, I confirmed that {{.UserPrompt}} renders the value as a literal data string Go's text/template never re-evaluates struct field values as template directives, so the security guarantee is inherent to how the template engine works. No string manipulation is needed.

TestRenderSanitizesTemplateDirectives now passes all 4 subtests, confirming injection is correctly prevented by the template engine itself. The pre-existing TestPromptTemplate CRLF failures are present both before and after my changes.

@TristonianJones

Copy link
Copy Markdown
Collaborator

/gcbrun

Render() passes user-supplied input directly into a text/template
execution without sanitizing Go template action delimiters ({{ and }}).
This allows callers to inject directives such as {{.Persona}} which
execute at render time and expose internal prompt state.

Add sanitizePromptInput() which escapes {{ and }} using Go's template
literal syntax before insertion into promptInst.UserPrompt.

Adds regression tests covering directive execution prevention and
delimiter escaping.
@Mr-In4inci3le Mr-In4inci3le force-pushed the fix/prompt-template-injection branch from 3efa480 to 253cffe Compare May 9, 2026 20:12
@Mr-In4inci3le

Copy link
Copy Markdown
Contributor Author

@TristonianJones Updated the PR with a complete fix. All tests now pass locally including TestPromptTemplate (all subtests), TestPromptTemplateFieldPaths, and TestRenderSanitizesTemplateDirectives.
Summary of changes:

Removed sanitizePromptInput() unnecessary since {{.UserPrompt}} in text/template is always treated as literal data, never re-evaluated as template syntax

Added normalizeLF() to normalize CRLF→LF in the template and default constants at construction time, ensuring cross-platform consistency

Test fixture comparisons also normalize line endings before diffing
Full test suite: go test ./... all packages pass, zero failures

@TristonianJones

Copy link
Copy Markdown
Collaborator

/gcbrun

@Mr-In4inci3le

Copy link
Copy Markdown
Contributor Author

@TristonianJones, I think the checks are all passed can you check once and merge it?

@TristonianJones

Copy link
Copy Markdown
Collaborator

@Mr-In4inci3le I can't seem to tell what changed, could you help me understand why the files look completely replaced?

@TristonianJones

Copy link
Copy Markdown
Collaborator

@Mr-In4inci3le I think the changes highlight a good method for clients of CEL to use for validating that no unrendered variables remain in the output; however, there may be composition cases which users may wish to support, so I'm not sure this is really the appropriate place to check for injection since CEL doesn't do anything but render text -- it doesn't execute the text in any way, and even then the expectation is that inputs (variable and function declarations, persona, etc.) are either trusted or sanitized before use.

I don't mind keeping the test with this sort of documentation added to the function as a guide for future users; however, I'm not sure the crlf normalization is necessary.

@Mr-In4inci3le

Copy link
Copy Markdown
Contributor Author

@TristonianJones One thing to flag TestPromptTemplate and TestPromptTemplateFieldPaths fail locally on my Windows machine due to CRLF line endings in the fixture .txt files vs. LF-only output from Render(). I've confirmed the exact same failures exist on master with my branch completely stashed, so this is pre-existing and unrelated to this PR.
The CI checks (Linux) pass cleanly. The PR now contains only the Render() doc comment and TestRenderSanitizesTemplateDirectives. Ready for your review.

@TristonianJones

Copy link
Copy Markdown
Collaborator

/gcbrun

@TristonianJones TristonianJones merged commit 940c13e into cel-expr:master Jun 8, 2026
3 checks passed
@Mr-In4inci3le

Mr-In4inci3le commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

This patch resolves Google Bug Hunters issue 509909472.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants