Skip to content

Security: hyorman/RAGnarok

Security

docs/SECURITY.md

Security

RAGnarōk has one trust model. Both the VS Code extension and the MCP server run as the local operating-system user, on that user's data, at that user's authority. It is not a multi-tenant service, and it is not designed to be exposed to a network.

They share host-neutral core services but use separate storage roots: VS Code uses extension global storage and MCP uses RAGNAROK_STORAGE_DIR. There is no cross-host data sharing. Granting one host access does not grant it access to the other host's memories unless an operator explicitly copies data outside these interfaces.

Process boundary

The MCP server speaks stdio only. It binds no address, accepts no connection, and terminates when its client closes the pipe. Its attacker model is therefore whatever can already run code as that user — not a remote caller. There is no transport to configure and nothing to authenticate.

The variables that once configured a network listener (TLS paths, bind host, allowed hosts, CORS origin, bearer tokens, rate limit, trusted proxies, transfer limits) are no longer read. Setting one has no effect: the process still starts, still speaks stdio, and still listens on nothing, so a configuration written for a hardened network service becomes a local pipe without any warning.

Do not attempt to re-expose the server by wrapping stdio in a network relay. A relay would grant every caller the spawning user's full authority — including memory, storage reset, and read access to every path under security.allowedPaths — with no role, quota, or credential in between. Run a separate instance per user and per storage root instead.

Capabilities

There are no roles. All 8 tools are registered unconditionally, and the client that spawned the process may call any of them, including destructive ones (rag_delete_topic, rag_remove_document, rag_reset_memory). Each destructive tool requires an explicit confirm: true argument; that is a guard against an agent's mistake, not an authorization boundary.

Treat the decision to add this server to an MCP client as the security decision. The meaningful controls are which storage root it uses, which paths security.allowedPaths in <storageRoot>/config.json exposes, and which credentials (RAGNAROK_LLM_API_KEY, RAGNAROK_GITHUB_TOKEN) its environment carries. Credentials remain environment-only and cannot be set in the file.

Memory graph visualization exposure

rag_memory_visualize serves memory graphs only; the document knowledge graph no longer exists. It is a full-detail data export, not a topology-only view. A caller authorized to invoke it receives complete persisted memory node and edge descriptions, memory scope, branch, source memory IDs, confidence/strength values, timestamps, provenance, and arbitrary metadata. Embedding vectors are always excluded.

Memory is personal, and the tool that exports it is always registered, so the protection is the process boundary rather than a capability check: anything able to call this server can read the user's complete memory graph. The server does not substitute fallback or sample graph records; a scope with no stored entities returns an empty document.

The associated ui://ragnarok/graph MCP App is self-contained and loads no external scripts. It renders user-controlled strings through text DOM APIs, not HTML interpolation. The tool uses only modern _meta.ui.resourceUri, and both resource catalog and resource content declare text/html;profile=mcp-app.

VS Code's RAG: Show Memory Graph webview uses separate generated JS/CSS, a nonce and restrictive CSP, local resource roots, and the same text-only renderer. It reads only VS Code extension memory. The extension contributes no reset language-model tool at all: deleting that root is the sidebar's Reset Memory command, which a human triggers behind a modal confirmation, so a model cannot reach it. MCP's rag_reset_memory remains a tool and separately requires confirm: true for MCP storage, because a headless agent has no sidebar to click.

Authentication protocol scope

RAGnarok implements no authentication at all: authority comes from the OS user who spawned the process. It does not implement an OAuth authorization-code flow, dynamic client registration (DCR), credential persistence, or client ID metadata documents (CIMD), and it no longer validates bearer strings. The 2026 OAuth/DCR hardening items are therefore not applicable to this server.

File and network boundaries

rag_ingest (files) and archive import accept only paths under canonical security.allowedPaths or the configured export root, resolved on the machine running the server. There is no upload handle; content is indexed from the filesystem the server can already see. That allowlist therefore defines what an agent driving this server can read: default it to the project root rather than a home directory.

All three allowlists live only in <storageRoot>/config.json: security.allowedPaths for ingest roots, storage.exportDir for the export root — which is joined into the same ingest allowlist, so widening it widens what can be read — and security.githubHosts for GitHub ingestion. There is no environment variable for any of them.

Write access to the storage root is therefore write access to these allowlists, with no way to pin one from outside the store. Whoever can write config.json decides what the server may read. Keep the storage root under at least the same protection as the paths it grants: if the store is on a shared volume, a network mount, or a directory another process can write, treat its allowlists as controlled by that writer rather than by the MCP client.

An empty value is meaningful rather than absent. An empty security.allowedPaths narrows the ingest roots to the working directory — its documented default — so it widens nothing. An empty security.githubHosts is rejected at startup rather than falling back to github.com: emptying a security allowlist is a deliberate instruction, and silently restoring the default would grant back access that had just been revoked.

URL ingestion rejects unsafe targets. Credentials come from service configuration and are never accepted as tool arguments. Review DNS/proxy policy and outbound network egress independently; application validation is not a substitute for an egress firewall.

Archive import rejects traversal, absolute paths, duplicates, case collisions, unmanifested entries, checksum failures, excessive sizes, and excessive compression ratios before publication. Treat all imported content as untrusted text that may contain prompt injection.

Logging and secret handling

The server emits no audit ledger, and there is nothing meaningful to audit: one local user issues every request. Its stderr log is operational evidence for that user, is bounded by logging.level, and must not be relied on as a tamper-proof or attributable record — a local operator can alter the process, its environment, and the storage root.

Keep LLM keys and GitHub tokens in the OS keychain or a secret manager and inject them into the MCP client's server environment. Do not put them in committed client configuration, command history, archives, benchmark results, VSIX files, npm tarballs, or source control. RAGNAROK_GITHUB_TOKEN is read from configuration only and is never accepted as a tool argument.

Release audit exceptions

There are none. node scripts/audit-gate.mjs fails on any moderate-or-higher production advisory. If an exception is ever unavoidable, it is an exact, time-limited entry in release-policy.json, documented here with its advisory, dependency, affected range, introducing package, reason, invalidation condition, expiry and owner, and removed as soon as the invalidation condition is true.

Threat boundaries and non-goals

The server protects canonical path resolution, bounded archive and response handling, GitHub/URL ingestion allowlists, and storage integrity checks. It does not provide:

  • any authentication, authorization, or multi-user separation;
  • isolation from the user who spawned it, or from another process running as that user;
  • per-topic authorization within one server;
  • malware scanning or content moderation;
  • tamper-proof audit retention;
  • protection from a compromised host or LLM provider.

Run separate instances and storage roots when those boundaries differ. Use OS user accounts, egress controls, malware scanning, and centralized identity systems when the risk model requires them — none of those belong inside a stdio child process.

There aren't any published security advisories