diff --git a/lex.go b/lex.go index 6d6323b..a5be67b 100644 --- a/lex.go +++ b/lex.go @@ -275,11 +275,18 @@ func lexProtocol(l *lexer) stateFn { // lexSourceAddress consumes a source address. func lexSourceAddress(l *lexer) stateFn { l.ignoreSpaces() + var depth int for { switch l.next() { + case '[': + depth++ + case ']': + depth-- case ' ': - l.emit(itemSourceAddress, true) - return lexSourcePort + if depth <= 0 { + l.emit(itemSourceAddress, true) + return lexSourcePort + } case eof: return l.unexpectedEOF() } @@ -289,11 +296,18 @@ func lexSourceAddress(l *lexer) stateFn { // lexSourcePort consumes a source port. func lexSourcePort(l *lexer) stateFn { l.ignoreSpaces() + var depth int for { switch l.next() { + case '[': + depth++ + case ']': + depth-- case ' ': - l.emit(itemSourcePort, true) - return lexDirection + if depth <= 0 { + l.emit(itemSourcePort, true) + return lexDirection + } case eof: return l.unexpectedEOF() } @@ -314,11 +328,18 @@ func lexDirection(l *lexer) stateFn { // lexDestinationAddress consumes a destination address. func lexDestinationAddress(l *lexer) stateFn { l.ignoreSpaces() + var depth int for { switch l.next() { + case '[': + depth++ + case ']': + depth-- case ' ': - l.emit(itemDestinationAddress, true) - return lexDestinationPort + if depth <= 0 { + l.emit(itemDestinationAddress, true) + return lexDestinationPort + } case eof: return l.unexpectedEOF() } @@ -327,13 +348,20 @@ func lexDestinationAddress(l *lexer) stateFn { // lexDestinationPort consumes a destination port. func lexDestinationPort(l *lexer) stateFn { + var depth int for { switch l.next() { + case '[': + depth++ + case ']': + depth-- case '(': - l.backup() - l.emit(itemDestinationPort, true) - l.skipNext() - return lexOptionKey + if depth <= 0 { + l.backup() + l.emit(itemDestinationPort, true) + l.skipNext() + return lexOptionKey + } case eof: return l.unexpectedEOF() } diff --git a/lex_test.go b/lex_test.go index 1fb646e..e7b0c0b 100644 --- a/lex_test.go +++ b/lex_test.go @@ -145,6 +145,22 @@ func TestLexer(t *testing.T) { {itemEOR, ""}, }, }, + { + name: "spaces in network component", + input: "alert tcp [1.1.1.1, 1.1.1.2] [80, 443] -> [2.2.2.2, 2.2.2.3] [8080, 8443] (key1:value1;)", + items: []item{ + {itemAction, "alert"}, + {itemProtocol, "tcp"}, + {itemSourceAddress, "[1.1.1.1, 1.1.1.2]"}, + {itemSourcePort, "[80, 443]"}, + {itemDirection, "->"}, + {itemDestinationAddress, "[2.2.2.2, 2.2.2.3]"}, + {itemDestinationPort, "[8080, 8443]"}, + {itemOptionKey, "key1"}, + {itemOptionValue, "value1"}, + {itemEOR, ""}, + }, + }, { name: "parentheses in value", input: `alert dns $HOME_NET any -> any any (reference:url,en.wikipedia.org/wiki/Tor_(anonymity_network); sid:42;)`, diff --git a/parser.go b/parser.go index 64b3ba4..78a3ac1 100644 --- a/parser.go +++ b/parser.go @@ -470,9 +470,10 @@ func (r *Rule) protocol(key item) error { // network decodes an IDS rule network (networks and ports) based on its key. func (r *Rule) network(key item) error { + val := strings.ReplaceAll(key.value, " ", "") // Identify if the whole network component is negated. - tmp := strings.TrimPrefix(key.value, "!") - negated := len(tmp) < len(key.value) + tmp := strings.TrimPrefix(val, "!") + negated := len(tmp) < len(val) // This is a hack. We use a regexp to replace the outer `,` with `___` // to give us a discrete string to split on, avoiding the inner `,`. diff --git a/parser_test.go b/parser_test.go index fcd5046..5fabdb5 100644 --- a/parser_test.go +++ b/parser_test.go @@ -556,6 +556,30 @@ func TestParseRule(t *testing.T) { }, }, }, + { + name: "spaces in network object", + rule: `alert tcp any any -> [1.1.1.1, 1.1.1.2] any (msg:"test"; content:"123"; sid:1; rev:1;)`, + want: &Rule{ + Action: "alert", + Protocol: "tcp", + Source: Network{ + Nets: []string{"any"}, + Ports: []string{"any"}, + }, + Destination: Network{ + Nets: []string{"1.1.1.1,1.1.1.2"}, + Ports: []string{"any"}, + }, + SID: 1, + Revision: 1, + Description: "test", + Matchers: []orderedMatcher{ + &Content{ + Pattern: []byte("123"), + }, + }, + }, + }, { name: "simple content", rule: `alert udp $HOME_NET any -> $EXTERNAL_NET any (sid:1337; msg:"foo"; content:"AA"; rev:2;)`, @@ -2167,9 +2191,22 @@ func TestParseRule(t *testing.T) { wantErr: true, }, { - name: "network with space", - rule: `alert tcp $EXTERNAL_NET 443 -> $HOME_NET [123, 234] (msg:"bad network definition"; sid:4321;)`, - wantErr: true, + name: "network with space", + rule: `alert tcp $EXTERNAL_NET 443 -> $HOME_NET [123, 234] (msg:"bad network definition"; sid:4321;)`, + want: &Rule{ + Action: "alert", + Protocol: "tcp", + Source: Network{ + Nets: []string{"$EXTERNAL_NET"}, + Ports: []string{"443"}, + }, + Destination: Network{ + Nets: []string{"$HOME_NET"}, + Ports: []string{"123,234"}, + }, + SID: 4321, + Description: "bad network definition", + }, }, { name: "content with backslash at end",