Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Pet Laravel Package

nunomaduro/pet

Pet is a lightweight PHP ETL package for Laravel and plain PHP apps. It offers a simple, expressive API for extracting, transforming, and loading data between sources, making it easy to build clean, testable data pipelines.

View on GitHub
Deep Wiki
Context7

Getting Started

Install as a dev dependency — never in require:

composer require --dev nunomaduro/pet

Run it from the project root. The tool audits your composer.lock, so it needs to run where the lock file lives:

./vendor/bin/pet

If that binary name doesn't resolve, list what the package actually shipped (ls vendor/bin) — the package has been renamed over its lifetime (nunomaduro/pet → Laravel Vet), so the executable name can differ between versions. Check the README for the version you installed rather than trusting a blog post.

Where to look first. The output is a report of distinct things, and you should read them separately:

  1. Known vulnerabilities — packages in your lock file matched against a security advisory database.
  2. Abandoned packages — packages whose maintainers have flagged them as unmaintained via Composer's abandoned key.
  3. Minimum release age — an optional supply-chain gate (see below) that flags dependencies published too recently.

Vulnerabilities are urgent; abandoned packages are a roadmap item. Don't let a long abandoned list bury a single real CVE.

First use case. Run it immediately after installing, before you change anything. That baseline tells you whether you inherited a clean project or a debt-ridden one, and it tells you how noisy the tool will be in your CI later.

Optional config. The tool reads a vet.json from the project root if one exists. You don't need it to start — a bare ./vendor/bin/pet run is a valid first pass. As of v0.2.1 that file may contain only a minimum-release-age setting, so a config that exists purely to enforce one rule no longer needs placeholder keys. On older versions it did.

Implementation Patterns

Wrap it in a Composer script so the whole team runs the same thing and nobody has to remember the binary path:

{
    "scripts": {
        "vet": "pet",
        "vet:ci": "pet --no-interaction"
    }
}

Then composer vet is the daily habit.

The highest-value pattern is scheduled, not on-demand. Advisories are published continuously against code that hasn't changed. A lock file that was clean last month may not be clean today. Add a nightly job that runs the audit against main without touching dependencies:

# .github/workflows/audit.yml
name: Dependency Audit
on:
  schedule: [{ cron: '0 6 * * *' }]
  push: { branches: [main] }
jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with: { php-version: '8.3' }
      - run: composer install --no-interaction --no-progress
      - run: composer vet:ci

This is the workflow the tool is actually built for — it catches "we got a CVE disclosure overnight" rather than "we happened to run it before a release."

Use vet.json for the release-age gate. The minimum release age setting is a supply-chain defence: refuse to accept a dependency version that was published less than N days ago, so a compromised or yanked release has time to be caught before it reaches you. Keep the file minimal and committed:

{
    "minimumReleaseAge": 7
}

A file this small is the whole point of v0.2.1 — previously you had to pad it with unrelated keys to get past validation. Note the gate only helps if your workflow respects it: it's most useful in front of composer update, not inside a read-only audit job. Decide deliberately whether it's a hard failure or a warning in your pipeline.

Stage the CI gate. On an existing codebase, make the audit a non-required job first. Collect a week of results, fix or accept what it finds, then mark it required. Flipping it to blocking on day one blocks unrelated hotfixes and the team learns to ignore red builds. This applies doubly to the release-age check on a project that updates dependencies frequently.

Triage loop for a finding:

composer why vendor/affected-package     # is it direct or transitive?
composer update vendor/affected-package --with-dependencies

If there's no patched release: check whether the affected code path is reachable from your app, pin the constraint to block the vulnerable range, or replace the dependency. Write the decision down — a bare composer.json constraint with no comment is a trap for the next developer.

Pair it with composer outdated. The audit says "this is dangerous," outdated says "this is behind." Together they produce a prioritized upgrade list: vulnerable-and-outdated first, abandoned second, merely-behind last.

Overlap with composer audit. Composer 2.4+ ships its own advisory audit against the same public advisory database. Running both produces duplicate noise. Pick one as the source of truth for vulnerabilities; keep Vet if you want the abandoned-package signal, the release-age gate, and their presentation in the same report, otherwise use composer audit and drop this dependency.

Gotchas and Tips

It reads composer.lock, not composer.json. A stale or uncommitted lock produces a stale audit. In CI, always composer install (not update) first so you're auditing the exact resolved set. If the lock is missing, the tool has nothing meaningful to say.

Clean output is not a safety guarantee. The advisory database only knows about vulnerabilities someone reported and mapped to a package and version range. Unreported, mis-ranged, or freshly disclosed issues won't appear. Treat a green run as "no known issues," never as "safe." Likewise, a package passing the release-age gate is old, not safe.

Network dependency. Fetching advisories — and resolving package publish dates for the release-age check — may require outbound access. Verify this against your locked-down CI runners and air-gapped build environments early, or the first pipeline run will fail for a reason unrelated to your code.

Results change without a code change. Because the advisory set and publish timestamps are external, the same commit can pass on Monday and fail on Tuesday. When that happens, don't hunt for a bad merge — check whether a new advisory landed, or whether a pinned version's age window has shifted.

Config file quirks are version-sensitive. v0.2.1 changed validation so an empty vet.json containing only minimum-release-age settings is accepted. On earlier 0.2.x builds the same file can fail validation, which surfaces as a config error rather than a finding. If a teammate reports "the config doesn't work," compare versions before editing the JSON. And don't assume the key name — confirm it against the README for your installed version rather than a blog post.

Dev dependencies inflate the report. A vulnerability in a test-only or static-analysis tool shows up identically to one in a production runtime package. Sort by whether the package is reachable in production (composer why --tree) before you escalate. Same for abandoned packages: an abandoned dev-only helper is a low-priority cleanup, not an incident.

Expect abandoned-package noise on legacy projects. Long-lived Laravel apps accumulate abandoned packages that still work perfectly. Don't conflate them with vulnerabilities in your reporting, or stakeholders learn to ignore the whole output.

Pin the version and keep a fallback. This is a small-adoption package (181 stars, effectively no downstream dependents) and it's still pre-1.0, so 0.x releases can change config semantics — v0.2.1 is exactly that kind of change. Commit to a specific minor in composer.json, and know your escape hatch: composer audit covers the vulnerability half natively, composer show --tree plus the Packagist abandoned flags covers the rest, and the release-age idea can be approximated with composer update --min-age-style workflows or a small script.

Confirm flags with --help rather than copying them from a blog post. The package was renamed mid-life; option names and defaults have shifted between versions, and an unrecognized flag in CI looks like a vulnerability failure until you read the log.

Extension point is vet.json plus a wrapper script. The config file is where the release-age gate lives; there's no plugin surface worth relying on. If you need custom thresholds (e.g. "fail on vulnerabilities, warn on abandoned"), write a small script around the exit code and output rather than waiting for upstream configurability.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels