diff options
| author | Paul Buetow <paul@buetow.org> | 2026-06-23 06:40:44 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-06-23 06:40:44 +0300 |
| commit | f75924987a1685055d4eb4517a0f2443c6f7d6be (patch) | |
| tree | bc4712b81fbee8fd7fe4b3a82b95b4df8c03adec | |
| parent | c525eb4c200a1bc20a8d866efaa3eaa3ce564376 (diff) | |
Reject typed nil Taskwarrior injection for vq0
| -rw-r--r-- | internal/ui/table.go | 19 | ||||
| -rw-r--r-- | internal/ui/table_test.go | 22 |
2 files changed, 34 insertions, 7 deletions
diff --git a/internal/ui/table.go b/internal/ui/table.go index 774954c..d4c9d46 100644 --- a/internal/ui/table.go +++ b/internal/ui/table.go @@ -6,6 +6,7 @@ import ( "fmt" "os" "os/exec" + "reflect" "regexp" "strconv" "strings" @@ -363,12 +364,26 @@ func blinkCmd() tea.Cmd { } func (m *Model) taskwarriorClient() task.Taskwarrior { - if m.taskwarrior == nil { + if isNilTaskwarrior(m.taskwarrior) { panic("ui.Model Taskwarrior client is nil; use ui.New or ui.NewWithTaskwarrior") } return m.taskwarrior } +func isNilTaskwarrior(tw task.Taskwarrior) bool { + if tw == nil { + return true + } + + value := reflect.ValueOf(tw) + switch value.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice: + return value.IsNil() + default: + return false + } +} + // clearEditingModes ensures only one editing mode is active at a time func (m *Model) clearEditingModes() { m.annotating = false @@ -453,7 +468,7 @@ func New(filters []string, browserCmd string) (Model, error) { // NewWithTaskwarrior creates a UI model using the provided Taskwarrior client. func NewWithTaskwarrior(filters []string, browserCmd string, tw task.Taskwarrior) (Model, error) { - if tw == nil { + if isNilTaskwarrior(tw) { return Model{}, errors.New("taskwarrior client is nil") } m := Model{filters: filters, browserCmd: browserCmd, agentFilterHotkey: "3", taskwarrior: tw, blinkState: blinkState{blinkEnabled: true}} diff --git a/internal/ui/table_test.go b/internal/ui/table_test.go index a9348ba..14e78ca 100644 --- a/internal/ui/table_test.go +++ b/internal/ui/table_test.go @@ -188,12 +188,24 @@ func TestNewWithTaskwarriorUsesFakeForAddTask(t *testing.T) { } func TestNewWithTaskwarriorRejectsNilClient(t *testing.T) { - _, err := NewWithTaskwarrior(nil, "firefox", nil) - if err == nil { - t.Fatalf("NewWithTaskwarrior with nil client succeeded") + tests := []struct { + name string + tw task.Taskwarrior + }{ + {name: "plain nil", tw: nil}, + {name: "typed nil", tw: (*fakeTaskwarrior)(nil)}, } - if got, want := err.Error(), "taskwarrior client is nil"; got != want { - t.Fatalf("NewWithTaskwarrior error = %q, want %q", got, want) + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + _, err := NewWithTaskwarrior(nil, "firefox", tc.tw) + if err == nil { + t.Fatalf("NewWithTaskwarrior with nil client succeeded") + } + if got, want := err.Error(), "taskwarrior client is nil"; got != want { + t.Fatalf("NewWithTaskwarrior error = %q, want %q", got, want) + } + }) } } |
