From 5b490c0aeab9c9d7375b918424d45949d0eeea17 Mon Sep 17 00:00:00 2001 From: retr0 <42kdesigners@gmail.com> Date: Sun, 26 Apr 2026 12:34:21 +0200 Subject: [PATCH] Security: XSS-Fix, localhost-Binding, API-Validierung (v1.1.3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Flask bindet auf 127.0.0.1 statt 0.0.0.0 — Port 8099 nicht mehr direkt im LAN erreichbar (host_network: true umgeht sonst HA-Auth) - XSS: esc() Funktion + HTML-Escaping für alle user-controlled Werte in innerHTML (inv.name, modbus_ip, mqtt_topic_prefix, s.name, s.unit) - API: POST /api/inverters-config validiert inverter_model, Port (1-65535), Modbus-Adresse (1-247) vor dem Speichern - _poll_loop: int()-Aufrufe in try/except — kein Thread-Crash bei ungültiger Config Co-Authored-By: Claude Sonnet 4.6 --- haos-addon/config.yaml | 2 +- haos-addon/src/main.py | 27 +++++++++++++++++++++++---- haos-addon/src/web/index.html | 20 ++++++++++++-------- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/haos-addon/config.yaml b/haos-addon/config.yaml index 4e2c622..8ce8c8c 100644 --- a/haos-addon/config.yaml +++ b/haos-addon/config.yaml @@ -1,5 +1,5 @@ name: Growatt ShineLAN-X -version: "1.1.2" +version: "1.1.3" slug: growatt_shinelan_x description: Growatt Wechselrichter via ShineLAN-X (NuttX Modbus TCP) - MQTT Discovery + Web UI url: https://gitea.bitfire.work/retr0/Growatt-Wechselrichter-HAOS diff --git a/haos-addon/src/main.py b/haos-addon/src/main.py index 1b46ff6..12f39e6 100644 --- a/haos-addon/src/main.py +++ b/haos-addon/src/main.py @@ -87,12 +87,18 @@ def _poll_loop(inv_cfg: Dict[str, Any], stop: threading.Event): inverter = INVERTERS.get(model_id, INVERTERS["MIC_1500_TL_X"]) prefix = inv_cfg.get("mqtt_topic_prefix", f"growatt/{inv_id}") device_id = f"growatt_{inv_id}" - interval = max(5, int(inv_cfg.get("update_interval", 30))) + try: + interval = max(5, int(inv_cfg.get("update_interval", 30))) + port = int(inv_cfg.get("modbus_port", 502)) + slave = int(inv_cfg.get("modbus_address", 1)) + except (ValueError, TypeError) as e: + log.error("[%s] Ungültige Konfiguration: %s", inv_id, e) + return reader = ModbusReader( host=inv_cfg["modbus_ip"], - port=int(inv_cfg.get("modbus_port", 502)), - slave=int(inv_cfg.get("modbus_address", 1)), + port=port, + slave=slave, ) with State.lock: @@ -202,6 +208,19 @@ def api_get_inverters(): @app.post("/api/inverters-config") def api_save_inverters(): data = request.get_json(force=True) or [] + if not isinstance(data, list): + return jsonify({"error": "invalid"}), 400 + for inv in data: + if not isinstance(inv, dict): + return jsonify({"error": "invalid"}), 400 + if inv.get("inverter_model") not in INVERTERS: + return jsonify({"error": f"unknown model: {inv.get('inverter_model')}"}), 400 + port = inv.get("modbus_port", 502) + if not isinstance(port, int) or not (1 <= port <= 65535): + return jsonify({"error": "invalid port"}), 400 + addr = inv.get("modbus_address", 1) + if not isinstance(addr, int) or not (1 <= addr <= 247): + return jsonify({"error": "invalid modbus address (1-247)"}), 400 with State.lock: State.inverters_cfg = data save_config() @@ -284,4 +303,4 @@ if __name__ == "__main__": _restart_all() port = int(os.environ.get("INGRESS_PORT", "8099")) log.info("Web UI startet auf Port %d", port) - app.run(host="0.0.0.0", port=port, threaded=True) + app.run(host="127.0.0.1", port=port, threaded=True) diff --git a/haos-addon/src/web/index.html b/haos-addon/src/web/index.html index 9fa910c..dfd91bd 100644 --- a/haos-addon/src/web/index.html +++ b/haos-addon/src/web/index.html @@ -221,6 +221,10 @@