-
Notifications
You must be signed in to change notification settings - Fork 103
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add socketfilterfw parser table (#1812)
- Loading branch information
1 parent
8980c6c
commit 551d945
Showing
9 changed files
with
280 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
package socketfilterfw | ||
|
||
import ( | ||
"bufio" | ||
"io" | ||
"regexp" | ||
"strings" | ||
) | ||
|
||
var appRegex = regexp.MustCompile("(.*)(?:\\s\\(state:\\s)([0-9]+)") | ||
var lineRegex = regexp.MustCompile("(state|block|built-in|downloaded|stealth|log mode|log option)(?:.*\\s)([0-9a-z]+)") | ||
|
||
// socketfilterfw returns lines for each `get` argument supplied. | ||
// The output data is in the same order as the supplied arguments. | ||
// | ||
// This supports parsing the list of apps and their allow state, or | ||
// each line describes a part of the feature and what state it's in. | ||
// | ||
// These are not very well-formed, so I'm doing some regex magic to | ||
// know which option the current line is, and then sanitize the state. | ||
func socketfilterfwParse(reader io.Reader) (any, error) { | ||
results := make([]map[string]string, 0) | ||
row := make(map[string]string) | ||
parseAppData := false | ||
|
||
scanner := bufio.NewScanner(reader) | ||
for scanner.Scan() { | ||
line := scanner.Text() | ||
|
||
// When parsing the app list, the first line of output is a total | ||
// count of apps. We can break on this line to start parsing apps. | ||
if strings.Contains(line, "Total number of apps") { | ||
parseAppData = true | ||
continue | ||
} | ||
|
||
if parseAppData { | ||
appRow := parseAppList(line) | ||
if appRow != nil { | ||
results = append(results, appRow) | ||
} | ||
|
||
continue | ||
} | ||
|
||
k, v := parseLine(line) | ||
if k != "" { | ||
row[k] = v | ||
} | ||
} | ||
|
||
if len(row) > 0 { | ||
results = append(results, row) | ||
} | ||
|
||
return results, nil | ||
} | ||
|
||
// parseAppList parses the current line and returns the app name and | ||
// state matches as a row of data. | ||
func parseAppList(line string) map[string]string { | ||
matches := appRegex.FindStringSubmatch(line) | ||
if len(matches) != 3 { | ||
return nil | ||
} | ||
|
||
return map[string]string{ | ||
"name": matches[1], | ||
"allow_incoming_connections": sanitizeState(matches[2]), | ||
} | ||
} | ||
|
||
// parseLine parse the current line and returns a feature key with the | ||
// respective state/mode of said feature. We want all features to be a | ||
// part of the same row of data, so we do not return this pair as a row. | ||
func parseLine(line string) (string, string) { | ||
matches := lineRegex.FindStringSubmatch(strings.ToLower(line)) | ||
if len(matches) != 3 { | ||
return "", "" | ||
} | ||
|
||
var key string | ||
switch matches[1] { | ||
case "state": | ||
key = "global_state_enabled" | ||
case "block": | ||
key = "block_all_enabled" | ||
case "built-in": | ||
key = "allow_built-in_signed_enabled" | ||
case "downloaded": | ||
key = "allow_downloaded_signed_enabled" | ||
case "stealth": | ||
key = "stealth_enabled" | ||
case "log mode": | ||
key = "logging_enabled" | ||
case "log option": | ||
key = "logging_option" | ||
default: | ||
return "", "" | ||
} | ||
|
||
return key, sanitizeState(matches[2]) | ||
} | ||
|
||
// sanitizeState takes in a state like string and returns | ||
// the correct boolean to create a consistent state value. | ||
func sanitizeState(state string) string { | ||
switch state { | ||
// The app list state for when an app is blocking incoming connections | ||
// is output as `4`, while `1` is the state to allow those connections. | ||
case "0", "off", "disabled", "4": | ||
return "0" | ||
// When the "block all" firewall option is enabled, it doesn't | ||
// include a state like string, which is why we match on | ||
// the string value of "connections" for that mode. | ||
case "1", "on", "enabled", "connections": | ||
return "1" | ||
case "throttled", "brief", "detail": | ||
// The "logging option" value differs from the booleans. | ||
// Can be one of `throttled`, `brief`, or `detail`. | ||
return state | ||
default: | ||
return "" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
package socketfilterfw | ||
|
||
import ( | ||
"bytes" | ||
_ "embed" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
//go:embed test-data/apps.txt | ||
var apps []byte | ||
|
||
//go:embed test-data/data.txt | ||
var data []byte | ||
|
||
//go:embed test-data/empty.txt | ||
var empty []byte | ||
|
||
//go:embed test-data/malformed.txt | ||
var malformed []byte | ||
|
||
func TestParse(t *testing.T) { | ||
t.Parallel() | ||
|
||
var tests = []struct { | ||
name string | ||
input []byte | ||
expected []map[string]string | ||
}{ | ||
{ | ||
name: "apps", | ||
input: apps, | ||
expected: []map[string]string{ | ||
{ | ||
"name": "replicatord", | ||
"allow_incoming_connections": "1", | ||
}, | ||
{ | ||
"name": "Pop Helper.app", | ||
"allow_incoming_connections": "0", | ||
}, | ||
{ | ||
"name": "Google Chrome", | ||
"allow_incoming_connections": "0", | ||
}, | ||
{ | ||
"name": "rtadvd", | ||
"allow_incoming_connections": "1", | ||
}, | ||
{ | ||
"name": "com.docker.backend", | ||
"allow_incoming_connections": "1", | ||
}, | ||
{ | ||
"name": "sshd-keygen-wrapper", | ||
"allow_incoming_connections": "1", | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "data", | ||
input: data, | ||
expected: []map[string]string{ | ||
{ | ||
"global_state_enabled": "1", | ||
"block_all_enabled": "0", | ||
"allow_built-in_signed_enabled": "1", | ||
"allow_downloaded_signed_enabled": "1", | ||
"stealth_enabled": "0", | ||
"logging_enabled": "1", | ||
"logging_option": "throttled", | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "empty input", | ||
input: empty, | ||
}, | ||
{ | ||
name: "malformed", | ||
input: malformed, | ||
expected: []map[string]string{ | ||
{ | ||
"global_state_enabled": "0", | ||
"block_all_enabled": "1", | ||
"allow_built-in_signed_enabled": "0", | ||
"allow_downloaded_signed_enabled": "", | ||
"stealth_enabled": "0", | ||
"logging_enabled": "", | ||
"logging_option": "throttled", | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
tt := tt | ||
t.Run(tt.name, func(t *testing.T) { | ||
t.Parallel() | ||
|
||
p := New() | ||
result, err := p.Parse(bytes.NewReader(tt.input)) | ||
require.NoError(t, err, "unexpected error parsing input") | ||
require.ElementsMatch(t, tt.expected, result) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package socketfilterfw | ||
|
||
import ( | ||
"io" | ||
) | ||
|
||
type parser struct{} | ||
|
||
var Parser = New() | ||
|
||
func New() parser { | ||
return parser{} | ||
} | ||
|
||
func (p parser) Parse(reader io.Reader) (any, error) { | ||
return socketfilterfwParse(reader) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
Total number of apps = 6 | ||
replicatord (state: 1) | ||
Pop Helper.app (state: 4) | ||
Google Chrome (state: 4) | ||
rtadvd (state: 1) | ||
com.docker.backend (state: 1) | ||
sshd-keygen-wrapper (state: 1) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
Firewall is enabled. (State = 1) | ||
Firewall has block all state set to disabled. | ||
Automatically allow built-in signed software ENABLED. | ||
Automatically allow downloaded signed software ENABLED. | ||
Firewall stealth mode is off | ||
Log mode is on | ||
Log Option is throttled |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
Firewall is enabled. (State %#)*Q&^= 0) | ||
Firewall is blocking all non-essential incoming connections.x^CFS. | ||
%#UO | ||
Automatically allow built-in signed software DISABLED. | ||
|
||
Automatically allow downloaded signed software DISABLEDENABLED. | ||
Firewall stealth mode is off | ||
Log mode is onr\r\n\r\n | ||
Log Option is throttled |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters