Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package config

import (
"fmt"
"strings"

"github.com/coder/serpent"
"github.com/spf13/pflag"
)

// JailType represents the type of jail to use for network isolation
Expand All @@ -25,10 +27,38 @@ func NewJailTypeFromString(str string) (JailType, error) {
}
}

// AllowStringsArray is a custom type that implements pflag.Value to support
// repeatable --allow flags without splitting on commas. This allows comma-separated
// paths within a single allow rule (e.g., "path=/todos/1,/todos/2").
type AllowStringsArray []string

var _ pflag.Value = (*AllowStringsArray)(nil)

// Set implements pflag.Value. It appends the value to the slice without splitting on commas.
func (a *AllowStringsArray) Set(value string) error {
*a = append(*a, value)
return nil
}

// String implements pflag.Value.
func (a AllowStringsArray) String() string {
return strings.Join(a, ",")
}

// Type implements pflag.Value.
func (a AllowStringsArray) Type() string {
return "string"
}

// Value returns the underlying slice of strings.
func (a AllowStringsArray) Value() []string {
return []string(a)
}

type CliConfig struct {
Config serpent.YAMLConfigPath `yaml:"-"`
AllowListStrings serpent.StringArray `yaml:"allowlist"` // From config file
AllowStrings serpent.StringArray `yaml:"-"` // From CLI flags only
AllowStrings AllowStringsArray `yaml:"-"` // From CLI flags only
LogLevel serpent.String `yaml:"log_level"`
LogDir serpent.String `yaml:"log_dir"`
ProxyPort serpent.Int64 `yaml:"proxy_port"`
Expand Down
38 changes: 38 additions & 0 deletions e2e_tests/nsjail/http_path_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,41 @@ func TestHTTPPathWildCardSymbol(t *testing.T) {
nt.ExpectDeny("https://dev.coder.com/api")
})
}

func TestHTTPMultiPath(t *testing.T) {
// Create and configure nsjail test
nt := NewNSJailTest(t,
WithNSJailAllowedRule("domain=jsonplaceholder.typicode.com path=/todos/1,/todos/2"),
WithNSJailLogLevel("debug"),
).
Build().
Start()

// Ensure cleanup
defer nt.Stop()

// Test allowed HTTP request
t.Run("HTTPRequestThroughBoundary", func(t *testing.T) {
expectedResponse := `{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}`
nt.ExpectAllowed("http://jsonplaceholder.typicode.com/todos/1", expectedResponse)
})

t.Run("HTTPRequestThroughBoundary", func(t *testing.T) {
expectedResponse := `{
"userId": 1,
"id": 2,
"title": "quis ut nam facilis et officia qui",
"completed": false
}`
nt.ExpectAllowed("http://jsonplaceholder.typicode.com/todos/2", expectedResponse)
})

t.Run("HTTPRequestThroughBoundary", func(t *testing.T) {
nt.ExpectDeny("http://jsonplaceholder.typicode.com/todos/3")
})
}
10 changes: 10 additions & 0 deletions rulesengine/rules_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,16 @@ func TestParseAllowRule(t *testing.T) {
expectedRule: Rule{},
expectError: true,
},
{
name: "multipath",
input: "domain=jsonplaceholder.typicode.com path=/api/v1,/api/v2",
expectedRule: Rule{
Raw: "domain=jsonplaceholder.typicode.com path=/api/v1,/api/v2",
PathPattern: [][]string{{"api", "v1"}, {"api", "v2"}},
HostPattern: []string{"jsonplaceholder", "typicode", "com"},
},
expectError: false,
},
}

for _, tt := range tests {
Expand Down
Loading