Harden websocket and marketplace failure paths
Some checks are pending
Security Scan / security (push) Waiting to run
Test / test (push) Waiting to run

This commit is contained in:
Snider 2026-04-17 20:38:57 +01:00
parent 85dc7849b2
commit 90d7bfd9a4
4 changed files with 65 additions and 1 deletions

View file

@ -266,6 +266,15 @@ func (em *WSEventManager) HandleWebSocket(w http.ResponseWriter, r *http.Request
}
return
}
em.mu.RLock()
closed := em.closed
em.mu.RUnlock()
if closed {
if w != nil {
http.Error(w, http.StatusText(http.StatusServiceUnavailable), http.StatusServiceUnavailable)
}
return
}
if !trustedWebSocketOrigin(r) {
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
return
@ -277,6 +286,11 @@ func (em *WSEventManager) HandleWebSocket(w http.ResponseWriter, r *http.Request
}
em.mu.Lock()
if em.closed {
em.mu.Unlock()
_ = conn.Close()
return
}
em.clients[conn] = &clientState{
subscriptions: make(map[string]*Subscription),
}

View file

@ -180,6 +180,19 @@ func TestWSEventManager_HandleWebSocket_NilReceiverFailsClosed(t *testing.T) {
assert.Equal(t, http.StatusServiceUnavailable, recorder.Code)
}
func TestWSEventManager_HandleWebSocket_RejectsAfterClose(t *testing.T) {
em := NewWSEventManager()
em.Close()
req := httptest.NewRequest(http.MethodGet, "http://127.0.0.1/events", nil)
req.RemoteAddr = "127.0.0.1:12345"
recorder := httptest.NewRecorder()
em.HandleWebSocket(recorder, req)
assert.Equal(t, http.StatusServiceUnavailable, recorder.Code)
}
func TestEvents_trustedWebSocketOrigin_Good(t *testing.T) {
tests := []struct {
name string

View file

@ -153,6 +153,13 @@ func (i Installer) Install(ctx context.Context, manifest Manifest) (string, erro
if err != nil {
return "", err
}
cleanupTarget := true
defer func() {
if cleanupTarget {
_ = os.RemoveAll(targetDir)
}
}()
rel, err := filepath.Rel(rootResolved, targetAbs)
if err != nil {
return "", err
@ -160,7 +167,9 @@ func (i Installer) Install(ctx context.Context, manifest Manifest) (string, erro
if rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
return "", errors.New("install path escapes install dir")
}
_ = os.RemoveAll(targetDir)
if err := os.RemoveAll(targetDir); err != nil {
return "", err
}
args := []string{"clone", "--depth", "1"}
if manifest.Ref != "" {
args = append(args, "--branch", manifest.Ref)
@ -177,6 +186,7 @@ func (i Installer) Install(ctx context.Context, manifest Manifest) (string, erro
if err := writeInstalledManifest(targetDir, manifest); err != nil {
return "", err
}
cleanupTarget = false
return targetDir, nil
}

View file

@ -266,6 +266,33 @@ func TestMarketplace_Install_Ugly(t *testing.T) {
assert.NotContains(t, err.Error(), "token:")
}
func TestMarketplace_Install_CleansUpOnCloneFailure(t *testing.T) {
scriptDir := t.TempDir()
scriptPath := filepath.Join(scriptDir, "git")
targetRoot := t.TempDir()
script := "#!/bin/sh\nlast=''\nfor arg in \"$@\"; do last=\"$arg\"; done\nmkdir -p \"$last\"\ntouch \"$last/partial\"\nexit 1\n"
require.NoError(t, os.WriteFile(scriptPath, []byte(script), 0o755))
installer := Installer{
GitBinary: scriptPath,
InstallDir: targetRoot,
}
manifest := signedManifest(t, Manifest{
Name: "core-ui",
Version: "1.2.3",
Repository: "https://example.com/core-ui.git",
Ref: "main",
})
_, err := installer.Install(context.Background(), manifest)
require.Error(t, err)
targetDir := filepath.Join(targetRoot, "core-ui")
_, statErr := os.Stat(targetDir)
assert.Error(t, statErr)
assert.True(t, os.IsNotExist(statErr))
}
func TestMarketplace_Verify_Good(t *testing.T) {
manifest := signedManifest(t, Manifest{
Name: "core-ui",