CodeDocs Vault

Trusted Folders Security

The trusted folders system prevents untrusted project-local configuration, tools, agents, and skills from being loaded automatically.

Purpose

When a user navigates to a project directory that contains .vibe/ configuration, tools, agents, or skills, there is a risk that malicious project-local content could be loaded and executed. The trusted folders system ensures that users must explicitly approve each directory before its local content is used.

TrustedFoldersManager

File: vibe/core/trusted_folders.py:22

class TrustedFoldersManager:
    def __init__(self) -> None:
        self._file_path = TRUSTED_FOLDERS_FILE.path
        self._trusted: list[str] = []
        self._untrusted: list[str] = []
        self._load()

A singleton instance is created at module level:

# trusted_folders.py:83
trusted_folders_manager = TrustedFoldersManager()

Trust States

State is_trusted() return Behavior
Trusted True Local config/tools/agents/skills loaded
Untrusted False Only global config used
Unknown None Triggers trust dialog in interactive mode

Key Methods

Method Line Purpose
is_trusted(path) 58 Check trust state (True/False/None)
add_trusted(path) 66 Mark path as trusted, remove from untrusted
add_untrusted(path) 74 Mark path as untrusted, remove from trusted
_load() 32 Load state from TOML file
_save() 49 Persist state to TOML file

Path Normalization

All paths are normalized via expanduser().resolve() before storage:

def _normalize_path(self, path: Path) -> str:
    return str(path.expanduser().resolve())

Storage

Trust decisions are persisted in ~/.vibe/trusted_folders.toml:

trusted = [
    "/Users/user/projects/my-project",
    "/Users/user/projects/another-project",
]
untrusted = [
    "/Users/user/Downloads/suspicious-repo",
]

Trustable Content Detection

Function: has_trustable_content() at trusted_folders.py:13

TRUSTABLE_FILENAMES = ["AGENTS.md", "VIBE.md", ".vibe.md"]
 
def has_trustable_content(path: Path) -> bool:
    if (path / ".vibe").exists():
        return True
    for name in TRUSTABLE_FILENAMES:
        if (path / name).exists():
            return True
    return False

A directory is considered to have trustable content if it contains:

Trust Check Flow

Function: check_and_resolve_trusted_folder() at entrypoint.py:103

check_and_resolve_trusted_folder() [entrypoint.py:103-135]
    │
    ├─1─► Check if current directory has trustable content
    │     has_trustable_content(Path.cwd())
    │     If no trustable content: return (nothing to check)
    │
    ├─2─► Check existing trust state
    │     trusted_folders_manager.is_trusted(cwd)
    │     │
    │     ├── True: return (already trusted)
    │     └── False: return (already untrusted)
    │
    ├─3─► If None (unknown): show trust dialog
    │     TrustFolderDialog(cwd)
    │     │
    │     ├── User selects "Trust": add_trusted(cwd)
    │     └── User selects "Don't Trust": add_untrusted(cwd)
    │
    └─4─► Only runs in interactive mode
          Skipped for programmatic (--prompt) mode

Integration Points

Config Path Resolution

File: vibe/core/paths/config_paths.py

def _resolve_config_path(basename: str, type: Literal["file", "dir"]) -> Path:
    cwd = Path.cwd()
    is_folder_trusted = trusted_folders_manager.is_trusted(cwd)
    if not is_folder_trusted:
        return VIBE_HOME.path / basename  # Fall back to global
    # Only check local .vibe/ if trusted
    if type == "file":
        if (candidate := cwd / ".vibe" / basename).is_file():
            return candidate
    elif type == "dir":
        if (candidate := cwd / ".vibe" / basename).is_dir():
            return candidate
    return VIBE_HOME.path / basename

Tool Discovery

Function: resolve_local_tools_dir() at config_paths.py:34

def resolve_local_tools_dir(dir: Path) -> Path | None:
    if not trusted_folders_manager.is_trusted(dir):
        return None  # Block local tools from untrusted dirs
    if (candidate := dir / ".vibe" / "tools").is_dir():
        return candidate
    return None

Skill Discovery

Function: resolve_local_skills_dir() at config_paths.py:42

Same pattern as tools — returns None for untrusted directories.

Agent Discovery

Function: resolve_local_agents_dir() at config_paths.py:50

Same pattern as tools — returns None for untrusted directories.

ConfigPath and Locking

File: vibe/core/paths/config_paths.py

ConfigPath extends GlobalPath with a locking mechanism:

_config_paths_locked: bool = True
 
class ConfigPath(GlobalPath):
    @property
    def path(self) -> Path:
        if _config_paths_locked:
            raise RuntimeError("Config path is locked")
        return super().path

Config paths are locked by default and unlocked only after the trust check:

main() [entrypoint.py:137]
    │
    ├── check_and_resolve_trusted_folder()  # Trust check first
    └── unlock_config_paths()               # Then unlock

This prevents any code from accidentally resolving config paths before the trust decision is made.

Execution Sequence

vibe entrypoint [entrypoint.py:137]
    │
    ├─1─► Parse arguments
    │
    ├─2─► Change workdir if --workdir specified
    │
    ├─3─► If interactive mode:
    │     check_and_resolve_trusted_folder() [entrypoint.py:151]
    │     ├── Detect trustable content
    │     ├── Show dialog if unknown
    │     └── Record decision
    │
    ├─4─► unlock_config_paths() [entrypoint.py:152]
    │     Allow ConfigPath instances to resolve
    │
    └─5─► run_cli(args) [entrypoint.py:156]
          Config loading now respects trust decisions

Security Model

The trust system ensures:

  1. No silent loading: Project-local config/tools/agents/skills are never loaded without explicit user consent
  2. Persistent decisions: Trust decisions are saved, so the user is only asked once per directory
  3. Home directory exempt: The home directory itself is never treated as trustable
  4. Path normalization: All paths are expanduser().resolve() before storage
  5. Graceful degradation: If trust state is unknown or untrusted, only global config is used

Source File References

File Key Lines Description
trusted_folders.py:10 TRUSTABLE_FILENAMES Files that trigger trust check
trusted_folders.py:13-19 has_trustable_content() Detect trustable content
trusted_folders.py:22-80 TrustedFoldersManager Trust state management
trusted_folders.py:58-64 is_trusted() Check trust state
trusted_folders.py:83 trusted_folders_manager Singleton instance
config_paths.py:12-17 ConfigPath Trust-locked path resolver
config_paths.py:20-31 _resolve_config_path() Trust-aware config resolution
config_paths.py:34-39 resolve_local_tools_dir() Trust-aware tool discovery
config_paths.py:42-47 resolve_local_skills_dir() Trust-aware skill discovery
config_paths.py:50-55 resolve_local_agents_dir() Trust-aware agent discovery
config_paths.py:58-60 unlock_config_paths() Unlock config path resolution
entrypoint.py:103-135 check_and_resolve_trusted_folder() Trust dialog flow