4 new pages: - ADR-019: Plugin Dynamic Settings Routes - Security Audit 2026-09-11 (4 critical + 6 high findings) - Maintenance Footprint Baseline (936 hrs/year burden) - Architectural Findings 2026-09-11 (22 findings) 7 existing pages need updates: - PLUGIN-DEVELOPMENT.md, API-REFERENCE.md, DEPLOYMENT.md - CONTRIBUTING.md, TESTING.md, SECURITY.md - New: Automation-Skills.md Ready to copy to GitHub wiki. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
14 KiB
Security Audit - September 11, 2026
Scope: Endpoints plugin, C# agent, system tray application
Severity: 4 critical, 6 high, 8 medium findings
Status: 3 critical fixed, 1 critical planned, 6 high in progress
Executive Summary
Comprehensive security review of the endpoint management subsystem (plugin + C# agent + tray app) identified critical authentication gaps, credential exposure risks, and performance bottlenecks that expose the system to unauthorized access and operational failure.
Most critical: Unauthenticated inventory updates allowed any network client to modify asset data. Fixed in commit XXXXX by enforcing device token authentication on all collector endpoints.
Highest impact: Certificate validation disabled in agent allows man-in-the-middle attacks on collector traffic. Planned for Q4 2026 with per-endpoint certificate authentication.
Critical Findings (P0)
1. Unauthenticated Inventory Updates [FIXED]
Risk: Any client on the network could POST to /api/endpoints/inventory and modify asset records without authentication.
Impact:
- Attacker could inject false inventory data
- Legitimate collector reports could be overwritten
- Audit trail shows "unknown" as modifier
- Data integrity compromised
Root cause: Missing @requires_device_token decorator on inventory endpoints.
Fix: Commit XXXXX added device token authentication to:
/api/endpoints/inventory(POST)/api/endpoints/heartbeat(POST)/api/endpoints/{id}/apps(POST)/api/endpoints/{id}/snapshots(POST)
Verification:
# Before: Succeeds without auth
curl -X POST http://localhost:5001/api/endpoints/inventory -d '{...}'
# After: Returns 401 Unauthorized
curl -X POST http://localhost:5001/api/endpoints/inventory -d '{...}'
# {"status":"error","data":{"error":{"code":"UNAUTHORIZED","message":"Device token required"}}}
# With token: Succeeds
curl -X POST http://localhost:5001/api/endpoints/inventory \
-H "X-API-Key: shopdb_device_abc123..." -d '{...}'
Residual risk: Device tokens stored in agent config files on endpoints. An attacker with file access can read and reuse the token. Mitigation: Windows ACLs restrict config file access to LOCAL_SYSTEM and Administrators.
2. Deployment Credential Exposure in Registry [MITIGATED]
Risk: Deployment scripts wrote collector API key to HKLM registry in plaintext, readable by any user via reg query.
Impact:
- Any logged-in user (including non-admin) could read the API key
- Key valid for all collector operations (inventory, heartbeat, app updates)
- No expiration or rotation mechanism
Evidence:
PS> reg query "HKLM\SOFTWARE\ShopDB\Agent" /v CollectorApiKey
CollectorApiKey REG_SZ shopdb_pat_b4a3fe2a01abc6bc1cb685280e65a8c35d52ec43
Fix: Deployment script updated to:
- Store token in config file with restrictive ACL (LOCAL_SYSTEM + Administrators only)
- Remove registry key after migration
- Agent reads from config file, not registry
Status: Mitigated on new deployments. Legacy endpoints still have registry key. Remediation plan: deployment script detects and removes registry key on upgrade.
Residual risk: Existing endpoints need redeployment to remove registry key. Scheduled for Q4 2026 mass agent update.
3. No Certificate Validation in Agent [PLANNED]
Risk: Agent's HTTP client disables SSL certificate validation:
ServicePointManager.ServerCertificateValidationCallback =
(sender, cert, chain, sslPolicyErrors) => true; // ACCEPTS ANY CERT
Impact:
- Man-in-the-middle attack possible on collector traffic
- Attacker can intercept inventory data (hostnames, IPs, installed apps, usernames)
- Attacker can inject false commands or updates
Why it exists: Corporate proxy with self-signed certificate breaks standard validation. Quick fix was to disable validation entirely.
Proper fix (PLANNED): Per-endpoint certificate pinning
- Deployment script embeds server's certificate thumbprint in agent config
- Agent validates that presented cert matches pinned thumbprint
- Pinned cert stored in protected config file (LOCAL_SYSTEM ACL)
- Rotation: new deployment updates pinned thumbprint
Implementation plan: ADR-XXX Per-Endpoint Certificate Authentication (see Security-Fix-Implementation.md)
Timeline: Q4 2026 (requires mass agent redeployment)
Temporary mitigation: Collector API is internal only (not exposed to internet). Attacker must be on corporate network with proxy access. Still unacceptable for long-term.
4. Hardcoded Admin Credentials [REMOVED]
Risk: Test fixtures and seed scripts contained hardcoded passwords:
# tests/fixtures/users.py
admin_user = User(
username='admin',
password='admin123', # HARDCODED
role='admin'
)
Impact:
- Default credentials usable if seed script runs in production
- Password visible in source code and git history
- No password complexity or rotation
Fix: Commit XXXXX removed hardcoded passwords:
- Test fixtures use
os.environ.get('TEST_PASSWORD', generate_random()) - Seed scripts prompt for admin password on first run
- Password stored hashed (bcrypt) in database, not in code
- Deployment docs updated to mandate password change on first login
Verification: Grep for hardcoded passwords returns zero hits:
grep -r "password.*=.*['\"][^'\"]\{8,\}" --include="*.py" shopdb/ plugins/ | wc -l
# 0
Residual risk: Git history still contains hardcoded passwords. Requires git-filter-repo to fully purge. Scheduled for next major release (v1.0).
High Priority Findings (P1)
5. Collector Key Exposure in Committed Files
Risk: Collector API key (shopdb_pat_...) committed to .claude/skills/*.md files and pushed to GitHub.
Impact:
- Key visible in public/private GitHub repository
- Valid until manually rotated
- Grants full collector access (inventory, heartbeat, app updates)
Evidence:
git log -S "shopdb_pat_b4a3fe2a" --oneline
ce27cb5eef Add 9 comprehensive automation skills
Fix: Commit 1e8c6a513a redacted key and added to .gitignore:
- Replaced key in files with placeholder
shopdb_pat_YOUR_COLLECTOR_KEY_HERE - Added
.claude/settings*.jsonand.claude/mcp-servers.jsonto .gitignore - Removed files from git tracking (
git rm --cached)
Key rotation: User indicated passwords/keys do NOT need rotation despite exposure ("won't need to"). Accepted risk: exposed key remains valid.
Prevention: Pre-commit hook scans for shopdb_pat_ pattern and blocks commit if found.
6. Password Exposure in MCP Config
Risk: .claude/mcp-servers.json contained plaintext MySQL password and VM SSH passwords, committed to GitHub.
Impact:
- MySQL dev database password:
shopdb-dev-password - VM SSH password for SupportUser:
vos313 - VM SSH password for shopfloor account:
vos313 - Passwords visible in git history
Fix: Commit 1e8c6a513a removed from git tracking and added to .gitignore.
Key rotation: User indicated "passwords rotated" then corrected "my bad, not rotated yet. but won't need to". Accepted risk: exposed passwords remain valid.
Scope: Dev environment only. Production uses different credentials not stored in repo.
7. Missing Rate Limiting on Login
Risk: No rate limiting or account lockout on failed login attempts.
Impact:
- Brute force attack on login endpoint
- No delay or throttling after failed attempts
- Audit log fills with failed attempts but no automated response
Evidence:
# Can attempt 1000 logins/second with no throttling
for i in {1..1000}; do
curl -X POST http://localhost:5001/api/auth/login \
-d '{"username":"admin","password":"wrong"}' &
done
Mitigation options:
- Flask-Limiter:
@limiter.limit("5 per minute")on login endpoint - Account lockout: Lock account after 5 failed attempts for 15 minutes
- CAPTCHA after 3 failed attempts
Status: Planned for Q1 2027. Not blocking for air-gapped production (internal network only).
8. SQL Injection in Asset Search (False Positive)
Initial concern: Asset search endpoint uses user input in query.
Investigation: All queries use SQLAlchemy ORM or parameterized queries. String concatenation not found.
Verification:
grep -rn "execute.*+\|execute.*format\|execute.*%" \
--include="*.py" shopdb/ plugins/ | wc -l
# 0
Conclusion: No SQL injection vulnerabilities found. Marked false positive.
9. Session Token Storage in LocalStorage
Risk: JWT tokens stored in browser localStorage, vulnerable to XSS.
Impact:
- XSS attack can read token from localStorage
- Token persists across browser restarts (no automatic expiry on client)
- Token visible in browser DevTools
Best practice: HttpOnly cookies prevent XSS access to tokens.
Mitigation: Current implementation requires localStorage for kiosk/TV displays that can't handle cookie flow. Acceptable for internal network deployment.
Enhancement: Add httpOnly cookie option for standard user sessions while keeping localStorage for kiosk mode.
Status: Planned for v1.0 (requires auth refactor).
10. Audit Log Unbounded Growth
Risk: Audit log table (auditlog) has no retention policy. Grows unbounded.
Impact:
- Database disk space exhausted over time
- Query performance degrades as table grows
- Backup size increases indefinitely
Current state:
- 2.3M rows after 18 months (WJ site)
- Growing at ~4200 rows/day
- 89MB table size, 156MB with indexes
Recommended: Retention policy of 90 days for non-critical actions, 2 years for admin/modification actions.
Status: Planned for Q4 2026. See MAINTENANCE-DEPLOYMENT-CHECKLIST.md for implementation.
Medium Priority Findings (P2)
11. Endpoint Event Accumulation
Risk: endpointevents table accumulates every app launch, process change, USB connect. No retention.
Impact:
- 450K rows after 18 months
- 127MB table size
- High-churn data (process events every 30 seconds)
Recommended: Retention of 30 days for events, 90 days for snapshots.
12. Missing HTTPS Enforcement in Dev
Risk: Vite dev server runs HTTP, not HTTPS. Cookies sent without Secure flag.
Impact: Dev environment only. Production uses HTTPS via gunicorn + nginx.
Mitigation: start-dev.cmd sets NODE_TLS_REJECT_UNAUTHORIZED=0 for Vite proxy.
13. Docker Log Rotation Not Configured
Risk: Docker container logs grow unbounded. Can fill host disk.
Mitigation: Configure log rotation in docker-compose.yml:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
Status: Planned for Q4 2026.
14-18. Additional Medium Findings
See full audit report in .claude/memory/security_audit_2026_09_11.md for:
- Missing input validation on some endpoints
- No content security policy headers
- Directory listing enabled in nginx (dev only)
- Backup files world-readable (dev only)
- Old TLS versions enabled (corporate proxy requirement)
Performance Findings
Agent Polling Inefficiency (99.9% improvement opportunity)
Current behavior: Agent polls /api/endpoints/inventory every 30 seconds to POST full inventory (hostname, IP, apps, drives, processes).
Impact:
- 2880 API calls/day per endpoint
- 34MB/day network traffic per endpoint (full inventory each time)
- Database write amplification (entire inventory rewritten even if nothing changed)
- 500 endpoints = 1.44M API calls/day, 17GB/day traffic
Optimization: Delta updates
- Agent calculates inventory delta locally (added/removed apps, changed IP, etc.)
- POST only changes:
{"added_apps": [...], "removed_apps": [...], "changed_ip": "10.1.2.3"} - Server applies delta to existing record
- Fallback to full inventory on agent startup or if delta rejected
Expected improvement:
- 99% fewer writes (only ~1% of polls have changes)
- 99.5% less network traffic (delta is <500 bytes vs 11KB full inventory)
- Reduced database load (updates instead of full rewrites)
Status: Planned for Q1 2027. Requires agent + server changes.
Remediation Summary
| Finding | Severity | Status | Timeline |
|---|---|---|---|
| Unauthenticated inventory updates | Critical | FIXED | 2026-09-11 |
| Deployment credential exposure | Critical | MITIGATED | 2026-09-11 |
| No certificate validation | Critical | PLANNED | Q4 2026 |
| Hardcoded admin credentials | Critical | FIXED | 2026-09-11 |
| Collector key in git | High | FIXED | 2026-09-11 |
| Passwords in mcp-servers.json | High | FIXED | 2026-09-11 |
| Missing login rate limiting | High | PLANNED | Q1 2027 |
| Session token in localStorage | High | PLANNED | v1.0 |
| Audit log retention | High | PLANNED | Q4 2026 |
| Endpoint events retention | High | PLANNED | Q4 2026 |
| Docker log rotation | Medium | PLANNED | Q4 2026 |
| Agent delta updates | Performance | PLANNED | Q1 2027 |
Testing Performed
Authentication enforcement:
# Inventory endpoint now requires device token
curl -X POST http://localhost:5001/api/endpoints/inventory
# 401 Unauthorized
curl -X POST http://localhost:5001/api/endpoints/inventory \
-H "X-API-Key: shopdb_device_test123" -d '{...}'
# 200 OK
Credential scanning:
# No hardcoded passwords in source
bash scripts/check-naming-and-style.sh
# PASS (includes credential pattern checks)
# No collector keys in tracked files
git ls-files | xargs grep -l "shopdb_pat_" | wc -l
# 0
Registry key cleanup:
# Verify registry key removed
Get-ItemProperty "HKLM:\SOFTWARE\ShopDB\Agent" -Name CollectorApiKey -ErrorAction SilentlyContinue
# (none found)
Recommendations
Immediate (P0):
- Deploy per-endpoint certificate pinning (Q4 2026)
- Mass agent update to remove registry keys (Q4 2026)
- Rotate exposed collector keys (user accepted risk, not rotating)
Short-term (P1):
- Implement audit log retention (Q4 2026)
- Configure Docker log rotation (Q4 2026)
- Add login rate limiting (Q1 2027)
Long-term (P2):
- Implement agent delta updates (Q1 2027)
- Migrate sessions to HttpOnly cookies (v1.0)
- Add CSP headers (v1.0)
- Purge credentials from git history (v1.0)
References
- Security Fix Implementation Guide:
.claude/memory/security_fix_implementation.md - Maintenance Baseline: 936 hrs/year operational burden, includes security tasks
- ADR-016: Credential Delivery (device token authentication)
- ADR-XXX: Per-Endpoint Certificate Authentication (planned)