Skip to content

fix: harden memory services and web UI#9

Merged
ZeR020 merged 7 commits into
mainfrom
fix/heavy-lifts
May 6, 2026
Merged

fix: harden memory services and web UI#9
ZeR020 merged 7 commits into
mainfrom
fix/heavy-lifts

Conversation

@ZeR020

@ZeR020 ZeR020 commented May 6, 2026

Copy link
Copy Markdown
Owner

Purpose

This PR is opened solely to trigger a comprehensive CodeRabbit full-repo audit of all fixes and changes accumulated across previous audit rounds.

Scope

Please review the entire repository (all tracked files) for:

  • Security vulnerabilities (XSS, SQL injection, prototype pollution, credential leaks)
  • Race conditions and concurrency bugs
  • Logic errors and data integrity issues
  • Resource leaks (DB connections, timers, file handles)
  • Error handling gaps (swallowed errors, missing rollbacks)
  • Cross-platform compatibility issues
  • Test coverage gaps and fragility
  • Performance bottlenecks (N×M scans, unbounded loops)

Context

This branch contains:

  • Round 2 fixes: 6 critical + 17 major audit items
  • Round 3 fixes: 44 issues (security, races, logic, quick wins, tests)
  • Heavy lift fixes: N×M shard scan elimination, web worker auth/PII
  • XSS fixes: 4 unescaped HTML locations in web UI
  • Error handling: replaced 6 silent catch swallowers

Note

DO NOT MERGE — this PR exists only for the automated review. All verified fixes will be merged via separate PR.

Summary by CodeRabbit

  • New Features

    • Search now spans memory shards for broader tag-based results
    • Memory type shown as badges on memory cards
    • API key–based access control for the web server
  • Bug Fixes

    • Improved error logging for toast notification failures
    • Escaped user strings in profile rendering to prevent HTML injection
  • Improvements

    • Auto-generated summaries now include technical tags
    • Enhanced privacy handling and PII redaction in web server responses
    • Robust pagination for /api/memories and /api/search

ZeR020 added 2 commits May 6, 2026 23:54
…t markers

- api-handlers.ts: Replace O(N²) tag-based shard resolution in global search
  with direct unique-shard iteration (user + project shards, deduped by dbPath)
- web-server-worker.ts: Add API key auth for non-localhost access, PII redaction
  on all JSON responses (stripPrivateContent + email regex + sensitive key redaction)
- Clean up 83 AUDIT_MARKER/AUDIT_TRIGGER comments left from CodeRabbit review
- Typecheck passes, no test regressions (101 pass / 46 fail / 7 errors baseline)
…st errors

- XSS: escapeHtml on profile.displayName, profile.message, memory.memoryType (2x)
  in web/app.js to prevent script injection from user-controlled data
- Error handling: replace 6 silent .catch(() => {}) swallowers in index.ts with
  .catch((err) => log(...)) so TUI toast failures are visible in logs
Copilot AI review requested due to automatic review settings May 6, 2026 18:51
@ZeR020

ZeR020 commented May 6, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Please perform a comprehensive full-repo audit focusing on security, type safety, error handling, resource leaks, logic bugs, cross-platform issues, and test coverage. Review all files thoroughly — this branch contains fixes from multiple prior audit rounds and we need a final verification pass.

@coderabbitai

coderabbitai Bot commented May 6, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Removes many non-functional AUDIT_MARKER artifacts and stray tokens; adds API-key auth and PII redaction to web-server-worker, tightens search/pagination, exposes two singletons, adds tags to generateSummary, improves toast error logging and HTML escaping, and updates tests.

Changes

Mass cleanup (markers & stray tokens)

Layer / File(s) Summary
Whitespace / marker removal
scripts/*, src/*, tests/*
Removed numerous // AUDIT_MARKER comment blocks across scripts, services, providers, types, web files, and tests.
Stray token cleanup
src/services/ai/opencode-provider.ts, src/services/ai/tools/tool-schema.ts, src/services/vector-backends/exact-scan-backend.ts, various tests
Removed accidentally inserted numeric literal lines that created invalid syntax in several files.
Residual artifacts to review
scripts/migrate-v1-to-v2.ts, src/services/language-detector.ts, src/services/memory-scoring.ts
Change set includes or previously introduced stray numeric tokens in some files that merit focused review/fix.

Web-server Worker — Privacy, Auth, Redaction

Layer / File(s) Summary
Imports / Types
src/services/web-server-worker.ts
Imported privacy stripper; extended WorkerMessage with apiKey? and enabled?; added workerConfig.
Privacy utils
src/services/web-server-worker.ts
Added isLocalhost helper and redactPII that uses privacy stripping, email redaction, and masking of sensitive keys recursively.
Auth enforcement
src/services/web-server-worker.ts
handleRequest enforces x-opencode-mem-key for non-localhost requests when workerConfig.apiKey is configured; returns 401 on mismatch.
Response hardening / start
src/services/web-server-worker.ts
jsonResponse now redacts payload before JSON.stringify; start populates workerConfig from the start message and starts server with configured host/port.
Error handling
src/services/web-server-worker.ts
Augmented catch blocks to post error responses while preserving diagnostic paths.

Search & Pagination (API handlers + web-server)

Layer / File(s) Summary
Input parsing / bounds
src/services/web-server.ts, src/services/api-handlers.ts
Added robust parsing and clamping for page/pageSize (defaults and bounds); introduced safePage and safePageSize.
Per-shard querying
src/services/api-handlers.ts
Adjusted per-shard memory fetch limits and prompts batch sizing using safe values; tag vs non-tag search paths use distinct per-shard limits and deduplication.
Pagination wiring
src/services/api-handlers.ts
Paginated slicing, totalPages, and returned page/pageSize values now rely on safePage and safePageSize.
Non-functional marker
src/services/api-handlers.ts
Added small audit placeholder block (comment) at file end.

Singleton exports & data-shape addition

Layer / File(s) Summary
Singleton export
src/services/embedding.ts
Added export const embeddingService = EmbeddingService.getInstance().
Singleton export
src/services/sqlite/shard-manager.ts
Added export const shardManager = new ShardManager().
Data shape
src/services/auto-capture.ts
generateSummary now returns a tags field (normalized to lowercase strings).

Observability & UI escaping

Layer / File(s) Summary
Toast error logging
src/index.ts
Replaced several silent catch blocks around UI toast calls with console.error logging.
HTML escaping & UI
src/web/app.js
Escaped user-supplied strings in profile renderings and added/escaped memory-type badges in memory cards.

Tests — cleanup and small edits

Layer / File(s) Summary
Cleanup
tests/*
Removed many audit markers and stray tokens.
Test removals / changes
tests/memory-scope.test.ts, tests/anthropic-provider.test.ts, others
One test block ("queries across shards for all-projects") was removed; some tests updated or had stray numeric lines removed/fixed (verify tests for introduced stray-line artifacts).

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Worker as WebServerWorker
  participant Server as HTTPServer
  participant Privacy as PrivacyUtil

  Client->>Worker: HTTP request (/api/...), include Host header and x-opencode-mem-key?
  Worker->>Worker: isLocalhost(host) check
  alt apiKey configured and request not from localhost
    Worker->>Worker: validate x-opencode-mem-key
    alt mismatch
      Worker-->>Client: 401 Unauthorized (redacted jsonResponse)
    else match
      Worker->>Server: forward request
      Server-->>Worker: response data
      Worker->>Privacy: redactPII(response data)
      Privacy-->>Worker: redacted data
      Worker-->>Client: JSON response (redacted)
    end
  else auth not required
    Worker->>Server: forward request
    Server-->>Worker: response data
    Worker->>Privacy: redactPII(response data)
    Privacy-->>Worker: redacted data
    Worker-->>Client: JSON response (redacted)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • Copilot

Poem

🐰 I hopped through code both wide and deep,

I nibbled markers none should keep.
Singletons sprout where quiet seeds lay,
Secrets tucked and stray digits swept away.
A tidy trail for devs to leap!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title indicates this is a full-repo audit PR explicitly marked 'DO NOT MERGE', which accurately reflects the PR's stated purpose as a trigger for CodeRabbit automated review only, not a production change.
Description check ✅ Passed The description comprehensively documents the PR's purpose, scope, context (prior fixes from multiple rounds), and explicitly states 'DO NOT MERGE' with rationale. All major template sections are addressed, though not in the exact template format requested.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/heavy-lifts

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai

coderabbitai Bot commented May 6, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@ZeR020 Understood! I'll perform a comprehensive full-repo audit covering:

  • Security — XSS, injection vulnerabilities, credential/PII leaks, prototype pollution
  • Type safety — missing/incorrect types, unsafe casts, implicit any
  • Error handling — swallowed errors, missing rollbacks, unhandled promise rejections
  • Resource leaks — DB connections, timers, file handles, unbounded queues
  • Logic bugs — data integrity issues, race conditions, concurrency hazards
  • Cross-platform issues — path separators, line endings, OS-specific assumptions
  • Test coverage — gaps, fragile assertions, missing edge cases
  • Performance — N×M scans, unbounded loops, unnecessary blocking operations

Kicking off the full audit now — results will be posted as inline review comments.

[full_review]

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 71b053f635

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +512 to +515
const userShards = shardManager.getAllShards("user", "");
const projectShards = shardManager.getAllShards("project", "");
const uniqueTags = new Set<string>();
for (const shard of projectShards) {
const db = connectionManager.getConnection(shard.dbPath);
const tags = vectorSearch.getDistinctTags(db);
for (const t of tags) {
if (t.container_tag) uniqueTags.add(t.container_tag);
}
}
for (const containerTag of uniqueTags) {
const { scope, hash } = extractScopeFromTag(containerTag);
const shards = shardManager.getAllShards(scope, hash);
for (const shard of shards) {
try {
const results = await vectorSearch.searchInShard(
shard,
queryVector,
containerTag,
pageSize
);
memoryResults.push(...results);
} catch (error) {
log("Shard search error", { shardId: shard.id, error: String(error) });
}
const searchedPaths = new Set<string>();
for (const shard of [...userShards, ...projectShards]) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep untagged web searches scoped to project shards

When the web UI searches with no selected tag, it calls /api/search without tag from the project memory view, and handleListMemories/LocalMemoryClient both treat the untagged/all-projects case as project-only. Adding userShards here means any migrated mem_user_* memories are now searched with an empty containerTag and returned in that project UI, exposing personal/user-scope memories and making them selectable for project memory actions. Please keep this branch to project shards unless the API/UI explicitly requests a user scope.

Useful? React with 👍 / 👎.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR primarily removes // AUDIT_MARKER placeholders across the repo and includes a small set of security/perf hardening changes (notably in the web UI rendering, shard search logic, and the web-server worker).

Changes:

  • Remove // AUDIT_MARKER lines from many source, test, and script files.
  • Web UI: escape additional user-controlled fields to reduce XSS risk (memoryType, profile messages/display names).
  • Backend: optimize unscoped search to avoid N×M shard/tag scans; add API-key auth + response redaction in the web-server worker; log previously-swallowed toast errors.

Reviewed changes

Copilot reviewed 81 out of 82 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/windows-path.test.ts Remove prior audit marker.
tests/vector-search-backend-integration.test.ts Remove prior audit marker.
tests/vector-backends/usearch-backend.test.ts Remove prior audit marker.
tests/vector-backends/migration-fallback.test.ts Remove prior audit marker.
tests/vector-backends/exact-scan-backend.test.ts Remove prior audit marker.
tests/vector-backends/backend-factory.test.ts Remove prior audit marker.
tests/tool-scope.test.ts Remove prior audit marker.
tests/tags.test.ts Remove prior audit marker.
tests/project-scope.test.ts Remove prior audit marker.
tests/profile-write.test.ts Remove prior audit marker.
tests/profile-tool-runtime.test.ts Remove prior audit marker.
tests/privacy.test.ts Remove prior audit marker.
tests/plugin-loader-contract.test.ts Remove prior audit marker.
tests/opencode-provider.test.ts Remove prior audit marker.
tests/openai-chat-completion-provider.test.ts Remove prior audit marker.
tests/memory-scope.test.ts Remove prior audit marker.
tests/memory-engine.test.ts Remove prior audit marker.
tests/language-detector.test.ts Remove prior audit marker.
tests/config.test.ts Remove prior audit marker.
tests/config-resolution.test.ts Remove prior audit marker.
tests/anthropic-provider.test.ts Remove prior audit marker.
tests/ai-provider-config.test.ts Remove prior audit marker.
src/web/i18n.js Remove prior audit marker.
src/web/app.js Escape additional rendered fields to mitigate XSS in the web UI.
src/types/usearch.d.ts Remove prior audit marker.
src/types/index.ts Remove prior audit marker.
src/services/web-server.ts Remove prior audit marker.
src/services/web-server-worker.ts Add API key auth + PII redaction for JSON responses in worker server.
src/services/vector-backends/usearch-backend.ts Remove prior audit marker.
src/services/vector-backends/types.ts Remove prior audit marker.
src/services/vector-backends/exact-scan-backend.ts Remove prior audit marker.
src/services/vector-backends/backend-factory.ts Remove prior audit marker.
src/services/user-prompt/user-prompt-manager.ts Remove prior audit marker.
src/services/user-profile/user-profile-manager.ts Remove prior audit marker.
src/services/user-profile/types.ts Remove prior audit marker.
src/services/user-profile/profile-utils.ts Remove prior audit marker.
src/services/user-profile/profile-context.ts Remove prior audit marker.
src/services/user-memory-learning.ts Remove prior audit marker.
src/services/transcript-capture.ts Remove prior audit marker.
src/services/tags.ts Remove prior audit marker.
src/services/sqlite/vector-search.ts Remove prior audit marker.
src/services/sqlite/types.ts Remove prior audit marker.
src/services/sqlite/transcript-manager.ts Remove prior audit marker.
src/services/sqlite/sqlite-bootstrap.ts Remove prior audit marker.
src/services/sqlite/shard-manager.ts Remove prior audit marker.
src/services/sqlite/connection-manager.ts Remove prior audit marker.
src/services/secret-resolver.ts Remove prior audit marker.
src/services/retrieval-context.ts Remove prior audit marker.
src/services/privacy.ts Remove prior audit marker.
src/services/platform-server.ts Remove prior audit marker.
src/services/migration-service.ts Remove prior audit marker.
src/services/memory-scoring.ts Remove prior audit marker.
src/services/memory-scoring-service.ts Remove prior audit marker.
src/services/memory-lifecycle.ts Remove prior audit marker.
src/services/memory-conflicts.ts Remove prior audit marker.
src/services/language-detector.ts Remove prior audit marker.
src/services/jsonc.ts Remove prior audit marker.
src/services/embedding.ts Remove prior audit marker.
src/services/deduplication-service.ts Remove prior audit marker.
src/services/context.ts Remove prior audit marker.
src/services/client.ts Remove prior audit marker.
src/services/cleanup-service.ts Remove prior audit marker.
src/services/auto-capture.ts Remove prior audit marker.
src/services/api-handlers.ts Replace N×M shard/tag scan with single-pass search over shards.
src/services/ai/validators/user-profile-validator.ts Remove prior audit marker.
src/services/ai/tools/tool-schema.ts Remove prior audit marker.
src/services/ai/session/session-types.ts Remove prior audit marker.
src/services/ai/session/ai-session-manager.ts Remove prior audit marker.
src/services/ai/providers/openai-responses.ts Remove prior audit marker.
src/services/ai/providers/openai-chat-completion.ts Remove prior audit marker.
src/services/ai/providers/google-gemini.ts Remove prior audit marker.
src/services/ai/providers/base-provider.ts Remove prior audit marker.
src/services/ai/providers/anthropic-messages.ts Remove prior audit marker.
src/services/ai/provider-config.ts Remove prior audit marker.
src/services/ai/opencode-provider.ts Remove prior audit marker.
src/services/ai/ai-provider-factory.ts Remove prior audit marker.
src/plugin.ts Remove prior audit marker.
src/index.ts Replace swallowed toast errors with logging.
src/config.ts Remove prior audit marker.
scripts/migrate-v1-to-v2.ts Remove prior audit marker.
scripts/migrate-tests.mjs Remove prior audit marker.
scripts/build.mjs Remove prior audit marker.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/services/web-server-worker.ts Outdated
Comment on lines +94 to +105
// Auth: enforce API key for non-localhost access
const requiresAuth =
workerConfig.apiKey &&
workerConfig.enabled &&
workerConfig.host &&
!isLocalhost(workerConfig.host);
if (requiresAuth) {
const apiKey = req.headers.get("x-opencode-mem-key");
if (apiKey !== workerConfig.apiKey) {
return jsonResponse({ success: false, error: "Unauthorized" }, 401);
}
}
Comment on lines +73 to +81
if (value && typeof value === "object") {
const result: Record<string, unknown> = {};
for (const [k, v] of Object.entries(value)) {
// Redact known sensitive keys entirely
if (/token|secret|password|api[-_]?key|authorization|refresh|access/i.test(k)) {
result[k] = "[REDACTED]";
} else {
result[k] = redactPII(v);
}

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/services/web-server-worker.ts (1)

370-380: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Refuse non-localhost startup when no API key is configured.

Right now serve() still starts on 0.0.0.0/:: with message.apiKey unset, which makes requiresAuth false and exposes the full API anonymously. Please reject the start request before binding the socket.

🔐 Suggested guard
       case "start": {
         if (server) {
           self.postMessage({
             type: "error",
             error: "Server already running",
           } as WorkerResponse);
           return;
         }
+
+        if (message.host && !isLocalhost(message.host) && !message.apiKey) {
+          self.postMessage({
+            type: "error",
+            error: "An API key is required when binding the web server to a non-localhost host",
+          } as WorkerResponse);
+          return;
+        }

         workerConfig = {
           host: message.host,
           apiKey: message.apiKey,
           enabled: message.enabled,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/web-server-worker.ts` around lines 370 - 380, Before calling
serve() and binding the socket, validate the requested host/port and API key: if
message.apiKey is missing/empty and the requested hostname (message.host) is not
a localhost address (e.g., not "localhost", "127.0.0.1", "::1"), reject the
start request and do not assign workerConfig or call serve(); surface a clear
error (throw or return) so server is never created. Update the logic around
workerConfig, message.apiKey, and the call to serve() (and any code that sets
server) to perform this guard early.
src/index.ts (1)

69-75: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Guard CONFIG before the first dereference.

CI is currently crashing here with TypeError: undefined is not an object (evaluating 'CONFIG.webServerEnabled'), so the plugin never finishes bootstrapping. Please fail fast if initConfig(directory) does not populate CONFIG, or switch this file to a getter/local config object instead of dereferencing the mutable global immediately.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/index.ts` around lines 69 - 75, The code dereferences the global CONFIG
before ensuring it was initialized, causing a crash; update startup in the
module that calls startWebServer to guard CONFIG (the global) or replace direct
global access with a local config value returned from initConfig(directory) and
check it for truthiness before using it. Specifically, ensure initConfig(...) is
awaited and its result is validated (or throw a clear error) before evaluating
CONFIG.webServerEnabled, or obtain a local const config =
getConfig()/initConfig(...) and use config.webServerEnabled,
config.webServerPort, config.webServerHost, and config.webServerApiKey when
calling startWebServer; fail fast with a descriptive error if the config is
missing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/services/api-handlers.ts`:
- Around line 512-520: The cross-shard pagination is undercounting because you
only fetch pageSize results per shard before doing a global sort/slice; in the
block using shardManager.getAllShards(...) and calling
vectorSearch.searchInShard(shard, queryVector, "", pageSize), change the
per-shard fetch limit to at least page * pageSize (e.g., compute const
perShardLimit = page * pageSize and call vectorSearch.searchInShard(shard,
queryVector, "", perShardLimit)) so each shard can contribute enough candidates
for correct global ranking and totalPages calculation; keep the existing dedupe
by shard.dbPath and existing global sort/slice logic but use the larger
per-shard result sets (you may optionally cap perShardLimit to a sensible max to
avoid excessive work).

In `@src/services/web-server-worker.ts`:
- Around line 94-105: The current auth gate (requiresAuth computed from
workerConfig + isLocalhost) runs for all requests and checks
req.headers.get("x-opencode-mem-key"), which causes index.html/app.js to return
401 for browser loads; change the check so it only enforces the header for API
endpoints (not static app serving). Concretely, in the request handler around
the requiresAuth logic, detect API routes (e.g., request path prefix like "/api"
or requests that expect JSON) and only perform the apiKey header check and
jsonResponse(…,401) for those paths; allow requests for index.html, app.js and
other static assets to bypass this header check so the browser UI can load and
then send the key on subsequent API calls. Ensure you still use workerConfig,
isLocalhost, and req.headers.get("x-opencode-mem-key") for the guarded API
branch so behavior is unchanged for non-localhost API requests.

---

Outside diff comments:
In `@src/index.ts`:
- Around line 69-75: The code dereferences the global CONFIG before ensuring it
was initialized, causing a crash; update startup in the module that calls
startWebServer to guard CONFIG (the global) or replace direct global access with
a local config value returned from initConfig(directory) and check it for
truthiness before using it. Specifically, ensure initConfig(...) is awaited and
its result is validated (or throw a clear error) before evaluating
CONFIG.webServerEnabled, or obtain a local const config =
getConfig()/initConfig(...) and use config.webServerEnabled,
config.webServerPort, config.webServerHost, and config.webServerApiKey when
calling startWebServer; fail fast with a descriptive error if the config is
missing.

In `@src/services/web-server-worker.ts`:
- Around line 370-380: Before calling serve() and binding the socket, validate
the requested host/port and API key: if message.apiKey is missing/empty and the
requested hostname (message.host) is not a localhost address (e.g., not
"localhost", "127.0.0.1", "::1"), reject the start request and do not assign
workerConfig or call serve(); surface a clear error (throw or return) so server
is never created. Update the logic around workerConfig, message.apiKey, and the
call to serve() (and any code that sets server) to perform this guard early.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 22459a14-e908-496c-9181-e58ae0b21f02

📥 Commits

Reviewing files that changed from the base of the PR and between 9dba499 and 71b053f.

📒 Files selected for processing (82)
  • scripts/build.mjs
  • scripts/migrate-tests.mjs
  • scripts/migrate-v1-to-v2.ts
  • src/config.ts
  • src/index.ts
  • src/plugin.ts
  • src/services/ai/ai-provider-factory.ts
  • src/services/ai/opencode-provider.ts
  • src/services/ai/provider-config.ts
  • src/services/ai/providers/anthropic-messages.ts
  • src/services/ai/providers/base-provider.ts
  • src/services/ai/providers/google-gemini.ts
  • src/services/ai/providers/openai-chat-completion.ts
  • src/services/ai/providers/openai-responses.ts
  • src/services/ai/session/ai-session-manager.ts
  • src/services/ai/session/session-types.ts
  • src/services/ai/tools/tool-schema.ts
  • src/services/ai/validators/user-profile-validator.ts
  • src/services/api-handlers.ts
  • src/services/auto-capture.ts
  • src/services/cleanup-service.ts
  • src/services/client.ts
  • src/services/context.ts
  • src/services/deduplication-service.ts
  • src/services/embedding.ts
  • src/services/jsonc.ts
  • src/services/language-detector.ts
  • src/services/memory-conflicts.ts
  • src/services/memory-lifecycle.ts
  • src/services/memory-scoring-service.ts
  • src/services/memory-scoring.ts
  • src/services/migration-service.ts
  • src/services/platform-server.ts
  • src/services/privacy.ts
  • src/services/retrieval-context.ts
  • src/services/secret-resolver.ts
  • src/services/sqlite/connection-manager.ts
  • src/services/sqlite/shard-manager.ts
  • src/services/sqlite/sqlite-bootstrap.ts
  • src/services/sqlite/transcript-manager.ts
  • src/services/sqlite/types.ts
  • src/services/sqlite/vector-search.ts
  • src/services/tags.ts
  • src/services/transcript-capture.ts
  • src/services/user-memory-learning.ts
  • src/services/user-profile/profile-context.ts
  • src/services/user-profile/profile-utils.ts
  • src/services/user-profile/types.ts
  • src/services/user-profile/user-profile-manager.ts
  • src/services/user-prompt/user-prompt-manager.ts
  • src/services/vector-backends/backend-factory.ts
  • src/services/vector-backends/exact-scan-backend.ts
  • src/services/vector-backends/types.ts
  • src/services/vector-backends/usearch-backend.ts
  • src/services/web-server-worker.ts
  • src/services/web-server.ts
  • src/types/index.ts
  • src/types/usearch.d.ts
  • src/web/app.js
  • src/web/i18n.js
  • tests/ai-provider-config.test.ts
  • tests/anthropic-provider.test.ts
  • tests/config-resolution.test.ts
  • tests/config.test.ts
  • tests/language-detector.test.ts
  • tests/memory-engine.test.ts
  • tests/memory-scope.test.ts
  • tests/openai-chat-completion-provider.test.ts
  • tests/opencode-provider.test.ts
  • tests/plugin-loader-contract.test.ts
  • tests/privacy.test.ts
  • tests/profile-tool-runtime.test.ts
  • tests/profile-write.test.ts
  • tests/project-scope.test.ts
  • tests/tags.test.ts
  • tests/tool-scope.test.ts
  • tests/vector-backends/backend-factory.test.ts
  • tests/vector-backends/exact-scan-backend.test.ts
  • tests/vector-backends/migration-fallback.test.ts
  • tests/vector-backends/usearch-backend.test.ts
  • tests/vector-search-backend-integration.test.ts
  • tests/windows-path.test.ts
💤 Files with no reviewable changes (78)
  • src/services/ai/providers/openai-chat-completion.ts
  • tests/profile-tool-runtime.test.ts
  • src/services/user-profile/user-profile-manager.ts
  • src/web/i18n.js
  • src/services/platform-server.ts
  • src/services/ai/providers/base-provider.ts
  • tests/vector-search-backend-integration.test.ts
  • src/services/migration-service.ts
  • src/services/sqlite/transcript-manager.ts
  • src/services/ai/providers/openai-responses.ts
  • tests/config-resolution.test.ts
  • src/types/index.ts
  • tests/windows-path.test.ts
  • src/services/user-profile/profile-utils.ts
  • src/services/sqlite/connection-manager.ts
  • src/services/cleanup-service.ts
  • src/services/memory-conflicts.ts
  • src/services/embedding.ts
  • src/services/user-profile/types.ts
  • tests/config.test.ts
  • tests/privacy.test.ts
  • src/services/transcript-capture.ts
  • src/services/deduplication-service.ts
  • tests/language-detector.test.ts
  • src/services/ai/session/session-types.ts
  • src/config.ts
  • src/services/jsonc.ts
  • src/services/sqlite/vector-search.ts
  • tests/memory-scope.test.ts
  • tests/ai-provider-config.test.ts
  • src/services/ai/ai-provider-factory.ts
  • src/types/usearch.d.ts
  • src/services/vector-backends/usearch-backend.ts
  • src/services/tags.ts
  • src/services/privacy.ts
  • src/services/user-profile/profile-context.ts
  • src/services/sqlite/sqlite-bootstrap.ts
  • src/plugin.ts
  • src/services/ai/providers/google-gemini.ts
  • tests/profile-write.test.ts
  • tests/tool-scope.test.ts
  • src/services/memory-lifecycle.ts
  • tests/memory-engine.test.ts
  • tests/project-scope.test.ts
  • src/services/sqlite/types.ts
  • src/services/user-prompt/user-prompt-manager.ts
  • tests/vector-backends/usearch-backend.test.ts
  • src/services/secret-resolver.ts
  • src/services/client.ts
  • src/services/ai/validators/user-profile-validator.ts
  • tests/anthropic-provider.test.ts
  • src/services/retrieval-context.ts
  • src/services/vector-backends/types.ts
  • src/services/ai/opencode-provider.ts
  • src/services/web-server.ts
  • tests/opencode-provider.test.ts
  • scripts/build.mjs
  • src/services/sqlite/shard-manager.ts
  • src/services/memory-scoring-service.ts
  • src/services/ai/providers/anthropic-messages.ts
  • tests/vector-backends/exact-scan-backend.test.ts
  • tests/openai-chat-completion-provider.test.ts
  • src/services/ai/session/ai-session-manager.ts
  • tests/tags.test.ts
  • src/services/ai/tools/tool-schema.ts
  • tests/plugin-loader-contract.test.ts
  • tests/vector-backends/migration-fallback.test.ts
  • tests/vector-backends/backend-factory.test.ts
  • src/services/user-memory-learning.ts
  • scripts/migrate-v1-to-v2.ts
  • src/services/memory-scoring.ts
  • src/services/context.ts
  • src/services/vector-backends/exact-scan-backend.ts
  • src/services/auto-capture.ts
  • src/services/ai/provider-config.ts
  • src/services/language-detector.ts
  • src/services/vector-backends/backend-factory.ts
  • scripts/migrate-tests.mjs

Comment thread src/services/api-handlers.ts
Comment thread src/services/web-server-worker.ts Outdated
- web-server-worker.ts: Fix auth bypass when enabled is undefined — remove
  enabled from requiresAuth gate, rely only on apiKey + non-localhost host
- web-server-worker.ts: Narrow PII redaction regex to exact key matches
  (apiKey|access_token|refresh_token|secret|password|token|authorization)
  to prevent false positives like accessCount / lastAccessed
- web-server-worker.ts: Restrict API key auth to /api/* paths only, allowing
  browser to load static assets (index.html, app.js, styles.css) without
  the x-opencode-mem-key header
- api-handlers.ts: Fix cross-shard pagination truncation — fetch page*pageSize
  (capped at 500) per shard instead of just pageSize, ensuring correct global
  ranking for page > 1
@ZeR020

ZeR020 commented May 6, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Please perform a comprehensive full-repo review on the latest state of this branch. All previous findings have been addressed. Review the entire codebase for any remaining issues — security, type safety, error handling, resource leaks, logic bugs, race conditions, cross-platform issues, test coverage, and performance. This is the final verification pass before merge.

@coderabbitai

coderabbitai Bot commented May 6, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@ZeR020 Understood — kicking off the final comprehensive verification pass now. I'll re-audit the entire codebase on the current state of this branch, covering:

  • Security — XSS, injection, credential/PII leaks, prototype pollution
  • Type safety — missing/incorrect types, unsafe casts, implicit any
  • Error handling — swallowed errors, missing rollbacks, unhandled rejections
  • Resource leaks — DB connections, timers, file handles, unbounded queues
  • Logic bugs — data integrity, race conditions, concurrency hazards
  • Cross-platform — path separators, OS-specific assumptions
  • Test coverage — gaps, fragile assertions, missing edge cases
  • Performance — N×M scans, unbounded loops, blocking operations

Results will be posted as inline review comments momentarily.

[full_review]

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/services/api-handlers.ts`:
- Around line 519-520: Validate and clamp the incoming page and pageSize values
before using them to compute per-shard limits: ensure page and pageSize are
numbers >= 1 (and pageSize capped to a reasonable max, e.g., 500), compute a
sanitizedLimit = Math.min(clampedPage * clampedPageSize, 500) once, reuse
sanitizedLimit instead of recalculating perShardLimit, and pass that
sanitizedLimit into vectorSearch.searchInShard (and any other places in this
handler) to avoid NaN/negative shard limits and silent partial results; update
references to page, pageSize, perShardLimit and vectorSearch.searchInShard
accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3ec26e0c-1781-46bf-9d4a-ede2c68d1aa0

📥 Commits

Reviewing files that changed from the base of the PR and between 71b053f and 202232e.

📒 Files selected for processing (2)
  • src/services/api-handlers.ts
  • src/services/web-server-worker.ts

Comment thread src/services/api-handlers.ts Outdated
…aining issues

- api-handlers.ts: Add safePage/safePageSize sanitization in handleSearch and
  handleListMemories to prevent NaN/negative/invalid pagination inputs
- web-server.ts: Sanitize page/pageSize query params before passing to handlers
- web-server-worker.ts: Sanitize page/pageSize query params before passing to handlers
- All pagination now uses Number.isFinite checks and caps pageSize at 100

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/services/api-handlers.ts (2)

645-645: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Response returns raw page/pageSize instead of sanitized values.

Same issue as in handleListMemories - the response returns original parameters rather than the sanitized safePage/safePageSize values, causing a mismatch with totalPages.

Proposed fix
-    return { success: true, data: { items: paginatedResults, total, page, pageSize, totalPages } };
+    return { success: true, data: { items: paginatedResults, total, page: safePage, pageSize: safePageSize, totalPages } };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/api-handlers.ts` at line 645, The response currently returns the
original page and pageSize parameters, causing a mismatch with the sanitized
pagination values; update the handler that returns { success: true, data: {
items: paginatedResults, total, page, pageSize, totalPages } } to use the
sanitized safePage and safePageSize instead of page/pageSize (same fix as in
handleListMemories), so the payload becomes items: paginatedResults, total,
page: safePage, pageSize: safePageSize, totalPages and ensure totalPages is
computed based on those sanitized values.

276-276: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Response returns raw page/pageSize instead of sanitized values.

The response at line 276 returns the original page and pageSize parameters rather than safePage and safePageSize. This creates a mismatch where totalPages is calculated from safePageSize but the returned pageSize might be the unsanitized value (e.g., negative or NaN), confusing API consumers.

Proposed fix
-    return { success: true, data: { items, total, page, pageSize, totalPages } };
+    return { success: true, data: { items, total, page: safePage, pageSize: safePageSize, totalPages } };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/api-handlers.ts` at line 276, The return payload is using the
original page and pageSize values causing inconsistency with totalPages; update
the response to return the sanitized pagination values (safePage and
safePageSize) instead of page and pageSize so totalPages (computed from
safePageSize) matches the returned pageSize and the API never exposes
unsanitized/invalid pagination values; adjust the return object that currently
contains { items, total, page, pageSize, totalPages } to use safePage and
safePageSize while keeping items, total, and totalPages as-is.
♻️ Duplicate comments (1)
src/services/api-handlers.ts (1)

537-537: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Bug: Uses raw pageSize instead of safePageSize for prompt search in non-tag path.

Line 537 uses the unsanitized pageSize parameter instead of safePageSize, which could result in NaN * 2 or negative values being passed to searchPrompts. This is inconsistent with the tag-based path at line 521 which correctly uses safePageSize.

Proposed fix
-      promptResults = userPromptManager.searchPrompts(query, undefined, pageSize * 2);
+      promptResults = userPromptManager.searchPrompts(query, undefined, safePageSize * 2);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/api-handlers.ts` at line 537, The non-tag branch incorrectly
calls userPromptManager.searchPrompts(query, undefined, pageSize * 2) using the
raw pageSize; change this to use the sanitized safePageSize (i.e., call
userPromptManager.searchPrompts(query, undefined, safePageSize * 2)) so the
searchPrompts invocation uses the validated value used in the tag path and
avoids NaN/negative inputs; ensure safePageSize is the same variable computed
earlier in this handler before replacing the call.
🧹 Nitpick comments (1)
src/services/web-server-worker.ts (1)

348-356: 💤 Low value

Redacting all API responses may unexpectedly strip legitimate data.

Applying redactPII unconditionally to all JSON responses could cause issues:

  1. The email regex ([a-zA-Z0-9._%+-]+@...) may match non-email strings (e.g., version strings like lib-1.0+build@v2.com)
  2. Local/development clients may need unredacted data for debugging

Consider making redaction conditional based on isLocalhost similar to how web-server.ts handles it with the redact parameter.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/web-server-worker.ts` around lines 348 - 356, The jsonResponse
function currently unconditionally calls redactPII which can strip valid data;
change jsonResponse to accept a redact flag (or determine redaction from
isLocalhost like web-server.ts) and only call redactPII(data) when redaction is
enabled (e.g., redact === true && !isLocalhost). Update all call sites that use
jsonResponse to pass the appropriate redact value (default true for production
routes, false for local/dev debug paths) and preserve the existing
JSON.stringify/Response behavior when skipping redaction; reference jsonResponse
and redactPII to locate the change and mirror the conditional pattern used in
web-server.ts.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/services/api-handlers.ts`:
- Line 645: The response currently returns the original page and pageSize
parameters, causing a mismatch with the sanitized pagination values; update the
handler that returns { success: true, data: { items: paginatedResults, total,
page, pageSize, totalPages } } to use the sanitized safePage and safePageSize
instead of page/pageSize (same fix as in handleListMemories), so the payload
becomes items: paginatedResults, total, page: safePage, pageSize: safePageSize,
totalPages and ensure totalPages is computed based on those sanitized values.
- Line 276: The return payload is using the original page and pageSize values
causing inconsistency with totalPages; update the response to return the
sanitized pagination values (safePage and safePageSize) instead of page and
pageSize so totalPages (computed from safePageSize) matches the returned
pageSize and the API never exposes unsanitized/invalid pagination values; adjust
the return object that currently contains { items, total, page, pageSize,
totalPages } to use safePage and safePageSize while keeping items, total, and
totalPages as-is.

---

Duplicate comments:
In `@src/services/api-handlers.ts`:
- Line 537: The non-tag branch incorrectly calls
userPromptManager.searchPrompts(query, undefined, pageSize * 2) using the raw
pageSize; change this to use the sanitized safePageSize (i.e., call
userPromptManager.searchPrompts(query, undefined, safePageSize * 2)) so the
searchPrompts invocation uses the validated value used in the tag path and
avoids NaN/negative inputs; ensure safePageSize is the same variable computed
earlier in this handler before replacing the call.

---

Nitpick comments:
In `@src/services/web-server-worker.ts`:
- Around line 348-356: The jsonResponse function currently unconditionally calls
redactPII which can strip valid data; change jsonResponse to accept a redact
flag (or determine redaction from isLocalhost like web-server.ts) and only call
redactPII(data) when redaction is enabled (e.g., redact === true &&
!isLocalhost). Update all call sites that use jsonResponse to pass the
appropriate redact value (default true for production routes, false for
local/dev debug paths) and preserve the existing JSON.stringify/Response
behavior when skipping redaction; reference jsonResponse and redactPII to locate
the change and mirror the conditional pattern used in web-server.ts.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d5447beb-89c9-47a2-ac47-a63e8b8a7367

📥 Commits

Reviewing files that changed from the base of the PR and between 202232e and 69fa389.

📒 Files selected for processing (3)
  • src/services/api-handlers.ts
  • src/services/web-server-worker.ts
  • src/services/web-server.ts

…ponses

- handleSearch: return safePage/safePageSize in response instead of raw params
- handleListMemories: return safePage/safePageSize in response instead of raw params
- Prevents API consumers from receiving NaN/negative pagination values

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/services/api-handlers.ts (1)

540-540: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Bug: Raw pageSize used instead of safePageSize.

Line 540 uses the raw pageSize parameter instead of the sanitized safePageSize, defeating the validation performed at lines 500-502. This can propagate NaN, negative, or unbounded values to searchPrompts.

🐛 Proposed fix
-      promptResults = userPromptManager.searchPrompts(query, undefined, pageSize * 2);
+      promptResults = userPromptManager.searchPrompts(query, undefined, safePageSize * 2);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/api-handlers.ts` at line 540, The call to
userPromptManager.searchPrompts uses the raw pageSize variable (promptResults =
userPromptManager.searchPrompts(query, undefined, pageSize * 2)) which bypasses
the earlier validation; change it to use the sanitized safePageSize (i.e. pass
safePageSize * 2) so only validated numeric bounds are forwarded to
searchPrompts, ensuring safePageSize is defined and numeric before
multiplication.
🧹 Nitpick comments (1)
src/services/api-handlers.ts (1)

512-517: ⚡ Quick win

Consider using page-aware limit for tag-based search.

Unlike the non-tag search (line 533) which uses safePage * safePageSize to ensure sufficient candidates for later pages, the tag-based search uses a fixed safePageSize * 2 regardless of page number. For page > 2, this could truncate results similarly to the issue previously fixed for non-tag searches.

♻️ Suggested change for consistency
-          const results = await vectorSearch.searchInShard(
-            shard,
-            queryVector,
-            tag,
-            safePageSize * 2
-          );
+          const perShardLimit = Math.min(safePage * safePageSize, 500);
+          const results = await vectorSearch.searchInShard(
+            shard,
+            queryVector,
+            tag,
+            perShardLimit
+          );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/api-handlers.ts` around lines 512 - 517, The tag-based shard
search currently requests a fixed number of candidates using safePageSize * 2
which will underfetch for page > 2; update the call to
vectorSearch.searchInShard in the tag branch to request page-aware limits (use
safePage * safePageSize or otherwise ensure at least safePage * safePageSize
candidates, e.g. Math.max(safePage * safePageSize, safePageSize * 2)) so it
matches the non-tag behavior and prevents truncation of later pages.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@src/services/api-handlers.ts`:
- Line 540: The call to userPromptManager.searchPrompts uses the raw pageSize
variable (promptResults = userPromptManager.searchPrompts(query, undefined,
pageSize * 2)) which bypasses the earlier validation; change it to use the
sanitized safePageSize (i.e. pass safePageSize * 2) so only validated numeric
bounds are forwarded to searchPrompts, ensuring safePageSize is defined and
numeric before multiplication.

---

Nitpick comments:
In `@src/services/api-handlers.ts`:
- Around line 512-517: The tag-based shard search currently requests a fixed
number of candidates using safePageSize * 2 which will underfetch for page > 2;
update the call to vectorSearch.searchInShard in the tag branch to request
page-aware limits (use safePage * safePageSize or otherwise ensure at least
safePage * safePageSize candidates, e.g. Math.max(safePage * safePageSize,
safePageSize * 2)) so it matches the non-tag behavior and prevents truncation of
later pages.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5b7682c8-9696-47eb-baa6-7bbfd402fe71

📥 Commits

Reviewing files that changed from the base of the PR and between 69fa389 and 093e029.

📒 Files selected for processing (1)
  • src/services/api-handlers.ts

- handleSearch: use safePageSize instead of raw pageSize for promptResults limit
- Prevents invalid/NaN limits being passed to userPromptManager.searchPrompts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/services/api-handlers.ts (1)

512-517: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Later search pages still undercount because these fetch limits ignore page.

These branches only load safePageSize * 2 candidates before the global sort/slice. For page > 2, matching tag-scoped memories and prompts can never participate in ranking, so items and totalPages drift from the real result set. Use a window derived from safePage * safePageSize for every source that feeds combinedResults.

Also applies to: 524-524, 540-540

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/api-handlers.ts` around lines 512 - 517, The shard search fetch
limit currently uses safePageSize * 2 which prevents results beyond page 2 from
ever being included; change calls to vectorSearch.searchInShard (and the similar
calls that feed combinedResults) to request a window based on safePage *
safePageSize (i.e., safePage multiplied by safePageSize) so each source returns
enough candidates for global sort/slice for the requested page; update the three
call sites (the vectorSearch.searchInShard invocation and the two other similar
fetches that feed combinedResults) to use this computed window variable and keep
the existing global sort/slice logic unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/services/api-handlers.ts`:
- Around line 145-147: The code caps safePageSize but not safePage, so a huge
page value can be passed into listMemories and cause an effectively unbounded
read; modify the request handling that computes safePage to constrain the
overall window (e.g., enforce a maxPage and/or maxOffset = maxPage *
safePageSize) or reject requests where page > MAX_PAGE before calling
listMemories; update the logic around safePage/safePageSize and the call to
listMemories to use the bounded page or computed maxOffset (reference safePage,
safePageSize, listMemories) so storage is never asked to read beyond the allowed
window.

---

Duplicate comments:
In `@src/services/api-handlers.ts`:
- Around line 512-517: The shard search fetch limit currently uses safePageSize
* 2 which prevents results beyond page 2 from ever being included; change calls
to vectorSearch.searchInShard (and the similar calls that feed combinedResults)
to request a window based on safePage * safePageSize (i.e., safePage multiplied
by safePageSize) so each source returns enough candidates for global sort/slice
for the requested page; update the three call sites (the
vectorSearch.searchInShard invocation and the two other similar fetches that
feed combinedResults) to use this computed window variable and keep the existing
global sort/slice logic unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1c0c9203-ea4e-4870-b8ba-5e77fe047582

📥 Commits

Reviewing files that changed from the base of the PR and between 093e029 and 4c2aa9a.

📒 Files selected for processing (1)
  • src/services/api-handlers.ts

Comment thread src/services/api-handlers.ts Outdated
Comment on lines +145 to +147
const safePage = Number.isFinite(page) && page > 0 ? Math.floor(page) : 1;
const safePageSize =
Number.isFinite(pageSize) && pageSize > 0 && pageSize <= 100 ? Math.floor(pageSize) : 20;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Bound the page window before using it as a shard read limit.

safePageSize is capped, but safePage is not. On Line 153, a very large page can still turn listMemories into an effectively unbounded read and materialize far more rows than this endpoint can safely return. Cap or reject oversized page windows before calling storage.

Suggested fix
   try {
     const safePage = Number.isFinite(page) && page > 0 ? Math.floor(page) : 1;
     const safePageSize =
       Number.isFinite(pageSize) && pageSize > 0 && pageSize <= 100 ? Math.floor(pageSize) : 20;
+    const requestedWindow = safePage * safePageSize;
+    if (requestedWindow > 500) {
+      return { success: false, error: "Requested page window is too large" };
+    }
     await embeddingService.warmup();
     let allMemories: any[] = [];
     if (tag) {
       const { scope: tagScope, hash } = extractScopeFromTag(tag);
       const shards = shardManager.getAllShards(tagScope, hash);
-      const limit = safePage * safePageSize; // Fetch enough to cover the requested page across shards
+      const limit = requestedWindow;

Also applies to: 153-153

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/api-handlers.ts` around lines 145 - 147, The code caps
safePageSize but not safePage, so a huge page value can be passed into
listMemories and cause an effectively unbounded read; modify the request
handling that computes safePage to constrain the overall window (e.g., enforce a
maxPage and/or maxOffset = maxPage * safePageSize) or reject requests where page
> MAX_PAGE before calling listMemories; update the logic around
safePage/safePageSize and the call to listMemories to use the bounded page or
computed maxOffset (reference safePage, safePageSize, listMemories) so storage
is never asked to read beyond the allowed window.

…d limit computation

- Limit page number to 10,000 max in api-handlers, web-server, and web-server-worker
- Addresses CodeRabbit finding about very large page numbers causing unbounded limit read in vectorSearch.listMemories
@ZeR020
ZeR020 merged commit 48d0728 into main May 6, 2026
5 checks passed
@ZeR020
ZeR020 deleted the fix/heavy-lifts branch May 6, 2026 19:55
@ZeR020 ZeR020 changed the title 🔍 Full Codebase Audit — DO NOT MERGE fix: harden memory services and web UI May 6, 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.

2 participants