Sast SstiSAFE
Collection of agent skills to find vulnerabilities inside your web/mobile apps.
Overview
Collection of agent skills to find vulnerabilities inside your web/mobile apps.
d9c848ebb5f1OBSERVED · 2026-10-13What it tells the agent
The instruction file, verbatim from the audited commit — this is the text the model reads, and the surface the audit's instruction layer examines. Quoted here so you can judge it without cloning anything.
---
name: sast-ssti
description: >-
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. Use when asked to find SSTI or template injection bugs.
---
# Server-Side Template Injection (SSTI) Detection
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.
---
## What is SSTI
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.*
### What SSTI IS
- Passing user input as the template string to be compiled or rendered:
- `Template(user_input).render()` — Jinja2
- `env.from_string(user_input).render()` — Jinja2
- `render_template_string(user_input)` — Flask
- `ejs.render(user_input, ctx)` — EJS (Node.js)
- `nunjucks.renderString(user_input, ctx)` — Nunjucks
- `Handlebars.compile(user_input)(ctx)` — Handlebars
- `pug.render(user_input, ctx)` — Pug/Jade
- `_.template(user_input)(ctx)` — Lodash/Underscore
- `Velocity.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/template`
- `Template.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 list
- `res.render(req.query.template)` (Express) where the template name is user-controlled
### What SSTI is NOT
Do not flag these patterns:
- **User input as context data** (safe — the template is static, only the data changes):
```
render_template("profile.html", name=request.args.get("name"))
env.get_template("report.html").render(user=user_obj)
res.render("dashboard", { title: req.body.title })
```
- **XSS via template output**: If the template outputs unsanitized user data that is then rendered in a browser — that's XSS, not SSTI
- **Static templates with dynamic filenames validated against an allowlist**: If the template name comes from user input but is strictly validated against a hardcoded set of allowed template names, it's not SSTI
- **Sandboxed template engines configured with a restricted environment**: Liquid, Mustache, and similar logic-less engines cannot execute arbitrary code even if the template string comes from user input — but still flag them as "Needs Manual Review" unless you can confirm the engine is logic-less
### Patterns That Prevent SSTI
When you see these patterns, the code is likely **not vulnerable**:
**1. Static template file with dynamic context (most common safe pattern)**
```python
# Flask — static template, user input only in context dict
return render_template("user_profile.html", username=request.args.get("name"))
# Express — static view name
res.render("dashboard", { user: req.user })
```
**2. Allowlist validation for template names**
```python
ALLOWED_TEMPLATES = {"invoice.html", "receipt.html", "summary.html"}
template_name = request.args.get("tmpl", "invoice.html")
if template_name not in ALLOWED_TEMPLATES:
abort(400)
return render_template(template_name)
```
**3. Logic-less / sandboxed engines that don't support code execution**
```javascript
// Mustache — logic-less, cannot execute arbitrary code even if template is user-supplied
const output = Mustache.render(userTemplate, ctx); // lower risk, but still flag for review
```
---
## Vulnerable vs. Secure Examples
### Python — Flask / Jinja2
```python
# VULNERABLE: user input rendered as template string
@app.route('/greet')
def greet():
name = request.args.get('name', '')
template = f"<h1>Hello {name}!</h1>"
return render_template_string(template)
# Payload: ?name={{7*7}} → renders "49"
# RCE: ?name={{config.__class__.__init__.__globals__['os'].popen('id').read()}}
# SECURE: user input passed as context variable to a static template
@app.route('/greet')
def greet():
name = request.args.get('name', '')
return render_template("greet.html", name=name)
```
```python
# VULNERABLE: env.from_string with user-controlled template
@app.route('/preview')
def preview():
tmpl = request.form.get('template')
return Environment().from_string(tmpl).render()
# SECURE: load template from trusted file, pass user data as context
@app.route('/preview')
def previewTrust audit
SAFEgrade B · trust 89/100 Nothing in the source contradicts what it says it does. Grade A is reserved for packages that have also passed the behavioural sandbox.
| Layer | What it checks | Result |
|---|---|---|
| L0 | Provenance & inventory | PASS |
| L1 | Static analysis of the code | NA |
| L2 | Instruction surface (what it tells the agent) | PASS |
| L3 | Class-specific surface | PASS |
| L4 | Behavioural (sandbox) | SKIPPED |
What the source does
- Filesystem
- none-observed
- Network
- none-observed
- Shell
- none-observed
- Dependencies
- pinned
- Secrets in source
- none-found
Findings (0)
No findings outside the package's declared scope.
Gates applied: no_behavioural_pass.
d9c848ebb5f1full audit observations/trust-audit/skill/utkusen__sast-ssti.json · Report an issue / request a re-scanAudit history
Every audit this skill has had.
| Date | Source | Verdict | Grade | Score | Change |
|---|---|---|---|---|---|
| 2026-10-13 | d9c848ebb5f1 | SAFE | B | 89 | first audit |
Questions
What does the Sast Ssti skill do?
Collection of agent skills to find vulnerabilities inside your web/mobile apps.
Is Sast Ssti safe to install?
The audit found nothing in the source that contradicts what it says it does, and graded it B (89/100). Grade A is held back for packages that have also passed a sandboxed behavioural run, which is why a clean skill reads B.
What can Sast Ssti access on my machine?
The audit observed no filesystem, network or shell use at all in its source.
How current is this page?
The grade is for one exact copy of the source (d9c848ebb5f1), read on 2026-10-13. The repository is watched, and a new audit runs when it changes — this is the first audit.