"""Tests for obsidian_rag.security — path traversal, sanitization, sensitive detection."""
from __future__ import annotations
from pathlib import Path
import tempfile
from unittest.mock import MagicMock
import pytest
from obsidian_rag.security import (
detect_sensitive,
filter_tags,
is_symlink_outside_vault,
sanitize_text,
should_index_dir,
validate_path,
)
# ----------------------------------------------------------------------
# validate_path
# ----------------------------------------------------------------------
def test_validate_path_normal_file(tmp_path: Path):
vault = tmp_path / "vault"
vault.mkdir()
target = vault / "subdir" / "note.md"
target.parent.mkdir()
target.touch()
result = validate_path(Path("subdir/note.md"), vault)
assert result == target.resolve()
def test_validate_path_traversal_attempt(tmp_path: Path):
vault = tmp_path / "vault"
vault.mkdir()
with pytest.raises(ValueError, match="traversal"):
validate_path(Path("../etc/passwd"), vault)
def test_validate_path_deep_traversal(tmp_path: Path):
vault = tmp_path / "vault"
vault.mkdir()
with pytest.raises(ValueError, match="traversal"):
validate_path(Path("subdir/../../../etc/passwd"), vault)
def test_validate_path_absolute_path(tmp_path: Path):
vault = tmp_path / "vault"
vault.mkdir()
with pytest.raises(ValueError):
validate_path(Path("/etc/passwd"), vault)
def test_validate_path_path_with_dotdot_in_resolve(tmp_path: Path):
"""Path that resolves inside vault but has .. in parts should be caught."""
vault = tmp_path / "vault"
vault.mkdir()
sub = vault / "subdir"
sub.mkdir()
# validate_path checks parts for ".."
with pytest.raises(ValueError, match="traversal"):
validate_path(Path("subdir/../subdir/../note.md"), vault)
# ----------------------------------------------------------------------
# is_symlink_outside_vault
# ----------------------------------------------------------------------
def test_is_symlink_outside_vault_internal(tmp_path: Path):
vault = tmp_path / "vault"
vault.mkdir()
note = vault / "note.md"
note.touch()
link = vault / "link.md"
link.symlink_to(note)
assert is_symlink_outside_vault(link, vault) is False
def test_is_symlink_outside_vault_external(tmp_path: Path):
vault = tmp_path / "vault"
vault.mkdir()
outside = tmp_path / "outside.md"
outside.touch()
link = vault / "link.md"
link.symlink_to(outside)
assert is_symlink_outside_vault(link, vault) is True
# ----------------------------------------------------------------------
# sanitize_text
# ----------------------------------------------------------------------
def test_sanitize_text_strips_html():
raw = "Hello #world"
result = sanitize_text(raw)
assert "