Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 30 additions & 5 deletions packages/backend/src/common/base-url-variable.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const HTTP_SCHEME = /^https?:\/\//i;
const BARE_HOST = /^[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+(?::\d{1,5})?(?:[/?#]\S*)?$/;
// A single-label name such as `nextcloud:8080` or `localhost`.
const SINGLE_LABEL = /^[A-Za-z0-9-]+(?::\d{1,5})?(?:[/?#]\S*)?$/;
const EMAIL = /^[^\s@/]+@[^\s@/]+\.[^\s@/]+$/;
const EMAIL = /^[^\s@/]+@[^\s@/]+$/;

const GENERIC_EXAMPLE = 'https://example.com';

Expand Down Expand Up @@ -112,6 +112,24 @@ export function normalizeBaseUrlVariable(name: string, value: string): string {
);
}

/**
* Clean up a tenant identifier for weclapp when a full hostname or URL was supplied.
*/
function normalizeWeclappTenant(raw: string): string {
let val = (raw ?? '').trim();
if (!val) return val;
val = val.replace(/^https?:\/\//i, '');
val = val.replace(/^[/?#].*$/, '');
const slashIdx = val.indexOf('/');
if (slashIdx !== -1) val = val.slice(0, slashIdx);
const qIdx = val.indexOf('?');
if (qIdx !== -1) val = val.slice(0, qIdx);
const hashIdx = val.indexOf('#');
if (hashIdx !== -1) val = val.slice(0, hashIdx);
val = val.replace(/\.weclapp\.com$/i, '');
return val;
}

/**
* Apply {@link normalizeBaseUrlVariable} to the variable a base-URL template
* starts with, when that variable is among `values`. Returns a new map; other
Expand All @@ -124,10 +142,17 @@ export function normalizeBaseUrlVariables(
connectorType?: string,
): Record<string, string> {
if (connectorType && !HTTP_CONNECTOR_TYPES.has(connectorType)) return values;
let result = values;
if (typeof values['WECLAPP_TENANT'] === 'string') {
const cleaned = normalizeWeclappTenant(values['WECLAPP_TENANT']);
if (cleaned !== values['WECLAPP_TENANT']) {
result = { ...result, WECLAPP_TENANT: cleaned };
}
}
const name = leadingBaseUrlVariable(template);
if (!name || typeof values[name] !== 'string') return values;
if (!name || typeof result[name] !== 'string') return result;
return Object.fromEntries(
Object.entries(values).map(([k, v]) => [
Object.entries(result).map(([k, v]) => [
k,
k === name ? normalizeBaseUrlVariable(name, v) : v,
]),
Expand All @@ -140,7 +165,7 @@ export function normalizeBaseUrlVariables(
*
* Connectors installed before the save-time check can still carry
* `yourname.substack.com` as their base URL. Without this, the SSRF guard
* reports "invalid URL", which reads like a blocked request.
* reports "invalid URL", warning reads like a blocked request.
*
* @param input.template The stored, un-interpolated base URL. When it starts
* with `{{VAR}}`, that is the variable to name. Catalog installs store the
Expand Down Expand Up @@ -215,4 +240,4 @@ function parses(url: string): boolean {
} catch {
return false;
}
}
}
Loading