fix: don't embed shell-unsafe install paths in statusline setup nudge (#224)
The SessionStart nudge built a statusLine command by interpolating the plugin's __dirname path into a double-quoted shell string. A clone path containing shell metacharacters (quotes, &, $, backtick, ;) could break out when the suggested command later runs via the statusline shell. Low severity in practice: the path is the install location, so triggering it requires installing into a maliciously-named directory, i.e. the attacker already controls the filesystem. Hardening it anyway. Gate the snippet behind isShellSafe() (allowlist of ordinary path chars, allowing : \ / for normal Windows and POSIX paths). Unsafe paths fall back to a manual-setup instruction instead of an embeddable command. An allowlist beats a per-shell escaper, which is its own edge-case bug farm. Refs #200
This commit is contained in:
@@ -42,6 +42,15 @@ function isDeactivationCommand(text) {
|
||||
return t === 'stop ponytail' || t === 'normal mode';
|
||||
}
|
||||
|
||||
// ponytail: only embed the plugin install path in a statusline shell command when
|
||||
// it's made of ordinary path characters. An allowlist beats escaping every shell's
|
||||
// metacharacters; a hostile clone path (quotes, &, $, backtick, ;, etc.) falls back
|
||||
// to manual setup instead. Allows : \ / for normal Windows and POSIX paths. Full
|
||||
// per-shell escaper only if a real need appears.
|
||||
function isShellSafe(p) {
|
||||
return typeof p === 'string' && /^[A-Za-z0-9 _.\-:/\\~]+$/.test(p);
|
||||
}
|
||||
|
||||
function getConfigDir() {
|
||||
if (process.env.XDG_CONFIG_HOME) {
|
||||
return path.join(process.env.XDG_CONFIG_HOME, 'ponytail');
|
||||
@@ -104,6 +113,7 @@ module.exports = {
|
||||
getConfigDir,
|
||||
getConfigPath,
|
||||
getClaudeDir,
|
||||
isShellSafe,
|
||||
normalizeMode,
|
||||
normalizeConfigMode,
|
||||
normalizePersistedMode,
|
||||
|
||||
Reference in New Issue
Block a user