How OpenAI Validates Path Format for Plugin Configurations: Schema and Runtime Security

OpenAI validates plugin configuration paths through a two-layer defense combining JSON-Schema regex patterns in validate.mjs with runtime directory traversal checks in plugin.js to ensure paths stay within the plugin sandbox.

The openai/plugins repository implements strict path validation to prevent security vulnerabilities in plugin configurations. When a plugin manifest specifies a file path, the system must verify that the reference points only to files within the plugin's designated directory. This article examines the exact validation mechanisms, from regex patterns to filesystem resolution checks, that enforce these boundaries according to the source code.

Two-Layer Validation Architecture

OpenAI’s validation strategy employs schema validation followed by runtime safety checks.

Layer Purpose Location
Schema Validation Enforces string format rules (no traversal sequences or double slashes) plugins/plugin-eval/scripts/validate.mjs
Runtime Enforcement Resolves paths and verifies they remain inside the plugin root plugins/plugin-eval/src/evaluators/plugin.js

This combination guarantees that only relative paths such as "src/handler.ts" are accepted, while absolute paths or traversal attempts like "../secret.txt" are rejected before file system access occurs.

JSON-Schema Pattern Validation

The first line of defense is a JSON-Schema that validates the path field using a regular expression.

Schema Definition in validate.mjs

In plugins/plugin-eval/scripts/validate.mjs, the schema is constructed programmatically and passed to the ajv validator. The pattern uses negative lookahead assertions to block dangerous sequences:

// plugins/plugin-eval/scripts/validate.mjs
const pathPattern = /^(?!.*\.\.)(?!.*\/\/)[^\n\r]*$/; // no ".." or "//"
const configSchema = {
  type: "object",
  properties: {
    path: { type: "string", pattern: pathPattern.source },
    // … other fields …
  },
  required: ["path"],
  additionalProperties: false,
};

The regex ^(?!.*\.\.)(?!.*\/\/)[^\n\r]*$ enforces three critical constraints:

  • (?!.*\.\.) prevents path traversal by disallowing the double-dot sequence anywhere in the string.
  • (?!.*\/\/) blocks double slashes that could indicate protocol specifiers or path normalization tricks.
  • ^ and $ anchors ensure the entire string conforms to the pattern, preventing substring bypasses.

Runtime Root Directory Enforcement

After schema validation passes, the system performs a filesystem-aware check to guarantee the resolved path does not escape the plugin’s sandbox.

The ensureWithinRoot Safety Check

Located in plugins/plugin-eval/src/evaluators/plugin.js, the ensureWithinRoot function uses Node.js path utilities to validate the resolved location:

// plugins/plugin-eval/src/evaluators/plugin.js
import path from "path";

/**
 * Throws if candidatePath, once resolved, is outside rootDir.
 */
function ensureWithinRoot(candidatePath, rootDir) {
  const absolute = path.resolve(rootDir, candidatePath);
  const relative = path.relative(rootDir, absolute);
  if (relative.startsWith("..") || path.isAbsolute(relative)) {
    throw new Error(
      `Invalid plugin path – ${candidatePath} points outside the plugin root`
    );
  }
  return absolute;
}

This function implements a canonical path comparison:

  1. path.resolve(rootDir, candidatePath) generates an absolute path from the plugin root.
  2. path.relative(rootDir, absolute) calculates the relative path from root to the target.
  3. Security check: If the relative path starts with .. or is itself absolute, the target lies outside the sandbox and the function throws an error.

Integration and Validation Flow

When a plugin configuration is loaded, the evaluator orchestrates both validation layers. The following pattern demonstrates how the system combines ajv schema validation with the runtime root check:

import Ajv from "ajv";
import { configSchema } from "./validate.mjs";

export async function validatePluginConfig(rawConfig, pluginRoot) {
  const ajv = new Ajv();
  const valid = ajv.validate(configSchema, rawConfig);
  if (!valid) {
    throw new Error(`Schema validation error: ${ajv.errorsText()}`);
  }

  // Runtime safety check
  const safePath = ensureWithinRoot(rawConfig.path, pluginRoot);
  return { ...rawConfig, resolvedPath: safePath };
}

Error Examples

The system produces distinct error messages depending on which validation layer catches the violation.

Schema validation failure (blocked by regex):

const cfg = { path: "src//handler.ts" };
await validatePluginConfig(cfg, "/plugins/my-awesome-plugin");
// → throws: Schema validation error: path must match pattern "^(?!.*\.\.)(?!.*\/\/)[^\n\r]*$"

Runtime traversal detection:

const cfg = { path: "../secret.txt" };
await validatePluginConfig(cfg, "/plugins/my-awesome-plugin");
// → throws: Invalid plugin path – ../secret.txt points outside the plugin root

Summary

  • JSON-Schema validation in plugins/plugin-eval/scripts/validate.mjs uses a regex pattern /^(?!.*\.\.)(?!.*\/\/)[^\n\r]*$/ to block traversal sequences and double slashes at the configuration level.
  • Runtime enforcement via ensureWithinRoot in plugins/plugin-eval/src/evaluators/plugin.js resolves paths using path.resolve and path.relative to verify they remain within the plugin root directory.
  • Combined defense ensures that malicious paths like "/etc/passwd", "../secret.txt", or "src//../handler.ts" are rejected before any file system operations occur.
  • Test coverage exists in plugins/plugin-eval/tests/plugin-eval.test.js to verify both validation layers function correctly under edge cases.

Frequently Asked Questions

What regex pattern does OpenAI use to validate plugin paths?

OpenAI uses the pattern /^(?!.*\.\.)(?!.*\/\/)[^\n\r]*$/ defined in plugins/plugin-eval/scripts/validate.mjs. This regex employs negative lookaheads to disallow the substring .. (path traversal) and // (double slashes) anywhere in the path string, while the anchors ^ and $ ensure the entire value conforms to the rule.

How does the runtime check prevent directory traversal attacks?

The ensureWithinRoot function in plugins/plugin-eval/src/evaluators/plugin.js resolves the candidate path against the plugin root using path.resolve, then computes the relative path using path.relative. If the resulting relative path starts with .. or is absolute, the function throws an error, guaranteeing the resolved file location cannot escape the plugin’s sandbox.

Where is the path validation logic located in the openai/plugins repository?

The validation logic is split between two locations: the JSON-Schema definition resides in plugins/plugin-eval/scripts/validate.mjs, and the runtime security check is implemented in plugins/plugin-eval/src/evaluators/plugin.js. Test cases for both layers are located in plugins/plugin-eval/tests/plugin-eval.test.js.

What happens if a plugin configuration fails path validation?

If validation fails at the schema level, the ajv validator returns an error indicating the path does not match the required pattern. If it fails at the runtime level, the ensureWithinRoot function throws an Error with the message "Invalid plugin path – {path} points outside the plugin root". In both cases, the plugin configuration is rejected before any file system access is attempted.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →