bastion 0.53.2
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func TestParseTreeArgs(t *testing.T) {
|
||||
if _, _, err := parseTreeArgs(nil); err == nil {
|
||||
t.Fatal("a pid is required")
|
||||
}
|
||||
for _, bad := range []string{"0", "1", "-5", "abc", "12 ; rm -rf /", ""} {
|
||||
if _, _, err := parseTreeArgs([]string{bad}); err == nil {
|
||||
t.Fatalf("%q must be refused", bad)
|
||||
}
|
||||
}
|
||||
pid, n, err := parseTreeArgs([]string{" 4242 "})
|
||||
if err != nil || pid != 4242 || n != treeDefaultMaxNodes {
|
||||
t.Fatalf("defaults: %d %d %v", pid, n, err)
|
||||
}
|
||||
if _, n, _ := parseTreeArgs([]string{"7", "99999"}); n != treeMaxNodes {
|
||||
t.Fatalf("the cap is capped: %d", n)
|
||||
}
|
||||
if treeDefaultMaxNodes != 500 || treeMaxNodes != 800 {
|
||||
t.Fatalf("caps must match the host side: %d / %d", treeDefaultMaxNodes, treeMaxNodes)
|
||||
}
|
||||
if _, _, err := parseTreeArgs([]string{"7", "x"}); err == nil {
|
||||
t.Fatal("a non-integer cap is refused")
|
||||
}
|
||||
// the only channel into a helper is the environment, as two integers
|
||||
env := helperEnv(4242, 300)
|
||||
if len(env) != 2 || env[0] != "PRIVATAE_TARGET_PID=4242" || env[1] != "PRIVATAE_MAX_NODES=300" {
|
||||
t.Fatalf("helper env: %v", env)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoundTreeRecountsAndCaps(t *testing.T) {
|
||||
leaf := func(name string) *computerNode { return &computerNode{Role: "button", Name: name} }
|
||||
win := &computerNode{Role: "window", Name: "Fusion 360", Children: []*computerNode{
|
||||
{Role: "toolbar", Children: []*computerNode{leaf("Sketch"), leaf("Extrude"), leaf("Revolve")}},
|
||||
leaf("Close"),
|
||||
}}
|
||||
out, count, trunc := boundTree([]*computerNode{win}, 100)
|
||||
if count != 6 || trunc || len(out) != 1 || len(out[0].Children) != 2 || len(out[0].Children[0].Children) != 3 {
|
||||
t.Fatalf("full tree: count=%d trunc=%v %+v", count, trunc, out)
|
||||
}
|
||||
// the cap holds on THIS side even if a helper lied about truncation
|
||||
out, count, trunc = boundTree([]*computerNode{win}, 3)
|
||||
if count != 3 || !trunc {
|
||||
t.Fatalf("capped: count=%d trunc=%v", count, trunc)
|
||||
}
|
||||
// long strings are cut so a page-long label cannot fill the reply
|
||||
long := &computerNode{Role: "text", Name: strings.Repeat("n", 500), ID: strings.Repeat("i", 500), Value: strings.Repeat("v", 500)}
|
||||
out, _, _ = boundTree([]*computerNode{long}, 10)
|
||||
rc := utf8.RuneCountInString
|
||||
if rc(out[0].Name) > treeMaxText+1 || rc(out[0].ID) > treeMaxText+1 || rc(out[0].Value) > treeMaxText+1 {
|
||||
t.Fatalf("text caps: %d %d %d", rc(out[0].Name), rc(out[0].ID), rc(out[0].Value))
|
||||
}
|
||||
// the cut lands on a character boundary, never inside a multibyte one
|
||||
if got := cutRunes(strings.Repeat("é", 200), 5); got != "ééééé…" || !utf8.ValidString(got) {
|
||||
t.Fatalf("rune-safe cut: %q", got)
|
||||
}
|
||||
// a cycle-shaped depth bomb is cut at treeMaxDepth
|
||||
deep := &computerNode{Role: "group"}
|
||||
cur := deep
|
||||
for i := 0; i < 60; i++ {
|
||||
next := &computerNode{Role: "group"}
|
||||
cur.Children = []*computerNode{next}
|
||||
cur = next
|
||||
}
|
||||
_, count, trunc = boundTree([]*computerNode{deep}, 1000)
|
||||
if !trunc || count != treeMaxDepth+1 {
|
||||
t.Fatalf("depth cap: count=%d trunc=%v", count, trunc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTreeReportShape(t *testing.T) {
|
||||
// a bad argument is reported with no pid, which the handler turns into exit 2
|
||||
rep := computerTreeReport([]string{"nope"})
|
||||
if rep.Supported || rep.PID != 0 || rep.Reason == "" {
|
||||
t.Fatalf("bad arg: %+v", rep)
|
||||
}
|
||||
b, _ := json.Marshal(treeReport{Platform: "linux", Scan: computerTreeKind, Supported: true, PID: 7,
|
||||
Windows: []*computerNode{{Role: "window", Name: "x", Children: []*computerNode{{Role: "button", Name: "OK", Focused: true}}}},
|
||||
NodeCount: 2})
|
||||
s := string(b)
|
||||
for _, want := range []string{`"supported":true`, `"node_count":2`, `"focused":true`, `"role":"button"`} {
|
||||
if !strings.Contains(s, want) {
|
||||
t.Fatalf("report missing %s: %s", want, s)
|
||||
}
|
||||
}
|
||||
if strings.Contains(s, `"truncated"`) || strings.Contains(s, `"offscreen"`) {
|
||||
t.Fatalf("false flags are omitted: %s", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHelperTreeDecodes(t *testing.T) {
|
||||
raw := `{"app":"Fusion360","truncated":false,"windows":[{"role":"window","name":"flowerpot v3","x":10,"y":20,"w":800,"h":600,"enabled":true,
|
||||
"children":[{"role":"button","name":"Revolve","id":"cmdRevolve","enabled":true},{"role":"edit","name":"Angle","value":"360"}]}]}`
|
||||
var ht helperTree
|
||||
if err := json.Unmarshal([]byte(raw), &ht); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
out, count, _ := boundTree(ht.Windows, 100)
|
||||
if ht.App != "Fusion360" || count != 3 || out[0].Children[1].Value != "360" || out[0].Children[0].ID != "cmdRevolve" {
|
||||
t.Fatalf("%+v", out[0])
|
||||
}
|
||||
if out[0].Enabled == nil || !*out[0].Enabled {
|
||||
t.Fatal("enabled travels as a tri-state")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTreeOpInVocabulary(t *testing.T) {
|
||||
found := false
|
||||
for _, v := range mgmtOpVocabulary() {
|
||||
if v == "computer:tree" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found || handlerOps["computer:tree"].root != rootNone {
|
||||
t.Fatal("computer:tree must be a rootNone handler in the vocabulary")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user