mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-06 08:24:46 +00:00
fix(projects): harden host and remote identity
This commit is contained in:
@@ -17,4 +17,22 @@ describe("project settings target", () => {
|
||||
it("keeps a host-local route valid after the structural key changes", () => {
|
||||
expect(findProjectSettingsTarget([project], "host:host-a:project:prj_1234")).toBe(project);
|
||||
});
|
||||
|
||||
it("keeps legacy project IDs scoped to their host", () => {
|
||||
const legacyKey = "remote:github.com/acme/app";
|
||||
const unchangedProject = {
|
||||
projectKey: legacyKey,
|
||||
hosts: [{ serverId: "host-a", projectId: legacyKey }],
|
||||
};
|
||||
const changedProject = {
|
||||
projectKey: "remote:github.com/acme/app-fork",
|
||||
hosts: [{ serverId: "host-b", projectId: legacyKey }],
|
||||
};
|
||||
|
||||
const routeKey = resolveProjectSettingsRouteKey(changedProject);
|
||||
expect(routeKey).toBe(`host:host-b:project:${legacyKey}`);
|
||||
expect(findProjectSettingsTarget([unchangedProject, changedProject], routeKey)).toBe(
|
||||
changedProject,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { resolveProjectGroupKey } from "./project-group-key";
|
||||
|
||||
interface ProjectSettingsTarget {
|
||||
projectKey: string;
|
||||
hosts: ReadonlyArray<{ serverId: string; projectId?: string }>;
|
||||
@@ -8,7 +6,7 @@ interface ProjectSettingsTarget {
|
||||
function resolveHostLocalProjectKey(host: { serverId: string; projectId?: string }): string | null {
|
||||
const projectId = host.projectId?.trim();
|
||||
if (!projectId) return null;
|
||||
return resolveProjectGroupKey({ serverId: host.serverId, projectId });
|
||||
return `host:${host.serverId}:project:${projectId}`;
|
||||
}
|
||||
|
||||
export function resolveProjectSettingsRouteKey(project: ProjectSettingsTarget): string {
|
||||
|
||||
@@ -57,6 +57,19 @@ describe("deriveProjectGroupKey", () => {
|
||||
).toBe("remote:github.com/getpaseo/paseo");
|
||||
});
|
||||
|
||||
test("accepts an SCP-style remote with a bracketed IPv6 host", () => {
|
||||
const rootPath = path.resolve("repo");
|
||||
|
||||
expect(
|
||||
deriveProjectGroupKey({
|
||||
rootPath,
|
||||
remoteUrl: "git@[2001:db8::1]:getpaseo/paseo.git",
|
||||
worktreeRoot: rootPath,
|
||||
mainRepoRoot: null,
|
||||
}),
|
||||
).toBe("remote:[2001:db8::1]/getpaseo/paseo");
|
||||
});
|
||||
|
||||
test("includes the selected path within a repository", () => {
|
||||
const worktreeRoot = path.resolve("repo");
|
||||
const rootPath = path.join(worktreeRoot, "packages", "app");
|
||||
|
||||
@@ -39,7 +39,7 @@ function deriveRemoteProjectGroupKey(remoteUrl: string | null): string | null {
|
||||
let remotePath: string | null = null;
|
||||
const scpLike =
|
||||
!trimmed.includes("://") && !/^[A-Za-z]:[\\/]/.test(trimmed)
|
||||
? trimmed.match(/^(?:[^@/:]+@)?([^/:]+):(.+)$/)
|
||||
? trimmed.match(/^(?:[^@/:]+@)?(\[[^\]]+\]|[^/:]+):(.+)$/)
|
||||
: null;
|
||||
if (scpLike) {
|
||||
host = scpLike[1] ?? null;
|
||||
|
||||
@@ -48,6 +48,10 @@ describe("path equivalence", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("accepts contained path segments beginning with two dots", () => {
|
||||
expect(getRealpathAwareRelativePath("/repo", "/repo/..tools")).toBe("..tools");
|
||||
});
|
||||
|
||||
test.skipIf(process.platform === "win32")(
|
||||
"derives the contained suffix from a realpath-equivalent root",
|
||||
() => {
|
||||
|
||||
@@ -96,7 +96,9 @@ function getRelativePathInsideRoot(root: string, candidate: string): string | nu
|
||||
|
||||
if (
|
||||
comparisonRelative !== "" &&
|
||||
(comparisonRelative.startsWith("..") || platformPath.isAbsolute(comparisonRelative))
|
||||
(comparisonRelative === ".." ||
|
||||
comparisonRelative.startsWith(`..${platformPath.sep}`) ||
|
||||
platformPath.isAbsolute(comparisonRelative))
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user