Skip to content

Replace eval-based JS interop with named functions in polypilot-interop.js#219

Draft
Copilot wants to merge 2 commits intomainfrom
copilot/replace-eval-with-js-module
Draft

Replace eval-based JS interop with named functions in polypilot-interop.js#219
Copilot wants to merge 2 commits intomainfrom
copilot/replace-eval-with-js-module

Conversation

Copy link
Contributor

Copilot AI commented Feb 26, 2026

~26 instances of JS.InvokeVoidAsync("eval", $"...") across 5 Razor files interpolated C# values directly into JS strings — an XSS/injection surface. Replaced all of them with parameterized calls to named global functions.

New: wwwroot/js/polypilot-interop.js

Named functions covering every eval use case:

Function Replaces
setThemeAttribute(theme) eval($"...setAttribute('data-theme', '{dataTheme}')")
startSidebarResize(startX) eval($"...var startX = {e.ClientX}...")
setInputValue(id, value, cursorAtStart) eval($"...'{inputId}'...{JsonSerializer.Serialize(text)}...")
showPopup(trigger, headerHtml, contentHtml) 40-line eval IIFE for skills/agents popups
showPromptsPopup(..., sessionName) Same + click handler; uses CSS.escape() for safe selector construction
ensureDashboardKeyHandlers() ~90-line eval keydown registration block
focusAndSelect(id), focusAndSetValue(id, val) Various getElementById + focus/value evals
clearDashRef(), clearSettingsRef() Null-out globals
wireSettingsSearch(), wireSessionNameInputEnter() One-shot event listener setup
ensureTextareaAutoResize(), ensureLoadMoreObserver(), scrollMessagesToBottom() Lazy init / scroll helpers

Pre-defines window.__setDashRef and window.__setSettingsRef so the evals that dynamically defined them are removed entirely.

Call-site changes

Before:

var inputId = $"input-{sessionName.Replace(" ", "-")}";
var cursorExpr = cursorAtStart ? "0" : "el.value.length";
await JS.InvokeVoidAsync("eval",
    $"(function(){{ var el = document.getElementById('{inputId}'); " +
    $"if(el){{ el.value = {JsonSerializer.Serialize(text)}; var p = {cursorExpr}; el.setSelectionRange(p, p); }} }})()");

After:

var inputId = $"input-{sessionName.Replace(" ", "-")}";
await JS.InvokeVoidAsync("setInputValue", inputId, text, cursorAtStart);

Cleanup

  • EscapeForJs helper in ExpandedSessionView.razor removed (was only needed to embed C# values into JS strings)
  • Popup methods no longer call EscapeForJs on HTML before passing it — strings are now passed as parameters, not code
Original prompt

This section details on the original issue you should resolve

<issue_title>Replace eval-based JS interop with dedicated JS module</issue_title>
<issue_description>## Problem

The codebase has ~30 instances of \JS.InvokeVoidAsync("eval", $"...");\ across Dashboard.razor, Settings.razor, SessionSidebar.razor, ExpandedSessionView.razor, MainLayout.razor, and SessionListItem.razor. This pattern:

  1. XSS risk: Several calls interpolate C# values directly into JS strings (e.g. \$"...'{inputId}'...", \$"...'{dataTheme}'..."). If any value originates from user input, it could inject arbitrary JS.
  2. Prompt injection surface: Dashboard.razor sends user prompts through eval-based JS interop in the orchestrator routing path.
  3. Maintainability: Inline JS strings are hard to debug, lint, or test.

Proposed Fix

Create a dedicated \wwwroot/js/polypilot-interop.js\ module with named functions:
\\js
export function portalElement(selector) { ... }
export function setThemeAttribute(theme) { ... }
export function focusAndSetValue(elementId, value) { ... }
\\

Then call via \JS.InvokeVoidAsync("portalElement", selector)\ — parameterized, no eval, no injection.

Scope

~30 eval calls across 6 files. Can be done incrementally per-file.

Found during multi-model review of PR #205.</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
Copilot AI changed the title [WIP] Replace eval-based JS interop with dedicated JS module Replace eval-based JS interop with named functions in polypilot-interop.js Feb 26, 2026
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.

Replace eval-based JS interop with dedicated JS module

2 participants