Skip to content

fix: reject control characters in docker.env values (Dockerfile injection) - #5700

Closed
feiiiiii5 wants to merge 1 commit into
bentoml:mainfrom
feiiiiii5:fix/docker-env-injection-guard
Closed

feiiiiii5 wants to merge 1 commit into
bentoml:mainfrom
feiiiiii5:fix/docker-env-injection-guard

Conversation

@feiiiiii5

Copy link
Copy Markdown

Fixes #5656

Root cause

Dict-form docker.env entries were interpolated into the generated Dockerfile completely raw (ARG {{ key }}={{ value }} in base.j2), while sibling paths were already hardened:

  • list-form env → regex-validated in _convert_env (build_config.py)
  • system_packages → bash_quote
  • base_image → normalize_line

A newline in a value terminates the ARG instruction — Dockerfile instructions are newline-delimited and quotes do not span lines, so any following lines execute as instructions at build time (reproduced offline via generate_containerfile; the reporter's exact payload renders a live RUN echo PWNED... step).

Fix (src/bentoml/_internal/bento/build_config.py, +45/-3)

  1. Dict branch of _convert_env: keys/values are checked against a control-character class ([\x00-\x1f\x7f]) and raise BentoMLException with guidance to use the list form or an .env file for complex values. This is the single choke point every template renders from, so v1/v2/debian/alpine/ubi8 are all covered.
    • Plain values keep working byte-identically: spaces are legal unquoted on ARG KEY=value lines ({"GREETING": "hello world"} renders exactly as before).
  2. BentoEnvSchema (the v2 envs: path): same guard added as attr validators on name/value. The issue notes this path as "hardened", but bash_quote only wraps in single quotes — a raw newline inside still breaks the instruction, so the guard is needed at definition time.

Tests (tests/unit/_internal/container/test_generate.py, +40 lines)

Following the existing same-family patterns (test_generate_containerfile_quotes_system_packages, ..._normalizes_custom_base_image):

  • newline payload in dict env raises BentoMLException at DockerOptions construction (before any template rendering)
  • legit values ("hello world", URL) render unchanged and no payload appears
  • BentoEnvSchema rejects control chars in value and name, accepts legit entries

Red→green verified against the pre-fix tree: 5 passed → 8 passed (the 3 new tests fail without the fix; the reporter's repro script also flips from "Injection present: True" to raising at construction). ruff check/format --check: zero new findings vs. baseline on both touched files.

…file instruction injection

Dict-form `docker.env` entries were interpolated into the generated
Dockerfile without any sanitization (`ARG {{ key }}={{ value }}` in base.j2),
while the list form was regex-validated and system_packages/base_image went
through bash_quote/normalize_line. A value containing a newline breaks out of
the ARG line and executes arbitrary instructions at build time.

- _convert_env dict branch now rejects keys/values containing newline or
  control characters (single choke point, covers every template)
- BentoEnvSchema (v2 envs path) gets the same guard via attr validators;
  bash_quote alone is insufficient because Dockerfile quotes do not span lines

Fixes bentoml#5656
@feiiiiii5
feiiiiii5 requested a review from a team as a code owner August 22, 2026 21:08
@feiiiiii5
feiiiiii5 requested review from ssheng and removed request for a team August 22, 2026 21:08
@feiiiiii5

Copy link
Copy Markdown
Author

Closing as part of a contribution-portfolio consolidation: my effort is now focused on a single project (failroute, a static analyzer for silent-failure anti-patterns) and its upstream follow-through. Happy to revisit if there is interest.

@feiiiiii5 feiiiiii5 closed this Aug 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: Dockerfile injection via unsanitized docker.env dict values

1 participant