Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 6 additions & 15 deletions src/assets/__tests__/__snapshots__/assets.snapshot.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -4894,17 +4894,12 @@ def create_agent():
hooks=[ConfigBundleHook()],
)
{{else}}
_agent = None

def get_or_create_agent():
global _agent
if _agent is None:
_agent = Agent(
model=load_model(),
system_prompt=DEFAULT_SYSTEM_PROMPT,
tools=tools
)
return _agent
def create_agent():
return Agent(
model=load_model(),
system_prompt=DEFAULT_SYSTEM_PROMPT,
tools=tools
)
{{/if}}
{{/if}}

Expand All @@ -4918,11 +4913,7 @@ async def invoke(payload, context):
user_id = getattr(context, 'user_id', 'default-user')
agent = get_or_create_agent(session_id, user_id)
{{else}}
{{#if hasConfigBundle}}
agent = create_agent()
{{else}}
agent = get_or_create_agent()
{{/if}}
{{/if}}

# Execute and format response
Expand Down
21 changes: 6 additions & 15 deletions src/assets/python/http/strands/base/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,17 +173,12 @@ def create_agent():
hooks=[ConfigBundleHook()],
)
{{else}}
_agent = None

def get_or_create_agent():
global _agent
if _agent is None:
_agent = Agent(
model=load_model(),
system_prompt=DEFAULT_SYSTEM_PROMPT,
tools=tools
)
return _agent
def create_agent():
return Agent(
model=load_model(),
system_prompt=DEFAULT_SYSTEM_PROMPT,
tools=tools
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same global-singleton bug exists in the TypeScript HTTP Strands template at src/assets/typescript/http/strands/base/main.ts (lines 39–51), which uses a module-level let cachedAgent: Agent | null = null and a getOrCreateAgent() that caches the first agent forever. The TS Strands SDK has the same conversation-accumulation behavior on Agent instances, so the same cross-session history leak applies.

Please apply the equivalent fix there (replace getOrCreateAgent with a per-invocation factory) and update the corresponding snapshot. Otherwise the security fix only lands for half the users.

{{/if}}
{{/if}}

Expand All @@ -197,11 +192,7 @@ async def invoke(payload, context):
user_id = getattr(context, 'user_id', 'default-user')
agent = get_or_create_agent(session_id, user_id)
{{else}}
{{#if hasConfigBundle}}
agent = create_agent()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question / possible follow-up: src/assets/python/a2a/strands/base/main.py (lines 97 and 99–103) also constructs a single Agent at import time and passes it to serve_a2a(StrandsA2AExecutor(agent)). If StrandsA2AExecutor reuses that agent instance across requests without resetting conversation state, this template has the same cross-session leak.

Could you confirm whether StrandsA2AExecutor clones / scopes conversation per request, or if A2A is similarly affected? If it is affected, please either:

  1. Fix it in this PR (preferred — keeps the security fix complete), or
  2. File a follow-up issue and link it here so it isn't lost.

The PR description and linked issue (#809) only mention the HTTP template, so it's worth being explicit about the scope.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ## Test plan checkbox for the manual two-session leakage verification is still unchecked. Given that this is fixing a security/privacy bug, please run that verification before merging (or note in the PR why it isn't necessary).

{{else}}
agent = get_or_create_agent()
{{/if}}
{{/if}}

# Execute and format response
Expand Down
Loading