Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
168 commits
Select commit Hold shift + click to select a range
2fbecf5
feat(api): add PUT /sandboxes/{sandboxID}/network endpoint (stage 1)
levb Mar 8, 2026
af2b869
fix(test): use valid-format sandbox ID in TestUpdateNetworkConfig_Not…
levb Mar 8, 2026
d541327
feat(orchestrator): wire dynamic network egress updates end-to-end
levb Mar 8, 2026
2db7190
refactor(api): simplify UpdateSandboxNetworkConfig return and remove …
levb Mar 8, 2026
4511929
feat(api): support domain filtering in network update path
levb Mar 8, 2026
d4f93b8
test: add pause/resume persistence test for dynamic network updates
levb Mar 8, 2026
027a24a
fix: address revive lint for redundant if-return in update_network
levb Mar 8, 2026
f8c876f
test: add domain allow/remove tests for dynamic network updates
levb Mar 8, 2026
d5c8fbc
refactor: merge 3 RWMutexes into 1, guarantee GetNetwork() non-nil, u…
levb Mar 8, 2026
286c6f1
fix: validate CIDRs before firewall reset, add 409 to OpenAPI spec, u…
levb Mar 8, 2026
ef4708b
chore: auto-commit generated changes
github-actions[bot] Mar 8, 2026
cc5e43d
fix(test): initialize networkEgress in TestIsEgressAllowed
levb Mar 8, 2026
c452eae
Merge branch 'lev-allow-deny-dynamic' of github.com:e2b-dev/infra int…
levb Mar 8, 2026
8bd0c92
fix(api): persist store update only after gRPC node update succeeds
levb Mar 8, 2026
942d1e1
refactor: migrate all callers to atomic ReplaceUserRules, remove one-…
levb Mar 8, 2026
66dcfbb
chore: auto-commit generated changes
github-actions[bot] Mar 8, 2026
b986f2c
fix: bypass firewall_toolkit validation for 0.0.0.0/0 CIDR
levb Mar 8, 2026
1a95c13
lint
levb Mar 8, 2026
79cc831
fix(api): validate denyOut and domain rules in network update endpoint
levb Mar 9, 2026
8bf22fa
test: consolidate network update integration tests into comprehensive…
levb Mar 9, 2026
da52196
PR feedback: extract shared egress validation and use ReportErrorByCode
levb Mar 9, 2026
3e5923b
PR feedback: embed SandboxNetworkEgressConfig in update network request
levb Mar 9, 2026
73d370a
PR feedback: move state check into updateFunc to match KeepAliveFor p…
levb Mar 9, 2026
9417d82
PR feedback: always set firewallCustomRules after UpdateInternet
levb Mar 9, 2026
b853ee4
lint: simplify redundant if-return in updateSandboxNetworkOnNode call
levb Mar 9, 2026
dcbc89d
PR feedback: merge UpdateNetwork into unified Update RPC with transac…
levb Mar 9, 2026
ed87f50
PR feedback: remove unnecessary networkEgress init from ResumeSandbox
levb Mar 10, 2026
0b2193b
Merge branch 'main' of github.com:e2b-dev/infra into lev-allow-deny-d…
levb Mar 10, 2026
9524334
fix: restore networkEgress init in ResumeSandbox
levb Mar 10, 2026
31175f4
Merge branch 'main' of github.com:e2b-dev/infra into lev-allow-deny-d…
levb Mar 10, 2026
2c46352
PR feedback: unify egress config building and add domain validation
levb Mar 10, 2026
50fdf2f
PR feedback: extract ApplyAllOrRollback utility for transactional upd…
levb Mar 10, 2026
f8c2100
PR feedback: move network config into Config with own RWMutex
levb Mar 10, 2026
e1cc432
PR feedback: handle wildcard domains in IDNA validation
levb Mar 10, 2026
fe7a6db
PR feedback: rename ApplyAllOrRollback to ApplyAllOrNone, fix test
levb Mar 10, 2026
89c30ef
feat: add dynamic ingress control for sandboxes
levb Mar 10, 2026
2ba9d60
refactor: deduplicate ingress validation and avoid hot-path allocation
levb Mar 10, 2026
f4059c7
test: add ingress control integration tests
levb Mar 10, 2026
51fec51
test: speed up ingress integration tests
levb Mar 10, 2026
22671e3
test: restore egress CIDR intersection tests
levb Mar 11, 2026
65daa0d
chore: auto-commit generated changes
github-actions[bot] Mar 11, 2026
cf2b029
fix: lint issues from CI (protogetter, revive, modernize, unparam)
levb Mar 11, 2026
18ac69f
fix: consolidate client IP extraction and fix XFF spoofing
levb Mar 11, 2026
248ffcf
Merge branch 'lev-ingress-control' of github.com:e2b-dev/infra into l…
levb Mar 11, 2026
e449b68
test: add integration test for dynamic MaskRequestHost updates
levb Mar 11, 2026
f2df7ed
fix: include IPv6 CIDRs in client IP deny tests
levb Mar 11, 2026
b1240d0
fix: fix MaskRequestHost test echo server startup
levb Mar 11, 2026
2a4e32e
fix: reject allowIn without deny-all and clean up test names
levb Mar 11, 2026
a457ea6
Merge branch 'main' of github.com:e2b-dev/infra into lev-allow-deny-d…
levb Mar 11, 2026
60351fc
Merge branch 'lev-allow-deny-dynamic' into lev-ingress-control
levb Mar 11, 2026
b97f951
fix(orchestrator): use context.WithoutCancel for egress rollback
levb Mar 11, 2026
b609b47
refactor(orchestrator): restore ApplyAllOrNone for update rollbacks
levb Mar 11, 2026
e1b618d
test: trim redundant unit tests, add combined egress+ingress integrat…
levb Mar 11, 2026
2b4e58a
chore: auto-commit generated changes
github-actions[bot] Mar 11, 2026
68f5571
fix: close response body from WaitForStatus to satisfy bodyclose lint
levb Mar 11, 2026
c38bf3b
Merge branch 'lev-ingress-control' of github.com:e2b-dev/infra into l…
levb Mar 11, 2026
e1a910c
fix(client-proxy): strip X-E2B-Client-IP before extracting client IP
levb Mar 11, 2026
a5d435c
PR feedback: removed questionable domain "validation"
levb Mar 12, 2026
aa11b20
PR feedback: restored accidentally removed comment
levb Mar 12, 2026
bc61dae
PR feedback: clarify nftables buffering semantics in clearAndReplaceC…
levb Mar 12, 2026
15ee24a
PR feedback: add NewConfig constructor to guarantee non-nil network c…
levb Mar 12, 2026
6f21b57
Merge branch 'main' of github.com:e2b-dev/infra into lev-allow-deny-d…
levb Mar 12, 2026
19f2de9
Fix and simplify sandboxes_update_test.go after merge with main
levb Mar 12, 2026
d8be629
Fix nlreturn lint: add blank line before return in clearAndReplaceCIDRs
levb Mar 12, 2026
71ff3e0
Merge remote-tracking branch 'e2b/lev-allow-deny-dynamic' into lev-in…
levb Mar 12, 2026
e1eee7f
Fix data race on network egress/ingress config access
levb Mar 12, 2026
89c3dd2
Fix data race on network egress/ingress config access
levb Mar 12, 2026
4e8a061
Merge branch 'lev-allow-deny-dynamic' into lev-ingress-control
levb Mar 12, 2026
5d7abe5
Merge branch 'main' of github.com:e2b-dev/infra into lev-allow-deny-d…
levb Mar 12, 2026
645148b
Fix spoofed-IP ingress tests to use XFF instead of X-E2B-Client-IP
levb Mar 12, 2026
4de0587
Merge branch 'main' of github.com:e2b-dev/infra into lev-allow-deny-d…
levb Mar 14, 2026
cf29fc8
feat(api): add port range syntax to egress allow/deny rules (stage 1)
levb Mar 14, 2026
1fdee8a
feat(orch): unify proto fields, add port-specific egress rules, integ…
levb Mar 15, 2026
560afad
Merge branch 'main' of github.com:e2b-dev/infra into lev-allow-deny-d…
levb Mar 15, 2026
cd73d71
Merge branch 'lev-allow-deny-dynamic' into lev-egress-port-ranges
levb Mar 15, 2026
8b34a82
refactor(tests): speed up egress integration tests
levb Mar 15, 2026
ddef2ac
chore: auto-commit generated changes
github-actions[bot] Mar 15, 2026
c52a45c
fix(lint): use errors.Is for ErrNotFound, fix unparam in DNS helper
levb Mar 15, 2026
4c58142
Merge branch 'lev-egress-port-ranges' of github.com:e2b-dev/infra int…
levb Mar 15, 2026
0082dd0
Merge branch 'lev-egress-port-ranges' into lev-ingress-control
levb Mar 15, 2026
b6e803d
fix(lint): use errors.Is for ErrNotFound after #2100 removed NotFound…
levb Mar 16, 2026
2cab438
Merge branch 'lev-allow-deny-dynamic' into lev-egress-port-ranges
levb Mar 16, 2026
9492095
PR feedback: reject IPv6 addresses in ParseRule to prevent misparse
levb Mar 16, 2026
1cfe761
PR feedback: pre-parse egress rules at config time, not per-connection
levb Mar 16, 2026
7bf84ee
consolidate network egress integration tests into single-sandbox pattern
levb Mar 16, 2026
e910bab
fix(lint): flatten subtests to top-level funcs, rename clear helper
levb Mar 16, 2026
1340a03
remove UDP-only restriction from nftables port rules
levb Mar 16, 2026
5903d43
speed up integration tests: reduce blocked connection timeouts
levb Mar 16, 2026
6ee76ca
refactor: unify network ACL types, eliminate per-connection parsing i…
levb Mar 16, 2026
3e262b4
fix: treat empty host as 0.0.0.0/0, reject domains in deny at parse time
levb Mar 17, 2026
e24837d
Merge branch 'main' of github.com:e2b-dev/infra into lev-allow-deny-d…
levb Mar 17, 2026
a438dc7
Merge branch 'lev-allow-deny-dynamic' into lev-egress-port-ranges
levb Mar 17, 2026
3f6c11c
Merge branch 'lev-egress-port-ranges' into lev-ingress-control
levb Mar 17, 2026
7c0484d
refactor(tests): consolidate network integration tests into single sa…
levb Mar 17, 2026
74faef4
fix(tests): add IPv6 CIDRs to port-specific ingress deny tests
levb Mar 17, 2026
c618990
refactor: move ingress ACL from Metadata to Config, consistent with e…
levb Mar 17, 2026
0dce6e5
fix(security): fail closed on unparseable client IP in ingress check
levb Mar 17, 2026
8667c54
chore: auto-commit generated changes
github-actions[bot] Mar 17, 2026
0b8fb40
refactor: code review cleanup for ingress control
levb Mar 17, 2026
7f61f39
Merge branch 'lev-ingress-control' of github.com:e2b-dev/infra into l…
levb Mar 17, 2026
abe36c6
chore: auto-commit generated changes
github-actions[bot] Mar 17, 2026
cf896dd
refactor: use atomic.Pointer for parsed ACLs, lock-free hot path
levb Mar 17, 2026
93e1382
Merge branch 'lev-ingress-control' of github.com:e2b-dev/infra into l…
levb Mar 17, 2026
3be037c
refactor: use pointer-to-atomic for parsed ACLs, avoid copylocks
levb Mar 17, 2026
8165cf6
fix: add blank line before return to satisfy nlreturn linter
levb Mar 17, 2026
15eccf1
refactor: revert egress to allow-deny API, add structured ingress proto
levb Mar 17, 2026
a503ab1
Merge branch 'main' of github.com:e2b-dev/infra into lev-allow-deny-d…
levb Mar 17, 2026
80248a2
Merge branch 'lev-allow-deny-dynamic' into lev-ingress-control
levb Mar 17, 2026
4c8c701
revert unrelated assert→require changes in test files
levb Mar 17, 2026
2ba87a5
Merge branch 'main' of github.com:e2b-dev/infra into lev-ingress-control
levb Mar 17, 2026
50a1fde
refactor(tests): rename network test helpers to use In/Out convention
levb Mar 17, 2026
6c007a8
refactor(tests): rename ptrS, reduce sandbox timeout, use In/Out naming
levb Mar 17, 2026
cc5b856
chore: auto-commit generated changes
github-actions[bot] Mar 17, 2026
e583335
fix: restore bare IP support in network rules (backwards compat)
levb Mar 17, 2026
2213a7b
Merge branch 'lev-ingress-control' of github.com:e2b-dev/infra into l…
levb Mar 17, 2026
3c6adf8
test: add missing egress test cases from main
levb Mar 17, 2026
928205b
refactor(tests): restore _out_test.go from main, slim _update_test.go
levb Mar 17, 2026
0952f3f
docs: shorten OpenAPI descriptions, remove egress port references
levb Mar 17, 2026
5860b9a
refactor: pre-parsed ACLs, reject egress ports, simplify rule parsing
levb Mar 18, 2026
3ba34b6
refactor(tests): rename getHost to echoHost
levb Mar 18, 2026
e363fb6
refactor(tests): inline sandbox.NewConfig in server tests
levb Mar 18, 2026
0f24c07
Merge branch 'main' of github.com:e2b-dev/infra into lev-ingress-control
levb Mar 18, 2026
4d045b3
Merge branch 'main' of github.com:e2b-dev/infra into lev-ingress-control
levb Mar 18, 2026
8539142
fix: CI failures — remove unused error return, fix IPv6 ingress test
levb Mar 18, 2026
a8a5dce
refactor: match main's style for block-all checks in validation
levb Mar 18, 2026
3cec4f8
chore: auto-commit generated changes
github-actions[bot] Mar 18, 2026
1cfef7b
revert unrelated assert→require changes in sandbox_create_test.go
levb Mar 18, 2026
930993c
Merge branch 'lev-ingress-control' of github.com:e2b-dev/infra into l…
levb Mar 18, 2026
7966963
chore: auto-commit generated changes
github-actions[bot] Mar 18, 2026
bb97a11
refactor: buildEgressConfig takes egressUpdate, restore main's naming
levb Mar 18, 2026
fefb093
fix: preserve allowPublicAccess during network config updates
levb Mar 18, 2026
e15260f
revert cosmetic test changes in smoketest and nfsproxy
levb Mar 18, 2026
d56f3c8
cleanup: revert cosmetic changes, drop impossible-data tests
levb Mar 18, 2026
80a8752
restore comments in firewall.go to match main
levb Mar 18, 2026
31064d4
Update packages/shared/pkg/proxy/template/browser_ingress_denied.html
levb Mar 19, 2026
1f6b41e
Update packages/api/internal/handlers/sandbox_create.go
levb Mar 19, 2026
78d19ba
Merge branch 'main' of github.com:e2b-dev/infra into lev-ingress-control
levb Mar 19, 2026
e9b6eee
PR feedback: use a loop var instead of somearray[i]
levb Mar 19, 2026
4670760
PR feedback: renamed ClientIPHeader
levb Mar 19, 2026
6816ae1
Merge branch 'lev-ingress-control' of github.com:e2b-dev/infra into l…
levb Mar 19, 2026
cbbf8e6
Drop IPv6 from ingress rules; deny IPv6 clients fail-closed
levb Mar 19, 2026
a3b7d49
Restore ParseAddressesAndDomains, inline error message strings in tests
levb Mar 19, 2026
2ad55c6
lint, more IPv6 removed
levb Mar 19, 2026
eedb612
PR feedback: clarified some test cases
levb Mar 19, 2026
42983b7
Remove duplicate test cases in sandbox_create_test.go
levb Mar 19, 2026
5c3d9ec
Clean up parseIngressRules and fix update_network field assignment order
levb Mar 20, 2026
dc7cea3
Refactor network access control into Egress/Ingress value types
levb Mar 20, 2026
260cd55
Unify egress/ingress proto format to repeated string CIDRs
levb Mar 20, 2026
ebfa559
Revert unnecessary openapi description changes, keep PUT definitions …
levb Mar 20, 2026
028b17a
chore: auto-commit generated changes
github-actions[bot] Mar 20, 2026
b083d55
Fix ingress tests for IPv6 CI runners
levb Mar 20, 2026
f11c720
Merge branch 'lev-ingress-control' of github.com:e2b-dev/infra into l…
levb Mar 20, 2026
09745f4
Restore MatchDomain comment per review
levb Mar 20, 2026
7818793
Update packages/shared/pkg/proxy/errors.go
levb Mar 20, 2026
31a470e
Merge branch 'lev-ingress-control' of github.com:e2b-dev/infra into l…
levb Mar 20, 2026
19516d8
Fix sandboxID/sandboxId param mismatch in NewErrIngressDenied
levb Mar 20, 2026
140439b
Rename AllInternetTrafficCIDR to AllTraffic, use const instead of lit…
levb Mar 20, 2026
005ab5c
Remove proto clone, simplify sandbox config creation
levb Mar 20, 2026
3d2280c
Split ExtractClientIP into ExtractExternalClientIP and ExtractE2BClie…
levb Mar 20, 2026
547cbb7
Merge branch 'main' of github.com:e2b-dev/infra into lev-ingress-control
levb Mar 20, 2026
af16ac1
Merge branch 'main' of github.com:e2b-dev/infra into lev-ingress-control
levb Mar 20, 2026
efc86b4
Resolve merge conflicts, restore AllInternetTrafficCIDR and ErrMsg co…
levb Mar 20, 2026
f84e8f8
Merge remote-tracking branch 'e2b/main' into lev-ingress-control
levb Mar 20, 2026
70e5e3a
Fix MarkRunning call to pass context after main merge
levb Mar 20, 2026
69e1dfc
Tighten curl timeouts in integration tests, use IPs for blocked checks
levb Mar 20, 2026
80eea09
Merge branch 'main' of github.com:e2b-dev/infra into lev-ingress-control
levb Mar 22, 2026
bfd9758
chore: auto-commit generated changes
github-actions[bot] Mar 22, 2026
35ae165
Merge remote-tracking branch 'e2b/main' into lev-ingress-control
levb Mar 25, 2026
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
340 changes: 179 additions & 161 deletions packages/api/internal/api/api.gen.go

Large diffs are not rendered by default.

122 changes: 94 additions & 28 deletions packages/api/internal/handlers/sandbox_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ const (
metricTemplateAlias = metrics.MetricPrefix + "template.alias"
minEnvdVersionForSecureFlag = "0.2.0" // Minimum version of envd that supports secure flag

// Network validation error messages
ErrMsgDomainsRequireBlockAll = "When specifying allowed domains in allow out, you must include 'ALL_TRAFFIC' in deny out to block all other traffic."
ErrMsgDomainsRequireBlockAll = "When specifying allowed domains in allow out, you must include 'ALL_TRAFFIC' in deny out to block all other traffic."
ErrMsgAllowInRequiresBlockAll = "When specifying allowed sources in allow in, you must include 'ALL_TRAFFIC' (0.0.0.0/0) in deny in to block all other traffic."
)

func (a *APIStore) PostSandboxes(c *gin.Context) {
Expand Down Expand Up @@ -174,6 +174,8 @@ func (a *APIStore) PostSandboxes(c *gin.Context) {
Ingress: &types.SandboxNetworkIngressConfig{
AllowPublicAccess: n.AllowPublicTraffic,
MaskRequestHost: n.MaskRequestHost,
AllowedAddresses: sharedUtils.DerefOrDefault(n.AllowIn, nil),
DeniedAddresses: sharedUtils.DerefOrDefault(n.DenyIn, nil),
},
Egress: &types.SandboxNetworkEgressConfig{
AllowedAddresses: sharedUtils.DerefOrDefault(n.AllowOut, nil),
Expand Down Expand Up @@ -532,44 +534,108 @@ func validateNetworkConfig(network *api.SandboxNetworkConfig) *api.APIError {
denyOut := sharedUtils.DerefOrDefault(network.DenyOut, nil)
allowOut := sharedUtils.DerefOrDefault(network.AllowOut, nil)

return validateEgressRules(allowOut, denyOut)
if apiErr := validateEgressRules(allowOut, denyOut); apiErr != nil {
return apiErr
}

denyIn := sharedUtils.DerefOrDefault(network.DenyIn, nil)
allowIn := sharedUtils.DerefOrDefault(network.AllowIn, nil)

return validateIngressRules(allowIn, denyIn)
}

func badRequest(err error, format string, args ...any) *api.APIError {
s := fmt.Sprintf(format, args...)
if err != nil {
s = fmt.Sprintf("%s: %s", s, err.Error())
err = fmt.Errorf("%s: %w", s, err)
}

return &api.APIError{Code: http.StatusBadRequest, Err: err, ClientMsg: s}
}

// validateEgressRules validates egress allow/deny rules:
// - denyOut entries must be valid IPs or CIDRs (not domains)
// - allowOut entries must be valid IPs, CIDRs, or domain names
// - denyOut entries must be specified IPs or CIDRs (not domains)
// - allowOut entries can be specified IPs, CIDRs, or domain names
// - when allowOut contains domains, denyOut must include 0.0.0.0/0
func validateEgressRules(allowOut, denyOut []string) *api.APIError {
for _, cidr := range denyOut {
if !sandbox_network.IsSpecifiedIPOrCIDR(cidr) {
return &api.APIError{
Code: http.StatusBadRequest,
Err: fmt.Errorf("invalid denied CIDR %s", cidr),
ClientMsg: fmt.Sprintf("invalid denied CIDR %s", cidr),
}
for _, entry := range denyOut {
host, port, _ := sandbox_network.SplitHostPort(entry)
if port != "" {
return badRequest(nil, "invalid deny out entry %q: port-specific rules are not supported for egress", entry)
}

if !sandbox_network.IsSpecifiedIPOrCIDR(host) {
return badRequest(nil, "invalid denied CIDR %s", host)
}
}

if len(allowOut) > 0 {
allowedAddresses, allowedDomains := sandbox_network.ParseAddressesAndDomains(allowOut)
hasDomains := false
for _, entry := range allowOut {
host, port, _ := sandbox_network.SplitHostPort(entry)
if port != "" {
return badRequest(nil, "invalid allow out entry %q: port-specific rules are not supported for egress", entry)
}

for _, addr := range allowedAddresses {
if !sandbox_network.IsSpecifiedIPOrCIDR(addr) {
return &api.APIError{
Code: http.StatusBadRequest,
Err: fmt.Errorf("invalid allowed address %s", addr),
ClientMsg: fmt.Sprintf("invalid allowed address %s", addr),
}
if sandbox_network.IsIPOrCIDR(host) {
if !sandbox_network.IsSpecifiedIPOrCIDR(host) {
Comment thread
dobrac marked this conversation as resolved.
return badRequest(nil, "invalid allowed address %s", host)
}
} else {
hasDomains = true
}
hasBlockAll := slices.Contains(denyOut, sandbox_network.AllInternetTrafficCIDR)
}

if len(allowedDomains) > 0 && !hasBlockAll {
return &api.APIError{
Code: http.StatusBadRequest,
Err: fmt.Errorf("allow out contains domains but deny out is missing 0.0.0.0/0 (ALL_TRAFFIC)"),
ClientMsg: ErrMsgDomainsRequireBlockAll,
}
hasBlockAll := slices.Contains(denyOut, sandbox_network.AllInternetTrafficCIDR)
if hasDomains && !hasBlockAll {
return badRequest(nil, ErrMsgDomainsRequireBlockAll)
}

return nil
}

// validateIngressRules validates ingress allow/deny rules:
// - entries must be valid IPv4 or CIDR with optional port/port-range (no domains)
// - when allowIn is set, denyIn must include 0.0.0.0/0
func validateIngressRules(allowIn, denyIn []string) *api.APIError {
for _, entry := range denyIn {
if err := validateIngressEntry(entry); err != nil {
return badRequest(err, "invalid deny in entry %q", entry)
}
}

for _, entry := range allowIn {
if err := validateIngressEntry(entry); err != nil {
return badRequest(err, "invalid allow in entry %q", entry)
}
}

hasBlockAll := slices.Contains(denyIn, sandbox_network.AllInternetTrafficCIDR)
if len(allowIn) > 0 && !hasBlockAll {
return badRequest(nil, ErrMsgAllowInRequiresBlockAll)
}

return nil
}

// validateIngressEntry validates a single ingress rule entry (CIDR[:port]).
func validateIngressEntry(entry string) error {
host, portStr, err := sandbox_network.SplitHostPort(entry)
if err != nil {
return err
}

if !sandbox_network.IsIPOrCIDR(host) {
return fmt.Errorf("domains are not supported for ingress rules")
}
Comment thread
levb marked this conversation as resolved.

if !sandbox_network.IsSpecifiedIPOrCIDR(host) {
return fmt.Errorf("unspecified address")
}

if portStr != "" {
if _, _, err := sandbox_network.ParsePortRange(portStr); err != nil {
return err
}
}

Expand Down
188 changes: 161 additions & 27 deletions packages/api/internal/handlers/sandbox_create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,26 @@ func TestValidateNetworkConfig(t *testing.T) {
wantCode int
wantErrMsg string
}{
// Port syntax rejected for egress
{
name: "deny_out with port is rejected",
network: &api.SandboxNetworkConfig{
DenyOut: &[]string{"10.0.0.0/8:22"},
},
wantErr: true,
wantCode: http.StatusBadRequest,
wantErrMsg: `invalid deny out entry "10.0.0.0/8:22": port-specific rules are not supported for egress`,
},
{
name: "allow_out with port is rejected",
network: &api.SandboxNetworkConfig{
AllowOut: &[]string{"8.8.8.8:80"},
},
wantErr: true,
wantCode: http.StatusBadRequest,
wantErrMsg: `invalid allow out entry "8.8.8.8:80": port-specific rules are not supported for egress`,
},
// Valid configurations
{
name: "nil network config is valid",
network: nil,
Expand Down Expand Up @@ -109,33 +129,6 @@ func TestValidateNetworkConfig(t *testing.T) {
wantErrMsg: "invalid denied CIDR not-a-cidr",
},
// Domain validation tests
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed because these are already covered by the wildcard tests, redundant

{
name: "allow_out with domain requires deny_out block-all",
network: &api.SandboxNetworkConfig{
AllowOut: &[]string{"example.com"},
},
wantErr: true,
wantCode: http.StatusBadRequest,
wantErrMsg: ErrMsgDomainsRequireBlockAll,
},
{
name: "allow_out with domain and block-all deny_out is valid",
network: &api.SandboxNetworkConfig{
AllowOut: &[]string{"example.com"},
DenyOut: &[]string{sandbox_network.AllInternetTrafficCIDR},
},
wantErr: false,
},
{
name: "allow_out with domain and partial deny_out is invalid",
network: &api.SandboxNetworkConfig{
AllowOut: &[]string{"example.com"},
DenyOut: &[]string{"10.0.0.0/8"},
},
wantErr: true,
wantCode: http.StatusBadRequest,
wantErrMsg: ErrMsgDomainsRequireBlockAll,
},
{
name: "allow_out with wildcard domain requires deny_out block-all",
network: &api.SandboxNetworkConfig{
Expand Down Expand Up @@ -241,6 +234,129 @@ func TestValidateNetworkConfig(t *testing.T) {
},
wantErr: false,
},
// Ingress port validation tests (unified CIDR:port format)
{
name: "valid allowIn with port",
network: &api.SandboxNetworkConfig{
AllowIn: &[]string{"0.0.0.0/0:80", "0.0.0.0/0:443"},
Comment thread
levb marked this conversation as resolved.
DenyIn: &[]string{sandbox_network.AllInternetTrafficCIDR},
},
wantErr: false,
},
{
name: "valid allowIn with port range",
network: &api.SandboxNetworkConfig{
AllowIn: &[]string{"10.0.0.0/8:80-443"},
DenyIn: &[]string{sandbox_network.AllInternetTrafficCIDR},
},
wantErr: false,
},
{
name: "valid denyIn with port",
network: &api.SandboxNetworkConfig{
DenyIn: &[]string{"0.0.0.0/0:22", "0.0.0.0/0:3306"},
},
wantErr: false,
},
{
name: "valid denyIn port-only shorthand :80 means all IPs port 80",
network: &api.SandboxNetworkConfig{
DenyIn: &[]string{":80"},
},
wantErr: false,
},
{
name: "valid denyIn port-range shorthand :80-90 means all IPs ports 80-90",
network: &api.SandboxNetworkConfig{
DenyIn: &[]string{":80-90"},
},
wantErr: false,
},
{
name: "valid allowIn port-only shorthand :443 with deny-all",
network: &api.SandboxNetworkConfig{
AllowIn: &[]string{":443"},
DenyIn: &[]string{sandbox_network.AllInternetTrafficCIDR},
},
wantErr: false,
},
// Ingress CIDR validation tests
{
name: "valid allowIn CIDR with deny-all",
network: &api.SandboxNetworkConfig{
AllowIn: &[]string{"10.0.0.0/8"},
DenyIn: &[]string{sandbox_network.AllInternetTrafficCIDR},
},
wantErr: false,
},
{
name: "valid allowIn CIDR from IP with deny-all",
network: &api.SandboxNetworkConfig{
AllowIn: &[]string{"1.2.3.4/32"},
Comment thread
levb marked this conversation as resolved.
DenyIn: &[]string{sandbox_network.AllInternetTrafficCIDR},
},
wantErr: false,
},
{
name: "valid allowIn bare IP with deny-all",
network: &api.SandboxNetworkConfig{
AllowIn: &[]string{"1.2.3.4"},
DenyIn: &[]string{sandbox_network.AllInternetTrafficCIDR},
},
wantErr: false,
},
{
name: "valid allowIn bare IP with port and deny-all",
network: &api.SandboxNetworkConfig{
AllowIn: &[]string{"1.2.3.4:80"},
DenyIn: &[]string{sandbox_network.AllInternetTrafficCIDR},
},
wantErr: false,
},
{
name: "allowIn without deny-all is rejected",
network: &api.SandboxNetworkConfig{
AllowIn: &[]string{"10.0.0.0/8"},
},
wantErr: true,
wantCode: http.StatusBadRequest,
wantErrMsg: ErrMsgAllowInRequiresBlockAll,
},
{
name: "allowIn with partial denyIn (no deny-all) is rejected",
network: &api.SandboxNetworkConfig{
AllowIn: &[]string{"10.0.0.0/8"},
DenyIn: &[]string{"192.168.0.0/16"},
},
wantErr: true,
wantCode: http.StatusBadRequest,
wantErrMsg: ErrMsgAllowInRequiresBlockAll,
},
{
name: "invalid allowIn entry",
network: &api.SandboxNetworkConfig{
AllowIn: &[]string{"not-a-cidr"},
},
wantErr: true,
wantCode: http.StatusBadRequest,
wantErrMsg: `invalid allow in entry "not-a-cidr": domains are not supported for ingress rules`,
},
{
name: "valid denyIn CIDR",
network: &api.SandboxNetworkConfig{
DenyIn: &[]string{"192.168.0.0/16"},
},
wantErr: false,
},
{
name: "invalid denyIn entry",
network: &api.SandboxNetworkConfig{
DenyIn: &[]string{"bad"},
},
wantErr: true,
wantCode: http.StatusBadRequest,
wantErrMsg: `invalid deny in entry "bad": domains are not supported for ingress rules`,
},
// Mixed domain and CIDR tests
{
name: "allow_out with domain and CIDR without deny_out block-all is invalid",
Expand All @@ -260,6 +376,24 @@ func TestValidateNetworkConfig(t *testing.T) {
},
wantErr: false,
},
{
name: "deny_out with domain is rejected",
network: &api.SandboxNetworkConfig{
DenyOut: &[]string{"example.com"},
},
wantErr: true,
wantCode: http.StatusBadRequest,
wantErrMsg: `invalid denied CIDR example.com`,
},
{
name: "deny_out with invalid port is rejected",
network: &api.SandboxNetworkConfig{
DenyOut: &[]string{"10.0.0.0/8:abc"},
},
wantErr: true,
wantCode: http.StatusBadRequest,
wantErrMsg: `invalid deny out entry "10.0.0.0/8:abc": port-specific rules are not supported for egress`,
},
}

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