Skip to content

Security: pongpitag/link-agent

Security

docs/SECURITY.md

Security Documentation

Classification: Critical
Applies to: All contributors and AI agents operating this system

Threat Model

Threat Risk Mitigation
AI hallucination destroys files Critical HITL + Docker sandbox + read-only mounts
Prompt injection escapes container Critical Directory boundary checking + localhost-only
Malicious shell command execution Critical Command classification + approval workflow
Data exfiltration High Network bind to 127.0.0.1 only
Path traversal High resolve_safe_path() validation

4-Layer Security Architecture

Layer 1: Docker Sandbox (SEC1)

The backend must run inside a Docker container. It never executes directly on the host OS.

# docker-compose.yml
services:
  backend:
    build:
      context: .
      dockerfile: backend/Dockerfile
    ports:
      - "127.0.0.1:8000:8000" # Never 0.0.0.0
    volumes:
      - ./:/app/workspace:rw # Working directory
      - ./raw_sources:/app/workspace/raw_sources:ro # Read-only

Layer 2: Directory Boundary (SEC2)

All file paths are validated before use.

from backend.src.security import resolve_safe_path

safe_path = resolve_safe_path("../../../etc/passwd")
# Raises: ValueError("Path traversal blocked...")

Rules:

  • Paths are resolved to absolute form
  • Symlinks are followed and validated
  • The resolved path must be inside WORKING_DIR
  • /raw_sources is mounted read-only

Layer 2.5: Visibility Policy (SEC2.5)

User-facing directories are explicitly whitelisted for visibility and write access.

USER_ROOTS (user-facing, read/write):

USER_ROOTS = {"raw_sources", "workspace", "wiki", "skills"}
  • raw_sources/ — Source code for graph analysis (agent can read)
  • workspace/ — Working outputs, drafts, reports (agent can read/write)
  • wiki/ — Markdown knowledge base (agent can read/write)
  • skills/ — Skill definitions (agent can read/write for discovery + stubs)

SYSTEM_ROOTS (hidden, system-internal):

SYSTEM_ROOTS = {"node_modules", "venv", ".git", ".vscode", ".link", ...}
  • Hidden directories starting with . are blocked unless explicit exception (e.g., .env)
  • Agent writes to wiki/ and workspace/ freely; blocks to .git, .venv, etc.
  • Graphify and Bootstrap operations respect this boundary automatically

Layer 3: Human-in-the-Loop (SEC3)

Mutating commands require explicit user approval via the Web UI.

Auto-blocked commands:

  • rm, mv, cp (with overwrite)
  • pip install, npm install
  • docker, sudo
  • Any command with >, >>, or | redirection

Auto-allowed commands:

  • ls, cat, grep, find, head, tail
  • pwd, echo, git status

Flow:

User sends command
  → Backend classifies command
  → If mutating: create HITL request
  → Frontend shows approval modal
  → User clicks Approve/Reject
  → If approved: execute command
  → Result returned to chat

Layer 4: Network Binding (SEC4)

Backend binds exclusively to 127.0.0.1.

# main.py
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:5173"],  # Never *
)

This prevents external access even if the host firewall is misconfigured.

Layer 5: Tool Tier Classification (SEC5)

Tools are classified by risk to determine Human-in-the-Loop (HITL) approval requirements.

TIER_0_RO (Read-only, no approval needed):

  • read_file, search_files, list_directory
  • web_search, read_url (information gathering only)
  • read_todo, read_conversation
  • Bypass HITL: Yes
  • Reasoning: No local state mutation; external I/O is idempotent

TIER_1_WRITE (Safe write operations, no approval):

  • create_directory, write_file (non-destructive)
  • add_todo_task, mark_todo_done, update_identity
  • Bypass HITL: Yes
  • Reasoning: Write to user-visible paths (wiki, workspace, todo); user can undo

TIER_2_EXTERNAL (External with side effects, requires approval):

  • run_workflow, run_agentic_workflow (delegates to external agents)
  • Bypass HITL: No (currently unused, reserved for future)
  • Reasoning: Unpredictable impact from sub-agents

TIER_3_DESTRUCTIVE (Destructive, requires approval):

  • delete_file, copy_file (with overwrite)
  • Shell commands with rm, mv, redirects, pipe ops
  • Bypass HITL: No
  • Reasoning: Irreversible state change on user files

Classification in backend/src/tools.py:

def get_tool_tier(name: str, args: dict) -> OperationTier:
    ro_tools = {
        "read_file", ...,
        "web_search", "read_url",  # ← No HITL
    }
    if name in ro_tools:
        return OperationTier.TIER_0_RO

    if name in ("write_file", "create_directory", ...):
        return OperationTier.TIER_1_WRITE

    # ... TIER_2 and TIER_3 logic

API Key Handling

  • Never commit .env files to git (see .gitignore)
  • Keys are injected at runtime via environment variables
  • No API keys exist in source code

Security Checklist for Developers

Before submitting code:

  • Does this change expose any new endpoints?
  • Are new file operations using resolve_safe_path()?
  • Are new shell commands classified as mutating/readonly?
  • Is the CORS origin restricted to localhost?
  • Are secrets injected via env vars, not hardcoded?
  • Does docker-compose.yml bind to 127.0.0.1?

Reporting Security Issues

If you discover a security vulnerability, please report it immediately. Do not open a public issue.

There aren't any published security advisories