GHSA-322x-v876-g883
Fix: asymmetric-effort/NogginLessDom@785e6acGHSA-322x-v876-g883 is a security vulnerability in @asymmetric-effort/nogginlessdom. O3 Security confirms whether GHSA-322x-v876-g883 is actually reachable in your code before you act, and blocks exploitation at runtime until you patch.
@asymmetric-effort/nogginlessdom's Path Traversal in matchFileSnapshot allows arbitrary file write
Real-World Exposure
How broadly this vulnerability is actually deployed: weekly install volume shows current usage, and reverse-dependency count shows how many other packages break if it stays unpatched.
@asymmetric-effort/nogginlessdomnpmDescription
Summary
The matchFileSnapshot function in src/assertions/snapshots.ts accepted a filePath parameter with zero validation. When snapshot update mode was active (UPDATE_SNAPSHOTS=1 or setUpdateMode('all')), an attacker who controls test input could write arbitrary content to any filesystem path the process has write access to, including creating intermediate directories.
Affected Code
File: src/assertions/snapshots.ts, lines 732-769
export function matchFileSnapshot(actual: unknown, filePath: string): void {
const serialized = serialize(actual);
const mode = resolveUpdateMode();
const fileExists = fs.existsSync(filePath);
if (!fileExists) {
if (mode === 'all' || mode === 'new') {
const dir = path.dirname(filePath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
fs.writeFileSync(filePath, serialized, 'utf-8');
The filePath flows from expect(value).toMatchFileSnapshot(filePath) at index.ts:1033-1035 with no sanitization, no check that the path is within an expected directory, and no symlink resolution.
Proof of Concept
import { expect, setUpdateMode } from '@asymmetric-effort/nogginlessdom';
setUpdateMode('all');
// Writes arbitrary content to any writable path
expect('malicious content').toMatchFileSnapshot('/tmp/exploit/payload.txt');
// Path traversal via relative components
expect('data').toMatchFileSnapshot('../../../tmp/evil.txt');
// In CI environments, could overwrite CI config
expect('injected step').toMatchFileSnapshot('/home/runner/.github/workflows/backdoor.yml');
Impact
In CI/CD environments where test files may come from untrusted pull requests, this allows writing to any writable filesystem location with directory creation. An attacker could overwrite configuration files, inject code into build artifacts, or modify CI pipeline definitions.
Fix
Fixed in commit https://github.com/asymmetric-effort/NogginLessDom/commit/785e6ac6e124d1a89b3ccf40bbd75fc8e4cb215d on main. The matchFileSnapshot function now validates that the resolved file path is within the project root directory (defaults to process.cwd(), configurable via optional projectRoot parameter). Paths that resolve outside the project directory are rejected with a descriptive error.
export function matchFileSnapshot(
actual: unknown,
filePath: string,
projectRoot?: string,
): void {
const root = projectRoot ?? process.cwd();
const resolved = path.resolve(root, filePath);
if (!resolved.startsWith(root + path.sep) && resolved !== root) {
throw new Error(
`File snapshot path must be within the project directory: ${filePath}`,
);
}
// ... all subsequent file I/O uses `resolved` instead of `filePath`
}
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 📦npm | @asymmetric-effort/nogginlessdom | all versions | 0.0.22 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for @asymmetric-effort/nogginlessdom. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.
Fix
Update @asymmetric-effort/nogginlessdom to 0.0.22 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-322x-v876-g883 is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 pinpoints whether GHSA-322x-v876-g883 is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.
Tailored to GHSA-322x-v876-g883. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is GHSA-322x-v876-g883 in your dependencies?
O3 detects GHSA-322x-v876-g883 across npm dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.