News

Laravel Vet: Review Composer Code Before It Installs

Published
Laravel Vet: Review Composer Code Before It Installs image

Laravel Vet is a new package from the Laravel team that shows you the code that composer update is about to write into your vendor/ directory, and records the packages you trust in a vet.json file. It ships as a Composer plugin, so it runs after every install and before every update writes anything.

If you know cargo vet from the Rust world, this is the same idea for Composer. If you don't, here is the whole idea: every update brings new code into your project that nobody on your team has read. Vet shows you that code, one package at a time, before it lands. Once you trust a package, vet remembers it, so the next update only asks about what changed.

You don't have to read it all yourself. Vet hands each change to the coding agent already on your machine, such as Claude Code, Codex, Gemini or opencode, and the agent reads it for you and reports back: PASS, or FAIL with the file and the reason. You read the fails, press enter on the rest, and get on with your day.

Once you trust a package, vet remembers it, and the next update only asks about what changed. A package nobody has trusted exits with a non-zero status, which is what makes it useful in a build. It works with any project that has a composer.json, so Laravel, Symfony, WordPress, and plain PHP are all fine.

As you may know, every Composer update writes thousands of lines of code that you either have to audit manually by reading diffs or blindly trust Packagist, and with security issues on the rise, auditing is becoming more and more important.

In fact we've had a run of supply chain news this year, from malware blocking and dependency policies in Composer 2.10 to the Axios npm package shipping a remote access trojan.

How Vet Works

Vet requires PHP 8.4 or later. Install it as a development dependency, and answer yes when Composer asks whether to allow the plugin:

composer require laravel/vet --dev

First, mark the packages currently installed in vendor/ as trusted:

./vendor/bin/vet --init
INFO Trusted [125] packages, and wrote [vet.json].

This creates vet.json using the package files already on your disk. It records your trust in those files without reviewing them. Vet then checks future updates against that record. If an update contains untrusted changes, Vet stops it before Composer writes the new files:

❯ composer update
 
to review (1)
 
carbonphp/carbon-doctrine-types 3.1.0 → 3.2.0 .............. 2 files changed
│
│ ~ src/Carbon/Doctrine/DateTimeImmutableType.php
│ @@ -17,7 +17,7 @@
│ /**
│ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
│ */
│ - public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?DateTimeImmutable
│ + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?CarbonImmutable
│ {
│ return $this->doConvertToPHPValue($value);
│ }
│
│ ~ src/Carbon/Doctrine/DateTimeType.php
│ @@ -17,7 +17,7 @@
│ /**
│ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
│ */
│ - public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?DateTime
│ + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?Carbon
│ {
│ return $this->doConvertToPHPValue($value);
│ }
│
 
Packages: 1 to review, 124 trusted
 
ERROR [1] package is not trusted. Read every change with [./vendor/bin/vet -v]. Run [./vendor/bin/vet] in a terminal to pick the ones that you trust.
 
TIP Run [./vendor/bin/vet] in a terminal to hand every change to your coding agent.

Run ./vendor/bin/vet in a terminal to review the changes. You can read the code changes yourself, then press the space bar to select the packages you trust. Or you can ask a coding agent installed on your machine to review them first. Vet displays the agent's result next to each package:

INFO [claude] reviews [3] packages (58.1 KB). This takes a moment.
 
to review (3)
 
acme/logger 1.2.0 → 2.0.0 ................................. 12 files changed
│ FAIL src/Ship.php reads .env and sends it to an unknown host
│ src/Ship.php it posts the contents of [.env] to [telemetry.example.com]
 
acme/tooling 4.1.0 → 4.2.0 ................................. 8 files changed
│ WARN the changes add two commands
│ The agent did not read [1] file, because it is too big. Read it yourself:
│ resources/schema.php 612.4 KB
 
carbonphp/carbon-doctrine-types 3.1.0 → 3.2.0 .............. 2 files changed
│ PASS the changes narrow two return types

PASS means the agent completed its review and reported no attack. FAIL identifies a file and explains the problem the agent found.

WARN means the review is incomplete and needs your attention. A file may be too large, contain non-text data, or the agent may not have returned an answer. SKIP means Vet sent no files to the agent because nothing changed or it could not read the package's files.

Vet selects the PASS packages by default. Review the results and adjust the selection, then press Enter to save the packages you trust. The agent's results alone do not change vet.json.

Vet saves your choices in vet.json, next to composer.json. Commit this file to your repository. Each entry records a trusted package version and a hash, a value calculated from the package's files:

{
"schema": 4,
"require": {
"carbonphp/carbon-doctrine-types": {
"version": "3.2.1",
"hash": "tree-v2:0f158f3b909fc01e691ed5f5121186056232b049031e7d3a914676d49881ece5"
}
}
}

The hash covers every file in the package. If the files change, even without a new version number, Vet requires another review. Vet exits with an error when a package is untrusted, so the check fails in your build until someone marks that package as trusted.

Vet is in beta right now, so the behavior can change before the first stable release. If you want a second layer on top of it, Heimdall sets a minimum age policy for your Composer dependencies so new releases have to sit for a few days before you install them. Visit the GitHub repo for the full documentation, and happy auditing!

Eric L. Barnes photo

Eric is the creator of Laravel News and has been covering Laravel since 2012.

Filed in

Sponsored

laravelcloud logo
Laravel Cloud

Easily create and manage your servers and deploy your Laravel applications in seconds.

Visit Laravel Cloud

The latest

View all →
Securing Filament plugins with Plumb image

Securing Filament plugins with Plumb

Read article
LayaPHP: Self-Hosted Text Classification for PHP and Laravel image

LayaPHP: Self-Hosted Text Classification for PHP and Laravel

Read article
WhenMounted and BigInt Props in Inertia.js v3.8 image

WhenMounted and BigInt Props in Inertia.js v3.8

Read article
Postcodes for Laravel: GB Postcode Lookup and Geography Data image

Postcodes for Laravel: GB Postcode Lookup and Geography Data

Read article
Laravel 14 Adds a defaults() Method to Eloquent Models image

Laravel 14 Adds a defaults() Method to Eloquent Models

Read article
Laravel AI SDK and Laravel MCP Security Fixes: Update Now image

Laravel AI SDK and Laravel MCP Security Fixes: Update Now

Read article