fix(template): reject unescaped expressions in permission templates#7071
Conversation
Add a rejectUnescaped option to validateHandlebarTemplate /
isValidHandleBarTemplate that accepts only escaped expressions ({{ }}) and
rejects unescaped ones ({{{ }}} / {{& }}). Enable it for role and
additional-privilege create/update. The option defaults off, so dynamic-secret
statement validation (rendered with noEscape, e.g. LDAP) is unchanged. Adds unit
tests for both modes.
|
💬 Discussion in Slack: #pr-review-infisical-7071-fix-template-reject-unescaped-expressions-in-permission Posted by Review Police — reviews, comments, new commits, and CI failures will stream into this channel. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 94552dfbdc
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
| Filename | Overview |
|---|---|
| backend/src/lib/template/validate-handlebars.ts | Adds opt-in rejectUnescaped flag to both validator functions; uses Handlebars AST's escaped property correctly to distinguish {{ }} from {{{ }}} and {{& }}; default remains false preserving all existing callers. |
| backend/src/lib/template/validate-handlebars.test.ts | New unit-test file covering strict and lenient modes for both exported functions; logger is correctly stubbed so the BadRequestError surfaces cleanly in a unit context. |
| backend/src/services/role/role-service.ts | Adds rejectUnescaped: true to both role-create and role-update validateHandlebarTemplate calls; change is minimal and correctly scoped. |
| backend/src/services/additional-privilege/additional-privilege-service.ts | Adds rejectUnescaped: true to both privilege-create and privilege-update validateHandlebarTemplate calls; the pre-existing "Additional Privilege Create" template name used in the update path is unchanged by this PR. |
Reviews (1): Last reviewed commit: "fix(template): reject unescaped expressi..." | Re-trigger Greptile
Context
validateHandlebarTemplate/isValidHandleBarTemplatevalidate Handlebars templates by checkingthat each mustache statement's path is an allowed expression. They did not check whether the
expression was escaped, so unescaped forms (
{{{ ... }}}and the equivalent{{& ... }}) passed aslong as the path was allowed.
This adds an opt-in
rejectUnescapedoption to the validator: when set, only escaped expressions(
{{ ... }}) are accepted. It is enabled everywhere permission rules are authored: role andadditional-privilege creation/update, and project-template creation/update (template roles are
materialized into project custom roles and rendered by the same permission templating, so they need
the same validation). The option defaults off, so the other callers, the dynamic-secret providers,
which render their statements with
noEscape(e.g. LDAP renders raw values into LDIF), keep theircurrent behavior and are unaffected.
did not run this validator at all.
rejectUnescaped, role, additional-privilege, and project-template validators acceptonly escaped expressions; dynamic-secret statement validation is unchanged.
Changed files:
backend/src/lib/template/validate-handlebars.ts: add the opt-inrejectUnescapedoption tovalidateHandlebarTemplateandisValidHandleBarTemplate.backend/src/services/role/role-service.ts: passrejectUnescaped: trueon role create and update.backend/src/services/additional-privilege/additional-privilege-service.ts: passrejectUnescaped: trueon additional-privilege create and update.backend/src/ee/services/project-template/project-template-service.ts: validate template rolepermissions with
rejectUnescaped: trueon template create and update.backend/src/lib/template/validate-handlebars.test.ts: unit tests for both modes (opt-in rejectsunescaped forms; default accepts them, preserving non-permission caller behavior).
Screenshots
Steps to verify the change
npm run test:unit -- src/lib/template/validate-handlebars.test.ts. It coversthe opt-in mode (escaped accepted;
{{{ }}}and{{& }}rejected) and the default mode (unescapedaccepted), plus a disallowed expression.
{{ ... }}expressions works;one containing
{{{ ... }}}is rejected. Dynamic-secret statements (including LDAP) that use{{{ ... }}}continue to validate and work as before.Type
Checklist
type(scope): short description(scope is optional, e.g.,fix: prevent crash on syncorfix(api): handle null response).