(url: string)
| 137 | } |
| 138 | |
| 139 | export function validateURL(url: string): boolean { |
| 140 | if (url.length > MAX_URL_LENGTH) { |
| 141 | return false |
| 142 | } |
| 143 | |
| 144 | let parsed |
| 145 | try { |
| 146 | parsed = new URL(url) |
| 147 | } catch { |
| 148 | return false |
| 149 | } |
| 150 | |
| 151 | // We don't need to check protocol here, as we'll upgrade http to https when making the request |
| 152 | |
| 153 | // As long as we aren't supporting aiming to cookies or internal domains, |
| 154 | // we should block URLs with usernames/passwords too, even though these |
| 155 | // seem exceedingly unlikely. |
| 156 | if (parsed.username || parsed.password) { |
| 157 | return false |
| 158 | } |
| 159 | |
| 160 | // Initial filter that this isn't a privileged, company-internal URL |
| 161 | // by checking that the hostname is publicly resolvable |
| 162 | const hostname = parsed.hostname |
| 163 | const parts = hostname.split('.') |
| 164 | if (parts.length < 2) { |
| 165 | return false |
| 166 | } |
| 167 | |
| 168 | return true |
| 169 | } |
| 170 | |
| 171 | type DomainCheckResult = |
| 172 | | { status: 'allowed' } |
no outgoing calls
no test coverage detected