Dynamic MySolem/MyIndygo IoT bridge with MQTT, Home Assistant autodiscovery, and Homebridge EasyMQTT compatibility. - Device discovery via MySolem API on startup (all modules/inputs/outputs) - Configurable poll engine (fast/normal/slow buckets, per-input overrides) - MQTT retained state publishing + HA autodiscovery payloads - Unified manual command routing (linesControl vs sendManualModuleCommand) - Runtime config via MQTT $config/set, persisted to config.json - Expression override system (API-provided > hardcoded > passthrough) - Minimal REST API: /health, /state, /control, /rediscover - Docker deployment (Dockerfile + docker-compose.yml) - 38 unit tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
9.5 KiB
MySolem API — Security Critique
Assessment based on black-box reverse engineering of https://qualif.mysolem.com.
Critical
1. /modules/search leaks the entire platform's device database
POST /modules/search with an empty query ("query": {}) returned 4,607 modules belonging to all users on the platform. Confirmed exposed fields: name, serialNumber, macAddress, softwareVersion, seenAt (last-seen timestamp), batteryAlarm, uuid, type, and createdAt.
Any authenticated user can enumerate every device on the platform. This is a broken object-level authorization (BOLA / IDOR) vulnerability — the endpoint enforces authentication but not ownership scoping.
Impact: full device inventory of all platform customers — device names, serial numbers, MAC addresses, firmware versions, and last-seen timestamps across all tenants.
2. POST /users/search exposes full user records including password hashes and account takeover tokens
POST /users/search accepts an arbitrary MongoDB-style query with no ownership enforcement. Querying by any known user ID returns the complete user document, including:
- Email address and full name
- Hashed password (
passwordfield — 64-character hex string, consistent with unsalted SHA-256; notably not bcrypt which is Sails.js's default — warrants further analysis) - Active password reset token (
resetPasswordToken) — can be used directly to take over the account without knowing the current password - APNs and FCM push notification tokens — allows sending arbitrary push notifications to the user's devices
- Address fields, notification preferences, and app version history
User IDs are trivially obtained from finding #1: every module document in the /modules/search dump contains the owning user's ID in its relational fields.
Proof of concept: querying {"query": {"id": "641c3250efed32199bed0166"}} returned the full record for jdheilly@solem.fr including password hash, reset token, and 11 push tokens across iOS and Android.
Impact: complete account takeover of any platform user via the exposed reset token, plus credential exposure (unsalted SHA-256 hashes are trivially crackable). Push token exposure enables notification spam or phishing. This is the most severe finding in this assessment.
3. POST /outputs/search and POST /programs/search expose full platform watering configuration
Both search endpoints accept an empty MongoDB query with no ownership enforcement:
POST /outputs/searchwith{"query": {}}returned 12,034 outputs (valve/station definitions) across all tenants, including names, flow rates, and module associations.POST /programs/searchwith{"query": {}}returned 6,073 irrigation programs across all tenants, including full schedules, station durations, start times, water-budget percentages, and weekday patterns.
Impact: complete read access to every customer's watering configuration. Combined with finding #4 (device control), this gives an attacker enough information to silently modify or disrupt any installation on the platform.
4. GET /remote/module/configuration exposes device internals for any module
The endpoint accepts any moduleId without ownership verification. Probing a foreign active module (67697cc9673ef4bfbb4fdbbc) returned full device configuration including:
- WiFi SSID and all MAC addresses (WiFi, Bluetooth, Ethernet)
- Firmware, hardware, and BLE versions
- Connected network domain and IoT subdomain
- Uptime counter and reset count
- Active link states (WiFi, Bluetooth, modem)
Impact: network topology and credential-adjacent data (WiFi SSID + MAC) for any online device on the platform, without owning it.
5. No authorization check on /remote/input/getComputedSensorData
The sensor telemetry endpoint accepts any inputId. If an attacker enumerates input IDs (trivially derived from the module search above — they are sequential MongoDB ObjectIds), they can pull historical sensor data for any device on the platform without owning it.
Impact: silent exfiltration of long-term environmental data (soil moisture trends, rain events, watering schedules) for any customer site.
7. POST /module/sendManualModuleCommand — no apparent ownership check
The control endpoint takes a moduleSerialNumber, not a session-scoped module ID. Serial numbers are visible in the /modules/search dump. It is likely (not confirmed) that a valid session can send commands to modules owned by other users.
Impact: if confirmed, any authenticated user could start or stop irrigation on any device on the platform — including commercial agricultural installations.
High
8. Google Maps API key hard-coded in HTML
The key AIzaSyDh8fCC9vTEuN9eKewxgqWXGP9ENb5an1c is embedded in every page load with no referrer restriction visible. It can be extracted and abused for Maps API billing fraud against the platform owner.
9. Session cookie has no observed expiry enforcement beyond 7 days
Sails.js default session TTL. There is no SameSite attribute confirmed in the Set-Cookie header, which means the cookie is sent on cross-site navigated requests — making CSRF possible for state-changing endpoints despite the lack of a CSRF token check (both conditions compound each other).
A SameSite=Lax or SameSite=Strict attribute would mitigate this without requiring a token.
10. No rate limiting observed on /login
The login endpoint accepted repeated requests without delay, lockout, or CAPTCHA. Credential stuffing and brute-force attacks are uninhibited.
11. MongoDB ObjectIds as primary keys are sequentially guessable
MongoDB ObjectIds encode a timestamp + machine ID + process ID + counter. They are not random. An attacker who knows one valid ID can narrow the search space for adjacent resources created around the same time. Combined with findings #1–2, this accelerates enumeration significantly.
Medium
12. CORS is misconfigured (blank Access-Control-Allow-Origin)
The server returns Access-Control-Allow-Origin: (empty string) rather than omitting the header or setting a specific origin. This is a misconfiguration — browsers treat it as invalid, but it signals the header is being generated dynamically and may be inconsistently applied across routes.
13. No Content-Security-Policy header
The React frontend loads without a CSP. If an XSS vulnerability exists anywhere in the app, the attacker has full access to the DOM and cookie-less session (cookies are HttpOnly, but XSS can still make authenticated API calls).
14. Verbose server fingerprinting
X-Powered-By: Sails <sailsjs.org> is returned on every response. Attackers can immediately target known Sails.js CVEs without fingerprinting effort. This header should be removed.
15. Module serial numbers and MAC addresses exposed in bulk
The /modules/search dump includes full serialNumber, uuid, macAddress, and manufacturerType for every device. These are used as authentication tokens in some IoT protocols (LoRa device EUIs, BLE MAC-based pairing). Bulk exposure reduces the bar for physical-layer attacks.
Low / Informational
16. Qualification environment uses real production data
The qualif.mysolem.com environment appears to share the same database as production (or a live mirror), given that real device GPS coordinates, real customer addresses, and live telemetry timestamps are visible. Qualification/staging environments should use anonymized or synthetic data.
17. Input IDs discoverable via HTML scraping
Input IDs are embedded as data-input-id attributes in the module detail page. This is not a vulnerability on its own, but combined with #1 (module enumeration) and #2 (unrestricted telemetry), it completes the chain without requiring any guessing.
Summary table
| # | Finding | Confirmed | Severity | OWASP / CWE |
|---|---|---|---|---|
| 1 | /modules/search — 4,607 devices platform-wide |
Yes | Critical | API1:2023 BOLA |
| 2 | /users/search — password hashes, reset tokens, push tokens |
Yes | Critical | API1:2023 BOLA / CWE-312 |
| 3 | /outputs/search + /programs/search — 12K outputs, 6K programs |
Yes | Critical | API1:2023 BOLA |
| 4 | /remote/module/configuration — WiFi/MAC/firmware for any device |
Yes | Critical | API1:2023 BOLA |
| 5 | Sensor telemetry readable for any input ID | Likely | Critical | API1:2023 BOLA |
| 6 | Sensor history readable for any module ID | Likely | High | API1:2023 BOLA |
| 7 | Device control likely has no ownership check | Unconfirmed | Critical | API1:2023 BOLA |
| 8 | Google Maps API key in HTML, no restriction | Yes | High | CWE-312 |
| 9 | Session cookie missing SameSite attribute |
Yes | High | CWE-352 CSRF |
| 10 | No login rate limiting | Yes | High | CWE-307 |
| 11 | Sequential ObjectIds accelerate enumeration | Yes | High | CWE-330 |
| 12 | Blank CORS header (misconfigured) | Yes | Medium | CWE-942 |
| 13 | No Content-Security-Policy | Yes | Medium | CWE-1021 |
| 14 | X-Powered-By header leaks framework |
Yes | Low | CWE-200 |
| 15 | MAC/serial bulk exposure | Yes | Medium | CWE-200 |
| 16 | Staging uses real data | Yes | Medium | — |
| 17 | Input IDs in HTML source | Yes | Info | — |
Responsible disclosure note
These findings were discovered through authorized testing of an account owned by the researcher. The critical BOLA issues (#1–7) should be reported to the MySolem security team before any public disclosure, as they affect all platform customers.