惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tenable Blog
S
SegmentFault 最新的问题
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园 - 聂微东
罗磊的独立博客
MongoDB | Blog
MongoDB | Blog
美团技术团队
Recorded Future
Recorded Future
Microsoft Security Blog
Microsoft Security Blog
博客园 - 叶小钗
P
Proofpoint News Feed
aimingoo的专栏
aimingoo的专栏
博客园_首页
宝玉的分享
宝玉的分享
C
Check Point Blog
爱范儿
爱范儿
MyScale Blog
MyScale Blog
Microsoft Azure Blog
Microsoft Azure Blog
The Register - Security
The Register - Security
U
Unit 42
T
Tailwind CSS Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
D
Docker
博客园 - Franky
博客园 - 【当耐特】
腾讯CDC
N
Netflix TechBlog - Medium
Jina AI
Jina AI
博客园 - 司徒正美
Last Week in AI
Last Week in AI
PCI Perspectives
PCI Perspectives
GbyAI
GbyAI
Security Archives - TechRepublic
Security Archives - TechRepublic
J
Java Code Geeks
Cloudbric
Cloudbric
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Y
Y Combinator Blog
F
Full Disclosure
TaoSecurity Blog
TaoSecurity Blog
N
News and Events Feed by Topic
L
LINUX DO - 最新话题
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
小众软件
小众软件
O
OpenAI News
H
Hackread – Cybersecurity News, Data Breaches, AI and More
S
Security Affairs
Recent Announcements
Recent Announcements
Attack and Defense Labs
Attack and Defense Labs
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
S
Secure Thoughts

Socket

Large-Scale GitHub Actions Abuse Powers a Distributed cPanel... New Study Identifies 53 Slopsquatting Targets Across 5 Front... White House Launches Gold Eagle Initiative to Manage Surge i... Suno Breached via Shai-Hulud Worm, Leaked Code Exposes AI Mu... Next.js moves to scheduled security releases - Socket 11 Malicious NuGet Tools Pose as Game Cheats to Drop a Windo... Compromised npm Packages in the AsyncAPI Namespace Deliver M... jscrambler npm Package Compromised in Supply Chain Attack - ... Fake Braintree NuGet Package Skims Credit Cards and Harvests... Compromised Injective SDK npm Package Exfiltrates Wallet Key... npm v12 Ships With Install Scripts Off by Default, Begins De... Malicious Go Module Exposes GitHub Malware Lure Network Span... pnpm 11.10 Hardens Registry Authentication to Block Token Re... Coordinated npm and PyPI Campaign Typosquats Popular Secure ... Node.js Considers Public Workflow for Security Reports Amid ... PolinRider: North Korea-Linked Supply Chain Campaign Expands... Risky Biz Podcast: AI Agents Are Raising the Stakes for Soft... Chrome and Firefox Extensions Posing as Free VPNs Add Clipbo... Miasma Mini Shai-Hulud Hits ImmobiliareLabs npm Packages - S... Rolldown Pulls Rust React Compiler Integration After Binary ... Miasma Mini Shai-Hulud Hits LeoPlatform npm Packages and Git... Frontier AI Is Now Critical Infrastructure - Socket The Code You Didn't Write Is Still Yours to Defend - Socket GitHub Actions Checkout Now Blocks Risky pull_request_target... Introducing Repository Access Permissions and Custom Roles -... Socket MCP Adds Org Alerts, Threat Feed Review, and Package ... Socket Firewall Now Blocks Malicious VS Code and Open VSX Ex... 140+ Mastra npm Packages Compromised in Coordinated Supply C... npm Package Uses Prompt Injection and Token Flooding to Disr... Introducing Manifest Alerts - Socket GlassWASM: WebAssembly Malware Found in Trojanized Open VSX ... Socket for Linear Is Now Available - Socket US Government Forces Anthropic to Pull Claude Fable Days After Launch 152 Chrome Live Wallpaper Extensions Hid Ad Tracking and Faked Google Search Traffic Andrew Becherer Joins Socket as Chief Information Security Officer Socket Partners with Replit to Block Malicious Packages in AI-Powered Development npm Tooling Bug Incorrectly Marks One-Character Packages as Security Holders Mini Shai-Hulud, Miasma, and Hades Worms Target Bioinformatics and MCP Developers via Malicious PyPI Wheels Shai-Hulud Descends to Hades: Miasma Worm Campaign Spreads with New PyPI Wave RubyGems Adds Cooldown Feature to Bundler for Newly Published Gems RubyGems Adds Cooldown Feature to Bundler for Newly Published Gems pnpm 11.5 Adds Support for Recognizing npm Staged Publishes pnpm 11.5 Adds Support for Recognizing npm Staged Publishes Federal Audit Finds NIST Wasted Funds With No Plan to Clear NVD Backlog Federal Audit Finds NIST Wasted Funds With No Plan to Clear NVD Backlog Mini Shai-Hulud Campaign Hits Red Hat Cloud Services npm Packages Mini Shai-Hulud Campaign Hits Red Hat Cloud Services npm Packages Famous Chollima Targets PHP Developers Through Compromised Packagist Package Famous Chollima Targets PHP Developers Through Compromised Packagist Package Rust Moves to Restrict LLM Use in Contributions After Months of Internal Debate Rust Moves to Restrict LLM Use in Contributions After Months of Internal Debate Malicious NuGet Package Impersonates Sicoob SDK to Exfiltrate Banking Certificates and Passwords Malicious NuGet Package Impersonates Sicoob SDK to Exfiltrate Banking Certificates and Passwords Feross on TBPN: Socket's Series C and the State of Software Supply Chain Security Feross on TBPN: Socket's Series C and the State of Software Supply Chain Security OSV Withdraws 157 Malware Reports After Automated False Positives Hit npm and PyPI OSV Withdraws 157 Malware Reports After Automated False Positives Hit npm and PyPI TrapDoor Crypto Stealer Supply Chain Attack Hits 34 Packages and Hundreds of Versions Across npm, PyPI, and Crates.io TrapDoor Crypto Stealer Supply Chain Attack Hits 34 Packages and Hundreds of Versions Across npm, PyPI, and Crates.io Laravel Lang Compromised with RCE Backdoor Across 700+ Versions Malicious Postinstall Hook Found Across 700+ GitHub Repositories, Including Packagist and Node.js Projects AI Has Taken Over Open Source npm Invalidates Granular Access Tokens as Mini Shai-Hulud Sweeps the Registry Coruna Respawned: Compromised art-template npm Package Leads to iOS Browser Exploit Kit Socket raises $60M Series C at $1B valuation led by Thrive Capital to secure AI-driven software development Socket Raises $60M Series C at a $1B Valuation to Help Enterprises Build Securely With AI Popular Go Decimal Library Targeted by Long-Running Typosquat with DNS Backdoor Active Supply Chain Attack Compromises @antv Packages on npm Popular node-ipc npm Package Infected with Credential Stealer TeamPCP and BreachForums Launch $1,000 Contest for Supply Chain Attacks Packagist Urges Immediate Composer Update After GitHub Actions Token Leak GemStuffer Campaign Abuses RubyGems as Exfiltration Channel Targeting UK Local Government Socket Named to Rising in Cyber 2026 List of Top Cybersecurity Startups TanStack npm Packages Compromised in Ongoing Mini Shai-Hulud Supply-Chain Attack fsnotify Maintainer Dispute Sparks Supply Chain Concerns Socket Releases Free Certified Patches for Critical vm2 Sandbox Escape 5 Malicious NuGet Packages Impersonate Chinese UI Libraries to Distribute Crypto Wallet and Credential Stealer pnpm 11 Adds Supply Chain Protection Defaults for Minimum Release Age and Exotic Subdependencies PyPI Fixes High-Severity Access Control Issues Found in Security Audit Malicious Ruby Gems and Go Modules Impersonate Developer Tools to Steal Secrets and Poison CI Mini Shai-Hulud Spreads to Packagist: Malicious Intercom PHP Package Follows npm Compromise Intercom’s npm Package Compromised in Ongoing Mini Shai-Hulud Worm Attack lightning PyPI Package Compromised in Supply Chain Attack Malicious npm Package Brand-Squats TanStack to Exfiltrate Environment Variables SAP CAP npm Packages Hit by Supply Chain Attack Socket Has Acquired Secure Annex 73 Open VSX Sleeper Extensions Linked to GlassWorm Show New Malware Activations Introducing Data Exports Malicious Checkmarx Artifacts Found in Official KICS Docker Repository and Code Extensions Introducing Organization Notifications in Socket Introducing Reports: An Extensible Reporting Framework for Socket Data Socket for Jira Is Now Available Socket Named Top Sales Organization by RepVue NIST Officially Stops Enriching Most CVEs as Vulnerability Volume Skyrockets Socket Selected for OpenAI's Cybersecurity Grant Program Feross on the 10 Minutes or Less Podcast: Nobody Reads the Code 108 Chrome Extensions Linked to Data Exfiltration and Session Theft via Shared C2 Infrastructure Node.js Drops Bug Bounty Rewards After Funding Dries Up The Hidden Blast Radius of the Axios Compromise
Introducing Reachability for PHP
Benjamin Bar · 2026-04-24 · via Socket

Sidebar CTA Background

Secure your dependencies with us

Socket proactively blocks malicious open source packages in your code.

Install

Security teams are already struggling to keep pace with the volume of vulnerability disclosures. Every week brings more CVEs, and the arrival of AI-assisted vulnerability research is only going to push that number higher. Teams that can't tell which disclosures actually matter for their application will fall behind quickly.

PHP carries more of this weight than most ecosystems. Composer ranks third for CVE volume among package ecosystems, behind only Maven and npm, and PHP still runs a substantial share of the web. WordPress, Laravel, Symfony, and the long tail of PHP applications built on top of them all pull in the same transitive dependencies, which means a single advisory can light up dashboards across thousands of codebases without telling anyone whether the vulnerable code is actually used.

Reachability analysis answers that question. By pinpointing which vulnerabilities can be exploited within a given application, it lets teams prioritize real risks and skip the rest. This approach has already saved teams significant time across JavaScript/TypeScript, Python, Ruby, and other ecosystems, and today we're bringing it to PHP in experimental.

How Reachability Analysis Works#

Socket's reachability analysis for PHP builds on function-level call graph analysis. For each function in your application, the engine computes which other functions it may call. A vulnerable function is considered reachable if an application function can transitively invoke it.

Both Tier 1 (full application reachability, computed against your actual source code) and Tier 2 (pre-computed reachability against the dependency graph) are now available for PHP.

The analysis engine itself was built with researchers at Aarhus University, drawing on their work in static program analysis. We've already shipped function-level reachability for JavaScript/TypeScript, Python, and Ruby, and the PHP implementation inherits the lessons from each of those.

The analysis is deliberately conservative. When a call can't be fully resolved, it's marked reachable or unknown rather than unreachable, which protects exploitable paths from being filtered out by mistake.

Learn more about the analysis engines in the Static Reachability Analysis docs.

Why PHP is harder than it looks#

PHP presents unique challenges for static analysis. The language leans heavily on two dispatch patterns that break most off-the-shelf call graph analyzers.

Magic method dispatch through __call

class RealService {
    public function process(string $data): string {
        return "processed: $data";
    }
}

class Proxy {
    public function __construct(private object $target) {}

    public function __call(string $name, array $args): mixed {
        return $this->target->$name(...$args);
    }
}

$proxy = new Proxy(new RealService());
$result = $proxy->process("payload");

$proxy->process(...) has no matching method on Proxy, so PHP routes the call to __call, which then re-dispatches through a variable method name against a field of type object. A naive analyzer sees three stacked unknowns (the class on $this->target, the method name in $name, and the resulting callee) and gives up. RealService::process gets marked unreachable, and every CVE inside it is triaged as "not exploitable" when it actually is.

Socket's PHP engine propagates the RealService instance through the constructor into $this->target, carries the string "process" into the $name parameter, resolves $target->$name(...) back to RealService::process, and lands the edge in the call graph.

The same shape shows up in Laravel Facades, Eloquent dynamic relationships, Doctrine lazy-loading proxies, and PHPUnit mock objects. Getting __call right is the difference between a real call graph and one full of holes.

String-keyed service containers

class Logger { public function log(string $msg): void { /* ... */ } }
class Mailer { public function send(string $to): void  { /* ... */ } }

class Container {
    private array $bindings = [];

    public function bind(string $abstract, string $concrete): void {
        $this->bindings[$abstract] = $concrete;
    }

    public function make(string $abstract): object {
        $concrete = $this->bindings[$abstract];
        return new $concrete();
    }
}

$c = new Container();
$c->bind('logger', Logger::class);
$c->bind('mailer', Mailer::class);

$c->make('logger')->log('hello');
$c->make('mailer')->send('ops@example.com');

No other mainstream language leans on this pattern as heavily. A class name is a string, stored in an array keyed by another string, pulled out later, and passed to new $concrete(). Laravel's container, Symfony's DI, and PHP-DI all work this way, which means entire applications are wired through Container::make(). An analyzer that can't follow strings through array cells into new $var() sees $c->make('mailer')->send(...) as a method call on an unknown object and misses every sink behind it.

Socket's engine tracks class-name strings through binding, storage, lookup, and instantiation, so Logger::log and Mailer::send both show up as reachable.

Advanced example: Guzzle CookieJar (CVE-2022-29248)#

The payoff for getting these patterns right shows up on real advisories. CVE-2022-29248 in guzzlehttp/guzzle is a good one, because Guzzle is a transitive dependency of nearly everything on Packagist. The vulnerable sink is CookieJar::extractCookies, which can leak Set-Cookie values across domains when a request is redirected.

Same Guzzle version, two different verdicts

Consider two applications pinned to the same affected version of Guzzle.

The first constructs a client with no cookie handling. CookieJar::extractCookies is never reached, and the advisory is not exploitable in that application.

The second constructs a client with a cookie jar attached.

The application never names extractCookies directly. Guzzle's cookie middleware, installed automatically in the default handler stack, calls it on every response through a chain of closures and a Promise::then callback. Two applications on the same Guzzle version get different verdicts, and both are right. That's the triage you can't do with a dependency graph alone.

What's happening inside Guzzle

To turn the application's single call to $client->send(...) into an edge into CookieJar::extractCookies, the analyzer has to follow a chain of seven hops through Guzzle's internals.

1. The app creates a Client with a CookieJar:

$this->http = new Client([
    'cookies' => new CookieJar(),
]);
// ...
$this->http->send($request, $options);

2. Client::__construct pulls in the default handler stack and merges it into config (vendor/guzzlehttp/guzzle/src/Client.php):

public function __construct(array $config = [])
{
    if (!isset($config['handler'])) {
        $config['handler'] = HandlerStack::create();
    }
    // ...
    $this->configureDefaults($config);     // sets $this->config = $config + $defaults
}

3. HandlerStack::create() pushes four middlewares, including cookies (vendor/guzzlehttp/guzzle/src/HandlerStack.php):

public static function create(?callable $handler = null): self
{
    $stack = new self($handler ?: Utils::chooseHandler());
    $stack->push(Middleware::httpErrors(),  'http_errors');
    $stack->push(Middleware::redirect(),    'allow_redirects');
    $stack->push(Middleware::cookies(),     'cookies');
    $stack->push(Middleware::prepareBody(), 'prepare_body');
    return $stack;
}

Each push appends a [callable, name] pair to $this->stack.

4. Client::send dispatches through sendAsync to transfer (vendor/guzzlehttp/guzzle/src/Client.php):

namespace GuzzleHttp;
use GuzzleHttp\Promise as P;

public function send(RequestInterface $request, array $options = []): ResponseInterface
{
    $options[RequestOptions::SYNCHRONOUS] = true;
    return $this->sendAsync($request, $options)->wait();
}

public function sendAsync(RequestInterface $request, array $options = []): PromiseInterface
{
    $options = $this->prepareDefaults($options);           // $defaults = $this->config; ... return $defaults
    return $this->transfer($request, $options);
}

private function transfer(RequestInterface $request, array $options): PromiseInterface
{
    /** @var HandlerStack $handler */
    $handler = $options['handler'];                        // pulled out of the merged options
    return P\Create::promiseFor($handler($request, $options));
}

5. Invoking the HandlerStack as a callable lands in __invoke, which calls resolve() (HandlerStack.php):

public function __invoke(RequestInterface $request, array $options)
{
    $handler = $this->resolve();
    return $handler($request, $options);
}

6. HandlerStack::resolve() composes the stack via a reduce over [callable, name] pairs:

public function resolve(): callable
{
    if ($this->cached === null) {
        if (($prev = $this->handler) === null) { /* ... */ }

        foreach (\array_reverse($this->stack) as $fn) {
            $prev = $fn[0]($prev);                         // tuple-indexed callable on a property array
        }
        $this->cached = $prev;
    }
    return $this->cached;
}

7. The composed callable is the cookies middleware's inner closure, wrapping the rest of the stack:

// vendor/guzzlehttp/guzzle/src/Middleware.php
public static function cookies(): callable
{
    return static function (callable $handler): callable {
        return static function ($request, array $options) use ($handler) {
            if (empty($options['cookies'])) {
                return $handler($request, $options);
            }
            $cookieJar = $options['cookies'];
            $request = $cookieJar->withCookieHeader($request);

            return $handler($request, $options)
                ->then(
                    static function (ResponseInterface $response) use ($cookieJar, $request): ResponseInterface {
                        $cookieJar->extractCookies($request, $response);    // ← the sink
                        return $response;
                    }
                );
        };
    };
}

Three closures nested inside one another, the cookie jar pulled out of a string-keyed options array, and the vulnerable call buried inside a Promise::then callback that only fires once the response resolves.

Miss any one link in that chain and the sink is dead code. The advisory renders as "not reachable," real exposure gets triaged away, and the team moves on to the next alert.

The call stack the dashboard shows

The stack is eight frames deep. Only the top frame is application code. The other seven are inside Guzzle and its promises library.

The application calls send, which queues up a chain of middleware and returns a pending promise. Only when the promise resolves does Guzzle run the then callback that was installed by the cookie middleware, and that callback is where extractCookies gets called. The dashboard shows the resolution path because that's the shortest route from application code to the sink.

What we've tested it on#

We've validated the PHP engine against a range of real-world codebases:

  • WordPress, with roughly 140,000 call edges resolved. Its hook and filter system is a stress test for callback dispatch.
  • Laravel, both the framework itself and downstream applications including Snipe-IT, BookStack, Koel, and Akaunting, with end-to-end container and facade resolution.
  • Symfony applications like Kimai and the Symfony Demo, covering service definitions, the event dispatcher, and DI.
  • Twig, Guzzle, Monolog, PHPUnit, and Flysystem, which sit in nearly every composer.lock.

Measured against dynamically observed call graphs, our accuracy lands above 90% on PHPUnit, WordPress, and Flysystem, and in the mid-to-high 80s on Twig and Espo. The remaining gaps are concentrated in reflection-driven dispatch and runtime-generated code, and we’re actively working through them.

Experimental Status#

PHP reachability is launching in experimental, which means the engine is in active development. Coverage will expand and accuracy will improve as we work through the long tail of PHP patterns. We welcome feedback from the community on any incorrectly classified vulnerabilities.

Getting Started#

PHP reachability is opt-in during the experimental phase. Reach out if you'd like it turned on for your organization.

Once enabled:

  • Pre-computed reachability results are available directly in the Socket Dashboard.
  • Full application reachability is available to enterprise customers via the Socket CLI:

socket scan create --reach

For full setup instructions, see the Full Application Reachability docs.

PHP reachability is another step toward our goal of bringing precise, function-level analysis to every major ecosystem. We're excited for teams to try it out and see how much manual triage the engine can take off their plate.