Sicherheitsrunde: Befehlsinjektion, Pool-Pfad, Namensprüfung, Bild-Beacon
Vier Befunde aus einer Prüfung entlang der Angriffsflächen (MCP-Tools, Command-Grenze, Credentials, Schreibpfade und Abhängigkeiten): - todo.rs: Der SessionStart-Hook interpolierte den Projektpfad unquotiert in eine Shell-Kommandozeile, die Claude Code bei jedem Sessionstart ausfuehrt. Ein Ordner `repo$(...)` -- etwa aus einem geklonten Fremd-Repo -- ergab dauerhafte Codeausfuehrung; ein Leerzeichen zerbrach den Hook still. Der Pfad wird jetzt gequotet, inklusive Apostroph. - project_pool_dir: Der Pool-Name kommt aus der ai-control.json im Projektordner, also aus einer versionierten Datei. Ungeprueft bestimmte er mit ../ ein beliebiges Verzeichnis als CLAUDE_CONFIG_DIR, dessen settings.json wiederum apiKeyHelper traegt -- ein Kommando, das beim Sessionstart laeuft. Jetzt durch check_name abgesichert. - check_name: Steuerzeichen, Backslash und fuehrender Punkt werden abgelehnt. Ein Newline im Projektnamen hing bisher eigene Schluessel an die .desktop- Datei; Exec= braucht keinen Schraegstrich, den der alte Check verbot. - markdown.ts: Bilder duerfen nur noch lokale Ziele laden. Ein auswaertiges <img> laedt ohne Zutun und meldet damit IP, Zeitpunkt und im Pfad kodierte Daten -- der Panelinhalt stammt aus einer LLM-Session, ist also injizierbar. Schema-relative URLs (//host/x) waren durch ein zu laxes Muster erfasst. Links bleiben auswaerts erlaubt, jetzt mit rel="noopener noreferrer". escapeHtml escaped zusaetzlich das Apostroph. Dazu cargo audit nachgeholt: plist 1.9.0 -> 1.10.0 hebt quick-xml auf 0.41.0 und schliesst RUSTSEC-2026-0194/0195 fuer den Laufzeitpfad. Die zweite Instanz von quick-xml 0.39.4 bleibt ueber wayland-scanner im Lockfile -- ein proc-macro, das nur zur Compile-Zeit laeuft und ausgelieferte Protokoll-XMLs parst, nicht im Binary landet. 77 Rust-Tests, 39 Frontend-Tests.
This commit is contained in:
@@ -156,9 +156,20 @@ pub(crate) fn list_projects_in(paths: &Paths) -> Result<Vec<Project>, String> {
|
||||
|
||||
/// Pool-Config-Verzeichnis eines Projekts — wird dem Terminal als
|
||||
/// CLAUDE_CONFIG_DIR mitgegeben.
|
||||
///
|
||||
/// Der Name wird geprüft, obwohl `assign_pool_in` das beim Zuweisen schon tut:
|
||||
/// Die Quelle ist die `ai-control.json` **im Projektordner**, also eine
|
||||
/// versionierte Datei, die mit einem geklonten Repo hereinkommt. Ungeprüft
|
||||
/// bestimmte sie mit `../…` ein beliebiges Verzeichnis als CLAUDE_CONFIG_DIR —
|
||||
/// und dessen `settings.json` trägt `apiKeyHelper`, ein Kommando, das beim
|
||||
/// Sessionstart ausgeführt wird.
|
||||
pub(crate) fn project_pool_dir(project: &str) -> Result<Option<PathBuf>, String> {
|
||||
let paths = Paths::real();
|
||||
Ok(read_project_config_in(&paths, project)?.pool.map(|p| paths.pool_dir(&p)))
|
||||
let Some(pool) = read_project_config_in(&paths, project)?.pool else {
|
||||
return Ok(None);
|
||||
};
|
||||
check_name(&pool).map_err(|_| format!("ungültiger Pool in {PROJECT_FILE}: {pool}"))?;
|
||||
Ok(Some(paths.pool_dir(&pool)))
|
||||
}
|
||||
|
||||
/// Terminal-Einstellungen eines Projekts, für den Terminal-Prozess.
|
||||
@@ -534,6 +545,21 @@ mod tests {
|
||||
assert_eq!(v["pool"], serde_json::Value::String(pool));
|
||||
}
|
||||
|
||||
/// Ein Pool-Eintrag aus einer mitgeklonten `ai-control.json` darf kein
|
||||
/// beliebiges Verzeichnis zum CLAUDE_CONFIG_DIR machen.
|
||||
#[test]
|
||||
fn pool_aus_projektdatei_mit_traversal_wird_abgelehnt() {
|
||||
let p = tmp_paths();
|
||||
create_project(&p, "fremd").unwrap();
|
||||
let cfg_path = project_config_path(&p, "fremd").unwrap();
|
||||
fs::write(&cfg_path, r#"{"pool":"../../../../tmp/x"}"#).unwrap();
|
||||
|
||||
let cfg = read_project_config_in(&p, "fremd").unwrap();
|
||||
assert_eq!(cfg.pool.as_deref(), Some("../../../../tmp/x"));
|
||||
// Gelesen wird der Wert noch, aber er darf keinen Pfad bilden.
|
||||
assert!(check_name(cfg.pool.as_deref().unwrap()).is_err());
|
||||
}
|
||||
|
||||
/// Das Setzen der Terminal-Config kommt aus der Oberfläche und kennt nur
|
||||
/// theme/icon/title — unbekannte Keys müssen trotzdem stehen bleiben.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user