What's the vulnerability?

A prototype pollution vulnerability exists in the the npm package swiper (>=6.5.1, < 12.1.2). Despite a previous fix that attempted to mitigate prototype pollution by checking whether user input contained a forbidden key, it is still possible to pollute Object.prototype via a crafted input using Array.prototype. The exploit works across Windows and Linux and on Node and Bun runtimes. This issue is fixed in version 12.1.2

Root Cause Analysis

# Root Cause Analysis Report: GHSA-hmx5-qpq5-p643

## Summary

A prototype pollution vulnerability exists in the swiper npm package (versions >=6.5.1, < 12.1.2) that can be bypassed by modifying `Array.prototype.indexOf`. The vulnerability resides in `shared/utils.mjs` at line 94 where `indexOf()` is used to check whether user-provided input contains forbidden strings like `__proto__`. An attacker can override `Array.prototype.indexOf` to always return -1, causing the filter to incorrectly allow `__proto__` keys through, resulting in successful prototype pollution of `Object.prototype`.

## Impact

- **Package:** swiper (npm package)
- **Affected Versions:** >=6.5.1, < 12.1.2
- **Fixed In:** 12.1.2
- **Severity:** CRITICAL

### Risk and Consequences

This is a prototype pollution vulnerability which can have severe security implications:

1. **Authentication Bypass** - Polluted prototypes can modify authentication logic
2. **Denial of Service** - Modified prototypes can cause application crashes or unexpected behavior
3. **Property Injection** - Arbitrary properties can be injected into all objects
4. **Logic Manipulation** - Application business logic can be altered through prototype manipulation

Any application that processes attacker-controlled input using this package may be affected, particularly when using `extendDefaults()` or similar methods that merge user input with internal configuration objects.

## Root Cause

### Technical Explanation

The vulnerability exists in the swiper package's utility functions, specifically in `shared/utils.mjs` around line 94. The code attempts to prevent prototype pollution by checking if input keys contain forbidden strings:

```javascript
// Vulnerable pattern (conceptual)
if (forbiddenKeys.indexOf(key) !== -1) {
    // Block the key
}
```

The problem is that `indexOf()` is a method called on the `forbiddenKeys` array. Since `indexOf` is defined on `Array.prototype`, it can be globally overridden:

```javascript
Array.prototype.indexOf = () => -1;
```

When this override is in place:
1. `forbiddenKeys.indexOf('__proto__')` returns `-1` (instead of the actual index)
2. The check `indexOf(...) !== -1` evaluates to `false`
3. The `__proto__` key is allowed through the filter
4. `Object.prototype` gets polluted with attacker-controlled properties

### Bypass Mechanism

The exploit chain works as follows:

1. **Override Phase:** `Array.prototype.indexOf = () => -1;`
2. **Input Phase:** Attacker provides `{"__proto__":{"polluted":"yes"}}`
3. **Filter Bypass:** The `indexOf` check returns `-1`, so `__proto__` passes validation
4. **Merge Phase:** The library merges the malicious payload into the target object
5. **Pollution:** `Object.prototype.polluted` is set to `"yes"`
6. **Impact:** All objects now have the `polluted` property

### Fix Information

The issue was fixed in version 12.1.2. The fix likely replaces the `indexOf()`-based check with a more robust approach that doesn't rely on prototype methods that can be overridden, such as:
- Using `Object.prototype.hasOwnProperty.call()` checks
- Using a `Set` with `has()` method
- Using `includes()` from a frozen array
- Implementing a custom comparison function that doesn't rely on prototype methods

## Reproduction Steps

### Script Reference

The reproduction script is located at: `repro/reproduction_steps.sh`

### What the Script Does

1. Creates a temporary directory and initializes npm
2. Installs the vulnerable version of swiper (12.1.1)
3. Creates a test script that:
   - Imports the swiper package
   - Overrides `Array.prototype.indexOf` to always return `-1`
   - Checks `{}.polluted` before exploitation (should be `undefined`)
   - Calls `swiper.default.extendDefaults()` with a malicious payload containing `{"__proto__":{"polluted":"yes"}}`
   - Checks `{}.polluted` after exploitation
   - Restores the original `Array.prototype.indexOf`
4. Runs the test and reports results
5. Cleans up temporary files

### Expected Evidence of Reproduction

When the vulnerability is present, the script outputs:

```
Before exploit: {}.polluted = undefined
After exploit: {}.polluted = yes

[VULNERABILITY CONFIRMED] Prototype pollution successful!
Object.prototype was polluted with 'polluted' property.
```

The key indicator is that `{}.polluted` changes from `undefined` to `"yes"`, proving that `Object.prototype` was successfully polluted.

## Evidence

### Log Files

- **Installation log:** `logs/npm_install.log`
- **Test output:** `logs/test_output.log`

### Key Excerpts

From `logs/test_output.log`:
```
Testing prototype pollution in swiper...
Before exploit: {}.polluted = undefined
After exploit: {}.polluted = yes

[VULNERABILITY CONFIRMED] Prototype pollution successful!
Object.prototype was polluted with 'polluted' property.
```

### Environment Details

- **Node.js Version:** v22.22.0
- **npm Version:** 10.9.4
- **Tested Package:** swiper@12.1.1
- **Operating System:** Linux (containerized environment)

## Recommendations / Next Steps

### Suggested Fix Approach

1. **Replace indexOf()-based checks** with alternatives that cannot be easily bypassed:
   ```javascript
   // Instead of:
   if (forbiddenKeys.indexOf(key) !== -1)
   
   // Use:
   const forbiddenSet = new Set(['__proto__', 'constructor', 'prototype']);
   if (forbiddenSet.has(key))
   ```

2. **Use Object.freeze()** on critical arrays to prevent prototype manipulation

3. **Validate keys explicitly** without relying on prototype methods:
   ```javascript
   if (key === '__proto__' || key === 'constructor' || key === 'prototype')
   ```

4. **Use Object.prototype.hasOwnProperty.call()** for property checks

### Upgrade Guidance

**Immediate Action Required:** Upgrade to swiper version 12.1.2 or later.

```bash
npm install swiper@latest
```

### Testing Recommendations

1. Add regression tests that specifically test prototype pollution scenarios
2. Test with various prototype overrides (Array.prototype.indexOf, Array.prototype.includes, etc.)
3. Implement input validation tests for all user-controlled data entry points
4. Consider using libraries like `safe-object-assign` or `safe-extend` for object merging operations

### Additional Security Measures

1. Implement Content Security Policy (CSP) headers where applicable
2. Use input sanitization libraries
3. Regularly audit dependencies for prototype pollution vulnerabilities
4. Consider using Object.create(null) for objects that shouldn't inherit from Object.prototype

## Additional Notes

### Idempotency Confirmation

The reproduction script has been tested twice consecutively and produces consistent results:
- **First run:** Exit code 0, vulnerability confirmed
- **Second run:** Exit code 0, vulnerability confirmed

The script is fully idempotent - it creates a fresh temporary directory for each run, installs dependencies cleanly, runs the test, and removes all temporary files.

### Edge Cases and Limitations

1. **Runtime Dependency:** The exploit requires JavaScript runtime access to modify `Array.prototype`. This is typically only possible in server-side JavaScript (Node.js, Bun) or if an attacker can execute arbitrary code in a browser context.

2. **Method Scope:** The vulnerability specifically affects code paths that use `indexOf()` for forbidden key validation. Other protection mechanisms may still block some attack vectors.

3. **Cleanup Requirement:** The exploit modifies global prototypes. Proper cleanup (restoring original methods) is essential to prevent side effects in the same execution context.

4. **Bun Runtime:** According to the advisory, this exploit also works on Bun runtime. The same fix should be applicable.

5. **Previous Fix Bypass:** This vulnerability is a bypass of a previous fix. Future fixes should consider all possible prototype pollution vectors, not just the specific one addressed previously.
One Command

Verify with pruva-verify

Run the Pruva CLI to automatically fetch and execute the reproduction script.

pruva-verify REPRO-2026-00107
or pruva-verify GHSA-hmx5-qpq5-p643
or pruva-verify CVE-2026-27212
Install: curl -fsSL https://pruva.dev/install.sh | sh

Or Run Manually

1

Download the script

curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00107/artifacts/reproduction_steps.sh
2

Make executable

chmod +x reproduction_steps.sh
3

Run the script

./reproduction_steps.sh
Run in a VM, container, or disposable environment. This exploits a real vulnerability.

How Pruva Reproduced This

Watch the AI agent's step-by-step process.

Loading session...

Artifacts

No artifacts available