diff --git a/JOURNAL.md b/JOURNAL.md index 8c338d0..a6e0c29 100644 --- a/JOURNAL.md +++ b/JOURNAL.md @@ -6,6 +6,60 @@ --- +## Entry 2026-05-07 (Session 7): Full Project Audit & Fix + +### Context +User requested full project re-orientation. Deep audit of all hooks, tests, docs, and code against PRD. + +### Audit Findings + +#### Test Fixes (11 → 0 failures) +- **Root cause**: `grep 'iso)'` regex matched `test:iso)` before `iso|iso:demo)` due to `|` being regex OR +- **Fix**: Changed all affected greps to use `grep -F 'iso|iso:demo)'` (literal string match) +- **Files fixed**: `tests/unit/build-iso_comprehensive_test.bats` (8), `tests/unit/run_comprehensive_test.bats` (2), `tests/unit/run_test.bats` (1) +- Also increased grep context from `-A 5` to `-A 15` for FDE reference tests (content is 9+ lines into iso block) + +#### Stale Files Deleted +- `test-iso.sh` - merged into run.sh in Session 4, was still in repo +- `verify.sh` - orphaned, never referenced, duplicated `run.sh test` + +#### Documentation Fixes +- `docs/TEST-COVERAGE.md` line 23: Updated stale "delegates to test-iso.sh" reference +- `docs/COMPLIANCE.md` lines 73-75: Fixed wrong test filenames (firewall_test → firewall-setup_test, etc.) + +#### Uncommitted run.sh Changes (carried over from last session) +- Added `CACHE_VOLUME` for NVMe build cache via Docker volume +- Added `clean:cache` and `cache` commands +- Build cache restore/save around `lb build` for faster iteration +- Cache preserves bootstrap + package downloads between builds (~5 min saved) + +### Known Issues Identified (not fixed this session - deferred) + +| Issue | Severity | Notes | +|-------|----------|-------| +| `firewall-setup.sh` live hook blocks all outbound | High | Static default-deny, no WireGuard allow; live system has no network | +| `disable-package-management.sh` destroys dpkg db | High | `rm -rf /var/lib/dpkg/*` breaks anything querying installed packages | +| `encryption-validation.sh` inverted conditional | Medium | motd file created only if it already exists (backwards) | +| `kernel.exec-shield = 1` in kernel-hardening.sh | Low | Red Hat-specific, doesn't exist on Debian | +| `src/build-iso.sh` $VERSION undefined | Medium | Build always reports failure even on success | +| NTFS mount needs ntfs-3g but not in package list | Low | USB automount will fail for NTFS drives | +| `audispd-plugins` may not exist in Debian 13 | Low | Deprecated/removed from trixie | + +### Test Results +``` +786 tests, 0 failures, 16 VM skips +Lint: 0 shellcheck warnings +ISO: 824 MB, built 2026-05-01 +``` + +### Verification +```bash +./run.sh lint # Zero warnings +./run.sh test # 786 pass, 0 fail, 16 skip (VM) +``` + +--- + ## Entry 2026-02-20 (Session 6): Security Audit Findings Implementation ### Context diff --git a/STATUS.md b/STATUS.md index fe371c3..a6d59c1 100644 --- a/STATUS.md +++ b/STATUS.md @@ -1,6 +1,6 @@ # KNEL-Football Project Status Report -> **Last Updated**: 2026-05-01 11:20 CDT +> **Last Updated**: 2026-05-07 (Session 7 - Full Audit) > **Maintained By**: AI Agent (Crush) > **Purpose**: Quick-glance status for project manager diff --git a/docs/COMPLIANCE.md b/docs/COMPLIANCE.md index a42b7b6..4673e70 100644 --- a/docs/COMPLIANCE.md +++ b/docs/COMPLIANCE.md @@ -70,9 +70,9 @@ This document maps security compliance requirements to implementation components | Test Type | Test File | Validation Target | Coverage | |-----------|------------|-----------------|----------| -| Unit Tests | `tests/unit/firewall_test.bats` | Firewall configuration parsing | 🔧 | -| Unit Tests | `tests/unit/security_test.bats` | Security hardening functions | 🔧 | -| Unit Tests | `tests/unit/build_test.bats` | Build process functions | 🔧 | +| Unit Tests | `tests/unit/firewall-setup_test.bats` | Firewall configuration parsing | 🔧 | +| Unit Tests | `tests/unit/security-hardening_test.bats` | Security hardening functions | 🔧 | +| Unit Tests | `tests/unit/build-iso_comprehensive_test.bats` | Build process functions | 🔧 | | Integration Tests | `tests/integration/config_test.bats` | Configuration file validation | 🌐 | | Security Tests | `tests/security/compliance_test.bats` | Compliance verification | 🔒 | diff --git a/docs/TEST-COVERAGE.md b/docs/TEST-COVERAGE.md index 9d7e1c0..b5914a7 100644 --- a/docs/TEST-COVERAGE.md +++ b/docs/TEST-COVERAGE.md @@ -20,7 +20,7 @@ - run.sh exists and is executable - run.sh shows usage with help command - run.sh creates output and build directories -- run.sh test:iso delegates to test-iso.sh +- run.sh test:iso provides VM testing commands - run.sh clean removes artifacts **Lines Covered**: Basic validation and command dispatch diff --git a/run.sh b/run.sh index 1cb543c..720b714 100755 --- a/run.sh +++ b/run.sh @@ -13,6 +13,7 @@ readonly DOCKER_IMAGE="knel-football-dev:latest" readonly OUTPUT_DIR="${SCRIPT_DIR}/output" readonly BUILD_DIR="${SCRIPT_DIR}/tmp" readonly BUILD_LOG="/tmp/knel-iso-build.log" +readonly CACHE_VOLUME="knel-football-cache" # VM Testing Configuration (system libvirt for virt-manager visibility, /tmp for no sudo) readonly ISO_PATH="${SCRIPT_DIR}/output/knel-football-secure.iso" @@ -854,6 +855,8 @@ Build Commands: iso:demo Build demo/CI ISO (hardcoded test credentials, serial console) monitor [secs] Monitor build progress (default: check every 180s) clean Clean build artifacts + clean:cache Remove NVMe build cache (force full rebuild) + cache Show build cache status Test Commands: test Run all tests @@ -966,6 +969,18 @@ main() { rm -rf "${OUTPUT_DIR:?}"/* rm -rf "${BUILD_DIR:?}"/* ;; + clean:cache) + echo "Removing NVMe build cache (Docker volume: ${CACHE_VOLUME})..." + docker volume rm "${CACHE_VOLUME}" 2>/dev/null || echo "Cache volume not found" + ;; + cache) + echo "Build cache status (Docker volume: ${CACHE_VOLUME}):" + if docker volume inspect "${CACHE_VOLUME}" &>/dev/null; then + docker run --rm -v "${CACHE_VOLUME}:/cache" alpine sh -c 'echo "Size: $(du -sh /cache 2>/dev/null | cut -f1)" && ls -la /cache/' + else + echo "No cache volume exists (will be created on next build)" + fi + ;; validate) echo "Running ISO validation..." "${SCRIPT_DIR}/scripts/validate-iso.sh" @@ -1003,6 +1018,7 @@ main() { --user root \ -v "${SCRIPT_DIR}:/workspace:ro" \ -v "${OUTPUT_DIR}:/output" \ + -v "${CACHE_VOLUME}:/cache" \ -e TZ="America/Chicago" \ -e DEBIAN_FRONTEND="noninteractive" \ -e LC_ALL="C" \ @@ -1044,6 +1060,17 @@ if [ "${KNEL_BUILD_MODE}" = "demo" ]; then fi fi && +# Restore build cache from NVMe Docker volume +# Preserves bootstrap + package downloads between builds (~5 min saved) +if [ -d /cache/bootstrap ]; then + echo "Restoring build cache from NVMe..." && + mkdir -p ./cache && + cp -a /cache/* ./cache/ && + echo "Cache restored (bootstrap + packages)" +else + echo "No build cache found (first build or cache cleared)" +fi && + # Create Secure Boot binary hook inline echo "Creating Secure Boot hook..." && mkdir -p config/hooks/binary && @@ -1209,6 +1236,13 @@ chmod +x config/hooks/binary/0200-secureboot-uki.hook && echo "Starting ISO build..." && timeout 3600 lb build && + +# Save build cache to NVMe Docker volume for next rebuild +echo "Saving build cache to NVMe..." && +mkdir -p /cache && +cp -a ./cache/* /cache/ 2>/dev/null || true && +echo "Cache saved (bootstrap + packages)" && + ISO_FILE=$(find . -name "*.iso" -type f | head -1) && if [ -n "$ISO_FILE" ]; then echo "ISO created: $ISO_FILE" diff --git a/test-iso.sh b/test-iso.sh deleted file mode 100755 index 3e5ea3b..0000000 --- a/test-iso.sh +++ /dev/null @@ -1,298 +0,0 @@ -#!/bin/bash -# KNEL-Football ISO Testing Script -# Creates and boots a VM using libvirt/virsh to test the ISO -# Runs on HOST system (not inside Docker) -# Copyright © 2026 Known Element Enterprises LLC -# License: GNU Affero General Public License v3.0 only - -set -euo pipefail - -# Configuration variables -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -readonly SCRIPT_DIR -_vm_name="knel-test-$(date +%Y%m%d-%H%M%S)" -readonly VM_NAME="$_vm_name" -readonly ISO_PATH="${SCRIPT_DIR}/output/knel-football-secure-v1.0.0.iso" -readonly DISK_SIZE="20G" -readonly DISK_PATH="${SCRIPT_DIR}/tmp/${VM_NAME}.qcow2" -readonly RAM="4096" # 4GB RAM -readonly VCPUS="2" -# shellcheck disable=SC2034 -readonly NETWORK="none" -readonly CPU_MODEL="host-model" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Logging functions -log_info() { - echo -e "${GREEN}[INFO]${NC} $*" -} - -log_warn() { - echo -e "${YELLOW}[WARN]${NC} $*" -} - -log_error() { - echo -e "${RED}[ERROR]${NC} $*" >&2 -} - -# Usage information -usage() { - cat < /dev/null; then - log_error "virsh not found. Please install libvirt:" - echo " Ubuntu/Debian: sudo apt install libvirt-daemon-system virtinst" - echo " RHEL/CentOS: sudo yum install libvirt virt-install" - exit 1 - fi - - # Check if libvirtd is running - if ! virsh list &> /dev/null; then - log_error "libvirtd is not running. Please start it:" - echo " sudo systemctl start libvirtd" - exit 1 - fi - - # Check if ISO exists - if [[ ! -f "${ISO_PATH}" ]]; then - log_error "ISO not found at: ${ISO_PATH}" - log_warn "Please build the ISO first using: ./run.sh iso" - exit 1 - fi - - log_info "Prerequisites check passed" -} - -# Create VM -create_vm() { - log_info "Creating VM: ${VM_NAME}" - log_info "ISO: ${ISO_PATH}" - log_info "Disk: ${DISK_SIZE} (${DISK_PATH})" - log_info "RAM: ${RAM} MB, VCPUs: ${VCPUS}" - - # Create disk image - log_info "Creating disk image..." - qemu-img create -f qcow2 "${DISK_PATH}" "${DISK_SIZE}" - - # Create VM definition - log_info "Defining VM..." - virt-install \ - --name "${VM_NAME}" \ - --memory "${RAM}" \ - --vcpus "${VCPUS}" \ - --cpu "${CPU_MODEL}" \ - --disk path="${DISK_PATH}",format=qcow2,bus=virtio \ - --cdrom "${ISO_PATH}" \ - --graphics vnc \ - --noautoconsole \ - --os-variant debian12 \ - --boot cdrom,hd \ - --metadata description="KNEL-Football Secure OS Test VM" - - log_info "VM created and started successfully" - log_info "Connect to console with: $0 console ${VM_NAME}" -} - -# Start VM -start_vm() { - log_info "Starting VM: ${VM_NAME}" - virsh start "${VM_NAME}" - log_info "VM started" -} - -# Stop VM -stop_vm() { - log_info "Stopping VM: ${VM_NAME}" - virsh shutdown "${VM_NAME}" - log_info "VM shutdown signal sent" -} - -# Connect to console -connect_console() { - log_info "Connecting to VM console: ${VM_NAME}" - log_info "Press Ctrl+] to exit console" - virsh console "${VM_NAME}" -} - -# Destroy VM -destroy_vm() { - log_warn "This will permanently remove VM: ${VM_NAME}" - - # Check if VM is running - if virsh domstate "${VM_NAME}" 2>/dev/null | grep -q "running"; then - log_info "Stopping VM..." - virsh destroy "${VM_NAME}" - fi - - # Undefine VM - log_info "Undefining VM..." - virsh undefine "${VM_NAME}" - - # Remove disk - if [[ -f "${DISK_PATH}" ]]; then - log_info "Removing disk: ${DISK_PATH}" - rm -f "${DISK_PATH}" - fi - - log_info "VM destroyed" -} - -# Show VM status -show_status() { - log_info "VM Status: ${VM_NAME}" - - if ! virsh dominfo "${VM_NAME}" 2>/dev/null; then - log_error "VM not found: ${VM_NAME}" - exit 1 - fi - - echo "" - virsh dominfo "${VM_NAME}" - echo "" - log_info "VM interfaces:" - virsh domiflist "${VM_NAME}" || log_warn "No interface information available" -} - -# List all test VMs -list_vms() { - log_info "Listing KNEL-Football test VMs..." - echo "" - - virsh list --all | grep "knel-test-" || log_warn "No test VMs found" - - echo "" - log_info "Disk images:" - ls -lh "${SCRIPT_DIR}"/tmp/knel-test-*.qcow2 2>/dev/null || log_warn "No test disk images found" -} - -# Parse command line arguments -COMMAND="" -CUSTOM_NAME="" - -while [[ $# -gt 0 ]]; do - case $1 in - -h | --help) - usage - exit 0 - ;; - -n | --name) - CUSTOM_NAME="$2" - shift 2 - ;; - -r | --ram) - RAM="$2" - shift 2 - ;; - -c | --cpus) - VCPUS="$2" - shift 2 - ;; - -d | --disk) - DISK_SIZE="$2" - shift 2 - ;; - create | start | stop | console | destroy | status | list) - COMMAND="$1" - shift - ;; - *) - if [[ -z "${COMMAND}" ]]; then - log_error "Unknown option: $1" - usage - exit 1 - else - # VM name for commands that take it - CUSTOM_NAME="$1" - shift - fi - ;; - esac -done - -# Use custom name if provided -if [[ -n "${CUSTOM_NAME}" && "${COMMAND}" != "create" ]]; then - VM_NAME="${CUSTOM_NAME}" -fi - -# Set default command -COMMAND="${COMMAND:-help}" - -# Main execution -main() { - case "${COMMAND}" in - create) - check_prerequisites - create_vm - ;; - start) - check_prerequisites - start_vm - ;; - stop) - check_prerequisites - stop_vm - ;; - console) - connect_console - ;; - destroy) - destroy_vm - ;; - status) - show_status - ;; - list) - list_vms - ;; - help|*) - usage - ;; - esac -} - -main diff --git a/tests/unit/build-iso_comprehensive_test.bats b/tests/unit/build-iso_comprehensive_test.bats index 9e8d732..9de6e13 100644 --- a/tests/unit/build-iso_comprehensive_test.bats +++ b/tests/unit/build-iso_comprehensive_test.bats @@ -237,31 +237,31 @@ # ============================================================================= @test "run.sh iso uses docker run" { - grep -A 100 'iso)' /workspace/run.sh | grep -q "docker run" + grep -A 100 -F 'iso|iso:demo)' /workspace/run.sh | grep -q "docker run" } @test "run.sh iso runs as root in container" { - grep -A 100 'iso)' /workspace/run.sh | grep -q "\-\-user root" + grep -A 100 -F 'iso|iso:demo)' /workspace/run.sh | grep -q "\-\-user root" } @test "run.sh iso uses privileged mode for loop devices" { - grep -A 100 'iso)' /workspace/run.sh | grep -q "\-\-privileged" + grep -A 100 -F 'iso|iso:demo)' /workspace/run.sh | grep -q "\-\-privileged" } @test "run.sh iso mounts workspace read-only" { - grep -A 100 'iso)' /workspace/run.sh | grep -q "/workspace:ro" + grep -A 100 -F 'iso|iso:demo)' /workspace/run.sh | grep -q "/workspace:ro" } @test "run.sh iso mounts output directory" { - grep -A 100 'iso)' /workspace/run.sh | grep -q "/output" + grep -A 100 -F 'iso|iso:demo)' /workspace/run.sh | grep -q "/output" } @test "run.sh iso sets timezone" { - grep -A 100 'iso)' /workspace/run.sh | grep -q "TZ=" + grep -A 100 -F 'iso|iso:demo)' /workspace/run.sh | grep -q "TZ=" } @test "run.sh iso sets noninteractive frontend" { - grep -A 100 'iso)' /workspace/run.sh | grep -q "DEBIAN_FRONTEND" + grep -A 100 -F 'iso|iso:demo)' /workspace/run.sh | grep -q "DEBIAN_FRONTEND" } # ============================================================================= @@ -285,7 +285,7 @@ # ============================================================================= @test "run.sh iso references host FDE" { - grep -A 10 'iso)' /workspace/run.sh | grep -qi "fde\|encryption" + grep -A 10 -F 'iso|iso:demo)' /workspace/run.sh | grep -qi "fde\|encryption" } @test "run.sh has check_host_fde function defined" { diff --git a/tests/unit/run_comprehensive_test.bats b/tests/unit/run_comprehensive_test.bats index 38f7a9d..d76efbc 100644 --- a/tests/unit/run_comprehensive_test.bats +++ b/tests/unit/run_comprehensive_test.bats @@ -182,7 +182,7 @@ # ============================================================================= @test "run.sh iso command uses Docker" { - grep -A 50 'iso)' /workspace/run.sh | grep -q "docker run" + grep -A 50 -F 'iso|iso:demo)' /workspace/run.sh | grep -q "docker run" } @test "run.sh test command uses Docker" { @@ -307,7 +307,7 @@ } @test "run.sh iso command references host FDE" { - grep -A 5 'iso)' /workspace/run.sh | grep -qi "fde\|encryption" + grep -A 15 -F 'iso|iso:demo)' /workspace/run.sh | grep -qi "fde\|encryption" } @test "run.sh provides clear FDE error message" { diff --git a/tests/unit/run_test.bats b/tests/unit/run_test.bats index 02da5c4..0e370e2 100644 --- a/tests/unit/run_test.bats +++ b/tests/unit/run_test.bats @@ -132,7 +132,7 @@ } @test "run.sh references host FDE for iso command" { - grep -A 5 "iso)" /workspace/run.sh | grep -qi "fde\|encryption" + grep -A 15 -F 'iso|iso:demo)' /workspace/run.sh | grep -qi "fde\|encryption" } @test "run.sh has check_host_fde function" { diff --git a/verify.sh b/verify.sh deleted file mode 100755 index 45a97e2..0000000 --- a/verify.sh +++ /dev/null @@ -1,144 +0,0 @@ -#!/bin/bash -# Comprehensive project verification script -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -cd "$SCRIPT_DIR" - -PASS=0 -FAIL=0 -RESULTS="" - -log_pass() { PASS=$((PASS+1)); RESULTS+=" [PASS] $1\n"; } -log_fail() { FAIL=$((FAIL+1)); RESULTS+=" [FAIL] $1\n"; } -log_info() { RESULTS+=" [INFO] $1\n"; } - -echo "=== KNEL-Football Project Verification ===" -echo "" - -# 1. Docker available -echo "Phase 1: Environment checks..." -if docker info &>/dev/null; then - log_pass "Docker daemon running" -else - log_fail "Docker daemon not running" -fi - -# 2. Docker image exists -if docker images --format '{{.Repository}}' | grep -q 'knel-football-dev'; then - log_pass "Docker build image exists" -else - log_fail "Docker build image missing (run: ./run.sh build)" -fi - -# 3. Lint (warning level only) -echo "Phase 2: Lint checks..." -LINT_OUTPUT=$(docker run --rm -v "$SCRIPT_DIR":/workspace knel-football-dev:latest bash -c \ - 'shellcheck --severity=warning /workspace/src/*.sh /workspace/config/hooks/installed/*.sh /workspace/config/hooks/live/*.sh' 2>&1 || true) -if [ -z "$LINT_OUTPUT" ]; then - log_pass "Shellcheck (warning level) clean" -else - log_fail "Shellcheck warnings found:" - echo "$LINT_OUTPUT" | while read -r line; do log_info " $line"; done -fi - -# 4. Run full test suite -echo "Phase 3: Test suite..." -TEST_OUTPUT=$(./run.sh test 2>&1) -TEST_COUNT=$(echo "$TEST_OUTPUT" | grep -c "^ok" || true) -TEST_FAIL=$(echo "$TEST_OUTPUT" | grep -c "^not ok" || true) -if [ "$TEST_FAIL" -eq 0 ]; then - log_pass "All $TEST_COUNT tests passing" -else - log_fail "$TEST_FAIL tests failing out of $((TEST_COUNT+TEST_FAIL))" - echo "$TEST_OUTPUT" | grep "^not ok" | while read -r line; do log_info " $line"; done -fi - -# 5. ISO artifact check -echo "Phase 4: ISO artifact..." -if ls output/*.iso &>/dev/null; then - ISO_FILE=$(find output -name '*.iso' -type f | head -1) - ISO_SIZE=$(du -h "$ISO_FILE" | cut -f1) - log_pass "ISO exists: $ISO_FILE ($ISO_SIZE)" - # Check checksums - if [ -f "${ISO_FILE}.sha256" ]; then - log_pass "SHA256 checksum file exists" - else - log_fail "SHA256 checksum file missing" - fi -else - log_info "No ISO artifact found (build with: ./run.sh iso)" -fi - -# 6. VM testing capability -echo "Phase 5: VM test capability..." -if command -v virsh &>/dev/null; then - log_pass "virsh available for VM testing" - if virsh list --all &>/dev/null; then - log_pass "libvirt daemon accessible" - # Check for any existing test VMs - EXISTING_VMS=$(virsh list --all --name 2>/dev/null | grep -c 'knel-test' || true) - if [ "$EXISTING_VMS" -gt 0 ]; then - log_info "Found $EXISTING_VMS existing test VM(s)" - else - log_info "No existing test VMs" - fi - else - log_info "libvirt daemon not accessible (may need sudo/libvirtd group)" - fi -else - log_info "virsh not installed - VM testing not available on this host" -fi - -# 7. Git status -echo "Phase 6: Git status..." -if git diff --quiet && git diff --cached --quiet; then - log_pass "Working tree clean" -else - log_fail "Uncommitted changes present" -fi -AHEAD=$(git rev-list --count '@{u}..HEAD' 2>/dev/null || echo "?") -log_info "Branch is $AHEAD commit(s) ahead of origin/main" - -# 8. Source file integrity -echo "Phase 7: Source file integrity..." -for f in src/build-iso.sh src/security-hardening.sh src/firewall-setup.sh; do - if [ -f "$f" ] && [ -x "$f" ]; then - log_pass "$f exists and is executable" - else - log_fail "$f missing or not executable" - fi -done - -# 9. Config file integrity -echo "Phase 8: Config integrity..." -for f in config/preseed.cfg config/hooks/installed/encryption-setup.sh config/hooks/installed/encryption-validation.sh config/hooks/live/security-hardening.sh config/hooks/live/firewall-setup.sh; do - if [ -f "$f" ]; then - log_pass "$f exists" - else - log_fail "$f missing" - fi -done - -# 10. Check for Unicode characters that break shellcheck -echo "Phase 9: Unicode check..." -UNICODE_FILES=$(grep -rl '✓\|✗\|✔\|✘' src/ config/ 2>/dev/null || true) -if [ -z "$UNICODE_FILES" ]; then - log_pass "No problematic Unicode characters in shell scripts" -else - log_fail "Unicode characters found in: $UNICODE_FILES" -fi - -# Summary -echo "" -echo "=== RESULTS ===" -echo -e "$RESULTS" -echo "" -echo "Summary: $PASS passed, $FAIL failed" -if [ "$FAIL" -gt 0 ]; then - echo "STATUS: ACTION REQUIRED" - exit 1 -else - echo "STATUS: ALL GOOD" - exit 0 -fi