Skip to content

[AI-665] Normalize workspaceFolder before Cursor path redaction - #87

Merged
alexeyzimarev merged 2 commits into
mainfrom
alexeyzimarev/ai-665-cursor-cli-normalize-workspacefolder-before-path-redaction
May 22, 2026
Merged

alexeyzimarev merged 2 commits into
mainfrom
alexeyzimarev/ai-665-cursor-cli-normalize-workspacefolder-before-path-redaction

Conversation

@alexeyzimarev

Copy link
Copy Markdown
Member

Summary

  • CursorCommand.ResolveWorkspaces now normalises folderPath once (Path.GetFullPath + TrimEnd) and yields the canonical native form to all call sites.
  • CursorPayloadAssembler.RedactPaths tries three variants of the (trimmed) repoPath — forward-slash, native backslash, and JSON-escaped backslash (\\\\) — replacing the escaped form first so we don't consume half of an escape sequence. Comparison is OrdinalIgnoreCase on Windows, Ordinal on POSIX.
  • Closes the Windows-only path-leak gap where bubble JSON used backslashes and workspace.json used forward slashes (or vice versa).

Follow-up from Qodo review on #85 (bug #3 — Windows correctness).
Linear: AI-665

Test plan

  • Redacts_paths_with_trailing_separator_on_repoPath — trailing-slash variant matches.
  • Redacts_backslash_paths_when_repoPath_uses_forward_slash — escaped Windows form in bubble JSON is stripped.
  • Redacts_forward_slash_paths_when_repoPath_uses_backslash — inverse case.
  • Redacts_unescaped_backslash_paths_in_object_typed_params — object-typed params via GetRawText().
  • Existing Redacts_absolute_paths_to_relative still passes.
  • All 26 Cursor unit tests + 814 CLI unit tests pass.
  • AOT publish clean.

🤖 Generated with Claude Code

CursorPayloadAssembler.RedactPaths previously did a single exact-string
Replace(repoPath, "/repo"). On Windows that misses any path that doesn't
exactly match repoPath's normalisation — different separator, different
case, trailing-slash variant, JSON-escaped backslashes — and absolute
paths leak to the server.

Two coordinated fixes:

* CursorCommand.ResolveWorkspaces normalises folderPath once (Path.GetFullPath
  + TrimEnd) and yields the normalised value to all call sites, so the
  redaction repoPath always arrives in canonical native form.

* CursorPayloadAssembler.RedactPaths now tries three variants of the
  trimmed repoPath against the bubble JSON: forward-slash, native
  backslash, and the JSON-escaped backslash form ("\\\\"). Comparison is
  case-insensitive on Windows, case-sensitive on POSIX. The escaped form
  is replaced before the raw backslash form so we don't consume half of
  an escape sequence mid-string.

Follow-up from Qodo review on #85 (bug #3 — Windows correctness).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@linear-code

linear-code Bot commented May 21, 2026

Copy link
Copy Markdown

AI-665

@qodo-code-review

Copy link
Copy Markdown

Review Summary by Qodo

Fix Windows path redaction with normalized workspace folders

🐞 Bug fix

Grey Divider

Walkthroughs

Description
• Normalize folderPath in CursorCommand.ResolveWorkspaces to canonical native form
• Enhance RedactPaths to handle path separator variants (forward, backward, escaped)
• Fix Windows path-leak where workspace.json and bubble JSON use different separators
• Add comprehensive tests for cross-platform path redaction scenarios
Diagram
flowchart LR
  A["workspace.json<br/>folderPath"] -->|NormalizePath| B["Canonical<br/>Native Form"]
  B -->|ResolveWorkspaces| C["Normalized<br/>repoPath"]
  C -->|RedactPaths| D["Path Variants<br/>forward/backward/escaped"]
  D -->|Replace| E["Redacted JSON<br/>with /repo"]

Loading

File Changes

1. src/Kapacitor.Cli.Core/Cursor/CursorPayloadAssembler.cs 🐞 Bug fix +31/-3

Implement multi-variant path redaction for cross-platform compatibility

• Enhanced RedactPaths to handle three path separator variants: forward-slash, native backslash,
 and JSON-escaped backslash
• Added case-insensitive comparison on Windows and case-sensitive on POSIX
• Replaces escaped backslash form before raw backslash to avoid consuming half of escape sequences
• Trims trailing separators from repoPath for consistent matching

src/Kapacitor.Cli.Core/Cursor/CursorPayloadAssembler.cs


2. src/Kapacitor.Cli/Commands/CursorCommand.cs 🐞 Bug fix +11/-3

Normalize folderPath to canonical form in ResolveWorkspaces

• Added path normalization in ResolveWorkspaces to canonicalize folderPath once using
 NormalizePath
• Yields normalized folder path to all call sites instead of raw workspace.json value
• Ensures downstream consumers receive consistent canonical native form
• Moved normalization earlier in the flow to apply before workspace database path construction

src/Kapacitor.Cli/Commands/CursorCommand.cs


3. test/Kapacitor.Cli.Tests.Unit/Cursor/CursorPayloadAssemblerTests.cs 🧪 Tests +43/-0

Add comprehensive cross-platform path redaction tests

• Added test for trailing separator handling on repoPath with forward slash
• Added test for redacting JSON-escaped backslash paths when repoPath uses forward slashes
• Added test for inverse scenario with backslash repoPath and forward-slash bubble JSON
• Added test for unescaped backslash paths in object-typed params via GetRawText()

test/Kapacitor.Cli.Tests.Unit/Cursor/CursorPayloadAssemblerTests.cs


Grey Divider

Qodo Logo

@qodo-code-review

qodo-code-review Bot commented May 21, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Remediation recommended

1. Silent workspace skip ✓ Resolved 🐞 Bug ◔ Observability
Description
ResolveWorkspaces swallows any exception from NormalizePath(folderPath) and continues without
logging, which can cause the command to report “No matching Cursor workspace found” without
indicating which workspace entry was invalid. This makes real workspace discovery failures
significantly harder to diagnose (especially for --all).
Code

src/Kapacitor.Cli/Commands/CursorCommand.cs[R409-414]

Evidence
The new try/catch { continue; } around NormalizePath(folderPath) drops entries without any
diagnostics, and RunAsync only reports a generic error when the resolved workspace list is empty.

src/Kapacitor.Cli/Commands/CursorCommand.cs[407-414]
src/Kapacitor.Cli/Commands/CursorCommand.cs[35-40]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`CursorCommand.ResolveWorkspaces` catches all exceptions thrown by `NormalizePath(folderPath)` and silently `continue`s. When this happens for all workspaces, `RunAsync` emits only a generic “No matching Cursor workspace found.” message, giving users no actionable clue about which `workspace.json` (or `folder` value) is malformed.

### Issue Context
The PR intentionally normalizes `folderPath` early to feed a canonical repo path to redaction and other downstream logic. That’s good, but the new failure mode should be diagnosable.

### Fix Focus Areas
- src/Kapacitor.Cli/Commands/CursorCommand.cs[407-414]
- src/Kapacitor.Cli/Commands/CursorCommand.cs[35-40]

### Suggested fix
- In the `catch` around `NormalizePath(folderPath)`, capture the exception and write a one-line warning to `Console.Error` including `wsJsonPath` (or `subdir`), the problematic `folderPath`, and `ex.Message`, then continue.
- Optional: keep the generic “No matching…” message, but consider mentioning that some workspaces may have been skipped due to invalid paths.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Previous catch silently continued when NormalizePath threw, leaving users
with only the generic "No matching Cursor workspace found." message and
no clue which workspace.json was malformed. Now writes a one-line
diagnostic to stderr including the workspace storage subdir, the
problematic folder path, and the exception message before continuing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@alexeyzimarev

Copy link
Copy Markdown
Member Author

Pushed fix for the silent-skip finding in 13f1c79:

The new catch around NormalizePath(folderPath) now writes a one-line diagnostic to Console.Error with the workspace storage subdir, the problematic folder path, and the exception message before continuing — so users get an actionable clue when a workspace.json's folder URI is malformed instead of just the generic "No matching Cursor workspace found."

All 26 Cursor unit tests still pass.

@alexeyzimarev
alexeyzimarev merged commit 7301c69 into main May 22, 2026
4 checks passed
@alexeyzimarev
alexeyzimarev deleted the alexeyzimarev/ai-665-cursor-cli-normalize-workspacefolder-before-path-redaction branch May 22, 2026 08:10
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