mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-09-13 14:49:58 -04:00
References PR #1765. Docs: - Added PIHOLEMON to docs/PLUGINS.md and a new "Approach 4" section in docs/PIHOLE_GUIDE.md, leading with anomaly detection (the actual differentiator vs PIHOLEAPI) and explaining when to pick each plugin. - README/PLUGINS.md/config.json's UI-facing description all reordered and shortened to lead with anomaly detection instead of device import, and to drop implementation detail that belongs in the README, not the Settings page. - Trimmed the "Why not extend PIHOLEAPI" README section per feedback - useful context for a maintainer, not for an end user configuring the plugin. config.json / pihole_monitor.py: - RUN defaults to "disabled", matching every other non-core plugin. - VERIFY_SSL split into PRIMARY_VERIFY_SSL / SECONDARY_VERIFY_SSL - each instance can be http/https independently. Settings reordered so each *_VERIFY_SSL sits right under its matching *_PASSWORD. - GRAPHQL_TOKEN removed; graphql_token now reads the core API_TOKEN setting instead of a plugin-specific duplicate. - GRAPHQL_URL replaced with a GET_OWNER boolean - the endpoint is now derived from this app's own GRAPHQL_PORT (single source of truth) instead of a URL the user had to keep in sync by hand. - HISTORY_LENGTH (run count) replaced with HISTORY_DAYS (a real time window): state now stores [timestamp, delta] samples and trim_history() drops anything older than the window, so the baseline means the same thing regardless of schedule - a faster schedule adds more data points instead of shrinking the window. - STATE_FILE moved from the log folder to dbFolderPath, so the rolling anomaly baseline survives NetAlertX upgrades instead of being wiped with the logs. - netalertx_device_owner() (1 GraphQL call per device) replaced by netalertx_device_owners() (1 call per run, batched) - avoids N blocking round-trips on a large network. - Fixed a zero-baseline bug: `bool(... and baseline and ...)` silently exempted a device with an all-zero blocked-query history (0.0 is falsy in Python) from ever being flagged, even on its first real spike. Now checks `baseline is not None`. - Fixed the placeholder-MAC filter: only excluded the literal "ip-::", not Pi-hole's general "ip-<address>" placeholder pattern. Caught downstream by is_mac() either way, but now the actual placeholder check does what it looks like it does. - Fixed a cumulative-counter bug: Pi-hole's /api/stats/top_clients returns a count that's cumulative since FTL last started, not a per-interval or daily-resetting one (confirmed against FTL's own source and long-standing user reports that it doesn't reset at midnight). Comparing that raw total directly against a rolling average made any device's ordinary growing traffic look like an escalating anomaly. compute_delta() now diffs each run's raw count against the previous run's (state gained a per-key last_raw reference point alongside the delta history) - None (not 0) on the first-ever run for a device or right after a counter reset, so those runs re-anchor the reference point instead of fabricating or swallowing a delta. - RUN_SCHD default changed from every 6 hours to every 5 minutes now that the baseline window is real days, not run count, so a frequent schedule only adds data points instead of narrowing the window; also matches the default most other device-scanner plugins use. - RUN_SCHD gained the same live cron-validity checkmark ARPSCAN and other scanner plugins use (a ✓/✗ icon next to the field, validated client-side against a regex) - reuses the existing generic validateRegex() widget, nothing plugin-specific to build. Tests: 48 tests (up from 37), 99% line+branch coverage. Every fix above verified via mutation testing (deliberately broken, confirmed the relevant test fails, then restored).
575 lines
21 KiB
JSON
575 lines
21 KiB
JSON
{
|
|
"code_name": "pihole_monitor",
|
|
"unique_prefix": "PIHOLEMON",
|
|
"plugin_type": "device_scanner",
|
|
"execution_order": "Layer_0",
|
|
"enabled": true,
|
|
"data_source": "script",
|
|
"mapped_to_table": "CurrentScan",
|
|
"data_filters": [
|
|
{
|
|
"compare_column": "objectPrimaryId",
|
|
"compare_operator": "==",
|
|
"compare_field_id": "txtMacFilter",
|
|
"compare_js_template": "'{value}'.toString()",
|
|
"compare_use_quotes": true
|
|
}
|
|
],
|
|
"show_ui": true,
|
|
"localized": ["display_name", "description", "icon"],
|
|
"display_name": [
|
|
{
|
|
"language_code": "en_us",
|
|
"string": "Pi-hole Monitor"
|
|
}
|
|
],
|
|
"description": [
|
|
{
|
|
"language_code": "en_us",
|
|
"string": "Flags devices with a spike in blocked DNS queries - a common malware/compromised-device signature - and imports devices from one or two Pi-hole v6 instances."
|
|
}
|
|
],
|
|
"icon": [
|
|
{
|
|
"language_code": "en_us",
|
|
"string": "<i class=\"fa-solid fa-shield-halved\"></i>"
|
|
}
|
|
],
|
|
"params": [],
|
|
"settings": [
|
|
{
|
|
"function": "RUN",
|
|
"events": ["run"],
|
|
"type": {
|
|
"dataType": "string",
|
|
"elements": [
|
|
{ "elementType": "select", "elementOptions": [], "transformers": [] }
|
|
]
|
|
},
|
|
"default_value": "disabled",
|
|
"options": [
|
|
"disabled",
|
|
"once",
|
|
"schedule",
|
|
"always_after_scan",
|
|
"on_new_device",
|
|
"on_notification"
|
|
],
|
|
"localized": ["name", "description"],
|
|
"name": [{ "language_code": "en_us", "string": "When to run" }],
|
|
"description": [
|
|
{
|
|
"language_code": "en_us",
|
|
"string": "<code>schedule</code> is recommended over <code>always_after_scan</code>."
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"function": "RUN_SCHD",
|
|
"type": {
|
|
"dataType": "string",
|
|
"elements": [
|
|
{
|
|
"elementType": "span",
|
|
"elementOptions": [
|
|
{ "cssClasses": "input-group-addon validityCheck" },
|
|
{ "getStringKey": "Gen_ValidIcon" }
|
|
],
|
|
"transformers": []
|
|
},
|
|
{
|
|
"elementType": "input",
|
|
"elementOptions": [
|
|
{ "focusout": "validateRegex(this)" },
|
|
{ "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldKyg/Oi9bMC05XSspP3xcKi9bMC05XSspKSg/OiwoPzpbMC05XXxbMS01XVswLTldfFswLTldKy1bMC05XSsoPzovWzAtOV0rKT98XCovWzAtOV0rKSkqXHMrKD86XCp8KD86WzAtOV18MVswLTldfDJbMC0zXXxbMC05XSstWzAtOV0rKD86L1swLTldKyk/fFwqL1swLTldKykpKD86LCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldKyg/Oi9bMC05XSspP3xcKi9bMC05XSspKSpccysoPzpcKnwoPzpbMS05XXxbMTJdWzAtOV18M1swMV18WzAtOV0rLVswLTldKyg/Oi9bMC05XSspP3xcKi9bMC05XSspKSg/OiwoPzpbMS05XXxbMTJdWzAtOV18M1swMV18WzAtOV0rLVswLTldKyg/Oi9bMC05XSspP3xcKi9bMC05XSspKSpccysoPzpcKnwoPzpbMS05XXwxWzAtMl18WzAtOV0rLVswLTldKyg/Oi9bMC05XSspP3xcKi9bMC05XSspKSg/OiwoPzpbMS05XXwxWzAtMl18WzAtOV0rLVswLTldKyg/Oi9bMC05XSspP3xcKi9bMC05XSspKSpccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XSg/Oi9bMC05XSspP3xcKi9bMC05XSspKSg/OiwoPzpbMC02XXxbMC02XS1bMC02XSg/Oi9bMC05XSspP3xcKi9bMC05XSspKSok" }
|
|
],
|
|
"transformers": []
|
|
}
|
|
]
|
|
},
|
|
"default_value": "*/5 * * * *",
|
|
"options": [],
|
|
"localized": ["name", "description"],
|
|
"name": [{ "language_code": "en_us", "string": "Schedule" }],
|
|
"description": [
|
|
{ "language_code": "en_us", "string": "Only used when <code>RUN</code> is <code>schedule</code>. Default: every 5 minutes." }
|
|
]
|
|
},
|
|
{
|
|
"function": "SET_ALWAYS",
|
|
"type": {
|
|
"dataType": "array",
|
|
"elements": [
|
|
{ "elementType": "select", "elementOptions": [{ "multiple": "true", "orderable": "true" }], "transformers": [] }
|
|
]
|
|
},
|
|
"default_value": ["devMac", "devLastIP", "devName", "devVendor"],
|
|
"options": ["devMac", "devLastIP", "devName", "devVendor"],
|
|
"localized": ["name", "description"],
|
|
"name": [{ "language_code": "en_us", "string": "Set always columns" }],
|
|
"description": [
|
|
{
|
|
"language_code": "en_us",
|
|
"string": "These columns are treated as authoritative and overwrite existing values (including those set by other plugins), unless the current value was set by the user (<code>Source = USER</code> or <code>LOCKED</code>)."
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"function": "SET_EMPTY",
|
|
"type": {
|
|
"dataType": "array",
|
|
"elements": [
|
|
{ "elementType": "select", "elementOptions": [{ "multiple": "true", "orderable": "true" }], "transformers": [] }
|
|
]
|
|
},
|
|
"default_value": [],
|
|
"options": ["devMac", "devLastIP", "devName", "devVendor", "devType", "devSourcePlugin"],
|
|
"localized": ["name", "description"],
|
|
"name": [{ "language_code": "en_us", "string": "Set empty columns" }],
|
|
"description": [
|
|
{ "language_code": "en_us", "string": "These columns are only overwritten if they are empty or their Source is <code>NEWDEV</code>." }
|
|
]
|
|
},
|
|
{
|
|
"function": "PRIMARY_URL",
|
|
"type": {
|
|
"dataType": "string",
|
|
"elements": [
|
|
{ "elementType": "input", "elementOptions": [], "transformers": [] }
|
|
]
|
|
},
|
|
"default_value": "",
|
|
"options": [],
|
|
"localized": ["name", "description"],
|
|
"name": [{ "language_code": "en_us", "string": "Primary Pi-hole URL" }],
|
|
"description": [
|
|
{ "language_code": "en_us", "string": "Required. URL to your primary Pi-hole instance, for example <code>http://pi.hole:8080/</code>." }
|
|
]
|
|
},
|
|
{
|
|
"function": "PRIMARY_PASSWORD",
|
|
"type": {
|
|
"dataType": "string",
|
|
"elements": [
|
|
{ "elementType": "input", "elementOptions": [{ "type": "password" }], "transformers": [] }
|
|
]
|
|
},
|
|
"default_value": "",
|
|
"options": [],
|
|
"localized": ["name", "description"],
|
|
"name": [{ "language_code": "en_us", "string": "Primary Pi-hole password" }],
|
|
"description": [
|
|
{ "language_code": "en_us", "string": "Web UI password or App Password for the primary Pi-hole. A dedicated App Password is recommended over the admin login password." }
|
|
]
|
|
},
|
|
{
|
|
"function": "PRIMARY_VERIFY_SSL",
|
|
"type": {
|
|
"dataType": "boolean",
|
|
"elements": [
|
|
{ "elementType": "input", "elementOptions": [{ "type": "checkbox" }], "transformers": [] }
|
|
]
|
|
},
|
|
"default_value": true,
|
|
"options": [],
|
|
"localized": ["name", "description"],
|
|
"name": [{ "language_code": "en_us", "string": "Primary: Verify SSL" }],
|
|
"description": [
|
|
{
|
|
"language_code": "en_us",
|
|
"string": "Only relevant for an <code>https://</code> primary URL. Disabling this accepts any certificate, including one from an attacker on the network - install the self-signed CA as trusted instead if possible. Leave on unless you understand the trade-off."
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"function": "SECONDARY_URL",
|
|
"type": {
|
|
"dataType": "string",
|
|
"elements": [
|
|
{ "elementType": "input", "elementOptions": [], "transformers": [] }
|
|
]
|
|
},
|
|
"default_value": "",
|
|
"options": [],
|
|
"localized": ["name", "description"],
|
|
"name": [{ "language_code": "en_us", "string": "Secondary Pi-hole URL (optional)" }],
|
|
"description": [
|
|
{
|
|
"language_code": "en_us",
|
|
"string": "Optional. Leave empty if you only run one Pi-hole - most setups do. If set, this second instance (typically a failover/secondary resolver) is imported and its blocked-query counts combined with the primary's, so a device can't dodge detection by using the less-watched resolver, e.g. <code>http://192.168.1.11:8080/</code>."
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"function": "SECONDARY_PASSWORD",
|
|
"type": {
|
|
"dataType": "string",
|
|
"elements": [
|
|
{ "elementType": "input", "elementOptions": [{ "type": "password" }], "transformers": [] }
|
|
]
|
|
},
|
|
"default_value": "",
|
|
"options": [],
|
|
"localized": ["name", "description"],
|
|
"name": [{ "language_code": "en_us", "string": "Secondary Pi-hole password (optional)" }],
|
|
"description": [
|
|
{ "language_code": "en_us", "string": "Only used if Secondary Pi-hole URL is set." }
|
|
]
|
|
},
|
|
{
|
|
"function": "SECONDARY_VERIFY_SSL",
|
|
"type": {
|
|
"dataType": "boolean",
|
|
"elements": [
|
|
{ "elementType": "input", "elementOptions": [{ "type": "checkbox" }], "transformers": [] }
|
|
]
|
|
},
|
|
"default_value": true,
|
|
"options": [],
|
|
"localized": ["name", "description"],
|
|
"name": [{ "language_code": "en_us", "string": "Secondary: Verify SSL" }],
|
|
"description": [
|
|
{
|
|
"language_code": "en_us",
|
|
"string": "Only relevant for an <code>https://</code> secondary URL. Same trade-off as the primary instance's Verify SSL - independent because primary and secondary can each be on http:// or https:// separately."
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"function": "GET_OFFLINE",
|
|
"type": {
|
|
"dataType": "boolean",
|
|
"elements": [
|
|
{ "elementType": "input", "elementOptions": [{ "type": "checkbox" }], "transformers": [] }
|
|
]
|
|
},
|
|
"default_value": false,
|
|
"options": [],
|
|
"localized": ["name", "description"],
|
|
"name": [{ "language_code": "en_us", "string": "Import offline devices" }],
|
|
"description": [
|
|
{ "language_code": "en_us", "string": "If enabled, devices not seen recently (see below) are still imported/updated." }
|
|
]
|
|
},
|
|
{
|
|
"function": "CONSIDER_ONLINE",
|
|
"type": {
|
|
"dataType": "integer",
|
|
"elements": [
|
|
{ "elementType": "input", "elementOptions": [{ "type": "number" }], "transformers": [] }
|
|
]
|
|
},
|
|
"default_value": 300,
|
|
"options": [],
|
|
"localized": ["name", "description"],
|
|
"name": [{ "language_code": "en_us", "string": "Online if last seen (seconds)" }],
|
|
"description": [
|
|
{ "language_code": "en_us", "string": "Time in seconds to consider a device online." }
|
|
]
|
|
},
|
|
{
|
|
"function": "API_MAXCLIENTS",
|
|
"type": {
|
|
"dataType": "integer",
|
|
"elements": [
|
|
{ "elementType": "input", "elementOptions": [{ "type": "number" }], "transformers": [] }
|
|
]
|
|
},
|
|
"default_value": 500,
|
|
"options": [],
|
|
"localized": ["name", "description"],
|
|
"name": [{ "language_code": "en_us", "string": "Max clients per instance" }],
|
|
"description": [
|
|
{ "language_code": "en_us", "string": "Maximum number of devices requested from each Pi-hole instance's device list." }
|
|
]
|
|
},
|
|
{
|
|
"function": "FAKE_MAC",
|
|
"type": {
|
|
"dataType": "boolean",
|
|
"elements": [
|
|
{ "elementType": "input", "elementOptions": [{ "type": "checkbox" }], "transformers": [] }
|
|
]
|
|
},
|
|
"default_value": false,
|
|
"options": [],
|
|
"localized": ["name", "description"],
|
|
"name": [{ "language_code": "en_us", "string": "Generate fake MAC from IP" }],
|
|
"description": [
|
|
{
|
|
"language_code": "en_us",
|
|
"string": "Devices with a non-standard hardware address are skipped unless enabled, in which case a deterministic fake MAC is derived from the IP - accept the data-consistency trade-off that implies."
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"function": "GET_OWNER",
|
|
"type": {
|
|
"dataType": "boolean",
|
|
"elements": [
|
|
{ "elementType": "input", "elementOptions": [{ "type": "checkbox" }], "transformers": [] }
|
|
]
|
|
},
|
|
"default_value": true,
|
|
"options": [],
|
|
"localized": ["name", "description"],
|
|
"name": [{ "language_code": "en_us", "string": "Look up device owner" }],
|
|
"description": [
|
|
{
|
|
"language_code": "en_us",
|
|
"string": "If enabled, looks up an already-known device's owner (<code>devOwner</code>) - device import itself never depends on this. Disable if you don't use device owners or don't want the extra lookup."
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"function": "MULTIPLIER",
|
|
"type": {
|
|
"dataType": "string",
|
|
"elements": [
|
|
{ "elementType": "input", "elementOptions": [{ "type": "number" }], "transformers": [] }
|
|
]
|
|
},
|
|
"default_value": "4",
|
|
"options": [],
|
|
"localized": ["name", "description"],
|
|
"name": [{ "language_code": "en_us", "string": "Anomaly multiplier" }],
|
|
"description": [
|
|
{ "language_code": "en_us", "string": "Flag a device when the blocked queries it generated since the last run exceed this many times its own recent per-run average. Starting value, tune after a week of real data." }
|
|
]
|
|
},
|
|
{
|
|
"function": "MIN_BLOCKED",
|
|
"type": {
|
|
"dataType": "string",
|
|
"elements": [
|
|
{ "elementType": "input", "elementOptions": [{ "type": "number" }], "transformers": [] }
|
|
]
|
|
},
|
|
"default_value": "20",
|
|
"options": [],
|
|
"localized": ["name", "description"],
|
|
"name": [{ "language_code": "en_us", "string": "Minimum blocked queries" }],
|
|
"description": [
|
|
{ "language_code": "en_us", "string": "Ignore devices below this many blocked queries since the last run, even if the multiplier is exceeded (e.g. 1 -> 5 is technically 5x but is statistical noise). Depends on your Schedule - a more frequent schedule means fewer blocked queries accumulate per run, so a lower value may be needed." }
|
|
]
|
|
},
|
|
{
|
|
"function": "HISTORY_DAYS",
|
|
"type": {
|
|
"dataType": "string",
|
|
"elements": [
|
|
{ "elementType": "input", "elementOptions": [{ "type": "number" }], "transformers": [] }
|
|
]
|
|
},
|
|
"default_value": "7",
|
|
"options": [],
|
|
"localized": ["name", "description"],
|
|
"name": [{ "language_code": "en_us", "string": "History window (days)" }],
|
|
"description": [
|
|
{ "language_code": "en_us", "string": "How many days of recent runs to keep per device for the rolling baseline. A real time window, not a sample count - it means the same thing regardless of your <code>Schedule</code> above, and a faster schedule only adds more data points inside it. Default 7 (one week)." }
|
|
]
|
|
},
|
|
{
|
|
"function": "WATCH",
|
|
"type": {
|
|
"dataType": "array",
|
|
"elements": [
|
|
{ "elementType": "select", "elementOptions": [{ "multiple": "true", "orderable": "true" }], "transformers": [] }
|
|
]
|
|
},
|
|
"default_value": ["watchedValue4"],
|
|
"options": ["watchedValue1", "watchedValue2", "watchedValue3", "watchedValue4"],
|
|
"localized": ["name", "description"],
|
|
"name": [{ "language_code": "en_us", "string": "Watched" }],
|
|
"description": [
|
|
{
|
|
"language_code": "en_us",
|
|
"string": "Which columns count as \"changed\" for notification purposes. Default is <code>watchedValue4</code> (the anomaly/normal flag) only - <code>watchedValue3</code> (raw blocked-query count) fluctuates every run by design and would otherwise notify constantly."
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"function": "REPORT_ON",
|
|
"type": {
|
|
"dataType": "array",
|
|
"elements": [
|
|
{ "elementType": "select", "elementOptions": [{ "multiple": "true", "orderable": "true" }], "transformers": [] }
|
|
]
|
|
},
|
|
"default_value": ["watched-changed"],
|
|
"options": ["new", "watched-changed", "watched-not-changed", "missing-in-last-scan"],
|
|
"localized": ["name", "description"],
|
|
"name": [{ "language_code": "en_us", "string": "Report on" }],
|
|
"description": [
|
|
{
|
|
"language_code": "en_us",
|
|
"string": "Which statuses generate a notification, dispatched through whichever publisher(s) you already have enabled under Settings (ntfy, Apprise, email, ...) - this plugin never sends notifications on its own. Default: only <code>watched-changed</code>, i.e. when a device flips between normal and anomaly."
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"function": "CMD",
|
|
"type": {
|
|
"dataType": "string",
|
|
"elements": [
|
|
{ "elementType": "input", "elementOptions": [{ "readonly": "true" }], "transformers": [] }
|
|
]
|
|
},
|
|
"default_value": "python3 /app/server/plugins/pihole_monitor/pihole_monitor.py",
|
|
"options": [],
|
|
"localized": ["name", "description"],
|
|
"name": [{ "language_code": "en_us", "string": "Command" }],
|
|
"description": [{ "language_code": "en_us", "string": "Command to run. This can not be changed." }]
|
|
},
|
|
{
|
|
"function": "RUN_TIMEOUT",
|
|
"type": {
|
|
"dataType": "integer",
|
|
"elements": [
|
|
{ "elementType": "input", "elementOptions": [{ "type": "number" }], "transformers": [] }
|
|
]
|
|
},
|
|
"default_value": 60,
|
|
"options": [],
|
|
"localized": ["name", "description"],
|
|
"name": [{ "language_code": "en_us", "string": "Run timeout" }],
|
|
"description": [
|
|
{ "language_code": "en_us", "string": "Maximum time in seconds to wait for the script to finish (covers device list + stats for up to two Pi-hole instances, plus optional owner lookups)." }
|
|
]
|
|
}
|
|
],
|
|
"database_column_definitions": [
|
|
{
|
|
"column": "index",
|
|
"css_classes": "col-sm-1",
|
|
"show": true,
|
|
"type": "none",
|
|
"default_value": "",
|
|
"options": [],
|
|
"localized": ["name"],
|
|
"name": [{ "language_code": "en_us", "string": "Index" }]
|
|
},
|
|
{
|
|
"column": "objectPrimaryId",
|
|
"mapped_to_column": "scanMac",
|
|
"css_classes": "col-sm-2",
|
|
"show": true,
|
|
"type": "device_name_mac",
|
|
"default_value": "",
|
|
"options": [],
|
|
"localized": ["name"],
|
|
"name": [{ "language_code": "en_us", "string": "Device" }]
|
|
},
|
|
{
|
|
"column": "objectSecondaryId",
|
|
"mapped_to_column": "scanLastIP",
|
|
"css_classes": "col-sm-2",
|
|
"show": true,
|
|
"type": "device_ip",
|
|
"default_value": "",
|
|
"options": [],
|
|
"localized": ["name"],
|
|
"name": [{ "language_code": "en_us", "string": "IP" }]
|
|
},
|
|
{
|
|
"column": "watchedValue1",
|
|
"mapped_to_column": "scanName",
|
|
"css_classes": "col-sm-2",
|
|
"show": true,
|
|
"type": "label",
|
|
"default_value": "",
|
|
"options": [],
|
|
"localized": ["name"],
|
|
"name": [{ "language_code": "en_us", "string": "Name" }]
|
|
},
|
|
{
|
|
"column": "watchedValue2",
|
|
"mapped_to_column": "scanVendor",
|
|
"css_classes": "col-sm-2",
|
|
"show": true,
|
|
"type": "label",
|
|
"default_value": "",
|
|
"options": [],
|
|
"localized": ["name"],
|
|
"name": [{ "language_code": "en_us", "string": "Vendor" }]
|
|
},
|
|
{
|
|
"column": "watchedValue3",
|
|
"css_classes": "col-sm-1",
|
|
"show": true,
|
|
"type": "label",
|
|
"default_value": "",
|
|
"options": [],
|
|
"localized": ["name"],
|
|
"name": [{ "language_code": "en_us", "string": "Blocked queries" }]
|
|
},
|
|
{
|
|
"column": "watchedValue4",
|
|
"css_classes": "col-sm-1",
|
|
"show": true,
|
|
"type": "replace",
|
|
"default_value": "",
|
|
"options": [
|
|
{
|
|
"equals": "anomaly",
|
|
"replacement": "<div style='text-align:center'><i class='fa-solid fa-triangle-exclamation'></i></div>"
|
|
},
|
|
{
|
|
"equals": "normal",
|
|
"replacement": "<div style='text-align:center'><i class='fa-solid fa-circle-check'></i></div>"
|
|
}
|
|
],
|
|
"localized": ["name"],
|
|
"name": [{ "language_code": "en_us", "string": "Anomaly?" }]
|
|
},
|
|
{
|
|
"column": "extra",
|
|
"mapped_to_column": "scanSourcePlugin",
|
|
"css_classes": "col-sm-3",
|
|
"show": true,
|
|
"type": "label",
|
|
"default_value": "",
|
|
"options": [],
|
|
"localized": ["name"],
|
|
"name": [{ "language_code": "en_us", "string": "Detail" }]
|
|
},
|
|
{
|
|
"column": "dateTimeChanged",
|
|
"css_classes": "col-sm-2",
|
|
"show": true,
|
|
"type": "label",
|
|
"default_value": "",
|
|
"options": [],
|
|
"localized": ["name"],
|
|
"name": [{ "language_code": "en_us", "string": "Last checked" }]
|
|
},
|
|
{
|
|
"column": "status",
|
|
"css_classes": "col-sm-1",
|
|
"show": true,
|
|
"type": "replace",
|
|
"default_value": "",
|
|
"options": [
|
|
{
|
|
"equals": "watched-not-changed",
|
|
"replacement": "<div style='text-align:center'><i class='fa-solid fa-square-check'></i><div></div>"
|
|
},
|
|
{
|
|
"equals": "watched-changed",
|
|
"replacement": "<div style='text-align:center'><i class='fa-solid fa-triangle-exclamation'></i></div>"
|
|
},
|
|
{
|
|
"equals": "new",
|
|
"replacement": "<div style='text-align:center'><i class='fa-solid fa-circle-plus'></i></div>"
|
|
},
|
|
{
|
|
"equals": "missing-in-last-scan",
|
|
"replacement": "<div style='text-align:center'><i class='fa-solid fa-question'></i></div>"
|
|
}
|
|
],
|
|
"localized": ["name"],
|
|
"name": [{ "language_code": "en_us", "string": "Status" }]
|
|
}
|
|
]
|
|
}
|