can1357/oh-my-pi · error · SecurityDisabledError

Security disabled

Error message

Security disabled

What it means

The security:// protocol handler is gated behind the security.enabled setting. At the top of resolve(), if neither the ResolveContext settings nor the global settings report security.enabled === true, the handler throws SecurityDisabledError with the message "Security disabled" (expanded text tells the user to set security.enabled = true). This protects the read-only security scan namespace so it is only reachable when the user has opted in.

Source

Thrown at packages/coding-agent/src/internal-urls/security-protocol.ts:120

	readonly scheme = "security";
	readonly immutable = true;
	readonly #resolveStore: SecurityStoreResolver;
	readonly #enabled: () => boolean;

	constructor(
		resolveStore: SecurityStoreResolver = (cwd, signal) => SecurityStore.openForCwd(cwd, { signal }),
		enabled: () => boolean = isSecurityEnabled,
	) {
		this.#resolveStore = resolveStore;
		this.#enabled = enabled;
	}

	async #store(context?: ResolveContext): Promise<SecurityStore> {
		return this.#resolveStore(path.resolve(context?.cwd ?? process.cwd()), context?.signal);
	}

	async resolve(url: InternalUrl, context?: ResolveContext): Promise<InternalResource> {
		if (!(securityEnabledFromContext(context) ?? this.#enabled())) throw new SecurityDisabledError();
		const parts = splitSecurityPath(url);
		const store = await this.#store(context);
		if (parts.length === 0) {
			return createSecurityResource({
				url: "security://",
				content: [
					"# Security",
					"",
					"OMP-owned software-security analysis resources. The namespace is read-only; use explicit security commands or tools for mutations.",
					"",
					"- `security://scans` — list scans",
					"",
				].join("\n"),
				contentType: "text/markdown",
				isDirectory: true,
			});
		}
		if (parts[0] !== "scans") throw new Error(`Unknown security resource: security://${parts.join("/")}`);

View on GitHub (pinned to 9690622007)

Solutions

  1. Enable the feature: set security.enabled = true in settings (Settings → Tools → Security) or via the settings API before resolving.
  2. If resolving programmatically, pass a ResolveContext whose settings.get("security.enabled") returns true.
  3. Check the return of isSecurityEnabled() before attempting security:// lookups and surface the enable-instructions message to the user instead of failing.
  4. If you expected it to be enabled, verify the settings file actually loaded (isSettingsInitialized) and that no project config overrides security.enabled to false.

Example fix

// before
const res = await handler.resolve(new URL("security://scans"));
// after
if (!isSecurityEnabled()) {
  throw new Error("Enable security: set security.enabled = true in settings.");
}
const res = await handler.resolve(new URL("security://scans"));
Defensive patterns

Strategy: type-guard

Validate before calling

import { isSecurityEnabled } from "./internal-urls/security-protocol";

async function resolveSecurityUrl(url: URL, context?: ResolveContext) {
  if (!isSecurityEnabled()) {
    throw new Error("security:// is disabled. Set security.enabled = true (Settings → Tools → Security).");
  }
  return securityHandler.resolve(url as InternalUrl, context);
}

Type guard

function securityEnabledIn(context?: ResolveContext): boolean {
  if (!context?.settings || typeof context.settings !== "object") return isSecurityEnabled();
  try {
    const get = Reflect.get(context.settings, "get");
    if (typeof get !== "function") return isSecurityEnabled();
    const enabled = Reflect.apply(get, context.settings, ["security.enabled"]);
    return typeof enabled === "boolean" ? enabled : isSecurityEnabled();
  } catch {
    return isSecurityEnabled();
  }
}

Try / catch

try {
  return await handler.resolve(url, ctx);
} catch (err) {
  if (err instanceof SecurityDisabledError) {
    logger.warn("security:// access attempted while disabled", { url: url.href });
    return null; // or surface err.message to the user as guidance
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling SecurityProtocolHandler.resolve(url) (e.g. resolving security:// or security://scans/...) when (a) no context is passed and the global security.enabled setting is false (or unset and defaults to false), or (b) context.settings.get("security.enabled") returns false or a non-boolean, overriding to disabled.

Common situations: An agent or script tries to read security://scans on a fresh install where the feature was never enabled; a caller passes a ResolveContext whose settings object lacks a boolean for security.enabled; tests or embeddings construct the handler with the default isSecurityEnabled but never toggle the setting; a settings schema change leaves the default off.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/d1a409fcfbdef84a. Report an issue: GitHub.