mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-22 12:45:25 -04:00
CodeQL hardening for cookbook sync
This commit is contained in:
@@ -619,7 +619,7 @@ async function _startQueuedDownload(task) {
|
||||
if (t.sessionId === data.session_id) return false;
|
||||
return !(key && t.type === 'download' && t.status === 'queued' && _downloadDedupeKey(t) === key);
|
||||
});
|
||||
if (!found) tasks.push(_stripTaskSecrets(launchedTask));
|
||||
if (!found) tasks.push(_redactTaskForStorage(launchedTask));
|
||||
_saveTasks(tasks);
|
||||
_renderRunningTab();
|
||||
_startBackgroundMonitor();
|
||||
@@ -760,12 +760,22 @@ function _isTombstoned(id) {
|
||||
return ts != null && (Date.now() - ts) <= _TOMBSTONE_TTL_MS;
|
||||
}
|
||||
|
||||
function _stripTaskSecrets(task) {
|
||||
function _redactStoredText(value) {
|
||||
return String(value || '')
|
||||
.replace(/hf_[A-Za-z0-9]{20,}/g, '[redacted-token]')
|
||||
.replace(/((?:api[_-]?key|token|authorization|password|passwd|secret)\s*[=:]\s*)(["']?)[^\s"']+/gi, '$1$2[redacted]');
|
||||
}
|
||||
|
||||
function _redactTaskForStorage(task) {
|
||||
if (!task || typeof task !== 'object') return task;
|
||||
const safe = { ...task };
|
||||
if (typeof safe.output === 'string') safe.output = _redactStoredText(safe.output);
|
||||
if (safe.payload && typeof safe.payload === 'object') {
|
||||
safe.payload = { ...safe.payload };
|
||||
delete safe.payload.hf_token;
|
||||
delete safe.payload.hfToken;
|
||||
if (typeof safe.payload._cmd === 'string') safe.payload._cmd = _redactStoredText(safe.payload._cmd);
|
||||
if (typeof safe.payload.cmd === 'string') safe.payload.cmd = _redactStoredText(safe.payload.cmd);
|
||||
}
|
||||
return safe;
|
||||
}
|
||||
@@ -774,15 +784,14 @@ function _stripStateSecrets(state) {
|
||||
const safe = { ...state };
|
||||
if (safe.env && typeof safe.env === 'object') {
|
||||
const { hfToken, ...env } = safe.env;
|
||||
if (hfToken) env.hfToken = hfToken;
|
||||
safe.env = env;
|
||||
}
|
||||
if (Array.isArray(safe.tasks)) safe.tasks = safe.tasks.map(_stripTaskSecrets);
|
||||
if (Array.isArray(safe.tasks)) safe.tasks = safe.tasks.map(_redactTaskForStorage);
|
||||
return safe;
|
||||
}
|
||||
|
||||
export function _saveTasks(tasks) {
|
||||
localStorage.setItem(TASKS_KEY, JSON.stringify((tasks || []).map(_stripTaskSecrets)));
|
||||
localStorage.setItem(TASKS_KEY, JSON.stringify((tasks || []).map(_redactTaskForStorage)));
|
||||
_syncToServer();
|
||||
}
|
||||
|
||||
@@ -807,7 +816,7 @@ export function _addTask(sessionId, name, type, payload) {
|
||||
return !(key && t.type === 'download' && t.status === 'queued' && _downloadDedupeKey(t) === key);
|
||||
});
|
||||
}
|
||||
const task = _stripTaskSecrets({ id: sessionId, sessionId, name, type, status: 'running', output: '', ts: Date.now(), payload: payload || null, remoteHost, remoteServerKey, remoteServerName, sshPort, platform });
|
||||
const task = _redactTaskForStorage({ id: sessionId, sessionId, name, type, status: 'running', output: '', ts: Date.now(), payload: payload || null, remoteHost, remoteServerKey, remoteServerName, sshPort, platform });
|
||||
tasks.push(task);
|
||||
_saveTasks(tasks);
|
||||
// New action → collapse all other cards, leave only this one open.
|
||||
@@ -1102,14 +1111,24 @@ function _presetEnvFields(task) {
|
||||
};
|
||||
}
|
||||
|
||||
function _redactPresetForStorage(preset) {
|
||||
if (!preset || typeof preset !== 'object') return preset;
|
||||
const safe = { ...preset };
|
||||
if (typeof safe.cmd === 'string') safe.cmd = _redactStoredText(safe.cmd);
|
||||
if (typeof safe.command === 'string') safe.command = _redactStoredText(safe.command);
|
||||
delete safe.hf_token;
|
||||
delete safe.hfToken;
|
||||
return safe;
|
||||
}
|
||||
|
||||
function _saveTaskAsPreset(task, label) {
|
||||
const host = task.remoteHost || 'localhost';
|
||||
const portMatch = task.payload?._cmd?.match(/--port\s+(\d+)/);
|
||||
const port = portMatch ? portMatch[1] : '8000';
|
||||
const presets = _loadPresets();
|
||||
if (presets.some(p => p.cmd === task.payload._cmd)) return false;
|
||||
presets.push({ name: task.name, model: task.payload.repo_id, backend: 'vllm', host, port, cmd: task.payload._cmd, remoteHost: task.remoteHost || '', label: label || task.name, ..._presetEnvFields(task) });
|
||||
_savePresets(presets);
|
||||
presets.push(_redactPresetForStorage({ name: task.name, model: task.payload.repo_id, backend: 'vllm', host, port, cmd: task.payload._cmd, remoteHost: task.remoteHost || '', label: label || task.name, ..._presetEnvFields(task) }));
|
||||
_savePresets(presets.map(_redactPresetForStorage));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1152,7 +1171,7 @@ function _autoSaveWorkingConfig(task) {
|
||||
const existing = presets.find(p => p.cmd === cmd);
|
||||
if (existing) {
|
||||
task._autoSaved = true;
|
||||
if (!existing.confirmedWorking) { existing.confirmedWorking = true; _savePresets(presets); }
|
||||
if (!existing.confirmedWorking) { existing.confirmedWorking = true; _savePresets(presets.map(_redactPresetForStorage)); }
|
||||
return; // already saved → just confirm it, no duplicate, no toast
|
||||
}
|
||||
// Respect the per-model cap the manual save flow uses (max 5).
|
||||
@@ -1160,13 +1179,13 @@ function _autoSaveWorkingConfig(task) {
|
||||
const host = task.remoteHost || 'localhost';
|
||||
const portMatch = cmd.match(/--port[=\s]+(\d+)/);
|
||||
const port = portMatch ? portMatch[1] : '8000';
|
||||
presets.push({
|
||||
presets.push(_redactPresetForStorage({
|
||||
name: task.name, model, backend: 'vllm', host, port,
|
||||
cmd, remoteHost: task.remoteHost || '',
|
||||
label: _autoConfigLabel(task), confirmedWorking: true, autoSaved: true,
|
||||
..._presetEnvFields(task),
|
||||
});
|
||||
_savePresets(presets);
|
||||
}));
|
||||
_savePresets(presets.map(_redactPresetForStorage));
|
||||
task._autoSaved = true;
|
||||
uiModule.showToast('Saved working config');
|
||||
}
|
||||
@@ -1252,7 +1271,7 @@ export async function _syncFromServer() {
|
||||
merged.push(t);
|
||||
}
|
||||
}
|
||||
localStorage.setItem(TASKS_KEY, JSON.stringify(merged.map(_stripTaskSecrets)));
|
||||
localStorage.setItem(TASKS_KEY, JSON.stringify(merged.map(_redactTaskForStorage)));
|
||||
|
||||
if (state.env) {
|
||||
// The active server selection (remoteHost + its env/path/platform) is a
|
||||
@@ -3711,7 +3730,7 @@ async function _pollBackgroundStatus() {
|
||||
}
|
||||
}
|
||||
if (added > 0) {
|
||||
localStorage.setItem(TASKS_KEY, JSON.stringify(merged.map(_stripTaskSecrets)));
|
||||
localStorage.setItem(TASKS_KEY, JSON.stringify(merged.map(_redactTaskForStorage)));
|
||||
_renderRunningTab();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,28 @@ function _saveServeFavorites(favorites) {
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function _redactStoredCommand(value) {
|
||||
return String(value || '')
|
||||
.replace(/hf_[A-Za-z0-9]{20,}/g, '[redacted-token]')
|
||||
.replace(/((?:api[_-]?key|token|authorization|password|passwd|secret)\s*[=:]\s*)(["']?)[^\s"']+/gi, '$1$2[redacted]');
|
||||
}
|
||||
|
||||
function _redactServeStateForStorage(value) {
|
||||
if (!value || typeof value !== 'object') return value;
|
||||
if (Array.isArray(value)) return value.map(_redactServeStateForStorage);
|
||||
const safe = { ...value };
|
||||
for (const key of Object.keys(safe)) {
|
||||
if (/token|password|passwd|secret|api[_-]?key/i.test(key)) {
|
||||
delete safe[key];
|
||||
} else if (typeof safe[key] === 'string' && /cmd|command|args|env/i.test(key)) {
|
||||
safe[key] = _redactStoredCommand(safe[key]);
|
||||
} else if (safe[key] && typeof safe[key] === 'object') {
|
||||
safe[key] = _redactServeStateForStorage(safe[key]);
|
||||
}
|
||||
}
|
||||
return safe;
|
||||
}
|
||||
|
||||
function _isServeFavorite(repo) {
|
||||
return _loadServeFavorites().has(String(repo || ''));
|
||||
}
|
||||
@@ -2118,7 +2140,7 @@ function _rerenderCachedModels() {
|
||||
if (el.type === 'checkbox') fields[el.dataset.field] = el.checked;
|
||||
else fields[el.dataset.field] = el.value;
|
||||
});
|
||||
presets.push({ name: shortName, model: repo, cmd, remoteHost: host, port: fields.port || '8000', label, fields });
|
||||
presets.push(_redactServeStateForStorage({ name: shortName, model: repo, cmd, remoteHost: host, port: fields.port || '8000', label, fields }));
|
||||
_savePresets(presets);
|
||||
uiModule.showToast(`Saved "${label}"`);
|
||||
_updateSavedToggleLabel();
|
||||
@@ -2209,7 +2231,7 @@ function _rerenderCachedModels() {
|
||||
const target = _presetsForModel(cur, repo)[slotIdx];
|
||||
if (target) {
|
||||
target.favorite = !target.favorite;
|
||||
_savePresets(cur);
|
||||
_savePresets(cur.map(_redactServeStateForStorage));
|
||||
uiModule.showToast(target.favorite ? 'Favorited — pinned to top' : 'Unfavorited');
|
||||
_showSavedConfigMenu(anchor);
|
||||
}
|
||||
@@ -2223,7 +2245,7 @@ function _rerenderCachedModels() {
|
||||
if (toRemove) {
|
||||
const gi = cur.indexOf(toRemove);
|
||||
if (gi >= 0) cur.splice(gi, 1);
|
||||
_savePresets(cur);
|
||||
_savePresets(cur.map(_redactServeStateForStorage));
|
||||
}
|
||||
uiModule.showToast(`Deleted "${label}"`);
|
||||
_updateSavedToggleLabel();
|
||||
@@ -3205,7 +3227,7 @@ function _rerenderCachedModels() {
|
||||
const _saved = { ...serveState, _forceBackend: true };
|
||||
delete _saved._replaceTaskId;
|
||||
byRepo[repo] = _saved;
|
||||
localStorage.setItem(SERVE_STATE_KEY, JSON.stringify({ _byRepo: byRepo, _lastUsed: _saved }));
|
||||
localStorage.setItem(SERVE_STATE_KEY, JSON.stringify(_redactServeStateForStorage({ _byRepo: byRepo, _lastUsed: _saved })));
|
||||
} catch {}
|
||||
const origEnv = _envState.env;
|
||||
const origEnvPath = _envState.envPath;
|
||||
@@ -3352,6 +3374,7 @@ async function _deleteCachedModel(repo, itemEl, skipConfirm = false, model = nul
|
||||
const host = _resolveCacheHost();
|
||||
let cmd;
|
||||
if (_isWindows()) {
|
||||
const _psSingleQuote = (value) => `'${String(value || '').replace(/'/g, "''")}'`;
|
||||
const winTarget = target.startsWith('~')
|
||||
? target.replace(/^~/, '$env:USERPROFILE').replace(/\//g, '\\')
|
||||
: target.replace(/\//g, '\\');
|
||||
@@ -3361,9 +3384,9 @@ async function _deleteCachedModel(repo, itemEl, skipConfirm = false, model = nul
|
||||
.filter(Boolean)
|
||||
.map(rel => `${winTarget}\\${rel.replace(/\//g, '\\')}`);
|
||||
if (!targets.length) return;
|
||||
cmd = targets.map(p => `Remove-Item -Force "${p.replace(/"/g, '\\"')}" -ErrorAction SilentlyContinue`).join('; ');
|
||||
cmd = targets.map(p => `Remove-Item -Force ${_psSingleQuote(p)} -ErrorAction SilentlyContinue`).join('; ');
|
||||
} else {
|
||||
cmd = `Remove-Item -Recurse -Force "${winTarget}" -ErrorAction SilentlyContinue`;
|
||||
cmd = `Remove-Item -Recurse -Force ${_psSingleQuote(winTarget)} -ErrorAction SilentlyContinue`;
|
||||
}
|
||||
if (host) {
|
||||
const pf = _sshPrefix(_getPort(host));
|
||||
@@ -3490,7 +3513,7 @@ export async function openServePanelForRepo(repo, fields) {
|
||||
// overridable defaults.
|
||||
const _seeded = { ...fields, _forceBackend: true };
|
||||
byRepo[repo] = _seeded;
|
||||
localStorage.setItem(SERVE_STATE_KEY, JSON.stringify({ _byRepo: byRepo, _lastUsed: _seeded }));
|
||||
localStorage.setItem(SERVE_STATE_KEY, JSON.stringify(_redactServeStateForStorage({ _byRepo: byRepo, _lastUsed: _seeded })));
|
||||
} catch {}
|
||||
}
|
||||
// Switch to the Serve tab (its click handler triggers _fetchCachedModels).
|
||||
|
||||
+2
-2
@@ -1825,13 +1825,13 @@ function _renderNotes() {
|
||||
const agentTitle = agentStatus === 'stream_complete'
|
||||
? 'Agent stream finished for this todo'
|
||||
: (agentStatus === 'running' ? 'Agent is working on this todo' : 'Solve this todo with the agent');
|
||||
const agentSessionAttr = item.agent_session_id ? ` data-session-id="${_esc(item.agent_session_id)}"` : '';
|
||||
const agentSessionAttr = item.agent_session_id ? ` data-session-id="${_attrEsc(item.agent_session_id)}"` : '';
|
||||
const agentMenuTitle = item.agent_session_title || `Agent: ${(item.text || '').slice(0, 40)}`;
|
||||
const indent = Math.min(item.indent || 0, 3);
|
||||
contentHtml += `<div class="note-checkbox${doneClass}" data-note-id="${note.id}" data-idx="${i}" style="padding-left:${indent * 16}px">
|
||||
<span class="note-check-dot" title="Mark done"></span>
|
||||
<span class="note-check-text">${_linkify(item.text)}</span>
|
||||
<button class="note-checkbox-agent${agentDoneClass}" data-note-id="${note.id}" data-idx="${i}"${agentSessionAttr} data-agent-title="${_attrEsc(agentMenuTitle)}" title="${agentTitle}">
|
||||
<button class="note-checkbox-agent${agentDoneClass}" data-note-id="${_attrEsc(note.id)}" data-idx="${i}"${agentSessionAttr} data-agent-title="${_attrEsc(agentMenuTitle)}" title="${_attrEsc(agentTitle)}">
|
||||
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 8V4H8"/><rect x="4" y="8" width="16" height="12" rx="2"/><path d="M2 14h2M20 14h2M15 13v2M9 13v2"/></svg>
|
||||
</button>
|
||||
<button class="note-checkbox-rm" data-note-id="${note.id}" data-idx="${i}" title="Delete item">
|
||||
|
||||
Reference in New Issue
Block a user