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

0.4.0 2026-02-18 17:17 UTC

This package is auto-updated.

Last update: 2026-02-18 19:20:22 UTC


README

CI Latest Stable Version License

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-core for compatibility
  • typo3/cms-core transitively depends on firebase/php-jwt
  • When firebase/php-jwt gets 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: project or type: library without explicit upstream config) 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

  1. Detection — Identifies platform/framework packages from your type or explicit config
  2. 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
  3. 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.