Cybersecurity-Projects/PROJECTS/intermediate/credential-enumeration/tests/test_all.nim

241 lines
7.2 KiB
Nim

# ©AngelaMos | 2026
# test_all.nim
#
# Unit tests for core utility and parsing functions
#
# Exercises exported helpers from four modules across eight test
# suites. redactValue covers short, long, exact-length, and empty
# strings. isPrivateKey validates detection of five PEM header
# formats (OpenSSH, RSA, ECDSA, DSA, PKCS8) and rejection of
# public keys and non-key content. isEncrypted checks for
# ENCRYPTED, bcrypt, and aes256-ctr markers versus unencrypted
# keys. matchesSecretPattern verifies detection of export-prefixed
# and bare KEY=/SECRET=/TOKEN=/PASSWORD= assignments while
# rejecting PATH exports and ordinary commands.
# matchesCommandPattern tests curl with auth headers and -u flag,
# wget with authorization header and password, mysql -p, psql
# password, and sshpass detection, rejecting safe commands.
# matchesExclude validates exact filename and directory segment
# matching without false positives on partial or embedded
# substrings. permissionSeverity confirms svInfo for nonexistent
# paths. parseModules tests single, multiple, whitespace-padded,
# full-set, empty, and unknown module string parsing. redactLine
# checks export-prefixed quoted, unquoted, and single-quoted value
# redaction plus passthrough for lines without an equals sign.
#
# Connects to:
# types.nim - Category enum values for parseModules
# collectors/base.nim - redactValue, matchesExclude,
# permissionSeverity
# collectors/ssh.nim - isPrivateKey, isEncrypted
# collectors/history.nim - matchesSecretPattern,
# matchesCommandPattern, redactLine
# harvester.nim - parseModules
import std/[unittest, strutils]
import types
import collectors/base
import collectors/ssh
import collectors/history
import harvester
suite "redactValue":
test "short value fully redacted":
check redactValue("abc", 4) == "***"
test "value longer than showChars":
check redactValue("mysecret", 4) == "myse****"
test "exact showChars length":
check redactValue("abcd", 4) == "****"
test "empty string":
check redactValue("", 4) == ""
suite "isPrivateKey":
test "OpenSSH key":
check isPrivateKey("-----BEGIN OPENSSH PRIVATE KEY-----\ndata")
test "RSA key":
check isPrivateKey("-----BEGIN RSA PRIVATE KEY-----\ndata")
test "ECDSA key":
check isPrivateKey("-----BEGIN EC PRIVATE KEY-----\ndata")
test "DSA key":
check isPrivateKey("-----BEGIN DSA PRIVATE KEY-----\ndata")
test "generic PKCS8 key":
check isPrivateKey("-----BEGIN PRIVATE KEY-----\ndata")
test "public key rejected":
check isPrivateKey("-----BEGIN PUBLIC KEY-----\ndata") == false
test "random text rejected":
check isPrivateKey("this is not a key") == false
test "empty string rejected":
check isPrivateKey("") == false
suite "isEncrypted":
test "ENCRYPTED marker":
check isEncrypted(
"-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\ndata"
)
test "bcrypt marker":
check isEncrypted(
"-----BEGIN OPENSSH PRIVATE KEY-----\nbcrypt\ndata"
)
test "aes256-ctr marker":
check isEncrypted("data with aes256-ctr in it")
test "unencrypted key":
check isEncrypted(
"-----BEGIN OPENSSH PRIVATE KEY-----\nAAAAB3NzaC1\ndata"
) == false
suite "matchesSecretPattern":
test "export with KEY=":
check matchesSecretPattern("export API_KEY=some_value")
test "export with SECRET=":
check matchesSecretPattern(
"export AWS_SECRET_ACCESS_KEY=abc123"
)
test "bare TOKEN= at start":
check matchesSecretPattern("TOKEN=abcdef12345")
test "bare PASSWORD=":
check matchesSecretPattern("PASSWORD=hunter2")
test "non-secret assignment":
check matchesSecretPattern("export PATH=/usr/bin") == false
test "ordinary command":
check matchesSecretPattern("ls -la /tmp") == false
test "empty string":
check matchesSecretPattern("") == false
suite "matchesCommandPattern":
test "curl with auth header":
check matchesCommandPattern(
"curl -H \"Authorization: Bearer token\" https://api.example.com"
)
test "curl with lowercase -h auth":
check matchesCommandPattern(
"curl -h \"authorization: bearer token\" https://api.example.com"
)
test "curl with -u flag":
check matchesCommandPattern(
"curl -u user:pass https://api.example.com"
)
test "wget with authorization header":
check matchesCommandPattern(
"wget --header=\"Authorization: Basic abc\" https://example.com"
)
test "wget with password":
check matchesCommandPattern(
"wget --password=secret https://files.example.com/data.zip"
)
test "mysql with -p flag":
check matchesCommandPattern("mysql -u root -psecret mydb")
test "psql with password":
check matchesCommandPattern(
"psql password=secret host=db.example.com"
)
test "sshpass command":
check matchesCommandPattern("sshpass -p 'mypass' ssh user@host")
test "safe curl rejected":
check matchesCommandPattern("curl https://example.com") == false
test "safe git command rejected":
check matchesCommandPattern("git push origin main") == false
test "empty string rejected":
check matchesCommandPattern("") == false
suite "matchesExclude":
test "exact filename match":
check matchesExclude("/home/user/.env", @[".env"])
test "directory segment match":
check matchesExclude("/home/user/.git/config", @[".git"])
test "no false positive on partial name":
check matchesExclude(
"/home/user/.venv/lib/site.py", @[".env"]
) == false
test "no match on embedded substring":
check matchesExclude(
"/home/user/environment/data", @[".env"]
) == false
test "empty patterns":
check matchesExclude("/home/user/.env", @[]) == false
suite "permissionSeverity":
test "returns svInfo for unreadable path":
check permissionSeverity("/nonexistent/path/abc123") == svInfo
suite "parseModules":
test "single module":
check parseModules("ssh") == @[catSsh]
test "multiple modules":
let mods = parseModules("ssh,git,cloud")
check mods.len == 3
check mods.contains(catSsh)
check mods.contains(catGit)
check mods.contains(catCloud)
test "with whitespace":
let mods = parseModules(" browser , keyring ")
check mods.len == 2
check mods.contains(catBrowser)
check mods.contains(catKeyring)
test "all modules":
let mods = parseModules(
"browser,ssh,cloud,history,keyring,git,apptoken"
)
check mods.len == 7
test "empty string":
check parseModules("").len == 0
test "unknown module ignored":
check parseModules("fake,nonexistent").len == 0
suite "redactLine":
test "export with quoted value":
let got = redactLine("export KEY=\"secret\"")
check got.contains("KEY=")
check got.contains("\"") == false
test "export with unquoted value":
let got = redactLine("export API_KEY=mysecretvalue")
check got.contains("API_KEY=")
check got.contains("myse")
check got.contains("cretvalue") == false
test "no equals sign":
check redactLine("no assignment here") == "no assignment here"
test "single-quoted value":
let got = redactLine("export TOKEN='abcdefgh'")
check got.contains("TOKEN=")
check got.contains("'") == false