Advanced scenarios and edge cases for URL Sheriff.
Comprehensive IPv6 address handling:
import URLSheriff from 'url-sheriff';
const sheriff = new URLSheriff();
// IPv6 loopback
sheriff.isPrivateIPAddress('::1'); // true
await sheriff.isSafeURL('http://[::1]:3000'); // throws Error
// IPv6 link-local
sheriff.isPrivateIPAddress('fe80::1'); // true
await sheriff.isSafeURL('http://[fe80::1]:8080'); // throws Error
// IPv6 private ranges
sheriff.isPrivateIPAddress('fc00::1'); // true (unique local)
sheriff.isPrivateIPAddress('fd00::1'); // true (unique local)
// IPv4-mapped IPv6
sheriff.isPrivateIPAddress('::ffff:127.0.0.1'); // true
sheriff.isPrivateIPAddress('::ffff:192.168.1.1'); // true
sheriff.isPrivateIPAddress('::ffff:8.8.8.8'); // false
// Public IPv6
sheriff.isPrivateIPAddress('2001:4860:4860::8888'); // false
await sheriff.isSafeURL('http://[2001:4860:4860::8888]'); // returns trueAll RFC 1918 and related private ranges:
import URLSheriff from 'url-sheriff';
const sheriff = new URLSheriff();
// RFC 1918 Private Addresses
sheriff.isPrivateIPAddress('10.0.0.1'); // true - 10.0.0.0/8
sheriff.isPrivateIPAddress('10.255.255.255'); // true
sheriff.isPrivateIPAddress('172.16.0.1'); // true - 172.16.0.0/12
sheriff.isPrivateIPAddress('172.31.255.255'); // true
sheriff.isPrivateIPAddress('192.168.0.1'); // true - 192.168.0.0/16
sheriff.isPrivateIPAddress('192.168.255.255'); // true
// Loopback
sheriff.isPrivateIPAddress('127.0.0.1'); // true
sheriff.isPrivateIPAddress('127.255.255.255'); // true
// Link-local
sheriff.isPrivateIPAddress('169.254.0.1'); // true
sheriff.isPrivateIPAddress('169.254.255.255'); // true
// Multicast (also considered non-unicast)
sheriff.isPrivateIPAddress('224.0.0.1'); // true
sheriff.isPrivateIPAddress('239.255.255.255'); // trueHow the library handles invalid URL formats:
import URLSheriff from 'url-sheriff';
const sheriff = new URLSheriff();
// Invalid URL strings
try {
await sheriff.isSafeURL('not a url');
} catch (error) {
console.error(error.message); // "Invalid URL provided"
}
try {
await sheriff.isSafeURL('://missing-scheme');
} catch (error) {
console.error(error.message); // "Invalid URL provided"
}
try {
await sheriff.isSafeURL('http://');
} catch (error) {
console.error(error.message); // "Invalid URL provided"
}
// Empty string
try {
await sheriff.isSafeURL('');
} catch (error) {
console.error(error.message); // "Invalid URL provided"
}Handling DNS failures and edge cases:
import URLSheriff from 'url-sheriff';
const sheriff = new URLSheriff();
// Non-existent domain
try {
await sheriff.isSafeURL('https://nonexistent-domain-12345-xyz.com');
} catch (error) {
// DNS resolution fails - error message will indicate DNS failure
console.error(error.message); // DNS error (e.g., "ENOTFOUND")
}
// Domain with no A/AAAA records
try {
await sheriff.isSafeURL('https://domain-with-only-mx-records.example');
} catch (error) {
// DNS lookup may fail or return empty
console.error(error.message);
}
// Using custom resolver when not configured
const sheriffNoCustom = new URLSheriff(); // No dnsResolvers
try {
await sheriffNoCustom.resolveHostnameViaServers('example.com');
} catch (error) {
console.error(error.message); // "DNS resolver is not defined"
}Complex allow-list scenarios:
import URLSheriff from 'url-sheriff';
const sheriff = new URLSheriff({
allowList: ['localhost', /^.*\.internal\.com$/]
});
// Exact string match (case-sensitive for strings)
await sheriff.isSafeURL('http://localhost:3000'); // true
await sheriff.isSafeURL('http://LOCALHOST:3000'); // false (case-sensitive string match)
// Regex is case-sensitive by default
await sheriff.isSafeURL('https://app.internal.com'); // true
await sheriff.isSafeURL('https://APP.INTERNAL.COM'); // false (unless regex is case-insensitive)
// IP address in allow-list matches hostname, not resolved IPs
const sheriffIP = new URLSheriff({
allowList: ['192.168.1.1']
});
// If domain resolves to 192.168.1.1, it's still blocked unless hostname matches
await sheriffIP.isSafeURL('http://192.168.1.1'); // true (hostname matches)
await sheriffIP.isSafeURL('http://some-domain.com'); // false (even if resolves to 192.168.1.1)
// Removing non-existent entries is safe
sheriff.removeFromAllowList(['non-existent-entry']); // No error, silently ignored
// Adding duplicate entries
sheriff.addToAllowList(['localhost']);
sheriff.addToAllowList(['localhost']); // Duplicate added (no deduplication)Scheme handling edge cases:
import URLSheriff from 'url-sheriff';
const sheriff = new URLSheriff({
allowedSchemes: ['https']
});
// Empty scheme array clears restrictions
sheriff.setAllowedSchemes([]); // Returns null, clears restrictions
await sheriff.isSafeURL('http://example.com'); // Now allowed
// Setting to null/undefined
sheriff.clearSchemeRestrictions();
await sheriff.isSafeURL('ftp://example.com'); // Now allowed
// Case variations
await sheriff.setAllowedSchemes(['HTTPS', 'Http']);
const schemes = sheriff.getAllowedSchemes();
console.log(schemes); // ['https', 'http'] - normalized to lowercaseHandling multiple simultaneous validations:
import URLSheriff from 'url-sheriff';
const sheriff = new URLSheriff();
// Multiple concurrent validations
const urls = [
'https://example.com',
'https://github.com',
'http://127.0.0.1',
'https://api.example.com'
];
const results = await Promise.allSettled(
urls.map(url => sheriff.isSafeURL(url))
);
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`${urls[index]} is safe`);
} else {
console.log(`${urls[index]} is unsafe: ${result.reason.message}`);
}
});try {
await sheriff.isSafeURL('not a valid url');
} catch (error) {
console.error(error.message); // "Invalid URL provided"
// error is an instance of Error
}const sheriff = new URLSheriff({ allowedSchemes: ['https'] });
try {
await sheriff.isSafeURL('ftp://example.com');
} catch (error) {
console.error(error.message); // "URL scheme 'ftp' is not allowed"
// error is an instance of Error
}try {
await sheriff.isSafeURL('http://192.168.1.1');
} catch (error) {
console.error(error.message); // "URL uses a private hostname"
// error is an instance of Error
}try {
await sheriff.isSafeURL('https://nonexistent-domain-12345.com');
} catch (error) {
console.error(error.message); // DNS error message (e.g., "ENOTFOUND")
// error is an instance of Error, may be a DNS-specific error
}const sheriff = new URLSheriff(); // No custom resolvers configured
try {
await sheriff.resolveHostnameViaServers('example.com');
} catch (error) {
console.error(error.message); // "DNS resolver is not defined"
// error is an instance of Error
}All errors thrown by URL Sheriff are instances of the standard JavaScript Error class. The error message provides specific information about why validation failed:
resolveHostnameViaServers without configuring dnsResolvers