1package captureallow
2
3import (
4 "errors"
5 "strings"
6 "testing"
7)
8
9func TestParse(t *testing.T) {
10 tests := []struct {
11 name string
12 in string
13 mode Mode
14 allowed []string
15 refused []string
16 errLine int
17 wantFail bool
18 }{
19 {name: "paths comments blanks", in: "# header\n\n/usr/bin/grim\n /usr/lib/xdg-desktop-portal-wlr \n\t# indented comment\n/opt/a#b\n",
20 mode: ModeList, allowed: []string{"/usr/bin/grim", "/usr/lib/xdg-desktop-portal-wlr", "/opt/a#b"}, refused: []string{"/usr/bin/grim2", "/usr/bin", "grim", ""}},
21 {name: "cleaned", in: "/usr//bin/../bin/grim\n", mode: ModeList, allowed: []string{"/usr/bin/grim"}},
22 {name: "no trailing newline", in: "/a/b", mode: ModeList, allowed: []string{"/a/b"}},
23 {name: "empty file allows nothing", in: "", mode: ModeList, refused: []string{"/usr/bin/grim"}},
24 {name: "star disables", in: "# all\n*\n", mode: ModeDisabled, allowed: []string{"/anything"}},
25 {name: "star with paths still disables", in: "/usr/bin/grim\n*\n", mode: ModeDisabled, allowed: []string{"/x"}},
26 {name: "relative", in: "/ok\ngrim\n", errLine: 2, wantFail: true},
27 {name: "dot relative", in: "./grim\n", errLine: 1, wantFail: true},
28 {name: "glob is not star", in: "/usr/bin/*\n", mode: ModeList, allowed: []string{"/usr/bin/*"}, refused: []string{"/usr/bin/grim"}},
29 {name: "star inside garbage", in: "# c\n\n* \nfoo bar\n", errLine: 4, wantFail: true},
30 {name: "directory", in: "/usr/bin/\n", errLine: 1, wantFail: true},
31 {name: "nul", in: "/usr/bin/g\x00rim\n", errLine: 1, wantFail: true},
32 {name: "windows newline", in: "/a/b\r\n", mode: ModeList, allowed: []string{"/a/b"}},
33 }
34 for _, tc := range tests {
35 t.Run(tc.name, func(t *testing.T) {
36 p, err := Parse(strings.NewReader(tc.in))
37 if tc.wantFail {
38 var pe *ParseError
39 if !errors.As(err, &pe) || pe.Line != tc.errLine || p != nil {
40 t.Fatalf("Parse = %v, %v; want ParseError on line %d", p, err, tc.errLine)
41 }
42 return
43 }
44 if err != nil {
45 t.Fatal(err)
46 }
47 if p.Mode() != tc.mode {
48 t.Fatalf("mode %v, want %v", p.Mode(), tc.mode)
49 }
50 for _, e := range tc.allowed {
51 if !p.Allows(e) {
52 t.Errorf("%q refused", e)
53 }
54 }
55 for _, e := range tc.refused {
56 if p.Allows(e) {
57 t.Errorf("%q allowed", e)
58 }
59 }
60 })
61 }
62}
63
64func TestParseTooLarge(t *testing.T) {
65 _, err := Parse(strings.NewReader(strings.Repeat("# filler line\n", maxSize/10)))
66 var pe *ParseError
67 if !errors.As(err, &pe) {
68 t.Fatalf("err = %v, want ParseError", err)
69 }
70 _, err = Parse(strings.NewReader("/" + strings.Repeat("a", maxSize+10)))
71 if !errors.As(err, &pe) {
72 t.Fatalf("long line err = %v, want ParseError", err)
73 }
74}
75
76func TestBuiltin(t *testing.T) {
77 p := Builtin()
78 want := []string{"/usr/bin/grim", "/usr/bin/nefercap", "/usr/lib/xdg-desktop-portal-wlr", "/usr/libexec/xdg-desktop-portal-wlr"}
79 if p.Mode() != ModeList || p.Len() != len(want) {
80 t.Fatalf("mode %v, len %d", p.Mode(), p.Len())
81 }
82 for _, e := range want {
83 if !p.Allows(e) {
84 t.Fatalf("%s refused", e)
85 }
86 }
87 if p.Allows("/usr/bin/other") {
88 t.Fatal("unlisted allowed")
89 }
90}
91
92func TestPolicyModes(t *testing.T) {
93 if !Disabled().Allows("/x") || Closed().Allows("/x") || (&Policy{}).Allows("/x") {
94 t.Fatal("Disabled allows, Closed and the zero value refuse")
95 }
96 p, _ := Parse(strings.NewReader("/a\n"))
97 if p.Allows("a") {
98 t.Fatal("relative exe allowed")
99 }
100}