17 KiB
17 KiB
Audit Logging Feature - User Acceptance Test Plan
Test Environment Setup
Prerequisites:
- Fresh database via
node backend/setup.js, OR existing database migrated vianode backend/migrate-audit-log.js - Backend running on port 3001
- Frontend running on port 3000
- Three test accounts created:
admin/admin123(role: admin)editor1(role: editor)viewer1(role: viewer)
Verify setup: Run sqlite3 backend/cve_database.db ".tables" and confirm audit_logs is listed.
1. Database & Schema
| # | Test Case | Steps | Expected Result | Pass/Fail |
|---|---|---|---|---|
| 1.1 | Fresh install creates table | Run node setup.js on a new DB. Query SELECT sql FROM sqlite_master WHERE name='audit_logs' |
Table exists with columns: id, user_id, username, action, entity_type, entity_id, details, ip_address, created_at | |
| 1.2 | Indexes created | Query SELECT name FROM sqlite_master WHERE type='index' AND name LIKE 'idx_audit%' |
Four indexes: idx_audit_user_id, idx_audit_action, idx_audit_entity_type, idx_audit_created_at | |
| 1.3 | Migration is idempotent | Run node migrate-audit-log.js twice on the same DB |
Second run prints "already exists, nothing to do". No errors. Backup file created each run. | |
| 1.4 | Migration backs up DB | Run node migrate-audit-log.js |
Backup file cve_database_backup_<timestamp>.db created in backend directory |
|
| 1.5 | Setup summary updated | Run node setup.js |
Console output lists audit_logs in the tables line |
2. Authentication Audit Logging
| # | Test Case | Steps | Expected Result | Pass/Fail |
|---|---|---|---|---|
| 2.1 | Successful login logged | Log in as admin. Query SELECT * FROM audit_logs WHERE action='login' ORDER BY id DESC LIMIT 1 |
Row with user_id=admin's ID, username='admin', action='login', entity_type='auth', details contains {"role":"admin"}, ip_address populated |
|
| 2.2 | Failed login - wrong password | Attempt login with admin / wrongpass. Query audit_logs. |
Row with action='login_failed', username='admin', details contains {"reason":"invalid_password"} |
|
| 2.3 | Failed login - unknown user | Attempt login with nonexistent / anypass. Query audit_logs. |
Row with action='login_failed', user_id=NULL, username='nonexistent', details contains {"reason":"user_not_found"} |
|
| 2.4 | Failed login - disabled account | Disable a user account via admin, then attempt login as that user. Query audit_logs. | Row with action='login_failed', details contains {"reason":"account_disabled"} |
|
| 2.5 | Logout logged | Log in as admin, then log out. Query audit_logs. | Row with action='logout', entity_type='auth', username='admin' | |
| 2.6 | Login does not block on audit error | Verify login succeeds even if audit_logs table had issues (non-critical path) | Login response returns normally regardless of audit insert result |
3. CVE Operation Audit Logging
| # | Test Case | Steps | Expected Result | Pass/Fail |
|---|---|---|---|---|
| 3.1 | CVE create logged | Log in as editor or admin. Add a new CVE (e.g., CVE-2025-TEST-1 / Microsoft / Critical). Query audit_logs. | Row with action='cve_create', entity_type='cve', entity_id='CVE-2025-TEST-1', details contains {"vendor":"Microsoft","severity":"Critical"} |
|
| 3.2 | CVE status update logged | Update a CVE's status to "Addressed" via the API (PATCH /api/cves/CVE-2025-TEST-1/status). Query audit_logs. |
Row with action='cve_update_status', entity_id='CVE-2025-TEST-1', details contains {"status":"Addressed"} |
|
| 3.3 | CVE status update bug fix | Update a CVE's status. Verify the CVE record in the cves table. |
Status is correctly updated. No SQL error (the old vendor reference bug is fixed). |
|
| 3.4 | Audit captures acting user | Log in as editor1, create a CVE. Query audit_logs. |
username='editor1' on the cve_create row |
4. Document Operation Audit Logging
| # | Test Case | Steps | Expected Result | Pass/Fail |
|---|---|---|---|---|
| 4.1 | Document upload logged | Upload a document to a CVE via the UI. Query audit_logs. | Row with action='document_upload', entity_type='document', entity_id=CVE ID, details contains vendor, type, and filename | |
| 4.2 | Document delete logged | Delete a document (admin only) via the UI. Query audit_logs. | Row with action='document_delete', entity_type='document', entity_id=document DB ID, details contains file_path | |
| 4.3 | Upload captures file metadata | Upload a file named advisory.pdf of type advisory for vendor Cisco. Query audit_logs. |
details = {"vendor":"Cisco","type":"advisory","filename":"advisory.pdf"} |
5. User Management Audit Logging
| # | Test Case | Steps | Expected Result | Pass/Fail |
|---|---|---|---|---|
| 5.1 | User create logged | As admin, create a new user testuser with role viewer. Query audit_logs. |
Row with action='user_create', entity_type='user', entity_id=new user's ID, details contains {"created_username":"testuser","role":"viewer"} |
|
| 5.2 | User update logged | As admin, change testuser's role to editor. Query audit_logs. |
Row with action='user_update', entity_id=testuser's ID, details contains {"role":"editor"} |
|
| 5.3 | User update - password change | As admin, change testuser's password. Query audit_logs. |
details contains {"password_changed":true} (password itself is NOT logged) |
|
| 5.4 | User update - multiple fields | Change username and role at the same time. Query audit_logs. | details contains both changed fields | |
| 5.5 | User delete logged | As admin, delete testuser. Query audit_logs. |
Row with action='user_delete', details contains {"deleted_username":"testuser"} |
|
| 5.6 | User deactivation logged | As admin, set a user's is_active to false. Query audit_logs. | Row with action='user_update', details contains {"is_active":false} |
|
| 5.7 | Self-delete prevented, no log | As admin, attempt to delete your own account. Query audit_logs. | 400 error returned. NO audit_log entry created for the attempt. |
6. API Access Control
| # | Test Case | Steps | Expected Result | Pass/Fail |
|---|---|---|---|---|
| 6.1 | Admin can query audit logs | Log in as admin. GET /api/audit-logs. |
200 response with logs array and pagination object | |
| 6.2 | Editor denied audit logs | Log in as editor. GET /api/audit-logs. |
403 response with {"error":"Insufficient permissions"} |
|
| 6.3 | Viewer denied audit logs | Log in as viewer. GET /api/audit-logs. |
403 response | |
| 6.4 | Unauthenticated denied | Without a session cookie, GET /api/audit-logs. |
401 response | |
| 6.5 | Admin can get actions list | GET /api/audit-logs/actions as admin. |
200 response with array of distinct action strings | |
| 6.6 | Non-admin denied actions list | GET /api/audit-logs/actions as editor. |
403 response |
7. API Filtering & Pagination
| # | Test Case | Steps | Expected Result | Pass/Fail |
|---|---|---|---|---|
| 7.1 | Default pagination | GET /api/audit-logs (no params). |
Returns up to 25 entries, page=1, correct total count and totalPages | |
| 7.2 | Custom page size | GET /api/audit-logs?limit=5. |
Returns exactly 5 entries (if >= 5 exist). Pagination reflects limit=5. | |
| 7.3 | Page size capped at 100 | GET /api/audit-logs?limit=999. |
Returns at most 100 entries per page | |
| 7.4 | Navigate to page 2 | GET /api/audit-logs?page=2&limit=5. |
Returns entries 6-10 (offset=5). Entries differ from page 1. | |
| 7.5 | Filter by username | GET /api/audit-logs?user=admin. |
Only entries where username contains "admin" | |
| 7.6 | Partial username match | GET /api/audit-logs?user=adm. |
Matches "admin" (LIKE search) | |
| 7.7 | Filter by action | GET /api/audit-logs?action=login. |
Only entries with action='login' (exact match) | |
| 7.8 | Filter by entity type | GET /api/audit-logs?entityType=auth. |
Only auth-related entries | |
| 7.9 | Filter by date range | GET /api/audit-logs?startDate=2025-01-01&endDate=2025-12-31. |
Only entries within the date range (inclusive) | |
| 7.10 | Combined filters | GET /api/audit-logs?user=admin&action=login&entityType=auth. |
Only entries matching ALL filters simultaneously | |
| 7.11 | Empty result set | GET /api/audit-logs?user=nonexistentuser. |
{"logs":[],"pagination":{"page":1,"limit":25,"total":0,"totalPages":0}} |
|
| 7.12 | Ordering | Query audit logs without filters. | Entries ordered by created_at DESC (newest first) |
8. Frontend - Audit Log Menu Access
| # | Test Case | Steps | Expected Result | Pass/Fail |
|---|---|---|---|---|
| 8.1 | Admin sees Audit Log menu item | Log in as admin. Click user avatar to open dropdown menu. | "Audit Log" option visible with clock icon, positioned between "Manage Users" and "Sign Out" | |
| 8.2 | Editor does NOT see Audit Log | Log in as editor. Click user avatar. | No "Audit Log" or "Manage Users" options visible | |
| 8.3 | Viewer does NOT see Audit Log | Log in as viewer. Click user avatar. | No "Audit Log" or "Manage Users" options visible | |
| 8.4 | Clicking Audit Log opens modal | As admin, click "Audit Log" in the menu. | Modal overlay appears with audit log table. Menu dropdown closes. | |
| 8.5 | Menu closes on outside click | Open the user menu, then click outside the dropdown. | Dropdown closes |
9. Frontend - Audit Log Modal
| # | Test Case | Steps | Expected Result | Pass/Fail |
|---|---|---|---|---|
| 9.1 | Modal displays header | Open the Audit Log modal. | Title "Audit Log", subtitle "Track all user actions across the system", X close button visible | |
| 9.2 | Close button works | Click the X button on the modal. | Modal closes, returns to dashboard | |
| 9.3 | Loading state shown | Open the modal (observe briefly). | Spinner with "Loading audit logs..." appears before data loads | |
| 9.4 | Table columns correct | Open modal with data present. | Six columns visible: Time, User, Action, Entity, Details, IP Address | |
| 9.5 | Time formatting | Check the Time column. | Dates display in local format (e.g., "1/29/2026, 3:45:00 PM"), not raw ISO strings | |
| 9.6 | Action badges color-coded | View entries with different action types. | login=green, logout=gray, login_failed=red, cve_create=blue, cve_update_status=yellow, document_upload=purple, document_delete=red, user_create=blue, user_update=yellow, user_delete=red | |
| 9.7 | Entity column format | View entries with entity_type and entity_id. | Shows "cve CVE-2025-TEST-1" or "auth" (no ID for auth entries) | |
| 9.8 | Details column formatting | View an entry with JSON details. | Displays "key: value, key: value" format, not raw JSON | |
| 9.9 | Details truncation | View entry with long details. | Text truncated with ellipsis. Full text visible on hover (title attribute). | |
| 9.10 | IP address display | View entries. | IP addresses shown in monospace font. Null IPs show "-" | |
| 9.11 | Empty state | Apply filters that return no results. | "No audit log entries found." message displayed | |
| 9.12 | Error state | (Simulate: stop backend while modal is open, then apply filters.) | Error icon with error message displayed |
10. Frontend - Filters
| # | Test Case | Steps | Expected Result | Pass/Fail |
|---|---|---|---|---|
| 10.1 | Username filter | Type "admin" in username field, click Apply Filters. | Only entries with "admin" in username shown | |
| 10.2 | Action dropdown populated | Click the Action dropdown. | Lists all distinct actions present in the database (from /api/audit-logs/actions) |
|
| 10.3 | Action filter | Select "login" from Action dropdown, click Apply. | Only login entries shown | |
| 10.4 | Entity type dropdown | Click the Entity Type dropdown. | Lists: auth, cve, document, user | |
| 10.5 | Entity type filter | Select "cve", click Apply. | Only CVE-related entries shown | |
| 10.6 | Date range filter | Set start date to today, set end date to today, click Apply. | Only entries from today shown | |
| 10.7 | Combined filters | Set username="admin", action="login", click Apply. | Only admin login entries shown | |
| 10.8 | Reset button | Set multiple filters, click Reset. | All filter fields cleared. (Note: table does not auto-refresh until Apply is clicked again.) | |
| 10.9 | Apply after reset | Click Reset, then click Apply Filters. | Full unfiltered results shown | |
| 10.10 | Filter resets to page 1 | Navigate to page 2, then apply a filter. | Results start from page 1 |
11. Frontend - Pagination
| # | Test Case | Steps | Expected Result | Pass/Fail |
|---|---|---|---|---|
| 11.1 | Pagination info displayed | Open modal with >25 entries. | Shows "Showing 1 - 25 of N entries" and "Page 1 of X" | |
| 11.2 | Next page button | Click the right chevron. | Page advances. Entry range updates. "Page 2 of X" shown. | |
| 11.3 | Previous page button | Navigate to page 2, then click left chevron. | Returns to page 1 | |
| 11.4 | First page - prev disabled | On page 1, check left chevron. | Button is disabled (grayed out, not clickable) | |
| 11.5 | Last page - next disabled | Navigate to the last page. | Right chevron is disabled | |
| 11.6 | Pagination hidden for few entries | Open modal with <= 25 total entries. | No pagination controls shown (totalPages <= 1) | |
| 11.7 | Entry count accuracy | Compare "Showing X - Y of Z" with actual table rows. | Row count matches Y - X + 1. Total Z matches database count. |
12. Fire-and-Forget Behavior
| # | Test Case | Steps | Expected Result | Pass/Fail |
|---|---|---|---|---|
| 12.1 | Audit failure does not break login | (Requires code-level test or corrupting audit_logs table temporarily.) Rename audit_logs table, attempt login. | Login succeeds. Console shows "Audit log error:" message. | |
| 12.2 | Audit failure does not break CVE create | With corrupted audit table, create a CVE. | CVE created successfully. Error logged to console only. | |
| 12.3 | Response not delayed by audit | Create a CVE and observe response time. | Response returns immediately; audit insert is non-blocking. |
13. Data Integrity
| # | Test Case | Steps | Expected Result | Pass/Fail |
|---|---|---|---|---|
| 13.1 | Audit survives user deletion | Create user, perform actions, delete user. Query audit_logs for that username. | Audit entries remain with the username preserved (denormalized). No foreign key cascade. | |
| 13.2 | Details stored as valid JSON | Query SELECT details FROM audit_logs WHERE details IS NOT NULL LIMIT 5. Parse each. |
All non-null details values are valid JSON strings | |
| 13.3 | IP address captured | Query entries created via browser. | ip_address field contains the client IP (e.g., ::1 for localhost or 127.0.0.1) |
|
| 13.4 | Timestamps auto-populated | Query entries without explicitly setting created_at. | All rows have a created_at value, not NULL | |
| 13.5 | Null entity_id for auth actions | Query SELECT * FROM audit_logs WHERE entity_type='auth'. |
entity_id is NULL for login/logout/login_failed entries |
14. End-to-End Workflow
| # | Test Case | Steps | Expected Result | Pass/Fail |
|---|---|---|---|---|
| 14.1 | Full user lifecycle | 1. Admin logs in 2. Creates user "testuser2" 3. testuser2 logs in 4. testuser2 creates a CVE 5. Admin updates testuser2's role 6. Admin deletes testuser2 7. Open Audit Log and review | All 6 actions visible in the audit log in reverse chronological order. Each entry has correct user, action, entity, and details. | |
| 14.2 | Filter down to one user's actions | Perform test 14.1, then filter by username="testuser2". | Only testuser2's own actions shown (login, cve_create). Admin actions on testuser2 show admin as the actor. | |
| 14.3 | Security audit trail | Attempt 3 failed logins with wrong password, then succeed. Open Audit Log, filter action="login_failed". | All 3 failed attempts visible with timestamps and IP addresses. Useful for detecting brute force. |
Test Summary
| Section | Tests | Description |
|---|---|---|
| 1. Database & Schema | 5 | Table creation, indexes, migration idempotency |
| 2. Auth Logging | 6 | Login success/failure variants, logout |
| 3. CVE Logging | 4 | Create, status update, bug fix verification |
| 4. Document Logging | 3 | Upload, delete, metadata capture |
| 5. User Mgmt Logging | 7 | Create, update, delete, edge cases |
| 6. API Access Control | 6 | Admin-only enforcement on all endpoints |
| 7. API Filtering | 12 | Pagination, filters, combined queries |
| 8. Menu Access | 5 | Role-based UI visibility |
| 9. Modal Display | 12 | Table rendering, formatting, states |
| 10. Frontend Filters | 10 | Filter UI interaction and behavior |
| 11. Pagination UI | 7 | Navigation, boundary conditions |
| 12. Fire-and-Forget | 3 | Non-blocking audit behavior |
| 13. Data Integrity | 5 | Denormalization, JSON, timestamps |
| 14. End-to-End | 3 | Full workflow validation |
| Total | 88 |