Notice: This document reflects the verified implementation in the current source code. For the comprehensive security audit, see Security Documentation.
| Old Claim / Assumption | Current Implementation (Verified from Code) | Required Correction |
|---|---|---|
| Claim: WebSocket interceptor binds auth to the session and enforces scoping. | WebSocketAuthInterceptor only validates CONNECT frames. It does NOT validate topic permissions on SUBSCRIBE frames. Any authenticated user or agent from Company A can subscribe to /topic/device/{id} or /topic/agent/{id} of Company B if they know or guess the device UUID. |
Document the critical multi-tenant subscription authorization bypass vulnerability. |
| Claim: Company identity is derived from JWT and used to scope devices and metrics. | DeviceController.getMetrics(deviceId) completely omits company verification (deviceService.getMetrics(deviceId) does not check companyId). |
Document the missing tenant validation on historical metrics queries. |
| Claim: Rate limiting is enforced per IP and request path at the API edge. | RateLimitFilter.shouldNotFilter() completely exempts /agent, /agent/*, /ws, and /ws/*. Agents pushing metrics or attackers flooding WebSocket handshakes are not throttled. |
Document that edge rate limiting only protects /auth/*, /devices/*, and /company/*. |
| Claim: Destructive commands are blocked by regex. | The agent regex (blockedCommandPattern) uses a naive keyword blacklist (rm, del, format, etc.). Attackers can easily bypass this with exfiltration commands (curl -d @/etc/shadow), file overwrites (echo > /file), or encoded payloads (`echo ... |
base64 -d |
- Transport Security: TLS termination is assumed at the edge/reverse proxy (Nginx/ALB). The Spring backend runs HTTP/WS on port 8080.
- CORS Policy: Configured in
SecurityConfig.javaandWebSocketConfig.javaviaapp.cors.allowed-origins: http://localhost:3000. Supports comma-separated origin patterns. - CSRF: Disabled (
csrf.disable()) across all endpoints. Appropriate for stateless JWT authentication and machine-to-machine agent traffic. - Rate Limiting: Enforced in
RateLimitFilter(Fixed Window: 120 req / 60s per IP+URI). Only applies to user REST routes.
- User / Dashboard: HMAC-SHA256 JWT issued by
/auth/login, signed withapp.jwt.secret(>= 32 bytes). Validated on/devices/**and/company/**viaJwtFilter. - Agent: Machine-level
apiTokenissued per company on registration. Stored in plaintext incompany.api_token. Passed via HTTP headerx-agent-tokenor STOMPCONNECTframe headerx-agent-token.
- Broken Object Level Authorization on STOMP Subscriptions (Critical):
- File:
backend/src/main/java/com/monitor/config/WebSocketAuthInterceptor.java - Issue: Clients can subscribe to any device topic across tenant boundaries without ownership validation.
- File:
- Missing Tenant Check on Metrics REST Endpoint (High):
- File:
backend/src/main/java/com/monitor/controller/DeviceController.java:33 - Issue:
GET /devices/{deviceId}/metricsallows cross-tenant historical data access.
- File:
- Arbitrary Command Execution Bypass (High):
- File:
monitor-agent/service/commands.go:45 - Issue: Regex blacklist is easily bypassed, allowing arbitrary remote code execution on monitored hosts.
- File:
- JWT Stored in Browser
localStorage(Medium):- File:
frontend/lib/auth.ts:8 - Issue: Tokens are accessible to JavaScript in the browser, vulnerable to XSS.
- File:
- Enforce Topic Authorization:
In
WebSocketAuthInterceptor.preSend(), interceptStompCommand.SUBSCRIBEframes and verify caller owns the targeteddeviceId. - Authorize
GET /devices/{deviceId}/metrics: PasscompanyIdintodeviceService.getMetrics()and verifydevice.getCompany().getId().equals(companyId). - Replace Command Blacklist with Script Allowlist:
Do not accept raw shell strings over WebSocket. Allow only predefined command identifiers (e.g.
CMD_RESTART_SERVICE,CMD_DISK_DIAGNOSTICS). - Store JWTs in
httpOnlyCookies: Transition frontend authentication fromlocalStorageto secure, HTTP-only cookies.\n