paperclipai/paperclip · error · Error

Plugin migrationsDir escapes package root: ${migrationsDir}

Error message

Plugin migrationsDir escapes package root: ${migrationsDir}

What it means

Path-traversal guard in resolveMigrationsDir: after joining and resolving, the plugin's configured migrationsDir resolves outside the plugin package root. A malicious or misconfigured manifest could otherwise point migrations at arbitrary host directories; the escaping migrationsDir value is at fault.

Source

Thrown at server/src/services/plugin-database.ts:341

    throw new Error("Every ctx.db parameter must be referenced by a $n placeholder");
  }
  return sql.join(chunks, sql.raw(""));
}

async function listSqlMigrationFiles(migrationsDir: string): Promise<string[]> {
  const entries = await readdir(migrationsDir, { withFileTypes: true });
  return entries
    .filter((entry) => entry.isFile() && entry.name.endsWith(".sql"))
    .map((entry) => entry.name)
    .sort((a, b) => a.localeCompare(b));
}

function resolveMigrationsDir(packageRoot: string, migrationsDir: string): string {
  const resolvedRoot = path.resolve(packageRoot);
  const resolvedDir = path.resolve(resolvedRoot, migrationsDir);
  const relative = path.relative(resolvedRoot, resolvedDir);
  if (relative.startsWith("..") || path.isAbsolute(relative)) {
    throw new Error(`Plugin migrationsDir escapes package root: ${migrationsDir}`);
  }
  return resolvedDir;
}

type PluginDatabaseClient = Pick<Db, "select" | "insert" | "update" | "execute">;
type PluginDatabaseRootClient = PluginDatabaseClient & Partial<Pick<Db, "transaction">>;

export interface ApplyPluginMigrationsOptions {
  /**
   * Persist failed migration ledger rows. Fresh install uses false because the
   * caller owns a larger transaction and must roll back the plugin row and
   * namespace together.
   */
  persistFailure?: boolean;
}

export function pluginDatabaseService(db: PluginDatabaseRootClient) {
  async function getPluginRecord(pluginId: string) {

View on GitHub (pinned to 120ae5428f)

Solutions

  1. Set migrationsDir to a path inside the plugin package root.
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at server/src/services/plugin-database.ts:341 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of paperclipai/paperclip@120ae5428f (2026-08-18). Data as JSON: /api/errors/b55626915b274be3. Report an issue: GitHub.