{"record":{"id":"8be1b642f78c6b67","repo":"composer/composer","slug":"invalid-package-found-during-dependency-resolution","errorCode":null,"errorMessage":"Invalid package found during dependency resolution, aborting: {error}","messagePattern":"Invalid package found during dependency resolution, aborting: (.+?)","errorType":"exception","errorClass":"SecurityException","httpStatus":null,"severity":"critical","filePath":"src/Composer/Package/Loader/ValidatingArrayLoader.php","lineNumber":686,"sourceCode":"     * installed from the lock file. This guards against malicious package names and source/dist\n     * URLs or references that could be interpreted as command-line options (argument injection)\n     * by the VCS/download tooling.\n     *\n     * @throws SecurityException\n     */\n    public static function validatePackage(PackageInterface $package): void\n    {\n        // The root package's name/metadata is locally controlled and already validated by\n        // RootPackageLoader (and its \"__root__\" placeholder name would be a false positive here).\n        // RootPackageInterface covers both RootPackage and RootAliasPackage.\n        if ($package instanceof RootPackageInterface) {\n            return;\n        }\n\n        // getName() is already lowercased, so the uppercase style branch never fires and only\n        // structural/security failures throw. Platform packages return null here.\n        if (null !== ($err = self::hasPackageNamingError($package->getName()))) {\n            throw new SecurityException('Invalid package found during dependency resolution, aborting: '.$err);\n        }\n\n        // A url or reference starting with a \"-\" may be misinterpreted as a command-line option\n        // by the VCS/download tooling, same protection as the source/dist checks done in load().\n        $sourceDist = [\n            'source.url' => $package->getSourceUrl(),\n            'source.reference' => $package->getSourceReference(),\n            'dist.url' => $package->getDistUrl(),\n            'dist.reference' => $package->getDistReference(),\n        ];\n        foreach ($sourceDist as $field => $value) {\n            if ($value !== null && Preg::isMatch('{^\\s*-}', $value)) {\n                throw new SecurityException($package->getName().' has an invalid '.$field.', it must not start with a \"-\": '.$value);\n            }\n        }\n\n        // Bin paths are resolved relative to the package install dir and then chmod'd (and\n        // proxied) by BinaryInstaller. A \"..\" segment escapes that directory and lets a","sourceCodeStart":668,"sourceCodeEnd":704,"githubUrl":"https://github.com/composer/composer/blob/c435d285c9120efdca35696769c72ea9fdcc0466/src/Composer/Package/Loader/ValidatingArrayLoader.php#L668-L704","documentation":"Thrown by ValidatingArrayLoader::validatePackage() (a static, security-sensitive re-check) as a SecurityException when a resolved package's name fails hasPackageNamingError() — i.e. it violates the vendor/name regex, uses reserved names (con/nul/aux/etc.), ends in .json, or contains uppercase. This is a defense-in-depth guard re-applied after dependency resolution, before install/write, to stop malicious names that slipped past earlier validation.","triggerScenarios":"Calling ValidatingArrayLoader::validatePackage($package) where $package is not a RootPackageInterface and getName() returns a value for which hasPackageNamingError() is non-null (structurally invalid name, reserved name, .json suffix, or uppercase letters). Triggered internally during dependency resolution and lock-file writing.","commonSituations":"A malicious or corrupted provider/lock file injects a package whose name would be interpreted dangerously (e.g. contains shell metacharacters that evade the regex, or a reserved Windows device name). Encountered after a 'composer update' that pulled bad metadata or when a tampered composer.lock is present.","solutions":["Run 'composer clear-cache' and re-run update to refetch clean metadata from trusted sources.","Inspect composer.lock for packages with abnormal names; remove offending entries and re-resolve.","If you control the offending package, rename it to a valid lowercase vendor/name with no reserved words.","Verify you are resolving against the official packagist.org or a trusted private repository, not a mirrored/poisoned one."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Pre-check a resolved package name with the public static guard before install\n$err = \\Composer\\Package\\Loader\\ValidatingArrayLoader::hasPackageNamingError($package->getName());\nif ($err !== null) {\n    throw new \\RuntimeException('Refusing to install: '.$err);\n}","typeGuard":"function isSafePackageName(string $name): bool {\n    return \\Composer\\Package\\Loader\\ValidatingArrayLoader::hasPackageNamingError($name) === null;\n}","tryCatchPattern":"try {\n    \\Composer\\Package\\Loader\\ValidatingArrayLoader::validatePackage($package);\n} catch (\\Composer\\Util\\SecurityException $e) {\n    // Do NOT auto-resolve; treat as a security incident and abort the operation.\n    throw $e;\n}","preventionTips":["Only resolve dependencies from trusted repositories (official packagist.org or vetted private mirrors).","Commit composer.lock and review diffs in CI so unexpected package additions are caught.","Keep Composer updated to receive the latest security-validation rules."],"tags":["security","package-metadata","dependency-resolution","argument-injection"],"backgroundTag":null,"analyzedSha":"c435d285c9120efdca35696769c72ea9fdcc0466","analyzedAt":"2026-08-07T18:58:23.525Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}