Skip to content

neutralizeTemplateDelimiters absent from sanitize_content.cjs alias branch — template syntax passes through safe-output sani [Content truncated due to length] #31711

@szabta89

Description

@szabta89

Summary

In v0.68.3, sanitize_content.cjs (the alias branch, exercised for every issue/PR-triggered workflow) did not import or call neutralizeTemplateDelimiters, while sanitize_content_core.cjs did (line ~1136). Because collect_ndjson_output.cjs always pushes the trigger author into allowedMentions, the alias branch is the only sanitization path taken during normal workflow execution. As a result, template syntax (\{\{...}}, \$\{...}, \{\%...%}, \<%= %>, \{\#...#}) passed through the alias-branch pipeline verbatim and appeared unescaped in AI-generated safe-output issue bodies and comments. The fix (adding the import and call) is present in the current main branch HEAD (sanitize_content.cjs line 25 and line 128 at SHA 0d1047f3), but a regression test and optional refactor to prevent future dual-pipeline drift are still needed.

Affected Area

Output trust boundary — the safe-output sanitization pipeline in sanitize_content.cjs applied before AI-generated content is written to GitHub issues and comments.

Reproduction Outline

  1. Deploy v0.68.3 sanitize_content.cjs (SHA 6d26fa5d) and sanitize_content_core.cjs (SHA 159c2fed).
  2. Trigger any gh-aw workflow via a user-created issue or PR (ensuring issueAuthor is pushed to allowedMentions → alias branch exercised).
  3. Have the AI agent include template syntax in a safe-output body (e.g., \{\{config.adminToken}}, \$\{secrets.API_KEY}, \{\%- include x %}).
  4. Observe the created GitHub issue or comment: template syntax appears verbatim, unescaped.
  5. Compare: calling sanitizeContentCore directly on the same input produces correctly escaped output.

Observed Behavior

sanitizeContent(text, { allowedAliases: ['author'] }) returns template syntax unchanged — e.g., \{\{secrets.TOKEN}} passes through as-is. The neutralizeTemplateDelimiters step is never reached on the alias branch.

Expected Behavior

All sanitization paths should apply neutralizeTemplateDelimiters. sanitizeContent (alias branch) should produce identical template-escaping behavior as sanitizeContentCore for inputs containing \{\{, \$\{, \{\%, \<%=, \{\#.

Security Relevance

This is a defense-in-depth gap: the protection exists in one code path but is absent from the always-exercised path. Actual end-to-end exploitation requires a downstream consumer (GitHub Pages Jekyll/Liquid, Ansible/Jinja2, external CI) that reads AI-generated issue bodies and evaluates template expressions. In XPIA scenarios where an adversary embeds template syntax in a trigger issue and the agent echoes it back into a safe-output body, the unescaped syntax reaches any such downstream consumer verbatim. The jsdoc for neutralizeTemplateDelimiters explicitly documents this threat ("prevents issues if content is later processed by template engines").

Recommended Follow-Up

  1. Verify the fix is included in the next tagged releasesanitize_content.cjs at HEAD already imports and calls neutralizeTemplateDelimiters.
  2. Add a parity regression test asserting sanitizeContent(text, { allowedAliases: ['x'] }) and sanitizeContentCore(text) produce identical escaping for all template-syntax patterns (\{\{, \$\{, \{\%, \<%= , \{\#).
  3. Consider refactoring sanitizeContent to delegate to sanitizeContentCore and then selectively de-neutralize allowed aliases, eliminating future dual-pipeline drift.

Additional Context

If this inconsistency is considered by-design (e.g., alias-branch intentionally omits certain steps for performance), that assumption should be documented explicitly in the code or architecture documentation so future reviewers can distinguish intentional omissions from accidental gaps.

Original finding: https://github.com/githubnext/gh-aw-security/issues/2232


gh-aw version: v0.68.3

Generated by File Issue · ● 359K ·

Metadata

Metadata

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions