Skip to content

bug(security): agent server still parses author-controlled YAML with bare safe_load — outside the ent#314 sweep #1965

Description

@vybe

utils/safe_yaml.py (ent#314, PR #1961) consolidated every author-controlled YAML reader in the backend onto one hardened loader — size cap, alias policy, duplicate-key rejection — and its guard test walks the whole backend with an empty allowlist, so a new bare yaml.safe_load there fails the build.

The agent server was outside that sweep. test_no_service_parses_yaml_without_the_shared_loader walks _BACKEND.rglob("*.py") only, and docker/base-image/agent_server/ still parses author-controlled documents with bare yaml.safe_load:

Site Document Backend-side policy for the same document
routers/info.py:108,173,252 template.yaml BUDGET (catalog) / REJECT (live agent-writable, credential_requirements_service)
routers/skills.py:66 skill frontmatter REJECT (skill_packaging)
routers/dashboard.py:205 dashboard.yaml REJECT (via compatibility/static_checks)
routers/files.py:244 .trinity/persistent-state.yaml —

Why it matters

The vector ent#314 measured is amplification at serialization, not parse: a 416 B level-6 anchor bomb resolves in ~0.001 s and blows up to ~110 MB when something walks the graph. The backend proxies /info and /dashboard from the agent server, so the same walk happens — just inside the container first, then across the wire.

Lower privilege than the catalog path ent#314 closed (this one needs an already-created agent whose workspace you can write, rather than any creator-role user pointing creation at a public repo), which is why #1961 shipped without it. But template.yaml inside a running container is exactly the "live agent-writable" document ent#314 assigns the strictest REJECT policy on the backend side.

Shape of the fix

Invariant #5's vendored-mirror pattern, as already done for credential_paths.py and model_context.py: vendor utils/safe_yaml.py byte-identically into docker/base-image/agent_server/, add a parity test, wire the six call sites with the policy their backend counterpart uses, and extend the ent#314 AST guard to walk docker/base-image/ too — otherwise the next agent-server YAML reader lands unguarded exactly the same way.

Acceptance

  • safe_yaml.py vendored into the agent server with a byte-parity test (the credential_paths.py shape)
  • All six sites on the shared loader, policy matching the backend counterpart for the same document
  • The ent314 AST guard covers docker/base-image/ with an empty-by-default allowlist
  • A level-6 bomb in a container's template.yaml is refused by GET /api/agents/{name}/info with a named error, not a timeout

Follow-up to trinity-enterprise#314 · surfaced in review of #1961.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions