The junior pastes composer.lock. The file contains laravel/ai. Dependabot is green. The ticket says “AI package is patched.”
I stop the run there. A laravel/ai lockfile line is not a patched fetch client. The public advisory that named this field is blunt: the Vercel adapter and the AG-UI adapter in laravel/ai 1.0.0 accepted a client-supplied file URL and fetched it on the server without a guard. That is one package, one pair of adapters, one fetch client. It is not laravel/mcp. It is not framework 13.34. It is not “Composer found a line, so the chat endpoint is safe.” [Source: https://github.com/laravel/ai/security/advisories/GHSA-6qhr-3g93-pxhw] [Source: https://laravel-news.com/laravel-ai-mcp-security-advisories]
I already refused to treat a 2,500+ Actions count as an exact run inventory in A 2,500+ Actions Count Is Not an Exact Run Inventory. I already refused to treat a NUL byte in a permission rule as a wildcard allow in A NUL Byte in a Permission Rule Is Not a Wildcard Allow. I already refused to treat Copilot’s ready-to-approve line as a required merge vote in Leave Copilot Approve Off. This post is the same desk rule for a Composer lock line. Print the package. Print the locked version. Name who owns the Laravel answers.
The question is not whether the lockfile looks patched. The question is whether the named owner can tell this fetch client from a different advisory and from a framework bump.

The ticket that looks like all-patched
Juniors treat a lockfile the way they treat a green CI badge. The name is in the file. The bot did not scream. They page the desk: “laravel/ai is in the lockfile, we are patched.”
Two jobs collide on that line.
- Keep Composer honest.
composer.lockrecords the exact package and version this deploy installs. That is inventory. [Source: https://packagist.org/packages/laravel/ai] - Keep the fetch client honest. The advisory that shipped on 30 September 2026 names a fetch of client-supplied file URLs inside two adapters. A lockfile line does not print those adapters. [Source: https://github.com/laravel/ai/security/advisories/GHSA-6qhr-3g93-pxhw]
If you only screenshot “laravel/ai is present,” you will file all-patched. You will not file the fetch client.
I do not invent a fake overnight policy flip. I use the public contract. The fetch-client field is the ticket, not a version pin in the title.
What the lockfile line actually is
Composer’s lockfile is a list of packages this install resolved. Packagist currently lists laravel/ai with latest v1.0.1 (29 September 2026, 18:50 UTC) and still lists v1.0.0 (23 September 2026). A lockfile that contains the name laravel/ai tells you the package is a dependency. It does not tell you which of those two lines you ship. [Source: https://packagist.org/packages/laravel/ai]
The GitHub release that moved the fetch client is v1.0.1. The notes name “Improve remote file fetching” as pull request 1082. That sentence is evidence after you print the lockfile. It is not a heading. [Source: https://github.com/laravel/ai/releases/tag/v1.0.1]
The repo advisory is more specific than the release list:
- Package: laravel/ai (Composer).
- Affected:
>= 1.0.0, < 1.0.1. - Patched: 1.0.1.
- Severity: Moderate. Laravel News restates CVSS 5.3. No CVE ID in the public notes.
- Adapters:
Laravel\Ai\Vercel\VercelandLaravel\Ai\AgentUserInteraction\AgentUserInteraction. - Field: file parts with a URL from the client, fetched on the server without a guard.
- Scope: only apps that expose one of those adapters to untrusted clients.
- 0.x: both adapters first shipped in 1.0.0. No 0.x release contains them. [Source: https://github.com/laravel/ai/security/advisories/GHSA-6qhr-3g93-pxhw] [Source: https://laravel-news.com/laravel-ai-mcp-security-advisories]
A lockfile line of laravel/ai at 0.11.2 is a different field. That line is not this fetch client. Do not file 1.0.0-only work as a 0.x emergency.
A lockfile line of laravel/framework at 13.34.0 is a different field again. Framework weekly is not this advisory. Do not merge them. [Source: https://laravel-news.com/laravel-ai-mcp-security-advisories]
Where the public notes sit (not the title)
/advisories/GHSA-6qhr-3g93-pxhw URL 404s; the live page is the repo advisory. Later npm-style scanners that wait for a CVE will miss this line. Compare the locked version after you name the owner. Do not put a version in the heading. [Source: https://github.com/laravel/ai/security/advisories/GHSA-6qhr-3g93-pxhw] [Source: https://github.com/laravel/ai/releases/tag/v1.0.1] [Source: https://packagist.org/packages/laravel/ai]Two advisories, two owners
The same week Laravel published a second advisory: laravel/mcp, GHSA-mx2h-h55v-pm44, insecure OAuth redirect. Severity Low. Needs user interaction. Fixed in 0.9.6 or 1.0.1. That is a redirect field, not a fetch client. [Source: https://github.com/laravel/mcp/security/advisories/GHSA-mx2h-h55v-pm44] [Source: https://x.com/pushpak1300/status/2105336372471234860]
A junior who sees both packages in one composer update laravel/ai laravel/mcp paste will screenshot “both AI packages are patched.” Print two tickets:
| Ticket | Package | Field | Untrusted-client question |
|---|---|---|---|
| Fetch client | laravel/ai | Client file URL fetched on the server by Vercel or AG-UI adapters | Does this app expose those adapters to callers you do not trust? |
| OAuth redirect | laravel/mcp | Redirect URL validation in the OAuth flow | Does this app run MCP OAuth, and did a human follow a crafted link? |
Same Composer command. Two fields. Two named owners if the teams split. One human is enough if one person owns Laravel on this desk. Do not let a coding agent collapse them into “AI is patched.”
Pushpak’s public note is the same split: laravel/ai 1.0.0 fetch of client-supplied file URLs in Vercel + AG-UI adapters, fix 1.0.1; laravel/mcp insecure OAuth redirect, fix 0.9.6 or 1.0.1. Do not wait for a CVE. Do not treat a missing CVE as a missing advisory. [Source: https://x.com/pushpak1300/status/2105336372471234860]

Probe: print the package, not the framework
Copy this probe. Run it against the lockfile the deploy actually uses. Do not run it against a gist. Do not turn it into a live fetch of an internal URL. This script only reads JSON.
1#!/usr/bin/env python3
2"""Print laravel/ai and laravel/mcp from composer.lock. Inventory only."""
3from __future__ import annotations
4
5import json
6import sys
7from pathlib import Path
8
9WANTED = ("laravel/ai", "laravel/mcp")
10
11
12def packages(lock: Path) -> dict[str, str]:
13 data = json.loads(lock.read_text())
14 found: dict[str, str] = {}
15 for row in data.get("packages", []) + data.get("packages-dev", []):
16 name = row.get("name")
17 if name in WANTED:
18 found[name] = str(row.get("version"))
19 return found
20
21
22def main() -> int:
23 lock = Path(sys.argv[1] if len(sys.argv) > 1 else "composer.lock")
24 if not lock.is_file():
25 print(f"missing lockfile: {lock}")
26 return 2
27 found = packages(lock)
28 print(f"lockfile={lock.resolve()}")
29 for name in WANTED:
30 version = found.get(name, "ABSENT")
31 print(f"{name} locked={version}")
32 ai = found.get("laravel/ai")
33 if ai in {"1.0.0", "v1.0.0"}:
34 print("fetch_client_field=THIS_PACKAGE_AT_AFFECTED_LINE")
35 elif ai in {"1.0.1", "v1.0.1"}:
36 print("fetch_client_field=THIS_PACKAGE_AT_PATCH_LINE")
37 elif ai is None:
38 print("fetch_client_field=PACKAGE_NOT_LOCKED")
39 else:
40 print("fetch_client_field=OTHER_LINE_PRINT_ADAPTERS")
41 print("owner_required=LARAVEL_AI_LOCKFILE_OWNER")
42 return 0
43
44
45if __name__ == "__main__":
46 raise SystemExit(main())
Save it as probe_laravel_ai_lockfile.py. Point it at the lockfile on the branch that ships.
PHP on the same desk, if you refuse a Python helper in a Laravel repo:
1<?php
2// Inventory only. Reads composer.lock. Does not fetch URLs.
3$lockFile = $argv[1] ?? 'composer.lock';
4$raw = file_get_contents($lockFile);
5if ($raw === false) {
6 fwrite(STDERR, "missing lockfile: {$lockFile}\n");
7 exit(2);
8}
9$data = json_decode($raw, true);
10$wanted = ['laravel/ai' => 'ABSENT', 'laravel/mcp' => 'ABSENT'];
11foreach (array_merge($data['packages'] ?? [], $data['packages-dev'] ?? []) as $row) {
12 $name = $row['name'] ?? '';
13 if (array_key_exists($name, $wanted)) {
14 $wanted[$name] = (string) ($row['version'] ?? '');
15 }
16}
17echo "lockfile={$lockFile}\n";
18foreach ($wanted as $name => $version) {
19 echo "{$name} locked={$version}\n";
20}
Composer itself, from the app root, after you already trust the lockfile path:
1composer show laravel/ai --locked --format=json
2composer show laravel/mcp --locked --format=json
Those three blocks print inventory. They do not open a chat endpoint. They do not fetch a file URL. They do not demonstrate the old adapter. The public advisory already states the old fetch. You do not need a live poison request. [Source: https://github.com/laravel/ai/security/advisories/GHSA-6qhr-3g93-pxhw]
If composer show says the package is not locked, the ticket is “this app does not install laravel/ai,” not “the fetch client is patched.” Absence is a field. Do not upgrade a package you do not ship because a social post named it.

Who is affected (adapters, not every Laravel app)
The advisory is explicit: only applications that expose one of the two adapters to untrusted clients are affected. Both adapters first shipped in 1.0.0. [Source: https://github.com/laravel/ai/security/advisories/GHSA-6qhr-3g93-pxhw]
That sentence kills three lazy tickets.
- Every Laravel 13 app is on fire. False. Framework 13.34 is a weekly. This field is laravel/ai adapters. Print the package.
- Every laravel/ai install is on fire. False if the locked line is 0.x, or if the app never exposes Vercel or AG-UI chat to untrusted callers. Print the adapters.
- Packagist latest means this deploy is patched. False. Packagist latest is the registry. The deploy is the lockfile. Print the lockfile.
Laravel News restates the same boundary: you are only affected if the app exposes one of these adapters to untrusted clients. [Source: https://laravel-news.com/laravel-ai-mcp-security-advisories]
The v1.0 product post is useful background and a trap. Laravel shipped AI SDK v1.0 on 23 September 2026 with classification, frontend chat protocols, and storage changes. That post is not the advisory. Do not file an upgrade-guide ticket as this fetch client. [Source: https://laravel.com/blog/introducing-laravel-ai-sdk-v1]
On this desk the named owner answers four lines before any composer update:
- Lockfile path (the file CI installs, not a laptop copy).
laravel/ailocked version.- Yes or no: this app exposes Vercel or AG-UI to callers we do not trust.
LARAVEL_AI_LOCKFILE_OWNER= a human name.
If line 3 is no, the ticket is “adapters not exposed.” Keep the lockfile current as change control. Do not pretend a private artisan command is a public chat endpoint.
If line 2 is the affected line and line 3 is yes, the ticket is “this fetch client.” The named owner schedules the patch as a normal Laravel change: lockfile, tests, rollback tag. The advisory’s own workaround is upgrade, or reject URL-based file parts before they reach the adapter, or block outbound traffic to internal and metadata addresses. I am not going to write a fetch recipe. Print those three options as owner choices. Do not paste an internal URL into a chat form to “prove” it. [Source: https://github.com/laravel/ai/security/advisories/GHSA-6qhr-3g93-pxhw]
What still is not this ticket
Print these so a junior does not collapse every AI headline into this lockfile.
- laravel/mcp OAuth redirect. Different GHSA. Different package. Different field. [Source: https://github.com/laravel/mcp/security/advisories/GHSA-mx2h-h55v-pm44]
- Framework weekly. laravel/framework 13.34.0 is not this advisory.
- 0.x laravel/ai. Adapters not present. Do not file 1.0.0-only work on 0.11.2.
- A Dependabot green check. A bot that waits for a CVE will miss a GHSA with no CVE. Pushpak said that out loud. [Source: https://x.com/pushpak1300/status/2105336372471234860]
- A 2,500+ Actions count. Inventory of workflow runs is a different post. Do not clone it. [Source: https://zemna.net/blog/a-2500-plus-actions-count-is-not-an-exact-run-inventory/]
- Sandbox auto-allow. An equals matcher on
python3 -cis a different post. Do not merge. [Source: https://zemna.net/blog/sandbox-auto-allow-is-not-a-retry-tax-on-equals/] - A moved runner deadline. Actions runner images are a different post. Do not merge. [Source: https://zemna.net/blog/a-moved-runner-deadline-is-not-an-all-clear/]
What you must not do
Forbidden:
- File an “AI package is patched” ticket without printing the lockfile path,
laravel/ailocked version, adapter exposure yes/no, and one human name on the Laravel answers. - Put
1.0.1or13.34.0in the title or the first line. The pin is evidence after the decision. - Mix this field with laravel/mcp OAuth, a framework weekly, a 2,500+ inventory ticket, a runner-deadline ticket, or a sandbox-equals ticket. Those are other posts.
- Write a live chat request with a file URL to “demo” the old fetch. The public advisory already states the old fetch. You do not need a live poison request. [Source: https://github.com/laravel/ai/security/advisories/GHSA-6qhr-3g93-pxhw]
- Treat Packagist latest as this deploy. The deploy is the lockfile.
- Treat GitHub global advisory 404 as “no advisory.” The live page is the repo advisory.
- Treat a missing CVE as a missing field. Laravel News and the maintainer both said scanners that wait for a CVE will miss this. [Source: https://laravel-news.com/laravel-ai-mcp-security-advisories]
- Recommend buying a plan, a seat, or a scanner because one lockfile line is old.
- Run
composer updateon laravel/mcp to close a laravel/ai fetch-client ticket, or the reverse. - Paste internal, metadata, or loopback URLs into a chat form. That is a fetch recipe. This post does not write one.
- Clone the auto-start post, the green-deploy post, the /readyz post, the runner-deadline post, or the sandbox-equals post as a synonym. Those URLs already shipped. GSC this week has no striking-distance query that asks for another copy.
- File “every Laravel app” or “every laravel/ai 0.x app” as this field. The advisory names 1.0.0 adapters and untrusted clients.
Allowed:
- Print
laravel/aiandlaravel/mcpfrom the lockfile CI installs. - Classify the locked laravel/ai line with
probe_laravel_ai_lockfile.py. Do not fetch a file URL as a demo. - Answer adapter exposure in one yes/no owned by a human.
- Name one human as
LARAVEL_AI_LOCKFILE_OWNER. - Print
composer show laravel/ai --lockedafter the decision, in a details block, not in the title. - Schedule the patch as Laravel change control when the locked line is affected and adapters are exposed.
- Keep 0.x and “adapters not exposed” as separate tickets, not as fake all-clears.
If you need the broader habit, start at /ai-agent-operations/. Tooling notes live under /developer-tools/. Laravel plus Vue notes live under /laravel-vue-saas/. A first-week map is at /start-here/.
A changelog bullet about remote file fetching is not permission to skip the four lines.

What you should do Monday morning
- Open the repo that actually ships. Export
LARAVEL_AI_LOCKFILE_OWNERto a human name. Runprobe_laravel_ai_lockfile.pyagainst thecomposer.lockCI installs. Write the lockedlaravel/ailine and the lockedlaravel/mcpline on the ticket next to that name. - Answer adapter exposure in one sentence: this app does or does not expose the Vercel or AG-UI adapter to callers we do not trust. If you cannot answer, the ticket is “owner missing,” not “patched.”
- If laravel/ai is locked on the affected line and adapters are exposed, file “this fetch client,” not “AI is patched.” If the package is absent, file absent. If the line is 0.x, file 0.x. If adapters are not exposed, file that.
- If someone pastes a single
composer update laravel/ai laravel/mcpas the close of both fields, split the ticket. Print two packages. Close each field on its own evidence. [Source: https://github.com/laravel/mcp/security/advisories/GHSA-mx2h-h55v-pm44] - Print
composer show laravel/ai --locked. If the laptop lockfile is not the CI lockfile, do not treat the laptop as production. Compare, then decide an upgrade as change control, not as a social post. [Source: https://github.com/laravel/ai/releases/tag/v1.0.1] [Source: https://packagist.org/packages/laravel/ai] - Confirm coding-agent instructions on this desk name the same owner and forbid “AI package is patched” without the four lines. Forbid any live fetch of a file URL as a demo.
The question is not whether the advisory demos well in a gist. The question is whether the named owner can still tell a lockfile line from a patched fetch client after handoff.
Further reading
Source GitHub — laravel/ai advisory on client-supplied file URLs
