测试版 Shoulder 目前处于测试阶段 — 结果有时可能不正确。您的反馈塑造我们接下来要修复的内容。 分享反馈
📂

Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

🛡️ 6 条规则检测到此问题

Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.

Many file operations are intended to take place within a restricted directory. By using special elements such as '..' and '/' separators, attackers can escape outside of the restricted location to access files or directories that are elsewhere on the system.

普遍性
频繁被利用
影响
关键
1 条严重级别为关键的规则
预防
已记录
6 个修复示例
2 预防
2 预防

如何修复此漏洞

基于 6 条 Shoulder 检测规则的 Path Traversal 预防策略。

Path Traversal via File Operations HIGH

Resolve the full path and verify it stays within the intended base directory

+19 -6 go
  package main
  
  import (
      "net/http"
      "os"
- )
- 
- func handler(w http.ResponseWriter, r *http.Request) {
-     filename := r.URL.Query().Get("file")
-     // Vulnerable: user input directly in file path
-     data, err := os.ReadFile("/uploads/" + filename)
+     "path/filepath"
+     "strings"
+ )
+ 
+ const baseDir = "/uploads"
+ 
+ func handler(w http.ResponseWriter, r *http.Request) {
+     filename := r.URL.Query().Get("file")
+     // Clean and resolve the path
+     cleanName := filepath.Clean(filename)
+     fullPath := filepath.Join(baseDir, cleanName)
+     absPath, _ := filepath.Abs(fullPath)
+     absBase, _ := filepath.Abs(baseDir)
+     // Verify path is within base directory
+     if !strings.HasPrefix(absPath, absBase+string(os.PathSeparator)) {
+         http.Error(w, "Forbidden", 403)
+         return
+     }
+     data, err := os.ReadFile(absPath)
      if err != nil {
          http.Error(w, "Not found", 404)
          return
      }
      w.Write(data)
  }
  
Zip Slip / Path Traversal in Archive HIGH

Validate that extracted archive paths resolve within the target directory

+22 -10 go
  package main
  
  import (
      "archive/zip"
-     "io"
-     "os"
- )
- 
- func extractZip(zipPath, destDir string) error {
-     r, _ := zip.OpenReader(zipPath)
-     defer r.Close()
-     for _, f := range r.File {
-         // Vulnerable: using archive filename directly
-         outFile, _ := os.Create(f.Name)
+     "errors"
+     "io"
+     "os"
+     "path/filepath"
+     "strings"
+ )
+ 
+ func extractZip(zipPath, destDir string) error {
+     r, _ := zip.OpenReader(zipPath)
+     defer r.Close()
+     destDir = filepath.Clean(destDir) + string(os.PathSeparator)
+     for _, f := range r.File {
+         // Safe: validate path is within destination
+         destPath := filepath.Join(destDir, filepath.Clean(f.Name))
+         if !strings.HasPrefix(destPath, destDir) {
+             return errors.New("illegal file path in archive: " + f.Name)
+         }
+         if f.FileInfo().IsDir() {
+             os.MkdirAll(destPath, 0755)
+             continue
+         }
+         outFile, _ := os.Create(destPath)
          rc, _ := f.Open()
          io.Copy(outFile, rc)
          rc.Close()
          outFile.Close()
      }
      return nil
  }
  
Path Traversal in File Operations CRITICAL

Use path.basename() to strip directory components or validate resolved paths stay within allowed directories

+2 -1 javascript
  const express = require('express');
  const fs = require('fs');
  const path = require('path');
  const app = express();
  
  app.get('/files/:filename', (req, res) => {
-   const filePath = path.join(__dirname, 'uploads', req.params.filename);
+   const safeName = path.basename(req.params.filename);
+   const filePath = path.join(__dirname, 'uploads', safeName);
    fs.readFile(filePath, (err, data) => {
      if (err) return res.status(404).send('Not found');
      res.send(data);
    });
  });
  
Zip Slip Path Traversal HIGH

Validate that extracted archive entry paths resolve within the target directory before writing

+15 -5 javascript
  const express = require('express');
  const AdmZip = require('adm-zip');
  const path = require('path');
  const app = express();
  
- app.post('/upload', (req, res) => {
-   const zip = new AdmZip(req.file.path);
-   const entries = zip.getEntries();
-   for (const entry of entries) {
-     zip.extractEntryTo(entry, './uploads/', false, true);
+ function isPathSafe(baseDir, targetPath) {
+   const resolvedBase = path.resolve(baseDir);
+   const resolvedTarget = path.resolve(baseDir, targetPath);
+   return resolvedTarget.startsWith(resolvedBase + path.sep);
+ }
+ 
+ app.post('/upload', (req, res) => {
+   const targetDir = './uploads/';
+   const zip = new AdmZip(req.file.path);
+   const entries = zip.getEntries();
+   for (const entry of entries) {
+     if (!isPathSafe(targetDir, entry.entryName)) {
+       return res.status(400).json({ error: 'Path traversal detected' });
+     }
+     zip.extractEntryTo(entry, targetDir, false, true);
    }
    res.json({ success: true });
  });
  
Path Traversal / Directory Traversal HIGH

Use os.path.basename() or pathlib to restrict file access to intended directories

+13 -6 python
- from flask import request
- 
- @app.route('/read')
- def read_file():
-     filename = request.args.get('file')
-     with open(f'/var/uploads/{filename}', 'r') as f:
+ import os
+ from flask import request, abort
+ 
+ UPLOAD_DIR = '/var/uploads'
+ 
+ @app.route('/read')
+ def read_file():
+     filename = request.args.get('file', '')
+     safe_name = os.path.basename(filename)
+     filepath = os.path.join(UPLOAD_DIR, safe_name)
+     if not os.path.isfile(filepath):
+         abort(404)
+     with open(filepath, 'r') as f:
          return f.read()
  
Zip Slip / Archive Path Traversal HIGH

Validate extracted file paths stay within the target directory before writing

+9 -4 python
  import zipfile
- 
- def extract_archive(zip_path, dest):
-     with zipfile.ZipFile(zip_path, 'r') as zf:
-         zf.extractall(dest)
+ import os
+ 
+ def safe_extract(zip_path, dest):
+     with zipfile.ZipFile(zip_path, 'r') as zf:
+         for member in zf.namelist():
+             target = os.path.normpath(os.path.join(dest, member))
+             if not target.startswith(os.path.abspath(dest)):
+                 raise ValueError(f"Path traversal: {member}")
+             zf.extract(member, dest)
  
3 检测
3 检测

查找代码中的漏洞

使用Shoulder扫描代码中的Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')模式。 6 规则.

终端
# Scan with Shoulder CLI
npx @shoulderdev/cli trust --cwe=22

# Or scan entire project
npx @shoulderdev/cli trust .

检测规则 (6)

4 警告信号
4 警告信号

代码审查中需要关注的内容

这些模式表明潜在的Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')漏洞。在代码审查和安全审计中注意查找。

🟠
unsafe extraction of zip/tar archives without path validation, which can lead to arbitrary file writ javascript-zip-slip
🟠
untrusted user input being used in file system operations without proper validation python-path-traversal
🟠
unsafe extraction of ZIP/TAR archives without path validation python-zip-slip
🔴
untrusted user input used in file system operations without proper validation javascript-path-traversal
🔍

扫描你的代码库: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

Shoulder CLI 在整个代码库中找到易受攻击的模式。