Skip to content

Instantly share code, notes, and snippets.

@shuntksh
Created November 5, 2023 04:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shuntksh/2559d4dbf8ddc4263aa0a781c51c6b0b to your computer and use it in GitHub Desktop.
Save shuntksh/2559d4dbf8ddc4263aa0a781c51c6b0b to your computer and use it in GitHub Desktop.
Simple Trie for Domain Matching
class TrieNode {
children: Map<string, TrieNode> = new Map();
isValidDomain = false;
hasWildcard = false;
}
class Trie {
static build(allowedDomains: string | string[] | Trie, allowedSubDomains: string[]): Trie {
if (allowedDomains instanceof Trie) return allowedDomains;
if (!Array.isArray(allowedDomains)) allowedDomains = [allowedDomains];
const trie = new Trie();
for (const domain of allowedDomains) {
const parsedDomain = safeURL(domain);
if (!parsedDomain) throw new Error("allowedDomains has to be valid domain format");
trie.insert(parsedDomain);
for (const subdomain of allowedSubDomains) {
const parsedSubDomain = safeURL(subdomain + "." + domain);
if (!parsedSubDomain) throw new Error("allowedDomains has to be valid domain format");
trie.insert(parsedSubDomain);
}
}
return trie;
}
#root: TrieNode = new TrieNode();
insert(domain: string): void {
let node = this.#root;
for (const part of domain.split(".").reverse()) {
if (part === "*") {
node.hasWildcard = true;
break; // A wildcard applies to all subdomains, so stop here.
}
if (!node.children.has(part)) {
node.children.set(part, new TrieNode());
}
node = node.children.get(part)!;
}
node.isValidDomain = true;
}
has(fqdn: string | null | undefined): boolean {
const parsed = safeURL(fqdn);
if (!parsed) return false;
let node = this.#root;
for (const part of parsed.split(".").reverse()) {
if (node.hasWildcard) return true;
if (!node.children.has(part)) return false;
node = node.children.get(part)!;
}
return node.isValidDomain;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment