Address all 5 P0 issues from QE analysis (55/100 score): - P0-1: Rate limiter bypass — validate X-Forwarded-For against trusted proxy list - P0-2: Exception detail leak — generic 500 messages, exception_type gated by dev mode - P0-3: WebSocket JWT in URL (CWE-598) — first-message auth pattern replaces query param - P0-4: Rust tests not in CI — add rust-tests job gating docker-build and notify - P0-5: WebSocket path mismatch — use WS_PATH constant instead of hardcoded /ws/sensing Includes ADR-080 remediation plan and 9 QE reports (4,914 lines). Firmware validated on ESP32-S3 (COM8): CSI collecting, calibration OK. Co-Authored-By: claude-flow <ruv@ruv.net>
30 KiB
Security Review Report -- wifi-densepose
Date: 2026-04-05 Reviewer: QE Security Reviewer (V3) Scope: Full codebase -- Python API, Rust crates, ESP32 C firmware Severity Weights: CRITICAL=3, HIGH=2, MEDIUM=1, LOW=0.5, INFORMATIONAL=0.25 Weighted Finding Score: 19.25 (minimum required: 3.0)
Executive Summary
This security review examined all security-sensitive code across the wifi-densepose project: the Python FastAPI backend (authentication, rate limiting, CORS, WebSocket, API endpoints), Rust workspace crates (API, DB, config, WASM), and ESP32-S3 C firmware (NVS credentials, OTA update, WASM upload, swarm bridge, UDP streaming).
Recommendation: CONDITIONAL PASS -- No critical data-exfiltration or remote code execution vulnerabilities were found in the production code paths. However, 3 HIGH severity findings and several MEDIUM issues require remediation before any production deployment. The codebase demonstrates solid security awareness in many areas (constant-time OTA PSK comparison, Ed25519 WASM signature verification, parameterized queries via SQLAlchemy/sqlx, bcrypt password hashing), but gaps remain in WebSocket security, rate limiting bypass vectors, and firmware transport encryption.
Vulnerability Summary
| Severity | Count | Categories |
|---|---|---|
| CRITICAL | 0 | -- |
| HIGH | 3 | Auth bypass, information disclosure, IP spoofing |
| MEDIUM | 7 | CORS, token lifecycle, transport security, memory growth |
| LOW | 5 | Deprecated APIs, logging, configuration hardening |
| INFORMATIONAL | 3 | Best practice improvements |
Detailed Findings
HIGH-001: WebSocket Authentication Token Passed in URL Query String (CWE-598)
Severity: HIGH OWASP: A07:2021 -- Identification and Authentication Failures Files:
v1/src/api/routers/stream.py:74(WebSockettokenquery parameter)v1/src/middleware/auth.py:243(fallback torequest.query_params.get("token"))v1/src/api/middleware/auth.py:173(request.query_params.get("token"))
Description: JWT tokens are accepted via URL query parameters for WebSocket connections. URL parameters are logged in web server access logs, browser history, proxy logs, and HTTP Referer headers. This creates multiple credential leakage vectors.
# v1/src/api/routers/stream.py:74
token: Optional[str] = Query(None, description="Authentication token")
# v1/src/middleware/auth.py:243
if request.url.path.startswith("/ws"):
token = request.query_params.get("token")
Impact: JWT tokens may be captured from server logs, proxy caches, or browser history, enabling session hijacking.
Remediation:
- Use the WebSocket
Sec-WebSocket-Protocolheader to pass tokens during the upgrade handshake. - Alternatively, require clients to send the token as the first WebSocket message after connection, then authenticate before processing further messages.
- If query parameter tokens must be supported during a transition, ensure all web server and reverse proxy log configurations redact the
tokenparameter.
HIGH-002: Rate Limiter Trusts X-Forwarded-For Header Without Validation (CWE-348)
Severity: HIGH
OWASP: A05:2021 -- Security Misconfiguration
File: v1/src/middleware/rate_limit.py:200-206
Description:
The _get_client_ip method trusts the X-Forwarded-For header without any validation. An attacker can spoof this header to bypass IP-based rate limiting entirely by rotating forged IP addresses on each request.
# v1/src/middleware/rate_limit.py:200-206
def _get_client_ip(self, request: Request) -> str:
forwarded_for = request.headers.get("X-Forwarded-For")
if forwarded_for:
return forwarded_for.split(",")[0].strip()
real_ip = request.headers.get("X-Real-IP")
if real_ip:
return real_ip
return request.client.host if request.client else "unknown"
Impact: Complete rate limiting bypass for unauthenticated requests. An attacker can send unlimited requests by setting arbitrary X-Forwarded-For values.
Remediation:
- Only trust
X-Forwarded-Forwhen the application is deployed behind a known reverse proxy. Configure a trusted proxy allowlist. - Use the uvicorn/Starlette
--proxy-headersflag only when behind a trusted proxy, and strip these headers at the edge. - Consider using a middleware like
starlette.middleware.trustedhost.TrustedHostMiddlewareand validating the number of proxy hops.
HIGH-003: Error Responses Leak Internal Exception Details in Non-Production (CWE-209)
Severity: HIGH OWASP: A09:2021 -- Security Logging and Monitoring Failures Files:
v1/src/api/routers/pose.py:140-141--detail=f"Pose estimation failed: {str(e)}"v1/src/api/routers/pose.py:176-177--detail=f"Pose analysis failed: {str(e)}"v1/src/api/routers/stream.py:297--detail=f"Failed to get stream status: {str(e)}"- All exception handlers in
v1/src/api/routers/stream.py(lines 326, 351, 404, 442, 463) v1/src/middleware/error_handler.py:101-104-- traceback in development mode
Description:
Multiple API endpoints directly interpolate Python exception messages into HTTP error responses. While the global error handler in error_handler.py correctly suppresses details in production, the per-endpoint HTTPException handlers bypass this and always expose str(e) regardless of environment.
# v1/src/api/routers/pose.py:140-141
raise HTTPException(
status_code=500,
detail=f"Pose estimation failed: {str(e)}"
)
Impact: Internal error messages (including database connection strings, file paths, stack traces, and library-specific error codes) are exposed to unauthenticated callers. This aids reconnaissance for targeted attacks.
Remediation:
- Replace all endpoint-level
detail=f"...{str(e)}"patterns with a generic message:detail="Internal server error". - Log the full exception server-side with
logger.exception(). - Rely on the centralized
ErrorHandlerclass for all error formatting, which already has production-safe behavior.
MEDIUM-001: CORS Allows Wildcard Origins with Credentials in Development (CWE-942)
Severity: MEDIUM OWASP: A05:2021 -- Security Misconfiguration Files:
v1/src/config/settings.py:33-34-- defaults:cors_origins=["*"],cors_allow_credentials=Truev1/src/middleware/cors.py:255-256-- development config combinesallow_origins=["*"]+allow_credentials=True
Description:
The default settings allow CORS from all origins (*) with credentials (allow_credentials=True). Per the CORS specification, Access-Control-Allow-Origin: * cannot be used with Access-Control-Allow-Credentials: true. However, the CORSMiddleware implementation echoes the requesting origin header verbatim, effectively granting credentialed access from any origin.
# v1/src/middleware/cors.py:255-256 (development_config)
"allow_origins": ["*"],
"allow_credentials": True,
The validate_cors_config function at line 354 correctly flags this combination but is only advisory -- it does not prevent the configuration from being applied.
Impact: Any website can make authenticated cross-origin requests to the API when running in development mode. If development defaults leak to production, this becomes a credential theft vector via CSRF-like attacks.
Remediation:
- Change the default
cors_originsto[](empty list) and require explicit configuration. - Make
validate_cors_configenforce the rule by raising an exception rather than returning warnings. - In the
CORSMiddleware.__init__, reject the combination ofallow_credentials=Truewith wildcard origins at construction time.
MEDIUM-002: WebSocket Connections Lack Message Size Limits (CWE-400)
Severity: MEDIUM OWASP: A04:2021 -- Insecure Design Files:
v1/src/api/routers/stream.py:127-128--message = await websocket.receive_text()with no size limitv1/src/api/websocket/connection_manager.py-- nomax_sizeconfiguration
Description:
WebSocket endpoints accept incoming messages of arbitrary size. The receive_text() call at stream.py:127 has no size limit, allowing a client to send extremely large messages that consume server memory.
Additionally, the ConnectionManager does not enforce a maximum number of connections. An attacker could open thousands of WebSocket connections to exhaust server resources.
Impact: Denial of service through memory exhaustion or connection pool exhaustion.
Remediation:
- Configure
websocket.accept(max_size=...)or use Starlette'sWebSocketmax_sizeparameter (default is 16 MB -- reduce to 64 KB or less for control messages). - Add a maximum connection limit in
ConnectionManager.connect()and reject new connections when the limit is reached. - Implement per-client message rate limiting in the WebSocket handler.
MEDIUM-003: Token Blacklist Uses Periodic Full Clear Instead of Per-Token Expiry (CWE-613)
Severity: MEDIUM
OWASP: A07:2021 -- Identification and Authentication Failures
File: v1/src/api/middleware/auth.py:246-252
Description:
The TokenBlacklist class clears all blacklisted tokens every hour, regardless of their actual expiry time. This means:
- A revoked token could be re-usable after the next hourly clear.
- Tokens revoked just before a clear cycle have nearly zero effective blacklist time.
# v1/src/api/middleware/auth.py:246-252
def _cleanup_if_needed(self):
now = datetime.utcnow()
if (now - self._last_cleanup).total_seconds() > self._cleanup_interval:
self._blacklisted_tokens.clear() # Clears ALL tokens
self._last_cleanup = now
Furthermore, the TokenBlacklist is not consulted in the AuthMiddleware.dispatch() or AuthenticationMiddleware._authenticate_request() flows -- the token_blacklist global instance exists but is never checked during token validation.
Impact: Token revocation (logout) is not enforceable. A stolen JWT remains valid until its natural expiry.
Remediation:
- Store each blacklisted token with its
expclaim timestamp. Only remove entries whoseexphas passed. - Integrate the blacklist check into
_verify_token()/verify_token()so that blacklisted tokens are rejected. - For production, replace the in-memory set with a Redis-backed store for cross-process consistency.
MEDIUM-004: OTA Update Endpoint Has No Authentication by Default (CWE-306)
Severity: MEDIUM
OWASP: A07:2021 -- Identification and Authentication Failures
File: firmware/esp32-csi-node/main/ota_update.c:44-49
Description:
The OTA firmware update endpoint (POST /ota on port 8032) has authentication disabled unless an OTA pre-shared key (PSK) is manually provisioned into NVS. The ota_check_auth function returns true when no PSK is configured, allowing unauthenticated firmware uploads.
// firmware/esp32-csi-node/main/ota_update.c:44-49
static bool ota_check_auth(httpd_req_t *req)
{
if (s_ota_psk[0] == '\0') {
/* No PSK provisioned -- auth disabled (permissive for dev). */
return true;
}
...
}
The firmware logs a warning about this (ESP_LOGW(..., "OTA authentication DISABLED")), but it is the default state for all new devices.
Impact: Any device on the same network can flash arbitrary firmware to the ESP32 without authentication, enabling persistent compromise of the sensing node.
Remediation:
- Require PSK provisioning as part of the mandatory device setup flow. Reject OTA uploads if no PSK is provisioned (fail-closed).
- Alternatively, require physical button press confirmation for OTA updates when no PSK is set.
- Document the PSK provisioning step prominently in the deployment guide.
MEDIUM-005: ESP32 UDP CSI Stream Has No Encryption or Authentication (CWE-319)
Severity: MEDIUM
OWASP: A02:2021 -- Cryptographic Failures
File: firmware/esp32-csi-node/main/stream_sender.c:66-106
Description:
CSI data frames are transmitted via plain UDP (SOCK_DGRAM, IPPROTO_UDP) with no encryption, authentication, or integrity protection. An attacker on the same network segment can:
- Eavesdrop on CSI data (potentially revealing occupancy/activity information).
- Inject forged CSI frames to manipulate pose estimation.
- Replay captured frames.
// firmware/esp32-csi-node/main/stream_sender.c:92-93
int sent = sendto(s_sock, data, len, 0,
(struct sockaddr *)&s_dest_addr, sizeof(s_dest_addr));
Impact: CSI data exposure and injection on the local network. The severity is moderated by the fact that CSI data requires specialized knowledge to interpret, but the UDP transport provides zero confidentiality for the sensor data.
Remediation:
- Implement DTLS (Datagram TLS) for the UDP stream, using mbedTLS which is already available in ESP-IDF.
- At minimum, add HMAC authentication to each frame using a pre-shared key to prevent injection.
- Consider adding a sequence number and replay window to detect replayed frames.
MEDIUM-006: Swarm Bridge Seed Token Transmitted in Cleartext HTTP (CWE-319)
Severity: MEDIUM
OWASP: A02:2021 -- Cryptographic Failures
File: firmware/esp32-csi-node/main/swarm_bridge.c:211-229
Description:
The swarm bridge HTTP client configuration does not enforce TLS. The esp_http_client_config_t struct at line 211 specifies only .url and .timeout_ms without setting .transport_type = HTTP_TRANSPORT_OVER_SSL or .cert_pem. If the seed_url uses http:// rather than https://, the Bearer token is transmitted in cleartext.
// firmware/esp32-csi-node/main/swarm_bridge.c:211-216
esp_http_client_config_t http_cfg = {
.url = url,
.method = HTTP_METHOD_POST,
.timeout_ms = SWARM_HTTP_TIMEOUT,
};
// firmware/esp32-csi-node/main/swarm_bridge.c:226-229
if (s_cfg.seed_token[0] != '\0') {
char auth_hdr[80];
snprintf(auth_hdr, sizeof(auth_hdr), "Bearer %s", s_cfg.seed_token);
esp_http_client_set_header(client, "Authorization", auth_hdr);
}
Impact: Bearer token can be sniffed on the local network, enabling unauthorized access to the Cognitum Seed ingest API.
Remediation:
- Validate that
seed_urlstarts withhttps://inswarm_bridge_init()and rejecthttp://URLs. - Configure TLS certificate verification in the HTTP client config.
- Consider certificate pinning for the Seed server.
MEDIUM-007: In-Memory Rate Limiter Does Not Bound Memory Growth (CWE-400)
Severity: MEDIUM OWASP: A04:2021 -- Insecure Design Files:
v1/src/api/middleware/rate_limit.py:28-29--self.request_counts = defaultdict(lambda: deque())v1/src/middleware/rate_limit.py:132--self._sliding_windows: Dict[str, SlidingWindowCounter] = {}
Description:
Both rate limiter implementations store per-client sliding window data in unbounded in-memory dictionaries. An attacker sending requests from many spoofed IPs (see HIGH-002) can create millions of entries, each containing a deque of timestamps. The cleanup tasks run only periodically (every 5 minutes or on-demand) and cannot keep pace with a high-rate attack.
Impact: Memory exhaustion denial of service through rate limiter state amplification.
Remediation:
- Cap the total number of tracked clients (e.g., 100,000 entries). Use an LRU eviction policy.
- Use a fixed-size data structure (e.g., a counter array with hash bucketing) instead of per-client deques.
- For production, use Redis-backed rate limiting with automatic key expiry.
LOW-001: Test Script Contains Hardcoded Placeholder Secret (CWE-798)
Severity: LOW
OWASP: A07:2021 -- Identification and Authentication Failures
File: v1/test_auth_rate_limit.py:26
Description: A test script in the repository contains a hardcoded JWT secret key placeholder:
SECRET_KEY = "your-secret-key-here" # This should match your settings
While marked with a comment indicating it should be changed, this file is checked into the repository and could be mistaken for a real configuration.
Impact: Low -- this is a test file, not production configuration. However, if a developer copies this value into production settings, JWT tokens become trivially forgeable.
Remediation:
- Replace with an environment variable reference:
SECRET_KEY = os.environ.get("SECRET_KEY", ""). - Add a validation check that fails if the secret is the placeholder value.
LOW-002: User Information Exposed in Response Headers (CWE-200)
Severity: LOW OWASP: A01:2021 -- Broken Access Control Files:
v1/src/middleware/auth.py:298-299--response.headers["X-User"] = user_info["username"]andresponse.headers["X-User-Roles"] = ",".join(user_info["roles"])v1/src/api/middleware/auth.py:111--response.headers["X-User-ID"] = request.state.user.get("id", "")
Description: Authenticated user information (username, roles, user ID) is included in HTTP response headers. These headers are visible to any intermediary (CDN, reverse proxy, browser extensions) and in browser developer tools.
Impact: Information disclosure of user identity and authorization roles to intermediaries and client-side code.
Remediation:
- Remove
X-User,X-User-Roles, andX-User-IDresponse headers, or restrict them to internal/debug environments only. - If needed for debugging, use a configuration flag to enable these headers.
LOW-003: Deprecated datetime.utcnow() Usage (CWE-1235)
Severity: LOW Files: Throughout the Python codebase (auth.py, rate_limit.py, connection_manager.py, pose_stream.py, error_handler.py, stream.py)
Description:
datetime.utcnow() is deprecated in Python 3.12+ in favor of datetime.now(datetime.timezone.utc). While not a security vulnerability per se, timezone-naive datetimes can cause token expiry comparison bugs in environments where the system clock timezone differs from UTC.
Remediation:
Replace all instances of datetime.utcnow() with datetime.now(datetime.timezone.utc).
LOW-004: JWT Algorithm Not Restricted to Asymmetric in Production (CWE-327)
Severity: LOW
OWASP: A02:2021 -- Cryptographic Failures
File: v1/src/config/settings.py:30 -- jwt_algorithm: str = Field(default="HS256")
Description: The default JWT algorithm is HS256 (HMAC-SHA256), a symmetric algorithm. This means the same secret is used for both signing and verification, requiring the secret to be distributed to every service that needs to verify tokens. For multi-service architectures, asymmetric algorithms (RS256, ES256) are preferred.
Additionally, the jwt_algorithm setting is not validated against a safe algorithm allowlist, leaving open the possibility of configuration to none (no signature).
Remediation:
- Validate
jwt_algorithmagainst an allowlist of safe algorithms:["HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "ES256", "ES384", "ES512"]. - Explicitly reject the
nonealgorithm. - For production deployments with multiple services, recommend RS256 or ES256.
LOW-005: No Password Complexity Validation (CWE-521)
Severity: LOW
OWASP: A07:2021 -- Identification and Authentication Failures
File: v1/src/middleware/auth.py:115 -- create_user() method
Description:
The create_user() method accepts any password without minimum length, complexity, or entropy requirements. Test credentials in v1/test_auth_rate_limit.py:21-23 demonstrate weak passwords ("admin123", "user123").
Remediation:
- Enforce minimum password length (12+ characters).
- Check passwords against a common-password blocklist.
- Require mixed character classes or calculate entropy.
INFORMATIONAL-001: Rust API, DB, and Config Crates Are Stubs
Files:
rust-port/wifi-densepose-rs/crates/wifi-densepose-api/src/lib.rs--//! WiFi-DensePose REST API (stub)rust-port/wifi-densepose-rs/crates/wifi-densepose-db/src/lib.rs--//! WiFi-DensePose database layer (stub)rust-port/wifi-densepose-rs/crates/wifi-densepose-config/src/lib.rs--//! WiFi-DensePose configuration (stub)
Description:
The Rust API, database, and configuration crates contain only single-line stub comments. No security review of Rust API endpoints, database queries, or configuration handling was possible because no implementation exists. The wifi-densepose-sensing-server crate contains the actual Rust server implementation.
Note: The sensing server (crates/wifi-densepose-sensing-server/src/main.rs) was checked for SQL injection patterns, CORS issues, and authentication concerns. No SQL injection risks were found (no string-formatted queries). The server appears to use in-memory data structures rather than a database.
INFORMATIONAL-002: Rust unsafe Blocks in WASM Edge Crate
Files: rust-port/wifi-densepose-rs/crates/wifi-densepose-wasm-edge/src/*.rs (multiple files)
Description:
The wifi-densepose-wasm-edge crate contains approximately 40 unsafe blocks, primarily for:
- Writing to static mutable event arrays (
static mut EVENTS: [...]) - Raw pointer casts for
repr(C)struct serialization inrvf.rs
These patterns are common in no_std WASM edge environments where heap allocation is unavailable. The static event arrays use a fixed-size pattern (EVENTS[..n]) that prevents out-of-bounds writes as long as n is bounded correctly. Visual inspection of the bounds checks suggests they are correct, but formal verification or fuzzing of the bounds logic is recommended.
The main workspace crate (wifi-densepose-train) explicitly notes it avoids unsafe blocks.
INFORMATIONAL-003: ESP32 Firmware C Code Uses Safe String Handling
Files: firmware/esp32-csi-node/main/*.c
Description:
The firmware codebase consistently uses strncpy with explicit null termination, snprintf (not sprintf), and proper bounds checking throughout. No instances of strcpy, strcat, sprintf, or gets were found. Buffer sizes are defined via #define constants. The rvf_parser.c performs thorough size validation before any pointer arithmetic.
This is a positive finding reflecting good security practices.
Dependency Analysis
Python Dependencies (requirements.txt)
| Package | Version Spec | Risk |
|---|---|---|
python-jose[cryptography]>=3.3.0 |
MEDIUM -- python-jose has had JWT confusion vulnerabilities. Consider migrating to PyJWT or authlib. |
|
paramiko>=3.0.0 |
LOW -- SSH library. Ensure latest minor version for CVE patches. | |
fastapi>=0.95.0 |
LOW -- Version floor is old. Pin to latest stable for security patches. |
Recommendation: Run pip audit or safety check against the locked dependency file (v1/requirements-lock.txt) to identify known CVEs.
Rust Dependencies (Cargo.toml)
| Crate | Version | Notes |
|---|---|---|
sqlx 0.7 |
OK -- uses parameterized queries by design. | |
axum 0.7 |
OK -- current major version. | |
wasm-bindgen 0.2 |
OK -- standard WASM interface. |
Recommendation: Run cargo audit against Cargo.lock to check for known advisories.
Positive Security Practices Observed
The following areas demonstrate security-conscious design:
-
OTA PSK constant-time comparison (
firmware/esp32-csi-node/main/ota_update.c:66-72): Uses XOR-accumulator pattern to prevent timing attacks on authentication. -
WASM signature verification (
firmware/esp32-csi-node/main/wasm_upload.c:112-137): Ed25519 signature verification is enabled by default (wasm_verify=1). Unsigned uploads are rejected unless explicitly disabled via Kconfig. -
RVF build hash validation (
firmware/esp32-csi-node/main/rvf_parser.c:126-137): SHA-256 hash of the WASM payload is verified against the manifest before loading, preventing tampered module execution. -
Password hashing with bcrypt (
v1/src/middleware/auth.py:21): Proper use ofpasslibwithbcryptscheme. -
Protected user fields (
v1/src/middleware/auth.py:139):update_user()prevents modification ofusername,created_at, andhashed_password. -
Production error suppression (
v1/src/middleware/error_handler.py:214-218): The centralized error handler correctly suppresses internal details in production mode. -
No hardcoded secrets in source (verified via entropy-based search across entire repository): No API keys, passwords, or tokens found in source files (the test script placeholder at
test_auth_rate_limit.py:26is marked as requiring replacement). -
.envfile excluded via.gitignore(.gitignore:171): Environment files are properly excluded from version control. -
C string safety (all
firmware/esp32-csi-node/main/*.c): Consistent use ofstrncpy,snprintf, and null-termination guards. No unsafe C string functions. -
NVS input validation (
firmware/esp32-csi-node/main/nvs_config.c): Bounds checking on all NVS-loaded values (channel range, dwell time minimums, array index clamping).
Files Examined
Python (v1/src/)
v1/src/middleware/auth.py(457 lines) -- JWT auth, user management, middlewarev1/src/middleware/rate_limit.py(465 lines) -- Rate limiting with sliding windowv1/src/middleware/cors.py(375 lines) -- CORS middleware and validationv1/src/middleware/error_handler.py(505 lines) -- Error handling middlewarev1/src/api/middleware/auth.py(303 lines) -- API-layer JWT authv1/src/api/middleware/rate_limit.py(326 lines) -- API-layer rate limitingv1/src/api/websocket/connection_manager.py(461 lines) -- WebSocket managerv1/src/api/websocket/pose_stream.py(384 lines) -- Pose streaming handlerv1/src/api/routers/pose.py(420 lines) -- Pose API endpointsv1/src/api/routers/stream.py(465 lines) -- Streaming API endpointsv1/src/config/settings.py(436 lines) -- Application settingsv1/src/sensing/rssi_collector.py(partial) -- Subprocess usage reviewv1/src/tasks/backup.py(partial) -- Subprocess command constructionv1/test_auth_rate_limit.py(partial) -- Test credentials review
Rust (rust-port/wifi-densepose-rs/)
crates/wifi-densepose-api/src/lib.rs(1 line -- stub)crates/wifi-densepose-db/src/lib.rs(1 line -- stub)crates/wifi-densepose-config/src/lib.rs(1 line -- stub)crates/wifi-densepose-wasm/src/lib.rs(133 lines) -- WASM bindingscrates/wifi-densepose-wasm/src/mat.rs(partial) -- MAT dashboardcrates/wifi-densepose-wasm-edge/src/*.rs(unsafe block audit)crates/wifi-densepose-sensing-server/src/main.rs(SQL injection pattern search)Cargo.toml(workspace dependencies)
C Firmware (firmware/esp32-csi-node/main/)
main.c(302 lines) -- Application entry pointnvs_config.c(333 lines) -- NVS configuration loadingnvs_config.h(77 lines) -- Configuration struct definitionsstream_sender.c(117 lines) -- UDP stream senderota_update.c(267 lines) -- OTA firmware updatewasm_upload.c(433 lines) -- WASM module managementrvf_parser.c(169+ lines) -- RVF container parserswarm_bridge.c(328 lines) -- Cognitum Seed bridge
Configuration & Dependencies
requirements.txt(47 lines).gitignore(verified .env exclusion)
Patterns Checked
| Check Category | Patterns Searched | Result |
|---|---|---|
| Hardcoded secrets | password=, secret_key=, api_key=, high-entropy strings |
Clean (1 test placeholder found) |
| SQL injection | String-formatted SQL queries (format! + SQL keywords, f-string + SQL) |
Clean |
| Command injection | subprocess with user input, os.system, eval |
Safe (fixed command arrays only) |
| Path traversal | User-controlled file paths without sanitization | Not applicable (no file serving endpoints) |
| Insecure deserialization | pickle.loads, yaml.unsafe_load, eval on user input |
Clean |
| Weak cryptography | md5, sha1 for security, DES, RC4 |
Clean (uses bcrypt, SHA-256, Ed25519) |
| Unsafe C functions | strcpy, strcat, sprintf, gets |
Clean (uses safe alternatives throughout) |
| Unsafe Rust blocks | unsafe { ... } in workspace crates |
~40 in wasm-edge (acceptable for no_std) |
.env files committed |
.env, .env.local, .env.production |
Clean (properly gitignored) |
| CORS misconfiguration | Wildcard + credentials | Found (MEDIUM-001) |
Remediation Priority
| Priority | Finding | Effort | Impact |
|---|---|---|---|
| 1 | HIGH-002: Rate limiter IP spoofing | Low | Eliminates rate limiting bypass |
| 2 | HIGH-001: WebSocket token in URL | Medium | Prevents credential leakage |
| 3 | HIGH-003: Error detail exposure | Low | Prevents information disclosure |
| 4 | MEDIUM-003: Token blacklist not enforced | Medium | Enables logout functionality |
| 5 | MEDIUM-004: OTA default no-auth | Low | Prevents unauthorized firmware flash |
| 6 | MEDIUM-002: WebSocket message limits | Low | Prevents DoS via large messages |
| 7 | MEDIUM-001: CORS wildcard + credentials | Low | Prevents CSRF-like attacks |
| 8 | MEDIUM-005: UDP stream no encryption | High | Adds transport security |
| 9 | MEDIUM-006: Swarm bridge cleartext | Medium | Protects Seed authentication |
| 10 | MEDIUM-007: Rate limiter memory growth | Medium | Prevents state amplification DoS |
Security Score
| Category | Score | Max | Notes |
|---|---|---|---|
| Authentication | 6/10 | 10 | Good JWT implementation; token blacklist non-functional |
| Authorization | 8/10 | 10 | Role-based access control present; missing RBAC on some endpoints |
| Input Validation | 8/10 | 10 | Pydantic models, NVS bounds checks; WebSocket lacks size limits |
| Cryptography | 7/10 | 10 | bcrypt, Ed25519, SHA-256; UDP transport unencrypted |
| Configuration | 6/10 | 10 | Good validation functions; unsafe defaults for development |
| Error Handling | 7/10 | 10 | Centralized handler good; per-endpoint leaks |
| Transport Security | 5/10 | 10 | No TLS enforcement for firmware; no DTLS for UDP |
| Dependency Security | 7/10 | 10 | Reasonable version floors; no pinned versions |
| Firmware Security | 7/10 | 10 | OTA auth optional; WASM verification strong |
| Logging/Monitoring | 7/10 | 10 | Comprehensive logging; token blacklist not wired |
Overall Security Score: 68/100
Generated by QE Security Reviewer (V3) -- Domain: security-compliance (ADR-008)