50 lines
1.5 KiB
TypeScript
50 lines
1.5 KiB
TypeScript
|
import { createMiddleware } from 'hono/factory'
|
||
|
import { escape } from './escape'
|
||
|
|
||
|
/**
|
||
|
* Converts a list of strings into a regular expression group.
|
||
|
* Each string in the list is escaped using `RegExp.escape()` or polyfill
|
||
|
* and then joined by a '|' (OR) operator. The entire result is wrapped in
|
||
|
* parentheses to form a capturing group.
|
||
|
*
|
||
|
* @param list An array of strings to include in the regex.
|
||
|
* @returns A RegExp matching any of the strings in the capture group.
|
||
|
*/
|
||
|
function listToRegex(list: string[]): RegExp | undefined {
|
||
|
let regex
|
||
|
|
||
|
if (list.length > 0) {
|
||
|
const formatted = list.map((item) => escape(item.toUpperCase())).join('|')
|
||
|
regex = new RegExp(`(${formatted})`)
|
||
|
}
|
||
|
|
||
|
return regex
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @param params - `blocklist`: An array of user-agents to block, or a RegExp to match against. NOTE: If passing a RegExp, it should match on UPPERCASE User Agents.
|
||
|
* @returns the Hono middleware to block requests based on User-Agent header.
|
||
|
*/
|
||
|
export function uaBlocker(params = { blocklist: [] as string[] | RegExp }) {
|
||
|
const regex = Array.isArray(params.blocklist) ? listToRegex(params.blocklist) : params.blocklist
|
||
|
|
||
|
return createMiddleware(async (c, next) => {
|
||
|
const userAgent = c.req.header('User-Agent')?.toUpperCase()
|
||
|
|
||
|
if (userAgent && regex && userAgent.match(regex)) {
|
||
|
return c.text('Forbidden', 403)
|
||
|
}
|
||
|
|
||
|
await next()
|
||
|
return
|
||
|
})
|
||
|
}
|
||
|
|
||
|
export default uaBlocker
|
||
|
|
||
|
// Export for testing purposes
|
||
|
export const __test = {
|
||
|
listToRegex,
|
||
|
}
|