mirror of
https://github.com/BerriAI/litellm.git
synced 2026-08-28 05:25:59 +00:00
Merge 7385867b4b into 44d84360fb
This commit is contained in:
commit
5f07fa21c8
2 changed files with 32 additions and 8 deletions
|
|
@ -524,11 +524,22 @@ describe("parseSkillSource — security boundary", () => {
|
|||
}
|
||||
});
|
||||
|
||||
it("rejects URLs with embedded credentials", () => {
|
||||
expect(parseSkillSource("https://user:token@gitlab.com/org/repo")).toBeNull();
|
||||
expect(parseSkillSource("https://user@gitlab.com/org/repo")).toBeNull();
|
||||
// userinfo confusion: the real host is evil.com, not github.com
|
||||
expect(parseSkillSource("https://github.com@evil.com/org/repo")).toBeNull();
|
||||
it("accepts URLs with credentials and strips them", () => {
|
||||
// Credentials are stripped; the repo URL parses normally afterward
|
||||
expect(parseSkillSource("https://user:token@gitlab.com/org/repo")?.parsed).toEqual({
|
||||
source: "url",
|
||||
url: "https://gitlab.com/org/repo",
|
||||
});
|
||||
expect(parseSkillSource("https://user@gitlab.com/org/repo")?.parsed).toEqual({
|
||||
source: "url",
|
||||
url: "https://gitlab.com/org/repo",
|
||||
});
|
||||
// userinfo confusion: the real host is evil.com — stripping credentials
|
||||
// correctly identifies evil.com as the host, NOT github.com
|
||||
expect(parseSkillSource("https://github.com@evil.com/org/repo")?.parsed).toEqual({
|
||||
source: "url",
|
||||
url: "https://evil.com/org/repo",
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects IP-literal hosts (loopback, private, metadata, obfuscated, IPv6)", () => {
|
||||
|
|
@ -543,6 +554,18 @@ describe("parseSkillSource — security boundary", () => {
|
|||
}
|
||||
});
|
||||
|
||||
it("preserves validated host when stripping credentials from //path URLs", () => {
|
||||
// A path starting with // is a new authority if re-parsed after stripping.
|
||||
// In-place credential clearing prevents the host from changing.
|
||||
// The validated host (safe.com) stays as-is — not bypassed to 127.0.0.1.
|
||||
const result = parseSkillSource("https://user@safe.com//127.0.0.1/repo");
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.parsed).toEqual({
|
||||
source: "url",
|
||||
url: "https://safe.com//127.0.0.1/repo",
|
||||
});
|
||||
});
|
||||
|
||||
it("does not grant GitHub shorthand to a look-alike host", () => {
|
||||
expect(parseSkillSource("https://github.com.evil.com/org/repo")?.parsed).toEqual({
|
||||
source: "url",
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ const pathSegments = (url: URL): string[] => url.pathname.split("/").filter((seg
|
|||
|
||||
/**
|
||||
* Validate and normalize a repository URL into a parsed URL, or null. Enforces https (rejects
|
||||
* http/ssh/git/etc.), rejects embedded credentials, and requires a dotted host, so the public
|
||||
* http/ssh/git/etc.), strips embedded credentials, and requires a dotted host, so the public
|
||||
* skill feeds never serve an insecure or credentialed clone URL. Everything downstream parses
|
||||
* this normalized object rather than the raw string.
|
||||
*/
|
||||
|
|
@ -54,14 +54,15 @@ const parseRepoUrl = (raw: string): URL | null => {
|
|||
}
|
||||
if (
|
||||
url.protocol !== "https:" ||
|
||||
url.username !== "" ||
|
||||
url.password !== "" ||
|
||||
!url.hostname.includes(".") ||
|
||||
url.hostname.startsWith("[") ||
|
||||
IPV4_HOST_REGEX.test(url.hostname)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
// Strip embedded credentials in-place so the validated host cannot change
|
||||
url.username = "";
|
||||
url.password = "";
|
||||
return url;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue