Bug bash 2026-09-19: 17 bugs fixed and verified (BUGS.md)
Widgets: a bad pattern no longer silences every widget; malformed layouts load. Shell: lost session returns to login at once; tab titles; Escape closes menus and dialogs. Files: names are one path segment; unsaved edits ask before page/machine/tab changes; only folders that opened are remembered. Terminals: unique default names; 64-char names. API: queries capped at 10 000 values; 32 streams per client, 512 in all; only /embed/widget is frameable. ZFS sensors: one unprivileged zpool list, no sudo journal spam. Phone: 36 px inputs; dataset rows as wide as their cells. Machines: valid name pattern. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
36de4ad4a1
commit
8ee2a2d293
@@ -209,10 +209,16 @@ func Shape(all []Sensor, query json.RawMessage, full bool) (any, error) {
|
||||
if err := json.Unmarshal(query, &q); err != nil {
|
||||
return nil, errors.New("query is not valid JSON")
|
||||
}
|
||||
return shape(all, q, full, 0)
|
||||
budget := MaxValues
|
||||
return shape(all, q, full, 0, &budget)
|
||||
}
|
||||
|
||||
func shape(all []Sensor, q any, full bool, depth int) (any, error) {
|
||||
// MaxValues is how many sensor values one query may return. A query is a few bytes that
|
||||
// can ask for everything many times over ({"a":"*","b":"*",…}); without a ceiling 10 KB of
|
||||
// request buys 27 MB of response. Ten thousand is thirty times a whole machine.
|
||||
const MaxValues = 10000
|
||||
|
||||
func shape(all []Sensor, q any, full bool, depth int, budget *int) (any, error) {
|
||||
if depth > 16 {
|
||||
return nil, errors.New("query nested too deep")
|
||||
}
|
||||
@@ -232,8 +238,12 @@ func shape(all []Sensor, q any, full bool, depth int) (any, error) {
|
||||
if len(got) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
*budget--
|
||||
return leaf(got[0]), nil
|
||||
}
|
||||
if *budget -= len(got); *budget < 0 {
|
||||
return nil, fmt.Errorf("query returns more than %d values; ask for less", MaxValues)
|
||||
}
|
||||
m := make(map[string]any, len(got))
|
||||
for _, s := range got {
|
||||
m[s.ID] = leaf(s)
|
||||
@@ -242,7 +252,7 @@ func shape(all []Sensor, q any, full bool, depth int) (any, error) {
|
||||
case map[string]any:
|
||||
m := make(map[string]any, len(v))
|
||||
for k, sub := range v {
|
||||
r, err := shape(all, sub, full, depth+1)
|
||||
r, err := shape(all, sub, full, depth+1, budget)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -252,7 +262,7 @@ func shape(all []Sensor, q any, full bool, depth int) (any, error) {
|
||||
case []any:
|
||||
a := make([]any, len(v))
|
||||
for i, sub := range v {
|
||||
r, err := shape(all, sub, full, depth+1)
|
||||
r, err := shape(all, sub, full, depth+1, budget)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -75,3 +75,21 @@ func TestWriteProm(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestShapeBudget(t *testing.T) {
|
||||
all := make([]Sensor, 300)
|
||||
for i := range all {
|
||||
all[i] = Sensor{ID: "s." + strings.Repeat("x", i%7) + string(rune('a'+i%26)) + strings.Repeat("y", i/26), Value: 1.0}
|
||||
}
|
||||
q := map[string]string{}
|
||||
for i := 0; i < 40; i++ { // 40 × 300 = 12 000 values
|
||||
q["k"+strings.Repeat("z", i)] = "*"
|
||||
}
|
||||
b, _ := json.Marshal(q)
|
||||
if _, err := Shape(all, b, false); err == nil {
|
||||
t.Fatal("a query returning more than MaxValues must be refused")
|
||||
}
|
||||
if _, err := Shape(all, json.RawMessage(`{"a":"*","b":"*"}`), false); err != nil {
|
||||
t.Fatalf("an ordinary query must pass: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user