UNPKG

azure-kusto-data

Version:
132 lines 5.14 kB
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. import { getStringTailLowerCase } from "./utils.js"; import { default as endpointsData } from "./wellKnownKustoEndpoints.js"; export class MatchRule { constructor( /** * The suffix which the candidate must end with in order to match. */ suffix, /** * Indicates whether the match must be exact (the candidate must * not have any prefix) or not. */ exact) { this.suffix = suffix; this.exact = exact; } } export class FastSuffixMatcher { constructor(rules) { this.rules = {}; this._suffixLength = rules ? Math.min(...rules.map((r) => r.suffix.length)) : 0; const processedRules = {}; rules === null || rules === void 0 ? void 0 : rules.forEach((rule) => { const suffix = getStringTailLowerCase(rule.suffix, this._suffixLength); if (!processedRules[suffix]) { processedRules[suffix] = []; } processedRules[suffix].push(rule); }); this.rules = processedRules; } isMatch(candidate) { if (candidate.length < this._suffixLength) { return false; } const matchRules = this.rules[getStringTailLowerCase(candidate, this._suffixLength)]; if (matchRules) { for (const rule of matchRules) { if (candidate.endsWith(rule.suffix)) { if (candidate.length === rule.suffix.length || !rule.exact) { return true; } } } } return false; } static createFromExisting(existing, rules) { if (!existing || !Object.keys(existing.rules).length) { return new FastSuffixMatcher(rules); } if (!rules) { return existing; } return new FastSuffixMatcher(rules.concat(...Object.values(existing.rules))); } } class KustoTrustedEndpointsImpl { constructor() { this.matchers = {}; this.additionalMatcher = null; this.overrideMatcher = null; // We could unify this with matchers, but separating makes debugging easier const etr = Object.entries(endpointsData.AllowedEndpointsByLogin); for (const [k, v] of etr) { const rules = []; v.AllowedKustoSuffixes.forEach((suffix) => rules.push(new MatchRule(suffix, false))); v.AllowedKustoHostnames.forEach((hostname) => rules.push(new MatchRule(hostname, true))); this.matchers[k] = new FastSuffixMatcher(rules); } } setOverridePolicy(matcher) { this.overrideMatcher = matcher; } validateTrustedEndpoint(url, loginEndpoint) { const uri = typeof url === "string" ? new URL(url) : url; const host = uri.hostname; // Check that target hostname is trusted and can accept security token this._validateHostnameIsTrusted(host != null ? host : url.toString(), loginEndpoint); } addTrustedHosts(rules, replace) { if (!(rules === null || rules === void 0 ? void 0 : rules.length)) { if (replace) { this.additionalMatcher = null; } return; } this.additionalMatcher = FastSuffixMatcher.createFromExisting(replace ? null : this.additionalMatcher, rules); } _validateHostnameIsTrusted(hostname, loginEndpoint) { // The loopback is unconditionally allowed (since we trust ourselves) if (this._isLocalAddress(hostname)) { return; } // Either check the override matcher OR the matcher: const override = this.overrideMatcher; if (override != null) { if (override(hostname)) { return; } } else { const matcher = this.matchers[loginEndpoint === null || loginEndpoint === void 0 ? void 0 : loginEndpoint.toLowerCase()]; if (matcher === null || matcher === void 0 ? void 0 : matcher.isMatch(hostname)) { return; } } const additionalMatcher = this.additionalMatcher; if (additionalMatcher === null || additionalMatcher === void 0 ? void 0 : additionalMatcher.isMatch(hostname)) { return; } throw new Error(`Can't communicate with '${hostname}' as this hostname is currently not trusted; please see https://aka.ms/kustotrustedendpoints`); } _isLocalAddress(host) { if (["localhost", "127.0.0.1", "::1", "[::1]"].find((c) => c === host)) { return true; } if (host.startsWith("127.") && host.length <= 15 && host.length >= 9) { for (let i = 0; i < host.length; i++) { const ch = host.charAt(i); if (ch !== "." && (ch < "0" || ch > "9")) { return false; } } return true; } return false; } } export const kustoTrustedEndpoints = new KustoTrustedEndpointsImpl(); //# sourceMappingURL=kustoTrustedEndpoints.js.map