netresearch/composer-audit-responsibility
A Composer plugin that extends composer audit with responsibility metadata, letting teams assign owners to dependencies so vulnerability findings reach the right people and remediation gets tracked. Configuration-driven, lightweight, and easy to drop into existing Composer workflows.
This is a Composer plugin, not a Laravel package — it has no service provider, no config/*.php, no Artisan commands, and nothing at runtime. It hooks Composer's post-install/post-update audit and turns advisories into a hard failure unless the project has explicitly accepted the risk with a documented reason. Treat it as part of your build/CI contract, not your application code.
Minimal steps:
Install per project, never globally.
composer require --dev netresearch/composer-audit-responsibility:^0.4.1
Composer 2.2+ requires plugin consent, so add it to config.allow-plugins when prompted. The global-install path is exactly what 0.4.1 fixed (a globally installed plugin was overriding each project's risk acceptance), so keep it in require-dev and pin tightly — this is still 0.x.
Run an install and watch it fail. Trigger a known advisory in a scratch branch or point at a package with an open advisory. The point of the first run is to see the blocking output and confirm your CI gate is real.
Accept the risk explicitly. Add the advisory to your project policy with a reason — the reason is printed in the output, which is the whole "responsibility" model: no silent ignores.
Where to look first: the README's config table, then PolicyConfig and Auditor::processAdvisories() in the plugin source. Those two classes define exactly which keys are honoured and how matching works, including the Composer 2.10+ code path.
Keep one source of truth for ignores. Configure the policy.advisories block (with ignore, ignore-id, ignore-severity) and let the plugin handle config.audit.* interop itself. Since 0.4.1, when policy.advisories is set on Composer 2.10+, the plugin injects platform-only ignores into policy.advisories.ignore-id because Composer skips audit.ignore in that case. Duplicating the same CVE into both policy.advisories and config.audit.ignore creates drift and confusing double-reporting.
Ignore by ID with a reason, not by package.
{
"config": {
"allow-plugins": {
"netresearch/composer-audit-responsibility": true
}
},
"extra": {
"policy": {
"advisories": {
"ignore-id": {
"CVE-2024-XXXXX": "Not reachable: affected code path is unused (see SEC-412)"
},
"ignore-severity": ["low"]
}
}
}
}
(Confirm exact placement against the README for your version.) Ignoring by package name suppresses future advisories for that package — precisely the risk you're trying to stop accepting implicitly.
Split local and CI behaviour deliberately. Locals can use policy.advisories.block: false to downgrade to a warning and keep moving; CI should run with blocking fully enabled, so composer install itself is the gate. Don't rely on a separate composer audit step as your only check — the two read config differently on Composer 2.10+.
Pipeline shape for a Laravel app:
composer install --no-dev --prefer-dist against the committed composer.lock; a non-zero exit is the security gate.composer audit --format=json job for reporting/SBOM output, but don't treat it as the enforcement point.composer.json — a reviewable diff with a justification string.SEC-412) rather than prose like "false positive". It ends up in logs and, for regulated teams, in audit evidence.Pair with composer audit correctly. Because the plugin now injects ignores where Composer 2.10 actually reads them, your composer audit output and your post-install gate should agree. If you set policy.advisories, verify by running both on the same lockfile and diffing the advisory lists — a mismatch means a config key is being read from the wrong place.
Rollout pattern for existing codebases: first land the plugin with blocking disabled plus a baseline of ignores, capture the advisory list, then flip blocking on in a follow-up PR. Otherwise the first composer install on main fails for everyone at once.
>=0.4.1. Before this release, a globally installed plugin overrode a project's explicit ignores and broke composer install — the worst kind of failure, because the "fix" people reach for is uninstalling the plugin. Per-project install plus ^0.4.1 avoids the whole class.policy: false, policy.advisories: false, policy.advisories.block: false and audit.block-insecure: false all downgrade to warnings. Good for rollout, dangerous as a silent failure mode: a typo'd or misplaced key turns your gate into noise that nobody reads. Assert in CI that the policy is actually active (e.g. a smoke job that expects a non-zero exit on a fixture advisory), not merely present in composer.json.policy.advisories is set, Composer skips audit.ignore entirely. Projects migrating from legacy audit.ignore / audit.ignore-severity while also adopting policy.advisories will find some ignores silently stop applying. Migrate keys in one commit and re-run both composer install and composer audit.PolicyConfig. If CI and developers are on different Composer versions, matching behaviour can differ. Pin Composer (composer-runtime-api constraint or the CI image) so everyone runs the same code path.composer install -vvv shows which advisories were matched and ignored and why. composer diagnose catches plugin/allow-plugins problems; composer audit --format=json gives you machine-readable parity checks.ignore-id over ignore, low over medium, and never a wildcard. An ignore without a reason is the thing this plugin exists to prevent — treat a reasonless entry in code review the same as a failing test.How can I help you explore Laravel packages today?