mirror of
https://github.com/cdr/code-server.git
synced 2026-04-01 19:13:33 +02:00
feat: add regex support to trusted-origins
- Add isTrustedOrigin() helper that matches exact strings, '*', or regex literals in the form /pattern/flags (e.g. /^.*\.example\.com$/i) - Use isTrustedOrigin in authenticateOrigin() for --trusted-origins - Add unit tests for isTrustedOrigin and for regex/wildcard in origin auth Made-with: Cursor
This commit is contained in:
parent
1af5ce5ab6
commit
4a056b8736
2 changed files with 77 additions and 2 deletions
|
|
@ -351,6 +351,33 @@ export function ensureOrigin(req: express.Request, _?: express.Response, next?:
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the origin matches any trusted origin. Entries are matched
|
||||
* as exact strings, the special wildcard `"*"`, or regex literals in the form
|
||||
* `/pattern/flags` (e.g. `/^.*\.example\.com$/i`).
|
||||
*/
|
||||
export function isTrustedOrigin(origin: string, trustedOrigins: string[]): boolean {
|
||||
return trustedOrigins.some((trusted) => {
|
||||
if (trusted === "*" || trusted === origin) {
|
||||
return true
|
||||
}
|
||||
// Regex literal: /pattern/ or /pattern/flags
|
||||
if (trusted.startsWith("/")) {
|
||||
const closingSlash = trusted.lastIndexOf("/")
|
||||
if (closingSlash > 0) {
|
||||
const pattern = trusted.slice(1, closingSlash)
|
||||
const flags = trusted.slice(closingSlash + 1)
|
||||
try {
|
||||
return new RegExp(pattern, flags).test(origin)
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate the request origin against the host. Throw if invalid.
|
||||
*/
|
||||
|
|
@ -370,7 +397,7 @@ export function authenticateOrigin(req: express.Request): void {
|
|||
}
|
||||
|
||||
const trustedOrigins = req.args["trusted-origins"] || []
|
||||
if (trustedOrigins.includes(origin) || trustedOrigins.includes("*")) {
|
||||
if (isTrustedOrigin(origin, trustedOrigins)) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,36 @@ describe("http", () => {
|
|||
expect(http.relativeRoot("/foo/bar/")).toStrictEqual("./../..")
|
||||
})
|
||||
|
||||
describe("isTrustedOrigin", () => {
|
||||
it("should match exact origins", () => {
|
||||
expect(http.isTrustedOrigin("localhost:8080", ["localhost:8080"])).toBe(true)
|
||||
expect(http.isTrustedOrigin("example.com", ["example.com"])).toBe(true)
|
||||
expect(http.isTrustedOrigin("example.com", ["other.com"])).toBe(false)
|
||||
})
|
||||
|
||||
it("should match the wildcard *", () => {
|
||||
expect(http.isTrustedOrigin("anything.example.com", ["*"])).toBe(true)
|
||||
expect(http.isTrustedOrigin("localhost:8080", ["*"])).toBe(true)
|
||||
})
|
||||
|
||||
it("should match regex patterns", () => {
|
||||
expect(http.isTrustedOrigin("sub.example.com", ["/\\.example\\.com$/"])).toBe(true)
|
||||
expect(http.isTrustedOrigin("evil.com", ["/\\.example\\.com$/"])).toBe(false)
|
||||
})
|
||||
|
||||
it("should support regex flags", () => {
|
||||
expect(http.isTrustedOrigin("SUB.EXAMPLE.COM", ["/\\.example\\.com$/i"])).toBe(true)
|
||||
})
|
||||
|
||||
it("should return false for invalid regex patterns", () => {
|
||||
expect(http.isTrustedOrigin("example.com", ["/[invalid/"])).toBe(false)
|
||||
})
|
||||
|
||||
it("should return false for an empty trusted origins list", () => {
|
||||
expect(http.isTrustedOrigin("example.com", [])).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("origin", () => {
|
||||
;[
|
||||
{
|
||||
|
|
@ -54,6 +84,22 @@ describe("http", () => {
|
|||
host: "localhost:8080",
|
||||
expected: "malformed", // Parsing fails completely.
|
||||
},
|
||||
{
|
||||
origin: "http://sub.example.com",
|
||||
host: "other.com",
|
||||
trustedOrigins: ["/\\.example\\.com$/"],
|
||||
},
|
||||
{
|
||||
origin: "http://evil.com",
|
||||
host: "other.com",
|
||||
trustedOrigins: ["/\\.example\\.com$/"],
|
||||
expected: "does not match",
|
||||
},
|
||||
{
|
||||
origin: "http://sub.example.com",
|
||||
host: "other.com",
|
||||
trustedOrigins: ["*"],
|
||||
},
|
||||
].forEach((test) => {
|
||||
;[
|
||||
["host", test.host],
|
||||
|
|
@ -70,7 +116,9 @@ describe("http", () => {
|
|||
origin: test.origin,
|
||||
[key]: value,
|
||||
},
|
||||
args: {},
|
||||
args: {
|
||||
"trusted-origins": (test as { trustedOrigins?: string[] }).trustedOrigins,
|
||||
},
|
||||
})
|
||||
if (typeof test.expected === "string") {
|
||||
expect(() => http.authenticateOrigin(req)).toThrow(test.expected)
|
||||
|
|
|
|||
Loading…
Reference in a new issue