Comprehensive usage examples for URL Sheriff in production applications.
import URLSheriff from 'url-sheriff';
const sheriff = new URLSheriff();
// Safe public URLs - returns true
await sheriff.isSafeURL('https://example.com');
await sheriff.isSafeURL('http://api.github.com/users');
// Unsafe private URLs - throws Error
try {
await sheriff.isSafeURL('http://127.0.0.1:3000');
} catch (error) {
console.error(error.message); // "URL uses a private hostname"
}
try {
await sheriff.isSafeURL('http://localhost:8080');
} catch (error) {
console.error(error.message); // "URL uses a private hostname"
}
try {
await sheriff.isSafeURL('http://192.168.1.1');
} catch (error) {
console.error(error.message); // "URL uses a private hostname"
}The library accepts both string URLs and URL objects:
import URLSheriff from 'url-sheriff';
const sheriff = new URLSheriff();
// String URL
await sheriff.isSafeURL('https://example.com');
// URL object
const urlObj = new URL('https://example.com');
await sheriff.isSafeURL(urlObj);
// Both work identicallyimport URLSheriff from 'url-sheriff';
const sheriff = new URLSheriff({
allowList: [
'localhost', // Exact string match
'127.0.0.1', // IP address match
/^.*\.internal\.company\.com$/, // Regex pattern match
/^api-\d+\.staging\.example\.org$/ // Dynamic subdomain pattern
]
});
// These URLs are now allowed
await sheriff.isSafeURL('http://localhost:3000'); // true
await sheriff.isSafeURL('http://127.0.0.1:8080'); // true
await sheriff.isSafeURL('https://app.internal.company.com'); // true
await sheriff.isSafeURL('https://api-01.staging.example.org'); // trueimport URLSheriff from 'url-sheriff';
const sheriff = new URLSheriff({
allowList: ['localhost']
});
// Add more entries at runtime
sheriff.addToAllowList(['trusted-domain.com', /^.*\.safe\.net$/]);
await sheriff.isSafeURL('https://trusted-domain.com'); // true
// Remove entries from allow-list
sheriff.removeFromAllowList(['localhost']);
// Now localhost URLs will be blocked again
// Get current allow-list
const currentAllowList = sheriff.getAllowList();
console.log(currentAllowList); // Array of current entriesComplex pattern matching for dynamic hostnames:
import URLSheriff from 'url-sheriff';
const sheriff = new URLSheriff({
allowList: [
// Match any subdomain of internal.example.com
/^.*\.internal\.example\.com$/,
// Match numbered API instances
/^api-\d+\.staging\.example\.org$/,
// Match specific pattern with port
/^service-[a-z]+\.dev\.company\.com:\d+$/
]
});
await sheriff.isSafeURL('https://app.internal.example.com'); // true
await sheriff.isSafeURL('https://api-01.staging.example.org'); // true
await sheriff.isSafeURL('https://api-99.staging.example.org'); // true
await sheriff.isSafeURL('https://service-auth.dev.company.com:8080'); // trueimport URLSheriff from 'url-sheriff';
const sheriff = new URLSheriff({
allowedSchemes: ['https']
});
// Only HTTPS is allowed
await sheriff.isSafeURL('https://example.com'); // Returns true
try {
await sheriff.isSafeURL('http://example.com');
} catch (error) {
console.error(error.message); // "URL scheme 'http' is not allowed"
}
try {
await sheriff.isSafeURL('ftp://example.com');
} catch (error) {
console.error(error.message); // "URL scheme 'ftp' is not allowed"
}import URLSheriff from 'url-sheriff';
const sheriff = new URLSheriff({
allowedSchemes: ['https']
});
// Update allowed schemes at runtime
sheriff.setAllowedSchemes(['https', 'http']);
await sheriff.isSafeURL('http://example.com'); // Now returns true
// Get current allowed schemes
const schemes = sheriff.getAllowedSchemes(); // Returns ['https', 'http']
// Remove all scheme restrictions
sheriff.clearSchemeRestrictions();
await sheriff.isSafeURL('ftp://example.com'); // Now returns trueScheme validation is case-insensitive:
import URLSheriff from 'url-sheriff';
const sheriff = new URLSheriff({
allowedSchemes: ['https']
});
// All of these work (case-insensitive)
await sheriff.isSafeURL('https://example.com'); // true
await sheriff.isSafeURL('HTTPS://example.com'); // true
await sheriff.isSafeURL('Https://example.com'); // trueDirect IP address checking:
import URLSheriff from 'url-sheriff';
const sheriff = new URLSheriff();
// Check if IP addresses are private
sheriff.isPrivateIPAddress('192.168.1.1'); // true - private range
sheriff.isPrivateIPAddress('10.0.0.1'); // true - private range
sheriff.isPrivateIPAddress('127.0.0.1'); // true - loopback
sheriff.isPrivateIPAddress('169.254.1.1'); // true - link-local
sheriff.isPrivateIPAddress('8.8.8.8'); // false - public IP
sheriff.isPrivateIPAddress('1.1.1.1'); // false - public IP
// IPv6 addresses
sheriff.isPrivateIPAddress('::1'); // true - loopback
sheriff.isPrivateIPAddress('fe80::1'); // true - link-local
sheriff.isPrivateIPAddress('2001:4860:4860::8888'); // false - public IP
// IPv4-mapped IPv6 addresses are handled correctly
sheriff.isPrivateIPAddress('::ffff:192.168.1.1'); // true - private
sheriff.isPrivateIPAddress('::ffff:8.8.8.8'); // false - publicimport URLSheriff from 'url-sheriff';
const sheriff = new URLSheriff();
const ips = await sheriff.hostnameLookup('example.com');
console.log(ips); // ['93.184.216.34', '2606:2800:220:1:248:1893:25c8:1946']import URLSheriff from 'url-sheriff';
const sheriff = new URLSheriff({
dnsResolvers: ['1.1.1.1']
});
const ipsCustom = await sheriff.resolveHostnameViaServers('example.com');
console.log(ipsCustom); // ['93.184.216.34'] - IPv4 onlyUsing multiple DNS servers for redundancy:
import URLSheriff from 'url-sheriff';
const sheriff = new URLSheriff({
dnsResolvers: ['1.1.1.1', '8.8.8.8', '9.9.9.9']
});
// Will use the configured resolvers in order
await sheriff.isSafeURL('https://example.com');import express from 'express';
import URLSheriff from 'url-sheriff';
const app = express();
const sheriff = new URLSheriff({ allowedSchemes: ['https'] });
app.use(express.json());
app.post('/fetch-url', async (req, res) => {
const { url } = req.body;
try {
await sheriff.isSafeURL(url);
// URL is safe, proceed with fetch
const response = await fetch(url);
const data = await response.json();
res.json(data);
} catch (error) {
res.status(400).json({ error: error.message });
}
});import URLSheriff from 'url-sheriff';
const sheriff = new URLSheriff();
async function safeFetch(url: string | URL, options?: RequestInit): Promise<Response> {
await sheriff.isSafeURL(url);
return fetch(url, options);
}
// Usage
try {
const response = await safeFetch('https://api.example.com/data');
const data = await response.json();
} catch (error) {
if (error.message.includes('private hostname') || error.message.includes('Invalid URL')) {
console.error('Security violation:', error.message);
} else {
console.error('Fetch error:', error);
}
}import URLSheriff from 'url-sheriff';
const sheriff = new URLSheriff();
async function validateURLs(urls: string[]): Promise<Map<string, boolean>> {
const results = new Map<string, boolean>();
await Promise.all(
urls.map(async (url) => {
try {
await sheriff.isSafeURL(url);
results.set(url, true);
} catch (error) {
results.set(url, false);
}
})
);
return results;
}
// Usage
const urls = ['https://example.com', 'http://127.0.0.1', 'https://api.github.com'];
const validationResults = await validateURLs(urls);
validationResults.forEach((isSafe, url) => {
console.log(`${url}: ${isSafe ? 'SAFE' : 'UNSAFE'}`);
});import URLSheriff from 'url-sheriff';
// Recommended: Restrict to HTTPS only in production
const productionSheriff = new URLSheriff({
allowedSchemes: ['https']
});
// Recommended: Use minimal allow-list, only for trusted internal services
const internalSheriff = new URLSheriff({
allowList: [
// Only specific trusted domains
'trusted-api.internal.com',
// Avoid overly permissive regex patterns
// BAD: /.*/ (allows everything)
// GOOD: /^[a-z0-9-]+\.internal\.company\.com$/ (specific pattern)
]
});
// Recommended: Validate user-provided URLs before processing
async function validateUserURL(userProvidedURL: string): Promise<boolean> {
try {
await productionSheriff.isSafeURL(userProvidedURL);
return true;
} catch (error) {
// Log security event
console.error('Blocked unsafe URL:', userProvidedURL, error.message);
return false;
}
}DNS lookups are asynchronous and may introduce latency:
import URLSheriff from 'url-sheriff';
const sheriff = new URLSheriff();
// DNS resolution happens for every domain validation
// Consider caching results for frequently validated domains
const urlCache = new Map<string, boolean>();
async function cachedIsSafeURL(url: string): Promise<boolean> {
if (urlCache.has(url)) {
return urlCache.get(url)!;
}
try {
const isSafe = await sheriff.isSafeURL(url);
urlCache.set(url, isSafe);
return isSafe;
} catch (error) {
urlCache.set(url, false);
throw error;
}
}import URLSheriff from 'url-sheriff';
// Using fast public DNS resolvers
const fastSheriff = new URLSheriff({
dnsResolvers: ['1.1.1.1', '8.8.8.8'] // Cloudflare and Google DNS
});
// May be faster than system DNS in some environments
await fastSheriff.isSafeURL('https://example.com');