# Unescaped config injected into build scripts

User-controlled config values rendered raw into Dockerfiles or build scripts, letting newlines or braces break out

_Area: Software supply chain and CI/CD. Seen in 3 disclosed reports; latest 2026-07-16. Updated 2026-10-30._

## How it works
A build tool takes a user-supplied config value (a repository URL, an env var name, a base image) and pastes it as text into a generated build artifact: a Dockerfile, a Gradle script, a shell line. The code assumes the value is a single safe token. But the value is just a string, and the template engine used does no escaping. A newline ends the current directive and starts a new one; a `}` closes the surrounding block. The injected text becomes a real build instruction and runs on the build host, often as root.

## Where it shows up
- Go `text/template` (not `html/template`) with `{{ . }}` emitting user input into a DSL: Groovy/Gradle, YAML, shell [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-3v79-m2cg-89ww.md)
- Jinja templates rendering config fields into Dockerfile `FROM`, `ARG`, `ENV`, `RUN` lines [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-w2pm-x38x-jp44.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-78f9-r8mh-4xm2.md)
- A field quoted or validated while a sibling field in the same template is left raw [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-w2pm-x38x-jp44.md)
- Config structs with no validator/converter on fields that reach a template [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-3v79-m2cg-89ww.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-78f9-r8mh-4xm2.md)
- Multi-line values accepted where only a single token is expected

## How to review
Trace every user-controlled config field to the template that renders it. The invariant: any value placed into a build artifact must be escaped for that artifact's syntax, or rejected if it contains characters outside the expected token set (newlines, braces, quotes, spaces). Check that the escaping matches the destination context, not just the source language. For each template, list every `{{ }}` and confirm each is quoted or validated. Render the template with a multi-line and a brace-containing value and confirm the output still has one directive, not several [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-3v79-m2cg-89ww.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-78f9-r8mh-4xm2.md).

## Variants seen
- Groovy injection via `runtimeAttributes.repositories` into `build.gradle` using `text/template` [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-3v79-m2cg-89ww.md)
- Newline injection via `envs[*].name` into Dockerfile `ARG`/`ENV` lines, while only `env.value` was quoted [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-w2pm-x38x-jp44.md)
- Newline injection via `docker.base_image` into the `FROM` line [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-78f9-r8mh-4xm2.md)

## Why fixes fail
Fixes quoted one field and missed siblings in the same template: prior CVEs quoted `system_packages` but left `env.name` raw [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-w2pm-x38x-jp44.md), and the same `base_v2.j2` still rendered `base_image` unescaped [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-78f9-r8mh-4xm2.md). Field-by-field patching leaves the rest of the template exposed.

## Impact and severity
Build-time code execution on the build host, often as root, from unauthenticated input in some cases [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-3v79-m2cg-89ww.md). Severity is high because the build host holds credentials, signing keys and release artifacts. It worsens when the value is reachable without auth and when the build runs privileged.

## Evidence
- E1: https://bugbountylibrary.com/r/ghsa%3AGHSA-3v79-m2cg-89ww.md
- E2: https://bugbountylibrary.com/r/ghsa%3AGHSA-w2pm-x38x-jp44.md
- E3: https://bugbountylibrary.com/r/ghsa%3AGHSA-78f9-r8mh-4xm2.md
