@chiranthmoger/fortifyjs 1.1.0 → 1.1.1
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 +231 -100
- package/bin/banner.js +29 -0
- package/bin/fortifyjs.js +12 -12
- package/index.d.ts +103 -3
- package/package.json +3 -2
- package/src/adapters/express.js +36 -21
- package/src/core/engine.js +63 -1
- package/src/core/normalizer.js +2 -2
- package/src/core/sinks.js +214 -0
- package/src/detectors/cmdi.js +7 -2
- package/src/detectors/open-redirect.js +10 -0
- package/src/detectors/path-traversal.js +17 -7
- package/src/detectors/prompt-injection.js +110 -0
- package/src/detectors/prototype-pollution.js +12 -2
- package/src/detectors/sqli.js +10 -0
- package/src/detectors/ssrf.js +202 -13
- package/src/detectors/template-injection.js +2 -2
- package/src/detectors/xss.js +22 -17
- package/src/index.js +34 -2
- package/src/presets.js +19 -1
- package/src/shields/csrf.js +10 -4
- package/src/shields/file-upload.js +5 -0
- package/src/shields/llm-guard.js +235 -0
- package/src/shields/rate-limiter.js +53 -11
- package/src/shields/sanitizer.js +113 -0
- package/src/shields/store.js +97 -0
- package/src/detectors/sqli.js.bak +0 -446
package/README.md
CHANGED
|
@@ -1,58 +1,80 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
#
|
|
3
|
+
# FortifyJS
|
|
4
4
|
|
|
5
|
-
**The Zero-Dependency Web Application Firewall for Node.js**
|
|
5
|
+
**The Zero-Dependency Web Application Firewall and AI Security Suite for Node.js**
|
|
6
6
|
|
|
7
|
-
[](https://www.npmjs.com/package/fortifyjs)
|
|
7
|
+
[](https://www.npmjs.com/package/@chiranthmoger/fortifyjs)
|
|
8
8
|
[](https://opensource.org/licenses/MIT)
|
|
9
9
|
[](https://www.typescriptlang.org/)
|
|
10
|
-
[](https://www.npmjs.com/package/@chiranthmoger/fortifyjs)
|
|
11
11
|
|
|
12
|
-
*One-line protection against injection, XSS, CSRF, SSRF, and
|
|
13
|
-
*Replaces
|
|
12
|
+
*One-line protection against injection, XSS, CSRF, SSRF, prompt injection, and 15 attack classes.*<br>
|
|
13
|
+
*Replaces helmet, cors, csurf, express-rate-limit, and legacy sanitizers with zero external dependencies.*
|
|
14
14
|
|
|
15
15
|
</div>
|
|
16
16
|
|
|
17
17
|
<hr>
|
|
18
18
|
|
|
19
|
-
##
|
|
19
|
+
## Why FortifyJS?
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
Securing modern Node.js and TypeScript backends traditionally required installing half a dozen separate packages, managing conflicting configurations, and writing custom defenses for modern threats like AI prompt injection and SSRF.
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
FortifyJS provides a single, high-performance, zero-dependency security engine with sub-millisecond execution, comprehensive attack vector detection, runtime sink guardrails, and built-in AI/LLM prompt protection.
|
|
24
24
|
|
|
25
|
-
###
|
|
25
|
+
### Comprehensive Solution Comparison
|
|
26
26
|
|
|
27
|
-
|
|
|
28
|
-
| :--- | :--- |
|
|
29
|
-
|
|
|
30
|
-
|
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
|
27
|
+
| Feature / Dimension | FortifyJS | Legacy Node Packages | Cloud WAF (Cloudflare/AWS) | AI-Only Guardrails |
|
|
28
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
29
|
+
| **External Dependencies** | **0 (Zero)** | 6 to 10 packages | N/A (Cloud proxy) | Heavy (Python/Model weights) |
|
|
30
|
+
| **Inspection Latency** | **< 0.07 ms (Fast-path < 0.005 ms)** | 2 to 8 ms (Combined) | 15 to 50 ms (Network hop) | 100 to 500 ms (LLM inference) |
|
|
31
|
+
| **AI Prompt Injection Guard** | **Built-in (`llmGuard`)** | None | Limited / Cost add-on | Yes |
|
|
32
|
+
| **Traditional WAF (15 Vectors)**| **Built-in** | Fragmented | Yes | None |
|
|
33
|
+
| **Runtime Sink Guardrails** | **Built-in (`assertSafe*`)** | None | None (Edge proxy blind to sinks) | None |
|
|
34
|
+
| **SSRF Bitwise CIDR Validation** | **Built-in (RFC subnets & metadata)**| None (Requires custom DNS code) | Partial | None |
|
|
35
|
+
| **Mass-Assignment Sanitizer** | **Built-in (`sanitizeObject`)** | Fragmented | None | None |
|
|
36
|
+
| **Framework Portability** | **Express, Fastify, Next.js, Hono, Koa, NestJS** | Express only | Protocol level | API level |
|
|
37
|
+
| **Cloud & Vendor Lock-In** | **None (Runs in-process)** | None | Vendor locked | Model locked |
|
|
38
|
+
|
|
39
|
+
### Legacy Package Replacement Matrix
|
|
40
|
+
|
|
41
|
+
| Legacy Package | FortifyJS Feature | Performance Advantage |
|
|
42
|
+
| :--- | :--- | :--- |
|
|
43
|
+
| `helmet` | Security Headers Shield | Zero external dependencies, uniform configuration |
|
|
44
|
+
| `cors` | CORS Shield | Dynamic origins, regex matching, credential isolation |
|
|
45
|
+
| `csurf` | CSRF Protection Shield | Double-submit cookie pattern with secure timing comparisons |
|
|
46
|
+
| `express-rate-limit` | Distributed / Memory Rate Limiting | In-process bounded `MemoryStore` with pluggable distributed backends |
|
|
47
|
+
| `express-mongo-sanitize` | NoSQLi Detector and Sink Guard | Deep object tree inspection covering MongoDB, CouchDB, Elasticsearch |
|
|
48
|
+
| `xss-clean` | Multi-Vector XSS Detector | Context-aware HTML attribute, SVG, and DOM execution sink inspection |
|
|
49
|
+
| Custom LLM regexes | AI Prompt Guard (`llmGuard`) | Multi-lingual instruction override, DAN persona, delimiter hijack defense |
|
|
35
50
|
|
|
36
51
|
---
|
|
37
52
|
|
|
38
|
-
##
|
|
53
|
+
## Quick Start
|
|
39
54
|
|
|
40
55
|
```bash
|
|
41
|
-
npm install fortifyjs
|
|
56
|
+
npm install @chiranthmoger/fortifyjs
|
|
42
57
|
```
|
|
43
58
|
|
|
44
|
-
###
|
|
59
|
+
### Express (One-Line Setup)
|
|
60
|
+
|
|
45
61
|
```javascript
|
|
46
62
|
const express = require('express');
|
|
47
63
|
const { shield } = require('fortifyjs');
|
|
48
64
|
|
|
49
65
|
const app = express();
|
|
50
|
-
app.use(
|
|
66
|
+
app.use(express.json());
|
|
67
|
+
app.use(shield('medium')); // All shields active: WAF + Headers + Rate Limit + CORS + CSRF
|
|
68
|
+
|
|
69
|
+
app.get('/api/data', (req, res) => {
|
|
70
|
+
res.json({ status: 'secure' });
|
|
71
|
+
});
|
|
51
72
|
|
|
52
|
-
app.listen(3000
|
|
73
|
+
app.listen(3000);
|
|
53
74
|
```
|
|
54
75
|
|
|
55
|
-
###
|
|
76
|
+
### Fastify
|
|
77
|
+
|
|
56
78
|
```javascript
|
|
57
79
|
const fastify = require('fastify')();
|
|
58
80
|
const { fastifyPlugin } = require('fortifyjs/adapters/fastify');
|
|
@@ -61,126 +83,235 @@ fastify.register(fastifyPlugin, { tier: 'medium' });
|
|
|
61
83
|
fastify.listen({ port: 3000 });
|
|
62
84
|
```
|
|
63
85
|
|
|
64
|
-
|
|
86
|
+
### Next.js (App Router / Edge Middleware)
|
|
65
87
|
|
|
66
|
-
|
|
88
|
+
```javascript
|
|
89
|
+
import nextjsAdapter from 'fortifyjs/adapters/nextjs';
|
|
90
|
+
|
|
91
|
+
export async function middleware(request) {
|
|
92
|
+
const verdict = await nextjsAdapter(request, { tier: 'medium' });
|
|
93
|
+
if (!verdict.safe) {
|
|
94
|
+
return new Response(JSON.stringify({ error: verdict.error }), {
|
|
95
|
+
status: 403,
|
|
96
|
+
headers: { 'Content-Type': 'application/json' }
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
67
101
|
|
|
68
|
-
|
|
102
|
+
---
|
|
69
103
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
|
75
|
-
|
|
|
104
|
+
## 15-Vector Attack Arsenal Matrix
|
|
105
|
+
|
|
106
|
+
FortifyJS actively inspects incoming requests, query strings, headers, and bodies across 15 attack classes:
|
|
107
|
+
|
|
108
|
+
| Vector | Attack Class | Target Description | Detection Mechanism | Protection Layer |
|
|
109
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
110
|
+
| **SQLi** | SQL Injection | Tautologies, UNION dumps, stacked queries, OOB | Structural token parser & AST boolean abuse | WAF & `assertSafeSqlQuery` |
|
|
111
|
+
| **XSS** | Cross-Site Scripting | Stored, reflected, DOM sinks, SVG event handlers | HTML attribute context & JS protocol sinks | WAF & Normalizer |
|
|
112
|
+
| **NoSQLi**| NoSQL Injection | MongoDB `$where`, `$ne`, `$gt`, `$regex` bypasses | Query operator heuristics & object tree walk | WAF & `assertSafeNoSql` |
|
|
113
|
+
| **CmdI** | Command Injection | Shell metacharacters (`\|`, `;`, `&`), PowerShell, subshells | Command chain heuristics & binary denylist | WAF & `assertSafeCommand` |
|
|
114
|
+
| **Path** | Path Traversal | Directory escapes (`../`), overlong UTF-8, null bytes | Lexical containment & prefix verification | WAF & `assertSafePath` |
|
|
115
|
+
| **SSRF** | Server-Side Forgery | Cloud metadata (`169.254`), decimal/octal/IPv6 loopbacks | Bitwise CIDR validation & DNS rebinding check | WAF & `assertSafeUrl` |
|
|
116
|
+
| **AI/LLM**| Prompt Injection | DAN 12.0 jailbreaks, system prompt exfiltration | Multi-lingual instruction override classifier | `llmGuard` & `scanPrompt` |
|
|
117
|
+
| **XXE** | XML Entity Injection | DTD entities, external system entities, billion laughs | XML entity scanner & schema enforcement | WAF Parser |
|
|
118
|
+
| **Proto**| Prototype Pollution | `__proto__`, `constructor.prototype` key pollution | Object key scanning & assignment validation | WAF & `sanitizeObject` |
|
|
119
|
+
| **HPP** | Parameter Pollution | Split parameter arrays to bypass validation rules | Array normalization & query deduplication | Adapter Engine |
|
|
120
|
+
| **Redir**| Open Redirect | Protocol-relative URLs (`//evil.com`), domain fakes | Domain whitelist & absolute URL parser | `assertSafeRedirect` |
|
|
121
|
+
| **CRLF** | Response Splitting | `\r\n` carriage return injections into HTTP headers | Header delimiter scanner | Security Headers Shield |
|
|
122
|
+
| **SSTI** | Template Injection | Jinja2, Twig, EJS, Pug expressions (`{{...}}`, `${...}`) | Template tag & code execution heuristics | WAF Engine |
|
|
123
|
+
| **LDAP** | LDAP Injection | Filter bypasses and wildcard directory harvesting | LDAP filter expression tokenizer | WAF Engine |
|
|
124
|
+
| **GQL** | GraphQL Abuse | Introspection dumping, circular queries, alias batching | Query depth analyzer & complexity limits | WAF Engine |
|
|
76
125
|
|
|
77
126
|
---
|
|
78
127
|
|
|
79
|
-
##
|
|
80
|
-
|
|
81
|
-
Under the hood, FortifyJS acts as a complete Web Application Firewall, actively analyzing payloads against 14 distinct attack vectors:
|
|
82
|
-
|
|
83
|
-
1. 💉 **SQLi**: Identifies SQL injection attempts across popular SQL dialects.
|
|
84
|
-
2. 🎭 **XSS**: Blocks cross-site scripting attacks including mutations and DOM-based vectors.
|
|
85
|
-
3. 🍃 **NoSQLi**: Detects query operator injections tailored for MongoDB, CouchDB, and Elasticsearch.
|
|
86
|
-
4. 💻 **CmdI**: Prevents operating system command injection across Unix and Windows platforms.
|
|
87
|
-
5. 📂 **Path Traversal**: Stops directory traversal attempts aiming to read arbitrary files.
|
|
88
|
-
6. 🌐 **SSRF**: Intercepts Server-Side Request Forgery attempts against internal infrastructure.
|
|
89
|
-
7. 📄 **XXE**: Prevents XML External Entity processing attacks.
|
|
90
|
-
8. 🧬 **Prototype Pollution**: Detects and stops JavaScript object prototype manipulation.
|
|
91
|
-
9. 🔀 **HPP**: Mitigates HTTP Parameter Pollution vulnerabilities.
|
|
92
|
-
10. ↪️ **Open Redirect**: Validates destination paths to prevent malicious redirection.
|
|
93
|
-
11. ✂️ **CRLF**: Stops HTTP response splitting via carriage return and line feed characters.
|
|
94
|
-
12. 🧩 **Template Injection**: Blocks server-side template injection (e.g., Jinja2, Twig, EJS).
|
|
95
|
-
13. 📇 **LDAP Injection**: Identifies unauthorized LDAP query manipulation.
|
|
96
|
-
14. 🕸️ **GraphQL Abuse**: Limits introspection, deep nesting, and alias batching.
|
|
128
|
+
## AI and LLM Prompt Protection
|
|
97
129
|
|
|
98
|
-
|
|
130
|
+
FortifyJS includes dedicated guardrails for AI applications, chat endpoints, agent tools, and RAG pipelines:
|
|
131
|
+
|
|
132
|
+
### 1. LLM Endpoint Middleware (`llmGuard`)
|
|
133
|
+
|
|
134
|
+
Mount `llmGuard()` directly on chat and generation routes to inspect, log, or block malicious prompts before they reach your model:
|
|
99
135
|
|
|
100
|
-
|
|
136
|
+
```javascript
|
|
137
|
+
const express = require('express');
|
|
138
|
+
const { llmGuard } = require('fortifyjs');
|
|
101
139
|
|
|
102
|
-
|
|
140
|
+
const app = express();
|
|
141
|
+
app.use(express.json());
|
|
142
|
+
|
|
143
|
+
// Protect chat endpoint
|
|
144
|
+
app.post('/api/chat', llmGuard(), (req, res) => {
|
|
145
|
+
// Safe prompt verified
|
|
146
|
+
const userPrompt = req.body.prompt;
|
|
147
|
+
res.json({ response: 'Processed safely' });
|
|
148
|
+
});
|
|
149
|
+
```
|
|
103
150
|
|
|
104
|
-
|
|
105
|
-
|
|
151
|
+
### 2. Standalone Prompt Scan & Guardrail Assertions
|
|
152
|
+
|
|
153
|
+
Validate user prompts before sending them to OpenAI, Gemini, Anthropic, or local Ollama instances:
|
|
106
154
|
|
|
107
155
|
```javascript
|
|
108
|
-
const {
|
|
109
|
-
|
|
156
|
+
const { assertSafePrompt, scanPrompt } = require('fortifyjs');
|
|
157
|
+
|
|
158
|
+
// Detailed diagnostics
|
|
159
|
+
const verdict = scanPrompt(userMessage);
|
|
160
|
+
console.log(verdict.safe); // true or false
|
|
161
|
+
console.log(verdict.confidence); // 0.0 to 1.0
|
|
162
|
+
console.log(verdict.matches); // Matched signal IDs
|
|
163
|
+
|
|
164
|
+
// Guardrail assertion: throws FortifyPromptError if malicious
|
|
165
|
+
assertSafePrompt(userMessage);
|
|
110
166
|
```
|
|
111
|
-
</details>
|
|
112
167
|
|
|
113
|
-
|
|
114
|
-
|
|
168
|
+
### 3. Pluggable Hybrid AI Judge (Optional Deep Semantic Layer)
|
|
169
|
+
|
|
170
|
+
For applications requiring secondary semantic validation on ambiguous prompts, FortifyJS supports pluggable AI judges:
|
|
115
171
|
|
|
116
172
|
```javascript
|
|
117
|
-
|
|
118
|
-
|
|
173
|
+
app.post('/api/chat', llmGuard({
|
|
174
|
+
aiJudge: {
|
|
175
|
+
provider: 'openai',
|
|
176
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
177
|
+
fallback: 'block'
|
|
178
|
+
}
|
|
179
|
+
}), (req, res) => {
|
|
180
|
+
// Process LLM chat
|
|
181
|
+
});
|
|
119
182
|
```
|
|
120
|
-
</details>
|
|
121
183
|
|
|
122
|
-
|
|
123
|
-
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Runtime Sink Guardrails Reference
|
|
187
|
+
|
|
188
|
+
Defense-in-depth requires verifying values at the exact execution sink to prevent second-order injection and unsafe operations:
|
|
189
|
+
|
|
190
|
+
| Guardrail Function | Primary Target / Vulnerability | Defense Strategy | Default Policy |
|
|
191
|
+
| :--- | :--- | :--- | :--- |
|
|
192
|
+
| [`assertSafeCommand`](file:///D:/Projects/fortifyjs/src/core/sinks.js#L25) | OS Command Execution (`child_process.exec`) | Blocks shell metacharacters, subshells, and dangerous binaries | Strict command validation |
|
|
193
|
+
| [`assertSafePath`](file:///D:/Projects/fortifyjs/src/core/sinks.js#L44) | Filesystem Reads/Writes (`fs.readFile`, `fs.writeFile`)| Canonical directory boundary check (`path.resolve`) | Root containment enforced |
|
|
194
|
+
| [`assertSafeUrl`](file:///D:/Projects/fortifyjs/src/core/sinks.js#L78) | Outbound Network Calls (`fetch`, `axios`) | Bitwise CIDR validation against private / metadata IPs | Private IPs blocked |
|
|
195
|
+
| [`assertSafeSqlQuery`](file:///D:/Projects/fortifyjs/src/core/sinks.js#L101) | Raw SQL Execution (`db.query`) | AST structural analysis preventing boolean & tautology attacks | SQLi patterns rejected |
|
|
196
|
+
| [`assertSafeNoSql`](file:///D:/Projects/fortifyjs/src/core/sinks.js#L124) | Document Queries (`collection.find`) | Deep object inspection stripping unauthorized `$` operators | Forbidden keys stripped |
|
|
197
|
+
| [`assertSafeRedirect`](file:///D:/Projects/fortifyjs/src/core/sinks.js#L166) | HTTP Redirects (`res.redirect`) | Validates destination hostname against allowed whitelist | Whitelist / Relative only |
|
|
198
|
+
| [`assertSafePrompt`](file:///D:/Projects/fortifyjs/src/core/sinks.js#L212) | LLM Generation Calls | Multi-lingual instruction override & jailbreak evaluation | Malicious prompts rejected |
|
|
124
199
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
200
|
+
### Runtime Sink Usage Example
|
|
201
|
+
|
|
202
|
+
```javascript
|
|
203
|
+
const {
|
|
204
|
+
assertSafeCommand,
|
|
205
|
+
assertSafePath,
|
|
206
|
+
assertSafeUrl,
|
|
207
|
+
assertSafeNoSql,
|
|
208
|
+
assertSafeRedirect,
|
|
209
|
+
assertSafeSqlQuery
|
|
210
|
+
} = require('fortifyjs');
|
|
211
|
+
|
|
212
|
+
// 1. Command Execution Sink
|
|
213
|
+
assertSafeCommand(req.body.command);
|
|
214
|
+
|
|
215
|
+
// 2. File Download Sink
|
|
216
|
+
const safePath = assertSafePath(req.query.file, { rootDir: '/var/www/uploads' });
|
|
217
|
+
|
|
218
|
+
// 3. Webhook Outbound Sink
|
|
219
|
+
assertSafeUrl(req.body.webhookUrl, { allowPrivate: false });
|
|
220
|
+
|
|
221
|
+
// 4. MongoDB Filter Sink
|
|
222
|
+
assertSafeNoSql(req.body.filter);
|
|
223
|
+
|
|
224
|
+
// 5. Open Redirect Sink
|
|
225
|
+
assertSafeRedirect(targetUrl, { allowedHosts: ['myapp.com'] });
|
|
226
|
+
|
|
227
|
+
// 6. SQL Query Sink
|
|
228
|
+
assertSafeSqlQuery(dynamicSql);
|
|
129
229
|
```
|
|
130
|
-
</details>
|
|
131
230
|
|
|
132
231
|
---
|
|
133
232
|
|
|
134
|
-
##
|
|
233
|
+
## Mass-Assignment and Parameter Sanitization
|
|
135
234
|
|
|
136
|
-
|
|
235
|
+
Automatically strip or reject forbidden fields (such as `isAdmin`, `role`, `permissions`, `balance`, `__proto__`) from incoming request bodies and query parameters:
|
|
137
236
|
|
|
138
237
|
```javascript
|
|
139
|
-
const { shield } = require('fortifyjs');
|
|
238
|
+
const { shield, sanitizeObject } = require('fortifyjs');
|
|
140
239
|
|
|
240
|
+
// 1. Middleware mode
|
|
141
241
|
app.use(shield('medium', {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
rateLimit: {
|
|
146
|
-
max: 300,
|
|
147
|
-
windowMs: 10 * 60 * 1000
|
|
242
|
+
sanitize: {
|
|
243
|
+
stripFields: ['isAdmin', 'role', 'permissions', 'credit', 'balance'],
|
|
244
|
+
rejectOnForbidden: false // Set true to reject with 400 Bad Request
|
|
148
245
|
}
|
|
149
246
|
}));
|
|
247
|
+
|
|
248
|
+
// 2. Programmatic utility
|
|
249
|
+
const { sanitized, strippedKeys } = sanitizeObject(req.body, {
|
|
250
|
+
stripFields: ['isAdmin', 'role']
|
|
251
|
+
});
|
|
150
252
|
```
|
|
151
253
|
|
|
152
254
|
---
|
|
153
255
|
|
|
154
|
-
##
|
|
256
|
+
## The 4 Protection Tiers
|
|
155
257
|
|
|
156
|
-
|
|
258
|
+
| Capability / Shield | basic | medium | hard | advanced |
|
|
259
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
260
|
+
| **Detection Level** | Balanced (0.50 threshold) | Balanced (0.50 threshold) | Strict (0.25 threshold) | Strict (0.25 threshold) |
|
|
261
|
+
| **Security Headers**| Yes (Standard) | Yes (Standard) | Yes (Strict CSP / HSTS) | Yes (Strict CSP / HSTS) |
|
|
262
|
+
| **Rate Limiting** | 100 req / 15 min | 200 req / 15 min | 100 req / 15 min | 100 req / 15 min |
|
|
263
|
+
| **CORS Policy** | Same-Origin | Same-Origin | Strict Whitelist | Strict Whitelist |
|
|
264
|
+
| **CSRF Shield** | No | No | Yes (Double-Submit Token) | Yes (Double-Submit Token) |
|
|
265
|
+
| **Bot Detection** | Flag Mode | Block Known Bad Bots | Block Known Bad Bots | Adaptive Fingerprinting |
|
|
266
|
+
| **Behavioral Profiler**| Entropy Scoring | Anomaly Scoring | Anomaly Scoring | Anomaly Scoring |
|
|
267
|
+
| **File Upload Shield** | No | Yes (Extension whitelist) | Yes (Extension + MIME) | Yes (Deep scan + MIME) |
|
|
268
|
+
| **Threat Dashboard** | No | No | No | Yes (`/admin/security`) |
|
|
157
269
|
|
|
158
|
-
|
|
159
|
-
```bash
|
|
160
|
-
fortifyjs scan "<test-input>"
|
|
161
|
-
```
|
|
270
|
+
---
|
|
162
271
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
272
|
+
## Framework Compatibility Matrix
|
|
273
|
+
|
|
274
|
+
| Framework | Import Path | Adapter Style | Edge / Serverless Ready |
|
|
275
|
+
| :--- | :--- | :--- | :--- |
|
|
276
|
+
| **Express** | `require('fortifyjs')` | `shield(tier, options)` | Yes |
|
|
277
|
+
| **Fastify** | `require('fortifyjs/adapters/fastify')` | `fastify.register(fastifyPlugin)` | Yes |
|
|
278
|
+
| **Next.js** | `import nextjsAdapter from 'fortifyjs/adapters/nextjs'` | Edge / Route Handler Middleware | Yes |
|
|
279
|
+
| **Hono** | `import { honoMiddleware } from 'fortifyjs/adapters/hono'` | `app.use('*', honoMiddleware())` | Yes (Cloudflare Workers, Deno, Bun) |
|
|
280
|
+
| **Koa** | `const { koaMiddleware } = require('fortifyjs/adapters/koa')` | `app.use(koaMiddleware())` | Yes |
|
|
281
|
+
| **NestJS** | `import { FortifyGuard } from 'fortifyjs/adapters/nestjs'` | `@UseGuards(FortifyGuard)` | Yes |
|
|
167
282
|
|
|
168
283
|
---
|
|
169
284
|
|
|
170
|
-
##
|
|
285
|
+
## Latency and Performance Benchmarks
|
|
171
286
|
|
|
172
|
-
|
|
287
|
+
FortifyJS operates an in-memory inspection pipeline designed for high-throughput microservice architectures:
|
|
173
288
|
|
|
174
|
-
|
|
289
|
+
| Inspection Pipeline Stage | p50 Latency | p99 Latency | Throughput Capacity | Strategy |
|
|
290
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
291
|
+
| **Fast-Path Filter (Clean input)** | **0.003 ms** | **0.008 ms** | **> 300,000 req/sec** | Short-circuits clean alphanumeric tokens and numbers |
|
|
292
|
+
| **15-Vector Deep WAF Scan** | **0.067 ms** | **0.140 ms** | **> 14,000 req/sec** | Tri-variant normalization and structural scoring |
|
|
293
|
+
| **AI Prompt Injection Scan** | **0.052 ms** | **0.110 ms** | **> 18,000 req/sec** | Multi-lingual instruction and delimiter heuristics |
|
|
294
|
+
| **Runtime Sink Assertion** | **0.004 ms** | **0.012 ms** | **> 250,000 req/sec** | Bitwise IP check, directory path containment |
|
|
175
295
|
|
|
176
296
|
---
|
|
177
297
|
|
|
178
|
-
##
|
|
298
|
+
## Offline CLI Testing
|
|
179
299
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
300
|
+
FortifyJS includes a standalone command-line interface for offline testing and CI/CD pipelines:
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
# Scan a single input string
|
|
304
|
+
fortifyjs scan "1 UNION SELECT username, password FROM users--"
|
|
305
|
+
|
|
306
|
+
# Scan a batch file of test payloads with CSV output
|
|
307
|
+
fortifyjs scan-file test-payloads.txt --format csv
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## Contributing and License
|
|
313
|
+
|
|
314
|
+
* Refer to [CONTRIBUTING.md](CONTRIBUTING.md) for development guidelines.
|
|
315
|
+
* Refer to [SECURITY.md](SECURITY.md) for reporting security vulnerabilities.
|
|
316
|
+
* Licensed under the [MIT License](LICENSE).
|
|
183
317
|
|
|
184
|
-
<div align="center">
|
|
185
|
-
<i>Built with absolute security and zero bloat in mind.</i>
|
|
186
|
-
</div>
|
package/bin/banner.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const banner = `
|
|
5
|
+
______ _ _ __ _ _____
|
|
6
|
+
| ____| | | (_)/ _| | |/ ____|
|
|
7
|
+
| |__ ___ _ __| |_ _| |_ _ _| | (___
|
|
8
|
+
| __/ _ \\| '__| __| | _| | | | |\\___ \\
|
|
9
|
+
| | | (_) | | | |_| | | | |_| | |____) |
|
|
10
|
+
|_| \\___/|_| \\__|_|_| \\__, |_|_____/
|
|
11
|
+
__/ |
|
|
12
|
+
|___/
|
|
13
|
+
|
|
14
|
+
Zero-Dependency Web Application Firewall & AI Security Suite
|
|
15
|
+
Built by Chiranth Moger
|
|
16
|
+
Version: 1.1.1
|
|
17
|
+
Ready for Express, Fastify, Koa, Hono, NestJS, Next.js
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
function showBanner() {
|
|
21
|
+
if (process.env.npm_config_loglevel === 'silent') return;
|
|
22
|
+
console.log(banner);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (require.main === module) {
|
|
26
|
+
showBanner();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = { banner, showBanner };
|
package/bin/fortifyjs.js
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const readline = require('readline');
|
|
6
6
|
const util = require('util');
|
|
7
7
|
const { DetectionEngine } = require('../src/index');
|
|
8
8
|
|
|
9
|
+
const { banner } = require('./banner');
|
|
10
|
+
|
|
9
11
|
const MAX_CLI_PAYLOAD_LENGTH = 50000;
|
|
10
|
-
|
|
12
|
+
|
|
11
13
|
function printHelp() {
|
|
12
|
-
console.log(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
Options:
|
|
20
|
-
--format <json|csv> - Output format (default: json)
|
|
14
|
+
console.log(banner);
|
|
15
|
+
console.log(`Usage:
|
|
16
|
+
fortifyjs scan <payload> - Scan a single payload
|
|
17
|
+
fortifyjs scan-file <filepath> - Scan a file with one payload per line
|
|
18
|
+
|
|
19
|
+
Options:
|
|
20
|
+
--format <json|csv> - Output format (default: json)
|
|
21
21
|
`);
|
|
22
22
|
}
|
|
23
23
|
|
package/index.d.ts
CHANGED
|
@@ -3,12 +3,35 @@
|
|
|
3
3
|
declare module 'fortifyjs' {
|
|
4
4
|
export type Tier = 'basic' | 'medium' | 'hard' | 'advanced';
|
|
5
5
|
|
|
6
|
-
export type DetectionLabel =
|
|
7
|
-
|
|
6
|
+
export type DetectionLabel =
|
|
7
|
+
| 'sqli'
|
|
8
|
+
| 'xss'
|
|
9
|
+
| 'nosqli'
|
|
10
|
+
| 'cmdi'
|
|
11
|
+
| 'path-traversal'
|
|
12
|
+
| 'ssrf'
|
|
13
|
+
| 'xxe'
|
|
14
|
+
| 'prototype-pollution'
|
|
15
|
+
| 'hpp'
|
|
16
|
+
| 'open-redirect'
|
|
17
|
+
| 'crlf'
|
|
18
|
+
| 'template-injection'
|
|
19
|
+
| 'ldap'
|
|
20
|
+
| 'graphql'
|
|
21
|
+
| 'prompt-injection'
|
|
22
|
+
| 'benign'
|
|
23
|
+
| 'anomaly'
|
|
24
|
+
| string;
|
|
25
|
+
|
|
26
|
+
export type DetectorType = DetectionLabel;
|
|
8
27
|
|
|
9
28
|
export interface RateLimitOptions {
|
|
10
29
|
max?: number;
|
|
11
30
|
windowMs?: number;
|
|
31
|
+
store?: BaseStore;
|
|
32
|
+
keyGenerator?: (req: any) => string;
|
|
33
|
+
standardHeaders?: boolean;
|
|
34
|
+
handler?: (req: any, res: any, next: any, options: any) => void;
|
|
12
35
|
}
|
|
13
36
|
|
|
14
37
|
export interface CorsOptions {
|
|
@@ -30,6 +53,8 @@ declare module 'fortifyjs' {
|
|
|
30
53
|
path?: string;
|
|
31
54
|
};
|
|
32
55
|
ignoreMethods?: string[];
|
|
56
|
+
secret?: string;
|
|
57
|
+
silent?: boolean;
|
|
33
58
|
}
|
|
34
59
|
|
|
35
60
|
export interface BotDetectionOptions {
|
|
@@ -63,6 +88,34 @@ declare module 'fortifyjs' {
|
|
|
63
88
|
pattern?: RegExp[];
|
|
64
89
|
}
|
|
65
90
|
|
|
91
|
+
export interface SanitizerOptions {
|
|
92
|
+
stripFields?: string[];
|
|
93
|
+
rejectOnForbidden?: boolean;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface LlmGuardOptions {
|
|
97
|
+
threshold?: number;
|
|
98
|
+
fields?: string[];
|
|
99
|
+
dryRun?: boolean;
|
|
100
|
+
aiJudge?: ((promptText: string) => Promise<{ safe: boolean; reason?: string }>) | {
|
|
101
|
+
provider?: 'openai' | 'ollama' | 'gemini';
|
|
102
|
+
apiKey?: string;
|
|
103
|
+
endpoint?: string;
|
|
104
|
+
model?: string;
|
|
105
|
+
fallback?: 'allow' | 'block';
|
|
106
|
+
};
|
|
107
|
+
onThreat?: (event: any, req: any, res: any) => void;
|
|
108
|
+
onBlocked?: (req: any, res: any, event: any) => void;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface PromptScanResult {
|
|
112
|
+
label: 'prompt-injection' | 'benign';
|
|
113
|
+
confidence: number;
|
|
114
|
+
safe: boolean;
|
|
115
|
+
matches: Array<{ id: string; label: string; confidence: number }>;
|
|
116
|
+
scores: Record<string, number>;
|
|
117
|
+
}
|
|
118
|
+
|
|
66
119
|
export interface FortifyOptions {
|
|
67
120
|
tier?: Tier;
|
|
68
121
|
level?: 'strict' | 'balanced' | 'permissive';
|
|
@@ -73,9 +126,11 @@ declare module 'fortifyjs' {
|
|
|
73
126
|
botDetection?: boolean | BotDetectionOptions;
|
|
74
127
|
behavioral?: boolean | BehavioralOptions;
|
|
75
128
|
fileUpload?: boolean | FileUploadOptions;
|
|
129
|
+
sanitize?: boolean | SanitizerOptions;
|
|
76
130
|
dashboard?: boolean | { enabled?: boolean; path?: string };
|
|
77
131
|
whitelist?: WhitelistOptions;
|
|
78
132
|
mode?: 'input' | 'query';
|
|
133
|
+
onBlocked?: (req: any, res: any, threat: any) => void;
|
|
79
134
|
logging?: { level?: 'silent' | 'error' | 'warn' | 'info' | 'debug'; format?: 'json' | 'text' };
|
|
80
135
|
}
|
|
81
136
|
|
|
@@ -89,6 +144,36 @@ declare module 'fortifyjs' {
|
|
|
89
144
|
export function honoMiddleware(options?: FortifyOptions): (c: any, next: () => Promise<any>) => Promise<void>;
|
|
90
145
|
export function genericAdapter(options?: FortifyOptions): (req: any, res: any, next: any) => void;
|
|
91
146
|
|
|
147
|
+
// Runtime Sink Guardrails
|
|
148
|
+
export function assertSafeSqlQuery(query: string, options?: any): any;
|
|
149
|
+
export function scanSqlQuery(query: string, options?: any): any;
|
|
150
|
+
export function assertSafeCommand(command: string, options?: any): any;
|
|
151
|
+
export function assertSafePath(userPath: string, options?: { rootDir?: string; threshold?: number }): string;
|
|
152
|
+
export function assertSafeUrl(targetUrl: string, options?: { allowPrivate?: boolean; allowedProtocols?: string[] }): URL;
|
|
153
|
+
export function assertSafeNoSql(query: any, options?: { forbiddenOperators?: string[]; disallowAllOperators?: boolean }): any;
|
|
154
|
+
export function assertSafeRedirect(destination: string, options?: { allowedHosts?: string[]; allowRelative?: boolean }): string;
|
|
155
|
+
export function assertSafePrompt(prompt: string | any, options?: LlmGuardOptions): PromptScanResult;
|
|
156
|
+
export function scanPrompt(prompt: string | any, options?: LlmGuardOptions): PromptScanResult;
|
|
157
|
+
export function llmGuard(options?: LlmGuardOptions): (req: any, res: any, next: any) => void;
|
|
158
|
+
|
|
159
|
+
// Sanitizer
|
|
160
|
+
export function sanitizerFactory(options?: SanitizerOptions): (req: any, res: any, next: any) => void;
|
|
161
|
+
export function sanitizeObject(obj: any, options?: SanitizerOptions): { sanitized: any; strippedKeys: string[]; wasForbidden: boolean };
|
|
162
|
+
|
|
163
|
+
// Storage
|
|
164
|
+
export class BaseStore {
|
|
165
|
+
get(key: string): Promise<any>;
|
|
166
|
+
set(key: string, value: any, ttlMs?: number): Promise<boolean>;
|
|
167
|
+
increment(key: string, ttlMs?: number): Promise<{ count: number; resetTime: number }>;
|
|
168
|
+
delete(key: string): Promise<boolean>;
|
|
169
|
+
clear(): Promise<void>;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export class MemoryStore extends BaseStore {
|
|
173
|
+
constructor(options?: { maxEntries?: number; cleanupIntervalMs?: number });
|
|
174
|
+
destroy(): void;
|
|
175
|
+
}
|
|
176
|
+
|
|
92
177
|
export class DetectionEngine {
|
|
93
178
|
constructor(options?: any);
|
|
94
179
|
detect(payload: string, context?: { source?: 'query' | 'body' | 'header' | 'cookie' | 'path' | 'filename' | string; route?: string; [key: string]: any }): any;
|
|
@@ -96,6 +181,21 @@ declare module 'fortifyjs' {
|
|
|
96
181
|
|
|
97
182
|
export class Normalizer {
|
|
98
183
|
constructor(options?: any);
|
|
99
|
-
normalizePayload(payload: string | Buffer, options?: any): string;
|
|
184
|
+
static normalizePayload(payload: string | Buffer, options?: any): string;
|
|
185
|
+
static payloadVariants(payload: string | Buffer, options?: any): string[];
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export class FortifySinkError extends Error {
|
|
189
|
+
sinkType: string;
|
|
190
|
+
status: number;
|
|
191
|
+
code: string;
|
|
192
|
+
result?: any;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export class FortifyPromptError extends Error {
|
|
196
|
+
status: number;
|
|
197
|
+
code: string;
|
|
198
|
+
result: any;
|
|
199
|
+
promptPreview: string;
|
|
100
200
|
}
|
|
101
201
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chiranthmoger/fortifyjs",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.1.1",
|
|
4
|
+
"description": "Zero-dependency Web Application Firewall and AI Security Suite for Node.js. One-line protection against injection, XSS, CSRF, SSRF, prompt injection, and 15 attack classes. Replaces helmet, cors, csurf, and express-rate-limit.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
7
7
|
"bin": {
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"index.d.ts"
|
|
44
44
|
],
|
|
45
45
|
"scripts": {
|
|
46
|
+
"postinstall": "node bin/banner.js",
|
|
46
47
|
"test": "jest",
|
|
47
48
|
"benchmark": "node benchmarks/detection-speed.js && node benchmarks/throughput.js"
|
|
48
49
|
},
|