netresearch / composer-audit-responsibility
Composer plugin implementing responsibility propagation for security audits — stops upstream/framework transitive dependency advisories from blocking library/extension CI
Installs: 203
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 1
Open Issues: 0
Type:composer-plugin
pkg:composer/netresearch/composer-audit-responsibility
Requires
- php: >=8.1
- composer-plugin-api: ^2.6
Requires (Dev)
- captainhook/captainhook: ^5.28
- composer/composer: ^2.9
- friendsofphp/php-cs-fixer: ^3.94
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^10.5 || ^11.0
README
A Composer plugin implementing responsibility propagation for security audits.
Stops upstream/framework transitive dependency advisories from blocking your library, extension, or plugin CI — while keeping them visible in audit reports.
The Problem
Since Composer 2.9, block-insecure defaults to true, blocking any package version with a security advisory during composer update, require, or remove (and install without a lock file, which triggers dependency resolution). For library/extension developers, this means:
- Your TYPO3 extension requires
typo3/cms-corefor compatibility typo3/cms-coretransitively depends onfirebase/php-jwt- When
firebase/php-jwtgets a security advisory, your CI breaks - You have no control over this — you didn't choose
firebase/php-jwt - The TYPO3 team is responsible for updating their framework dependencies
This affects every framework ecosystem: Drupal modules, Symfony bundles, Laravel packages, WordPress plugins, Magento modules, Shopware plugins, and more.
What People Do Today (and Why It's Bad)
1. Disable the security check entirely
COMPOSER_NO_SECURITY_BLOCKING=1 composer update
This silences all advisories — including ones in your own dependencies that you can and should fix. You lose the safety net completely. A real vulnerability in a package you chose goes unnoticed.
2. Manually maintain config.audit.ignore
{
"config": {
"audit": {
"ignore": {
"PKSA-y2cr-5h3j-g3ys": "firebase/php-jwt - framework dep",
"CVE-2024-XXXXX": "some-other/lib - framework dep"
}
}
}
}
Every new advisory requires a manual commit to every affected repo. With dozens of extensions and frequent advisories, this becomes a constant maintenance burden. Worse: stale ignore entries accumulate and nobody reviews whether they're still needed — or whether they're now hiding a vulnerability in a package you do control.
Both approaches share the same fundamental flaw: they treat security as all-or-nothing when the real question is who is responsible for which dependency.
The Solution: Responsibility Propagation
Security responsibility follows the dependency chain:
| Role | Responsible For |
|---|---|
| Extension/Plugin developer | Their direct dependencies |
| Framework team | Framework's transitive dependencies |
| Application/Project assembler | Everything (they ship the final product) |
This plugin automatically detects your framework dependencies and prevents their transitive security advisories from blocking dependency resolution (composer update/require/remove). Advisories are still reported — they just don't block.
Installation
This plugin must be installed globally — it needs to be loaded before your project's dependencies are resolved.
composer global config allow-plugins.netresearch/composer-audit-responsibility true
composer global require netresearch/composer-audit-responsibility
CI Setup (GitHub Actions)
Add this step after PHP setup and before composer install:
- name: Install audit-responsibility plugin run: | composer global config allow-plugins.netresearch/composer-audit-responsibility true composer global require netresearch/composer-audit-responsibility --no-interaction
No changes to your project's composer.json are needed.
Configuration
Automatic Detection
The plugin auto-detects your framework from the type field in composer.json:
| Package Type | Detected Framework |
|---|---|
typo3-cms-extension |
typo3/cms-core |
symfony-bundle |
symfony/framework-bundle, symfony/http-kernel |
drupal-module |
drupal/core |
wordpress-plugin |
johnpbloch/wordpress-core, roots/wordpress |
magento2-module |
magento/framework |
shopware-platform-plugin |
shopware/core |
contao-bundle |
contao/core-bundle |
cakephp-plugin |
cakephp/cakephp |
neos-plugin |
neos/neos |
flow-package |
neos/flow |
oroplatform-bundle |
oro/platform |
silverstripe-vendormodule |
silverstripe/framework |
pimcore-bundle |
pimcore/pimcore |
laravel-package |
laravel/framework |
yii2-extension |
yiisoft/yii2 |
Explicit Configuration
For projects that use type: library or need custom upstream declarations:
{
"extra": {
"audit-responsibility": {
"upstream": ["typo3/cms-core", "helhum/typo3-console"]
}
}
}
Disable the Plugin per Project
If you have the plugin installed globally but want a specific extension/library to opt out (i.e., have all advisories block normally):
{
"extra": {
"audit-responsibility": {
"block-upstream": true
}
}
}
Note: Application projects (
type: projectortype: librarywithout explicitupstreamconfig) are never affected by this plugin — it only activates for framework-specific package types (extensions, bundles, modules, plugins). No configuration needed to exclude them.
How It Works
- Detection — Identifies platform/framework packages from your
typeor explicit config - Graph analysis — Walks the dependency graph (BFS) to classify every package:
- Direct: In your
require— your responsibility - Platform-only: Only reachable through framework packages — framework's responsibility
- Shared: Reachable through both your deps AND framework — your responsibility (conservative)
- User-transitive: Only reachable through your non-framework deps — your responsibility
- Direct: In your
- Policy enforcement — Platform-only advisories don't block; everything else still blocks normally
The Diamond Problem
When a package is reachable through both your dependencies AND the framework:
your-extension
├── typo3/cms-core → psr/log (platform path)
└── my/logging-lib → psr/log (user path)
Conservative rule: If you have ANY dependency path to a package, it's your responsibility. psr/log blocks in this case because you chose my/logging-lib which also depends on it.
Comparison with Alternatives
| Approach | Scope | Maintenance | Visibility |
|---|---|---|---|
config.audit.ignore per advisory |
Per-advisory | Update for every new advisory | Hidden |
COMPOSER_NO_SECURITY_BLOCKING=1 |
All deps | None | Hidden |
| This plugin | Framework deps only | None (auto-detected) | Preserved |
Requirements
- PHP >= 8.1
- Composer >= 2.9
License
MIT License. See LICENSE for details.
Contributing
Contributions are welcome! Please open an issue or pull request on GitHub.