summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-06-23 06:40:44 +0300
committerPaul Buetow <paul@buetow.org>2026-06-23 06:40:44 +0300
commitf75924987a1685055d4eb4517a0f2443c6f7d6be (patch)
treebc4712b81fbee8fd7fe4b3a82b95b4df8c03adec /internal
parentc525eb4c200a1bc20a8d866efaa3eaa3ce564376 (diff)
Reject typed nil Taskwarrior injection for vq0
Diffstat (limited to 'internal')
-rw-r--r--internal/ui/table.go19
-rw-r--r--internal/ui/table_test.go22
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)
+ }
+ })
}
}