mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-10-02 02:35:05 -04:00
BE: plugin input improvements
This commit is contained in:
1 parent
1c410bf2be
commit
6cc092f2ad
22 files changed
+1499
-330
No files matched your search
@@ -245,6 +245,7 @@ Check your plugin against these repo-wide conventions before opening a PR (verif
|
||||
- **Persist plugin state under `dbFolderPath`, config artifacts under `configPath`** — see [Persisting Plugin Data](#persisting-plugin-data-state--config-files) below.
|
||||
- **A plugin mapped to `mapped_to_table: "CurrentScan"` must also map `scanSourcePlugin`** (a static value via `mapped_to_column_data`, see [Static Value Mapping](#static-value-mapping) below) — not mechanically enforced by `test_plugin_conventions.py`, so review it by eye. Omitting it leaves `scanSourcePlugin` `NULL` on every row this plugin inserts, which silently breaks two things in `server/scan/device_handling.py`: `create_new_devices()`'s `plugin_prefix = str(scanSourcePlugin).strip() if scanSourcePlugin else "NEWDEV"` mislabels devices this plugin creates as source `NEWDEV`; and `update_devices_data_from_scan()`'s `SELECT DISTINCT scanSourcePlugin FROM CurrentScan` + `[row[0] for row in plugin_rows if row[0]] or [None]` drops the `NULL` rows entirely (the `or [None]` fallback never triggers once any other plugin contributes a non-null prefix), so this plugin's `CurrentScan` rows never get picked up by the per-plugin device-update loop at all — the plugin can *insert* into `CurrentScan` but never actually confirm/update a device's presence.
|
||||
- **A setting's `dataType` and `default_value` must actually agree.** `dataType: "array"` (or `"object"`) means `default_value` must be a real JSON literal for that shape — `'["default"]'`, not the bare string `"default"`. `setting_value_to_python_type()` (`server/helper.py`) `json.loads()`s the default at runtime; a bare string fails that parse, silently logs a decode error, and returns `[]` instead of your intended default — this shipped for real in `devParentRelType`/`UI_theme`/`UI_TOPOLOGY_ORDER` before being caught. If `elementOptions` already sets `multiple`/`orderable: "false"`, that's a strong signal the setting is actually scalar and `dataType` should be `"string"`, not `"array"`, regardless of what UI widget (`select`, etc.) renders it.
|
||||
- **Only set `"allow_raw_text": true` on a display-only column type (`textarea_readonly`).** Every plugin-sourced field is HTML/control-char-stripped by default before it reaches the DB; see [Field Sanitization](#field-sanitization) below. `test_allow_raw_text_only_on_safe_types` (`test/plugins/test_plugin_conventions.py`) enforces the type restriction; it can't catch a column that legitimately needs the opt-out but is rendered somewhere unsafe, so use it only for values that are never interpreted as HTML.
|
||||
|
||||
---
|
||||
|
||||
@@ -313,6 +314,25 @@ To always map a static value (not read from plugin output):
|
||||
|
||||
Every `mapped_to_table: "CurrentScan"` plugin needs this `scanSourcePlugin` mapping — see the Conventions Checklist above for what breaks downstream if it's left out.
|
||||
|
||||
### Field Sanitization
|
||||
|
||||
Plugin output is untrusted: it's parsed from network responses, device-reported names, headers, and similar attacker-influenceable sources. `plugin_object_class.__init__` (`server/plugin.py`) strips HTML tag-delimiter (`<`, `>`) and control characters from every mapped `objectPrimaryId`/`objectSecondaryId`/`watchedValue1-4`/`extra`/`helpVal1-4` field by default, via `plugin_helper.sanitize_plugin_text()`, before the value is persisted. `foreignKey` is sanitized unconditionally the same way, since it has no `database_column_definitions` entry of its own to attach an opt-out to.
|
||||
|
||||
This is defense-in-depth, not a substitute for output encoding: every renderer must still escape on display. It's also not a validator: a MAC-shaped `objectPrimaryId` still goes through `normalize_mac()` separately, and a malformed value is stripped of dangerous characters, not rejected or blanked.
|
||||
|
||||
A column only needs to opt out (`"allow_raw_text": true`) if it legitimately displays raw text that sanitization would otherwise mangle, e.g. a publisher plugin's raw API response body, shown in a `textarea_readonly` field:
|
||||
|
||||
```json
|
||||
{
|
||||
"column": "watchedValue2",
|
||||
"type": "textarea_readonly",
|
||||
"allow_raw_text": true,
|
||||
"name": [{"language_code": "en_us", "string": "Response"}]
|
||||
}
|
||||
```
|
||||
|
||||
Restrict this to types that are never rendered as HTML; see the Conventions Checklist above.
|
||||
|
||||
### Import Behavior Columns (`scanCreatesDevice`, `scanNotificationMode`, `scanPresence`)
|
||||
|
||||
Three optional columns on `CurrentScan` control what happens once a row reaches it — see [Plugin Import Behavior](PLUGINS_IMPORT_BEHAVIOR.md) for the full contract (allowed values, defaults, downstream effects). All three default to today's behavior if never mapped, so existing plugins need no changes.
|
||||
|
||||
Reference in new issue
Block a user