fix(coding-agent): harden git package install paths

This commit is contained in:
Armin Ronacher
2026-06-02 18:20:35 +02:00
parent 0462d44f56
commit a98e087e5d
5 changed files with 109 additions and 27 deletions

View File

@@ -62,6 +62,19 @@ describe("Git URL Parsing", () => {
});
});
it("should reject unsafe git install path inputs", () => {
for (const source of [
"git:git@evil.example:../../victim/repo",
"https://evil.example/..%2F..%2Fvictim/repo",
"https://evil.example/..%2F..%2Fvictim/repo%",
"git:git@evil.example:/absolute/repo",
"git:git@evil.example:user\\repo/name",
"git:git@evil.example:user/repo\0name",
]) {
expect(parseGitUrl(source)).toBeNull();
}
});
describe("unsupported without git: prefix", () => {
it("should reject git@host:path without git: prefix", () => {
expect(parseGitUrl("git@github.com:user/repo")).toBeNull();

View File

@@ -33,6 +33,10 @@ interface PackageManagerInternals {
options?: { cwd?: string; timeoutMs?: number; env?: Record<string, string> },
): Promise<string>;
getLocalGitUpdateTarget(installedPath: string): Promise<{ ref: string; head: string; fetchArgs: string[] }>;
getGitInstallPath(
source: { type: "git"; repo: string; host: string; path: string; pinned: boolean; ref?: string },
scope: "user" | "project" | "temporary",
): string;
}
// Helper to check if a resource is enabled
@@ -1138,6 +1142,25 @@ Content`,
});
});
describe("git install paths", () => {
it("should reject paths outside git install roots", () => {
const managerWithInternals = packageManager as unknown as PackageManagerInternals;
const traversalSource = {
type: "git" as const,
repo: "git@evil.example:../../victim/repo",
host: "evil.example",
path: "../../victim/repo",
pinned: false,
};
for (const scope of ["user", "project", "temporary"] as const) {
expect(() => managerWithInternals.getGitInstallPath(traversalSource, scope)).toThrow(
"outside package install root",
);
}
});
});
describe("settings source normalization", () => {
it("should store global local packages relative to agent settings base", () => {
const pkgDir = join(tempDir, "packages", "local-global-pkg");