Skip to content

Commit a3a91ba

Browse files
committed
test: cover HTTP artifact reference rejection flow
1 parent 5c614e4 commit a3a91ba

1 file changed

Lines changed: 74 additions & 0 deletions

File tree

tests/unittests/cli/test_fast_api.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from google.adk.agents.run_config import RunConfig
3434
from google.adk.apps.app import App
3535
from google.adk.artifacts.base_artifact_service import ArtifactVersion
36+
from google.adk.artifacts.in_memory_artifact_service import InMemoryArtifactService
3637
from google.adk.cli import fast_api as fast_api_module
3738
from google.adk.cli.fast_api import get_fast_api_app
3839
from google.adk.errors.input_validation_error import InputValidationError
@@ -1701,6 +1702,79 @@ def test_save_artifact(test_app, create_test_session, mock_artifact_service):
17011702
assert stored["artifact"].text == "hello world"
17021703

17031704

1705+
def test_save_artifact_rejects_cross_scope_artifact_reference_http_flow(
1706+
mock_memory_service,
1707+
mock_agent_loader,
1708+
mock_eval_sets_manager,
1709+
mock_eval_set_results_manager,
1710+
):
1711+
"""The HTTP artifact flow rejects forged cross-scope artifact references."""
1712+
session_service = InMemorySessionService()
1713+
artifact_service = InMemoryArtifactService()
1714+
test_client = _create_test_client(
1715+
session_service,
1716+
artifact_service,
1717+
mock_memory_service,
1718+
mock_agent_loader,
1719+
mock_eval_sets_manager,
1720+
mock_eval_set_results_manager,
1721+
)
1722+
1723+
asyncio.run(
1724+
session_service.create_session(
1725+
app_name="victim-app",
1726+
user_id="victim",
1727+
session_id="victim-session",
1728+
state={},
1729+
)
1730+
)
1731+
asyncio.run(
1732+
session_service.create_session(
1733+
app_name="attacker-app",
1734+
user_id="attacker",
1735+
session_id="attacker-session",
1736+
state={},
1737+
)
1738+
)
1739+
asyncio.run(
1740+
artifact_service.save_artifact(
1741+
app_name="victim-app",
1742+
user_id="victim",
1743+
session_id="victim-session",
1744+
filename="private.txt",
1745+
artifact=types.Part(text="CROSS_SCOPE_SECRET"),
1746+
)
1747+
)
1748+
1749+
save_response = test_client.post(
1750+
"/apps/attacker-app/users/attacker/sessions/attacker-session/artifacts",
1751+
json={
1752+
"filename": "loot.txt",
1753+
"artifact": {
1754+
"fileData": {
1755+
"fileUri": (
1756+
"artifact://apps/victim-app/users/victim/sessions/"
1757+
"victim-session/artifacts/private.txt/versions/0"
1758+
),
1759+
"mimeType": "text/plain",
1760+
}
1761+
},
1762+
},
1763+
)
1764+
1765+
assert save_response.status_code == 400
1766+
assert (
1767+
save_response.json()["detail"]
1768+
== "Artifact references must stay within the same app and user scope."
1769+
)
1770+
1771+
load_response = test_client.get(
1772+
"/apps/attacker-app/users/attacker/sessions/attacker-session/"
1773+
"artifacts/loot.txt"
1774+
)
1775+
assert load_response.status_code == 404
1776+
1777+
17041778
def test_artifact_endpoints_support_nested_names(
17051779
test_app, create_test_session, mock_artifact_service
17061780
):

0 commit comments

Comments
 (0)