{"id":"GHSA-jfjg-vc52-wqvf","summary":"BentoML has Dockerfile Command Injection via system_packages in bentofile.yaml","details":"## Summary\n\nThe `docker.system_packages` field in `bentofile.yaml` accepts arbitrary strings that are interpolated directly into Dockerfile `RUN` commands without sanitization. Since `system_packages` is semantically a list of OS package names (data), users do not expect values to be interpreted as shell commands. A malicious `bentofile.yaml` achieves arbitrary command execution during `bentoml containerize` / `docker build`.\n\n## Affected Component\n\n- `src/_bentoml_sdk/images.py:85-89` — `.format(packages=\" \".join(packages))` into shell command\n- `src/bentoml/_internal/container/frontend/dockerfile/templates/base_debian.j2:13` — `{{ __options__system_packages | join(' ') }}`\n- `src/bentoml/_internal/bento/build_config.py:174` — No validation on `system_packages`\n- All distro install commands in `src/bentoml/_internal/container/frontend/dockerfile/__init__.py`\n\n## Affected Versions\n\nAll versions supporting `docker.system_packages` in `bentofile.yaml`, confirmed on 1.4.36.\n\n## Steps to Reproduce\n\n1. Create a project directory with:\n\n**service.py:**\n```python\nimport bentoml\n\n@bentoml.service\nclass MyService:\n    @bentoml.api\n    def predict(self) -\u003e str:\n        return \"hello\"\n```\n\n**bentofile.yaml:**\n```yaml\nservice: \"service:MyService\"\ndocker:\n  system_packages:\n    - \"curl && id \u003e /tmp/bentoml-pwned #\"\n```\n\n2. Run:\n```bash\nbentoml build\n```\n\n3. Examine the generated Dockerfile at `~/bentoml/bentos/my_service/\u003ctag\u003e/env/docker/Dockerfile`. Line 41 will contain:\n```dockerfile\nRUN apt-get install -q -y -o Dpkg::Options::=--force-confdef curl && id \u003e /tmp/bentoml-pwned #\n```\n\n4. Running `bentoml containerize my_service:\u003ctag\u003e` will execute `id \u003e /tmp/bentoml-pwned` as root during the Docker build.\n\n## Root Cause\n\nThe `system_packages` field values are treated as package names (data) by the user but are string-formatted directly into shell commands in the Dockerfile:\n\n```python\n# images.py:85-89\nself.commands.append(\n    CONTAINER_METADATA[self.distro][\"install_command\"].format(\n        packages=\" \".join(packages)  # No escaping\n    )\n)\n```\n\nWhere `install_command` is `\"apt-get install -q -y -o Dpkg::Options::=--force-confdef {packages}\"`.\n\nA `bash_quote` filter (wrapping `shlex.quote`) exists in the codebase and is registered in both Jinja2 environments, but it is only applied to environment variable values, never to `system_packages`.\n\n## Impact\n\n1. **Malicious repositories**: An attacker publishes an ML project with a crafted `bentofile.yaml`. Anyone who clones and builds it gets arbitrary code execution during `docker build`.\n2. **CI/CD compromise**: Automated pipelines running `bentoml containerize` on PRs that modify `bentofile.yaml` are vulnerable.\n3. **BentoCloud**: If BentoCloud builds images from user-supplied `bentofile.yaml`, this could achieve RCE on cloud infrastructure.\n4. **Supply chain**: Shared bentos or model repos in the BentoML ecosystem can contain malicious configs.\n\n## Suggested Fix\n\n### Option 1: Input validation (recommended)\n\nAdd a regex validator to `system_packages` in `build_config.py`:\n\n```python\nimport re\n\nVALID_PACKAGE_NAME = re.compile(r'^[a-zA-Z0-9][a-zA-Z0-9.+\\-_:]*$')\n\ndef _validate_system_packages(instance, attribute, value):\n    if value is None:\n        return\n    for pkg in value:\n        if not VALID_PACKAGE_NAME.match(pkg):\n            raise BentoMLException(\n                f\"Invalid system package name: {pkg!r}. \"\n                \"Package names may only contain alphanumeric characters, \"\n                \"dots, plus signs, hyphens, underscores, and colons.\"\n            )\n\nsystem_packages: t.Optional[t.List[str]] = attr.field(\n    default=None, validator=_validate_system_packages\n)\n```\n\n### Option 2: Output escaping\n\nApply `shlex.quote()` to each package name before interpolation in `images.py:system_packages()` and apply the `bash_quote` Jinja2 filter in `base_debian.j2`.","aliases":["CVE-2026-33744","PYSEC-2026-157"],"modified":"2026-06-08T20:15:16.553484510Z","published":"2026-03-26T07:32:44Z","database_specific":{"severity":"HIGH","github_reviewed":true,"github_reviewed_at":"2026-03-26T07:32:44Z","nvd_published_at":"2026-03-27T01:16:21Z","cwe_ids":["CWE-94"]},"references":[{"type":"WEB","url":"https://github.com/bentoml/BentoML/security/advisories/GHSA-jfjg-vc52-wqvf"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-33744"},{"type":"PACKAGE","url":"https://github.com/bentoml/BentoML"},{"type":"WEB","url":"https://github.com/pypa/advisory-database/tree/main/vulns/bentoml/PYSEC-2026-157.yaml"}],"affected":[{"package":{"name":"bentoml","ecosystem":"PyPI","purl":"pkg:pypi/bentoml"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.4.37"}]}],"versions":["0.0.1","0.0.2","0.0.3","0.0.5","0.0.6a0","0.0.7","0.0.7.dev0","0.0.8","0.0.8.post1","0.0.9","0.1.1","0.1.2","0.10.0","0.10.1","0.11.0","0.11.dev0","0.12.0","0.12.1","0.13.0","0.13.1","0.13.2","0.2.0","0.2.1","0.2.2","0.3.0","0.3.1","0.3.3","0.3.4","0.4.0","0.4.1","0.4.2","0.4.3","0.4.4","0.4.5","0.4.7","0.4.8","0.4.9","0.5.0","0.5.1","0.5.2","0.5.3","0.5.4","0.5.5","0.5.6","0.5.7","0.5.8","0.6.0","0.6.1","0.6.2","0.6.3","0.7.0","0.7.1","0.7.2","0.7.3","0.7.4","0.7.5","0.7.6","0.7.7","0.7.8","0.8.0","0.8.1","0.8.2","0.8.3","0.8.4","0.8.5","0.8.6","0.9.0","0.9.0rc0","0.9.1","0.9.2","1.0.0","1.0.0.dev0","1.0.0.dev1","1.0.0a1","1.0.0a2","1.0.0a3","1.0.0a4","1.0.0a5","1.0.0a6","1.0.0a7","1.0.0rc0","1.0.0rc1","1.0.0rc2","1.0.0rc3","1.0.10","1.0.11","1.0.12","1.0.13","1.0.14","1.0.15","1.0.16","1.0.17","1.0.18","1.0.19","1.0.2","1.0.20","1.0.21","1.0.22","1.0.23","1.0.24","1.0.25","1.0.3","1.0.4","1.0.5","1.0.6","1.0.7","1.0.8","1.0.9","1.1.0","1.1.1","1.1.10","1.1.11","1.1.2","1.1.3","1.1.4","1.1.5","1.1.6","1.1.7","1.1.8","1.1.9","1.2.0","1.2.0a0","1.2.0a1","1.2.0a2","1.2.0a3","1.2.0a4","1.2.0a5","1.2.0a6","1.2.0a7","1.2.0rc1","1.2.1","1.2.10","1.2.11","1.2.12","1.2.13","1.2.14","1.2.15","1.2.16","1.2.17","1.2.18","1.2.19","1.2.1a1","1.2.2","1.2.20","1.2.3","1.2.4","1.2.5","1.2.6","1.2.7","1.2.8","1.2.9","1.3.0","1.3.0a1","1.3.0a2","1.3.0a3","1.3.1","1.3.10","1.3.11","1.3.12","1.3.13","1.3.14","1.3.15","1.3.16","1.3.17","1.3.18","1.3.19","1.3.2","1.3.20","1.3.21","1.3.22","1.3.3","1.3.4.post1","1.3.5","1.3.6","1.3.7","1.3.8","1.3.9","1.4.0","1.4.0a1","1.4.0a2","1.4.1","1.4.10","1.4.11","1.4.12","1.4.13","1.4.14","1.4.15","1.4.16","1.4.17","1.4.18","1.4.19","1.4.2","1.4.20","1.4.21","1.4.22","1.4.23","1.4.24","1.4.25","1.4.26","1.4.27","1.4.28","1.4.29","1.4.3","1.4.30","1.4.31","1.4.32","1.4.33","1.4.34","1.4.35","1.4.36","1.4.4","1.4.5","1.4.6","1.4.7","1.4.8","1.4.9"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/03/GHSA-jfjg-vc52-wqvf/GHSA-jfjg-vc52-wqvf.json","last_known_affected_version_range":"\u003c= 1.4.36"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H"}]}