From 0eca39021874a673933ebb82b973002b682e7a4b Mon Sep 17 00:00:00 2001 From: Agando Date: Thu, 18 Jun 2026 18:00:54 +0200 Subject: auto: 2026-06-18_18:00 changes --- chat/chat-commands.js | 28 ++-- chat/chat-modules.js | 20 ++- chat/chat-ui.html | 2 +- docs/audit-2026-06-18_chatstudio-overlay.md | 47 ++++++ main.html | 35 ++++- modules/core/core-bundle.js | 236 ++++++++++++++++++++++------ modules/core/module-aiassist.js | 46 ++++-- modules/core/module-chatstudio.js | 131 +++++++++++---- modules/core/module-fs.js | 17 ++ modules/core/module-loader.js | 2 +- modules/core/terminal.js | 42 +++-- modules/ext/console.js | 12 +- 12 files changed, 492 insertions(+), 126 deletions(-) create mode 100644 docs/audit-2026-06-18_chatstudio-overlay.md diff --git a/chat/chat-commands.js b/chat/chat-commands.js index d5cfdd9..c0c77ee 100644 --- a/chat/chat-commands.js +++ b/chat/chat-commands.js @@ -4,6 +4,16 @@ * Handles /search, /fetch, /explain, /help, /status, /clear, /modules, etc. */ +// Fetch with timeout — AbortController-based, 30s default +function _csFetch(url, opts, timeout) { + timeout = timeout || 30000; + opts = opts || {}; + var ctrl = new AbortController(); + opts.signal = ctrl.signal; + var timer = setTimeout(function() { ctrl.abort(); }, timeout); + return fetch(url, opts).finally(function() { clearTimeout(timer); }); +} + async function handleCommand(cmd) { var parts = cmd.split(' '); var command = parts[0].toLowerCase(); @@ -67,7 +77,7 @@ async function handleCommand(cmd) { if (typeof OLLAMA_API_KEY !== 'undefined' && OLLAMA_API_KEY) { headers['Authorization'] = 'Bearer ' + OLLAMA_API_KEY; } - var response = await fetch((typeof OLLAMA_URL !== 'undefined' ? OLLAMA_URL : '/ollama') + '/api/generate', { + var response = await _csFetch((typeof OLLAMA_URL !== 'undefined' ? OLLAMA_URL : '/ollama') + '/api/generate', { method: 'POST', headers: headers, body: JSON.stringify({ @@ -95,7 +105,7 @@ async function handleCommand(cmd) { } showTyping(); try { - var response = await fetch('/api/search?q=' + encodeURIComponent(query) + '&summarize=true'); + var response = await _csFetch('/api/search?q=' + encodeURIComponent(query) + '&summarize=true'); var data = await response.json(); hideTyping(); if (data.error) { @@ -140,11 +150,11 @@ async function handleCommand(cmd) { } showTyping(); try { - var extractResp = await fetch('/api/extract?url=' + encodeURIComponent(url)); + var extractResp = await _csFetch('/api/extract?url=' + encodeURIComponent(url)); var extractOk = extractResp.ok; var extractData = extractOk ? await extractResp.json() : null; - var htmlResp = await fetch('/api/fetch?url=' + encodeURIComponent(url)); + var htmlResp = await _csFetch('/api/fetch?url=' + encodeURIComponent(url)); if (!htmlResp.ok) throw new Error('HTTP ' + htmlResp.status); var html = await htmlResp.text(); var htmlPreview = html.substring(0, 262144) + (html.length > 262144 ? '\n\n... (truncated)' : ''); @@ -178,7 +188,7 @@ async function handleCommand(cmd) { } showTyping(); try { - var response = await fetch('/api/summarize?url=' + encodeURIComponent(url)); + var response = await _csFetch('/api/summarize?url=' + encodeURIComponent(url)); if (!response.ok) throw new Error('HTTP ' + response.status); var data = await response.json(); hideTyping(); @@ -204,7 +214,7 @@ async function handleCommand(cmd) { } showTyping(); try { - var response = await fetch('/api/extract?url=' + encodeURIComponent(url)); + var response = await _csFetch('/api/extract?url=' + encodeURIComponent(url)); if (!response.ok) throw new Error('HTTP ' + response.status); var data = await response.json(); hideTyping(); @@ -225,7 +235,7 @@ async function handleCommand(cmd) { // === /status === if (command === '/status') { try { - var response = await fetch((typeof OLLAMA_URL !== 'undefined' ? OLLAMA_URL : '/ollama') + '/api/tags'); + var response = await _csFetch((typeof OLLAMA_URL !== 'undefined' ? OLLAMA_URL : '/ollama') + '/api/tags'); var data = await response.json(); var modelCount = data.models ? data.models.length : 0; var current = document.getElementById('modelSelect').value; @@ -241,7 +251,7 @@ async function handleCommand(cmd) { showTyping(); try { var start = Date.now(); - var response = await fetch((typeof OLLAMA_URL !== 'undefined' ? OLLAMA_URL : '/ollama') + '/api/tags'); + var response = await _csFetch((typeof OLLAMA_URL !== 'undefined' ? OLLAMA_URL : '/ollama') + '/api/tags'); var ms = Date.now() - start; hideTyping(); addMessage('ai', '**Connection Test**\n\n- Response time: ' + ms + 'ms\n- Status: OK'); @@ -255,7 +265,7 @@ async function handleCommand(cmd) { // === /models or /list models === if (command === '/models') { try { - var response = await fetch((typeof OLLAMA_URL !== 'undefined' ? OLLAMA_URL : '/ollama') + '/api/tags'); + var response = await _csFetch((typeof OLLAMA_URL !== 'undefined' ? OLLAMA_URL : '/ollama') + '/api/tags'); var data = await response.json(); var list = data.models ? data.models.map(function (m) { return '- ' + m.name; }).join('\n') : 'No models'; addMessage('ai', '**Available Models:**\n\n' + list); diff --git a/chat/chat-modules.js b/chat/chat-modules.js index 140e379..295c7c9 100644 --- a/chat/chat-modules.js +++ b/chat/chat-modules.js @@ -10,9 +10,9 @@ const DEFAULT_MODULES = [ id: 'default', name: 'AI Chat', desc: 'General purpose AI assistant', - prompt: 'You are a helpful AI assistant on WebX11. You have access to these tools:\n\n1. **Search:** /api/search?q=QUERY - Searches the web via DuckDuckGo. Returns HTML results. Use this for current events, facts, and web information.\n\n2. **Fetch:** /api/fetch?url=URL - Fetches content from any URL. Use this to read web pages, APIs, or documents.\n\n3. **Commands:** Users can type /help, /search [query], /fetch [url], /explain [code], /status, /clear directly.\n\nWhen a user asks about current events, recent info, or something you dont know, suggest they use /search or tell them to ask you to search the web. If they provide a URL, offer to fetch it.\n\nBe concise, helpful, and use the available tools when appropriate.', - help: 'Just type any question! I can help with:\n- Current events (I can search the web)\n- Web pages (I can fetch URLs)\n- Coding questions\n- General knowledge\n- Problem solving\n\nTry: /search latest news', - commands: '/help, /search, /fetch, /explain, /status, /clear' + prompt: 'You are a helpful AI assistant on WebX11 newOS4. Current date is injected dynamically. You have access to these tools:\n\n1. **Search:** /api/search?q=QUERY - Searches the web via DuckDuckGo/SearXNG. Returns results with titles, URLs, and snippets. Use this for current events, facts, and web information.\n2. **Fetch:** /api/fetch?url=URL - Fetches raw HTML from any URL. Use this to read web pages, APIs, or documents.\n3. **Extract:** /api/extract?url=URL - Extracts readable content (title, word count, excerpt, full text) from a URL. Prefer this over /fetch for reading articles.\n4. **Summarize:** /api/summarize?url=URL - AI summarization of a URL.\n\n**Tool-Use Rules:**\n- NEVER announce that you are about to use a tool (no "Hold on...", "Let me search...", "Fetching...", or waiting indicators). Just use the tool and present the result immediately.\n- If a /search returns generic/portal results (e.g., google.com homepages), automatically retry with a more specific query using site: operators or exact phrases in quotes.\n- If a user types a malformed command (e.g., /extraxt instead of /extract, /serch instead of /search), recognize their intent and suggest the correct command immediately: "Did you mean /extract [url]? Here are the available commands: /search, /fetch, /extract, /summarize"\n- When a user shares a URL, proactively suggest /extract or /fetch with the URL pre-filled.\n- Be concise. Deliver results, not commentary about the process.\n\n**Available slash commands:** /help, /search [query], /fetch [url], /extract [url], /summarize [url], /explain [code], /status, /models, /clear', + help: 'Just type any question! I can help with:\n- Current events (I can search the web)\n- Web pages (I can extract/fetch URLs)\n- Coding questions\n- General knowledge\n- Problem solving\n\nCommands: /search, /fetch, /extract, /summarize, /explain, /status, /clear\n\nTry: /search latest news', + commands: '/help, /search, /fetch, /extract, /summarize, /explain, /status, /models, /clear' }, { id: 'search', @@ -72,7 +72,8 @@ const DEFAULT_MODULES = [ } ]; -var modules = []; +// modules array lives on window.modules (set by _initLiteEngine in module-chatstudio.js) +// Do NOT redeclare here — just reference the global var currentModule = 'default'; var editingModuleId = null; @@ -266,14 +267,19 @@ function saveModule() { // === AI Module Generator === -async function generateModuleWithAI() { +async function generateModuleWithAI(evt) { + var e = evt || window.event; var desc = document.getElementById('aiModuleDesc').value.trim(); if (!desc) { showToast('Please describe what the module should do'); return; } - var btn = event.target; + var btn = e ? e.target : null; + if (!btn) { + var modal = document.getElementById('moduleModal'); + btn = modal ? modal.querySelector('button[onclick*="generateModule"]') : null; + } btn.disabled = true; btn.textContent = 'Generating...'; @@ -283,7 +289,7 @@ async function generateModuleWithAI() { headers['Authorization'] = 'Bearer ' + OLLAMA_API_KEY; } - var response = await fetch((typeof OLLAMA_URL !== 'undefined' ? OLLAMA_URL : '/ollama') + '/api/generate', { + var response = await _csFetch((typeof OLLAMA_URL !== 'undefined' ? OLLAMA_URL : '/ollama') + '/api/generate', { method: 'POST', headers: headers, body: JSON.stringify({ diff --git a/chat/chat-ui.html b/chat/chat-ui.html index efe0347..5b8e421 100644 --- a/chat/chat-ui.html +++ b/chat/chat-ui.html @@ -62,7 +62,7 @@
- +
diff --git a/docs/audit-2026-06-18_chatstudio-overlay.md b/docs/audit-2026-06-18_chatstudio-overlay.md new file mode 100644 index 0000000..8134caf --- /dev/null +++ b/docs/audit-2026-06-18_chatstudio-overlay.md @@ -0,0 +1,47 @@ +# ChatStudio Overlay Audit — v4.5.70 +Date: 2026-06-18 +Auditor: Hermes Agent +Files reviewed: module-chatstudio.js (1,064 lines), chat-commands.js (276 lines), chat-modules.js (375 lines), chat-ui.html (160 lines), chat-ui.css (747 lines) + +## P0 — Critical Security (3 issues) — FIXED in v4.5.70 + +| # | Issue | File:Line | Status | +|---|-------|-----------|--------| +| 1 | XSS via single-quote injection — `escapeHtml()` didn't escape `'`, module IDs injected into `onclick` with single-quote delimiters | module-chatstudio.js:254, chat-modules.js:113-117 | ✅ Fixed — added `'` escaping | +| 2 | Iframe preview without sandbox — `showHtmlPreview()`/`previewCodeBlock()` write AI content into unsandboxed iframe with full parent access | chat-ui.html:157 | ✅ Fixed — `sandbox="allow-same-origin"` | +| 3 | `importModules()` accepts unvalidated JSON — zero schema check, no ID sanitization, one-click XSS via crafted import file | chat-modules.js:339-367 | ✅ Fixed — 1MB limit, schema validation, ID sanitization | + +## P1 — Bugs & Reliability (7 issues) — NOT YET FIXED + +| # | Issue | File:Line | Impact | +|---|-------|-----------|--------| +| 4 | No fetch timeouts — 11 bare `fetch()` calls with no AbortController | chat-commands.js:98,143,147,181,207,228,244,258 + module-chatstudio.js:446,465,773,860 | Slow/hung backend freezes chat indefinitely | +| 5 | No streaming cancel — once `isStreaming=true`, no abort button or timeout | module-chatstudio.js:751-856 | Model hang = page reload required | +| 6 | CSS load race condition — `resolve()` fires before CSS `` onload | module-chatstudio.js:39-46 | FOUC on first overlay open | +| 7 | `oninput` for Enter key — unreliable across browsers, should be `onkeydown` | chat-ui.html:65 | Enter key may not submit on some browsers | +| 8 | Duplicate `modules` array — `chat-modules.js:75` declares local `var modules` shadowing `window.modules` | chat-modules.js:75 vs module-chatstudio.js:960 | If load order changes, `handleCommand` sees empty array | +| 9 | `_csGlobalsInited` stuck true after init failure — placeholder stubs remain active on retry | module-chatstudio.js:94 vs 1054 | Retry silently does nothing | +| 10 | `generateModuleWithAI` uses deprecated global `event` | chat-modules.js:276 | Fails in strict mode | + +## P2 — UX & Architecture (8 issues) — NOT YET FIXED + +| # | Issue | File:Line | Notes | +|---|-------|-----------|-------| +| 11 | API key in base64 localStorage — not encryption, trivially decodable | module-chatstudio.js:419 | Should use secret-vault.js | +| 12 | Inconsistent storage encoding — messages plain JSON, logs/settings base64 | module-chatstudio.js:505 | Should be consistent | +| 13 | Chat history no max size — long conversations overflow localStorage | module-chatstudio.js:502-508 | Cap at N messages | +| 14 | Static `?v=5` cache-busting — doesn't use NEWOS4_VERSION | module-chatstudio.js:10489,10550 | Manual version bump needed | +| 15 | `buildChatContext` naive HTML strip — regex-based, easily fooled | module-chatstudio.js:888 | Use DOMParser | +| 16 | User messages get no markdown formatting — asymmetric UX | module-chatstudio.js:548 | Apply formatContent to user msgs too | +| 17 | Activity log hardcoded 50 limit | module-chatstudio.js:325 | Not configurable | +| 18 | `CHATSTUDIO_VERSION` not synced to `NEWOS4_VERSION` | module-chatstudio.js:231 | Stale version info | + +## Tool-Use Optimization — Applied in v4.5.70 + +From AI self-report: +- Search retry strategy: system prompt instructs AI to retry with `site:` operators if first search returns generic portals +- No "waiting" hallucinations: system prompt bans announcing tool use ("Hold on...", "Fetching...") +- Typo tolerance: system prompt instructs AI to recognize malformed commands and suggest correct one +- Date injection: ChatStudio overlay now injects current date into every AI request +- Updated default module prompt: lists all 4 tools (search, fetch, extract, summarize) with tool-use rules +- Updated help text: shows all available commands \ No newline at end of file diff --git a/main.html b/main.html index e548b81..8d8a931 100644 --- a/main.html +++ b/main.html @@ -849,6 +849,35 @@ animation: float-in 0.15s ease-out; } + +
@@ -1281,11 +1310,11 @@ - - + + - +