# Claude-Obsidian URL Safety Validation Rules: How the Tool Validates URLs Before Capture

> Learn how Claude-Obsidian validates URLs before capture. Discover rules for preventing credential leaks, blocking unsafe schemes, and rejecting private networks.

- Repository: [Agrici.Daniel/claude-obsidian](https://github.com/AgriciDaniel/claude-obsidian)
- Tags: how-to-guide
- Published: 2026-08-26

---

**Claude-Obsidian validates every URL before capture to prevent credential leaks, block unsafe schemes like `javascript:`, and reject private network addresses, aborting the operation with a `URLSafetyIssue` if any check fails.**

The URL safety validation rules in Claude-Obsidian ensure that only clean, secure links enter your knowledge vault. These rules are enforced by the [`url_safety.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/url_safety.py) module and are automatically invoked during the capture workflow via [`claude_obsidian/capture.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/claude_obsidian/capture.py) and [`claude_obsidian/ledgers.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/claude_obsidian/ledgers.py) to protect against accidental secret exposure and malicious content injection.

## The Three Core Validation Layers

The validation logic in [`claude_obsidian/url_safety.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/claude_obsidian/url_safety.py) performs three distinct security checks before allowing a URL to be captured. If any check fails, the system raises a `URLSafetyIssue` exception and aborts the capture process.

### Credential Detection in the Netloc Component

The parser examines the *netloc* component of the URL for embedded user-info strings (e.g., `username:password@host`). The function `url_credential_issue` specifically checks for this pattern and returns a `URLSafetyIssue` describing the leak when credentials are detected. This prevents secret keys, API tokens, or passwords from being stored in your Obsidian vault.

### Unsafe Scheme Filtering

Only a whitelist of safe URL schemes is permitted. **Safe schemes** include `http`, `https`, `ftp`, and `ftps`. Schemes such as `javascript:`, `data:`, or custom protocols are flagged as unsafe because they can execute arbitrary code or embed malicious payloads. Any URL using a non-approved scheme is rejected immediately by the validator.

### Private Network and Loopback Protection

URLs that resolve to private IP ranges or localhost are blocked unless explicitly allowed. This includes addresses in the `127.0.0.1` loopback range, `10.*.*.*` private networks, and `192.168.*.*` local subnets. This guardrail prevents accidental capture of internal services that should not be part of a public knowledge base.

## Implementation Across the Codebase

The safety validation is integrated into multiple entry points across the repository:

- **[`claude_obsidian/capture.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/claude_obsidian/capture.py)** – The central capture routine calls the safety validator before storing any link, ensuring no unsafe URLs reach the vault.
- **[`claude_obsidian/ledgers.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/claude_obsidian/ledgers.py)** – Records provenance and links to evidence, invoking URL safety checks to maintain integrity of the knowledge graph.
- **[`claude_obsidian/cli.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/claude_obsidian/cli.py)** – Provides the command-line interface (`claude-obsidian capture …`) that triggers the validation pipeline before any write operations.
- **[`tests/test_capture.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/tests/test_capture.py)** – Contains the test suite verifying that the URL safety rules correctly reject credentials, unsafe schemes, and private IPs.

## Practical Code Example

Here is how the validation logic can be implemented using the Claude-Obsidian safety utilities:

```python
from claude_obsidian.url_safety import url_credential_issue
from urllib.parse import urlparse

def is_url_safe(url: str) -> bool:
    parsed = urlparse(url)
    
    # Reject if credentials are embedded

    if url_credential_issue(parsed):
        return False
    
    # Reject unsafe schemes

    if parsed.scheme not in {"http", "https", "ftp", "ftps"}:
        return False
    
    # Reject private network addresses

    host = parsed.hostname or ""
    if host.startswith("127.") or host.startswith("10.") or host.startswith("192.168."):
        return False
    
    return True

# Example usage:

print(is_url_safe("https://example.com/resource"))      # → True

print(is_url_safe("https://user:pass@example.com"))   # → False (credential leak)

print(is_url_safe("http://192.168.1.5/internal"))     # → False (private IP)

print(is_url_safe("javascript:alert(1)"))             # → False (unsafe scheme)

```

## Summary

- **Credential Detection** in [`claude_obsidian/url_safety.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/claude_obsidian/url_safety.py) rejects URLs containing `user:pass@host` patterns via the `url_credential_issue` function.
- **Scheme Filtering** permits only `http`, `https`, `ftp`, and `ftps`, blocking executable protocols like `javascript:` and `data:`.
- **Network Protection** prevents capture of private IPs (`127.x.x.x`, `10.x.x.x`, `192.168.x.x`) and internal services.
- **Error Handling** raises `URLSafetyIssue` to abort capture and surface clear error messages when validation fails.
- The validation is invoked by [`capture.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/capture.py), [`ledgers.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/ledgers.py), and [`cli.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/cli.py) to ensure comprehensive protection across all entry points.

## Frequently Asked Questions

### What happens if a URL contains embedded credentials?

If `url_credential_issue` detects a username and password in the netloc component (e.g., `https://token:secret@api.example.com`), it returns a `URLSafetyIssue` describing the credential leak. The capture process aborts immediately, and the user receives an error message instructing them to remove the sensitive information from the URL before retrying.

### Why does Claude-Obsidian block private IP addresses?

Private IP ranges—including `127.0.0.1`, `10.x.x.x`, and `192.168.x.x`—are blocked to prevent accidental documentation of internal services, development endpoints, or local network resources that should remain isolated from a shared knowledge vault. This guardrail ensures that captured content refers to stable, publicly resolvable resources unless explicitly configured otherwise.

### Which URL schemes are considered safe in Claude-Obsidian?

The validator explicitly permits `http`, `https`, `ftp`, and `ftps`. Schemes such as `javascript:`, `data:`, `file:`, or custom application protocols are rejected because they can execute code, embed arbitrary binary data, or access the local filesystem. This restriction in [`claude_obsidian/url_safety.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/claude_obsidian/url_safety.py) prevents injection attacks and ensures link portability.