diff --git a/.claude/skills/plugin-development/SKILL.md b/.claude/skills/plugin-development/SKILL.md index 9bf58f0b9..4d404d4ce 100644 --- a/.claude/skills/plugin-development/SKILL.md +++ b/.claude/skills/plugin-development/SKILL.md @@ -48,6 +48,8 @@ plugin_objects.write_result_file() # exactly once, at the end Full column spec: `docs/PLUGINS_DEV_DATA_CONTRACT.md`. Note `helpVal1-4`/`watchedValue1-4` both preserve a real `0`/`False` you pass explicitly — only an omitted (`None`) value defaults to `""`. +Every mapped field (`objectPrimaryId`/`objectSecondaryId`/`watchedValue1-4`/`extra`/`helpVal1-4`) is HTML/control-char-stripped by default before it's persisted: plugin output is untrusted (network responses, device-reported names, etc.). `foreignKey` is always sanitized too, unconditionally. Only opt a column out (`"allow_raw_text": true`) if it's a display-only type (`textarea_readonly`); see `docs/PLUGINS_DEV.md#field-sanitization`. + ## Execution Phases | Phase | Trigger | @@ -71,7 +73,7 @@ Full column spec: `docs/PLUGINS_DEV_DATA_CONTRACT.md`. Note `helpVal1-4`/`watche ## Before Opening a PR -Check the plugin against the [Conventions Checklist](../../../docs/PLUGINS_DEV.md#conventions-checklist) — `RUN` default, schedule precedent, `RUN_TIMEOUT` semantics, reusing core settings instead of duplicating them, description length (renders in the Settings UI — keep it short), and the multi-instance settings pattern (nested array + popup-form, see `rest_import`, not a hardcoded "primary"/"secondary" pair). Most plugin PR review comments trace back to one of these, and `test/plugins/test_plugin_conventions.py` mechanically enforces the RUN-default, description-length, hardcoded-default-drift, RUN_TIMEOUT-reuse-in-loop, and array/object dataType-default_value-mismatch items — run it after touching a plugin. +Check the plugin against the [Conventions Checklist](../../../docs/PLUGINS_DEV.md#conventions-checklist) — `RUN` default, schedule precedent, `RUN_TIMEOUT` semantics, reusing core settings instead of duplicating them, description length (renders in the Settings UI — keep it short), the multi-instance settings pattern (nested array + popup-form, see `rest_import`, not a hardcoded "primary"/"secondary" pair), and `allow_raw_text` restricted to display-only column types. Most plugin PR review comments trace back to one of these, and `test/plugins/test_plugin_conventions.py` mechanically enforces the RUN-default, description-length, hardcoded-default-drift, RUN_TIMEOUT-reuse-in-loop, array/object dataType-default_value-mismatch, and allow_raw_text-type-restriction items — run it after touching a plugin. ## Starting Point diff --git a/.claude/skills/skill-hygiene/SKILL.md b/.claude/skills/skill-hygiene/SKILL.md index 240bcaeb4..1ec1e921f 100644 --- a/.claude/skills/skill-hygiene/SKILL.md +++ b/.claude/skills/skill-hygiene/SKILL.md @@ -27,7 +27,11 @@ Cut anything that narrates the past instead of stating the present: ## Rule 2: plain words, fewer words -If a shorter or simpler phrasing says the same thing, use it. Cut qualifiers that don't change the meaning ("actually," "really," "genuinely," "in this exact process"). Prefer a plain verb over a nominalization. A dense skill with real information beats a padded one — trim narration and hedging before trimming facts. +If a shorter or simpler phrasing says the same thing, use it. Cut qualifiers that don't change the meaning ("actually," "really," "genuinely," "in this exact process"). Prefer a plain verb over a nominalization. A dense skill with real information beats a padded one: trim narration and hedging before trimming facts. + +## Rule 3: no em-dashes + +Never use an em-dash ("—"), in a skill or anywhere else this session writes prose (docs, code comments, PRDs, commit messages, chat replies). Use a period, comma, colon, semicolon, or parentheses instead, whichever actually fits the sentence. ## Sweep before calling a skill clean @@ -35,9 +39,10 @@ Run this across `.claude/skills/`, `.gemini/skills/`, `.github/skills/` (or a si ```bash grep -rniE "as of 202|caught in review|caught mid-review|correction:|correction \(|shipped for real|previously|used to be|no longer|originally|was later|historically|in the past|distilled from|real mistake|it turned out|turns out|discovered that|during a past" .claude/skills/ .gemini/skills/ .github/skills/ +grep -rn "—" .claude/skills/ .gemini/skills/ .github/skills/ ``` -Read every hit in context — some are legitimate (a rule instructing PRD authors to write correction trails, or "previously down" describing device state, are not violations). Fix the ones that narrate the skill's own history instead of the system's current behavior. +Read every hit in context: some are legitimate (a rule instructing PRD authors to write correction trails, or "previously down" describing device state, are not violations). Fix the ones that narrate the skill's own history instead of the system's current behavior, and replace every em-dash hit per Rule 3. ## Also applies to: research/audit docs diff --git a/.gemini/skills/plugin-development/plugin-skill.md b/.gemini/skills/plugin-development/plugin-skill.md index ba28a1528..2f1666225 100644 --- a/.gemini/skills/plugin-development/plugin-skill.md +++ b/.gemini/skills/plugin-development/plugin-skill.md @@ -60,6 +60,8 @@ plugin_objects.write_result_file() # Exactly once at end **Important:** The backend processes and deletes the result file almost immediately. Retrieve it quickly if inspecting output. +Every mapped field (`objectPrimaryId`/`objectSecondaryId`/`watchedValue1-4`/`extra`/`helpVal1-4`) is HTML/control-char-stripped by default before it's persisted: plugin output is untrusted (network responses, device-reported names, etc.). `foreignKey` is always sanitized too, unconditionally. Only opt a column out (`"allow_raw_text": true`) if it's a display-only type (`textarea_readonly`); see `docs/PLUGINS_DEV.md#field-sanitization`. + ## Execution Phases | Phase | Trigger | @@ -83,7 +85,7 @@ plugin_objects.write_result_file() # Exactly once at end ## Before Opening a PR -Check the plugin against the [Conventions Checklist](../../../docs/PLUGINS_DEV.md#conventions-checklist) in `docs/PLUGINS_DEV.md` — `RUN` default, schedule precedent, `RUN_TIMEOUT` semantics, reusing core settings, description length, and the multi-instance settings pattern. Most plugin PR review comments trace back to one of these. +Check the plugin against the [Conventions Checklist](../../../docs/PLUGINS_DEV.md#conventions-checklist) in `docs/PLUGINS_DEV.md` — `RUN` default, schedule precedent, `RUN_TIMEOUT` semantics, reusing core settings, description length, the multi-instance settings pattern, and `allow_raw_text` restricted to display-only column types. Most plugin PR review comments trace back to one of these. ## Starting Point diff --git a/.gemini/skills/skill-hygiene/SKILL.md b/.gemini/skills/skill-hygiene/SKILL.md index 240bcaeb4..1ec1e921f 100644 --- a/.gemini/skills/skill-hygiene/SKILL.md +++ b/.gemini/skills/skill-hygiene/SKILL.md @@ -27,7 +27,11 @@ Cut anything that narrates the past instead of stating the present: ## Rule 2: plain words, fewer words -If a shorter or simpler phrasing says the same thing, use it. Cut qualifiers that don't change the meaning ("actually," "really," "genuinely," "in this exact process"). Prefer a plain verb over a nominalization. A dense skill with real information beats a padded one — trim narration and hedging before trimming facts. +If a shorter or simpler phrasing says the same thing, use it. Cut qualifiers that don't change the meaning ("actually," "really," "genuinely," "in this exact process"). Prefer a plain verb over a nominalization. A dense skill with real information beats a padded one: trim narration and hedging before trimming facts. + +## Rule 3: no em-dashes + +Never use an em-dash ("—"), in a skill or anywhere else this session writes prose (docs, code comments, PRDs, commit messages, chat replies). Use a period, comma, colon, semicolon, or parentheses instead, whichever actually fits the sentence. ## Sweep before calling a skill clean @@ -35,9 +39,10 @@ Run this across `.claude/skills/`, `.gemini/skills/`, `.github/skills/` (or a si ```bash grep -rniE "as of 202|caught in review|caught mid-review|correction:|correction \(|shipped for real|previously|used to be|no longer|originally|was later|historically|in the past|distilled from|real mistake|it turned out|turns out|discovered that|during a past" .claude/skills/ .gemini/skills/ .github/skills/ +grep -rn "—" .claude/skills/ .gemini/skills/ .github/skills/ ``` -Read every hit in context — some are legitimate (a rule instructing PRD authors to write correction trails, or "previously down" describing device state, are not violations). Fix the ones that narrate the skill's own history instead of the system's current behavior. +Read every hit in context: some are legitimate (a rule instructing PRD authors to write correction trails, or "previously down" describing device state, are not violations). Fix the ones that narrate the skill's own history instead of the system's current behavior, and replace every em-dash hit per Rule 3. ## Also applies to: research/audit docs diff --git a/.github/skills/plugin-run-development/SKILL.md b/.github/skills/plugin-run-development/SKILL.md index acc9e60a6..2d4ef519d 100644 --- a/.github/skills/plugin-run-development/SKILL.md +++ b/.github/skills/plugin-run-development/SKILL.md @@ -63,6 +63,8 @@ plugin_objects.add_object(...) # During processing plugin_objects.write_result_file() # Exactly once at end ``` +Every mapped field (`objectPrimaryId`/`objectSecondaryId`/`watchedValue1-4`/`extra`/`helpVal1-4`) is HTML/control-char-stripped by default before it's persisted: plugin output is untrusted (network responses, device-reported names, etc.). `foreignKey` is always sanitized too, unconditionally. Only opt a column out (`"allow_raw_text": true`) if it's a display-only type (`textarea_readonly`); see `docs/PLUGINS_DEV.md#field-sanitization`. + ## Execution Phases - `once`: runs once at startup @@ -84,7 +86,7 @@ plugin_objects.write_result_file() # Exactly once at end ## Before Opening a PR -Check the plugin against the [Conventions Checklist](../../../docs/PLUGINS_DEV.md#conventions-checklist) in `docs/PLUGINS_DEV.md` — `RUN` default, schedule precedent, `RUN_TIMEOUT` semantics, reusing core settings, description length, and the multi-instance settings pattern. Most plugin PR review comments trace back to one of these. +Check the plugin against the [Conventions Checklist](../../../docs/PLUGINS_DEV.md#conventions-checklist) in `docs/PLUGINS_DEV.md` — `RUN` default, schedule precedent, `RUN_TIMEOUT` semantics, reusing core settings, description length, the multi-instance settings pattern, and `allow_raw_text` restricted to display-only column types. Most plugin PR review comments trace back to one of these. ## Starting Point diff --git a/.github/skills/skill-hygiene/SKILL.md b/.github/skills/skill-hygiene/SKILL.md index 609a733ec..65ac23dc7 100644 --- a/.github/skills/skill-hygiene/SKILL.md +++ b/.github/skills/skill-hygiene/SKILL.md @@ -27,7 +27,11 @@ Cut anything that narrates the past instead of stating the present: ## Rule 2: plain words, fewer words -If a shorter or simpler phrasing says the same thing, use it. Cut qualifiers that don't change the meaning ("actually," "really," "genuinely," "in this exact process"). Prefer a plain verb over a nominalization. A dense skill with real information beats a padded one — trim narration and hedging before trimming facts. +If a shorter or simpler phrasing says the same thing, use it. Cut qualifiers that don't change the meaning ("actually," "really," "genuinely," "in this exact process"). Prefer a plain verb over a nominalization. A dense skill with real information beats a padded one: trim narration and hedging before trimming facts. + +## Rule 3: no em-dashes + +Never use an em-dash ("—"), in a skill or anywhere else this session writes prose (docs, code comments, PRDs, commit messages, chat replies). Use a period, comma, colon, semicolon, or parentheses instead, whichever actually fits the sentence. ## Sweep before calling a skill clean @@ -35,9 +39,10 @@ Run this across `.claude/skills/`, `.gemini/skills/`, `.github/skills/` (or a si ```bash grep -rniE "as of 202|caught in review|caught mid-review|correction:|correction \(|shipped for real|previously|used to be|no longer|originally|was later|historically|in the past|distilled from|real mistake|it turned out|turns out|discovered that|during a past" .claude/skills/ .gemini/skills/ .github/skills/ +grep -rn "—" .claude/skills/ .gemini/skills/ .github/skills/ ``` -Read every hit in context — some are legitimate (a rule instructing PRD authors to write correction trails, or "previously down" describing device state, are not violations). Fix the ones that narrate the skill's own history instead of the system's current behavior. +Read every hit in context: some are legitimate (a rule instructing PRD authors to write correction trails, or "previously down" describing device state, are not violations). Fix the ones that narrate the skill's own history instead of the system's current behavior, and replace every em-dash hit per Rule 3. ## Also applies to: research/audit docs diff --git a/docs/PLUGINS_DEV.md b/docs/PLUGINS_DEV.md index eb538686e..290512cc7 100755 --- a/docs/PLUGINS_DEV.md +++ b/docs/PLUGINS_DEV.md @@ -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. diff --git a/server/plugin.py b/server/plugin.py index 181463cbd..0cad0a3b8 100755 --- a/server/plugin.py +++ b/server/plugin.py @@ -29,7 +29,7 @@ from models.notification_instance import NotificationInstance from messaging.in_app import write_notification from models.user_events_queue_instance import UserEventsQueueInstance from utils.crypto_utils import generate_deterministic_guid -from plugin_helper import normalize_mac +from plugin_helper import normalize_mac, sanitize_plugin_text # ------------------------------------------------------------------------------- @@ -1105,6 +1105,24 @@ def process_plugin_events(db, plugin, plugEventsArr): return +# Maps a database_column_definitions "column" name to the plugin_object_class +# attribute it feeds, for the sanitize-by-default pass below. Same vocabulary +# already used by the CurrentScan-mapping loop in process_plugin_events(). +_SANITIZE_COLUMN_MAP = { + "objectPrimaryId": "primaryId", + "objectSecondaryId": "secondaryId", + "watchedValue1": "watched1", + "watchedValue2": "watched2", + "watchedValue3": "watched3", + "watchedValue4": "watched4", + "extra": "extra", + "helpVal1": "helpVal1", + "helpVal2": "helpVal2", + "helpVal3": "helpVal3", + "helpVal4": "helpVal4", +} + + # ------------------------------------------------------------------------------- class plugin_object_class: def __init__(self, plugin, objDbRow): @@ -1129,6 +1147,34 @@ class plugin_object_class: self.helpVal2 = objDbRow[16] self.helpVal3 = objDbRow[17] self.helpVal4 = objDbRow[18] + + # Sanitize plugin-sourced text fields by default (defense-in-depth + # against a plugin persisting HTML-dangerous content) - skip a field + # only if its config.json entry declares "allow_raw_text": true. + for col in plugin.get("database_column_definitions", []): + attr = _SANITIZE_COLUMN_MAP.get(col.get("column")) + if attr is None or col.get("allow_raw_text"): + continue + raw_value = getattr(self, attr) + sanitized = sanitize_plugin_text(raw_value) + if sanitized != raw_value: + mylog( + "none", + f"[Plugins] {plugin['unique_prefix']}.{col['column']} sanitized (HTML/control chars stripped): {raw_value!r} -> {sanitized!r}", + ) + setattr(self, attr, sanitized) + + # foreignKey has no database_column_definitions entry of its own (no + # config flag to attach an opt-out to), so it's sanitized unconditionally. + if self.foreignKey: + sanitized = sanitize_plugin_text(self.foreignKey) + if sanitized != self.foreignKey: + mylog( + "none", + f"[Plugins] {plugin['unique_prefix']}.foreignKey sanitized (HTML/control chars stripped): {self.foreignKey!r} -> {sanitized!r}", + ) + self.foreignKey = sanitized + self.objectGUID = generate_deterministic_guid( self.pluginPref, self.primaryId, self.secondaryId ) diff --git a/server/plugins/_publisher_apprise/config.json b/server/plugins/_publisher_apprise/config.json index e34366017..fb20defda 100755 --- a/server/plugins/_publisher_apprise/config.json +++ b/server/plugins/_publisher_apprise/config.json @@ -5,7 +5,11 @@ "enabled": true, "data_source": "script", "show_ui": true, - "localized": ["display_name", "description", "icon"], + "localized": [ + "display_name", + "description", + "icon" + ], "display_name": [ { "language_code": "en_us", @@ -37,7 +41,9 @@ "type": "none", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -52,7 +58,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -71,7 +79,9 @@ "type": "url", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -86,7 +96,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -105,7 +117,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -120,7 +134,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -144,7 +160,9 @@ "param": "`Link`" } ], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -159,13 +177,16 @@ "type": "textarea_readonly", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", "string": "Result" } - ] + ], + "allow_raw_text": true }, { "column": "watchedValue3", @@ -174,7 +195,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -193,7 +216,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -212,7 +237,9 @@ "type": "textbox_save", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -248,7 +275,9 @@ "replacement": "
" } ], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -267,7 +296,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -283,16 +314,28 @@ "settings": [ { "function": "RUN", - "events": ["test"], + "events": [ + "test" + ], "type": { "dataType": "string", "elements": [ - { "elementType": "select", "elementOptions": [], "transformers": [] } + { + "elementType": "select", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "disabled", - "options": ["disabled", "on_notification"], - "localized": ["name", "description"], + "options": [ + "disabled", + "on_notification" + ], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -321,14 +364,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "readonly": "true" }], + "elementOptions": [ + { + "readonly": "true" + } + ], "transformers": [] } ] }, "default_value": "python3 /app/server/plugins/_publisher_apprise/apprise.py", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -357,14 +407,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "type": "number" }], + "elementOptions": [ + { + "type": "number" + } + ], "transformers": [] } ] }, "default_value": 10, "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -395,12 +452,19 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "input", "elementOptions": [], "transformers": [] } + { + "elementType": "input", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -427,12 +491,22 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "select", "elementOptions": [], "transformers": [] } + { + "elementType": "select", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "url", - "options": ["url", "tag"], - "localized": ["name", "description"], + "options": [ + "url", + "tag" + ], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -459,12 +533,19 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "input", "elementOptions": [], "transformers": [] } + { + "elementType": "input", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -491,12 +572,19 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "input", "elementOptions": [], "transformers": [] } + { + "elementType": "input", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -523,12 +611,22 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "select", "elementOptions": [], "transformers": [] } + { + "elementType": "select", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "html", - "options": ["html", "text"], - "localized": ["name", "description"], + "options": [ + "html", + "text" + ], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -557,14 +655,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "type": "number" }], + "elementOptions": [ + { + "type": "number" + } + ], "transformers": [] } ] }, "default_value": 1024, "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", diff --git a/server/plugins/_publisher_email/config.json b/server/plugins/_publisher_email/config.json index 5b6b56177..8cee4535d 100755 --- a/server/plugins/_publisher_email/config.json +++ b/server/plugins/_publisher_email/config.json @@ -5,7 +5,11 @@ "enabled": true, "data_source": "script", "show_ui": true, - "localized": ["display_name", "description", "icon"], + "localized": [ + "display_name", + "description", + "icon" + ], "display_name": [ { "language_code": "en_us", @@ -37,7 +41,9 @@ "type": "none", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -52,7 +58,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -71,7 +79,9 @@ "type": "url", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -86,7 +96,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -105,7 +117,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -120,7 +134,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -144,7 +160,9 @@ "param": "`${value}`" } ], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -159,13 +177,16 @@ "type": "textarea_readonly", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", "string": "Result" } - ] + ], + "allow_raw_text": true }, { "column": "watchedValue3", @@ -174,7 +195,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -193,7 +216,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -212,7 +237,9 @@ "type": "textbox_save", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -248,7 +275,9 @@ "replacement": "
" } ], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -267,7 +296,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -283,16 +314,28 @@ "settings": [ { "function": "RUN", - "events": ["test"], + "events": [ + "test" + ], "type": { "dataType": "string", "elements": [ - { "elementType": "select", "elementOptions": [], "transformers": [] } + { + "elementType": "select", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "disabled", - "options": ["disabled", "on_notification"], - "localized": ["name", "description"], + "options": [ + "disabled", + "on_notification" + ], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -321,14 +364,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "readonly": "true" }], + "elementOptions": [ + { + "readonly": "true" + } + ], "transformers": [] } ] }, "default_value": "python3 /app/server/plugins/_publisher_email/email_smtp.py", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -357,14 +407,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "type": "number" }], + "elementOptions": [ + { + "type": "number" + } + ], "transformers": [] } ] }, "default_value": 20, "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -395,12 +452,19 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "input", "elementOptions": [], "transformers": [] } + { + "elementType": "input", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -429,14 +493,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "type": "number" }], + "elementOptions": [ + { + "type": "number" + } + ], "transformers": [] } ] }, "default_value": 587, "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -465,14 +536,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "type": "checkbox" }], + "elementOptions": [ + { + "type": "checkbox" + } + ], "transformers": [] } ] }, "default_value": false, "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -499,12 +577,19 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "input", "elementOptions": [], "transformers": [] } + { + "elementType": "input", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -533,14 +618,23 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "type": "password" }], - "transformers": ["prefix|base64"] + "elementOptions": [ + { + "type": "password" + } + ], + "transformers": [ + "prefix|base64" + ] } ] }, "default_value": "", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -569,14 +663,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "type": "checkbox" }], + "elementOptions": [ + { + "type": "checkbox" + } + ], "transformers": [] } ] }, "default_value": false, "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -605,14 +706,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "type": "checkbox" }], + "elementOptions": [ + { + "type": "checkbox" + } + ], "transformers": [] } ] }, "default_value": false, "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -639,12 +747,19 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "input", "elementOptions": [], "transformers": [] } + { + "elementType": "input", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "to@email.com", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -671,12 +786,19 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "input", "elementOptions": [], "transformers": [] } + { + "elementType": "input", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "NetAlertX ", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -695,12 +817,19 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "input", "elementOptions": [], "transformers": [] } + { + "elementType": "input", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "NetAlertX Report", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", diff --git a/server/plugins/_publisher_ntfy/config.json b/server/plugins/_publisher_ntfy/config.json index 66ffc4f97..376c607d7 100755 --- a/server/plugins/_publisher_ntfy/config.json +++ b/server/plugins/_publisher_ntfy/config.json @@ -5,7 +5,11 @@ "enabled": true, "data_source": "script", "show_ui": true, - "localized": ["display_name", "description", "icon"], + "localized": [ + "display_name", + "description", + "icon" + ], "display_name": [ { "language_code": "en_us", @@ -37,7 +41,9 @@ "type": "none", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -52,7 +58,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -71,7 +79,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -86,7 +96,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -106,7 +118,9 @@ "param": "`${value}`" } ], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -121,13 +135,16 @@ "type": "textarea_readonly", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", "string": "Response" } - ] + ], + "allow_raw_text": true }, { "column": "watchedValue3", @@ -136,7 +153,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -151,7 +170,9 @@ "type": "device_mac", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -166,7 +187,9 @@ "type": "textbox_save", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -202,7 +225,9 @@ "replacement": "
" } ], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -221,7 +246,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -237,16 +264,28 @@ "settings": [ { "function": "RUN", - "events": ["test"], + "events": [ + "test" + ], "type": { "dataType": "string", "elements": [ - { "elementType": "select", "elementOptions": [], "transformers": [] } + { + "elementType": "select", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "disabled", - "options": ["disabled", "on_notification"], - "localized": ["name", "description"], + "options": [ + "disabled", + "on_notification" + ], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -275,14 +314,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "readonly": "true" }], + "elementOptions": [ + { + "readonly": "true" + } + ], "transformers": [] } ] }, "default_value": "python3 /app/server/plugins/_publisher_ntfy/ntfy.py", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -311,14 +357,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "type": "number" }], + "elementOptions": [ + { + "type": "number" + } + ], "transformers": [] } ] }, "default_value": 10, "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -349,12 +402,19 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "input", "elementOptions": [], "transformers": [] } + { + "elementType": "input", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "https://ntfy.sh", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -381,12 +441,19 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "input", "elementOptions": [], "transformers": [] } + { + "elementType": "input", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -422,7 +489,10 @@ }, "default_value": "", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -441,12 +511,19 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "input", "elementOptions": [], "transformers": [] } + { + "elementType": "input", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -475,14 +552,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "type": "password" }], + "elementOptions": [ + { + "type": "password" + } + ], "transformers": [] } ] }, "default_value": "", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -509,12 +593,25 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "select", "elementOptions": [], "transformers": [] } + { + "elementType": "select", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "urgent", - "options": ["urgent", "high", "default", "low", "min"], - "localized": ["name", "description"], + "options": [ + "urgent", + "high", + "default", + "low", + "min" + ], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -535,14 +632,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "type": "checkbox" }], + "elementOptions": [ + { + "type": "checkbox" + } + ], "transformers": [] } ] }, "default_value": true, "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -561,12 +665,23 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "input", "elementOptions": [{ "type": "password" }], "transformers": [] } + { + "elementType": "input", + "elementOptions": [ + { + "type": "password" + } + ], + "transformers": [] + } ] }, "default_value": "", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -588,21 +703,41 @@ { "elementType": "input", "elementOptions": [ - { "placeholder": "Enter value" }, - { "suffix": "_in" }, - { "cssClasses": "col-sm-10" }, - { "prefillValue": "null" } + { + "placeholder": "Enter value" + }, + { + "suffix": "_in" + }, + { + "cssClasses": "col-sm-10" + }, + { + "prefillValue": "null" + } ], "transformers": [] }, { "elementType": "button", "elementOptions": [ - { "sourceSuffixes": ["_in"] }, - { "separator": "" }, - { "cssClasses": "col-xs-12" }, - { "onClick": "addList(this, false)" }, - { "getStringKey": "Gen_Add" } + { + "sourceSuffixes": [ + "_in" + ] + }, + { + "separator": "" + }, + { + "cssClasses": "col-xs-12" + }, + { + "onClick": "addList(this, false)" + }, + { + "getStringKey": "Gen_Add" + } ], "transformers": [] }, @@ -610,31 +745,57 @@ "elementType": "select", "elementHasInputValue": 1, "elementOptions": [ - { "multiple": "true" }, - { "readonly": "true" }, - { "editable": "true" } + { + "multiple": "true" + }, + { + "readonly": "true" + }, + { + "editable": "true" + } ], "transformers": [] }, { "elementType": "button", "elementOptions": [ - { "sourceSuffixes": [] }, - { "separator": "" }, - { "cssClasses": "col-xs-6" }, - { "onClick": "removeAllOptions(this)" }, - { "getStringKey": "Gen_Remove_All" } + { + "sourceSuffixes": [] + }, + { + "separator": "" + }, + { + "cssClasses": "col-xs-6" + }, + { + "onClick": "removeAllOptions(this)" + }, + { + "getStringKey": "Gen_Remove_All" + } ], "transformers": [] }, { "elementType": "button", "elementOptions": [ - { "sourceSuffixes": [] }, - { "separator": "" }, - { "cssClasses": "col-xs-6" }, - { "onClick": "removeFromList(this)" }, - { "getStringKey": "Gen_Remove_Last" } + { + "sourceSuffixes": [] + }, + { + "separator": "" + }, + { + "cssClasses": "col-xs-6" + }, + { + "onClick": "removeFromList(this)" + }, + { + "getStringKey": "Gen_Remove_Last" + } ], "transformers": [] } @@ -642,7 +803,10 @@ }, "default_value": [], "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", diff --git a/server/plugins/_publisher_pushover/config.json b/server/plugins/_publisher_pushover/config.json index 94d3dff77..ead78fe5d 100755 --- a/server/plugins/_publisher_pushover/config.json +++ b/server/plugins/_publisher_pushover/config.json @@ -5,7 +5,11 @@ "enabled": true, "data_source": "script", "show_ui": true, - "localized": ["display_name", "description", "icon"], + "localized": [ + "display_name", + "description", + "icon" + ], "display_name": [ { "language_code": "en_us", @@ -37,7 +41,9 @@ "type": "none", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -52,7 +58,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -71,7 +79,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -86,7 +96,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -106,7 +118,9 @@ "param": "`${value}`" } ], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -121,13 +135,16 @@ "type": "textarea_readonly", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", "string": "Response" } - ] + ], + "allow_raw_text": true }, { "column": "watchedValue3", @@ -136,7 +153,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -151,7 +170,9 @@ "type": "device_mac", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -166,7 +187,9 @@ "type": "textbox_save", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -202,7 +225,9 @@ "replacement": "
" } ], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -221,7 +246,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -237,16 +264,28 @@ "settings": [ { "function": "RUN", - "events": ["test"], + "events": [ + "test" + ], "type": { "dataType": "string", "elements": [ - { "elementType": "select", "elementOptions": [], "transformers": [] } + { + "elementType": "select", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "disabled", - "options": ["disabled", "on_notification"], - "localized": ["name", "description"], + "options": [ + "disabled", + "on_notification" + ], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -275,14 +314,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "readonly": "true" }], + "elementOptions": [ + { + "readonly": "true" + } + ], "transformers": [] } ] }, "default_value": "python3 /app/server/plugins/_publisher_pushover/pushover.py", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -311,14 +357,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "type": "number" }], + "elementOptions": [ + { + "type": "number" + } + ], "transformers": [] } ] }, "default_value": 10, "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -345,12 +398,19 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "input", "elementOptions": [], "transformers": [] } + { + "elementType": "input", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "USER_KEY", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -369,12 +429,19 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "input", "elementOptions": [], "transformers": [] } + { + "elementType": "input", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "APP_TOKEN", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -393,12 +460,19 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "input", "elementOptions": [], "transformers": [] } + { + "elementType": "input", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "DEVICE_NAME", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", diff --git a/server/plugins/_publisher_pushsafer/config.json b/server/plugins/_publisher_pushsafer/config.json index dec13c764..83d8282d7 100755 --- a/server/plugins/_publisher_pushsafer/config.json +++ b/server/plugins/_publisher_pushsafer/config.json @@ -5,7 +5,11 @@ "enabled": true, "data_source": "script", "show_ui": true, - "localized": ["display_name", "description", "icon"], + "localized": [ + "display_name", + "description", + "icon" + ], "display_name": [ { "language_code": "en_us", @@ -37,7 +41,9 @@ "type": "none", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -52,7 +58,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -71,7 +79,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -86,7 +96,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -106,7 +118,9 @@ "param": "`${value}`" } ], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -121,13 +135,16 @@ "type": "textarea_readonly", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", "string": "Response" } - ] + ], + "allow_raw_text": true }, { "column": "watchedValue3", @@ -136,7 +153,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -151,7 +170,9 @@ "type": "device_mac", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -166,7 +187,9 @@ "type": "textbox_save", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -202,7 +225,9 @@ "replacement": "
" } ], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -221,7 +246,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -237,16 +264,28 @@ "settings": [ { "function": "RUN", - "events": ["test"], + "events": [ + "test" + ], "type": { "dataType": "string", "elements": [ - { "elementType": "select", "elementOptions": [], "transformers": [] } + { + "elementType": "select", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "disabled", - "options": ["disabled", "on_notification"], - "localized": ["name", "description"], + "options": [ + "disabled", + "on_notification" + ], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -275,14 +314,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "readonly": "true" }], + "elementOptions": [ + { + "readonly": "true" + } + ], "transformers": [] } ] }, "default_value": "python3 /app/server/plugins/_publisher_pushsafer/pushsafer.py", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -311,14 +357,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "type": "number" }], + "elementOptions": [ + { + "type": "number" + } + ], "transformers": [] } ] }, "default_value": 10, "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -349,12 +402,19 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "input", "elementOptions": [], "transformers": [] } + { + "elementType": "input", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "ApiKey", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", diff --git a/server/plugins/_publisher_telegram/config.json b/server/plugins/_publisher_telegram/config.json index 7d1fa7658..293ed165f 100755 --- a/server/plugins/_publisher_telegram/config.json +++ b/server/plugins/_publisher_telegram/config.json @@ -5,7 +5,11 @@ "enabled": true, "data_source": "script", "show_ui": true, - "localized": ["display_name", "description", "icon"], + "localized": [ + "display_name", + "description", + "icon" + ], "display_name": [ { "language_code": "en_us", @@ -33,7 +37,9 @@ "type": "none", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -48,7 +54,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -67,7 +75,9 @@ "type": "url", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -82,7 +92,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -101,7 +113,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -116,7 +130,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -140,7 +156,9 @@ "param": "`${value}`" } ], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -155,13 +173,16 @@ "type": "textarea_readonly", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", "string": "Result" } - ] + ], + "allow_raw_text": true }, { "column": "watchedValue3", @@ -170,7 +191,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -189,7 +212,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -208,7 +233,9 @@ "type": "textbox_save", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -244,7 +271,9 @@ "replacement": "
" } ], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -263,7 +292,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -279,16 +310,28 @@ "settings": [ { "function": "RUN", - "events": ["test"], + "events": [ + "test" + ], "type": { "dataType": "string", "elements": [ - { "elementType": "select", "elementOptions": [], "transformers": [] } + { + "elementType": "select", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "disabled", - "options": ["disabled", "on_notification"], - "localized": ["name", "description"], + "options": [ + "disabled", + "on_notification" + ], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -313,14 +356,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "readonly": "true" }], + "elementOptions": [ + { + "readonly": "true" + } + ], "transformers": [] } ] }, "default_value": "python3 /app/server/plugins/_publisher_telegram/tg.py", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -349,14 +399,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "type": "number" }], + "elementOptions": [ + { + "type": "number" + } + ], "transformers": [] } ] }, "default_value": 10, "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -387,12 +444,19 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "input", "elementOptions": [], "transformers": [] } + { + "elementType": "input", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -411,12 +475,19 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "input", "elementOptions": [], "transformers": [] } + { + "elementType": "input", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -437,14 +508,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "type": "number" }], + "elementOptions": [ + { + "type": "number" + } + ], "transformers": [] } ] }, "default_value": 1024, "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", diff --git a/server/plugins/_publisher_webhook/config.json b/server/plugins/_publisher_webhook/config.json index bc30d73fc..60f179ba1 100755 --- a/server/plugins/_publisher_webhook/config.json +++ b/server/plugins/_publisher_webhook/config.json @@ -5,7 +5,11 @@ "enabled": true, "data_source": "script", "show_ui": true, - "localized": ["display_name", "description", "icon"], + "localized": [ + "display_name", + "description", + "icon" + ], "display_name": [ { "language_code": "en_us", @@ -37,7 +41,9 @@ "type": "none", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -52,7 +58,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -71,7 +79,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -86,7 +96,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -106,7 +118,9 @@ "param": "`${value}`" } ], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -121,13 +135,16 @@ "type": "textarea_readonly", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", "string": "Response (stdout)" } - ] + ], + "allow_raw_text": true }, { "column": "watchedValue3", @@ -136,13 +153,16 @@ "type": "textarea_readonly", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", "string": "Response (stderr)" } - ] + ], + "allow_raw_text": true }, { "column": "watchedValue4", @@ -151,7 +171,9 @@ "type": "device_mac", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -166,7 +188,9 @@ "type": "textbox_save", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -202,7 +226,9 @@ "replacement": "
" } ], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -221,7 +247,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -237,16 +265,28 @@ "settings": [ { "function": "RUN", - "events": ["test"], + "events": [ + "test" + ], "type": { "dataType": "string", "elements": [ - { "elementType": "select", "elementOptions": [], "transformers": [] } + { + "elementType": "select", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "disabled", - "options": ["disabled", "on_notification"], - "localized": ["name", "description"], + "options": [ + "disabled", + "on_notification" + ], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -275,14 +315,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "readonly": "true" }], + "elementOptions": [ + { + "readonly": "true" + } + ], "transformers": [] } ] }, "default_value": "python3 /app/server/plugins/_publisher_webhook/webhook.py", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -311,14 +358,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "type": "number" }], + "elementOptions": [ + { + "type": "number" + } + ], "transformers": [] } ] }, "default_value": 10, "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -349,12 +403,19 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "input", "elementOptions": [], "transformers": [] } + { + "elementType": "input", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -381,12 +442,23 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "select", "elementOptions": [], "transformers": [] } + { + "elementType": "select", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "json", - "options": ["json", "html", "text"], - "localized": ["name", "description"], + "options": [ + "json", + "html", + "text" + ], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -413,12 +485,23 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "select", "elementOptions": [], "transformers": [] } + { + "elementType": "select", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "GET", - "options": ["GET", "POST", "PUT"], - "localized": ["name", "description"], + "options": [ + "GET", + "POST", + "PUT" + ], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -447,14 +530,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "type": "number" }], + "elementOptions": [ + { + "type": "number" + } + ], "transformers": [] } ] }, "default_value": 1024, "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -481,12 +571,19 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "input", "elementOptions": [], "transformers": [] } + { + "elementType": "input", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", diff --git a/server/plugins/icmp_scan/config.json b/server/plugins/icmp_scan/config.json index 6b6faefc4..147e4abd7 100755 --- a/server/plugins/icmp_scan/config.json +++ b/server/plugins/icmp_scan/config.json @@ -2,7 +2,7 @@ "code_name": "icmp_scan", "unique_prefix": "ICMP", "plugin_type": "other", - "execution_order" : "Layer_4", + "execution_order": "Layer_4", "enabled": true, "data_source": "script", "mapped_to_table": "CurrentScan", @@ -16,7 +16,11 @@ "compare_use_quotes": true } ], - "localized": ["display_name", "description", "icon"], + "localized": [ + "display_name", + "description", + "icon" + ], "display_name": [ { "language_code": "en_us", @@ -46,11 +50,17 @@ "settings": [ { "function": "RUN", - "events": ["run"], + "events": [ + "run" + ], "type": { "dataType": "string", "elements": [ - { "elementType": "select", "elementOptions": [], "transformers": [] } + { + "elementType": "select", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "disabled", @@ -61,7 +71,10 @@ "schedule", "always_after_scan" ], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -77,11 +90,17 @@ }, { "function": "MODE", - "events": ["run"], + "events": [ + "run" + ], "type": { "dataType": "string", "elements": [ - { "elementType": "select", "elementOptions": [], "transformers": [] } + { + "elementType": "select", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "fping", @@ -89,7 +108,10 @@ "fping", "ping" ], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -110,14 +132,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "readonly": "true" }], + "elementOptions": [ + { + "readonly": "true" + } + ], "transformers": [] } ] }, "default_value": "python3 /app/server/plugins/icmp_scan/icmp.py", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -145,7 +174,10 @@ }, "default_value": "-i 0.5 -c 3", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -173,7 +205,10 @@ }, "default_value": ".*", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -255,7 +290,10 @@ }, "default_value": "*/5 * * * *", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -276,14 +314,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "type": "number" }], + "elementOptions": [ + { + "type": "number" + } + ], "transformers": [] } ] }, "default_value": 10, "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -304,17 +349,28 @@ "elements": [ { "elementType": "select", - "elementOptions": [{ "multiple": "true", "orderable": "true" }], + "elementOptions": [ + { + "multiple": "true", + "orderable": "true" + } + ], "transformers": [] } ] }, - "default_value": ["devMac", "devLastIP"], + "default_value": [ + "devMac", + "devLastIP" + ], "options": [ "devMac", "devLastIP" ], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -335,7 +391,12 @@ "elements": [ { "elementType": "select", - "elementOptions": [{ "multiple": "true", "orderable": "true" }], + "elementOptions": [ + { + "multiple": "true", + "orderable": "true" + } + ], "transformers": [] } ] @@ -347,7 +408,10 @@ "devName", "devSourcePlugin" ], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -448,7 +512,8 @@ "language_code": "en_us", "string": "Output" } - ] + ], + "allow_raw_text": true }, { "column": "watchedValue3", diff --git a/server/plugins/internet_ip/config.json b/server/plugins/internet_ip/config.json index 27a9656fe..e86a4c2a3 100755 --- a/server/plugins/internet_ip/config.json +++ b/server/plugins/internet_ip/config.json @@ -2,7 +2,7 @@ "code_name": "internet_ip", "unique_prefix": "INTRNT", "plugin_type": "device_scanner", - "execution_order" : "Layer_3", + "execution_order": "Layer_3", "enabled": true, "mapped_to_table": "CurrentScan", "data_filters": [ @@ -16,7 +16,11 @@ ], "data_source": "script", "show_ui": true, - "localized": ["display_name", "description", "icon"], + "localized": [ + "display_name", + "description", + "icon" + ], "display_name": [ { "language_code": "en_us", @@ -63,16 +67,30 @@ "settings": [ { "function": "RUN", - "events": ["run"], + "events": [ + "run" + ], "type": { "dataType": "string", "elements": [ - { "elementType": "select", "elementOptions": [], "transformers": [] } + { + "elementType": "select", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "disabled", - "options": ["disabled", "once", "schedule", "always_after_scan"], - "localized": ["name", "description"], + "options": [ + "disabled", + "once", + "schedule", + "always_after_scan" + ], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -105,14 +123,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "readonly": "true" }], + "elementOptions": [ + { + "readonly": "true" + } + ], "transformers": [] } ] }, "default_value": "python3 /app/server/plugins/internet_ip/script.py prev_ip={prev_ip} INTRNT_DIG_GET_IP_ARG={INTRNT_DIG_GET_IP_ARG}", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -147,12 +172,19 @@ "type": { "dataType": "string", "elements": [ - { "elementType": "input", "elementOptions": [], "transformers": [] } + { + "elementType": "input", + "elementOptions": [], + "transformers": [] + } ] }, "default_value": "-4 myip.opendns.com @resolver1.opendns.com", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -215,7 +247,10 @@ }, "default_value": "*/5 * * * *", "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -252,14 +287,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "type": "number" }], + "elementOptions": [ + { + "type": "number" + } + ], "transformers": [] } ] }, "default_value": 30, "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -296,14 +338,21 @@ "elements": [ { "elementType": "input", - "elementOptions": [{ "type": "number" }], + "elementOptions": [ + { + "type": "number" + } + ], "transformers": [] } ] }, "default_value": 3, "options": [], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -324,19 +373,29 @@ "elements": [ { "elementType": "select", - "elementOptions": [{ "multiple": "true", "orderable": "true"}], + "elementOptions": [ + { + "multiple": "true", + "orderable": "true" + } + ], "transformers": [] } ] }, - "default_value": ["watchedValue1"], + "default_value": [ + "watchedValue1" + ], "options": [ "watchedValue1", "watchedValue2", "watchedValue3", "watchedValue4" ], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -369,19 +428,30 @@ "elements": [ { "elementType": "select", - "elementOptions": [{ "multiple": "true", "orderable": "true"}], + "elementOptions": [ + { + "multiple": "true", + "orderable": "true" + } + ], "transformers": [] } ] }, - "default_value": ["new", "watched-changed"], + "default_value": [ + "new", + "watched-changed" + ], "options": [ "new", "watched-changed", "watched-not-changed", "missing-in-last-scan" ], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -418,17 +488,28 @@ "elements": [ { "elementType": "select", - "elementOptions": [{ "multiple": "true", "orderable": "true" }], + "elementOptions": [ + { + "multiple": "true", + "orderable": "true" + } + ], "transformers": [] } ] }, - "default_value": ["devMac", "devLastIP"], + "default_value": [ + "devMac", + "devLastIP" + ], "options": [ "devMac", "devLastIP" ], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -449,7 +530,12 @@ "elements": [ { "elementType": "select", - "elementOptions": [{ "multiple": "true", "orderable": "true" }], + "elementOptions": [ + { + "multiple": "true", + "orderable": "true" + } + ], "transformers": [] } ] @@ -461,7 +547,10 @@ "devType", "devSourcePlugin" ], - "localized": ["name", "description"], + "localized": [ + "name", + "description" + ], "name": [ { "language_code": "en_us", @@ -484,7 +573,9 @@ "type": "none", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -500,7 +591,9 @@ "type": "device_name_mac", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -524,7 +617,9 @@ "type": "device_ip", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -547,7 +642,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -566,13 +663,16 @@ "type": "textarea_readonly", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", "string": "Response" } - ] + ], + "allow_raw_text": true }, { "column": "watchedValue3", @@ -581,7 +681,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -597,7 +699,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -616,7 +720,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -639,7 +745,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -663,7 +771,9 @@ "type": "label", "default_value": "", "options": [], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", @@ -703,7 +813,9 @@ "replacement": "
" } ], - "localized": ["name"], + "localized": [ + "name" + ], "name": [ { "language_code": "en_us", diff --git a/server/plugins/plugin_helper.py b/server/plugins/plugin_helper.py index ba641b0ed..9d77e8e64 100755 --- a/server/plugins/plugin_helper.py +++ b/server/plugins/plugin_helper.py @@ -264,6 +264,30 @@ def normalize_mac(mac): return ':'.join(normalized_parts) +# ------------------------------------------------------------------- +_UNSAFE_TEXT_RE = re.compile(r'[<>\x00-\x08\x0b\x0c\x0e-\x1f]') # tag delimiters + control chars (tab/LF/CR kept) + + +def sanitize_plugin_text(value): + """ + Strip HTML tag-delimiter and control characters from a plugin-sourced + value. Applied to every plugin field by default in plugin_object_class + (server/plugin.py) - skip only via a column's config.json + 'allow_raw_text: true'. Defense-in-depth only - every renderer must + still escape on display, this does not replace that. Stripping (not + HTML-encoding) avoids double-encoding wherever the value is later + escaped for display. Deliberately does not truncate: length-limiting is + a data-integrity/DoS concern, not an XSS one, and belongs in a separate + mechanism if ever added. + + :param value: the plugin-sourced value to sanitize, or None. + :return: the sanitized value, or None if value was None. + """ + if value is None: + return value + return _UNSAFE_TEXT_RE.sub('', str(value)) + + # ------------------------------------------------------------------- def per_item_timeout(run_timeout, item_count, floor=1): """ diff --git a/test/plugins/test_plugin_conventions.py b/test/plugins/test_plugin_conventions.py index 13d8bfdfb..69188f177 100644 --- a/test/plugins/test_plugin_conventions.py +++ b/test/plugins/test_plugin_conventions.py @@ -369,3 +369,27 @@ def test_run_timeout_not_reused_in_loop(plugin_name): 'plugin_helper.per_item_timeout() for a runtime-variable-length one. ' "See docs/PLUGINS_DEV.md#conventions-checklist:\n" + "\n".join(issues) ) + + +# Column types where the rendered value is never HTML-interpreted and a plugin +# legitimately needs to show raw text (e.g. an API response body) - the only +# types allowed to opt out of the default sanitize-on-persist pass via +# "allow_raw_text": true. See docs/PLUGINS_DEV.md#conventions-checklist. +_ALLOW_RAW_TEXT_TYPES = {"textarea_readonly"} + + +@pytest.mark.parametrize('plugin_name', _PLUGIN_NAMES) +def test_allow_raw_text_only_on_safe_types(plugin_name): + config = _load_config(plugin_name) + for col in config.get('database_column_definitions', []): + if not col.get('allow_raw_text'): + continue + col_type = col.get('type') + assert col_type in _ALLOW_RAW_TEXT_TYPES, ( + f"{plugin_name}: column {col.get('column')!r} sets \"allow_raw_text\": true " + f"but has type {col_type!r}, not one of {sorted(_ALLOW_RAW_TEXT_TYPES)}. " + 'allow_raw_text skips HTML/control-char stripping for this field - only safe ' + 'on a type that is never rendered as HTML (e.g. a read-only textarea), and ' + 'still requires the renderer to escape on display - see ' + 'docs/PLUGINS_DEV.md#conventions-checklist.' + ) diff --git a/test/server/test_app_state_sse.py b/test/server/test_app_state_sse.py index ce4ec053b..a68156edc 100644 --- a/test/server/test_app_state_sse.py +++ b/test/server/test_app_state_sse.py @@ -1,11 +1,9 @@ """ Regression guard for server/app_state.py's updateState()/broadcast_state_update() call - pluginsStates must reach the SSE broadcast payload, not just the -persisted app_state.json. See -.gemini/internal-docs/PRDs/to_review/execution-queue-fe-locking-fix.md's -post-implementation addendum: the original broadcast_state_update() call -never passed pluginsStates, so front/js/ui_components.js's watchPluginState() -(fix C1) waited on an SSE event that could never arrive. +persisted app_state.json. The original broadcast_state_update() call never +passed pluginsStates, so front/js/ui_components.js's watchPluginState() waited +on an SSE event that could never arrive. """ import os diff --git a/test/server/test_plugin_object_field_sanitization.py b/test/server/test_plugin_object_field_sanitization.py new file mode 100644 index 000000000..408a15fce --- /dev/null +++ b/test/server/test_plugin_object_field_sanitization.py @@ -0,0 +1,135 @@ +""" +Tests for the sanitize-by-default pass in plugin_object_class.__init__. + +A plugin's watchedValue*/extra/helpVal*/foreignKey fields are attacker- +influenced (parsed from network responses, headers, etc.) but persisted and +later rendered. plugin_object_class strips HTML tag-delimiter and control +characters from every mapped field by default; a column opts out via +config.json's "allow_raw_text": true, restricted to display-only types +(textarea_readonly) by +test/plugins/test_plugin_conventions.py::test_allow_raw_text_only_on_safe_types. + +Run from inside the NetAlertX container - server/plugin.py isn't importable +standalone outside it (real conf/database/api imports). + + pytest "test/server/test_plugin_object_field_sanitization.py" -v +""" + +import os +import sys + +# --------------------------------------------------------------------------- +# Path setup +# --------------------------------------------------------------------------- +INSTALL_PATH = os.getenv("NETALERTX_APP", "/app") +sys.path.extend([f"{INSTALL_PATH}/server/plugins", f"{INSTALL_PATH}/server"]) + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) +from db_test_helpers import make_plugin_event_row # noqa: E402 + +import plugin as plugin_module # noqa: E402 +from plugin import plugin_object_class # noqa: E402 + +PREFIX = "TESTPLG" +PAYLOAD = "" +STRIPPED = "img src=x onerror=alert(1)" + + +def _publisher_plugin(allow_raw_text_on_watched2=False): + """Shaped like a real publisher plugin's config.json: watchedValue2 + holds a raw API-response body, optionally marked allow_raw_text.""" + return { + "unique_prefix": PREFIX, + "settings": [], + "database_column_definitions": [ + {"column": "watchedValue1", "type": "text"}, + { + "column": "watchedValue2", + "type": "textarea_readonly", + "allow_raw_text": allow_raw_text_on_watched2, + }, + {"column": "extra", "type": "text"}, + {"column": "helpVal1", "type": "text"}, + ], + } + + +class TestDefaultSanitization: + def test_watched_field_without_allow_raw_text_is_sanitized(self): + row = make_plugin_event_row(PREFIX, "id1", watched2=PAYLOAD) + obj = plugin_object_class(_publisher_plugin(), row) + assert obj.watched2 == STRIPPED + + def test_extra_and_helpval_are_sanitized_by_default(self): + row = make_plugin_event_row(PREFIX, "id1", extra=PAYLOAD, help_val1=PAYLOAD) + obj = plugin_object_class(_publisher_plugin(), row) + assert obj.extra == STRIPPED + assert obj.helpVal1 == STRIPPED + + def test_clean_text_passes_through_unchanged(self): + row = make_plugin_event_row(PREFIX, "id1", watched2="200 OK") + obj = plugin_object_class(_publisher_plugin(), row) + assert obj.watched2 == "200 OK" + + def test_preexisting_dirty_value_is_sanitized_on_readback(self): + """A row already persisted with an unsanitized value before this + mechanism shipped must be cleaned the next time it's read, not just + at write time - __init__ runs on every DB read, not only on insert.""" + row = make_plugin_event_row(PREFIX, "id1", watched2=PAYLOAD) + obj = plugin_object_class(_publisher_plugin(), row) + assert "<" not in obj.watched2 and ">" not in obj.watched2 + + +class TestAllowRawTextOptOut: + def test_column_with_allow_raw_text_true_is_not_sanitized(self): + row = make_plugin_event_row(PREFIX, "id1", watched2=PAYLOAD) + obj = plugin_object_class(_publisher_plugin(allow_raw_text_on_watched2=True), row) + assert obj.watched2 == PAYLOAD + + def test_other_fields_still_sanitized_when_one_column_opts_out(self): + row = make_plugin_event_row(PREFIX, "id1", watched2=PAYLOAD, extra=PAYLOAD) + obj = plugin_object_class(_publisher_plugin(allow_raw_text_on_watched2=True), row) + assert obj.watched2 == PAYLOAD # opted out + assert obj.extra == STRIPPED # no opt-out on this column + + +class TestForeignKeySanitization: + """foreignKey has no database_column_definitions entry of its own (no + config flag to attach an opt-out to) - always sanitized, unconditionally.""" + + def test_mac_shaped_foreign_key_passes_unchanged(self): + row = make_plugin_event_row(PREFIX, "id1", foreign_key="aa:bb:cc:dd:ee:ff") + obj = plugin_object_class(_publisher_plugin(), row) + assert obj.foreignKey == "aa:bb:cc:dd:ee:ff" + + def test_guid_shaped_foreign_key_passes_unchanged(self): + guid = "550e8400-e29b-41d4-a716-446655440000" + row = make_plugin_event_row(PREFIX, "id1", foreign_key=guid) + obj = plugin_object_class(_publisher_plugin(), row) + assert obj.foreignKey == guid + + def test_internet_sentinel_passes_unchanged(self): + row = make_plugin_event_row(PREFIX, "id1", foreign_key="internet") + obj = plugin_object_class(_publisher_plugin(), row) + assert obj.foreignKey == "internet" + + def test_injection_payload_is_stripped_not_blanked(self): + row = make_plugin_event_row(PREFIX, "id1", foreign_key=PAYLOAD) + obj = plugin_object_class(_publisher_plugin(), row) + assert obj.foreignKey == STRIPPED + + +class TestSanitizationLogging: + def test_mylog_fires_when_value_changes(self, monkeypatch): + calls = [] + monkeypatch.setattr(plugin_module, "mylog", lambda level, msg: calls.append((level, msg))) + row = make_plugin_event_row(PREFIX, "id1", watched2=PAYLOAD) + plugin_object_class(_publisher_plugin(), row) + assert any(level == "none" and "watchedValue2" in msg for level, msg in calls) + + def test_mylog_does_not_fire_for_clean_values(self, monkeypatch): + calls = [] + monkeypatch.setattr(plugin_module, "mylog", lambda level, msg: calls.append((level, msg))) + row = make_plugin_event_row(PREFIX, "id1", watched2="200 OK", foreign_key="aa:bb:cc:dd:ee:ff") + plugin_object_class(_publisher_plugin(), row) + assert calls == [] diff --git a/test/test_plugin_helper.py b/test/test_plugin_helper.py index 1f9f68ec8..87eb7ac58 100644 --- a/test/test_plugin_helper.py +++ b/test/test_plugin_helper.py @@ -1,4 +1,4 @@ -from server.plugins.plugin_helper import Plugin_Object, is_mac, normalize_mac, per_item_timeout +from server.plugins.plugin_helper import Plugin_Object, is_mac, normalize_mac, per_item_timeout, sanitize_plugin_text def test_is_mac_accepts_wildcard(): @@ -64,4 +64,21 @@ def test_watched_columns_unaffected_by_helpval_fix(): assert obj.watched1 == 0 assert obj.watched2 is False assert obj.watched3 == "" - assert obj.watched4 is None \ No newline at end of file + assert obj.watched4 is None + + +def test_sanitize_plugin_text_strips_tag_delimiters(): + assert sanitize_plugin_text("") == "img src=x onerror=alert(1)" + + +def test_sanitize_plugin_text_strips_control_chars_but_keeps_tab_and_newline(): + assert sanitize_plugin_text("a\x00b\x1fc\td\ne\rf") == "abc\td\ne\rf" + + +def test_sanitize_plugin_text_passes_through_clean_text_unchanged(): + text = "Living Room TV (Samsung) - " + ("x" * 2000) # no length cap + assert sanitize_plugin_text(text) == text + + +def test_sanitize_plugin_text_passes_none_through(): + assert sanitize_plugin_text(None) is None