Skip to content

[Security] Consensus translator multi-statement expression smuggling -> sandbox code execution #335

Description

Audit finding: BUG-R2-S1-A4-H2 | CWE-94 | CVSS 7.7 | PoC: reproduced, EXP confirmed

Summary

TranslationLayer._validate_math_output() (translator.py:84-108) — the documented OWASP LLM05 gate before the expression is interpolated into result = {expression} Python code — validates characters, not shape. Three gaps:

  1. The safe-charset ^[0-9+\-*/().\sa-z]+$ (translator.py:99) includes \s, which matches newlines; the pre-match replace(' ', '') strips only literal spaces. Multi-line expressions pass.
  2. The dangerous_keywords denylist (translator.py:90) omits import (it lists __import__, which requires double underscores).
  3. Neither the Pydantic schema (MathVerificationTask.expression) nor _generate_verification_code (consensus_verifier.py:922, raw f"result = {task.expression}") enforces a single-expression structure.

A multi-line expression becomes multi-statement module code that also passes both downstream gates (pattern scanner is warning-only for os.system; executor AST gate blocklists only 7 module roots / 8 builtins).

Proof of concept

Provider returns expression:

1
import pandas.io.common as pc
pc.os.system(chr(105) + chr(100))

-> generated code result = 1\nimport pandas.io.common as pc\npc.os.system(chr(105)+chr(100)) executes in the sandbox. import posix / posix.system(...) variant triggers zero scanner matches. Engines run in parallel, so execution happens during the engine phase before any verdict.

Delivery: attacker-influenced provider output (default local Ollama, operator CUSTOM_BASE_URL, MITM'd provider — deterministic) or prompt injection via tenant query (probabilistic). Container execution requires the bare-metal deployment envelope; the gate acceptance itself occurs in every mode.

Code anchors

File Line Symbol
src/qwed_new/core/translator.py 90/99 denylist missing import; charset admits newlines
src/qwed_new/core/schemas.py 34 no single-expression constraint
src/qwed_new/core/consensus_verifier.py 922/679/542 raw interpolation; engine phase
src/qwed_new/core/secure_code_executor.py 330 AST gate (companion issue)

Suggested fix

Structural gate in _validate_math_output: ast.parse(task.expression, mode="eval") — reject anything that is not exactly one Python expression (statements, imports, multi-line input fail by construction). See audit patch sketch.

Related: executor gate bypass issue (module-indirection gadgets), OWASP LLM05/LLM06.


Source: OpenVuln external audit of QWED-AI/qwed-verification (snapshot v7.0.0, re-verified against current main v7.1.0). All code anchors below were spot-checked against current main before filing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P0Critical priority — blocks trust boundary completionbugSomething isn't workingexternal-auditFindings from external security auditsqwed securityqwed securitysecuritySecurity finding from adversarial auditverificationverification

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions