From 315c73eddb44fc65d6b05206b371ca3d93a3b5f1 Mon Sep 17 00:00:00 2001 From: Dominik Bezic Date: Thu, 1 Oct 2026 13:48:25 +0100 Subject: [PATCH 1/3] update package versions --- pyproject.toml | 9 +++++-- uv.lock | 65 ++++++++++++++++++++++++++++++++------------------ 2 files changed, 49 insertions(+), 25 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 34c1150f..e52c1b9a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -89,7 +89,9 @@ constraint-dependencies = [ # --- AI Frameworks --- "agent-framework >= 1.0.0", "agent-framework-core >= 1.0.0", - "langchain-core >= 0.1.0", + "langchain-core >= 1.3.3", + "langsmith >= 0.8.18", + "mcp >= 1.28.1", "openai-agents >= 0.2.6", "semantic-kernel >= 1.39.3", # google-adk >=1.28.1,<2.0.0 - security remediation. Fixes CVE-2026-4810 @@ -111,10 +113,13 @@ constraint-dependencies = [ # --- Data Validation & Utilities --- "aiohttp >= 3.8.0", + "anyio >= 4.14.2", "asyncio-throttle >= 1.0.0", "httpx >= 0.27.0", + "pyasn1 >= 0.6.4", "pydantic >= 2.0.0", - "PyJWT >= 2.8.0", + "PyJWT >= 2.13.0", + "python-multipart >= 0.0.30", "typing-extensions >= 4.0.0", # --- Tox --- diff --git a/uv.lock b/uv.lock index f9f13b87..6045e655 100644 --- a/uv.lock +++ b/uv.lock @@ -29,6 +29,7 @@ constraints = [ { name = "agent-framework", specifier = ">=1.0.0" }, { name = "agent-framework-core", specifier = ">=1.0.0" }, { name = "aiohttp", specifier = ">=3.8.0" }, + { name = "anyio", specifier = ">=4.14.2" }, { name = "asyncio-throttle", specifier = ">=1.0.0" }, { name = "azure-ai-agents", specifier = ">=1.0.0b251001" }, { name = "azure-ai-projects", specifier = ">=1.0.0b12,<2.0.0" }, @@ -38,7 +39,9 @@ constraints = [ { name = "black", specifier = ">=23.0.0" }, { name = "google-adk", specifier = ">=1.28.1,<2.0.0" }, { name = "httpx", specifier = ">=0.27.0" }, - { name = "langchain-core", specifier = ">=0.1.0" }, + { name = "langchain-core", specifier = ">=1.3.3" }, + { name = "langsmith", specifier = ">=0.8.18" }, + { name = "mcp", specifier = ">=1.28.1" }, { name = "microsoft-agents-activity", specifier = ">=0.4.0" }, { name = "microsoft-agents-hosting-core", specifier = ">=0.4.0" }, { name = "mypy", specifier = ">=1.0.0" }, @@ -49,13 +52,15 @@ constraints = [ { name = "opentelemetry-exporter-otlp-proto-grpc", specifier = ">=1.36.0" }, { name = "opentelemetry-instrumentation", specifier = ">=0.47b0" }, { name = "opentelemetry-sdk", specifier = ">=1.36.0" }, + { name = "pyasn1", specifier = ">=0.6.4" }, { name = "pydantic", specifier = ">=2.0.0" }, - { name = "pyjwt", specifier = ">=2.8.0" }, + { name = "pyjwt", specifier = ">=2.13.0" }, { name = "pytest", specifier = ">=7.0.0" }, { name = "pytest-asyncio", specifier = ">=0.21.0" }, { name = "pytest-cov", specifier = ">=7.0.0" }, { name = "pytest-mock", specifier = ">=3.10.0" }, { name = "python-dotenv", specifier = ">=1.0.0" }, + { name = "python-multipart", specifier = ">=0.0.30" }, { name = "ruff", specifier = ">=0.1.0" }, { name = "semantic-kernel", specifier = ">=1.39.3" }, { name = "tox", specifier = ">=4.23.0" }, @@ -693,15 +698,15 @@ wheels = [ [[package]] name = "anyio" -version = "4.12.1" +version = "4.14.2" source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } dependencies = [ { name = "idna" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/anyio/4.12.1/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/anyio/4.14.2/anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/anyio/4.12.1/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/anyio/4.14.2/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494" }, ] [[package]] @@ -2653,10 +2658,11 @@ wheels = [ [[package]] name = "langchain-core" -version = "1.2.28" +version = "1.3.3" source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } dependencies = [ { name = "jsonpatch" }, + { name = "langchain-protocol" }, { name = "langsmith" }, { name = "packaging" }, { name = "pydantic" }, @@ -2665,9 +2671,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "uuid-utils" }, ] -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/langchain-core/1.2.28/langchain_core-1.2.28.tar.gz", hash = "sha256:271a3d8bd618f795fdeba112b0753980457fc90537c46a0c11998516a74dc2cb" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/langchain-core/1.3.3/langchain_core-1.3.3.tar.gz", hash = "sha256:fa510a5db8efdc0c6ff41c0939fb5c00a0183c11f6b84233e892e3227ff69182" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/langchain-core/1.2.28/langchain_core-1.2.28-py3-none-any.whl", hash = "sha256:80764232581eaf8057bcefa71dbf8adc1f6a28d257ebd8b95ba9b8b452e8c6ac" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/langchain-core/1.3.3/langchain_core-1.3.3-py3-none-any.whl", hash = "sha256:18aae8506f37da7f74398492279a7d6efcee4f8e23c4c41c7af080eeb7ef7bd1" }, ] [[package]] @@ -2684,9 +2690,21 @@ wheels = [ { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/langchain-openai/1.1.12/langchain_openai-1.1.12-py3-none-any.whl", hash = "sha256:da71ca3f2d18c16f7a2443cc306aa195ad2a07054335ac9b0626dcae02b6a0c5" }, ] +[[package]] +name = "langchain-protocol" +version = "0.0.19" +source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/langchain-protocol/0.0.19/langchain_protocol-0.0.19.tar.gz", hash = "sha256:79d90a1425122ac87e8052e2ec054fbd09c3edbf341bdfb6397112a495c7bf8c" } +wheels = [ + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/langchain-protocol/0.0.19/langchain_protocol-0.0.19-py3-none-any.whl", hash = "sha256:4cdf879a492a35980fd859ae792d3c65458ccaae504e183c9a10d7eac1f0720f" }, +] + [[package]] name = "langsmith" -version = "0.7.31" +version = "0.8.18" source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } dependencies = [ { name = "httpx" }, @@ -2696,12 +2714,13 @@ dependencies = [ { name = "requests" }, { name = "requests-toolbelt" }, { name = "uuid-utils" }, + { name = "websockets" }, { name = "xxhash" }, { name = "zstandard" }, ] -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/langsmith/0.7.31/langsmith-0.7.31.tar.gz", hash = "sha256:331ee4f7c26bb5be4022b9859b7d7b122cbf8c9d01d9f530114c1914b0349ffb" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/langsmith/0.8.18/langsmith-0.8.18.tar.gz", hash = "sha256:32dde9c0e67e053e0fb738921fc8ced768af7b8fa83d7a0e3fd63597cf8776dd" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/langsmith/0.7.31/langsmith-0.7.31-py3-none-any.whl", hash = "sha256:0291d49203f6e80dda011af1afda61eb0595a4d697adb684590a8805e1d61fb6" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/langsmith/0.8.18/langsmith-0.8.18-py3-none-any.whl", hash = "sha256:3940183349993faef48e6c7d08e4822ee9cefd906b362d0e3c2d650314d2f282" }, ] [[package]] @@ -2894,7 +2913,7 @@ wheels = [ [[package]] name = "mcp" -version = "1.26.0" +version = "1.28.1" source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } dependencies = [ { name = "anyio" }, @@ -2912,9 +2931,9 @@ dependencies = [ { name = "typing-inspection" }, { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, ] -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/mcp/1.26/mcp-1.26.0.tar.gz", hash = "sha256:db6e2ef491eecc1a0d93711a76f28dec2e05999f93afd48795da1c1137142c66" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/mcp/1.28.1/mcp-1.28.1.tar.gz", hash = "sha256:d51e36a5f5644faea4f85ea649bfffa6bc6c26770d42798ad6a3de3d2ba69683" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/mcp/1.26/mcp-1.26.0-py3-none-any.whl", hash = "sha256:904a21c33c25aa98ddbeb47273033c435e595bbacfdb177f4bd87f6dceebe1ca" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/mcp/1.28.1/mcp-1.28.1-py3-none-any.whl", hash = "sha256:2726bca5e7193f61c5dde8b12500a6de2d9acf6d1a1c0be9e8c2e706437991df" }, ] [[package]] @@ -4692,11 +4711,11 @@ wheels = [ [[package]] name = "pyasn1" -version = "0.6.3" +version = "0.6.4" source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyasn1/0.6.3/pyasn1-0.6.3.tar.gz", hash = "sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyasn1/0.6.4/pyasn1-0.6.4.tar.gz", hash = "sha256:9c447d8431c947fe4c8febc4ed9e760bc29011a5b01e5c74b67025bd9fb8ce81" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyasn1/0.6.3/pyasn1-0.6.3-py3-none-any.whl", hash = "sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyasn1/0.6.4/pyasn1-0.6.4-py3-none-any.whl", hash = "sha256:deda9277cfd454080ec40b207fb6df82206a3a2688735233cdcd8d3d565f088b" }, ] [[package]] @@ -4883,11 +4902,11 @@ wheels = [ [[package]] name = "pyjwt" -version = "2.12.0" +version = "2.13.0" source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyjwt/2.12/pyjwt-2.12.0.tar.gz", hash = "sha256:2f62390b667cd8257de560b850bb5a883102a388829274147f1d724453f8fb02" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyjwt/2.13/pyjwt-2.13.0.tar.gz", hash = "sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyjwt/2.12/pyjwt-2.12.0-py3-none-any.whl", hash = "sha256:9bb459d1bdd0387967d287f5656bf7ec2b9a26645d1961628cda1764e087fd6e" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyjwt/2.13/pyjwt-2.13.0-py3-none-any.whl", hash = "sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728" }, ] [package.optional-dependencies] @@ -5034,11 +5053,11 @@ wheels = [ [[package]] name = "python-multipart" -version = "0.0.26" +version = "0.0.30" source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/python-multipart/0.0.26/python_multipart-0.0.26.tar.gz", hash = "sha256:08fadc45918cd615e26846437f50c5d6d23304da32c341f289a617127b081f17" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/python-multipart/0.0.30/python_multipart-0.0.30.tar.gz", hash = "sha256:0edfe0475c1f46ddd3ff7785a626f6118af32bdcf359bb21260367313bb32118" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/python-multipart/0.0.26/python_multipart-0.0.26-py3-none-any.whl", hash = "sha256:c0b169f8c4484c13b0dcf2ef0ec3a4adb255c4b7d18d8e420477d2b1dd03f185" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/python-multipart/0.0.30/python_multipart-0.0.30-py3-none-any.whl", hash = "sha256:830964def8c90607ac5daa00514e3987815865713ade8d20febc9177ac0c3c5b" }, ] [[package]] From 8c57c02ca16a86dc1efae8fd4af8e82e9aa1c375 Mon Sep 17 00:00:00 2001 From: Dominik Bezic Date: Thu, 1 Oct 2026 13:59:15 +0100 Subject: [PATCH 2/3] bump pyJWT --- pyproject.toml | 2 +- uv.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e52c1b9a..65b58f80 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -118,7 +118,7 @@ constraint-dependencies = [ "httpx >= 0.27.0", "pyasn1 >= 0.6.4", "pydantic >= 2.0.0", - "PyJWT >= 2.13.0", + "PyJWT >= 2.14.0", "python-multipart >= 0.0.30", "typing-extensions >= 4.0.0", diff --git a/uv.lock b/uv.lock index 6045e655..5f7b7827 100644 --- a/uv.lock +++ b/uv.lock @@ -54,7 +54,7 @@ constraints = [ { name = "opentelemetry-sdk", specifier = ">=1.36.0" }, { name = "pyasn1", specifier = ">=0.6.4" }, { name = "pydantic", specifier = ">=2.0.0" }, - { name = "pyjwt", specifier = ">=2.13.0" }, + { name = "pyjwt", specifier = ">=2.14.0" }, { name = "pytest", specifier = ">=7.0.0" }, { name = "pytest-asyncio", specifier = ">=0.21.0" }, { name = "pytest-cov", specifier = ">=7.0.0" }, @@ -4902,11 +4902,11 @@ wheels = [ [[package]] name = "pyjwt" -version = "2.13.0" +version = "2.14.0" source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyjwt/2.13/pyjwt-2.13.0.tar.gz", hash = "sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyjwt/2.14/pyjwt-2.14.0.tar.gz", hash = "sha256:77283c83fb56ecf566a886c757a714bc83668e38156de2cce8263302f42e0b86" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyjwt/2.13/pyjwt-2.13.0-py3-none-any.whl", hash = "sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyjwt/2.14/pyjwt-2.14.0-py3-none-any.whl", hash = "sha256:ad0cef71c756a56e74863c2919cf0985f72decbcfcb550ee2f422e7c62b5eedc" }, ] [package.optional-dependencies] From 896825ed9a25eb5386e5aa067817ce80d64e5acf Mon Sep 17 00:00:00 2001 From: Dominik Bezic Date: Thu, 1 Oct 2026 16:31:31 +0100 Subject: [PATCH 3/3] fix pr comments --- .../pyproject.toml | 2 + .../pyproject.toml | 3 + .../pyproject.toml | 4 + .../pyproject.toml | 4 + .../pyproject.toml | 5 + .../pyproject.toml | 5 + .../pyproject.toml | 4 + .../pyproject.toml | 4 + .../pyproject.toml | 2 + pyproject.toml | 2 + tests/test_published_security_constraints.py | 167 ++++++++++++++++++ uv.lock | 48 +++++ 12 files changed, 250 insertions(+) create mode 100644 tests/test_published_security_constraints.py diff --git a/libraries/microsoft-agents-a365-notifications/pyproject.toml b/libraries/microsoft-agents-a365-notifications/pyproject.toml index 8ec5d86b..1469cd06 100644 --- a/libraries/microsoft-agents-a365-notifications/pyproject.toml +++ b/libraries/microsoft-agents-a365-notifications/pyproject.toml @@ -30,6 +30,8 @@ dependencies = [ "microsoft-agents-activity", "microsoft-agents-hosting-core", "pydantic", + # Publish the security floor for the hosting SDK's transitive dependency. + "PyJWT", ] [project.urls] diff --git a/libraries/microsoft-agents-a365-observability-extensions-langchain/pyproject.toml b/libraries/microsoft-agents-a365-observability-extensions-langchain/pyproject.toml index 4ed21eea..7677b032 100644 --- a/libraries/microsoft-agents-a365-observability-extensions-langchain/pyproject.toml +++ b/libraries/microsoft-agents-a365-observability-extensions-langchain/pyproject.toml @@ -31,6 +31,9 @@ dependencies = [ "opentelemetry-sdk", "opentelemetry-instrumentation", "wrapt", + # Publish security floors for LangChain's transitive dependencies. + "langsmith", + "anyio", ] [project.urls] diff --git a/libraries/microsoft-agents-a365-observability-extensions-openai/pyproject.toml b/libraries/microsoft-agents-a365-observability-extensions-openai/pyproject.toml index 604f5196..e3d6acae 100644 --- a/libraries/microsoft-agents-a365-observability-extensions-openai/pyproject.toml +++ b/libraries/microsoft-agents-a365-observability-extensions-openai/pyproject.toml @@ -30,6 +30,10 @@ dependencies = [ "opentelemetry-api", "opentelemetry-sdk", "opentelemetry-instrumentation", + # Publish security floors for the framework's transitive dependencies. + "mcp", + "anyio", + "python-multipart", ] [project.urls] diff --git a/libraries/microsoft-agents-a365-observability-extensions-semantickernel/pyproject.toml b/libraries/microsoft-agents-a365-observability-extensions-semantickernel/pyproject.toml index 8c898b98..1ba97661 100644 --- a/libraries/microsoft-agents-a365-observability-extensions-semantickernel/pyproject.toml +++ b/libraries/microsoft-agents-a365-observability-extensions-semantickernel/pyproject.toml @@ -30,6 +30,10 @@ dependencies = [ "opentelemetry-api", "opentelemetry-sdk", "opentelemetry-instrumentation", + # Publish security floors for the framework's transitive dependencies. + "mcp", + "anyio", + "python-multipart", ] [project.urls] diff --git a/libraries/microsoft-agents-a365-tooling-extensions-agentframework/pyproject.toml b/libraries/microsoft-agents-a365-tooling-extensions-agentframework/pyproject.toml index f149536d..db3d0840 100644 --- a/libraries/microsoft-agents-a365-tooling-extensions-agentframework/pyproject.toml +++ b/libraries/microsoft-agents-a365-tooling-extensions-agentframework/pyproject.toml @@ -29,6 +29,11 @@ dependencies = [ "azure-identity", "typing-extensions", "httpx", + # Publish security floors for the framework's transitive dependencies. + "mcp", + "anyio", + "python-multipart", + "pyasn1", ] [project.urls] diff --git a/libraries/microsoft-agents-a365-tooling-extensions-googleadk/pyproject.toml b/libraries/microsoft-agents-a365-tooling-extensions-googleadk/pyproject.toml index e0b831ec..3c03576d 100644 --- a/libraries/microsoft-agents-a365-tooling-extensions-googleadk/pyproject.toml +++ b/libraries/microsoft-agents-a365-tooling-extensions-googleadk/pyproject.toml @@ -26,6 +26,11 @@ dependencies = [ "microsoft-agents-a365-tooling", "microsoft-agents-hosting-core", "google-adk", + # Publish security floors for the framework's transitive dependencies. + "mcp", + "anyio", + "python-multipart", + "pyasn1", ] [project.urls] diff --git a/libraries/microsoft-agents-a365-tooling-extensions-openai/pyproject.toml b/libraries/microsoft-agents-a365-tooling-extensions-openai/pyproject.toml index 1e118515..cca48319 100644 --- a/libraries/microsoft-agents-a365-tooling-extensions-openai/pyproject.toml +++ b/libraries/microsoft-agents-a365-tooling-extensions-openai/pyproject.toml @@ -26,6 +26,10 @@ dependencies = [ "microsoft-agents-a365-tooling", "openai-agents", "asyncio-throttle", + # Publish security floors for the framework's transitive dependencies. + "mcp", + "anyio", + "python-multipart", ] [project.urls] diff --git a/libraries/microsoft-agents-a365-tooling-extensions-semantickernel/pyproject.toml b/libraries/microsoft-agents-a365-tooling-extensions-semantickernel/pyproject.toml index a3cdbca7..0244a1bd 100644 --- a/libraries/microsoft-agents-a365-tooling-extensions-semantickernel/pyproject.toml +++ b/libraries/microsoft-agents-a365-tooling-extensions-semantickernel/pyproject.toml @@ -26,6 +26,10 @@ dependencies = [ "microsoft-agents-a365-tooling", "semantic-kernel", "aiohttp", + # Publish security floors for the framework's transitive dependencies. + "mcp", + "anyio", + "python-multipart", ] [project.urls] diff --git a/libraries/microsoft-agents-a365-tooling/pyproject.toml b/libraries/microsoft-agents-a365-tooling/pyproject.toml index 0f0780d9..be0f7197 100644 --- a/libraries/microsoft-agents-a365-tooling/pyproject.toml +++ b/libraries/microsoft-agents-a365-tooling/pyproject.toml @@ -26,6 +26,8 @@ dependencies = [ "pydantic", "typing-extensions", "microsoft-agents-hosting-core", + # Publish the security floor for the hosting SDK's transitive dependency. + "PyJWT", ] [project.urls] diff --git a/pyproject.toml b/pyproject.toml index 65b58f80..516d2b8f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,6 +71,8 @@ override-dependencies = [ # Centralized version constraints for all external dependencies # Individual package pyproject.toml files declare dependencies by name only (no version) # uv applies these constraints during dependency resolution +# Security-critical transitive dependencies must also be declared by affected libraries +# so the build backend includes their minimum versions in published wheel metadata. constraint-dependencies = [ # --- Observability (OpenTelemetry) --- "opentelemetry-api >= 1.36.0", diff --git a/tests/test_published_security_constraints.py b/tests/test_published_security_constraints.py new file mode 100644 index 00000000..9dd75c8c --- /dev/null +++ b/tests/test_published_security_constraints.py @@ -0,0 +1,167 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +"""Ensure published wheels enforce security floors without the workspace lockfile.""" + +import os +import shutil +import subprocess +import sys +import tomllib +from collections.abc import Iterator +from email.parser import BytesParser +from pathlib import Path +from zipfile import ZipFile + +import pytest +from packaging.requirements import Requirement +from packaging.utils import canonicalize_name +from packaging.version import Version + +REPO_ROOT = Path(__file__).resolve().parent.parent +PACKAGE_PREFIX = "microsoft-agents-a365-" +SECURITY_FLOORS = { + "anyio": "4.14.2", + "langchain-core": "1.3.3", + "langsmith": "0.8.18", + "mcp": "1.28.1", + "pyasn1": "0.6.4", + "pyjwt": "2.14.0", + "python-multipart": "0.0.30", +} +MCP_REQUIREMENTS = {"mcp", "anyio", "python-multipart", "pyjwt"} +EXPECTED_REQUIREMENTS = { + "notifications": {"pyjwt"}, + "observability-core": {"pyjwt"}, + "observability-extensions-agent-framework": {"pyjwt"}, + "observability-extensions-langchain": {"langchain-core", "langsmith", "anyio", "pyjwt"}, + "observability-extensions-openai": MCP_REQUIREMENTS, + "observability-extensions-semantic-kernel": MCP_REQUIREMENTS, + "observability-hosting": {"pyjwt"}, + "runtime": {"pyjwt"}, + "tooling": {"pyjwt"}, + "tooling-extensions-agentframework": MCP_REQUIREMENTS | {"pyasn1"}, + "tooling-extensions-azureaifoundry": {"pyjwt"}, + "tooling-extensions-googleadk": MCP_REQUIREMENTS | {"pyasn1"}, + "tooling-extensions-openai": MCP_REQUIREMENTS, + "tooling-extensions-semantickernel": MCP_REQUIREMENTS, +} + + +@pytest.fixture(scope="module") +def published_requirements( + tmp_path_factory: pytest.TempPathFactory, +) -> dict[str, list[Requirement]]: + """Build real wheels offline in a copy so backend rewrites cannot affect the checkout.""" + workspace = tmp_path_factory.mktemp("wheels") + shutil.copy2(REPO_ROOT / "pyproject.toml", workspace / "pyproject.toml") + shutil.copy2(REPO_ROOT / "LICENSE.md", workspace / "LICENSE.md") + shutil.copytree( + REPO_ROOT / "versioning" / "helper", + workspace / "versioning" / "helper", + ignore=shutil.ignore_patterns("__pycache__"), + ) + # Short directory names leave room for setuptools build paths on Windows. + for index, manifest in enumerate(sorted((REPO_ROOT / "libraries").glob("*/pyproject.toml"))): + shutil.copytree( + manifest.parent, + workspace / "libraries" / f"p{index}", + ignore=shutil.ignore_patterns("build", "dist", "*.egg-info", "__pycache__"), + ) + wheel_dir = workspace / "wheels" + wheel_dir.mkdir() + env = { + **os.environ, + "PYTHONPATH": str(workspace / "versioning" / "helper"), + "AGENT365_PYTHON_SDK_PACKAGE_VERSION": "0.0.0", + } + requirements: dict[str, list[Requirement]] = {} + for pyproject in sorted((workspace / "libraries").glob("*/pyproject.toml")): + original = pyproject.read_bytes() + result = subprocess.run( + [ + sys.executable, + "-c", + "import build_backend, sys; build_backend.build_wheel(sys.argv[1])", + str(wheel_dir), + ], + cwd=pyproject.parent, + env=env, + capture_output=True, + text=True, + timeout=120, + check=False, + ) + assert result.returncode == 0, ( + f"Wheel build failed for {pyproject.parent.name}:\n{result.stdout}\n{result.stderr}" + ) + assert pyproject.read_bytes() == original, f"Build did not restore {pyproject}" + + for wheel in sorted(wheel_dir.glob("*.whl")): + with ZipFile(wheel) as archive: + metadata_files = [ + name for name in archive.namelist() if name.endswith(".dist-info/METADATA") + ] + assert len(metadata_files) == 1, f"Expected one METADATA file in {wheel}" + metadata = BytesParser().parsebytes(archive.read(metadata_files[0])) + name = metadata["Name"] + assert name is not None + requirements[canonicalize_name(name)] = [ + Requirement(value) for value in metadata.get_all("Requires-Dist", []) + ] + + assert set(requirements) == {PACKAGE_PREFIX + suffix for suffix in EXPECTED_REQUIREMENTS} + return requirements + + +def _runtime_requirements( + name: str, published: dict[str, list[Requirement]] +) -> Iterator[Requirement]: + """Follow internal wheel requirements, never the lockfile or optional development extras.""" + pending = [name] + visited: set[str] = set() + while pending: + package = pending.pop() + if package in visited: + continue + visited.add(package) + for requirement in published[package]: + if requirement.marker is not None: + continue + dependency = canonicalize_name(requirement.name) + if dependency.startswith(PACKAGE_PREFIX): + assert str(requirement.specifier) == "==0.0.0" + pending.append(dependency) + else: + yield requirement + + +@pytest.mark.parametrize("package_suffix", EXPECTED_REQUIREMENTS) +def test_published_security_floors( + package_suffix: str, published_requirements: dict[str, list[Requirement]] +) -> None: + with (REPO_ROOT / "pyproject.toml").open("rb") as file: + root = tomllib.load(file) + constraints = { + canonicalize_name(requirement.name): requirement + for value in root["tool"]["uv"]["constraint-dependencies"] + for requirement in [Requirement(value)] + } + requirements = { + canonicalize_name(requirement.name): requirement + for requirement in _runtime_requirements( + PACKAGE_PREFIX + package_suffix, published_requirements + ) + } + # Also reject adding unrelated framework dependencies to lightweight packages. + assert requirements.keys() & SECURITY_FLOORS.keys() == EXPECTED_REQUIREMENTS[package_suffix] + for name in EXPECTED_REQUIREMENTS[package_suffix]: + requirement = requirements[name] + assert requirement.specifier == constraints[name].specifier, ( + f"{package_suffix} does not publish the centralized {name} constraint" + ) + assert any( + specifier.operator == ">=" + and Version(specifier.version) >= Version(SECURITY_FLOORS[name]) + for specifier in requirement.specifier + ), f"{package_suffix} allows vulnerable versions of {name}: {requirement.specifier}" diff --git a/uv.lock b/uv.lock index 5f7b7827..740549be 100644 --- a/uv.lock +++ b/uv.lock @@ -2961,6 +2961,7 @@ dependencies = [ { name = "microsoft-agents-activity" }, { name = "microsoft-agents-hosting-core" }, { name = "pydantic" }, + { name = "pyjwt" }, { name = "typing-extensions" }, ] @@ -2984,6 +2985,7 @@ requires-dist = [ { name = "microsoft-agents-hosting-core" }, { name = "mypy", marker = "extra == 'dev'" }, { name = "pydantic" }, + { name = "pyjwt" }, { name = "pytest", marker = "extra == 'dev'" }, { name = "pytest", marker = "extra == 'test'" }, { name = "pytest-asyncio", marker = "extra == 'dev'" }, @@ -3093,7 +3095,9 @@ provides-extras = ["dev", "test"] name = "microsoft-agents-a365-observability-extensions-langchain" source = { editable = "libraries/microsoft-agents-a365-observability-extensions-langchain" } dependencies = [ + { name = "anyio" }, { name = "langchain-core" }, + { name = "langsmith" }, { name = "microsoft-agents-a365-observability-core" }, { name = "opentelemetry-api" }, { name = "opentelemetry-instrumentation" }, @@ -3116,8 +3120,10 @@ test = [ [package.metadata] requires-dist = [ + { name = "anyio" }, { name = "black", marker = "extra == 'dev'" }, { name = "langchain-core" }, + { name = "langsmith" }, { name = "microsoft-agents-a365-observability-core", editable = "libraries/microsoft-agents-a365-observability-core" }, { name = "mypy", marker = "extra == 'dev'" }, { name = "opentelemetry-api" }, @@ -3136,11 +3142,14 @@ provides-extras = ["dev", "test"] name = "microsoft-agents-a365-observability-extensions-openai" source = { editable = "libraries/microsoft-agents-a365-observability-extensions-openai" } dependencies = [ + { name = "anyio" }, + { name = "mcp" }, { name = "microsoft-agents-a365-observability-core" }, { name = "openai-agents" }, { name = "opentelemetry-api" }, { name = "opentelemetry-instrumentation" }, { name = "opentelemetry-sdk" }, + { name = "python-multipart" }, ] [package.optional-dependencies] @@ -3158,7 +3167,9 @@ test = [ [package.metadata] requires-dist = [ + { name = "anyio" }, { name = "black", marker = "extra == 'dev'" }, + { name = "mcp" }, { name = "microsoft-agents-a365-observability-core", editable = "libraries/microsoft-agents-a365-observability-core" }, { name = "mypy", marker = "extra == 'dev'" }, { name = "openai-agents" }, @@ -3169,6 +3180,7 @@ requires-dist = [ { name = "pytest", marker = "extra == 'test'" }, { name = "pytest-asyncio", marker = "extra == 'dev'" }, { name = "pytest-asyncio", marker = "extra == 'test'" }, + { name = "python-multipart" }, { name = "ruff", marker = "extra == 'dev'" }, ] provides-extras = ["dev", "test"] @@ -3177,10 +3189,13 @@ provides-extras = ["dev", "test"] name = "microsoft-agents-a365-observability-extensions-semantic-kernel" source = { editable = "libraries/microsoft-agents-a365-observability-extensions-semantickernel" } dependencies = [ + { name = "anyio" }, + { name = "mcp" }, { name = "microsoft-agents-a365-observability-core" }, { name = "opentelemetry-api" }, { name = "opentelemetry-instrumentation" }, { name = "opentelemetry-sdk" }, + { name = "python-multipart" }, { name = "semantic-kernel" }, ] @@ -3199,7 +3214,9 @@ test = [ [package.metadata] requires-dist = [ + { name = "anyio" }, { name = "black", marker = "extra == 'dev'" }, + { name = "mcp" }, { name = "microsoft-agents-a365-observability-core", editable = "libraries/microsoft-agents-a365-observability-core" }, { name = "mypy", marker = "extra == 'dev'" }, { name = "opentelemetry-api" }, @@ -3209,6 +3226,7 @@ requires-dist = [ { name = "pytest", marker = "extra == 'test'" }, { name = "pytest-asyncio", marker = "extra == 'dev'" }, { name = "pytest-asyncio", marker = "extra == 'test'" }, + { name = "python-multipart" }, { name = "ruff", marker = "extra == 'dev'" }, { name = "semantic-kernel" }, ] @@ -3271,6 +3289,7 @@ source = { editable = "libraries/microsoft-agents-a365-tooling" } dependencies = [ { name = "microsoft-agents-hosting-core" }, { name = "pydantic" }, + { name = "pyjwt" }, { name = "typing-extensions" }, ] @@ -3289,6 +3308,7 @@ requires-dist = [ { name = "microsoft-agents-hosting-core" }, { name = "mypy", marker = "extra == 'dev'" }, { name = "pydantic" }, + { name = "pyjwt" }, { name = "pytest", marker = "extra == 'dev'" }, { name = "pytest-asyncio", marker = "extra == 'dev'" }, { name = "ruff", marker = "extra == 'dev'" }, @@ -3301,10 +3321,14 @@ name = "microsoft-agents-a365-tooling-extensions-agentframework" source = { editable = "libraries/microsoft-agents-a365-tooling-extensions-agentframework" } dependencies = [ { name = "agent-framework" }, + { name = "anyio" }, { name = "azure-identity" }, { name = "httpx" }, + { name = "mcp" }, { name = "microsoft-agents-a365-tooling" }, { name = "microsoft-agents-hosting-core" }, + { name = "pyasn1" }, + { name = "python-multipart" }, { name = "typing-extensions" }, ] @@ -3324,16 +3348,20 @@ test = [ [package.metadata] requires-dist = [ { name = "agent-framework" }, + { name = "anyio" }, { name = "azure-identity" }, { name = "black", marker = "extra == 'dev'" }, { name = "httpx" }, + { name = "mcp" }, { name = "microsoft-agents-a365-tooling", editable = "libraries/microsoft-agents-a365-tooling" }, { name = "microsoft-agents-hosting-core" }, { name = "mypy", marker = "extra == 'dev'" }, + { name = "pyasn1" }, { name = "pytest", marker = "extra == 'dev'" }, { name = "pytest", marker = "extra == 'test'" }, { name = "pytest-asyncio", marker = "extra == 'dev'" }, { name = "pytest-asyncio", marker = "extra == 'test'" }, + { name = "python-multipart" }, { name = "ruff", marker = "extra == 'dev'" }, { name = "typing-extensions" }, ] @@ -3382,9 +3410,13 @@ provides-extras = ["dev", "test"] name = "microsoft-agents-a365-tooling-extensions-googleadk" source = { editable = "libraries/microsoft-agents-a365-tooling-extensions-googleadk" } dependencies = [ + { name = "anyio" }, { name = "google-adk" }, + { name = "mcp" }, { name = "microsoft-agents-a365-tooling" }, { name = "microsoft-agents-hosting-core" }, + { name = "pyasn1" }, + { name = "python-multipart" }, ] [package.optional-dependencies] @@ -3402,15 +3434,19 @@ test = [ [package.metadata] requires-dist = [ + { name = "anyio" }, { name = "black", marker = "extra == 'dev'" }, { name = "google-adk" }, + { name = "mcp" }, { name = "microsoft-agents-a365-tooling", editable = "libraries/microsoft-agents-a365-tooling" }, { name = "microsoft-agents-hosting-core" }, { name = "mypy", marker = "extra == 'dev'" }, + { name = "pyasn1" }, { name = "pytest", marker = "extra == 'dev'" }, { name = "pytest", marker = "extra == 'test'" }, { name = "pytest-asyncio", marker = "extra == 'dev'" }, { name = "pytest-asyncio", marker = "extra == 'test'" }, + { name = "python-multipart" }, { name = "ruff", marker = "extra == 'dev'" }, ] provides-extras = ["dev", "test"] @@ -3419,9 +3455,12 @@ provides-extras = ["dev", "test"] name = "microsoft-agents-a365-tooling-extensions-openai" source = { editable = "libraries/microsoft-agents-a365-tooling-extensions-openai" } dependencies = [ + { name = "anyio" }, { name = "asyncio-throttle" }, + { name = "mcp" }, { name = "microsoft-agents-a365-tooling" }, { name = "openai-agents" }, + { name = "python-multipart" }, ] [package.optional-dependencies] @@ -3439,8 +3478,10 @@ test = [ [package.metadata] requires-dist = [ + { name = "anyio" }, { name = "asyncio-throttle" }, { name = "black", marker = "extra == 'dev'" }, + { name = "mcp" }, { name = "microsoft-agents-a365-tooling", editable = "libraries/microsoft-agents-a365-tooling" }, { name = "mypy", marker = "extra == 'dev'" }, { name = "openai-agents" }, @@ -3448,6 +3489,7 @@ requires-dist = [ { name = "pytest", marker = "extra == 'test'" }, { name = "pytest-asyncio", marker = "extra == 'dev'" }, { name = "pytest-asyncio", marker = "extra == 'test'" }, + { name = "python-multipart" }, { name = "ruff", marker = "extra == 'dev'" }, ] provides-extras = ["dev", "test"] @@ -3457,7 +3499,10 @@ name = "microsoft-agents-a365-tooling-extensions-semantickernel" source = { editable = "libraries/microsoft-agents-a365-tooling-extensions-semantickernel" } dependencies = [ { name = "aiohttp" }, + { name = "anyio" }, + { name = "mcp" }, { name = "microsoft-agents-a365-tooling" }, + { name = "python-multipart" }, { name = "semantic-kernel" }, ] @@ -3477,13 +3522,16 @@ test = [ [package.metadata] requires-dist = [ { name = "aiohttp" }, + { name = "anyio" }, { name = "black", marker = "extra == 'dev'" }, + { name = "mcp" }, { name = "microsoft-agents-a365-tooling", editable = "libraries/microsoft-agents-a365-tooling" }, { name = "mypy", marker = "extra == 'dev'" }, { name = "pytest", marker = "extra == 'dev'" }, { name = "pytest", marker = "extra == 'test'" }, { name = "pytest-asyncio", marker = "extra == 'dev'" }, { name = "pytest-asyncio", marker = "extra == 'test'" }, + { name = "python-multipart" }, { name = "ruff", marker = "extra == 'dev'" }, { name = "semantic-kernel" }, ]