Severity Daily

IT and AI security incidents, checked against the primary source

Tag: ADK

  • Google’s Agent Development Kit read any file for an unauthenticated caller, and the CVE landed 239 days after the fix

    Google’s Agent Development Kit read any file for an unauthenticated caller, and the CVE landed 239 days after the fix

    A CVE published this morning describes an unauthenticated arbitrary file read in Google’s Agent Development Kit; the code was fixed on January 8, 2026, and the record landed 239 days later with a changelog link for an advisory.

    What happened

    Google Cloud published CVE-2026-79707 at 11:17 a.m. UTC on Friday, September 4, 2026. The description is short and unambiguous: “A Path Traversal vulnerability in the builder endpoint in Google Cloud Agent Development Kit (ADK) versions 1.9.0 through 1.21.0 on Python allows an unauthenticated remote attacker to read arbitrary files using a crafted file_path query parameter.”

    Google scores it CVSS v4.0 8.7, High, on the vector AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N, with a Provider Urgency of Amber. The weakness is CWE-22. The affected package is google-adk on PyPI, from 1.9.0 up to but not including 1.22.0.

    The record carries two references and no advisory: a link to a heading in the project’s CHANGELOG.md, and a link to the commit. There is no Google Cloud security bulletin for it. There is no GitHub Security Advisory for it either — the GitHub Advisory Database listing for google-adk currently carries two entries, GHSA-rg7c-g689-fr3x (CVE-2026-4810, code injection and missing authentication, April 13, 2026) and GHSA-8qg5-x5vm-75jx (CVE-2026-18236, July 29, 2026), and this is neither of them.

    The code is public, so the timeline can be checked rather than inferred. Package upload times from PyPI: 1.9.0 was published July 31, 2025; 1.21.0, the last affected release, on December 12, 2025; and 1.22.0, which contains the fix, on January 8, 2026. The current release is 2.8.0, from August 26, 2026. The vulnerable window ran roughly five months. The gap between the fix shipping and the CVE describing it is 239 days.

    Downloading both wheels and reading the source shows exactly what the bug was. In 1.21.0, google/adk/cli/fast_api.py registers this route:

    @app.get("/builder/app/{app_name}", response_class=PlainTextResponse)
    async def get_agent_builder(app_name: str, file_path: Optional[str] = None, tmp: Optional[bool] = False):
        base_path = Path.cwd() / agents_dir
        agent_dir = base_path / app_name
        ...
        agent_file_path = agent_dir / file_path
        if not agent_file_path.is_file():
            return ""
        else:
            return FileResponse(path=agent_file_path, media_type="application/x-yaml", ...)

    The caller’s file_path goes straight into a path join and then into a FileResponse. The route declares no authentication dependency; a search of that module for Depends(, dependencies=, bearer, or API-key handling returns nothing.

    Two separate ways out of the directory follow from that one line. The obvious one is ../. The quieter one is that agent_dir / file_path uses pathlib’s join operator, and joining an absolute path discards the left-hand side entirely: Path("/agents/app") / "/etc/passwd" evaluates to /etc/passwd. No traversal sequence is needed, and a filter that only looks for .. would not see it. The media_type is set to application/x-yaml, but the response body is whatever the file contains.

    The fix in 1.22.0 addresses both. It adds _parse_file_path(), which rejects a path that starts with / and any path with a .. component; _has_parent_reference(); _get_app_root(), which validates the app name and confirms the resolved root stays under the agents directory; and _resolve_under_dir(), which resolves the final path and checks is_relative_to() before returning it. That is a correct fix, applied at four layers.

    The commit that carries it is titled “fix: Harden YAML builder tmp save/cleanup.” In the 1.22.0 release notes it sits in a list of bug fixes, between “Avoid local .adk storage in Cloud Run/GKE” and “Handle overriding of requirements when deploying to agent engine.” Nothing in the message, the changelog line, or the release notes says security, path traversal, or file disclosure.

    Why it matters

    The reflex reading is that this is a development server and therefore does not matter. That reading is wrong on two counts, and both are checkable in Google’s own materials.

    First, the vulnerable route is registered inside get_fast_api_app(), unconditionally. The function takes a required web parameter, and it is natural to assume the builder endpoints belong to the browser UI that flag controls. They do not. They are added after the base app is constructed, outside the if web: block, so they exist whether or not the web interface is being served. Running headless does not remove them.

    Second, get_fast_api_app() is not an internal detail. It is the function Google’s own Cloud Run deployment guide instructs developers to call from their main.py:

    from google.adk.cli.fast_api import get_fast_api_app
    app: FastAPI = get_fast_api_app(agents_dir=AGENT_DIR, session_service_uri=SESSION_SERVICE_URI,
                                   allow_origins=ALLOWED_ORIGINS, web=SERVE_WEB_INTERFACE)

    The same page’s example deployment command ends with --allow-unauthenticated, documented there as “Allows public access to the service. Remove this flag for private services.” Anyone who followed that guide between August 2025 and January 2026 and did not remove the flag put an unauthenticated arbitrary file read on the public internet, in a container that by construction holds agent configuration, and usually service-account material and API keys alongside it.

    There is a second detail in the version history worth noticing. When the endpoint first appeared in 1.9.0, it was decorated @working_in_progress("builder_get is not ready for use.") — the project’s own marker for code that is not meant to be relied on. By 1.21.0 that decorator is gone from the module entirely, while the unsafe path join is unchanged. The route was promoted out of work-in-progress status without the input handling being revisited. That is a specific and common failure mode: the guard rail that made the risk acceptable was the label, and the label was removed as a cleanup.

    The disclosure shape is the last thing, and it is the part that generalizes. Google Cloud published a second CVE from the same CNA at the same minute today: CVE-2026-4644, a privilege escalation in Integration Connectors, which does have a bulletin — GCP-2026-059, dated September 4, 2026 — and which says, in full, “No customer action is required. This vulnerability was patched on December 11, 2025.” For a managed service that is a defensible disclosure: Google fixed it in the fleet nine months ago, nobody has anything to do, and the CVE is a matter of record.

    ADK is not a managed service. It is a package a developer pins in a requirements file, and Google cannot upgrade it on anyone’s behalf. A team that pinned google-adk==1.20.0 last fall and has not moved since has been exposed for the entire period, is exposed right now, and until this morning had nothing in any feed to tell them. The two CVEs got the same treatment on the same day; only one of the two products could absorb it. The one that could not is the one that got a changelog anchor instead of an advisory.

    What to do

    • Upgrade google-adk to 1.22.0 or later. Current is 2.8.0. Anything from 1.9.0 through 1.21.0 is affected; 1.8.0 and earlier do not carry the read route at all.
    • Check what you actually run, not what you last installed. pip show google-adk in the running image; grep -r "google-adk" requirements*.txt pyproject.toml poetry.lock uv.lock across repositories. Pinned versions in container images are the ones that will be missed.
    • Find exposed deployments before you finish the upgrade. For Cloud Run: gcloud run services get-iam-policy SERVICE --region REGION and look for allUsers on roles/run.invoker. Anything ADK-derived and publicly invokable should be closed or put behind authentication now, independent of the package upgrade.
    • Test for it directly. On a build you control, request /builder/app/<app>?file_path=/etc/passwd against the service. A patched build returns an empty body and logs a rejection; a vulnerable one returns the file.
    • Treat credentials in reach of the process as exposed if the service was publicly invokable during the window. Service-account keys, model API keys, and anything in a mounted secret file were all readable by an unauthenticated caller who knew a valid app name.
    • There is no known exploitation of this CVE, and no exploit code has been published. It is not on the KEV catalog and carries no federal deadline.

    Sourcing note

    The CVE record was read from NVD at services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2026-79707: published September 4, 2026 at 11:17 a.m. UTC, last modified at 4:18 p.m. UTC, source identifier f45cbf4e-4146-4068-b7e1-655ffc2c548c (Google Cloud). Release dates for 1.9.0, 1.21.0, 1.22.0, and 2.8.0 are PyPI upload timestamps, read from the registry API rather than from the changelog. The vulnerable and fixed code was read from the 1.21.0 and 1.22.0 wheels downloaded from PyPI and unpacked locally; the 1.9.0 wheel was unpacked to confirm the @working_in_progress decorator and the introduction of the endpoint, and 1.8.0 to confirm the read route is absent there. Quotations of the code are from those files. The Cloud Run guidance and the --allow-unauthenticated example are quoted from Google’s ADK documentation.

    GCP-2026-059 and its “No customer action is required” sentence are quoted from Google Cloud’s security bulletins page. The statement that no GitHub Security Advisory exists for this CVE reflects the advisory database listing for google-adk as of publication; one may be added later. Google has not said who reported the bug, whether it was found internally or externally, or whether it was ever exploited; none of those are asserted here. Whether the eight-month gap between the January fix and today’s record was a backlog, a late external report against already-fixed code, or a deliberate delay is not stated anywhere in the record and is unresolved.

    Sources: NVD, CVE-2026-79707; google/adk-python commit 6f259f0; google-adk on PyPI; ADK Cloud Run deployment guide; Google Cloud security bulletins.