secubox-openwrt/package/secubox/luci-app-iot-guard/htdocs/luci-static/resources/view/iot-guard/policies.js
CyberMind-FR 65a82f7fdd refactor(luci): Convert iot-guard and magicmirror2 to KissTheme CSS variables
Replace hardcoded hex colors with KissTheme CSS variables:
- iot-guard: RISK_COLORS, status colors, table styling
- magicmirror2: :root variable definitions, nav bar, buttons, status badges

Files updated:
- iot-guard/devices.js, policies.js
- magicmirror2/dashboard.js, modules.js, overview.js, settings.js, webui.js

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-12 11:39:54 +01:00

207 lines
8.3 KiB
JavaScript

'use strict';
'require view';
'require rpc';
'require ui';
'require secubox/kiss-theme';
var callGetVendorRules = rpc.declare({
object: 'luci.iot-guard',
method: 'get_vendor_rules',
expect: {}
});
var callAddVendorRule = rpc.declare({
object: 'luci.iot-guard',
method: 'add_vendor_rule',
params: ['name', 'pattern', 'oui', 'class', 'risk', 'auto_isolate'],
expect: {}
});
var callDeleteVendorRule = rpc.declare({
object: 'luci.iot-guard',
method: 'delete_vendor_rule',
params: ['name'],
expect: {}
});
var RISK_COLORS = {
high: 'var(--kiss-red)',
medium: 'var(--kiss-orange)',
low: 'var(--kiss-green)'
};
var DEVICE_CLASSES = ['camera', 'thermostat', 'lighting', 'plug', 'assistant', 'media', 'lock', 'sensor', 'diy', 'mixed'];
var RISK_LEVELS = ['low', 'medium', 'high'];
return view.extend({
handleAddRule: function() {
var self = this;
var form = E('div', { 'style': 'min-width: 400px;' }, [
E('div', { 'style': 'margin-bottom: 15px;' }, [
E('label', { 'style': 'display: block; margin-bottom: 5px;' }, 'Rule Name'),
E('input', { 'type': 'text', 'id': 'rule-name', 'style': 'width: 100%; padding: 8px;' })
]),
E('div', { 'style': 'margin-bottom: 15px;' }, [
E('label', { 'style': 'display: block; margin-bottom: 5px;' }, 'Vendor Pattern (regex)'),
E('input', { 'type': 'text', 'id': 'rule-pattern', 'style': 'width: 100%; padding: 8px;', 'placeholder': 'Ring|Amazon Ring' })
]),
E('div', { 'style': 'margin-bottom: 15px;' }, [
E('label', { 'style': 'display: block; margin-bottom: 5px;' }, 'OUI Prefix'),
E('input', { 'type': 'text', 'id': 'rule-oui', 'style': 'width: 100%; padding: 8px;', 'placeholder': '40:B4:CD' })
]),
E('div', { 'style': 'margin-bottom: 15px;' }, [
E('label', { 'style': 'display: block; margin-bottom: 5px;' }, 'Device Class'),
E('select', { 'id': 'rule-class', 'style': 'width: 100%; padding: 8px;' },
DEVICE_CLASSES.map(function(c) { return E('option', { 'value': c }, c); })
)
]),
E('div', { 'style': 'margin-bottom: 15px;' }, [
E('label', { 'style': 'display: block; margin-bottom: 5px;' }, 'Risk Level'),
E('select', { 'id': 'rule-risk', 'style': 'width: 100%; padding: 8px;' },
RISK_LEVELS.map(function(r) { return E('option', { 'value': r }, r); })
)
]),
E('div', { 'style': 'margin-bottom: 15px;' }, [
E('label', {}, [
E('input', { 'type': 'checkbox', 'id': 'rule-auto-isolate', 'style': 'margin-right: 8px;' }),
'Auto-isolate matching devices'
])
])
]);
ui.showModal('Add Vendor Rule', [
form,
E('div', { 'class': 'right', 'style': 'margin-top: 20px;' }, [
E('button', { 'class': 'kiss-btn', 'click': ui.hideModal }, 'Cancel'),
E('button', {
'class': 'kiss-btn kiss-btn-blue',
'style': 'margin-left: 10px;',
'click': function() {
var name = document.getElementById('rule-name').value.trim().replace(/[^a-z0-9_]/gi, '_');
var pattern = document.getElementById('rule-pattern').value.trim();
var oui = document.getElementById('rule-oui').value.trim();
var deviceClass = document.getElementById('rule-class').value;
var risk = document.getElementById('rule-risk').value;
var autoIsolate = document.getElementById('rule-auto-isolate').checked ? '1' : '0';
if (!name) {
ui.addNotification(null, E('p', 'Rule name is required'), 'error');
return;
}
ui.hideModal();
callAddVendorRule(name, pattern, oui, deviceClass, risk, autoIsolate).then(function() {
ui.addNotification(null, E('p', 'Rule added: ' + name), 'success');
window.location.reload();
});
}
}, 'Add Rule')
])
]);
},
handleDeleteRule: function(name) {
if (!confirm('Delete rule "' + name + '"?')) return;
callDeleteVendorRule(name).then(function() {
ui.addNotification(null, E('p', 'Rule deleted'), 'success');
window.location.reload();
});
},
load: function() {
return callGetVendorRules();
},
render: function(data) {
var rules = (data && data.rules) || [];
var self = this;
var rows = rules.map(function(r) {
var riskColor = RISK_COLORS[r.risk_level] || 'var(--kiss-muted)';
return E('tr', {}, [
E('td', { 'style': 'padding: 10px; border-bottom: 1px solid var(--kiss-line);' }, r.name),
E('td', { 'style': 'padding: 10px; border-bottom: 1px solid var(--kiss-line); font-family: monospace;' }, r.pattern || '-'),
E('td', { 'style': 'padding: 10px; border-bottom: 1px solid var(--kiss-line); font-family: monospace;' }, r.oui_prefix || '-'),
E('td', { 'style': 'padding: 10px; border-bottom: 1px solid var(--kiss-line);' }, r.device_class),
E('td', { 'style': 'padding: 10px; border-bottom: 1px solid var(--kiss-line); color: ' + riskColor + ';' }, r.risk_level),
E('td', { 'style': 'padding: 10px; border-bottom: 1px solid var(--kiss-line);' }, r.auto_isolate ? 'Yes' : 'No'),
E('td', { 'style': 'padding: 10px; border-bottom: 1px solid var(--kiss-line);' }, [
E('button', {
'class': 'kiss-btn kiss-btn-red btn-sm',
'style': 'padding: 2px 8px; font-size: 12px;',
'click': function() { self.handleDeleteRule(r.name); }
}, 'Delete')
])
]);
});
var content = E('div', { 'class': 'cbi-map', 'style': 'padding: 20px;' }, [
E('h2', {}, 'IoT Guard Policies'),
E('p', { 'style': 'color: var(--kiss-muted); margin-bottom: 20px;' },
'Vendor classification rules determine how devices are identified and their default risk level.'),
E('div', { 'style': 'margin-bottom: 20px;' }, [
E('button', {
'class': 'kiss-btn kiss-btn-blue',
'click': L.bind(this.handleAddRule, this)
}, 'Add Vendor Rule')
]),
E('h3', { 'style': 'margin-top: 25px;' }, 'Vendor Classification Rules'),
rules.length === 0 ?
E('div', { 'style': 'padding: 20px; color: var(--kiss-muted); text-align: center;' },
'No custom vendor rules defined. Using built-in classification.') :
E('table', { 'style': 'width: 100%; border-collapse: collapse;' }, [
E('thead', {}, E('tr', { 'style': 'background: var(--kiss-bg2);' }, [
E('th', { 'style': 'padding: 10px; text-align: left;' }, 'Name'),
E('th', { 'style': 'padding: 10px; text-align: left;' }, 'Vendor Pattern'),
E('th', { 'style': 'padding: 10px; text-align: left;' }, 'OUI Prefix'),
E('th', { 'style': 'padding: 10px; text-align: left;' }, 'Class'),
E('th', { 'style': 'padding: 10px; text-align: left;' }, 'Risk'),
E('th', { 'style': 'padding: 10px; text-align: left;' }, 'Auto-Isolate'),
E('th', { 'style': 'padding: 10px; text-align: left;' }, 'Actions')
])),
E('tbody', {}, rows)
]),
E('h3', { 'style': 'margin-top: 30px;' }, 'Device Classes'),
E('table', { 'style': 'width: 100%; border-collapse: collapse; margin-top: 10px;' }, [
E('thead', {}, E('tr', { 'style': 'background: var(--kiss-bg2);' }, [
E('th', { 'style': 'padding: 8px; text-align: left;' }, 'Class'),
E('th', { 'style': 'padding: 8px; text-align: left;' }, 'Description'),
E('th', { 'style': 'padding: 8px; text-align: left;' }, 'Default Risk')
])),
E('tbody', {}, [
this.renderClassRow('camera', 'IP cameras, video doorbells', 'medium'),
this.renderClassRow('thermostat', 'Smart thermostats, HVAC', 'low'),
this.renderClassRow('lighting', 'Smart bulbs, LED strips', 'low'),
this.renderClassRow('plug', 'Smart plugs, outlets', 'medium'),
this.renderClassRow('assistant', 'Voice assistants', 'medium'),
this.renderClassRow('media', 'TVs, streaming devices', 'medium'),
this.renderClassRow('lock', 'Smart locks', 'high'),
this.renderClassRow('sensor', 'Motion, door sensors', 'low'),
this.renderClassRow('diy', 'ESP32, Raspberry Pi', 'high'),
this.renderClassRow('mixed', 'Multi-function devices', 'high')
])
])
]);
return KissTheme.wrap(content, 'admin/secubox/security/iot-guard/policies');
},
renderClassRow: function(name, desc, risk) {
var color = RISK_COLORS[risk] || 'var(--kiss-muted)';
return E('tr', {}, [
E('td', { 'style': 'padding: 8px; border-bottom: 1px solid var(--kiss-line);' }, name),
E('td', { 'style': 'padding: 8px; border-bottom: 1px solid var(--kiss-line); color: var(--kiss-muted);' }, desc),
E('td', { 'style': 'padding: 8px; border-bottom: 1px solid var(--kiss-line); color: ' + color + ';' }, risk)
]);
},
handleSaveApply: null,
handleSave: null,
handleReset: null
});