# How the SecurityValidator Class Prevents Directory Traversal Attacks in Python

> Discover how the SecurityValidator class prevents directory traversal attacks in Python. Learn its three-layer system for secure file handling and protect your applications.

- Repository: [Richard A/claude-code-telegram](https://github.com/richardatct/claude-code-telegram)
- Tags: how-to-guide
- Published: 2026-02-19

---

**The `SecurityValidator` class in [`src/security/validators.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/security/validators.py) blocks directory traversal attacks through a three-layer defense system that filters dangerous patterns, resolves canonical paths, and enforces strict directory boundaries.**

The `SecurityValidator` is the central security gatekeeper in the [RichardAtCT/claude-code-telegram](https://github.com/RichardAtCT/claude-code-telegram) repository, ensuring that every user-supplied filesystem path is sanitized before any file operation occurs. By implementing coordinated checks within the `validate_path()` method, this class prevents attackers from escaping the approved directory sandbox using sequences like `../` or shell injection patterns.

## Understanding Directory Traversal Vulnerabilities

Directory traversal (or path traversal) attacks exploit insufficient input validation to access files stored outside the intended directory. Attackers typically inject sequences like `../../../etc/passwd` to climb up the filesystem hierarchy. Without proper validation, applications might inadvertently serve sensitive system files or execute arbitrary code.

## How SecurityValidator Blocks Path Escapes

The `SecurityValidator` class implements a defense-in-depth strategy within [`src/security/validators.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/security/validators.py). Rather than relying on a single check, it coordinates three distinct validation layers that must all pass before a path is considered safe. If any layer detects a potential traversal attempt, the method immediately returns `(False, None, <error_message>)`, aborting the operation before file I/O occurs.

## Three-Layer Defense Mechanism

### Layer 1: Pattern-Based Filtering

Before any path resolution occurs, `validate_path()` scans the raw input string against `DANGEROUS_PATTERNS`. This set includes traversal indicators like `..`, shell expansion characters like `~`, and command injection sequences such as `$(`, `;`, `&&`, and `|`.

According to lines 61-75 of [`validators.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/validators.py), if the input contains any forbidden pattern and security patterns are not explicitly disabled, the validator rejects the request with a clear error message. This early exit prevents malicious strings from ever reaching the filesystem layer.

### Layer 2: Canonical Path Resolution

After pattern filtering, the input undergoes canonicalization. The validator converts the string into an absolute `Path` object and calls `Path.resolve()` (lines 84-88). This operation:

- Eliminates symbolic links that could point outside the sandbox
- Normalizes redundant separators and relative segments like `./` or `../`
- Produces an absolute, unambiguous filesystem location

By resolving the path completely before validation, the class ensures that obfuscated traversal attempts (such as `foo/../../../etc/passwd` resolved from within `/app/approved`) are caught in the next layer.

### Layer 3: Directory Boundary Enforcement

The final check enforces the sandbox boundary. The private helper `_is_within_directory()` (lines 110-117) attempts to compute `resolved_path.relative_to(approved_directory)`. If Python raises a `ValueError`, the path lies outside the approved directory tree, triggering an immediate rejection.

As shown in lines 90-97, only paths that successfully compute a relative relationship to the configured `APPROVED_DIRECTORY` pass validation. The method then returns `(True, resolved_path, None)`, providing the sanitized absolute path for safe file operations.

## Implementation Example

The following example demonstrates typical usage within a Telegram bot command handler:

```python
from pathlib import Path
from src.security.validators import SecurityValidator

# Initialize with approved directory from configuration

validator = SecurityValidator(approved_directory=Path("/app/approved"))

def handle_read_file(user_input_path: str):
    # Validate user-supplied path

    is_valid, resolved_path, error = validator.validate_path(user_input_path)
    
    if not is_valid:
        return f"❌ Security violation: {error}"
    
    # Safe to open - resolved_path is guaranteed to be within /app/approved

    try:
        with open(resolved_path, "r", encoding="utf-8") as f:
            return f.read()
    except FileNotFoundError:
        return "File not found within approved directory"

```

Testing the boundary check directly:

```python

# Attempt to escape the sandbox

valid, path, err = validator.validate_path("../../../etc/passwd")
assert not valid
assert "outside approved directory" in err

# Valid path within sandbox

valid, path, err = validator.validate_path("documents/report.txt")
assert valid
assert path == Path("/app/approved/documents/report.txt")

```

## Integration with the Telegram Bot

The `SecurityValidator` is instantiated in [`src/main.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/main.py) using the `APPROVED_DIRECTORY` configuration setting, making it available throughout the application. Bot handlers in `src/bot/handlers/` invoke `validate_path()` before any file operation, ensuring that malicious path inputs from Telegram messages cannot compromise the host system.

## Summary

- **Pattern Filtering**: The `SecurityValidator` scans input for dangerous sequences like `..`, `~`, and shell operators before path resolution occurs in [`src/security/validators.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/security/validators.py).
- **Canonical Resolution**: The `Path.resolve()` method eliminates symbolic links and normalizes relative segments to produce absolute, unambiguous paths.
- **Boundary Enforcement**: The `_is_within_directory()` helper uses `relative_to()` to ensure resolved paths remain strictly within the configured `APPROVED_DIRECTORY`.
- **Fail-Safe Design**: Any validation failure returns `(False, None, error)`, aborting file operations before they reach the filesystem.

## Frequently Asked Questions

### What specific patterns does SecurityValidator block?

The validator checks against `DANGEROUS_PATTERNS` including directory traversal sequences (`..`), shell expansion characters (`~`), and command injection symbols (`$(`, `;`, `&&`, `|`). These patterns are detected in lines 61-75 of [`src/security/validators.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/security/validators.py) before any filesystem interaction occurs.

### How does the validator handle symbolic links?

The `Path.resolve()` method (lines 84-88) fully resolves symbolic links to their actual targets during canonicalization. If a symlink points outside the approved directory, the subsequent boundary check in `_is_within_directory()` detects the escape attempt and rejects the path.

### Can the pattern filtering be disabled?

Yes, the `validate_path()` method accepts a parameter to disable security pattern checks, though this is not recommended for production use. When enabled (the default), the dangerous pattern scan provides the first line of defense against traversal attempts and command injection.

### What happens when a path fails validation?

The method returns a tuple of `(False, None, error_message)`. The calling code (typically in bot handlers) receives the error message and can notify the user while aborting the file operation. This ensures that invalid paths never reach Python's `open()` or similar filesystem functions.