hono-honeypot 1.2.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,9 @@
1
+ <p align="center">
2
+ <a href="https://github.com/ph33nx/hono-honeypot">
3
+ <img src="https://raw.githubusercontent.com/ph33nx/hono-honeypot/main/assets/hero.png" alt="hono-honeypot. Block bots before they reach your routes. Zero dependencies, MIT licensed." width="100%">
4
+ </a>
5
+ </p>
6
+
1
7
  # hono-honeypot
2
8
 
3
9
  Production-grade security middleware for [Hono.js](https://hono.dev). Intercepts vulnerability scanners, bot crawlers, and brute-force probes before they reach your application logic.
@@ -49,6 +55,10 @@ app.use('*', honeypot({
49
55
 
50
56
  ### Pattern Matching (stateless, zero-config)
51
57
 
58
+ <p align="center">
59
+ <img src="https://raw.githubusercontent.com/ph33nx/hono-honeypot/main/assets/patterns.png" alt="200+ attack patterns built in. wp-admin, .env, .git, /actuator, /@fs/. Smart anchoring, no false positives." width="100%">
60
+ </p>
61
+
52
62
  Out of the box, the middleware matches request paths against 200+ regex patterns covering:
53
63
 
54
64
  | Category | Examples |
@@ -56,6 +66,7 @@ Out of the box, the middleware matches request paths against 200+ regex patterns
56
66
  | PHP/WordPress | `*.php`, `/wp-admin`, `/xmlrpc.php`, `/wp-content/` |
57
67
  | Admin panels | `/admin`, `/phpmyadmin`, `/cpanel`, `/cgi-bin` |
58
68
  | CMS frameworks | `/typo3`, `/joomla`, `/drupal`, `/magento` |
69
+ | Magento REST API | `/rest/V1/store/storeConfigs` and store-scope variants |
59
70
  | JS framework fingerprinting | `/_next`, `/_rsc`, `/_vercel`, `next.config.js`, `nuxt.config.ts` |
60
71
  | Deployment configs | `serverless.yml`, `vercel.json`, `netlify.toml`, `package.json` |
61
72
  | Docker/container | `docker-compose.yml`, `Dockerfile`, `/docker/` |
@@ -65,6 +76,7 @@ Out of the box, the middleware matches request paths against 200+ regex patterns
65
76
  | SSH/auth tokens | `/.ssh/`, `/id_rsa`, `/.npmrc`, `/.pypirc` |
66
77
  | System path traversal | `/var/task/`, `/var/log/`, `/opt/` |
67
78
  | Command injection | `$(pwd)`, backtick injection |
79
+ | URL normalisation probes | Zero-width Unicode (`U+200B`, `U+FEFF` BOM, U+200C–U+200F, U+202A–U+202E directional overrides) |
68
80
  | Log files | `*.log`, `error_log` |
69
81
  | Java/Spring Boot | `/WEB-INF`, `/manager/html`, `/solr`, `/actuator` |
70
82
  | Dependency manifests | `composer.json`, `Gemfile`, `requirements.txt` |
@@ -136,6 +148,10 @@ Why `410 Gone` is the default:
136
148
 
137
149
  ## IP Strike/Ban System
138
150
 
151
+ <p align="center">
152
+ <img src="https://raw.githubusercontent.com/ph33nx/hono-honeypot/main/assets/strike-ban.png" alt="3 strikes, 24-hour ban. Memory, Redis, or Cloudflare KV. O(1) ban check before pattern matching." width="100%">
153
+ </p>
154
+
139
155
  Without a store, the middleware is stateless: it blocks matching paths but imposes no penalty on repeat offenders. With a store, it tracks strikes per IP and bans IPs that exceed the threshold.
140
156
 
141
157
  **Flow:**
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- 'use strict';var factory=require('hono/factory');var $=class{strikes=new Map;bans=new Map;strikeTTL;banTTL;constructor(i){this.strikeTTL=(i?.strikeTTL??3600)*1e3,this.banTTL=(i?.banTTL??86400)*1e3;}isBanned(i){let n=this.bans.get(i);return n===void 0?false:Date.now()>n?(this.bans.delete(i),false):true}addStrike(i){let n=Date.now(),r=this.strikes.get(i);return r&&n<r.expires?(r.count++,r.count):(this.strikes.set(i,{count:1,expires:n+this.strikeTTL}),1)}ban(i){this.bans.set(i,Date.now()+this.banTTL);}resetStrikes(i){this.strikes.delete(i);}},b=[/\.php/i,/\/config\.php/i,/\/phpinfo/i,/\/eval-stdin\.php/i,/\/xmlrpc\.php/i,/\/ALFA_DATA/i,/\/c99\.php/i,/\/r57\.php/i,/\/shell\.php/i,/\/webshell/i,/^\/wp$/i,/^\/wp-/i,/^\/wordpress/i,/\/wp-includes\//i,/\/wp-content\//i,/\/wp-admin/i,/wlwmanifest\.xml$/i,/^\/uploads?$/i,/^\/images$/i,/^\/assets$/i,/^\/files$/i,/^\/media$/i,/^\/public$/i,/\/admin\/(uploads?|images|editor|fckeditor|controller)/i,/^\/modules$/i,/^\/plugins$/i,/^\/components$/i,/^\/system$/i,/^\/template$/i,/^\/includes?$/i,/^\/vendor$/i,/^\/local$/i,/^\/php$/i,/\/fckeditor\/editor\/filemanager/i,/\/sites\/default\/files/i,/\/images\/stories/i,/\/modules\/mod_simplefileupload/i,/\/controller\/extension/i,/\/ckeditor/i,/\/tinymce/i,/\/elfinder/i,/^\/admin(\.php)?$/i,/^\/administrator/i,/^\/phpmyadmin/i,/^\/cpanel/i,/^\/whm/i,/^\/cgi-bin/i,/^\/typo3/i,/^\/joomla/i,/^\/drupal/i,/^\/magento/i,/\/\.git/i,/\/\.svn/i,/\/\.hg/i,/\/\.env/i,/\/\.sql$/i,/\/\.well-known\/security\.txt/i,/\/(vendor|node_modules)\//i,/\/\.htaccess$/i,/\/\.htpasswd$/i,/\/\.DS_Store$/i,/\/Thumbs\.db$/i,/\/\.ssh/i,/\/id_rsa/i,/\/id_ed25519/i,/\/\.npmrc$/i,/\/\.pypirc$/i,/\/\.aws\//i,/\.(bak|old|backup|orig|save|swp)$/i,/\.(7z|tgz|tar\.gz|tar|bz2|war|jar)$/i,/^\/config\.(js|json|yml|yaml|xml|ini|conf)$/i,/^\/settings\.(js|json|yml|yaml|xml)$/i,/^\/credentials\.(js|json|yml|yaml)$/i,/^\/secrets\.(js|json|yml|yaml|env)$/i,/^\/appsettings\.(json|yml|yaml)$/i,/^\/application\.(yml|yaml|xml|properties)$/i,/sftp-config\.json$/i,/ftpsync\.settings$/i,/\.ftpconfig$/i,/\.ftppass$/i,/\.remote-sync\.json$/i,/ftp-deploy\.json$/i,/^\/env\.js$/i,/^\/main\.js$/i,/^\/index\.js$/i,/^\/app\.js$/i,/^\/server-(status|info)$/i,/^\/info$/i,/^\/swagger/i,/^\/api\/swagger\.(json|yml|yaml)$/i,/^\/api-docs/i,/^\/v\d+\/api-docs/i,/^\/_env/i,/^\/env$/i,/^\/config\//i,/^\/backup/i,/^\/bk$/i,/^\/bak$/i,/^\/bac$/i,/^\/dump/i,/^\/db_/i,/^\/sql/i,/^\/shell/i,/^\/old$/i,/^\/new$/i,/^\/test$/i,/^\/demo$/i,/^\/www$/i,/^\/main$/i,/^\/site$/i,/^\/shop$/i,/^\/bc$/i,/^\/sitio$/i,/^\/sito$/i,/^\/oldsite$/i,/^\/old-site$/i,/^\/script$/i,/^\/\d{4}$/i,/^\/getcmd$/i,/\$\(/,/`/,/"/,/\{(curl|wget|bash|sh|nc|ncat|python|perl|ruby|php),/i,/\.oast\.(site|fun|live|me|online|pro)/i,/(%00|\x00)/,/^\/_next/i,/^\/_rsc/i,/^\/__rsc/i,/^\/_vercel/i,/next\.config\.(js|mjs|ts)$/i,/nuxt\.config\.(js|ts)$/i,/craco\.config\.js$/i,/serverless\.(yml|yaml|json)$/i,/vercel\.json$/i,/netlify\.toml$/i,/\/helm\//i,/\/package\.json$/i,/\/composer\.(json|lock)$/i,/\/Gemfile(\.lock)?$/i,/\/requirements\.txt$/i,/docker-compose\.(yml|yaml)$/i,/Dockerfile$/i,/\/docker\//i,/^\/aws/i,/\/aws[_-]s3/i,/\/aws[_-]ses/i,/\.\.\//,/\.\.%2f/i,/\.\.%5c/i,/^\/etc\//i,/^\/proc\//i,/^\/var\//i,/^\/opt\//i,/\/passwd$/i,/\.log$/i,/\/error_log$/i,/^\/@fs\//i,/^\/@vite\//i,/^\/@id\//i,/^\/_ignition/i,/^\/__debug__/i,/\/WEB-INF/i,/^\/manager\/html/i,/^\/solr/i,/^\/actuator/i,/\/elmah\.axd$/i,/^\/servlet\//i,/bsh\.servlet/i,/^\/struts\//i,/^\/invoker\//i,/\.action$/i,/\/mailcow/i,/^\/roundcube\//i,/^\/webmail\//i,/^\/adminer/i,/^\/pma\//i,/^\/myadmin\//i,/^\/mysqladmin/i,/^\/dbadmin/i,/^\/proxy\//i,/169\.254\.169\.254/,/^\/latest\/meta-data/i,/^\/HNAP1\//i,/^\/boaform\//i,/^\/GponForm\//i,/^\/setup\.cgi$/i,/\.htm$/i,/^\/owa\//i,/^\/aspnet_client\//i,/^\/autodiscover\//i,/^\/ecp\//i,/^\/_layouts\//i,/^\/_vti_bin\//i,/^\/WebInterface\//i,/^\/owncloud\//i,/^\/nextcloud\//i,/^\/geoserver\//i,/^\/geowebcache\//i,/^\/confluence\//i,/^\/jira\//i,/^\/grafana\//i,/^\/kibana\//i,/^\/prometheus\//i,/^\/jenkins\//i,/\/j_acegi_security_check/i,/^\/portainer\//i,/^\/gitea\//i,/^\/gitlab\//i,/^\/metrics$/i,/^\/healthz$/i,/^\/readyz$/i,/^\/livez$/i,/^\/console\//i,/^\/debug\//i,/^\/\.dockerenv$/i];function k(e){return e.req.header("cf-connecting-ip")||e.req.header("x-forwarded-for")?.split(",")[0]?.trim()||e.req.header("x-real-ip")||"unknown"}var v=(e={})=>{let i=[...b,...e.patterns||[]];e.exclude?.length&&(i=i.filter(t=>!e.exclude.some(p=>t.source===p.source)));let n=e.log??true,r=e.status??410,a=e.store,u=e.strikeThreshold??3,g=e.getIP??k,l=e.onBlocked;return factory.createMiddleware(async(t,p)=>{let s=g(t),m=s&&s!=="unknown";if(a&&m&&await a.isBanned(s)){let o=t.req.path.replace(/\/+/g,"/");return l?l({ip:s,path:o,method:t.req.method,reason:"banned"}):n&&console.log(`\u{1F6AB} Banned [${s}] ${t.req.method} ${o}`),t.body(null,r)}let c=t.req.path.replace(/\/+/g,"/");if(i.some(o=>o.test(c))){let o,d=false;if(a&&m&&(o=await a.addStrike(s),o>=u&&(await a.ban(s),await a.resetStrikes(s),d=true)),l)l({ip:s,path:c,method:t.req.method,reason:"pattern",strikes:o,banned:d});else if(n){let h=d?" \u{1F6AB} BANNED":"";console.log(`\u{1F36F} Blocked [${s}] ${t.req.method} ${c}${h}`);}return t.body(null,r)}return p()})};/**
1
+ 'use strict';var factory=require('hono/factory');var $=class{strikes=new Map;bans=new Map;strikeTTL;banTTL;constructor(i){this.strikeTTL=(i?.strikeTTL??3600)*1e3,this.banTTL=(i?.banTTL??86400)*1e3;}isBanned(i){let n=this.bans.get(i);return n===void 0?false:Date.now()>n?(this.bans.delete(i),false):true}addStrike(i){let n=Date.now(),r=this.strikes.get(i);return r&&n<r.expires?(r.count++,r.count):(this.strikes.set(i,{count:1,expires:n+this.strikeTTL}),1)}ban(i){this.bans.set(i,Date.now()+this.banTTL);}resetStrikes(i){this.strikes.delete(i);}},b=[/\.php/i,/\/config\.php/i,/\/phpinfo/i,/\/eval-stdin\.php/i,/\/xmlrpc\.php/i,/\/ALFA_DATA/i,/\/c99\.php/i,/\/r57\.php/i,/\/shell\.php/i,/\/webshell/i,/^\/wp$/i,/^\/wp-/i,/^\/wordpress/i,/\/wp-includes\//i,/\/wp-content\//i,/\/wp-admin/i,/wlwmanifest\.xml$/i,/^\/uploads?$/i,/^\/images$/i,/^\/assets$/i,/^\/files$/i,/^\/media$/i,/^\/public$/i,/\/admin\/(uploads?|images|editor|fckeditor|controller)/i,/^\/modules$/i,/^\/plugins$/i,/^\/components$/i,/^\/system$/i,/^\/template$/i,/^\/includes?$/i,/^\/vendor$/i,/^\/local$/i,/^\/php$/i,/\/fckeditor\/editor\/filemanager/i,/\/sites\/default\/files/i,/\/images\/stories/i,/\/modules\/mod_simplefileupload/i,/\/controller\/extension/i,/\/ckeditor/i,/\/tinymce/i,/\/elfinder/i,/^\/admin(\.php)?$/i,/^\/administrator/i,/^\/phpmyadmin/i,/^\/cpanel/i,/^\/whm/i,/^\/cgi-bin/i,/^\/typo3/i,/^\/joomla/i,/^\/drupal/i,/^\/magento/i,/^\/rest\/(?:[a-z0-9_-]+\/)?v\d+(?:\/|$)/i,/\/\.git/i,/\/\.svn/i,/\/\.hg/i,/\/\.env/i,/\/\.sql$/i,/\/(vendor|node_modules)\//i,/\/\.htaccess$/i,/\/\.htpasswd$/i,/\/\.DS_Store$/i,/\/Thumbs\.db$/i,/\/\.ssh/i,/\/id_rsa/i,/\/id_ed25519/i,/\/\.npmrc$/i,/\/\.pypirc$/i,/\/\.aws\//i,/\.(bak|old|backup|orig|save|swp)$/i,/\.(7z|tgz|tar\.gz|tar|bz2|war|jar)$/i,/^\/config\.(js|json|yml|yaml|xml|ini|conf)$/i,/^\/settings\.(js|json|yml|yaml|xml)$/i,/^\/credentials\.(js|json|yml|yaml)$/i,/^\/secrets\.(js|json|yml|yaml|env)$/i,/^\/appsettings\.(json|yml|yaml)$/i,/^\/application\.(yml|yaml|xml|properties)$/i,/sftp-config\.json$/i,/ftpsync\.settings$/i,/\.ftpconfig$/i,/\.ftppass$/i,/\.remote-sync\.json$/i,/ftp-deploy\.json$/i,/^\/env\.js$/i,/^\/main\.js$/i,/^\/index\.js$/i,/^\/app\.js$/i,/^\/server-(status|info)$/i,/^\/info$/i,/^\/swagger/i,/^\/api\/swagger\.(json|yml|yaml)$/i,/^\/api-docs/i,/^\/v\d+\/api-docs/i,/^\/_env/i,/^\/env$/i,/^\/config\//i,/^\/backup/i,/^\/bk$/i,/^\/bak$/i,/^\/bac$/i,/^\/dump/i,/^\/db_/i,/^\/sql/i,/^\/shell/i,/^\/old$/i,/^\/new$/i,/^\/test$/i,/^\/demo$/i,/^\/www$/i,/^\/main$/i,/^\/site$/i,/^\/shop$/i,/^\/bc$/i,/^\/sitio$/i,/^\/sito$/i,/^\/oldsite$/i,/^\/old-site$/i,/^\/script$/i,/^\/\d{4}$/i,/^\/getcmd$/i,/\$\(/,/`/,/"/,/\{(curl|wget|bash|sh|nc|ncat|python|perl|ruby|php),/i,/\.oast\.(site|fun|live|me|online|pro)/i,/(%00|\x00)/,/[\u2000-\u203F\uFEFF]/u,/^\/storfs-asup$/i,/^\/_next/i,/^\/_rsc/i,/^\/__rsc/i,/^\/_vercel/i,/next\.config\.(js|mjs|ts)$/i,/nuxt\.config\.(js|ts)$/i,/craco\.config\.js$/i,/serverless\.(yml|yaml|json)$/i,/vercel\.json$/i,/netlify\.toml$/i,/\/helm\//i,/\/package\.json$/i,/\/composer\.(json|lock)$/i,/\/Gemfile(\.lock)?$/i,/\/requirements\.txt$/i,/docker-compose\.(yml|yaml)$/i,/Dockerfile$/i,/\/docker\//i,/^\/aws/i,/\/aws[_-]s3/i,/\/aws[_-]ses/i,/\.\.\//,/\.\.%2f/i,/\.\.%5c/i,/^\/etc\//i,/^\/proc\//i,/^\/var\//i,/^\/opt\//i,/\/passwd$/i,/\.log$/i,/\/error_log$/i,/^\/@fs\//i,/^\/@vite\//i,/^\/@id\//i,/^\/_ignition/i,/^\/__debug__/i,/\/WEB-INF/i,/^\/manager\/html/i,/^\/solr/i,/^\/actuator/i,/\/elmah\.axd$/i,/^\/servlet\//i,/bsh\.servlet/i,/^\/struts\//i,/^\/invoker\//i,/\.action$/i,/\/mailcow/i,/^\/roundcube\//i,/^\/webmail\//i,/^\/adminer/i,/^\/pma\//i,/^\/myadmin\//i,/^\/mysqladmin/i,/^\/dbadmin/i,/^\/ip$/i,/^\/proxy\.pac$/i,/^\/proxy\//i,/169\.254\.169\.254/,/^\/latest\/meta-data/i,/^\/HNAP1\//i,/^\/boaform\//i,/^\/GponForm\//i,/\.cgi$/i,/\.htm$/i,/^\/sdk$/i,/^\/websso\//i,/^\/owa\//i,/^\/aspnet_client\//i,/^\/autodiscover\//i,/^\/ecp\//i,/^\/_layouts\//i,/^\/_vti_bin\//i,/^\/WebInterface\//i,/^\/owncloud\//i,/^\/nextcloud\//i,/^\/geoserver\//i,/^\/geowebcache\//i,/^\/confluence\//i,/^\/jira\//i,/^\/grafana\//i,/^\/kibana\//i,/^\/prometheus\//i,/^\/jenkins\//i,/\/j_acegi_security_check/i,/^\/portainer\//i,/^\/gitea\//i,/^\/gitlab\//i,/^\/metrics$/i,/^\/healthz$/i,/^\/readyz$/i,/^\/livez$/i,/^\/console\//i,/^\/debug\//i,/^\/\.dockerenv$/i];function k(e){return e.req.header("cf-connecting-ip")||e.req.header("x-forwarded-for")?.split(",")[0]?.trim()||e.req.header("x-real-ip")||"unknown"}var v=(e={})=>{let i=[...b,...e.patterns||[]];e.exclude?.length&&(i=i.filter(t=>!e.exclude.some(p=>t.source===p.source)));let n=e.log??true,r=e.status??410,a=e.store,u=e.strikeThreshold??3,g=e.getIP??k,l=e.onBlocked;return factory.createMiddleware(async(t,p)=>{let s=g(t),m=s&&s!=="unknown";if(a&&m&&await a.isBanned(s)){let o=t.req.path.replace(/\/+/g,"/");return l?l({ip:s,path:o,method:t.req.method,reason:"banned"}):n&&console.log(`\u{1F6AB} Banned [${s}] ${t.req.method} ${o}`),t.body(null,r)}let d=t.req.path.replace(/\/+/g,"/");if(i.some(o=>o.test(d))){let o,c=false;if(a&&m&&(o=await a.addStrike(s),o>=u&&(await a.ban(s),await a.resetStrikes(s),c=true)),l)l({ip:s,path:d,method:t.req.method,reason:"pattern",strikes:o,banned:c});else if(n){let h=c?" \u{1F6AB} BANNED":"";console.log(`\u{1F36F} Blocked [${s}] ${t.req.method} ${d}${h}`);}return t.body(null,r)}return p()})};/**
2
2
  * hono-honeypot - Zero-dependency security middleware for Hono.js
3
3
  * Blocks bot attacks, vulnerability scanners, and brute-force attempts.
4
4
  * Optional store-backed IP banning (3 strikes = 24hr ban by default).
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import {createMiddleware}from'hono/factory';var $=class{strikes=new Map;bans=new Map;strikeTTL;banTTL;constructor(i){this.strikeTTL=(i?.strikeTTL??3600)*1e3,this.banTTL=(i?.banTTL??86400)*1e3;}isBanned(i){let n=this.bans.get(i);return n===void 0?false:Date.now()>n?(this.bans.delete(i),false):true}addStrike(i){let n=Date.now(),r=this.strikes.get(i);return r&&n<r.expires?(r.count++,r.count):(this.strikes.set(i,{count:1,expires:n+this.strikeTTL}),1)}ban(i){this.bans.set(i,Date.now()+this.banTTL);}resetStrikes(i){this.strikes.delete(i);}},b=[/\.php/i,/\/config\.php/i,/\/phpinfo/i,/\/eval-stdin\.php/i,/\/xmlrpc\.php/i,/\/ALFA_DATA/i,/\/c99\.php/i,/\/r57\.php/i,/\/shell\.php/i,/\/webshell/i,/^\/wp$/i,/^\/wp-/i,/^\/wordpress/i,/\/wp-includes\//i,/\/wp-content\//i,/\/wp-admin/i,/wlwmanifest\.xml$/i,/^\/uploads?$/i,/^\/images$/i,/^\/assets$/i,/^\/files$/i,/^\/media$/i,/^\/public$/i,/\/admin\/(uploads?|images|editor|fckeditor|controller)/i,/^\/modules$/i,/^\/plugins$/i,/^\/components$/i,/^\/system$/i,/^\/template$/i,/^\/includes?$/i,/^\/vendor$/i,/^\/local$/i,/^\/php$/i,/\/fckeditor\/editor\/filemanager/i,/\/sites\/default\/files/i,/\/images\/stories/i,/\/modules\/mod_simplefileupload/i,/\/controller\/extension/i,/\/ckeditor/i,/\/tinymce/i,/\/elfinder/i,/^\/admin(\.php)?$/i,/^\/administrator/i,/^\/phpmyadmin/i,/^\/cpanel/i,/^\/whm/i,/^\/cgi-bin/i,/^\/typo3/i,/^\/joomla/i,/^\/drupal/i,/^\/magento/i,/\/\.git/i,/\/\.svn/i,/\/\.hg/i,/\/\.env/i,/\/\.sql$/i,/\/\.well-known\/security\.txt/i,/\/(vendor|node_modules)\//i,/\/\.htaccess$/i,/\/\.htpasswd$/i,/\/\.DS_Store$/i,/\/Thumbs\.db$/i,/\/\.ssh/i,/\/id_rsa/i,/\/id_ed25519/i,/\/\.npmrc$/i,/\/\.pypirc$/i,/\/\.aws\//i,/\.(bak|old|backup|orig|save|swp)$/i,/\.(7z|tgz|tar\.gz|tar|bz2|war|jar)$/i,/^\/config\.(js|json|yml|yaml|xml|ini|conf)$/i,/^\/settings\.(js|json|yml|yaml|xml)$/i,/^\/credentials\.(js|json|yml|yaml)$/i,/^\/secrets\.(js|json|yml|yaml|env)$/i,/^\/appsettings\.(json|yml|yaml)$/i,/^\/application\.(yml|yaml|xml|properties)$/i,/sftp-config\.json$/i,/ftpsync\.settings$/i,/\.ftpconfig$/i,/\.ftppass$/i,/\.remote-sync\.json$/i,/ftp-deploy\.json$/i,/^\/env\.js$/i,/^\/main\.js$/i,/^\/index\.js$/i,/^\/app\.js$/i,/^\/server-(status|info)$/i,/^\/info$/i,/^\/swagger/i,/^\/api\/swagger\.(json|yml|yaml)$/i,/^\/api-docs/i,/^\/v\d+\/api-docs/i,/^\/_env/i,/^\/env$/i,/^\/config\//i,/^\/backup/i,/^\/bk$/i,/^\/bak$/i,/^\/bac$/i,/^\/dump/i,/^\/db_/i,/^\/sql/i,/^\/shell/i,/^\/old$/i,/^\/new$/i,/^\/test$/i,/^\/demo$/i,/^\/www$/i,/^\/main$/i,/^\/site$/i,/^\/shop$/i,/^\/bc$/i,/^\/sitio$/i,/^\/sito$/i,/^\/oldsite$/i,/^\/old-site$/i,/^\/script$/i,/^\/\d{4}$/i,/^\/getcmd$/i,/\$\(/,/`/,/"/,/\{(curl|wget|bash|sh|nc|ncat|python|perl|ruby|php),/i,/\.oast\.(site|fun|live|me|online|pro)/i,/(%00|\x00)/,/^\/_next/i,/^\/_rsc/i,/^\/__rsc/i,/^\/_vercel/i,/next\.config\.(js|mjs|ts)$/i,/nuxt\.config\.(js|ts)$/i,/craco\.config\.js$/i,/serverless\.(yml|yaml|json)$/i,/vercel\.json$/i,/netlify\.toml$/i,/\/helm\//i,/\/package\.json$/i,/\/composer\.(json|lock)$/i,/\/Gemfile(\.lock)?$/i,/\/requirements\.txt$/i,/docker-compose\.(yml|yaml)$/i,/Dockerfile$/i,/\/docker\//i,/^\/aws/i,/\/aws[_-]s3/i,/\/aws[_-]ses/i,/\.\.\//,/\.\.%2f/i,/\.\.%5c/i,/^\/etc\//i,/^\/proc\//i,/^\/var\//i,/^\/opt\//i,/\/passwd$/i,/\.log$/i,/\/error_log$/i,/^\/@fs\//i,/^\/@vite\//i,/^\/@id\//i,/^\/_ignition/i,/^\/__debug__/i,/\/WEB-INF/i,/^\/manager\/html/i,/^\/solr/i,/^\/actuator/i,/\/elmah\.axd$/i,/^\/servlet\//i,/bsh\.servlet/i,/^\/struts\//i,/^\/invoker\//i,/\.action$/i,/\/mailcow/i,/^\/roundcube\//i,/^\/webmail\//i,/^\/adminer/i,/^\/pma\//i,/^\/myadmin\//i,/^\/mysqladmin/i,/^\/dbadmin/i,/^\/proxy\//i,/169\.254\.169\.254/,/^\/latest\/meta-data/i,/^\/HNAP1\//i,/^\/boaform\//i,/^\/GponForm\//i,/^\/setup\.cgi$/i,/\.htm$/i,/^\/owa\//i,/^\/aspnet_client\//i,/^\/autodiscover\//i,/^\/ecp\//i,/^\/_layouts\//i,/^\/_vti_bin\//i,/^\/WebInterface\//i,/^\/owncloud\//i,/^\/nextcloud\//i,/^\/geoserver\//i,/^\/geowebcache\//i,/^\/confluence\//i,/^\/jira\//i,/^\/grafana\//i,/^\/kibana\//i,/^\/prometheus\//i,/^\/jenkins\//i,/\/j_acegi_security_check/i,/^\/portainer\//i,/^\/gitea\//i,/^\/gitlab\//i,/^\/metrics$/i,/^\/healthz$/i,/^\/readyz$/i,/^\/livez$/i,/^\/console\//i,/^\/debug\//i,/^\/\.dockerenv$/i];function k(e){return e.req.header("cf-connecting-ip")||e.req.header("x-forwarded-for")?.split(",")[0]?.trim()||e.req.header("x-real-ip")||"unknown"}var v=(e={})=>{let i=[...b,...e.patterns||[]];e.exclude?.length&&(i=i.filter(t=>!e.exclude.some(p=>t.source===p.source)));let n=e.log??true,r=e.status??410,a=e.store,u=e.strikeThreshold??3,g=e.getIP??k,l=e.onBlocked;return createMiddleware(async(t,p)=>{let s=g(t),m=s&&s!=="unknown";if(a&&m&&await a.isBanned(s)){let o=t.req.path.replace(/\/+/g,"/");return l?l({ip:s,path:o,method:t.req.method,reason:"banned"}):n&&console.log(`\u{1F6AB} Banned [${s}] ${t.req.method} ${o}`),t.body(null,r)}let c=t.req.path.replace(/\/+/g,"/");if(i.some(o=>o.test(c))){let o,d=false;if(a&&m&&(o=await a.addStrike(s),o>=u&&(await a.ban(s),await a.resetStrikes(s),d=true)),l)l({ip:s,path:c,method:t.req.method,reason:"pattern",strikes:o,banned:d});else if(n){let h=d?" \u{1F6AB} BANNED":"";console.log(`\u{1F36F} Blocked [${s}] ${t.req.method} ${c}${h}`);}return t.body(null,r)}return p()})};/**
1
+ import {createMiddleware}from'hono/factory';var $=class{strikes=new Map;bans=new Map;strikeTTL;banTTL;constructor(i){this.strikeTTL=(i?.strikeTTL??3600)*1e3,this.banTTL=(i?.banTTL??86400)*1e3;}isBanned(i){let n=this.bans.get(i);return n===void 0?false:Date.now()>n?(this.bans.delete(i),false):true}addStrike(i){let n=Date.now(),r=this.strikes.get(i);return r&&n<r.expires?(r.count++,r.count):(this.strikes.set(i,{count:1,expires:n+this.strikeTTL}),1)}ban(i){this.bans.set(i,Date.now()+this.banTTL);}resetStrikes(i){this.strikes.delete(i);}},b=[/\.php/i,/\/config\.php/i,/\/phpinfo/i,/\/eval-stdin\.php/i,/\/xmlrpc\.php/i,/\/ALFA_DATA/i,/\/c99\.php/i,/\/r57\.php/i,/\/shell\.php/i,/\/webshell/i,/^\/wp$/i,/^\/wp-/i,/^\/wordpress/i,/\/wp-includes\//i,/\/wp-content\//i,/\/wp-admin/i,/wlwmanifest\.xml$/i,/^\/uploads?$/i,/^\/images$/i,/^\/assets$/i,/^\/files$/i,/^\/media$/i,/^\/public$/i,/\/admin\/(uploads?|images|editor|fckeditor|controller)/i,/^\/modules$/i,/^\/plugins$/i,/^\/components$/i,/^\/system$/i,/^\/template$/i,/^\/includes?$/i,/^\/vendor$/i,/^\/local$/i,/^\/php$/i,/\/fckeditor\/editor\/filemanager/i,/\/sites\/default\/files/i,/\/images\/stories/i,/\/modules\/mod_simplefileupload/i,/\/controller\/extension/i,/\/ckeditor/i,/\/tinymce/i,/\/elfinder/i,/^\/admin(\.php)?$/i,/^\/administrator/i,/^\/phpmyadmin/i,/^\/cpanel/i,/^\/whm/i,/^\/cgi-bin/i,/^\/typo3/i,/^\/joomla/i,/^\/drupal/i,/^\/magento/i,/^\/rest\/(?:[a-z0-9_-]+\/)?v\d+(?:\/|$)/i,/\/\.git/i,/\/\.svn/i,/\/\.hg/i,/\/\.env/i,/\/\.sql$/i,/\/(vendor|node_modules)\//i,/\/\.htaccess$/i,/\/\.htpasswd$/i,/\/\.DS_Store$/i,/\/Thumbs\.db$/i,/\/\.ssh/i,/\/id_rsa/i,/\/id_ed25519/i,/\/\.npmrc$/i,/\/\.pypirc$/i,/\/\.aws\//i,/\.(bak|old|backup|orig|save|swp)$/i,/\.(7z|tgz|tar\.gz|tar|bz2|war|jar)$/i,/^\/config\.(js|json|yml|yaml|xml|ini|conf)$/i,/^\/settings\.(js|json|yml|yaml|xml)$/i,/^\/credentials\.(js|json|yml|yaml)$/i,/^\/secrets\.(js|json|yml|yaml|env)$/i,/^\/appsettings\.(json|yml|yaml)$/i,/^\/application\.(yml|yaml|xml|properties)$/i,/sftp-config\.json$/i,/ftpsync\.settings$/i,/\.ftpconfig$/i,/\.ftppass$/i,/\.remote-sync\.json$/i,/ftp-deploy\.json$/i,/^\/env\.js$/i,/^\/main\.js$/i,/^\/index\.js$/i,/^\/app\.js$/i,/^\/server-(status|info)$/i,/^\/info$/i,/^\/swagger/i,/^\/api\/swagger\.(json|yml|yaml)$/i,/^\/api-docs/i,/^\/v\d+\/api-docs/i,/^\/_env/i,/^\/env$/i,/^\/config\//i,/^\/backup/i,/^\/bk$/i,/^\/bak$/i,/^\/bac$/i,/^\/dump/i,/^\/db_/i,/^\/sql/i,/^\/shell/i,/^\/old$/i,/^\/new$/i,/^\/test$/i,/^\/demo$/i,/^\/www$/i,/^\/main$/i,/^\/site$/i,/^\/shop$/i,/^\/bc$/i,/^\/sitio$/i,/^\/sito$/i,/^\/oldsite$/i,/^\/old-site$/i,/^\/script$/i,/^\/\d{4}$/i,/^\/getcmd$/i,/\$\(/,/`/,/"/,/\{(curl|wget|bash|sh|nc|ncat|python|perl|ruby|php),/i,/\.oast\.(site|fun|live|me|online|pro)/i,/(%00|\x00)/,/[\u2000-\u203F\uFEFF]/u,/^\/storfs-asup$/i,/^\/_next/i,/^\/_rsc/i,/^\/__rsc/i,/^\/_vercel/i,/next\.config\.(js|mjs|ts)$/i,/nuxt\.config\.(js|ts)$/i,/craco\.config\.js$/i,/serverless\.(yml|yaml|json)$/i,/vercel\.json$/i,/netlify\.toml$/i,/\/helm\//i,/\/package\.json$/i,/\/composer\.(json|lock)$/i,/\/Gemfile(\.lock)?$/i,/\/requirements\.txt$/i,/docker-compose\.(yml|yaml)$/i,/Dockerfile$/i,/\/docker\//i,/^\/aws/i,/\/aws[_-]s3/i,/\/aws[_-]ses/i,/\.\.\//,/\.\.%2f/i,/\.\.%5c/i,/^\/etc\//i,/^\/proc\//i,/^\/var\//i,/^\/opt\//i,/\/passwd$/i,/\.log$/i,/\/error_log$/i,/^\/@fs\//i,/^\/@vite\//i,/^\/@id\//i,/^\/_ignition/i,/^\/__debug__/i,/\/WEB-INF/i,/^\/manager\/html/i,/^\/solr/i,/^\/actuator/i,/\/elmah\.axd$/i,/^\/servlet\//i,/bsh\.servlet/i,/^\/struts\//i,/^\/invoker\//i,/\.action$/i,/\/mailcow/i,/^\/roundcube\//i,/^\/webmail\//i,/^\/adminer/i,/^\/pma\//i,/^\/myadmin\//i,/^\/mysqladmin/i,/^\/dbadmin/i,/^\/ip$/i,/^\/proxy\.pac$/i,/^\/proxy\//i,/169\.254\.169\.254/,/^\/latest\/meta-data/i,/^\/HNAP1\//i,/^\/boaform\//i,/^\/GponForm\//i,/\.cgi$/i,/\.htm$/i,/^\/sdk$/i,/^\/websso\//i,/^\/owa\//i,/^\/aspnet_client\//i,/^\/autodiscover\//i,/^\/ecp\//i,/^\/_layouts\//i,/^\/_vti_bin\//i,/^\/WebInterface\//i,/^\/owncloud\//i,/^\/nextcloud\//i,/^\/geoserver\//i,/^\/geowebcache\//i,/^\/confluence\//i,/^\/jira\//i,/^\/grafana\//i,/^\/kibana\//i,/^\/prometheus\//i,/^\/jenkins\//i,/\/j_acegi_security_check/i,/^\/portainer\//i,/^\/gitea\//i,/^\/gitlab\//i,/^\/metrics$/i,/^\/healthz$/i,/^\/readyz$/i,/^\/livez$/i,/^\/console\//i,/^\/debug\//i,/^\/\.dockerenv$/i];function k(e){return e.req.header("cf-connecting-ip")||e.req.header("x-forwarded-for")?.split(",")[0]?.trim()||e.req.header("x-real-ip")||"unknown"}var v=(e={})=>{let i=[...b,...e.patterns||[]];e.exclude?.length&&(i=i.filter(t=>!e.exclude.some(p=>t.source===p.source)));let n=e.log??true,r=e.status??410,a=e.store,u=e.strikeThreshold??3,g=e.getIP??k,l=e.onBlocked;return createMiddleware(async(t,p)=>{let s=g(t),m=s&&s!=="unknown";if(a&&m&&await a.isBanned(s)){let o=t.req.path.replace(/\/+/g,"/");return l?l({ip:s,path:o,method:t.req.method,reason:"banned"}):n&&console.log(`\u{1F6AB} Banned [${s}] ${t.req.method} ${o}`),t.body(null,r)}let d=t.req.path.replace(/\/+/g,"/");if(i.some(o=>o.test(d))){let o,c=false;if(a&&m&&(o=await a.addStrike(s),o>=u&&(await a.ban(s),await a.resetStrikes(s),c=true)),l)l({ip:s,path:d,method:t.req.method,reason:"pattern",strikes:o,banned:c});else if(n){let h=c?" \u{1F6AB} BANNED":"";console.log(`\u{1F36F} Blocked [${s}] ${t.req.method} ${d}${h}`);}return t.body(null,r)}return p()})};/**
2
2
  * hono-honeypot - Zero-dependency security middleware for Hono.js
3
3
  * Blocks bot attacks, vulnerability scanners, and brute-force attempts.
4
4
  * Optional store-backed IP banning (3 strikes = 24hr ban by default).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hono-honeypot",
3
- "version": "1.2.2",
3
+ "version": "1.3.0",
4
4
  "description": "Zero-dependency honeypot middleware for Hono.js that blocks bot attacks and vulnerability scanners",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -26,7 +26,7 @@
26
26
  ],
27
27
  "scripts": {
28
28
  "build": "tsup",
29
- "test": "vitest run",
29
+ "test": "bun test",
30
30
  "prepublishOnly": "bun run build && bun test"
31
31
  },
32
32
  "keywords": [
@@ -55,11 +55,10 @@
55
55
  "hono": "^4.0.0"
56
56
  },
57
57
  "devDependencies": {
58
- "@types/bun": "^1.3.5",
59
- "hono": "^4.11.3",
58
+ "@types/bun": "^1.3.13",
59
+ "hono": "^4.12.15",
60
60
  "tsup": "^8.5.1",
61
- "typescript": "^5.9.3",
62
- "vitest": "^4.0.16"
61
+ "typescript": "^6.0.3"
63
62
  },
64
63
  "engines": {
65
64
  "node": ">=18"