security: fix path traversal in plugin_rm, add coding tools
- prevent path traversal in ais plugin rm (reject .. and /) - validate plugin_add requires user/repo format - extract list_plugins() helper to deduplicate code - add git aliases (s, d, l, lg, pa for dual push, etc) - add cargo/rg aliases and mkcd helper to zshrc - fix zr alias (source instead of unreachable exec)
This commit is contained in:
@@ -75,9 +75,12 @@ fn plugin_add(name: &str) {
|
||||
let repo = name.rsplit('/').next().unwrap_or(name);
|
||||
let repo = repo.strip_suffix(".git").unwrap_or(repo);
|
||||
(name.to_string(), repo.to_string())
|
||||
} else {
|
||||
} else if name.contains('/') {
|
||||
let repo = name.split('/').last().unwrap_or(name);
|
||||
(format!("https://github.com/{}.git", name), repo.to_string())
|
||||
} else {
|
||||
eprintln!("usage: ais plugin add <user/repo>");
|
||||
return;
|
||||
};
|
||||
|
||||
let dest = format!("{}/{}", dir, repo_name);
|
||||
@@ -97,59 +100,53 @@ fn plugin_add(name: &str) {
|
||||
}
|
||||
}
|
||||
|
||||
fn plugin_ls() {
|
||||
fn list_plugins() -> Vec<String> {
|
||||
let dir = plugin_dir();
|
||||
let entries = match std::fs::read_dir(&dir) {
|
||||
Ok(e) => e,
|
||||
Err(_) => {
|
||||
eprintln!("no plugins");
|
||||
return;
|
||||
}
|
||||
Err(_) => return vec![],
|
||||
};
|
||||
entries
|
||||
.flatten()
|
||||
.filter(|e| e.file_type().map(|t| t.is_dir()).unwrap_or(false))
|
||||
.map(|e| e.file_name().to_string_lossy().to_string())
|
||||
.filter(|n| !n.starts_with('.'))
|
||||
.collect()
|
||||
}
|
||||
|
||||
for entry in entries.flatten() {
|
||||
if entry.file_type().map(|t| t.is_dir()).unwrap_or(false) {
|
||||
let name = entry.file_name().to_string_lossy().to_string();
|
||||
if !name.starts_with('.') {
|
||||
println!("{}", name);
|
||||
}
|
||||
}
|
||||
fn plugin_ls() {
|
||||
let plugins = list_plugins();
|
||||
if plugins.is_empty() {
|
||||
eprintln!("no plugins");
|
||||
return;
|
||||
}
|
||||
for name in &plugins {
|
||||
println!("{}", name);
|
||||
}
|
||||
}
|
||||
|
||||
fn plugin_rm(name: Option<&str>) {
|
||||
let dir = plugin_dir();
|
||||
|
||||
let target = if let Some(n) = name {
|
||||
n.to_string()
|
||||
} else {
|
||||
// fuzzy select from installed plugins
|
||||
let entries = match std::fs::read_dir(&dir) {
|
||||
Ok(e) => e,
|
||||
Err(_) => {
|
||||
eprintln!("no plugins");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let plugins: Vec<String> = entries
|
||||
.flatten()
|
||||
.filter(|e| e.file_type().map(|t| t.is_dir()).unwrap_or(false))
|
||||
.map(|e| e.file_name().to_string_lossy().to_string())
|
||||
.filter(|n| !n.starts_with('.'))
|
||||
.collect();
|
||||
|
||||
let plugins = list_plugins();
|
||||
if plugins.is_empty() {
|
||||
eprintln!("no plugins");
|
||||
return;
|
||||
}
|
||||
|
||||
match fuzzy_select(&plugins, "rm plugin") {
|
||||
Some(s) => s,
|
||||
None => return,
|
||||
}
|
||||
};
|
||||
|
||||
let path = format!("{}/{}", dir, target);
|
||||
// prevent path traversal
|
||||
if target.contains('/') || target.contains("..") {
|
||||
eprintln!("invalid plugin name: {}", target);
|
||||
return;
|
||||
}
|
||||
|
||||
let path = format!("{}/{}", plugin_dir(), target);
|
||||
if !std::path::Path::new(&path).exists() {
|
||||
eprintln!("{} not found", target);
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user