From 6af8617ff886f6a3d477e2a383204e2518511a06 Mon Sep 17 00:00:00 2001 From: Edgar Arakelyan Date: Wed, 7 Oct 2026 04:15:13 -0700 Subject: [PATCH] fix(store): an @local run hands its agents none of the configured store's credentials LocalRunObjectStore.shared_credentials_env delegated to the configured store, so under a deployment whose store shares its session (the Scale sdk's does, by design), every agent and judge an @local run deployed received the caller's own cloud credentials. An @local run's objects reach its sandboxes through grants, staging or pushed bytes, so it now shares none. snapshot_env already handed services nothing inside an @local run. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/agent_env/store/routing.py | 4 +++- tst/unit/store/routing_test.py | 25 ++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/agent_env/store/routing.py b/src/agent_env/store/routing.py index 282c5622..71442e35 100644 --- a/src/agent_env/store/routing.py +++ b/src/agent_env/store/routing.py @@ -556,7 +556,9 @@ def signed_post(self, url_prefix: str, *, expires_in: int = 3600, max_bytes: int return self._writing(url_prefix).signed_post(url_prefix, expires_in=expires_in, max_bytes=max_bytes) def shared_credentials_env(self) -> dict[str, str]: - return self.configured.shared_credentials_env() + """None: an ``@local`` run's objects reach its sandboxes through grants, staging or pushed bytes, so the + configured store's credentials, the caller's own, stay with the caller.""" + return {} def issue_read_grant(self, object_url: str, *, expires_in: int | None = None) -> HttpGetGrant: return self._at(object_url).issue_read_grant(object_url, **_lifetime(expires_in)) diff --git a/tst/unit/store/routing_test.py b/tst/unit/store/routing_test.py index 7ce27a42..2c0f1377 100644 --- a/tst/unit/store/routing_test.py +++ b/tst/unit/store/routing_test.py @@ -4,17 +4,19 @@ import inspect from contextlib import nullcontext from datetime import datetime, timedelta, timezone +from types import SimpleNamespace import click import pytest from click.testing import CliRunner +from agent_env.a2a_agent.a2a_agent import A2AAgent from agent_env.artifact.artifacts.docker_image import DockerImageArtifact from agent_env.artifact.artifacts.file import FileArtifact from agent_env.artifact.artifacts.file_artifact_universe import FileArtifactUniverse from agent_env.artifact.store import get_artifact_store from agent_env.cli import cli -from agent_env.config import configure, get_config +from agent_env.config import configure, get_config, reset_config, set_object_store from agent_env.config.errors import ConfigError from agent_env.config.paths import state_root from agent_env.store import DuplicateKeyError, Filter, LocalSqliteDocumentStore, Sort, UpdateSpec, VersionedEntityStore @@ -524,3 +526,24 @@ def test_a_routing_wrapper_says_how_every_store_method_routes(base, wrapper): if not name.startswith("_") and (callable(member) or isinstance(member, property)) } assert public - set(vars(wrapper)) == _NOT_ROUTED + + +class _SharingStore(LocalFilesystemObjectStore): + """A configured store that hands its credentials to the workloads agent-env deploys.""" + + def shared_credentials_env(self) -> dict[str, str]: + return {"AWS_ACCESS_KEY_ID": "configured-id", "AWS_SECRET_ACCESS_KEY": "configured-secret"} + + +def test_an_agent_deployed_in_an_at_local_run_gets_none_of_the_configured_stores_credentials(tmp_path): + configured = _SharingStore(str(tmp_path / "configured")) + routed = LocalRunObjectStore(configured, LocalFilesystemObjectStore(str(tmp_path / "local"))) + agent = A2AAgent(id="solver", version=1, docker_image_artifact=SimpleNamespace(image_name="img")) + try: + set_object_store(configured) + assert agent._build_merged_env({}, 8000)["AWS_ACCESS_KEY_ID"] == "configured-id" + set_object_store(routed) + assert routed.shared_credentials_env() == {} + assert "AWS_ACCESS_KEY_ID" not in agent._build_merged_env({}, 8000) + finally: + reset_config()