Skip to content

Commit 27c25c0

Browse files
authored
fix(security): refuse an extra slash after https:// in typed base URLs (#9913)
1 parent f947d95 commit 27c25c0

2 files changed

Lines changed: 7 additions & 4 deletions

File tree

‎apps/sim/lib/core/security/input-validation.test.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,8 @@ describe('typed base URLs', () => {
688688
['//evil.com', 'a protocol-relative URL'],
689689
['//user:pass@evil.com', 'a protocol-relative URL with credentials'],
690690
['/\\evil.com', 'a slash and backslash the parser reads as an authority'],
691+
['https:///user:pass@evil.com', 'an extra slash hiding credentials from the authority'],
692+
['https:///evil.com', 'an extra slash after the scheme'],
691693
])('refuses %j (%s) in both helpers', (typed) => {
692694
expect(() => refuseUnsafeTypedUrl(typed)).toThrow()
693695
expect(() => normalizeHttpBaseUrl(typed)).toThrow()

‎apps/sim/lib/core/security/input-validation.ts‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1412,13 +1412,14 @@ export function validateDatabaseIdentifier(
14121412
/**
14131413
* Drops the tabs and newlines the URL parser ignores, then refuses every typed form the parser
14141414
* would read as a host other than the one typed: a malformed `https:/host` (which would get a
1415-
* second scheme and parse as a host named `https`), any other scheme such as `ftp:/host`, and a
1416-
* leading slash or backslash, which the parser takes as a protocol-relative authority. A bare
1417-
* `host` or `host:port` and an `http(s)://` URL pass.
1415+
* second scheme and parse as a host named `https`), an extra slash or backslash after `https://`
1416+
* (which the parser skips, so `https:///user:pass@host` hides its credentials from the authority
1417+
* check), any other scheme such as `ftp:/host`, and a leading slash or backslash, which the parser
1418+
* takes as a protocol-relative authority. A bare `host` or `host:port` and an `http(s)://` URL pass.
14181419
*/
14191420
function refuseUnsafeUrlForm(typed: string): string {
14201421
const stripped = typed.replace(/[\t\n\r]/g, '')
1421-
if (/^https?:(?!\/\/)/i.test(stripped)) throw new Error('malformed scheme')
1422+
if (/^https?:(?!\/\/(?![/\\]))/i.test(stripped)) throw new Error('malformed scheme')
14221423
if (/^[/\\]/.test(stripped)) throw new Error('protocol-relative URL')
14231424
const hasScheme = /^[a-z][a-z\d+.-]*:/i.test(stripped)
14241425
const isHostWithPort = /^[a-z\d.-]+:\d+(?:[/?#]|$)/i.test(stripped)

0 commit comments

Comments
 (0)