Search by

netresearch / composer-audit-responsibility

netresearch

Composer plugin implementing responsibility propagation for security audits — stops upstream/framework transitive dependency advisories from blocking library/extension CI

Package info

github.com/netresearch/composer-audit-responsibility

Type:composer-plugin

pkg:composer/netresearch/composer-audit-responsibility

Statistics

Installs: 1 302

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 1

0.4.1 2026-10-07 12:49 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
        }
    }
}

Advisories Your Project Already Accepted

Advisories that your own composer.json ignores do not block the post-install audit either. The plugin asks Composer for the ignore rules that apply when blocking, so the same entries count as for Composer's own block-insecure: config.policy.advisories.ignore (package names), ignore-id (advisory ID, CVE or GHSA ID) and ignore-severity, or the legacy config.audit.ignore and config.audit.ignore-severity when policy.advisories is not set. Entries scoped to the audit only ("on-block": false, "apply": "audit") still block. Ignored advisories are listed with their reason in the install output. On Composer older than 2.10, which has no policy config, the plugin reads config.audit.ignore and config.audit.ignore-severity itself.

If the project turns advisory blocking off ("policy": false, "policy": {"advisories": false}, "policy": {"advisories": {"block": false}}, or the legacy "audit": {"block-insecure": false}), the post-install audit lists advisories as a warning and does not fail.

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.