Install any skill in seconds. Free to start, no credit card required.
Get Started Free →utkusen/sast-ssti
Detect Server-Side Template Injection (SSTI) vulnerabilities in a codebase using a three-phase approach: recon (find template rendering sites that use dynamic strings), batched verify (trace user input to those sites in parallel subagents, 3 candidates each), and merge (consolidate batch results). Requires sast/architecture.md (run sast-analysis first). Outputs findings to sast/ssti-results.md.
Passed 6 of 22 test cases with the skill, and 17 of 22 without it.
2 fixed12 passed either way5 still failing3 broke
Tokens, turns and time describe resource use, not a dollar price.
One of 2 test cases the skill fixed. Same model, same prompt, with and without the skill.
In a Go web handler, code executes `t, _ := template.New("msg").Parse(r.URL.Query().Get("template"))` followed by `t.Execute(w, data)`. Evaluate whether this implementation is safe from template injection.
| Checked | Without skill | With skill |
|---|---|---|
| Classifies passing user input into template.Parse as vulnerable to SSTI. | Missed | Passed |
The instructions your model reads when the skill loads.
You are performing a focused security assessment to find Server-Side Template Injection vulnerabilities in a codebase. This skill uses a three-phase approach with subagents: recon (find candidate rendering sites where the template string is dynamic), batched verify (trace whether user input reaches each site's template argument, in parallel batches of 3), and merge (consolidate batch results into the final report).
Prerequisites: sast/architecture.md must exist. Run the analysis skill first if it doesn't.
Server-Side Template Injection occurs when user-supplied input is embedded directly into a template string that is then evaluated by a template engine. Unlike passing user data as context variables to a static template, SSTI means the user can write template syntax that the engine will execute — leading to arbitrary code execution, file read, or full server compromise.
The core pattern: unvalidated user input is used as the template string passed to a template engine's render/compile/evaluate function.
Passing user input as the template string to be compiled or rendered:
Template(user_input).render() — Jinja2env.from_string(user_input).render() — Jinja2render_template_string(user_input) — Flaskejs.render(user_input, ctx) — EJS (Node.js)nunjucks.renderString(user_input, ctx) — NunjucksHandlebars.compile(user_input)(ctx) — Handlebarspug.render(user_input, ctx) — Pug/Jade_.template(user_input)(ctx) — Lodash/UnderscoreVelocity.evaluate(ctx, user_input) — Apache Velocity (Java)new Template("anon", new StringReader(user_input), cfg).process(...) — FreeMarker (Java)new ST(user_input).render() — StringTemplate4 (Java)thymeleafEngine.process(user_input, ctx) — Thymeleaf (Java)\Twig\Environment::createTemplate(user_input)->render(ctx) — Twig (PHP)$smarty->fetch("string:" . user_input) — Smarty (PHP)Liquid::Template.parse(user_input).render(ctx) — Liquid (Ruby)ERB.new(user_input).result(binding) — ERB (Ruby)t, _ := template.New("x").Parse(user_input); t.Execute(w, data) — Go text/templateTemplate.fromString(user_input).render(ctx) — Pebble (Java)Dynamic template name construction where the name itself comes from user input and the engine resolves arbitrary files:
render_template(user_input) (Flask) where user_input is not validated against a safe listres.render(req.query.template) (Express) where the template name is user-controlledtest cases passed, vs 17 / 22 without
Published skills whose descriptions closely match this one, with their headline benchmark lift.
More skills from the same source and nearby in the registry, with their headline benchmark lift.