Skip to content

Commit 08b34f4

Browse files
ddworkenclaude
andauthored
fix: prevent zip slip attacks in DXT unpack function (#74)
* fix: prevent zip slip attacks in DXT unpack function Add path validation to prevent directory traversal attacks when unpacking DXT files. The fix validates that extracted file paths remain within the intended output directory before writing files to disk. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * fix: make zip slip protection cross-platform compatible Use path.sep instead of hardcoded '/' separator to ensure path validation works correctly on both Windows and Unix systems. This fixes failing tests on Windows while maintaining security protection against directory traversal. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> --------- Co-authored-by: Claude <[email protected]>
1 parent a3ba5fc commit 08b34f4

File tree

3 files changed

+16
-1
lines changed

3 files changed

+16
-1
lines changed

CLI.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,12 @@ dxt unsign my-extension.dxt
157157
For signing extensions, you need:
158158

159159
1. **Certificate**: X.509 certificate in PEM format
160+
160161
- Should have Code Signing extended key usage
161162
- Can be self-signed (for development) or CA-issued (for production)
162163

163164
2. **Private Key**: Corresponding private key in PEM format
165+
164166
- Must match the certificate's public key
165167

166168
3. **Intermediate Certificates** (optional): For CA-issued certificates

MANIFEST.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,12 +290,14 @@ The `server` object defines how to run the MCP server:
290290
### Server Types
291291

292292
1. **Python**: `server.type = "python"`
293+
293294
- Requires `entry_point` to Python file
294295
- All dependencies must be bundled in the DXT
295296
- Can use `server/lib` for packages or `server/venv` for full virtual environment
296297
- Python runtime version specified in `compatibility.runtimes.python`
297298

298299
2. **Node.js**: `server.type = "node"`
300+
299301
- Requires `entry_point` to JavaScript file
300302
- All dependencies must be bundled in `node_modules`
301303
- Node.js runtime version specified in `compatibility.runtimes.node`

src/cli/unpack.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { unzipSync } from "fflate";
22
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
3-
import { join, resolve } from "path";
3+
import { join, resolve, sep } from "path";
44

55
import { extractSignatureBlock } from "../node/sign.js";
66
import { getLogger } from "../shared/log.js";
@@ -85,6 +85,17 @@ export async function unpackExtension({
8585
if (Object.prototype.hasOwnProperty.call(decompressed, relativePath)) {
8686
const data = decompressed[relativePath];
8787
const fullPath = join(finalOutputDir, relativePath);
88+
89+
// Prevent zip slip attacks by validating the resolved path
90+
const normalizedPath = resolve(fullPath);
91+
const normalizedOutputDir = resolve(finalOutputDir);
92+
if (
93+
!normalizedPath.startsWith(normalizedOutputDir + sep) &&
94+
normalizedPath !== normalizedOutputDir
95+
) {
96+
throw new Error(`Path traversal attempt detected: ${relativePath}`);
97+
}
98+
8899
const dir = join(fullPath, "..");
89100
if (!existsSync(dir)) {
90101
mkdirSync(dir, { recursive: true });

0 commit comments

Comments
 (0)