From 5f7f9c0ff7c409949fa5480005ccb43669ee675684de8939f6d8e9c8e8616906 Mon Sep 17 00:00:00 2001 From: Tyler J King Date: Thu, 2 Apr 2026 18:46:27 -0400 Subject: [PATCH] feat: configurable corpus base dir + Bascule dev config - corpus_check_with_base(): accepts explicit base directory - corpus_check(): still defaults to /opt/substrate/corpus - Improved corpus test with actual Allowed/Denied assertions - Updated bascule-dev.toml with [gsap] section and shell_command Co-Authored-By: Claude Opus 4.6 (1M context) --- config/bascule-dev.toml | 7 +++++++ libgsh/src/corpus.rs | 25 +++++++++++++++++++------ libgsh/src/lib.rs | 2 +- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/config/bascule-dev.toml b/config/bascule-dev.toml index c7adeb2..1e0d65c 100644 --- a/config/bascule-dev.toml +++ b/config/bascule-dev.toml @@ -6,8 +6,15 @@ ca_key_path = "/dev/null" host_key_path = "/dev/null" dispatch_mode = "direct" auth_mode = "permissive" +shell_command = "/home/tking/.local/bin/gsh" [elevation] operator_ttl_secs = 3600 admin_ttl_secs = 1800 emergency_ttl_secs = 900 + +[gsap] +# broker_url = "http://localhost:8091" +# token = "" +default_corpus_cid = "sha256:dev-jumphost" +default_accord_template = "shell-exec" diff --git a/libgsh/src/corpus.rs b/libgsh/src/corpus.rs index 38d520c..f394164 100644 --- a/libgsh/src/corpus.rs +++ b/libgsh/src/corpus.rs @@ -15,15 +15,24 @@ pub enum CorpusCheckResult { Denied { command: String, corpus_cid: String }, } +/// Default corpus base directory. +pub const DEFAULT_CORPUS_BASE: &str = "/opt/substrate/corpus"; + /// Check if a command is authorized in the corpus directory. /// +/// `base_dir` overrides the default /opt/substrate/corpus (set via GSH_CORPUS_DIR env). /// Returns Ok(result) always. Caller decides whether to block on Denied. pub fn corpus_check(corpus_cid: &str, command: &str) -> CorpusCheckResult { + corpus_check_with_base(corpus_cid, command, DEFAULT_CORPUS_BASE) +} + +/// corpus_check with an explicit base directory. +pub fn corpus_check_with_base(corpus_cid: &str, command: &str, base_dir: &str) -> CorpusCheckResult { if corpus_cid == "sha256:ungoverned" { return CorpusCheckResult::Ungoverned; } - let corpus_dir = Path::new("/opt/substrate/corpus").join(corpus_cid); + let corpus_dir = Path::new(base_dir).join(corpus_cid); if !corpus_dir.exists() { return CorpusCheckResult::NotMounted; } @@ -73,10 +82,14 @@ mod tests { std::fs::create_dir_all(&corpus_dir).unwrap(); std::fs::write(corpus_dir.join("kubectl"), "").unwrap(); - // Can't easily test with /opt/substrate/corpus, but the logic is straightforward. - // The unit test validates the command name extraction: - let cmd = "kubectl get pods -n test"; - let name = cmd.split_whitespace().next().unwrap(); - assert_eq!(name, "kubectl"); + let base = dir.path().to_str().unwrap(); + assert!(matches!( + corpus_check_with_base(cid, "kubectl get pods -n test", base), + CorpusCheckResult::Allowed + )); + assert!(matches!( + corpus_check_with_base(cid, "helm install", base), + CorpusCheckResult::Denied { .. } + )); } } diff --git a/libgsh/src/lib.rs b/libgsh/src/lib.rs index a1f4a29..ab35d27 100644 --- a/libgsh/src/lib.rs +++ b/libgsh/src/lib.rs @@ -9,7 +9,7 @@ pub mod session; pub use ac::{AcValidationError, AuthorizationContext}; pub use classifier::{classify_command, CommandClass, FREE_COMMANDS}; pub use config::GshConfig; -pub use corpus::{corpus_check, CorpusCheckResult}; +pub use corpus::{corpus_check, corpus_check_with_base, CorpusCheckResult, DEFAULT_CORPUS_BASE}; pub use cr::{post_cr, CrResult}; pub use registry::ConsumedRegistry; pub use session::SessionState;