fix(ook): address Copilot review — stale process, XSS presets, localStorage

- Detect crashed rtl_433 process via poll() and clean up stale state
  instead of permanently blocking restarts with 409
- Replace innerHTML+onclick preset rendering with createElement/addEventListener
  to prevent XSS via crafted localStorage frequency values
- Normalize preset frequencies to toFixed(3) on save and render
- Add try/catch + shape validation to loadPresets() for corrupted localStorage

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
thatsatechnique
2026-03-05 17:21:14 -08:00
parent 7b4ad20805
commit 91989a0216
2 changed files with 36 additions and 10 deletions

View File

@@ -63,7 +63,11 @@ def start_ook() -> Response:
with app_module.ook_lock:
if app_module.ook_process:
return jsonify({'status': 'error', 'message': 'OOK decoder already running'}), 409
# If the process exited/crashed, clean up stale state and allow restart
if app_module.ook_process.poll() is not None:
cleanup_ook(emit_status=False)
else:
return jsonify({'status': 'error', 'message': 'OOK decoder already running'}), 409
data = request.json or {}

View File

@@ -414,7 +414,16 @@ var OokMode = (function () {
function loadPresets() {
var saved = localStorage.getItem('ookFreqPresets');
return saved ? JSON.parse(saved) : DEFAULT_FREQ_PRESETS.slice();
if (!saved) return DEFAULT_FREQ_PRESETS.slice();
try {
var parsed = JSON.parse(saved);
if (Array.isArray(parsed) && parsed.every(function (v) {
return typeof v === 'string' && Number.isFinite(Number.parseFloat(v));
})) {
return parsed;
}
} catch (_) {}
return DEFAULT_FREQ_PRESETS.slice();
}
function savePresets(presets) {
@@ -425,24 +434,37 @@ var OokMode = (function () {
var container = document.getElementById('ookPresetButtons');
if (!container) return;
var presets = loadPresets();
container.innerHTML = presets.map(function (freq) {
return '<button class="preset-btn" onclick="OokMode.setFreq(\'' + freq + '\')" ' +
'oncontextmenu="OokMode.removePreset(\'' + freq + '\'); return false;" ' +
'title="Right-click to remove">' + freq + '</button>';
}).join('');
container.textContent = '';
presets.forEach(function (freq) {
var num = Number.parseFloat(freq);
if (!Number.isFinite(num)) return;
var normalized = num.toFixed(3);
var btn = document.createElement('button');
btn.className = 'preset-btn';
btn.title = 'Right-click to remove';
btn.textContent = normalized;
btn.addEventListener('click', function () { OokMode.setFreq(normalized); });
btn.addEventListener('contextmenu', function (e) {
e.preventDefault();
OokMode.removePreset(normalized);
});
container.appendChild(btn);
});
}
function addPreset() {
var input = document.getElementById('ookNewPresetFreq');
if (!input) return;
var freq = input.value.trim();
if (!freq || isNaN(parseFloat(freq))) {
var num = Number.parseFloat(freq);
if (!freq || !Number.isFinite(num)) {
alert('Enter a valid frequency (MHz)');
return;
}
var normalized = num.toFixed(3);
var presets = loadPresets();
if (presets.indexOf(freq) === -1) {
presets.push(freq);
if (presets.indexOf(normalized) === -1) {
presets.push(normalized);
savePresets(presets);
renderPresets();
}