Skip to content

Commit e9cd487

Browse files
1 parent 1055709 commit e9cd487

File tree

1 file changed

+161
-0
lines changed

1 file changed

+161
-0
lines changed
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-vvj3-c3rp-c85p",
4+
"modified": "2026-01-27T22:26:22Z",
5+
"published": "2026-01-27T22:26:22Z",
6+
"aliases": [
7+
"CVE-2026-24765"
8+
],
9+
"summary": "PHPUnit Vulnerable to Unsafe Deserialization in PHPT Code Coverage Handling",
10+
"details": "### Overview\n\nA vulnerability has been discovered involving unsafe deserialization of code coverage data in PHPT test execution. The vulnerability exists in the `cleanupForCoverage()` method, which deserializes code coverage files without validation, potentially allowing remote code execution if malicious `.coverage` files are present prior to the execution of the PHPT test.\n\n### Technical Details\n\n**Affected Component:** PHPT test runner, method `cleanupForCoverage()`\n**Affected Versions:** <= 8.5.51, <= 9.6.32, <= 10.5.61, <= 11.5.49, <= 12.5.7\n\n### Vulnerable Code Pattern\n\n```php\nif ($buffer !== false) {\n // Unsafe call without restrictions\n $coverage = @unserialize($buffer);\n}\n```\n\nThe vulnerability occurs when a `.coverage` file, which should not exist before test execution, is deserialized without the `allowed_classes` parameter restriction. An attacker with local file write access can place a malicious serialized object with a `__wakeup()` method into the file system, leading to arbitrary code execution during test runs with code coverage instrumentation enabled.\n\n### Attack Prerequisites and Constraints\n\nThis vulnerability requires **local file write access** to the location where PHPUnit stores or expects code coverage files for PHPT tests. This can occur through:\n\n* **CI/CD Pipeline Attacks:** A malicious pull request that places a `.coverage` file alongside test files, executed when the CI system runs tests using PHPUnit and collects code coverage information\n* **Local Development Environment:** An attacker with shell access or ability to write files to the project directory\n* **Compromised Dependencies:** A supply chain attack inserting malicious files into a package or monorepo\n\n**Critical Context:** Running test suites from unreviewed pull requests without isolated execution is inherently a code execution risk, independent of this specific vulnerability. This represents a broader class of [Poisoned Pipeline Execution (PPE) attacks](https://owasp.org/www-project-top-10-ci-cd-security-risks/CICD-SEC-04-Poisoned-Pipeline-Execution) affecting CI/CD systems.\n\n### Proposed Remediation Approach\n\nRather than just silently sanitizing the input via `['allowed_classes' => false]`, the maintainer has chosen to make the anomalous state explicit by treating pre-existing `.coverage` files for PHPT tests as an error condition.\n\n#### Rationale for Error-Based Approach:\n\n1. **Visibility Over Silence:** When an invariant is violated (a `.coverage` file existing before test execution), the error must be visible in CI/CD output, alerting operators to investigate the root cause rather than proceeding with sanitized input\n2. **Operational Security:** A `.coverage` file should never exist before tests run, coverage data is generated by executing tests, not sourced from artifacts. Its presence indicates:\n * A malicious actor placed it intentionally\n * Build artifacts from a previous run contaminated the environment\n * An unexpected filesystem state requiring investigation\n3. **Defense-in-Depth Principle:** Protecting a single deserialization call does not address the fundamental attack surface. Proper mitigations for PPE attacks lie outside PHPUnit's scope:\n * Isolate CI/CD runners (ephemeral, containerized environments)\n * Restrict code execution on protected branches\n * Scan pull requests and artifacts for tampering\n * Use branch protection rules to prevent unreviewed code execution\n\n### Severity Classification\n\n* **Attack Vector (AV):** Local (L) — requires write access to the file system where tests execute\n* **Attack Complexity (AC):** Low (L) — exploitation is straightforward once the malicious file is placed\n* **Privileges Required (PR):** Low (L) — PR submitter status or contributor role provides sufficient access\n* **User Interaction (UI):** None (N) — automatic execution during standard test execution\n* **Scope (S):** Unchanged (U) — impact remains within the affected test execution context\n* **Confidentiality Impact (C):** High (H) — full remote code execution enables complete system compromise\n* **Integrity Impact (I):** High (H) — arbitrary code execution allows malicious modifications\n* **Availability Impact (A):** High (H) — full code execution permits denial-of-service actions\n\n### Mitigating Factors (Environmental Context)\n\nOrganizations can reduce the effective risk of this vulnerability through proper CI/CD configuration:\n\n* **Ephemeral Runners:** Use containerized, single-use CI/CD runners that discard filesystem state between runs\n* **Code Review Enforcement:** Require human review and approval before executing code from pull requests\n* **Branch Protection:** Enforce branch protection rules that block unreviewed code execution\n* **Artifact Isolation:** Separate build artifacts from source; never reuse artifacts across independent builds\n* **Access Control:** Limit file write permissions in CI environments to authenticated, trusted actors\n\n### Fixed Behaviour\n\nWhen a `.coverage` file is detected for a PHPT test prior to execution, PHPUnit will emit a clear error message identifying the anomalous state. This ensures:\n\n* **Visibility:** The error appears prominently in CI/CD output and test logs\n* **Investigation:** Operations teams can investigate the root cause (potential tampering, environment contamination)\n* **Fail-Fast Semantics:** Test execution stops rather than proceeding with an unexpected state\n\n### Recommendation\n\n**Update to the patched version immediately** if a project runs PHPT tests using PHPUnit with coverage instrumentation in any CI/CD environment that executes code from external contributors. Additionally, audit the project's CI/CD configuration to ensure:\n\n* Pull requests from forks or untrusted sources execute in isolated environments\n* Branch protection rules require human review before code execution\n* CI/CD runners are ephemeral and discarded after each build\n* Build artifacts are not reused across independent runs without validation",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "Packagist",
21+
"name": "phpunit/phpunit"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "8.5.52"
32+
}
33+
]
34+
}
35+
]
36+
},
37+
{
38+
"package": {
39+
"ecosystem": "Packagist",
40+
"name": "phpunit/phpunit"
41+
},
42+
"ranges": [
43+
{
44+
"type": "ECOSYSTEM",
45+
"events": [
46+
{
47+
"introduced": "9.0.0"
48+
},
49+
{
50+
"fixed": "9.6.33"
51+
}
52+
]
53+
}
54+
]
55+
},
56+
{
57+
"package": {
58+
"ecosystem": "Packagist",
59+
"name": "phpunit/phpunit"
60+
},
61+
"ranges": [
62+
{
63+
"type": "ECOSYSTEM",
64+
"events": [
65+
{
66+
"introduced": "10.0.0"
67+
},
68+
{
69+
"fixed": "10.5.62"
70+
}
71+
]
72+
}
73+
]
74+
},
75+
{
76+
"package": {
77+
"ecosystem": "Packagist",
78+
"name": "phpunit/phpunit"
79+
},
80+
"ranges": [
81+
{
82+
"type": "ECOSYSTEM",
83+
"events": [
84+
{
85+
"introduced": "11.0.0"
86+
},
87+
{
88+
"fixed": "11.5.50"
89+
}
90+
]
91+
}
92+
]
93+
},
94+
{
95+
"package": {
96+
"ecosystem": "Packagist",
97+
"name": "phpunit/phpunit"
98+
},
99+
"ranges": [
100+
{
101+
"type": "ECOSYSTEM",
102+
"events": [
103+
{
104+
"introduced": "12.0.0"
105+
},
106+
{
107+
"fixed": "12.5.8"
108+
}
109+
]
110+
}
111+
]
112+
}
113+
],
114+
"references": [
115+
{
116+
"type": "WEB",
117+
"url": "https://github.com/sebastianbergmann/phpunit/security/advisories/GHSA-vvj3-c3rp-c85p"
118+
},
119+
{
120+
"type": "WEB",
121+
"url": "https://github.com/sebastianbergmann/phpunit/commit/613d142f5a8471ca71623ce5ca2795f79248329e"
122+
},
123+
{
124+
"type": "PACKAGE",
125+
"url": "https://github.com/sebastianbergmann/phpunit"
126+
},
127+
{
128+
"type": "WEB",
129+
"url": "https://github.com/sebastianbergmann/phpunit/releases/tag/10.5.63"
130+
},
131+
{
132+
"type": "WEB",
133+
"url": "https://github.com/sebastianbergmann/phpunit/releases/tag/11.5.50"
134+
},
135+
{
136+
"type": "WEB",
137+
"url": "https://github.com/sebastianbergmann/phpunit/releases/tag/12.5.8"
138+
},
139+
{
140+
"type": "WEB",
141+
"url": "https://github.com/sebastianbergmann/phpunit/releases/tag/8.5.52"
142+
},
143+
{
144+
"type": "WEB",
145+
"url": "https://github.com/sebastianbergmann/phpunit/releases/tag/9.6.33"
146+
},
147+
{
148+
"type": "WEB",
149+
"url": "https://owasp.org/www-project-top-10-ci-cd-security-risks/CICD-SEC-04-Poisoned-Pipeline-Execution"
150+
}
151+
],
152+
"database_specific": {
153+
"cwe_ids": [
154+
"CWE-502"
155+
],
156+
"severity": "HIGH",
157+
"github_reviewed": true,
158+
"github_reviewed_at": "2026-01-27T22:26:22Z",
159+
"nvd_published_at": null
160+
}
161+
}

0 commit comments

Comments
 (0)