Skip to content

Commit

Permalink
Merging of configs is now ready
Browse files Browse the repository at this point in the history
  • Loading branch information
simbados committed Feb 9, 2024
1 parent 5c5363c commit 2e5c55c
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 58 deletions.
25 changes: 16 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,21 @@
- [x] Support multiple shells (fish, zsh, bash, etc)
- [x] ~~Flag for creating shim executable for binary (aliasing will be enough for now)~~
- [x] Fixing node binary call in npm.json (currently for nvm)
- [ ] More testing for critical parts of the tool
- [x] Sophisticated parsing of config json keys with glob expansion
- [x] Add support of adding new config files with sb
- [x] Helper command to show all configs file for a binary and their content and which will be applied
- [ ] Add support for removing config files with sb
- [x] Print command that is run when passing args to sandbox-exec
- [x] Always apply __root-config__ of root config for binary and show in sb -s command
- [x] Merge local and root config
- [ ] Validate json config files, if anything can not be parsed return error (e.g. no array provided)
- [ ] Possibility to add overall config json file to apply to all commands
- [x] No array/bool provided
- [ ] Config keys duplicated (can lead to bug that only first config is applied, disallow double config keys)
- [ ] Add support for removing config files with sb
- [ ] Possibility to add overall config json file to apply to all commands (discuss if good idea?)
- [ ] Add TLDR; for README
- [ ] Vigilant mode, ask at the end to proceed with command and config
- [ ] Print command that is run when passing args to sandbox-exec
- [ ] Option for only applying root or local config ```-c local -c root```
- [ ] More testing for critical parts of the tool

## Installation
**Building from Source**
Expand Down Expand Up @@ -96,9 +101,11 @@ E.g.
**The config files need arrays for read/write/read-write/process, the cli config expects a string
which is comma seperated!**

**Attention**: If you have multiple configs for a binary, sb will take the arguments with the highest priority
Order of priority: cli arguments > local config > global config
E.g. You deny net-inbound on the global config, but your local config allows it, then it will be allowed
**Attention**: If you have multiple configs for a binary, sb will merge these configs, so it will append
any strings from read/write/read-write/process and net out and net in will only be allowed if
all configs allow it. If one config does not allow it, then it will be forbidden.
E.g. root config does allow ```"net-out": true```, but your local config has ```"net-out": false```,
then net-out will be forbidden.

## Configuration flags

Expand All @@ -111,10 +118,10 @@ Run sb --help for all available flags
Takes two additional arguments first must be either local/root and second name of binary you would like to edit
E.g. ```sb -e root npm``` (edits the npm.json file in the root directory)
E.g. ```sb -e local npm``` (edits the npm.json file in the local directory)
Does currently not support creating new config files, is on the TODO list :)
- --show (-s): Displays which config will be applied for binary, use as ```sb -s npm```


## FAQS
1. But sandbox-exec is deprecated and usage is discouraged
A: Yes that is true, but most browsers rely on sandbox-exec and v1 sandbox profiles still work.
There is currently no other way to sandbox scripts or cli tools.
There is currently no other way to sandbox scripts or cli tools.
1 change: 0 additions & 1 deletion cmd/sb/sb.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ func checkCliOptions(context *types.Context, commands []string) {
} else if currentOption == "--edit" || currentOption == "-e" {
util.EditFile(commands, context.Paths)
} else if currentOption == "--show" || currentOption == "-s" {
fmt.Println(commands)
if len(commands) != 1 {
log.LogErr(`
You need to specify which config files you want to see
Expand Down
4 changes: 2 additions & 2 deletions internal/sandbox/sandbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ func read(config *types.SbConfig, profile string) string {
}

func netOut(config *types.SbConfig, profile string) string {
if config.NetworkOutbound {
if config.NetworkOutbound != nil && config.NetworkOutbound.Value {
profile += `
; allow-net-outbound: enabled
(allow network-inbound
Expand Down Expand Up @@ -183,7 +183,7 @@ func addFallBackForCert(config *types.SbConfig, profile string) string {
}

func netIn(config *types.SbConfig, profile string) string {
if config.NetworkInbound {
if config.NetworkInbound != nil && config.NetworkInbound.Value {
profile += `
; allow-net-inbound: enabled
(allow network-bind network-inbound
Expand Down
2 changes: 2 additions & 0 deletions internal/types/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package types
const ConfigRepo = "/.sb-config"
const LocalConfigPath = "/.sb-config"

const RootConfigKey = "__root-config__"

const DEV_MODE = "DEV_MODE"

var AllEnvs = []string{DEV_MODE}
16 changes: 10 additions & 6 deletions internal/types/sbConfig.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package types

type BoolOrNil struct {
Value bool
}

type SbConfig struct {
Read []string `json:"read"`
Write []string `json:"write"`
ReadWrite []string `json:"read-write"`
Process []string `json:"process"`
NetworkOutbound bool `json:"net-out"`
NetworkInbound bool `json:"net-in"`
Read []string `json:"read"`
Write []string `json:"write"`
ReadWrite []string `json:"read-write"`
Process []string `json:"process"`
NetworkOutbound *BoolOrNil `json:"net-out"`
NetworkInbound *BoolOrNil `json:"net-in"`
}

var AllowedConfigKeys = map[string]string{
Expand Down
1 change: 0 additions & 1 deletion internal/util/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
)

func EditFile(commands []string, paths types.Paths) {
fmt.Println(commands)
if len(commands) != 2 {
showError()
}
Expand Down
17 changes: 8 additions & 9 deletions internal/util/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@ import (

func PrintHelp() {
fmt.Printf(
"Welcome to the sandbox cli tool - short sb \n" +
"The cli options are the following: \n" +
"--debug -d: Debug the different steps in the application\n" +
"--print -p: Print the sandbox profile and infos about the sandbox profile (e.g. which config files were loaded) \n" +
"--dry-run -dr Do not execute the sandbox with the wanted binary will set debug and print to true so that you have all the information what sb would do\n" +
"--create-exe -ce Creates executable shim for binary, so that you can use the sandboxed binary within other programs which would natively use the normal binary\n" +
"--help -h Print this help section\n" +
"--version -v Show which version of sb is installed\n" +
"--show -s Show location of all config files for this binary and the content of the file that would be applied\n")
`Welcome to the sandbox cli tool - short sb
The cli options are the following:
--debug -d: Debug the different steps in the application
--print -p: Print the sandbox profile and infos about the sandbox profile (e.g. which config files were loaded)
--dry-run -dr Do not execute the sandbox with the wanted binary will set debug and print to true so that you have all the information what sb would do
--help -h Print this help section
--version -v Show which version of sb is installed
--show -s Show location of all config files for this binary and the content of the file that would be applied`)
os.Exit(0)
}
4 changes: 2 additions & 2 deletions internal/util/parseCliOptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,14 @@ func addToConfig(config *types.SbConfig, key string, value string) *types.SbConf
if !exists {
log.LogErr("You must provide true or false value for cli config: ", value)
}
config.NetworkInbound = boolVal
config.NetworkInbound = &types.BoolOrNil{Value: boolVal}
break
case "--net-out":
boolVal, exists := parseStringBoolean(value)
if !exists {
log.LogErr("You must provide true or false value for cli config: ", value)
}
config.NetworkOutbound = boolVal
config.NetworkOutbound = &types.BoolOrNil{Value: boolVal}
break
}
return config
Expand Down
69 changes: 41 additions & 28 deletions internal/util/parseConfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,53 +50,54 @@ func LocalConfigPath(paths *types.Paths, binaryName string) (string, bool) {

// ConfigFileParsing Here we only parse the config files, cli configs have already been parsed
func ConfigFileParsing(context *types.Context) *types.SbConfig {
globalConfig := &types.SbConfig{}
sbConfig, localConfigExists := extractLocalConfig(context)
if localConfigExists {
return sbConfig
}
config, rootConfigExists := extractRootConfig(context, globalConfig)
if rootConfigExists {
return config
}
return nil
localConfig := extractLocalConfig(context)
rootConfig := extractRootConfig(context)
return mergeConfig(localConfig, rootConfig)
}

func extractLocalConfig(context *types.Context) (*types.SbConfig, bool) {
func extractLocalConfig(context *types.Context) *types.SbConfig {
localConfigPath, localConfigExists := LocalConfigPath(&context.Paths, context.Config.BinaryName)
if localConfigExists {
context.Paths.LocalConfigPath = localConfigPath
localConfig := parseJsonConfig(&context.Paths, localConfigPath, context.Config.Commands)
log.LogDebug("Using local config file at path ", localConfigPath)
return localConfig, true
return localConfig
} else {
log.LogDebug("No local config file found at: ", localConfigPath)
log.LogDebug("Proceeding without local config")
}
return nil, false
return nil
}

func extractRootConfig(context *types.Context, globalConfig *types.SbConfig) (*types.SbConfig, bool) {
func extractRootConfig(context *types.Context) *types.SbConfig {
binaryGlobalConfigPath, exists := doesRootConfigExists(context)
if exists {
globalConfig := parseJsonConfig(&context.Paths, binaryGlobalConfigPath, context.Config.Commands)
return globalConfig
}
return nil
}

func doesRootConfigExists(context *types.Context) (string, bool) {
if doesRootConfigDirExist(context.Paths.RootConfigPath) {
binaryGlobalConfigPath := context.Paths.RootConfigPath + "/" + context.Config.BinaryName + ".json"
binaryPathExists, _ := DoesPathExist(binaryGlobalConfigPath)
if !binaryPathExists {
log.LogWarn("No config for binary found. You might want to create a config file at: ", context.Paths.RootConfigPath)
} else {
globalConfig = parseJsonConfig(&context.Paths, binaryGlobalConfigPath, context.Config.Commands)
log.LogDebug("Using global config file")
return globalConfig, true
return binaryGlobalConfigPath, true
}
} else {
log.LogDebug("No root config file found at: ", context.Paths.RootConfigPath)
log.LogDebug("Proceeding without global config")
}
return nil, false
return "", false
}

func parseJsonConfig(paths *types.Paths, path string, commands []string) *types.SbConfig {
mapping := buildCommandMap(commands)
mapping["__root-config__"] = true
mapping[types.RootConfigKey] = true
var configs []*types.SbConfig
configJson := ParseJson(path)
for key, val := range configJson {
Expand Down Expand Up @@ -162,35 +163,35 @@ func parseConfigIntoStruct(paths *types.Paths, binaryConfig map[string]interface
netOut, netOutExists := binaryConfig["net-out"]
netIn, netInExists := binaryConfig["net-in"]
if readExists {
sbConfig.Read = parseIfExists(paths, read, sbConfig, path, "read")
sbConfig.Read = parseIfExists(paths, read, path, "read")
}
if writeExists {
sbConfig.Write = parseIfExists(paths, write, sbConfig, path, "write")
sbConfig.Write = parseIfExists(paths, write, path, "write")
}
if readWriteExists {
sbConfig.ReadWrite = parseIfExists(paths, readWrite, sbConfig, path, "read-write")
sbConfig.ReadWrite = parseIfExists(paths, readWrite, path, "read-write")
}
if processExists {
sbConfig.Process = parseIfExists(paths, process, sbConfig, path, "process")
sbConfig.Process = parseIfExists(paths, process, path, "process")
}
if netOutExists {
if value, exists := netOut.(bool); exists {
sbConfig.NetworkOutbound = value
sbConfig.NetworkOutbound = &types.BoolOrNil{Value: value}
} else {
log.LogErr(fmt.Sprintf("Your net-out config at path %v, is not a boolean value", path))
}
}
if netInExists {
if value, exists := netIn.(bool); exists {
sbConfig.NetworkInbound = value
sbConfig.NetworkInbound = &types.BoolOrNil{Value: value}
} else {
log.LogErr(fmt.Sprintf("Your net-in config at path %v, is not a boolean value", path))
}
}
return sbConfig
}

func parseIfExists(paths *types.Paths, jsonKey interface{}, sbConfig *types.SbConfig, path string, configName string) []string {
func parseIfExists(paths *types.Paths, jsonKey interface{}, path string, configName string) []string {
if arr, exists := jsonKey.([]interface{}); exists {
return convertJsonArrayToStringArray(paths, arr)
} else {
Expand Down Expand Up @@ -223,14 +224,26 @@ func mergeConfig(configToMerge ...*types.SbConfig) *types.SbConfig {
newConfig.Read = appendUniqueStrings(newConfig.Read, config.Read...)
newConfig.Process = appendUniqueStrings(newConfig.Process, config.Process...)
newConfig.ReadWrite = appendUniqueStrings(newConfig.ReadWrite, config.ReadWrite...)
// Network in/out-bound can not really be merged if it is prohibited once it should be enforced
newConfig.NetworkOutbound = newConfig.NetworkOutbound || config.NetworkOutbound
newConfig.NetworkInbound = newConfig.NetworkInbound || config.NetworkInbound
// Network in/out-bound can not really be merged if it is allowed once it should be allowed
newConfig.NetworkOutbound = mergeBoolOrNil(newConfig.NetworkOutbound, config.NetworkOutbound)
newConfig.NetworkInbound = mergeBoolOrNil(newConfig.NetworkInbound, config.NetworkInbound)
}
}
return newConfig
}

func mergeBoolOrNil(a *types.BoolOrNil, b *types.BoolOrNil) *types.BoolOrNil {
if a == nil && b == nil {
return nil
} else if a == nil {
return b
} else if b == nil {
return a
} else {
return &types.BoolOrNil{Value: a.Value && b.Value}
}
}

func appendUniqueStrings(array []string, stringsToMerge ...string) []string {
unique := make(map[string]struct{})
for _, s := range array {
Expand Down

0 comments on commit 2e5c55c

Please sign in to comment.