@vellumai/credential-executor 0.10.12-staging.1 → 0.10.12-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -109,12 +109,77 @@ export type RemoteWebPairingTokenResponse =
|
|
|
109
109
|
// only) so both Node and browser callers share one implementation.
|
|
110
110
|
|
|
111
111
|
/** Why a public base URL can't be advertised in a pairing challenge. */
|
|
112
|
-
export type PublicBaseUrlRejection =
|
|
112
|
+
export type PublicBaseUrlRejection =
|
|
113
|
+
| "unparseable"
|
|
114
|
+
| "loopback"
|
|
115
|
+
| "non-https"
|
|
116
|
+
| "service-website";
|
|
113
117
|
|
|
114
118
|
export type PublicBaseUrlResult =
|
|
115
119
|
| { ok: true; url: string }
|
|
116
120
|
| { ok: false; reason: PublicBaseUrlRejection };
|
|
117
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Hosts that are a tunnel/ingress vendor's own website, not a user's assistant
|
|
124
|
+
* endpoint. These are the tunnel vendors our docs mention, and their sites are
|
|
125
|
+
* exactly where a lost user grabs a URL from — e.g. a Tailscale admin invite
|
|
126
|
+
* link (`login.tailscale.com/admin/invite/…`), which is https and non-loopback
|
|
127
|
+
* and so clears every other check. Pairing refuses them with a targeted message
|
|
128
|
+
* rather than minting a challenge the scanning device can never reach.
|
|
129
|
+
*/
|
|
130
|
+
export const TUNNEL_PROVIDER_WEBSITE_HOSTS = [
|
|
131
|
+
"login.tailscale.com",
|
|
132
|
+
"tailscale.com",
|
|
133
|
+
"www.tailscale.com",
|
|
134
|
+
"ngrok.com",
|
|
135
|
+
"dashboard.ngrok.com",
|
|
136
|
+
"dash.cloudflare.com",
|
|
137
|
+
"cloudflare.com",
|
|
138
|
+
"www.cloudflare.com",
|
|
139
|
+
] as const;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* A URL whose exact host is a tunnel/ingress vendor's own website (see
|
|
143
|
+
* {@link TUNNEL_PROVIDER_WEBSITE_HOSTS}). Exact-host only: a user's real
|
|
144
|
+
* Tailscale endpoint (`*.ts.net`) or Cloudflare-fronted domain is never a
|
|
145
|
+
* listed host, so a legitimate address is never mistaken for a vendor site.
|
|
146
|
+
*/
|
|
147
|
+
export function isTunnelProviderWebsiteUrl(url: string): boolean {
|
|
148
|
+
let hostname: string;
|
|
149
|
+
try {
|
|
150
|
+
// WHATWG URL lowercases the hostname during parsing.
|
|
151
|
+
hostname = new URL(url).hostname;
|
|
152
|
+
} catch {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
return (TUNNEL_PROVIDER_WEBSITE_HOSTS as readonly string[]).includes(
|
|
156
|
+
hostname,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* The display name of the tunnel/ingress vendor a service-website URL points
|
|
162
|
+
* at (`"Tailscale"` / `"ngrok"` / `"Cloudflare"`), or null when the host is not
|
|
163
|
+
* a known vendor site. Drives the "This is <Name>'s website" pairing guidance.
|
|
164
|
+
*/
|
|
165
|
+
export function tunnelProviderWebsiteName(url: string): string | null {
|
|
166
|
+
let hostname: string;
|
|
167
|
+
try {
|
|
168
|
+
hostname = new URL(url).hostname;
|
|
169
|
+
} catch {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
if (
|
|
173
|
+
!(TUNNEL_PROVIDER_WEBSITE_HOSTS as readonly string[]).includes(hostname)
|
|
174
|
+
) {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
if (hostname.includes("tailscale")) return "Tailscale";
|
|
178
|
+
if (hostname.includes("ngrok")) return "ngrok";
|
|
179
|
+
if (hostname.includes("cloudflare")) return "Cloudflare";
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
|
|
118
183
|
/**
|
|
119
184
|
* A loopback URL — `localhost`, `[::1]`, or `127.x.x.x`. A pairing link that
|
|
120
185
|
* encodes a loopback address is unreachable from the scanning device.
|
|
@@ -167,6 +232,9 @@ export function resolvePublicBaseUrl(raw: string): PublicBaseUrlResult {
|
|
|
167
232
|
if (isLoopbackPublicUrl(normalized)) {
|
|
168
233
|
return { ok: false, reason: "loopback" };
|
|
169
234
|
}
|
|
235
|
+
if (isTunnelProviderWebsiteUrl(normalized)) {
|
|
236
|
+
return { ok: false, reason: "service-website" };
|
|
237
|
+
}
|
|
170
238
|
if (new URL(normalized).protocol !== "https:") {
|
|
171
239
|
return { ok: false, reason: "non-https" };
|
|
172
240
|
}
|