From 62fd24f408656a09de03c1eb75944c2cfdf9cb73 Mon Sep 17 00:00:00 2001 From: Adrian Bienkowski Date: Mon, 20 Jul 2026 12:21:31 -0400 Subject: [PATCH 1/2] fix: gracefully handle missing config directory in all three languages --- go/internal/policy/manager.go | 5 +++++ rs/src/policy.rs | 9 ++++++++- ts/src/policy.ts | 12 +++++++++++- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/go/internal/policy/manager.go b/go/internal/policy/manager.go index befbc42..91b004b 100644 --- a/go/internal/policy/manager.go +++ b/go/internal/policy/manager.go @@ -2,6 +2,7 @@ package policy import ( "fmt" + "log/slog" "os" "path/filepath" "strings" @@ -22,6 +23,10 @@ func NewManager(configDir string) (*Manager, error) { entries, err := os.ReadDir(configDir) if err != nil { + if os.IsNotExist(err) { + slog.Warn("config dir not found, starting with empty policy set", "dir", configDir) + return m, nil + } return nil, fmt.Errorf("failed to read config directory %s: %w", configDir, err) } diff --git a/rs/src/policy.rs b/rs/src/policy.rs index ebea8d8..392bf3e 100644 --- a/rs/src/policy.rs +++ b/rs/src/policy.rs @@ -55,7 +55,14 @@ impl Manager { pub fn new(config_dir: &str) -> Result> { let mut policies_by_name = HashMap::new(); - let entries = std::fs::read_dir(config_dir)?; + let entries = match std::fs::read_dir(config_dir) { + Ok(d) => d, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { + eprintln!("warn: config dir {} not found, starting with empty policy set", config_dir); + return Ok(Manager { policies_by_name }); + } + Err(e) => return Err(e.into()), + }; for entry in entries { let entry = entry?; diff --git a/ts/src/policy.ts b/ts/src/policy.ts index df4e2f7..4551dab 100644 --- a/ts/src/policy.ts +++ b/ts/src/policy.ts @@ -42,7 +42,17 @@ export class Manager { private policiesByName = new Map(); constructor(configDir: string) { - const entries = readdirSync(configDir); + let entries: string[]; + try { + entries = readdirSync(configDir); + } catch (err: any) { + if (err.code === "ENOENT") { + console.warn(`warn: config dir ${configDir} not found, starting with empty policy set`); + entries = []; + } else { + throw err; + } + } for (const entry of entries) { if (!entry.endsWith(".yaml") && !entry.endsWith(".yml")) continue; From eabd01d77060f5e36942fa3edc08e3668e06c348 Mon Sep 17 00:00:00 2001 From: Adrian Bienkowski Date: Mon, 20 Jul 2026 13:10:15 -0400 Subject: [PATCH 2/2] fix: graceful audit log failure fallback in Go and Rust --- go/internal/audit/audit.go | 8 ++++++++ go/main.go | 4 ++-- rs/src/audit.rs | 11 +++++++++++ rs/src/main.rs | 8 +++++++- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/go/internal/audit/audit.go b/go/internal/audit/audit.go index a57b5e3..05e1e11 100644 --- a/go/internal/audit/audit.go +++ b/go/internal/audit/audit.go @@ -37,6 +37,14 @@ func NewLogger(path string) (*Logger, error) { }, nil } +func NewNopLogger() *Logger { + f, _ := os.OpenFile(os.DevNull, os.O_WRONLY, 0644) + return &Logger{ + file: f, + enc: json.NewEncoder(f), + } +} + func generateRequestID() string { b := make([]byte, 16) if _, err := rand.Read(b); err != nil { diff --git a/go/main.go b/go/main.go index ead4ad2..fd48649 100644 --- a/go/main.go +++ b/go/main.go @@ -39,8 +39,8 @@ func main() { auditLog, err := audit.NewLogger(*logFile) if err != nil { - slog.Error("failed to initialize audit logger", "error", err) - os.Exit(1) + slog.Warn("audit logging disabled", "error", err) + auditLog = audit.NewNopLogger() } defer auditLog.Close() diff --git a/rs/src/audit.rs b/rs/src/audit.rs index 27864db..3a162b3 100644 --- a/rs/src/audit.rs +++ b/rs/src/audit.rs @@ -17,6 +17,17 @@ impl AuditLogger { }) } + pub fn nop() -> Self { + let file = OpenOptions::new() + .create(true) + .append(true) + .open("/dev/null") + .expect("failed to open /dev/null"); + AuditLogger { + file: Mutex::new(file), + } + } + pub fn allow(&self, method: &str, uri: &str) { self.log("ALLOW", method, uri, ""); } diff --git a/rs/src/main.rs b/rs/src/main.rs index e807610..4ecc217 100644 --- a/rs/src/main.rs +++ b/rs/src/main.rs @@ -49,7 +49,13 @@ async fn main() -> Result<(), Box> { let router = Arc::new(proxy::Router::new(policy_manager)); let chain = middleware::Chain::new(cli.readonly); - let audit = audit::AuditLogger::new(&cli.log_file)?; + let audit = match audit::AuditLogger::new(&cli.log_file) { + Ok(logger) => logger, + Err(e) => { + eprintln!("warn: audit logging disabled: {}", e); + audit::AuditLogger::nop() + } + }; let transport: Box = Box::new(transport::UnixSocketTransport::new(&cli.docker_host)); let handler = Arc::new(handler::Handler::new(router, chain, audit, transport));