fix: reject whitespace-only task_summary in orchestrator context capture (#459) - #580
Open
Deez-Automations wants to merge 1 commit into
Conversation
…ure (GenAI-Security-Project#459) OrchestratorAgent._capture_agent_context used `if summary:` to decide whether to store an upstream agent's task_summary for downstream propagation via _enrich_with_prior_context. A whitespace-only string (" ") is truthy in Python, so it passed the guard and got injected verbatim into every subsequent agent's task description as pure noise under a "Prior workflow context (include all directives when acting)" header -- wasted tokens, and per the issue, a guard a prompt-injection payload could hide behind (non-empty but content-free). Fixed to `if isinstance(summary, str) and summary.strip():`. The isinstance check goes one step past the issue's own suggested `summary and summary.strip()`: task_summary is only guaranteed to be a string today because it's schema-typed under a strict LLM tool-call contract (base.py's complete_task schema), not by any local check -- `.strip()` alone would raise AttributeError on a truthy non-string, converting "store nothing" into an unrelated delegation-attempt failure. Security review caught this; verified independently against the schema before applying. A real summary with incidental leading/trailing whitespace is still stored verbatim (not stripped) -- only the truthiness check changed, not the stored value. 9 new tests in test_orchestrator_whitespace_context.py: whitespace-only, empty-string, missing-key, tab/newline-only, non-string, a real summary, a real summary with incidental whitespace kept verbatim, and two integration tests through _enrich_with_prior_context confirming the polluted context never reaches a downstream task description.
8 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #459.
The bug
OrchestratorAgent._capture_agent_contextdecides whether to store an upstream agent'stask_summaryfor propagation to downstream agents (via_enrich_with_prior_context, which injects every stored(agent_label, summary)pair verbatim into the next agent's task description under a "Prior workflow context" header). The guard wasif summary:— a whitespace-only string (" ") is truthy in Python, so it passed the guard and got stored and propagated as pure noise.Per the issue: this wastes tokens, and the guard being satisfiable by a non-empty-but-content-free string is a soft spot a prompt-injection payload could exploit to slip something past it undetected.
Scope check
Verified against current source before writing anything: the issue's line numbers (421-422) match exactly, and this is the sole call site of this truthiness pattern in the file — the two other
task_summaryreads (an event-emission call, already[:200]-truncated, and a logging call) don't gate storage, so neither needed the fix. All 6 delegation methods (onboarding, invoice, fraud, payments, system_maintenance, communication agents) funnel through this one method, so the single fix covers all 6.Fix
Went one step past the issue's own suggested
summary and summary.strip(). Security review caught that.strip()alone would raiseAttributeErroron a truthy non-stringtask_summary— today that's unreachable in practice (schema-typed as"string"under a strict LLM tool-call contract inbase.py'scomplete_taskschema), verified independently against that schema before applying, but relying on an external API contract alone for local crash-safety doesn't match this repo's own boundary-validation convention. Added theisinstancecheck so a violation of that invariant fails safe (summary just isn't stored) instead of turning into anAttributeErrorthat burns one of the agent's limited delegation retries.A real summary with incidental leading/trailing whitespace is still stored verbatim — only the truthiness check changed, the stored value itself is untouched.
Tests
9 new tests in
tests/unit/agents/test_orchestrator_whitespace_context.py(no prior test file existed forOrchestratorAgentat all): whitespace-only, empty-string, missing-key, tab/newline-only, non-string, a real summary, a real summary with incidental whitespace kept verbatim, plus two integration tests through_enrich_with_prior_contextconfirming the polluted context never reaches a downstream task description. Confirmed RED before the fix (4/8 failed, exactly the whitespace-related cases), GREEN after.Test plan
tests/unit/agents/,tests/unit/ctf/) run — same 3 pre-existing failures intest_specialized_agents.py(unrelated agent classes), already confirmed unrelated in a sibling PR's testingorigin/main— cleanorchestrator.py; verified the auto-merge is genuinely non-overlapping (different, unrelated methods), not just conflict-free