Skip to content

Feature/ozwell chatbot#238

Draft
runleveldev wants to merge 3 commits intomainfrom
feature/ozwell-chatbot
Draft

Feature/ozwell chatbot#238
runleveldev wants to merge 3 commits intomainfrom
feature/ozwell-chatbot

Conversation

@runleveldev
Copy link
Collaborator

No description provided.

runleveldev and others added 3 commits March 13, 2026 09:17
Add an AI assistant chatbot powered by Ozwell that can read and interact
with any page in the application through a set of generic DOM tools.

Architecture:
- Server-side SSE proxy (routers/chatbot.js) with HMAC token auth so the
  browser never sees the real Ozwell API key. Includes a content filter
  that strips hallucinated JSON/function-call text from LLM responses.
- Client-side tool handlers (chatbot-tools.js) that use a hybrid scraping
  approach: structured extraction for form fields (name/id/type/value)
  and raw innerText from <main> for everything else (tables, logs,
  buttons, headings). This lets the LLM see anything the user sees
  without needing per-element-type extractors.
- Four tools: get_page_contents, set_page_contents, click_element
  (with 4-pass fuzzy matching), and submit_form.
- Agent config (ozwell-agent.yaml) with anti-hallucination instructions.
- Settings UI for configuring the Ozwell API URL and agent key.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tion

The change handler for the template <select> was removing the name
attribute when switching to 'custom', which meant the server never
received template=custom in the POST body. Preserve name="template"
so the server correctly resolves customTemplate as the image reference.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add table-layout:fixed and overflow-wrap:break-word so long domain names
wrap instead of pushing the Actions column off-screen. Also set explicit
column widths in the template.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

function generateToken(userId, hmacKey) {
const payload = `${userId}:${Date.now()}`;
const hmac = crypto.createHmac('sha256', hmacKey).update(payload).digest('hex');

Check failure

Code scanning / CodeQL

Use of password hash with insufficient computational effort High

Password from
an access to apiKeys
is hashed insecurely.

Copilot Autofix

AI 4 days ago

Copilot could not generate an autofix suggestion

Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.

}
}

const response = await axios(axiosConfig);

Check failure

Code scanning / CodeQL

Server-side request forgery Critical

The
URL
of this request depends on a
user-provided value
.

Copilot Autofix

AI 4 days ago

In general, to fix this SSRF issue, we must ensure that user-controlled data do not arbitrarily influence the destination URL of server-side HTTP requests. Since the host and base path (apiUrl) are already server-controlled, the main concern here is the unvalidated path suffix derived from req.params.path. We should normalize and validate the requested subpath, enforcing an allow-list or at least blocking path traversal (..) and dangerous leading slashes, and ensure the resulting URL stays under an expected base path.

The best fix with minimal functional change is to sanitize subPath right after it is built and before constructing targetUrl. We can split the path into segments, reject any segment that is empty, ".", or "..", and then re-join them. If any invalid segment is detected, we immediately return a 400 response instead of proxying the request. This preserves the general behavior of proxying user paths to /v1/<subPath> while preventing traversal-style manipulation. Additionally, we should ensure that targetUrl is constructed using a proper URL join: new URL() with a base, to avoid surprises with extra slashes, but that is optional; we can still keep string concatenation if we only sanitize the segments.

Concretely, in create-a-container/routers/chatbot.js around lines 228–232, we will: (1) keep the existing extraction of pathSegments and subPath as the base behavior, (2) add a new block that sanitizes subPath by splitting on /, rejecting "", ".", "..", and rejoining valid segments, and (3) use this sanitized string (safeSubPath) when building targetUrl. If an invalid segment is encountered, we respond with 400 Invalid path and do not call axios. No new external dependencies are required; we can implement this sanitization with basic JavaScript string and array operations.

Suggested changeset 1
create-a-container/routers/chatbot.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/create-a-container/routers/chatbot.js b/create-a-container/routers/chatbot.js
--- a/create-a-container/routers/chatbot.js
+++ b/create-a-container/routers/chatbot.js
@@ -228,8 +228,20 @@
     // path-to-regexp 8.x returns wildcard as an array of segments
     const pathSegments = req.params.path || [];
     const subPath = Array.isArray(pathSegments) ? pathSegments.join('/') : pathSegments;
-    const targetUrl = `${apiUrl}/v1/${subPath}`;
 
+    // Sanitize subPath to prevent path traversal and SSRF-style abuse
+    const rawSegments = String(subPath || '').split('/');
+    const safeSegments = [];
+    for (const segment of rawSegments) {
+      // Disallow empty, current-dir, or parent-dir segments
+      if (!segment || segment === '.' || segment === '..') {
+        return res.status(400).json({ error: 'Invalid path' });
+      }
+      safeSegments.push(segment);
+    }
+    const safeSubPath = safeSegments.join('/');
+    const targetUrl = `${apiUrl}/v1/${safeSubPath}`;
+
     const axiosConfig = {
       method: req.method,
       url: targetUrl,
EOF
@@ -228,8 +228,20 @@
// path-to-regexp 8.x returns wildcard as an array of segments
const pathSegments = req.params.path || [];
const subPath = Array.isArray(pathSegments) ? pathSegments.join('/') : pathSegments;
const targetUrl = `${apiUrl}/v1/${subPath}`;

// Sanitize subPath to prevent path traversal and SSRF-style abuse
const rawSegments = String(subPath || '').split('/');
const safeSegments = [];
for (const segment of rawSegments) {
// Disallow empty, current-dir, or parent-dir segments
if (!segment || segment === '.' || segment === '..') {
return res.status(400).json({ error: 'Invalid path' });
}
safeSegments.push(segment);
}
const safeSubPath = safeSegments.join('/');
const targetUrl = `${apiUrl}/v1/${safeSubPath}`;

const axiosConfig = {
method: req.method,
url: targetUrl,
Copilot is powered by AI and may make mistakes. Always verify output.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant