Individual skill
template-injection
User input reaching a template compiler rather than its context: the source-versus-values distinction, double-render pipelines, template names chosen by the caller, why a template sandbox is a mitigation and not a boundary, minimizing what the render context exposes, and template-driven resource exhaustion. Use when rendering templates with user-influenced values, when a template string or template name is assembled from input, or when a product lets users author templates.
dist/claude-skills/.claude/skills/template-injection/SKILL.md
Source commit eaba88102d91df65a9aa6397e8507c315c018f5e
What was checked
- Matches the observed repository HEAD
- Yes
- Scanner rules are current
- No
- Repository scan complete
- No
- Skill scan complete
- No
- Observed behavior
- Not checked yet
- Last checked
- Nov 6, 2026
These are observations at the recorded commit, not a guarantee of safety. Review the source before running it.
Install this skill
Choose an agent in your Stack or copy a command below. Commands fetch the pinned commit and copy only this skill directory. Existing destinations are refused. Copying a command does not install anything.
An install command is unavailable: current rules, complete repository and skill scans, an eligible behavior level and a permitted license are required.
Repository context
- Repository stars (not skill ratings)
- 23
- Repository last updated
- Aug 14, 2026
- License
- MIT
Observed agent integration: No specific integration evidence. Standard SKILL.md installation does not imply verified compatibility.
- claude-skills Repository topic
- llm Repository topic
- security Repository topic
- skills Repository topic
- engineering Derived category
Files and excerpt
File metadata and permitted, redacted excerpts come from the recorded scan. Source links are pinned to that commit.
Browse files
SKILL.mdcore · 6,292 bytesSKILL.mdcore · 6,292 bytesSKILL.mdcore · 6,292 bytesSKILL.mdcore · 6,292 bytesmetadata.jsonasset · 1,630 bytesreferences/engines.mddoc · 7,782 bytesreferences/verifying-findings.mddoc · 3,579 bytes
Read excerpt
<!-- Native skill bundle for Claude Code. Generated by `secure-vibe dev regenerate`. -->
<!-- Do not edit by hand; the source of truth is skills/template-injection/SKILL.md. -->
# Template Injection (SSTI)
User input reaching a template compiler rather than its context: the source-versus-values distinction, double-render pipelines, template names chosen by the caller, why a template sandbox is a mitigation and not a boundary, minimizing what the render context exposes, and template-driven resource exhaustion. Use when rendering templates with user-influenced values, when a template string or template name is assembled from input, or when a product lets users author templates.
## ALWAYS
- Pass user data as **bound values**, never into the template **source** string. This is the whole skill in one line: `render(tmpl, {"name": name})` is safe, `render("Hello " + name)` is SSTI. The compiler treats its source as code, and the context as data — so the only question that matters about any user value is which of the two it reaches.
- Keep the set of template sources **static and trusted**, loaded from files or constants. A template body read from a database column, an API response, or a request field is attacker-supplied code, whatever the column is called.
- Choose the template **by name from an allowlist**, never by a name the caller supplies. `render_template(user_value)` and `{% include user_value %}` are the same vulnerability as a user-supplied body: the caller picks which code runs, and on many engines the name resolves as a filesystem path, which makes it an arbitrary-file read as well.
- **Minimize the render context.** Pass the specific values the template needs, never the ambient objects — application config, settings, the request, `self`, a module, a live ORM instance. This is the control that still holds when a sandbox is escaped: an engine that cannot execute code can still print whatever it can reach, and configuration objects hold database URLs and signing keys.
- Treat a **second render pass** as compilation of whatever survived the first. If rendered output is parsed again, a user value that was inert data on pass one is `{{ }}` code on pass two. The fix is architectural — stop re-parsing rendered output. Neutralizing metacharacters before the second pass is a fallback that has to enumerate every delimiter of every engine, including ones a custom delimiter configuration introduces, and two adjacent fields can produce a delimiter that neither of them contains.