Skip to content

Commit eecd1a3

Browse files
committed
fix: narrow mounted route guards for file links
1 parent 08f6339 commit eecd1a3

4 files changed

Lines changed: 86 additions & 14 deletions

File tree

src/features/messages/components/Markdown.test.tsx

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -161,19 +161,19 @@ describe("Markdown file-like href behavior", () => {
161161
expect(onOpenFileLink).toHaveBeenCalledWith("/workspace/dist/assets");
162162
});
163163

164-
it("keeps generic workspace routes as normal markdown links", () => {
164+
it("keeps exact workspace routes as normal markdown links", () => {
165165
const onOpenFileLink = vi.fn();
166166
render(
167167
<Markdown
168-
value="See [overview](/workspace/reviews/overview)"
168+
value="See [reviews](/workspace/reviews)"
169169
className="markdown"
170170
workspacePath="/Users/sotiriskaniras/Documents/Development/Forks/CodexMonitor"
171171
onOpenFileLink={onOpenFileLink}
172172
/>,
173173
);
174174

175-
const link = screen.getByText("overview").closest("a");
176-
expect(link?.getAttribute("href")).toBe("/workspace/reviews/overview");
175+
const link = screen.getByText("reviews").closest("a");
176+
expect(link?.getAttribute("href")).toBe("/workspace/reviews");
177177

178178
const clickEvent = createEvent.click(link as Element, {
179179
bubbles: true,
@@ -588,4 +588,48 @@ describe("Markdown file-like href behavior", () => {
588588
expect(clickEvent.defaultPrevented).toBe(true);
589589
expect(onOpenFileLink).toHaveBeenCalledWith("/tmp/report#L12.md");
590590
});
591+
592+
it("still opens mounted file links when the workspace basename is settings", () => {
593+
const onOpenFileLink = vi.fn();
594+
render(
595+
<Markdown
596+
value="See [app](/workspace/settings/src/App.tsx)"
597+
className="markdown"
598+
onOpenFileLink={onOpenFileLink}
599+
/>,
600+
);
601+
602+
const link = screen.getByText("app").closest("a");
603+
expect(link?.getAttribute("href")).toBe("/workspace/settings/src/App.tsx");
604+
605+
const clickEvent = createEvent.click(link as Element, {
606+
bubbles: true,
607+
cancelable: true,
608+
});
609+
fireEvent(link as Element, clickEvent);
610+
expect(clickEvent.defaultPrevented).toBe(true);
611+
expect(onOpenFileLink).toHaveBeenCalledWith("/workspace/settings/src/App.tsx");
612+
});
613+
614+
it("linkifies mounted file paths when the nested workspace basename is reviews", () => {
615+
const onOpenFileLink = vi.fn();
616+
const { container } = render(
617+
<Markdown
618+
value="See /workspaces/team/reviews/src/App.tsx for details."
619+
className="markdown"
620+
onOpenFileLink={onOpenFileLink}
621+
/>,
622+
);
623+
624+
const link = container.querySelector('a[href^="codex-file:"]');
625+
expect(link).not.toBeNull();
626+
627+
const clickEvent = createEvent.click(link as Element, {
628+
bubbles: true,
629+
cancelable: true,
630+
});
631+
fireEvent(link as Element, clickEvent);
632+
expect(clickEvent.defaultPrevented).toBe(true);
633+
expect(onOpenFileLink).toHaveBeenCalledWith("/workspaces/team/reviews/src/App.tsx");
634+
});
591635
});

src/features/messages/components/Markdown.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -274,9 +274,13 @@ function isKnownLocalWorkspaceRoutePath(path: string) {
274274

275275
const routeSegment =
276276
mountedPath.prefix === "/workspace/"
277-
? mountedPath.segments[0]
278-
: mountedPath.segments[1];
279-
return Boolean(routeSegment) && LOCAL_WORKSPACE_ROUTE_SEGMENTS.has(routeSegment);
277+
? mountedPath.segments.length === 1
278+
? mountedPath.segments[0]
279+
: null
280+
: mountedPath.segments.length === 2
281+
? mountedPath.segments[1]
282+
: null;
283+
return routeSegment !== null && LOCAL_WORKSPACE_ROUTE_SEGMENTS.has(routeSegment);
280284
}
281285

282286
function isLikelyMountedWorkspaceFilePath(

src/utils/fileLinks.test.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, it } from "vitest";
2-
import { fromFileUrl } from "./fileLinks";
2+
import { fromFileUrl, isKnownLocalWorkspaceRoutePath } from "./fileLinks";
33

44
function withThrowingUrlConstructor(run: () => void) {
55
const originalUrl = globalThis.URL;
@@ -71,3 +71,23 @@ describe("fromFileUrl", () => {
7171
});
7272
});
7373
});
74+
75+
describe("isKnownLocalWorkspaceRoutePath", () => {
76+
it("matches only exact mounted settings and reviews routes", () => {
77+
expect(isKnownLocalWorkspaceRoutePath("/workspace/settings")).toBe(true);
78+
expect(isKnownLocalWorkspaceRoutePath("/workspace/reviews")).toBe(true);
79+
expect(isKnownLocalWorkspaceRoutePath("/workspaces/team/settings")).toBe(true);
80+
expect(isKnownLocalWorkspaceRoutePath("/workspaces/team/reviews")).toBe(true);
81+
});
82+
83+
it("does not treat deeper mounted paths as reserved routes", () => {
84+
expect(isKnownLocalWorkspaceRoutePath("/workspace/settings/src/App.tsx")).toBe(false);
85+
expect(isKnownLocalWorkspaceRoutePath("/workspace/reviews/src/App.tsx")).toBe(false);
86+
expect(isKnownLocalWorkspaceRoutePath("/workspaces/team/settings/src/App.tsx")).toBe(
87+
false,
88+
);
89+
expect(isKnownLocalWorkspaceRoutePath("/workspaces/team/reviews/src/App.tsx")).toBe(
90+
false,
91+
);
92+
});
93+
});

src/utils/fileLinks.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,18 +179,22 @@ export function normalizeFileLinkPath(rawPath: string) {
179179
export function isKnownLocalWorkspaceRoutePath(rawPath: string) {
180180
const normalizedPath = parseFileLocation(rawPath).path.trim().replace(/\\/g, "/");
181181
if (normalizedPath.startsWith("/workspace/")) {
182-
const routeSegment = normalizedPath
182+
const mountedSegments = normalizedPath
183183
.slice("/workspace/".length)
184184
.split("/")
185-
.filter(Boolean)[0];
186-
return routeSegment === "reviews" || routeSegment === "settings";
185+
.filter(Boolean);
186+
return mountedSegments.length === 1
187+
? mountedSegments[0] === "reviews" || mountedSegments[0] === "settings"
188+
: false;
187189
}
188190
if (normalizedPath.startsWith("/workspaces/")) {
189-
const routeSegment = normalizedPath
191+
const mountedSegments = normalizedPath
190192
.slice("/workspaces/".length)
191193
.split("/")
192-
.filter(Boolean)[1];
193-
return routeSegment === "reviews" || routeSegment === "settings";
194+
.filter(Boolean);
195+
return mountedSegments.length === 2
196+
? mountedSegments[1] === "reviews" || mountedSegments[1] === "settings"
197+
: false;
194198
}
195199
return false;
196200
}

0 commit comments

Comments
 (0)