diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 4f6c4f7..18fe108 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -15,14 +15,27 @@ jobs: steps: - name: Check out repository uses: actions/checkout@v4 + with: + path: catalog + + - name: Check out DOCSight manifest contract + uses: actions/checkout@v4 + with: + repository: itsDNNS/docsight + path: docsight-core - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.13' + - name: Validate manifests against DOCSight core + run: python3 docsight-core/app/manifest_contract.py catalog + - name: Run registry validation + working-directory: catalog run: python3 scripts/validate_registry.py - name: Run validation tests - run: python3 -m unittest tests/test_validate_registry.py + working-directory: catalog + run: python3 -m unittest discover -s tests -q diff --git a/README.md b/README.md index 55c5d16..2ba63bc 100644 --- a/README.md +++ b/README.md @@ -143,10 +143,13 @@ Declare default values and they are automatically registered in DOCSight's confi Use `config_secrets` for module passwords, API tokens, and other sensitive settings: -- Every key in `config_secrets` must also exist in the same manifest's `config` object. +- Every key in `config_secrets` must also exist in the same manifest's `config` object with a string default, normally `""`; non-string defaults are rejected so encrypted values never enter scalar coercion. +- `config_secrets` must be a list of unique strings. - Secret keys are encrypted at rest and masked in Settings after they are saved. - Community collectors can read only their own declared secret keys; they still cannot read DOCSight core secrets such as modem passwords or global API tokens. -- Settings templates should render secret fields as empty password/token inputs. Do not write saved secret values back into HTML `value` attributes. +- Settings templates should render secret fields as empty password/token inputs with `data-config-secret="true"`. Add `data-saved-secret="true"` only when the masked config value indicates an existing saved secret. This explicit metadata makes an untouched field post the mask while an edited field posts the new value. Do not write saved secret values back into HTML `value` attributes. + +The manifest capability contract is owned by DOCSight core rather than duplicated in this catalog. With sibling checkouts, run `python3 ../docsight/app/manifest_contract.py .`; catalog CI performs the same check against current core before running the registry-specific validator. --- @@ -534,7 +537,7 @@ Modules reviewed and tested by the DOCSight team receive `"verified": true`. Unv Before opening your PR, verify: - [ ] **Auth on all routes** — every `@bp.route` must have `@require_auth` (import from `app.web`) -- [ ] **No credentials in HTML** — password/token fields must use `value=""` with a `(saved)` placeholder, never `value="{{ config.password }}"` +- [ ] **No credentials in HTML** — password/token fields must use `value=""`, `data-config-secret="true"`, and conditional `data-saved-secret="true"` metadata, never `value="{{ config.password }}"` - [ ] **Escape dynamic content** — any value from API responses inserted via `innerHTML` must be HTML-escaped; use `textContent` where possible - [ ] **English API errors** — error messages returned by API endpoints must be English (UI translations go in i18n files) - [ ] **No exception details in responses** — use generic error messages ("Connection failed"), log details server-side with `logger.exception()` diff --git a/TEMPLATE-THRESHOLDS/manifest.json b/TEMPLATE-THRESHOLDS/manifest.json index b131feb..a6a6bc8 100644 --- a/TEMPLATE-THRESHOLDS/manifest.json +++ b/TEMPLATE-THRESHOLDS/manifest.json @@ -1,5 +1,5 @@ { - "id": "community.thresholds_CHANGEME", + "id": "community.thresholds_changeme", "name": "CHANGEME Thresholds", "description": "Signal thresholds for CHANGEME cable networks", "version": "1.0.0", diff --git a/TEMPLATE/README.md b/TEMPLATE/README.md index ddf4b44..e291b8e 100644 --- a/TEMPLATE/README.md +++ b/TEMPLATE/README.md @@ -14,9 +14,13 @@ docker restart docsight ## Configuration List any configuration keys here. If the module stores passwords or API tokens, -declare those keys in `config_secrets` in `manifest.json` and render the field as -an empty password/token input in Settings. Saved secret values must not be written -back into HTML `value` attributes. +declare those keys in `config_secrets` in `manifest.json` with string defaults, +normally `""`, and render the field as an empty password/token input in Settings +with `data-config-secret="true"`. Non-string secret defaults are invalid because +encrypted values must not enter boolean or integer coercion. When the masked +config value indicates an existing secret, also render +`data-saved-secret="true"`. Saved secret values must not be written back into +HTML `value` attributes. ## API Endpoints diff --git a/registry.json b/registry.json index 0df8620..dfe06ec 100644 --- a/registry.json +++ b/registry.json @@ -19,7 +19,7 @@ "description": "Monitor WAN1 and WAN2 on Ubiquiti UDM Pro/SE devices. Tracks link state, alive status, and failover events in the global DOCSight event log.", "author": "Oggy512", "repo": "https://github.com/itsDNNS/docsight-modules", - "version": "3.1.2", + "version": "3.1.3", "min_app_version": "2026.2", "type": "integration", "verified": true, diff --git a/tests/test_validate_registry.py b/tests/test_validate_registry.py index dcf09e2..4a87ebe 100644 --- a/tests/test_validate_registry.py +++ b/tests/test_validate_registry.py @@ -52,6 +52,18 @@ def make_valid_fixture( class ValidateRegistryTests(unittest.TestCase): + def test_udm_password_field_uses_explicit_saved_secret_metadata(self): + root = Path(__file__).resolve().parents[1] + template = ( + root / "udm-wan-monitor" / "templates" / "udm_wan_settings.html" + ).read_text(encoding="utf-8") + + self.assertIn('name="udm_wan_password"', template) + self.assertIn('value=""', template) + self.assertIn('data-config-secret="true"', template) + self.assertIn('data-saved-secret="true"', template) + self.assertNotIn('value="{{ config.udm_wan_password', template) + def test_valid_registry_accepts_installable_module_directory(self): with tempfile.TemporaryDirectory() as tmp: root = Path(tmp) diff --git a/udm-wan-monitor/manifest.json b/udm-wan-monitor/manifest.json index 9a69914..a287eb1 100644 --- a/udm-wan-monitor/manifest.json +++ b/udm-wan-monitor/manifest.json @@ -2,7 +2,7 @@ "id": "community.udm_wan_monitor", "name": "UDM WAN-Monitor", "description": "Überwacht WAN1 und WAN2 einer Ubiquiti UDM Pro und erzeugt Events im globalen DOCSight-Log.", - "version": "3.1.2", + "version": "3.1.3", "author": "Oggy512", "minAppVersion": "2026.2", "type": "integration", diff --git a/udm-wan-monitor/templates/udm_wan_settings.html b/udm-wan-monitor/templates/udm_wan_settings.html index 903d7c5..761c730 100644 --- a/udm-wan-monitor/templates/udm_wan_settings.html +++ b/udm-wan-monitor/templates/udm_wan_settings.html @@ -77,7 +77,8 @@ {{ t.get('community.udm_wan_monitor.password', 'Passwort') }} {{ t.get('community.udm_wan_monitor.password_hint', 'Wird verschlüsselt in der DOCSight-Konfiguration gespeichert.') }}