@shopify/app-bridge
Version:
**Shopify is doubling our engineering staff in 2021! [Join our team and work on libraries like this one.](https://smrtr.io/5GGrc)**
69 lines (68 loc) • 2.65 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeSafe = exports.isSafe = void 0;
var FILE_URI_MATCH = /\/\/\//;
var INVALID_RELATIVE_URL = /[/\\][/\\]/;
var VALID_PROTOCOLS = ['https:', 'http:'];
var DUMMY_HOSTNAME = 'http://test.com';
function isSafe(redirectUrl, _a) {
var _b = _a === void 0 ? {} : _a, _c = _b.allowedDomains, allowedDomains = _c === void 0 ? [] : _c, _d = _b.subdomains, subdomains = _d === void 0 ? [] : _d, matchPath = _b.matchPath, requireAbsolute = _b.requireAbsolute, requireSSL = _b.requireSSL;
if (FILE_URI_MATCH.test(redirectUrl)) {
return false;
}
if (redirectUrl.startsWith('/')) {
if (allowedDomains.length > 0 || subdomains.length > 0 || requireAbsolute || requireSSL) {
return false;
}
if (matchPath) {
// Creating a new URL expands the pathname in case of things like `/a/../b`
return pathMatches(new URL(redirectUrl, DUMMY_HOSTNAME), redirectUrl, matchPath);
}
return !INVALID_RELATIVE_URL.test(redirectUrl);
}
var url;
try {
url = new URL(redirectUrl);
}
catch (error) {
return false;
}
if (!VALID_PROTOCOLS.includes(url.protocol)) {
return false;
}
if (requireSSL && url.protocol !== 'https:') {
return false;
}
if (url.username || url.password) {
return false;
}
if (matchPath && !pathMatches(url, redirectUrl, matchPath)) {
return false;
}
if (!hostIsValid(url, allowedDomains, subdomains)) {
return false;
}
return true;
}
exports.isSafe = isSafe;
function hostIsValid(url, allowedDomains, subdomains) {
if (!subdomains.every(function (subdomain) { return subdomain.startsWith('.'); })) {
throw new TypeError('Subdomains must begin with .');
}
var hostname = url.hostname;
return ((allowedDomains.length === 0 && subdomains.length === 0) ||
allowedDomains.includes(hostname) ||
subdomains.some(function (subdomain) { return hostname.endsWith(subdomain); }));
}
function pathMatches(url, originalUrl, matcher) {
var pathname = url.pathname;
// Gets just the unresolve pathname, i.e., `http://foo.com/a/../b => /a/../b
var originalPathname = originalUrl.replace(url.origin, '').split('?')[0];
return typeof matcher === 'string'
? pathname === matcher && originalPathname === matcher
: matcher.test(pathname) && matcher.test(originalPathname);
}
function makeSafe(url, fallback, options) {
return isSafe(url, options) ? encodeURI(url) : fallback;
}
exports.makeSafe = makeSafe;