honeypot-tester 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ghost-flood Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # honeypot-tester šŸ‘»
2
+
3
+ > An ephemeral, zero-dependency local stress-testing harness that automatically untracks and deletes itself post-evaluation.
4
+
5
+ [![NPM Version](https://img.shields.io/badge/npm-v1.0.0-blue.svg?style=flat-square)](#)
6
+ [![Bundle Size](https://img.shields.io/badge/bundle%20size-%3C%201%20KB-brightgreen.svg?style=flat-square)](#)
7
+ [![Dependency Count](https://img.shields.io/badge/dependencies-0-purple.svg?style=flat-square)](#)
8
+ [![License](https://img.shields.io/badge/license-MIT-red.svg?style=flat-square)](#)
9
+
10
+ ---
11
+
12
+ ## The Philosophy of Ephemeral Testing
13
+
14
+ In modern DevOps and security engineering, stress-testing local endpoints against automated crawlers and sub-second velocity attacks is a crucial step before public deployment. However, maintaining load-test scripts, local JSON state files, and execution tokens directly within a production codebase introduces risk and noise.
15
+
16
+ `honeypot-tester` is built on the paradigm of **ephemeral testing utilities**: it resides within your workspace temporarily, conducts high-throughput parallel execution audits, and automatically purges all operational footprint (including its own source script file) once evaluation limits are met. This keeps your workspace clean, prevents testing files from leaking into Git histories or production bundles, and guarantees that local load tests remain strictly local.
17
+
18
+ ---
19
+
20
+ ## Core Features
21
+
22
+ * šŸš€ **Sub-millisecond concurrent asynchronous request handling** for high-throughput concurrency load testing.
23
+ * 🧠 **Dual-mode traffic generation** simulating both naive decoy trap trippers and sub-second velocity scripts.
24
+ * šŸ’„ **Built-in programmatic self-destruction sequence** unlinking script sources and state logs automatically.
25
+ * šŸ“¦ **Zero runtime external dependencies** ensuring a hyper-lightweight <1KB footprint.
26
+
27
+ ---
28
+
29
+ ## Installation
30
+
31
+ Install `honeypot-tester` inside your local developer dependencies:
32
+
33
+ ```bash
34
+ # npm
35
+ npm install --save-dev honeypot-tester
36
+
37
+ # pnpm
38
+ pnpm add -D honeypot-tester
39
+
40
+ # bun
41
+ bun add -d honeypot-tester
42
+ ```
43
+
44
+ ---
45
+
46
+ ## Quick Start: How to Use It
47
+
48
+ Below is a complete TypeScript example showing how to configure `honeypot-tester` to audit a high-volume ticket checkout endpoint. The script tracks state across 3 sequential runs, simulating a premium e-commerce flash sale, and cleanly self-deletes upon completing the 3rd lifecycle execution.
49
+
50
+ ```typescript
51
+ import { fileURLToPath } from 'url';
52
+ import { HoneypotTester } from 'honeypot-tester';
53
+
54
+ const __filename = fileURLToPath(import.meta.url);
55
+
56
+ interface TicketOrderPayload {
57
+ email: string;
58
+ ticketQuantity: number;
59
+ promoCode: string;
60
+ website: string;
61
+ formLoadedAt: number;
62
+ }
63
+
64
+ const floodRunner = new HoneypotTester({
65
+ targetUrl: 'http://localhost:3000/api/checkout',
66
+ batchSize: 500,
67
+ maxRuns: 3,
68
+ mixedTraffic: true,
69
+ counterFile: '.run-counter.json',
70
+ });
71
+
72
+ async function runAudit() {
73
+ console.log('⚔ Starting e-commerce ticket checkout load simulation...');
74
+ const metrics = await floodRunner.run(__filename);
75
+ console.log(`āœ… Run [${metrics.runIndex}/${floodRunner.maxRuns}] Completed successfully.`);
76
+ }
77
+
78
+ runAudit().catch((err) => {
79
+ console.error('āŒ Stress test failed to execute gracefully:', err);
80
+ process.exit(1);
81
+ });
82
+ ```
83
+
84
+ ---
85
+
86
+ ## Configuration Engine (API Matrix)
87
+
88
+ The `HoneypotTester` class constructor accepts an options block matching the parameters below:
89
+
90
+ | Field | Type | Default | Operational Impact |
91
+ | :--- | :--- | :--- | :--- |
92
+ | `targetUrl` | `string` | `'http://localhost:3000/api/checkout'` | The target local endpoint receiving simulated bot POST payloads. |
93
+ | `batchSize` | `number` | `500` | The total number of concurrent requests fired in parallel per run. |
94
+ | `maxRuns` | `number` | `3` | The threshold run index triggering self-destruction and unlinking. |
95
+ | `mixedTraffic` | `boolean` | `true` | Toggles between honeypot triggers and speed violations. |
96
+
97
+ ---
98
+
99
+ ## The Self-Destruction Mechanism
100
+
101
+ > [!WARNING]
102
+ > When `currentRun >= maxRuns`, the orchestrator initiates a teardown loop:
103
+ > 1. It calls `fs.unlinkSync` on the local `counterFile` path (e.g. `.run-counter.json`).
104
+ > 2. If a caller filename was supplied, it executes `fs.unlinkSync` on the script's entry path (e.g. `stress-test.mjs`).
105
+ >
106
+ > Ensure you only pass the script filepath that you explicitly intend to delete from the workspace directory. Do not pass critical application files to the runner.
107
+
108
+ ---
109
+
110
+ ## Security & Local-Only Disclaimer
111
+
112
+ This utility is designed **strictly** for local profiling, development infrastructure analysis, and authorized internal staging stress testing. Firing high-concurrency request floods against third-party public applications without prior explicit authorization is illegal and violates security policies. The authors assume no liability for misuse.
113
+
114
+ ---
115
+
116
+ ## Footer
117
+
118
+ Contributions to the codebase are welcome. Check the repository issues and submit pull requests.
119
+ Released under the [MIT License](LICENSE).
@@ -0,0 +1,34 @@
1
+ interface HoneypotTesterOptions {
2
+ targetUrl?: string;
3
+ batchSize?: number;
4
+ maxRuns?: number;
5
+ mixedTraffic?: boolean;
6
+ counterFile?: string;
7
+ }
8
+ interface RequestMetrics {
9
+ status: number;
10
+ success: boolean;
11
+ latency: number;
12
+ error: string | null;
13
+ }
14
+ interface RunMetrics {
15
+ runIndex: number;
16
+ totalRequests: number;
17
+ durationMs: number;
18
+ avgLatencyMs: number;
19
+ successRate: number;
20
+ failureRate: number;
21
+ connectionErrors: number;
22
+ }
23
+ declare class HoneypotTester {
24
+ targetUrl: string;
25
+ batchSize: number;
26
+ maxRuns: number;
27
+ mixedTraffic: boolean;
28
+ counterFile: string;
29
+ constructor(options?: HoneypotTesterOptions);
30
+ run(callerFilename?: string): Promise<RunMetrics>;
31
+ private sendRequest;
32
+ }
33
+
34
+ export { HoneypotTester, type HoneypotTesterOptions, type RequestMetrics, type RunMetrics };
@@ -0,0 +1,34 @@
1
+ interface HoneypotTesterOptions {
2
+ targetUrl?: string;
3
+ batchSize?: number;
4
+ maxRuns?: number;
5
+ mixedTraffic?: boolean;
6
+ counterFile?: string;
7
+ }
8
+ interface RequestMetrics {
9
+ status: number;
10
+ success: boolean;
11
+ latency: number;
12
+ error: string | null;
13
+ }
14
+ interface RunMetrics {
15
+ runIndex: number;
16
+ totalRequests: number;
17
+ durationMs: number;
18
+ avgLatencyMs: number;
19
+ successRate: number;
20
+ failureRate: number;
21
+ connectionErrors: number;
22
+ }
23
+ declare class HoneypotTester {
24
+ targetUrl: string;
25
+ batchSize: number;
26
+ maxRuns: number;
27
+ mixedTraffic: boolean;
28
+ counterFile: string;
29
+ constructor(options?: HoneypotTesterOptions);
30
+ run(callerFilename?: string): Promise<RunMetrics>;
31
+ private sendRequest;
32
+ }
33
+
34
+ export { HoneypotTester, type HoneypotTesterOptions, type RequestMetrics, type RunMetrics };
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ 'use strict';var r=require('fs'),l=require('path');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var r__default=/*#__PURE__*/_interopDefault(r);var l__default=/*#__PURE__*/_interopDefault(l);var b=class{targetUrl;batchSize;maxRuns;mixedTraffic;counterFile;constructor(s={}){this.targetUrl=s.targetUrl??"http://localhost:3000/api/checkout",this.batchSize=s.batchSize??500,this.maxRuns=s.maxRuns??3,this.mixedTraffic=s.mixedTraffic??true,this.counterFile=s.counterFile??".run-counter.json";}async run(s){let n=l__default.default.resolve(this.counterFile),t=1;try{if(r__default.default.existsSync(n)){let e=r__default.default.readFileSync(n,"utf8");t=(JSON.parse(e).runs||0)+1;}}catch{}try{r__default.default.writeFileSync(n,JSON.stringify({runs:t},null,2),"utf8");}catch(e){console.warn(`\u26A0\uFE0F Warning: Could not write counter state file: ${e.message}`);}console.log(`
2
+ `+"=".repeat(50)),console.log(`\u25B6\uFE0F Executing Test Run [${t}/${this.maxRuns}]`),console.log("=".repeat(50)+`
3
+ `);let o=[];for(let e=0;e<this.batchSize;e++)(this.mixedTraffic?e%2===0:true)?o.push({email:`naive-bot-${e}@spam.com`,website:"http://spam-payload.bot",formLoadedAt:Date.now()-1e4}):o.push({email:`velocity-bot-${e}@spam.com`,website:"",formLoadedAt:Date.now()});let p=performance.now(),d=o.map(e=>this.sendRequest(e)),y=await Promise.all(d),u=performance.now()-p,a=0,i=0,m=0,c=0;y.forEach(e=>{m+=e.latency,e.success?a++:(i++,e.status===0&&c++);});let h=this.batchSize>0?m/this.batchSize:0,f=this.batchSize>0?a/this.batchSize*100:0,g=this.batchSize>0?i/this.batchSize*100:0;if(console.log("\u{1F4C8} Performance Metrics Dashboard:"),console.log(`- Total Requests Sent : ${this.batchSize}`),console.log(`- Execution Duration : ${u.toFixed(2)} ms`),console.log(`- Average Request Latency : ${h.toFixed(2)} ms`),console.log(`- Successful Deliveries : ${a} (${f.toFixed(1)}%)`),console.log(`- Blocked / Failed Requests: ${i} (${g.toFixed(1)}%)`),c>0&&console.log(`- Connection Drops : ${c} (Local server might be offline)`),t>=this.maxRuns){console.log(`
4
+ \u{1F4A5} Max runs reached. Cleaning workspace and executing self-deletion...`);try{r__default.default.existsSync(n)&&(r__default.default.unlinkSync(n),console.log("\u{1F5D1}\uFE0F Successfully removed state file (.run-counter.json)."));}catch(e){console.error(`\u26A0\uFE0F Error deleting state file: ${e.message}`);}if(s)try{let e=l__default.default.resolve(s);r__default.default.existsSync(e)&&(r__default.default.unlinkSync(e),console.log(`\u{1F5D1}\uFE0F Successfully removed caller script (${l__default.default.basename(e)}).`));}catch(e){console.error(`\u26A0\uFE0F Error deleting caller script file: ${e.message}`);}console.log(`\u{1F3C1} Workspace cleaned. Exiting.
5
+ `);}else console.log(`
6
+ \u{1F4A1} To execute the next run, trigger the script again (Run ${t+1}/${this.maxRuns})
7
+ `);return {runIndex:t,totalRequests:this.batchSize,durationMs:u,avgLatencyMs:h,successRate:f,failureRate:g,connectionErrors:c}}async sendRequest(s){let n=performance.now();try{let t=await fetch(this.targetUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)}),o=performance.now()-n;return {status:t.status,success:t.ok,latency:o,error:null}}catch(t){return {status:0,success:false,latency:performance.now()-n,error:t.message}}}};exports.HoneypotTester=b;//# sourceMappingURL=index.js.map
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":["HoneypotTester","options","callerFilename","counterPath","path","currentRun","fs","raw","err","payloads","i","startTime","requests","payload","results","durationMs","successfulRequests","failedRequests","totalLatency","connectionErrors","res","avgLatencyMs","successRate","failureRate","resolvedCaller","start","response","latency"],"mappings":"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","file":"index.js","sourcesContent":["import fs from 'fs';\nimport path from 'path';\n\nexport interface HoneypotTesterOptions {\n targetUrl?: string;\n batchSize?: number;\n maxRuns?: number;\n mixedTraffic?: boolean;\n counterFile?: string;\n}\n\nexport interface RequestMetrics {\n status: number;\n success: boolean;\n latency: number;\n error: string | null;\n}\n\nexport interface RunMetrics {\n runIndex: number;\n totalRequests: number;\n durationMs: number;\n avgLatencyMs: number;\n successRate: number;\n failureRate: number;\n connectionErrors: number;\n}\n\nexport class HoneypotTester {\n public targetUrl: string;\n public batchSize: number;\n public maxRuns: number;\n public mixedTraffic: boolean;\n public counterFile: string;\n\n constructor(options: HoneypotTesterOptions = {}) {\n this.targetUrl = options.targetUrl ?? 'http://localhost:3000/api/checkout';\n this.batchSize = options.batchSize ?? 500;\n this.maxRuns = options.maxRuns ?? 3;\n this.mixedTraffic = options.mixedTraffic ?? true;\n this.counterFile = options.counterFile ?? '.run-counter.json';\n }\n\n public async run(callerFilename?: string): Promise<RunMetrics> {\n const counterPath = path.resolve(this.counterFile);\n let currentRun = 1;\n\n try {\n if (fs.existsSync(counterPath)) {\n const raw = fs.readFileSync(counterPath, 'utf8');\n const parsed = JSON.parse(raw);\n currentRun = (parsed.runs || 0) + 1;\n }\n } catch {}\n\n try {\n fs.writeFileSync(counterPath, JSON.stringify({ runs: currentRun }, null, 2), 'utf8');\n } catch (err: any) {\n console.warn(`āš ļø Warning: Could not write counter state file: ${err.message}`);\n }\n\n console.log('\\n' + '='.repeat(50));\n console.log(`ā–¶ļø Executing Test Run [${currentRun}/${this.maxRuns}]`);\n console.log('='.repeat(50) + '\\n');\n\n const payloads: Array<Record<string, any>> = [];\n for (let i = 0; i < this.batchSize; i++) {\n const isNaiveBot = this.mixedTraffic ? (i % 2 === 0) : true;\n if (isNaiveBot) {\n payloads.push({\n email: `naive-bot-${i}@spam.com`,\n website: 'http://spam-payload.bot',\n formLoadedAt: Date.now() - 10000,\n });\n } else {\n payloads.push({\n email: `velocity-bot-${i}@spam.com`,\n website: '',\n formLoadedAt: Date.now(),\n });\n }\n }\n\n const startTime = performance.now();\n const requests = payloads.map(payload => this.sendRequest(payload));\n const results = await Promise.all(requests);\n const durationMs = performance.now() - startTime;\n\n let successfulRequests = 0;\n let failedRequests = 0;\n let totalLatency = 0;\n let connectionErrors = 0;\n\n results.forEach(res => {\n totalLatency += res.latency;\n if (res.success) {\n successfulRequests++;\n } else {\n failedRequests++;\n if (res.status === 0) {\n connectionErrors++;\n }\n }\n });\n\n const avgLatencyMs = this.batchSize > 0 ? totalLatency / this.batchSize : 0;\n const successRate = this.batchSize > 0 ? (successfulRequests / this.batchSize) * 100 : 0;\n const failureRate = this.batchSize > 0 ? (failedRequests / this.batchSize) * 100 : 0;\n\n console.log('šŸ“ˆ Performance Metrics Dashboard:');\n console.log(`- Total Requests Sent : ${this.batchSize}`);\n console.log(`- Execution Duration : ${durationMs.toFixed(2)} ms`);\n console.log(`- Average Request Latency : ${avgLatencyMs.toFixed(2)} ms`);\n console.log(`- Successful Deliveries : ${successfulRequests} (${successRate.toFixed(1)}%)`);\n console.log(`- Blocked / Failed Requests: ${failedRequests} (${failureRate.toFixed(1)}%)`);\n if (connectionErrors > 0) {\n console.log(`- Connection Drops : ${connectionErrors} (Local server might be offline)`);\n }\n\n if (currentRun >= this.maxRuns) {\n console.log('\\nšŸ’„ Max runs reached. Cleaning workspace and executing self-deletion...');\n try {\n if (fs.existsSync(counterPath)) {\n fs.unlinkSync(counterPath);\n console.log('šŸ—‘ļø Successfully removed state file (.run-counter.json).');\n }\n } catch (err: any) {\n console.error(`āš ļø Error deleting state file: ${err.message}`);\n }\n\n if (callerFilename) {\n try {\n const resolvedCaller = path.resolve(callerFilename);\n if (fs.existsSync(resolvedCaller)) {\n fs.unlinkSync(resolvedCaller);\n console.log(`šŸ—‘ļø Successfully removed caller script (${path.basename(resolvedCaller)}).`);\n }\n } catch (err: any) {\n console.error(`āš ļø Error deleting caller script file: ${err.message}`);\n }\n }\n console.log('šŸ Workspace cleaned. Exiting.\\n');\n } else {\n console.log(`\\nšŸ’” To execute the next run, trigger the script again (Run ${currentRun + 1}/${this.maxRuns})\\n`);\n }\n\n return {\n runIndex: currentRun,\n totalRequests: this.batchSize,\n durationMs,\n avgLatencyMs,\n successRate,\n failureRate,\n connectionErrors,\n };\n }\n\n private async sendRequest(payload: Record<string, any>): Promise<RequestMetrics> {\n const start = performance.now();\n try {\n const response = await fetch(this.targetUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(payload),\n });\n const latency = performance.now() - start;\n return {\n status: response.status,\n success: response.ok,\n latency,\n error: null,\n };\n } catch (err: any) {\n const latency = performance.now() - start;\n return {\n status: 0,\n success: false,\n latency,\n error: err.message,\n };\n }\n }\n}\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,8 @@
1
+ import r from'fs';import l from'path';var b=class{targetUrl;batchSize;maxRuns;mixedTraffic;counterFile;constructor(s={}){this.targetUrl=s.targetUrl??"http://localhost:3000/api/checkout",this.batchSize=s.batchSize??500,this.maxRuns=s.maxRuns??3,this.mixedTraffic=s.mixedTraffic??true,this.counterFile=s.counterFile??".run-counter.json";}async run(s){let n=l.resolve(this.counterFile),t=1;try{if(r.existsSync(n)){let e=r.readFileSync(n,"utf8");t=(JSON.parse(e).runs||0)+1;}}catch{}try{r.writeFileSync(n,JSON.stringify({runs:t},null,2),"utf8");}catch(e){console.warn(`\u26A0\uFE0F Warning: Could not write counter state file: ${e.message}`);}console.log(`
2
+ `+"=".repeat(50)),console.log(`\u25B6\uFE0F Executing Test Run [${t}/${this.maxRuns}]`),console.log("=".repeat(50)+`
3
+ `);let o=[];for(let e=0;e<this.batchSize;e++)(this.mixedTraffic?e%2===0:true)?o.push({email:`naive-bot-${e}@spam.com`,website:"http://spam-payload.bot",formLoadedAt:Date.now()-1e4}):o.push({email:`velocity-bot-${e}@spam.com`,website:"",formLoadedAt:Date.now()});let p=performance.now(),d=o.map(e=>this.sendRequest(e)),y=await Promise.all(d),u=performance.now()-p,a=0,i=0,m=0,c=0;y.forEach(e=>{m+=e.latency,e.success?a++:(i++,e.status===0&&c++);});let h=this.batchSize>0?m/this.batchSize:0,f=this.batchSize>0?a/this.batchSize*100:0,g=this.batchSize>0?i/this.batchSize*100:0;if(console.log("\u{1F4C8} Performance Metrics Dashboard:"),console.log(`- Total Requests Sent : ${this.batchSize}`),console.log(`- Execution Duration : ${u.toFixed(2)} ms`),console.log(`- Average Request Latency : ${h.toFixed(2)} ms`),console.log(`- Successful Deliveries : ${a} (${f.toFixed(1)}%)`),console.log(`- Blocked / Failed Requests: ${i} (${g.toFixed(1)}%)`),c>0&&console.log(`- Connection Drops : ${c} (Local server might be offline)`),t>=this.maxRuns){console.log(`
4
+ \u{1F4A5} Max runs reached. Cleaning workspace and executing self-deletion...`);try{r.existsSync(n)&&(r.unlinkSync(n),console.log("\u{1F5D1}\uFE0F Successfully removed state file (.run-counter.json)."));}catch(e){console.error(`\u26A0\uFE0F Error deleting state file: ${e.message}`);}if(s)try{let e=l.resolve(s);r.existsSync(e)&&(r.unlinkSync(e),console.log(`\u{1F5D1}\uFE0F Successfully removed caller script (${l.basename(e)}).`));}catch(e){console.error(`\u26A0\uFE0F Error deleting caller script file: ${e.message}`);}console.log(`\u{1F3C1} Workspace cleaned. Exiting.
5
+ `);}else console.log(`
6
+ \u{1F4A1} To execute the next run, trigger the script again (Run ${t+1}/${this.maxRuns})
7
+ `);return {runIndex:t,totalRequests:this.batchSize,durationMs:u,avgLatencyMs:h,successRate:f,failureRate:g,connectionErrors:c}}async sendRequest(s){let n=performance.now();try{let t=await fetch(this.targetUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)}),o=performance.now()-n;return {status:t.status,success:t.ok,latency:o,error:null}}catch(t){return {status:0,success:false,latency:performance.now()-n,error:t.message}}}};export{b as HoneypotTester};//# sourceMappingURL=index.mjs.map
8
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":["HoneypotTester","options","callerFilename","counterPath","path","currentRun","fs","raw","err","payloads","i","startTime","requests","payload","results","durationMs","successfulRequests","failedRequests","totalLatency","connectionErrors","res","avgLatencyMs","successRate","failureRate","resolvedCaller","start","response","latency"],"mappings":"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","file":"index.mjs","sourcesContent":["import fs from 'fs';\nimport path from 'path';\n\nexport interface HoneypotTesterOptions {\n targetUrl?: string;\n batchSize?: number;\n maxRuns?: number;\n mixedTraffic?: boolean;\n counterFile?: string;\n}\n\nexport interface RequestMetrics {\n status: number;\n success: boolean;\n latency: number;\n error: string | null;\n}\n\nexport interface RunMetrics {\n runIndex: number;\n totalRequests: number;\n durationMs: number;\n avgLatencyMs: number;\n successRate: number;\n failureRate: number;\n connectionErrors: number;\n}\n\nexport class HoneypotTester {\n public targetUrl: string;\n public batchSize: number;\n public maxRuns: number;\n public mixedTraffic: boolean;\n public counterFile: string;\n\n constructor(options: HoneypotTesterOptions = {}) {\n this.targetUrl = options.targetUrl ?? 'http://localhost:3000/api/checkout';\n this.batchSize = options.batchSize ?? 500;\n this.maxRuns = options.maxRuns ?? 3;\n this.mixedTraffic = options.mixedTraffic ?? true;\n this.counterFile = options.counterFile ?? '.run-counter.json';\n }\n\n public async run(callerFilename?: string): Promise<RunMetrics> {\n const counterPath = path.resolve(this.counterFile);\n let currentRun = 1;\n\n try {\n if (fs.existsSync(counterPath)) {\n const raw = fs.readFileSync(counterPath, 'utf8');\n const parsed = JSON.parse(raw);\n currentRun = (parsed.runs || 0) + 1;\n }\n } catch {}\n\n try {\n fs.writeFileSync(counterPath, JSON.stringify({ runs: currentRun }, null, 2), 'utf8');\n } catch (err: any) {\n console.warn(`āš ļø Warning: Could not write counter state file: ${err.message}`);\n }\n\n console.log('\\n' + '='.repeat(50));\n console.log(`ā–¶ļø Executing Test Run [${currentRun}/${this.maxRuns}]`);\n console.log('='.repeat(50) + '\\n');\n\n const payloads: Array<Record<string, any>> = [];\n for (let i = 0; i < this.batchSize; i++) {\n const isNaiveBot = this.mixedTraffic ? (i % 2 === 0) : true;\n if (isNaiveBot) {\n payloads.push({\n email: `naive-bot-${i}@spam.com`,\n website: 'http://spam-payload.bot',\n formLoadedAt: Date.now() - 10000,\n });\n } else {\n payloads.push({\n email: `velocity-bot-${i}@spam.com`,\n website: '',\n formLoadedAt: Date.now(),\n });\n }\n }\n\n const startTime = performance.now();\n const requests = payloads.map(payload => this.sendRequest(payload));\n const results = await Promise.all(requests);\n const durationMs = performance.now() - startTime;\n\n let successfulRequests = 0;\n let failedRequests = 0;\n let totalLatency = 0;\n let connectionErrors = 0;\n\n results.forEach(res => {\n totalLatency += res.latency;\n if (res.success) {\n successfulRequests++;\n } else {\n failedRequests++;\n if (res.status === 0) {\n connectionErrors++;\n }\n }\n });\n\n const avgLatencyMs = this.batchSize > 0 ? totalLatency / this.batchSize : 0;\n const successRate = this.batchSize > 0 ? (successfulRequests / this.batchSize) * 100 : 0;\n const failureRate = this.batchSize > 0 ? (failedRequests / this.batchSize) * 100 : 0;\n\n console.log('šŸ“ˆ Performance Metrics Dashboard:');\n console.log(`- Total Requests Sent : ${this.batchSize}`);\n console.log(`- Execution Duration : ${durationMs.toFixed(2)} ms`);\n console.log(`- Average Request Latency : ${avgLatencyMs.toFixed(2)} ms`);\n console.log(`- Successful Deliveries : ${successfulRequests} (${successRate.toFixed(1)}%)`);\n console.log(`- Blocked / Failed Requests: ${failedRequests} (${failureRate.toFixed(1)}%)`);\n if (connectionErrors > 0) {\n console.log(`- Connection Drops : ${connectionErrors} (Local server might be offline)`);\n }\n\n if (currentRun >= this.maxRuns) {\n console.log('\\nšŸ’„ Max runs reached. Cleaning workspace and executing self-deletion...');\n try {\n if (fs.existsSync(counterPath)) {\n fs.unlinkSync(counterPath);\n console.log('šŸ—‘ļø Successfully removed state file (.run-counter.json).');\n }\n } catch (err: any) {\n console.error(`āš ļø Error deleting state file: ${err.message}`);\n }\n\n if (callerFilename) {\n try {\n const resolvedCaller = path.resolve(callerFilename);\n if (fs.existsSync(resolvedCaller)) {\n fs.unlinkSync(resolvedCaller);\n console.log(`šŸ—‘ļø Successfully removed caller script (${path.basename(resolvedCaller)}).`);\n }\n } catch (err: any) {\n console.error(`āš ļø Error deleting caller script file: ${err.message}`);\n }\n }\n console.log('šŸ Workspace cleaned. Exiting.\\n');\n } else {\n console.log(`\\nšŸ’” To execute the next run, trigger the script again (Run ${currentRun + 1}/${this.maxRuns})\\n`);\n }\n\n return {\n runIndex: currentRun,\n totalRequests: this.batchSize,\n durationMs,\n avgLatencyMs,\n successRate,\n failureRate,\n connectionErrors,\n };\n }\n\n private async sendRequest(payload: Record<string, any>): Promise<RequestMetrics> {\n const start = performance.now();\n try {\n const response = await fetch(this.targetUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(payload),\n });\n const latency = performance.now() - start;\n return {\n status: response.status,\n success: response.ok,\n latency,\n error: null,\n };\n } catch (err: any) {\n const latency = performance.now() - start;\n return {\n status: 0,\n success: false,\n latency,\n error: err.message,\n };\n }\n }\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "honeypot-tester",
3
+ "version": "1.0.0",
4
+ "description": "A high-performance, ephemeral local bot-traffic simulation and stress-testing harness.",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js",
13
+ "default": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup"
21
+ },
22
+ "keywords": [
23
+ "form-spam",
24
+ "antispam",
25
+ "honeypot",
26
+ "bot-defense",
27
+ "client-security",
28
+ "honeypot-fields",
29
+ "stress-test",
30
+ "load-testing",
31
+ "bot-simulation"
32
+ ],
33
+ "author": "",
34
+ "license": "MIT",
35
+ "devDependencies": {
36
+ "@types/node": "^25.9.3",
37
+ "tsup": "^8.0.2",
38
+ "typescript": "^5.4.5"
39
+ }
40
+ }