Skip to content

Commit aa5d35e

Browse files
author
Greyforge Admin
committed
Reject plugin path arguments for installed operations
1 parent 7954d02 commit aa5d35e

2 files changed

Lines changed: 50 additions & 5 deletions

File tree

src/cortex-cli/src/plugin_cmd.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use anyhow::{Context, Result, bail};
1616
use clap::Parser;
1717
use notify::{Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
1818
use serde::Serialize;
19-
use std::path::{Path, PathBuf};
19+
use std::path::{Component, Path, PathBuf};
2020
use std::process::{Command, Stdio};
2121
use std::sync::mpsc;
2222
use std::time::Duration;
@@ -703,6 +703,22 @@ fn get_plugins_dir() -> PathBuf {
703703
.unwrap_or_else(|| PathBuf::from(".cortex/plugins"))
704704
}
705705

706+
fn installed_plugin_path(plugins_dir: &Path, name: &str) -> Result<PathBuf> {
707+
let mut components = Path::new(name).components();
708+
let is_single_component = matches!(components.next(), Some(Component::Normal(_)))
709+
&& components.next().is_none()
710+
&& !name.chars().any(|c| matches!(c, '/' | '\\' | '\0'));
711+
712+
if name.is_empty() || !is_single_component {
713+
bail!(
714+
"Invalid plugin name '{}'. Use an installed plugin name, not a path.",
715+
name
716+
);
717+
}
718+
719+
Ok(plugins_dir.join(name))
720+
}
721+
706722
// =============================================================================
707723
// Plugin Scaffolding Functions
708724
// =============================================================================
@@ -1145,7 +1161,7 @@ fn copy_dir_recursive(src: &std::path::Path, dst: &std::path::Path) -> Result<()
11451161

11461162
async fn run_remove(args: PluginRemoveArgs) -> Result<()> {
11471163
let plugins_dir = get_plugins_dir();
1148-
let plugin_path = plugins_dir.join(&args.name);
1164+
let plugin_path = installed_plugin_path(&plugins_dir, &args.name)?;
11491165

11501166
if !plugin_path.exists() {
11511167
bail!("Plugin '{}' is not installed.", args.name);
@@ -1171,7 +1187,7 @@ async fn run_remove(args: PluginRemoveArgs) -> Result<()> {
11711187

11721188
async fn run_enable(args: PluginEnableArgs) -> Result<()> {
11731189
let plugins_dir = get_plugins_dir();
1174-
let plugin_path = plugins_dir.join(&args.name);
1190+
let plugin_path = installed_plugin_path(&plugins_dir, &args.name)?;
11751191
let manifest_path = plugin_path.join("plugin.toml");
11761192

11771193
if !manifest_path.exists() {
@@ -1192,7 +1208,7 @@ async fn run_enable(args: PluginEnableArgs) -> Result<()> {
11921208

11931209
async fn run_disable(args: PluginDisableArgs) -> Result<()> {
11941210
let plugins_dir = get_plugins_dir();
1195-
let plugin_path = plugins_dir.join(&args.name);
1211+
let plugin_path = installed_plugin_path(&plugins_dir, &args.name)?;
11961212
let manifest_path = plugin_path.join("plugin.toml");
11971213

11981214
if !manifest_path.exists() {
@@ -1213,7 +1229,7 @@ async fn run_disable(args: PluginDisableArgs) -> Result<()> {
12131229

12141230
async fn run_show(args: PluginShowArgs) -> Result<()> {
12151231
let plugins_dir = get_plugins_dir();
1216-
let plugin_path = plugins_dir.join(&args.name);
1232+
let plugin_path = installed_plugin_path(&plugins_dir, &args.name)?;
12171233
let manifest_path = plugin_path.join("plugin.toml");
12181234

12191235
if !manifest_path.exists() {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use std::process::Command;
2+
3+
use tempfile::tempdir;
4+
5+
#[test]
6+
fn plugin_remove_rejects_absolute_path() {
7+
let home_dir = tempdir().unwrap();
8+
let outside_dir = tempdir().unwrap();
9+
let target = outside_dir.path().join("srcplugin");
10+
std::fs::create_dir_all(&target).unwrap();
11+
std::fs::write(target.join("plugin.toml"), "name = \"demo\"\n").unwrap();
12+
std::fs::write(target.join("marker.txt"), "keep\n").unwrap();
13+
14+
let output = Command::new(env!("CARGO_BIN_EXE_Cortex"))
15+
.args(["plugin", "remove", "-y", target.to_str().unwrap()])
16+
.env("HOME", home_dir.path())
17+
.env_remove("CORTEX_HOME")
18+
.output()
19+
.unwrap();
20+
21+
let stderr = String::from_utf8_lossy(&output.stderr);
22+
assert!(!output.status.success());
23+
assert!(
24+
stderr.contains("Invalid plugin name"),
25+
"unexpected stderr: {stderr}"
26+
);
27+
assert!(target.exists(), "absolute plugin path was deleted");
28+
assert!(target.join("marker.txt").exists());
29+
}

0 commit comments

Comments
 (0)