summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-10 07:52:33 +0200
committerPaul Buetow <paul@buetow.org>2026-03-10 07:52:33 +0200
commitd2ebb8cf2c5f0fccdd703cc64b9f8cecfb539bf9 (patch)
tree79b63f3201f8eba63e94af51c19d7e5ba0cee33a /internal
parentea56e858a0b2194f540b9bf7523b9fbf4691863b (diff)
tui/flamegraph: tighten arrow escape parsing (task 423)
Diffstat (limited to 'internal')
-rw-r--r--internal/tui/flamegraph/model.go12
-rw-r--r--internal/tui/flamegraph/model_test.go19
2 files changed, 27 insertions, 4 deletions
diff --git a/internal/tui/flamegraph/model.go b/internal/tui/flamegraph/model.go
index 2238ba0..a7b26f8 100644
--- a/internal/tui/flamegraph/model.go
+++ b/internal/tui/flamegraph/model.go
@@ -968,14 +968,18 @@ func keyMatchesDirection(keyName, plain string, ansiFinal byte) bool {
}
func isArrowEscapeSequence(value string, ansiFinal byte) bool {
- if len(value) < 3 || value[0] != '\x1b' {
+ body, ok := strings.CutPrefix(value, "\x1b")
+ if !ok || len(body) < 2 {
return false
}
- last := value[len(value)-1]
- if last != ansiFinal {
+ switch body[0] {
+ case '[':
+ return body[len(body)-1] == ansiFinal
+ case 'O':
+ return len(body) == 2 && body[1] == ansiFinal
+ default:
return false
}
- return value[1] == '[' || value[1] == 'O'
}
func (m Model) visibleRowOffset() int {
diff --git a/internal/tui/flamegraph/model_test.go b/internal/tui/flamegraph/model_test.go
index 23c44e1..c2626cd 100644
--- a/internal/tui/flamegraph/model_test.go
+++ b/internal/tui/flamegraph/model_test.go
@@ -612,6 +612,25 @@ func TestArrowEscapeSequencesAreRecognized(t *testing.T) {
}
}
+func TestArrowEscapeSequencesRejectMalformedValues(t *testing.T) {
+ tests := []struct {
+ name string
+ key string
+ ansiCode byte
+ }{
+ {name: "missing escape", key: "up", ansiCode: 'A'},
+ {name: "too short", key: "\x1b[", ansiCode: 'A'},
+ {name: "wrong final", key: "\x1b[A", ansiCode: 'B'},
+ {name: "unsupported introducer", key: "\x1bPA", ansiCode: 'A'},
+ {name: "application mode with extra payload", key: "\x1bO1A", ansiCode: 'A'},
+ }
+ for _, tc := range tests {
+ if isArrowEscapeSequence(tc.key, tc.ansiCode) {
+ t.Fatalf("expected %s sequence %q to be rejected", tc.name, tc.key)
+ }
+ }
+}
+
func TestFilteredNavigationSkipsHiddenBranches(t *testing.T) {
m := NewModel(nil)
m.frames = []tuiFrame{