Render a welcome banner without template injection

A welcome banner is built by concatenating the user’s display name into a Jinja template string, so a crafted name is evaluated as a template: it runs expressions, bypasses autoescaping and reads the render context. Repair render_welcome so the name is data, not template source.

Requires Node.js 22 or newer, which runs the tests, and Python 3.12 or newer, which runs your code (the command python3, or the program named in the PYTHON environment variable). This lab also needs Jinja2==3.1.6, the version the browser runs. Install it into a virtual environment: python3 -m venv .venv, then .venv/bin/pip install Jinja2==3.1.6, and run the tests with PYTHON=.venv/bin/python node --test exercise.test.mjs (on Debian or Ubuntu, install the python3-venv package first). No server needed. To work in a container instead, open the folder with VS Code Dev Containers or GitHub Codespaces: .devcontainer/devcontainer.json uses the Dev Containers Node.js 24 image, pinned by digest.
1. Unzip all files into one folder.
2. In that folder, run: node --test exercise.test.mjs
3. Edit welcome.py, then run the same command again.

The starter deliberately fails some tests. A successful repair passes every case, including cases that catch common wrong fixes. The browser runs this same grader with your code in Pyodide (CPython compiled to WebAssembly); locally it runs your code with python3. If you completed the optional first stage in the browser, Download my work adds your input as stage1-input.json and the tests include it. Only run code you wrote or reviewed.

1. Reproduce the injection
Run the starter. A display name of {{ 7*7 }} renders as 49, <b> is not escaped, and {{ account.reset_token }} prints the secret: the name is being compiled as part of the template.

2. Render a fixed template
Replace the concatenation with one constant template that has a {{ name }} placeholder, built once from an Environment. Pass the display name as the name variable to render(), so Jinja treats it as data and autoescapes it.

3. Keep the context minimal
Render with only name and plan. Never put the account object (or any secret) into the context: a fixed template can only output the variables it names, so there is nothing for an injected expression to reach even if one slipped through.

4. Check escaping and legitimate names
Confirm autoescape is on so <b>bob</b> becomes text and O’Brien & Co keeps its characters escaped, while {{ 7*7 }} and {% %} now appear literally.

SUPPLIED CONTRACT
render_welcome(display_name, account) returns an HTML string. display_name is user-controlled; account is a dict with a "plan" shown on the page and a "reset_token" that must never reach it. Build a single fixed template — `<p>Welcome back, {{ name }}! You are on the {{ plan }} plan.</p>` — with an Environment whose autoescape is on, and render it with name=display_name and plan=account["plan"]. Do not concatenate display_name (or account values) into the template source, and do not place the account object in the render context. Autoescaping escapes the values a template outputs, not text you paste into the template itself.

REFERENCE
reference.py contains an explained solution: A fixed template `<p>Welcome back, {{ name }}! You are on the {{ plan }} plan.</p>` rendered with name=display_name and plan="Pro" shows every input as text: Ana and María López appear unchanged, <b>bob</b> becomes &lt;b&gt;bob&lt;/b&gt;, O’Brien & Co becomes O&#39;Brien &amp; Co, and {{ 7*7 }} and {% for … %} stay literal. Because the account object is not in the context, {{ account.reset_token }} cannot read the token even though it is a legitimate-looking expression. SandboxedEnvironment exists for the rare case where a user must supply the template itself; it restricts attribute access and operators, but it does not stop a user-supplied template from reading context data or producing huge output, so it is not a substitute for keeping user input out of the template source.

SCOPE
This lab renders one banner. SandboxedEnvironment is for genuinely user-authored templates and must be paired with resource limits (a template can still produce enormous output) and a minimal, side-effect-free context; the Jinja docs state plainly that the sandbox alone is not a complete security solution. Autoescaping covers HTML or XML output only — JavaScript, CSS, URLs and other sinks need their own encoding.

NEXT TASK
Where in your app does a template, email or label get built by string concatenation from user or tenant data, and could that input reach the template engine as syntax rather than as a value?

SOURCE
https://jinja.palletsprojects.com/en/stable/sandbox/
