diff --git a/workspace-server/cmd/memory-plugin-postgres/config_test.go b/workspace-server/cmd/memory-plugin-postgres/config_test.go new file mode 100644 index 00000000..252f0d1b --- /dev/null +++ b/workspace-server/cmd/memory-plugin-postgres/config_test.go @@ -0,0 +1,50 @@ +package main + +import ( + "strings" + "testing" +) + +// TestLoadConfig_DefaultListenAddrIsLoopback pins the default-bind contract. +// +// Why this matters: with the prior `:9100` default, the plugin listened on +// every interface. Inside the container it didn't matter (no host port +// mapping today), but a future change that publishes 9100 OR a cross-host +// sidecar deploy would have exposed an unauth'd memory store. Loopback by +// default is the least-privilege baseline; operators with a multi-host +// topology override via MEMORY_PLUGIN_LISTEN_ADDR. +func TestLoadConfig_DefaultListenAddrIsLoopback(t *testing.T) { + t.Setenv("MEMORY_PLUGIN_DATABASE_URL", "postgres://stub") + t.Setenv("MEMORY_PLUGIN_LISTEN_ADDR", "") + + cfg, err := loadConfig() + if err != nil { + t.Fatalf("loadConfig: %v", err) + } + if !strings.HasPrefix(cfg.ListenAddr, "127.0.0.1:") { + t.Errorf("default ListenAddr must bind loopback-only, got %q "+ + "(security regression — would expose plugin on every interface)", + cfg.ListenAddr) + } +} + +func TestLoadConfig_ListenAddrEnvOverride(t *testing.T) { + t.Setenv("MEMORY_PLUGIN_DATABASE_URL", "postgres://stub") + t.Setenv("MEMORY_PLUGIN_LISTEN_ADDR", ":9100") + + cfg, err := loadConfig() + if err != nil { + t.Fatalf("loadConfig: %v", err) + } + if cfg.ListenAddr != ":9100" { + t.Errorf("env override ignored: want :9100, got %q", cfg.ListenAddr) + } +} + +func TestLoadConfig_MissingDatabaseURL(t *testing.T) { + t.Setenv("MEMORY_PLUGIN_DATABASE_URL", "") + + if _, err := loadConfig(); err == nil { + t.Fatal("loadConfig must error when MEMORY_PLUGIN_DATABASE_URL is empty") + } +} diff --git a/workspace-server/cmd/memory-plugin-postgres/main.go b/workspace-server/cmd/memory-plugin-postgres/main.go index 84e01351..148c1dd4 100644 --- a/workspace-server/cmd/memory-plugin-postgres/main.go +++ b/workspace-server/cmd/memory-plugin-postgres/main.go @@ -31,7 +31,13 @@ const ( envListenAddr = "MEMORY_PLUGIN_LISTEN_ADDR" envSkipMigrate = "MEMORY_PLUGIN_SKIP_MIGRATE" - defaultListenAddr = ":9100" + // Loopback-only by default (defense in depth). The platform talks to + // the plugin over `http://localhost:9100` from the same container, so + // binding to all interfaces would only widen the reachable surface + // without enabling any in-design caller. Operators running the plugin + // on a separate host override via MEMORY_PLUGIN_LISTEN_ADDR=:9100 (or + // some other interface). + defaultListenAddr = "127.0.0.1:9100" ) func main() {