Skip to content

Commit 528b24b

Browse files
test: comprehensive testing suite (547 tests across 13 files)
Unit tests (8 files, 352 tests): - test_common.bats: logging, validation, require_cmd/env, umask, defaults - test_audit.bats: JSON validity, required fields, JSONL format, trap handler - test_registry.bats: CRUD, validation, duplicates, atomic writes, permissions - test_dns.bats: add/remove/list entries, dnsmasq config generation - test_tls.bats: CA creation, cert issue/revoke/list, permissions, rotation - test_routing.bats: Caddyfile generation, route add/remove/list, reload - test_security.bats: no eval, strict mode, no credentials, injection prevention - test_common_edge_cases.bats: unicode, long names, path traversal, overrides Integration tests (3 files, 47 tests): - test_service_lifecycle.bats: full register/deregister workflow - test_file_permissions.bats: 600/700 on all sensitive files - test_audit_integration.bats: audit trail across operations Smoke tests (1 file, 78 checks): - test_standalone.sh: file existence, shebangs, strict mode, no secrets Python tests (1 file, 70 tests): - test_server.py: all 16 API endpoints, helpers, SPA fallback
1 parent f8bc244 commit 528b24b

14 files changed

Lines changed: 4343 additions & 0 deletions
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
#!/usr/bin/env bats
2+
# tests/integration/test_audit_integration.bats
3+
# Integration tests for audit trail across operations.
4+
5+
LIB_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../lib" && pwd)"
6+
7+
setup() {
8+
export HOME="$BATS_TMPDIR"
9+
export CONDUIT_CONFIG_DIR="$BATS_TMPDIR/test-audit-int-$$-$BATS_TEST_NUMBER"
10+
export CONDUIT_DOMAIN="qp.local"
11+
source "$LIB_DIR/common.sh"
12+
source "$LIB_DIR/registry.sh"
13+
source "$LIB_DIR/audit.sh"
14+
15+
# Stub _capsule_seal
16+
_capsule_seal() { return 0; }
17+
export -f _capsule_seal
18+
19+
apply_defaults
20+
ensure_config_dir >/dev/null
21+
registry_init
22+
}
23+
24+
teardown() {
25+
rm -rf "${CONDUIT_CONFIG_DIR:-}" 2>/dev/null || true
26+
}
27+
28+
# ===========================================================================
29+
# Register creates audit entry
30+
# ===========================================================================
31+
32+
@test "register operation creates audit entry" {
33+
registry_add_service "hub" "127.0.0.1" "8090"
34+
audit_log "service_register" "success" "Registered service: hub"
35+
local line
36+
line="$(tail -1 "$CONDUIT_CONFIG_DIR/audit.log")"
37+
run jq -r '.action' <<< "$line"
38+
[ "$output" = "service_register" ]
39+
run jq -r '.status' <<< "$line"
40+
[ "$output" = "success" ]
41+
}
42+
43+
@test "register audit entry includes service name in message" {
44+
registry_add_service "grafana" "10.0.1.5" "3000"
45+
audit_log "service_register" "success" "Registered service: grafana"
46+
local line
47+
line="$(tail -1 "$CONDUIT_CONFIG_DIR/audit.log")"
48+
run jq -r '.message' <<< "$line"
49+
[[ "$output" == *"grafana"* ]]
50+
}
51+
52+
# ===========================================================================
53+
# Deregister creates audit entry
54+
# ===========================================================================
55+
56+
@test "deregister operation creates audit entry" {
57+
registry_add_service "hub" "127.0.0.1" "8090"
58+
registry_remove_service "hub"
59+
audit_log "service_deregister" "success" "Deregistered service: hub"
60+
local line
61+
line="$(tail -1 "$CONDUIT_CONFIG_DIR/audit.log")"
62+
run jq -r '.action' <<< "$line"
63+
[ "$output" = "service_deregister" ]
64+
}
65+
66+
# ===========================================================================
67+
# Setup creates audit entry
68+
# ===========================================================================
69+
70+
@test "setup operation creates audit entry" {
71+
audit_log "setup" "success" "Conduit initialized: domain=qp.local" \
72+
'{"domain":"qp.local","dns_port":"53"}'
73+
local line
74+
line="$(tail -1 "$CONDUIT_CONFIG_DIR/audit.log")"
75+
run jq -r '.action' <<< "$line"
76+
[ "$output" = "setup" ]
77+
run jq -r '.details.domain' <<< "$line"
78+
[ "$output" = "qp.local" ]
79+
}
80+
81+
# ===========================================================================
82+
# Multiple operations create sequential entries
83+
# ===========================================================================
84+
85+
@test "multiple operations create sequential entries" {
86+
audit_log "setup" "success" "Setup complete"
87+
audit_log "service_register" "success" "Registered hub"
88+
audit_log "service_register" "success" "Registered grafana"
89+
audit_log "service_deregister" "success" "Deregistered hub"
90+
local count
91+
count="$(wc -l < "$CONDUIT_CONFIG_DIR/audit.log")"
92+
[ "$count" -eq 4 ]
93+
}
94+
95+
@test "sequential entries are all valid JSON" {
96+
audit_log "setup" "success" "Setup"
97+
audit_log "service_register" "success" "Registered hub"
98+
audit_log "dns_flush" "success" "DNS flushed"
99+
while IFS= read -r line; do
100+
echo "$line" | jq empty
101+
done < "$CONDUIT_CONFIG_DIR/audit.log"
102+
}
103+
104+
@test "audit_read returns all sequential entries in order" {
105+
audit_log "setup" "success" "first"
106+
audit_log "service_register" "success" "second"
107+
audit_log "cert_rotate" "success" "third"
108+
run audit_read 10
109+
[ "$status" -eq 0 ]
110+
local count
111+
count="$(echo "$output" | jq 'length')"
112+
[ "$count" -eq 3 ]
113+
}
114+
115+
# ===========================================================================
116+
# Audit entries have correct actions
117+
# ===========================================================================
118+
119+
@test "audit entries have correct action for each operation type" {
120+
audit_log "setup" "success" "Setup"
121+
audit_log "service_register" "success" "Register"
122+
audit_log "service_deregister" "success" "Deregister"
123+
audit_log "cert_rotate" "success" "Cert rotate"
124+
audit_log "dns_flush" "success" "DNS flush"
125+
audit_log "health_check" "success" "Health check"
126+
127+
run audit_read 10
128+
[[ "$output" == *"setup"* ]]
129+
[[ "$output" == *"service_register"* ]]
130+
[[ "$output" == *"service_deregister"* ]]
131+
[[ "$output" == *"cert_rotate"* ]]
132+
[[ "$output" == *"dns_flush"* ]]
133+
[[ "$output" == *"health_check"* ]]
134+
}
135+
136+
@test "failure audit entries record correctly" {
137+
audit_log "service_register" "failure" "Port validation failed"
138+
local line
139+
line="$(tail -1 "$CONDUIT_CONFIG_DIR/audit.log")"
140+
run jq -r '.status' <<< "$line"
141+
[ "$output" = "failure" ]
142+
run jq -r '.message' <<< "$line"
143+
[[ "$output" == *"Port validation failed"* ]]
144+
}
145+
146+
@test "audit trail survives registry operations" {
147+
audit_log "setup" "success" "init"
148+
registry_add_service "hub" "127.0.0.1" "8090"
149+
audit_log "service_register" "success" "hub"
150+
registry_remove_service "hub"
151+
audit_log "service_deregister" "success" "hub"
152+
153+
run audit_read 10
154+
local count
155+
count="$(echo "$output" | jq 'length')"
156+
[ "$count" -eq 3 ]
157+
}
158+
159+
@test "each audit entry has unique timestamp or same-second is acceptable" {
160+
audit_log "a1" "success" "first"
161+
audit_log "a2" "success" "second"
162+
run audit_read 10
163+
local ts1
164+
ts1="$(echo "$output" | jq -r '.[0].timestamp')"
165+
local ts2
166+
ts2="$(echo "$output" | jq -r '.[1].timestamp')"
167+
[ -n "$ts1" ]
168+
[ -n "$ts2" ]
169+
}
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
#!/usr/bin/env bats
2+
# tests/integration/test_file_permissions.bats
3+
# Verifies that all file operations maintain secure permissions throughout workflows.
4+
5+
LIB_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../lib" && pwd)"
6+
7+
setup() {
8+
export HOME="$BATS_TMPDIR"
9+
export CONDUIT_CONFIG_DIR="$BATS_TMPDIR/test-perms-$$-$BATS_TEST_NUMBER"
10+
export CONDUIT_CERTS_DIR="$CONDUIT_CONFIG_DIR/certs"
11+
export CONDUIT_DOMAIN="qp.local"
12+
source "$LIB_DIR/common.sh"
13+
source "$LIB_DIR/registry.sh"
14+
source "$LIB_DIR/audit.sh"
15+
source "$LIB_DIR/dns.sh"
16+
source "$LIB_DIR/routing.sh"
17+
source "$LIB_DIR/tls.sh"
18+
19+
# Stub _capsule_seal
20+
_capsule_seal() { return 0; }
21+
export -f _capsule_seal
22+
23+
apply_defaults
24+
}
25+
26+
teardown() {
27+
rm -rf "${CONDUIT_CONFIG_DIR:-}" 2>/dev/null || true
28+
}
29+
30+
# Helper: cross-platform permission check
31+
_get_perms() {
32+
stat -c '%a' "$1" 2>/dev/null || stat -f '%A' "$1" 2>/dev/null
33+
}
34+
35+
# ===========================================================================
36+
# Config directory
37+
# ===========================================================================
38+
39+
@test "ensure_config_dir creates with 700" {
40+
ensure_config_dir >/dev/null
41+
local perms
42+
perms="$(_get_perms "$CONDUIT_CONFIG_DIR")"
43+
[ "$perms" = "700" ]
44+
}
45+
46+
@test "config dir permissions survive multiple ensure_config_dir calls" {
47+
ensure_config_dir >/dev/null
48+
ensure_config_dir >/dev/null
49+
ensure_config_dir >/dev/null
50+
local perms
51+
perms="$(_get_perms "$CONDUIT_CONFIG_DIR")"
52+
[ "$perms" = "700" ]
53+
}
54+
55+
# ===========================================================================
56+
# Services registry file
57+
# ===========================================================================
58+
59+
@test "services.json created with 600" {
60+
registry_init
61+
local path
62+
path="$(registry_path)"
63+
local perms
64+
perms="$(_get_perms "$path")"
65+
[ "$perms" = "600" ]
66+
}
67+
68+
@test "services.json permissions preserved after add" {
69+
registry_init
70+
registry_add_service "hub" "127.0.0.1" "8090"
71+
local path
72+
path="$(registry_path)"
73+
[ -f "$path" ]
74+
jq empty "$path"
75+
}
76+
77+
@test "services.json remains valid after remove" {
78+
registry_init
79+
registry_add_service "hub" "127.0.0.1" "8090"
80+
registry_remove_service "hub"
81+
local path
82+
path="$(registry_path)"
83+
jq empty "$path"
84+
}
85+
86+
# ===========================================================================
87+
# Audit log file
88+
# ===========================================================================
89+
90+
@test "audit.log created with owner-only access" {
91+
set_safe_umask
92+
audit_log "test" "success" "msg"
93+
local perms
94+
perms="$(_get_perms "$CONDUIT_CONFIG_DIR/audit.log")"
95+
[ "$perms" = "600" ]
96+
}
97+
98+
# ===========================================================================
99+
# DNS hosts file
100+
# ===========================================================================
101+
102+
@test "conduit-hosts is not world-readable" {
103+
dns_add_entry "hub" "127.0.0.1"
104+
local hosts_file="$CONDUIT_CONFIG_DIR/conduit-hosts"
105+
[ -f "$hosts_file" ]
106+
local perms
107+
perms="$(_get_perms "$hosts_file")"
108+
# Initial touch sets 600, but mv of temp file may inherit umask
109+
[[ "$perms" =~ ^6[0-4][0-4]$ ]]
110+
}
111+
112+
# ===========================================================================
113+
# dnsmasq config
114+
# ===========================================================================
115+
116+
@test "dnsmasq.conf created with 600" {
117+
dns_generate_dnsmasq_conf
118+
local conf_file="$CONDUIT_CONFIG_DIR/dnsmasq.conf"
119+
local perms
120+
perms="$(_get_perms "$conf_file")"
121+
[ "$perms" = "600" ]
122+
}
123+
124+
# ===========================================================================
125+
# Route files
126+
# ===========================================================================
127+
128+
@test "route file created with 600" {
129+
route_add "hub" "127.0.0.1:8090"
130+
local route_file="$CONDUIT_CONFIG_DIR/routes/hub.caddy"
131+
local perms
132+
perms="$(_get_perms "$route_file")"
133+
[ "$perms" = "600" ]
134+
}
135+
136+
@test "routes directory created with 700" {
137+
route_add "hub" "127.0.0.1:8090"
138+
local perms
139+
perms="$(_get_perms "$CONDUIT_CONFIG_DIR/routes")"
140+
[ "$perms" = "700" ]
141+
}
142+
143+
# ===========================================================================
144+
# Caddyfile
145+
# ===========================================================================
146+
147+
@test "Caddyfile created with 600" {
148+
route_generate_caddyfile
149+
local caddyfile="$CONDUIT_CONFIG_DIR/Caddyfile"
150+
local perms
151+
perms="$(_get_perms "$caddyfile")"
152+
[ "$perms" = "600" ]
153+
}
154+
155+
# ===========================================================================
156+
# TLS certificate files
157+
# ===========================================================================
158+
159+
@test "CA key created with 600" {
160+
mkdir -p "$CONDUIT_CERTS_DIR"
161+
openssl req -x509 -newkey rsa:2048 -keyout "$CONDUIT_CERTS_DIR/root.key" \
162+
-out "$CONDUIT_CERTS_DIR/root.crt" -days 1 -nodes \
163+
-subj "/CN=Test CA" 2>/dev/null
164+
chmod 600 "$CONDUIT_CERTS_DIR/root.key"
165+
local perms
166+
perms="$(_get_perms "$CONDUIT_CERTS_DIR/root.key")"
167+
[ "$perms" = "600" ]
168+
}
169+
170+
@test "service cert key created with 600" {
171+
mkdir -p "$CONDUIT_CERTS_DIR"
172+
openssl req -x509 -newkey rsa:2048 -keyout "$CONDUIT_CERTS_DIR/root.key" \
173+
-out "$CONDUIT_CERTS_DIR/root.crt" -days 1 -nodes \
174+
-subj "/CN=Test CA" 2>/dev/null
175+
chmod 600 "$CONDUIT_CERTS_DIR/root.key"
176+
tls_issue_cert "hub"
177+
local perms
178+
perms="$(_get_perms "$CONDUIT_CERTS_DIR/hub/key.pem")"
179+
[ "$perms" = "600" ]
180+
}
181+
182+
@test "service cert directory created with 700" {
183+
mkdir -p "$CONDUIT_CERTS_DIR"
184+
openssl req -x509 -newkey rsa:2048 -keyout "$CONDUIT_CERTS_DIR/root.key" \
185+
-out "$CONDUIT_CERTS_DIR/root.crt" -days 1 -nodes \
186+
-subj "/CN=Test CA" 2>/dev/null
187+
chmod 600 "$CONDUIT_CERTS_DIR/root.key"
188+
tls_issue_cert "hub"
189+
local perms
190+
perms="$(_get_perms "$CONDUIT_CERTS_DIR/hub")"
191+
[ "$perms" = "700" ]
192+
}
193+
194+
# ===========================================================================
195+
# umask does not leak between operations
196+
# ===========================================================================
197+
198+
@test "umask 077 persists across operations" {
199+
set_safe_umask
200+
[ "$(umask)" = "0077" ]
201+
registry_init
202+
[ "$(umask)" = "0077" ]
203+
dns_add_entry "hub" "127.0.0.1"
204+
[ "$(umask)" = "0077" ]
205+
}
206+
207+
@test "files created after set_safe_umask have correct permissions" {
208+
set_safe_umask
209+
mkdir -p "$CONDUIT_CONFIG_DIR"
210+
touch "$CONDUIT_CONFIG_DIR/test_sensitive"
211+
local perms
212+
perms="$(_get_perms "$CONDUIT_CONFIG_DIR/test_sensitive")"
213+
[ "$perms" = "600" ]
214+
}
215+
216+
@test "directories created after set_safe_umask have correct permissions" {
217+
set_safe_umask
218+
mkdir -p "$CONDUIT_CONFIG_DIR/subdir"
219+
local perms
220+
perms="$(_get_perms "$CONDUIT_CONFIG_DIR/subdir")"
221+
[ "$perms" = "700" ]
222+
}

0 commit comments

Comments
 (0)