From 7f73a2fd2a92f6d7bd1796547b31448b8e273bf9 Mon Sep 17 00:00:00 2001 From: Arnau Gris Date: Wed, 30 Sep 2026 08:27:48 +0200 Subject: [PATCH 1/2] Fix sync post-filter with unrelated OAuth credentials --- .../src/hyperforge_nucliadb/sync/agent.py | 2 + agents/nucliadb/tests/test_sync.py | 54 +++++++++++++++++++ hyperforge/pyproject.toml | 3 +- uv.lock | 24 +++++---- 4 files changed, 71 insertions(+), 12 deletions(-) diff --git a/agents/nucliadb/src/hyperforge_nucliadb/sync/agent.py b/agents/nucliadb/src/hyperforge_nucliadb/sync/agent.py index 3cc0b42d..59f59a77 100644 --- a/agents/nucliadb/src/hyperforge_nucliadb/sync/agent.py +++ b/agents/nucliadb/src/hyperforge_nucliadb/sync/agent.py @@ -398,6 +398,8 @@ async def _post_filter_configured_resources( connection_id, connection_credentials, ) in existing_credentials.items(): + if connection_id not in connections_by_resource: + continue source = connections[kb_source_id] if ( connection_id not in source.sync_configs diff --git a/agents/nucliadb/tests/test_sync.py b/agents/nucliadb/tests/test_sync.py index ddc6cca3..5aa4dd62 100644 --- a/agents/nucliadb/tests/test_sync.py +++ b/agents/nucliadb/tests/test_sync.py @@ -311,6 +311,60 @@ async def test_hybrid_dynamic_connection_is_authorized(): assert result == {"source": {SYNC_CONFIG_ID: ["connected-resource"]}} +async def test_hybrid_dynamic_connection_ignores_unrequested_credentials(): + agent = SyncAskAgent(SyncAskAgentConfig(sources=["source"])) + driver = SimpleNamespace( + config=SimpleNamespace(connection_ids=[]), + sync_configs={SYNC_CONFIG_ID: [EXTERNAL_CONNECTION_ID]}, + information={ + EXTERNAL_CONNECTION_ID: SimpleNamespace(provider="sharefile_oauth") + }, + validate_resources=AsyncMock(return_value=["connected-resource"]), + ) + resource = SimpleNamespace( + id="connected-resource", + origin=SimpleNamespace( + source_id=f"sync_config_{SYNC_CONFIG_ID}", + sync_metadata=SimpleNamespace(model_dump=MagicMock(return_value={})), + ), + ) + ndb = SimpleNamespace( + config=SimpleNamespace(kbid="kb"), + driver=SimpleNamespace(get_resource_by_id=AsyncMock(return_value=resource)), + ) + manager = SimpleNamespace( + drivers=SimpleNamespace(get=MagicMock(return_value=driver)) + ) + memory = MagicMock() + memory.get_session_id.return_value = "session" + memory.send_feedback = AsyncMock( + return_value=UserToAgentInteraction( + request_id="session", + response=json.dumps( + { + "existing_credentials": { + SYNC_CONFIG_ID: {EXTERNAL_CONNECTION_ID: "credential"}, + "00000000-0000-0000-0000-000000000002": { + "unrequested-connection": "credential" + }, + } + } + ), + ) + ) + + with patch("hyperforge_nucliadb.sync.agent.get_ndb_driver", return_value=ndb): + result = await agent._post_filter_configured_resources( + memory=memory, + manager=manager, # type: ignore[arg-type] + resources={"source": [resource.id]}, + allowed_connection_ids={SYNC_CONFIG_ID}, + ) + + assert result == {"source": {SYNC_CONFIG_ID: ["connected-resource"]}} + driver.validate_resources.assert_awaited_once() + + async def test_hybrid_resources_without_connections_skip_authorization(): agent = SyncAskAgent(SyncAskAgentConfig(sources=["source"])) driver = SimpleNamespace(config=SimpleNamespace(connection_ids=[])) diff --git a/hyperforge/pyproject.toml b/hyperforge/pyproject.toml index 5ecca712..254e7e69 100644 --- a/hyperforge/pyproject.toml +++ b/hyperforge/pyproject.toml @@ -28,11 +28,12 @@ dependencies = [ "prometheus_client", "mrflagly>=0.2.11", "aiodns", + "anyio>=4.15.1", "httpx>=0.28.1,<0.29", "httpcore>=1.0.9,<1.1", "jinja2", "jsonschema", - "RestrictedPython", + "RestrictedPython>=8.5", "databases[asyncpg]", "sqlalchemy", "cryptography", diff --git a/uv.lock b/uv.lock index 239dfdee..1347a4f6 100644 --- a/uv.lock +++ b/uv.lock @@ -253,15 +253,15 @@ wheels = [ [[package]] name = "anyio" -version = "4.14.2" +version = "4.15.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", marker = "python_full_version < '3.15'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/61/cc/a381afa6efea9f496eff839d4a6a1aed3bfafc7b3ab4b0d1b243a12573dd/anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f", size = 260176, upload-time = "2026-07-12T20:29:07.082Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/d2/f4d173e22df740bc37b1db102b386ba719b66e95b0f0d751f556b387e6d2/anyio-4.15.1.tar.gz", hash = "sha256:9f28306018cbd6d329e64a36d58256edff76dd996fe423bc957326e578b82a94", size = 276966, upload-time = "2026-09-05T10:42:39.44Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", size = 125813, upload-time = "2026-07-12T20:29:05.763Z" }, + { url = "https://files.pythonhosted.org/packages/12/b8/4bd346e22b28902df4d651910f5242c28d84e4a5c2435ca5c3f797ed7e2e/anyio-4.15.1-py3-none-any.whl", hash = "sha256:6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101", size = 132079, upload-time = "2026-09-05T10:42:37.923Z" }, ] [[package]] @@ -1069,6 +1069,7 @@ dependencies = [ { name = "a2a-sdk", extra = ["grpc", "http-server"] }, { name = "aiodns" }, { name = "alembic" }, + { name = "anyio" }, { name = "cryptography" }, { name = "databases", extra = ["asyncpg"] }, { name = "fastapi" }, @@ -1122,6 +1123,7 @@ requires-dist = [ { name = "a2a-sdk", extras = ["grpc", "http-server"], specifier = ">=1.0,<2" }, { name = "aiodns" }, { name = "alembic" }, + { name = "anyio", specifier = ">=4.15.1" }, { name = "cryptography" }, { name = "databases", extras = ["asyncpg"] }, { name = "fastapi" }, @@ -1144,7 +1146,7 @@ requires-dist = [ { name = "pydantic" }, { name = "pydantic-settings" }, { name = "redis", extras = ["hiredis"], specifier = ">=6.4.0" }, - { name = "restrictedpython" }, + { name = "restrictedpython", specifier = ">=8.5" }, { name = "ruff", specifier = ">=0.15.10" }, { name = "sentry-sdk" }, { name = "sqlalchemy" }, @@ -3537,11 +3539,11 @@ wheels = [ [[package]] name = "restrictedpython" -version = "8.1" +version = "8.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5f/1c/aec08bcb4ab14a1521579fbe21ceff2a634bb1f737f11cf7f9c8bb96e680/restrictedpython-8.1.tar.gz", hash = "sha256:4a69304aceacf6bee74bdf153c728221d4e3109b39acbfe00b3494927080d898", size = 838331, upload-time = "2025-10-19T14:11:32.531Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/3b/8e41f7cfabbb30b1013ebc7484303d6c87da2906ec432d69dea11d2f7d75/restrictedpython-8.5.tar.gz", hash = "sha256:4ed1269dbe3caa88db650d1af325198a952aeb1451eca05df0cfa65db4466215", size = 455879, upload-time = "2026-08-19T07:02:10.934Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/c0/3848f4006f7e164ee20833ca984067e4b3fc99fe7f1dfa88b4927e681299/restrictedpython-8.1-py3-none-any.whl", hash = "sha256:4769449c6cdb10f2071649ba386902befff0eff2a8fd6217989fa7b16aeae926", size = 27651, upload-time = "2025-10-19T14:11:30.201Z" }, + { url = "https://files.pythonhosted.org/packages/58/57/16ce3c721f5a33317e4110575d5c9976c0c45f7fd96ca2e0adeab06e6026/restrictedpython-8.5-py3-none-any.whl", hash = "sha256:6c70e0a3af13e830d37225788cdc8ab5804a8df4b500c135086eaef34b5c01e0", size = 30962, upload-time = "2026-08-19T07:02:09.553Z" }, ] [[package]] @@ -3860,11 +3862,11 @@ wheels = [ [[package]] name = "typing-extensions" -version = "4.15.0" +version = "4.16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, ] [[package]] From 097d6fb24adbbd17079ab75d3cb97efc6610a8b0 Mon Sep 17 00:00:00 2001 From: Arnau Gris Date: Wed, 30 Sep 2026 08:40:39 +0200 Subject: [PATCH 2/2] Fix BD --- uv.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uv.lock b/uv.lock index 1347a4f6..611e4eed 100644 --- a/uv.lock +++ b/uv.lock @@ -3228,11 +3228,11 @@ wheels = [ [[package]] name = "pyjwt" -version = "2.12.1" +version = "2.15.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564, upload-time = "2026-03-13T19:27:37.25Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/ea/5194e52748b0da83d71e082d75496eaec6e58f419f5e184786ded517e6a9/pyjwt-2.15.1.tar.gz", hash = "sha256:4f259e80cdfb6b3fc18a7de51fd1ef9ec79652f25019bae68975ca2468a34df8", size = 121252, upload-time = "2026-09-28T18:40:42.598Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726, upload-time = "2026-03-13T19:27:35.677Z" }, + { url = "https://files.pythonhosted.org/packages/50/ca/44de4e75f8aadc457f0634be3b542815078ded46dca30efb960edeecad6e/pyjwt-2.15.1-py3-none-any.whl", hash = "sha256:42d59d631f7768a1028a64c7ff581a9bf7519804daf91fc5b6c56e30eec5e193", size = 33860, upload-time = "2026-09-28T18:40:41.429Z" }, ] [package.optional-dependencies]