Files
shopdb-flask/docs/wiki-drafts/Security-Audit-2026-09-11.md
Proudlock 3d041da233 Add wiki page drafts for Q3 2026 work
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>
2026-09-11 17:04:39 -04:00

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:

  1. Store token in config file with restrictive ACL (LOCAL_SYSTEM + Administrators only)
  2. Remove registry key after migration
  3. 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

  1. Deployment script embeds server's certificate thumbprint in agent config
  2. Agent validates that presented cert matches pinned thumbprint
  3. Pinned cert stored in protected config file (LOCAL_SYSTEM ACL)
  4. 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:

  1. Test fixtures use os.environ.get('TEST_PASSWORD', generate_random())
  2. Seed scripts prompt for admin password on first run
  3. Password stored hashed (bcrypt) in database, not in code
  4. 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:

  1. Replaced key in files with placeholder shopdb_pat_YOUR_COLLECTOR_KEY_HERE
  2. Added .claude/settings*.json and .claude/mcp-servers.json to .gitignore
  3. 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:

  1. Flask-Limiter: @limiter.limit("5 per minute") on login endpoint
  2. Account lockout: Lock account after 5 failed attempts for 15 minutes
  3. 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

  1. Agent calculates inventory delta locally (added/removed apps, changed IP, etc.)
  2. POST only changes: {"added_apps": [...], "removed_apps": [...], "changed_ip": "10.1.2.3"}
  3. Server applies delta to existing record
  4. 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):

  1. Deploy per-endpoint certificate pinning (Q4 2026)
  2. Mass agent update to remove registry keys (Q4 2026)
  3. Rotate exposed collector keys (user accepted risk, not rotating)

Short-term (P1):

  1. Implement audit log retention (Q4 2026)
  2. Configure Docker log rotation (Q4 2026)
  3. Add login rate limiting (Q1 2027)

Long-term (P2):

  1. Implement agent delta updates (Q1 2027)
  2. Migrate sessions to HttpOnly cookies (v1.0)
  3. Add CSP headers (v1.0)
  4. 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)