Skip to content

Commit 6a33928

Browse files
authored
Use go-shellquote to split rules into arguments (#115)
The rule implementation naively split rules using strings.Fields, but this did not handle arguments that were quoted in cases where the arguments themselves contain spaces. Now go-shellquote is used to split the arguments before parsing. Fixes #114
1 parent 192499f commit 6a33928

File tree

7 files changed

+102
-19
lines changed

7 files changed

+102
-19
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
1212
- Fix change in behaviour that causes error when unmarshaling `AuditStatus` with a short buffer. [#110](https://github.com/elastic/go-libaudit/pull/110)
1313
- Reduce heap allocations when parsing and enriching auditd events. [#111](https://github.com/elastic/go-libaudit/pull/111)
1414
- Relax short buffer requirement further to allow for kernels that do not support the backlog wait feature. [#113](https://github.com/elastic/go-libaudit/pull/113)
15+
- Fix parsing of audit rules where arguments are quoted (like file paths containing spaces). [#115](https://github.com/elastic/go-libaudit/pull/115)
1516
- Fix minimum `AuditStatus` length so that library can support kernels from 2.6.32. [#119](https://github.com/elastic/go-libaudit/pull/119)
1617

1718
### Removed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/elastic/go-libaudit/v2
33
go 1.16
44

55
require (
6+
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
67
github.com/stretchr/testify v1.7.0
78
go.uber.org/multierr v1.7.0
89
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
22
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
33
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4+
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
5+
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
46
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
57
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
68
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

rule/flags/flags.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,22 @@ import (
2828
"regexp"
2929
"strings"
3030

31+
"github.com/kballard/go-shellquote"
32+
3133
"github.com/elastic/go-libaudit/v2/rule"
3234
)
3335

3436
// Parse parses an audit rule specified using flags. It can parse delete all
3537
// commands (-D), file watch rules (-w), and syscall rules (-a or -A).
36-
func Parse(args string) (rule.Rule, error) {
38+
func Parse(s string) (rule.Rule, error) {
39+
args, err := shellquote.Split(s)
40+
if err != nil {
41+
return nil, err
42+
}
43+
3744
// Parse the flags.
3845
ruleFlagSet := newRuleFlagSet()
39-
if err := ruleFlagSet.flagSet.Parse(strings.Fields(args)); err != nil {
46+
if err := ruleFlagSet.flagSet.Parse(args); err != nil {
4047
return nil, err
4148
}
4249
if err := ruleFlagSet.validate(); err != nil {

rule/gen_testdata_test.go

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ import (
3030
"os"
3131
"os/exec"
3232
"path/filepath"
33-
"regexp"
3433
"strings"
3534
"testing"
3635

36+
"github.com/kballard/go-shellquote"
3737
"gopkg.in/yaml.v2"
3838

3939
"github.com/elastic/go-libaudit/v2"
@@ -145,9 +145,8 @@ func auditctlExec(t testing.TB, command string) (string, []byte) {
145145
defer deleteRules(t, client)
146146

147147
// Replace paths with ones in a temp dir for test environment consistency.
148-
command = makePaths(t, tempDir, command)
148+
args := makePaths(t, tempDir, command)
149149

150-
args := strings.Fields(command)
151150
_, err = exec.Command("auditctl", args...).Output()
152151
if err != nil {
153152
var exitErr *exec.ExitError
@@ -166,39 +165,60 @@ func auditctlExec(t testing.TB, command string) (string, []byte) {
166165
t.Fatalf("expected 1 rule but got %d", len(rules))
167166
}
168167

169-
return command, rules[0]
168+
return shellquote.Join(args...), rules[0]
170169
}
171170

172171
// makePaths extracts any paths from the command, creates the path as either
173172
// a regular file or directory, then updates the paths to point to the one
174-
// created for the test. It returns the updated command that contains the test
175-
// paths.
176-
func makePaths(t testing.TB, tmpDir, rule string) string {
177-
re := regexp.MustCompile(`(-w |dir=|path=)/(\S+)`)
178-
matches := re.FindAllStringSubmatch(rule, -1)
179-
for _, match := range matches {
180-
path := match[2]
181-
realPath := filepath.Join(tmpDir, path)
173+
// created for the test. It returns the updated command arguments which contain
174+
// the test paths.
175+
func makePaths(t testing.TB, tmpDir, rule string) []string {
176+
args, err := shellquote.Split(rule)
177+
if err != nil {
178+
t.Fatal(err)
179+
}
180+
181+
for i, arg := range args {
182+
var prefix, path string
183+
switch {
184+
case arg == "-w":
185+
path = args[i+1]
186+
case strings.HasPrefix(arg, "dir="):
187+
prefix = "dir="
188+
path = strings.TrimPrefix(arg, prefix)
189+
case strings.HasPrefix(arg, "path="):
190+
prefix = "path="
191+
path = strings.TrimPrefix(arg, prefix)
192+
default:
193+
continue
194+
}
195+
196+
testPath := filepath.Join(tmpDir, path)
182197

183198
if strings.HasSuffix(path, "/") {
184199
// Treat paths with trailing slashes as a directory to monitor.
185-
if err := os.MkdirAll(realPath, 0o700); err != nil {
200+
if err := os.MkdirAll(testPath, 0o700); err != nil {
186201
t.Fatal(err)
187202
}
188203
} else {
189204
// Touch a file.
190-
dir := filepath.Dir(realPath)
205+
dir := filepath.Dir(testPath)
191206
if err := os.MkdirAll(dir, 0o700); err != nil {
192207
t.Fatal(err)
193208
}
194-
if err := ioutil.WriteFile(realPath, nil, 0o600); err != nil {
209+
if err := ioutil.WriteFile(testPath, nil, 0o600); err != nil {
195210
t.Fatal(err)
196211
}
197212
}
213+
214+
if prefix == "" {
215+
args[i+1] = testPath
216+
} else {
217+
args[i] = prefix + testPath
218+
}
198219
}
199220

200-
substitution := "$1" + filepath.Join(tmpDir, "$2")
201-
return re.ReplaceAllString(rule, substitution)
221+
return args
202222
}
203223

204224
func deleteRules(t testing.TB, client *libaudit.AuditClient) {
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-w "/folder with space" -p wa -k path-double-quoted
2+
3+
-w '/folder with space' -p wa -k path-single-quoted
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Linux 7fa0ce96f3a7 5.10.76-linuxkit #1 SMP PREEMPT Mon Nov 8 11:22:26 UTC 2021 aarch64 GNU/Linux
2+
# auditctl version 3.0
3+
rules:
4+
- flags: -w '/tmp/audit-test/folder with space' -p wa -k path-double-quoted
5+
bytes: !!binary |
6+
BAAAAAIAAAADAAAA//////////////////////////////////////////////////////
7+
//////////////////////////////////////////////////////////////////////
8+
//////////////////////////////////////////////////////////////////////
9+
//////////////////////////////////////////////////////////////////////
10+
//////////////////////////////////////////////////////////////////////
11+
////8AAGkAAABqAAAA0gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
12+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
13+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
14+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
15+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAh
16+
AAAACgAAABIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
17+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
18+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
19+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
20+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAE
21+
AAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
22+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
23+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
24+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
25+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMAAAAvdG1wL2F1ZGl0
26+
LXRlc3QvZm9sZGVyIHdpdGggc3BhY2VwYXRoLWRvdWJsZS1xdW90ZWQA
27+
- flags: -w '/tmp/audit-test/folder with space' -p wa -k path-single-quoted
28+
bytes: !!binary |
29+
BAAAAAIAAAADAAAA//////////////////////////////////////////////////////
30+
//////////////////////////////////////////////////////////////////////
31+
//////////////////////////////////////////////////////////////////////
32+
//////////////////////////////////////////////////////////////////////
33+
//////////////////////////////////////////////////////////////////////
34+
////8AAGkAAABqAAAA0gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
35+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
36+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
37+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
38+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAh
39+
AAAACgAAABIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
40+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
41+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
42+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
43+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAE
44+
AAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
45+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
46+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
47+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
48+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMAAAAvdG1wL2F1ZGl0
49+
LXRlc3QvZm9sZGVyIHdpdGggc3BhY2VwYXRoLXNpbmdsZS1xdW90ZWQA

0 commit comments

Comments
 (0)