@roboteby/parry 1.1.1 → 2.0.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/CHANGELOG.md CHANGED
@@ -1,33 +1,66 @@
1
1
  # Changelog
2
2
 
3
- All notable changes to this project will be documented in this file.
4
-
5
- This project follows [Semantic Versioning](https://semver.org/) for public API and
6
- runtime behavior. The format is based on [Keep a Changelog](https://keepachangelog.com/),
7
- without adding a changelog generation dependency.
3
+ All notable changes to this project are documented here. The format is based on
4
+ [Keep a Changelog](https://keepachangelog.com/) and the project follows Semantic
5
+ Versioning for public APIs and documented runtime behavior.
8
6
 
9
7
  ## [Unreleased]
10
8
 
9
+ ## [2.0.0] - 2026-09-01
10
+
11
+ ### Added
12
+
13
+ - Added exact-path NoSQL operator allowlists through
14
+ `nosql.allowedOperators`, configurable scalar header scanning, internal option
15
+ validation, typed advanced subpaths, TypeScript declaration tests, and 75
16
+ benign false-positive controls.
17
+ - Added security, contribution, issue, pull-request, testing, deployment,
18
+ architecture, and release guidance.
19
+
20
+ ### Changed
21
+
22
+ - Raised the minimum supported Node.js version from `>=18` to `>=22`. The package
23
+ remains CommonJS and keeps Express `^5.2.1` as its peer dependency.
24
+ - Made `createParry` and `ParryOptions` the recommended API names while retaining
25
+ every existing public export and the deprecated `Parry_DDoS`/
26
+ `Parry_DDoSOptions` aliases.
27
+ - Reordered scanning so Request Shape runs first, structured guards inspect each
28
+ surface once, scalar leaves are serialized once, duplicate findings are
29
+ removed, and aggregate severity uses the most severe finding.
30
+ - Resolved trusted proxy chains from right to left and changed generated request
31
+ IDs to `req_${crypto.randomUUID()}`.
32
+ - Migrated tests to `node:test`/`node:assert`, ESLint flat configuration, full
33
+ Prettier checks, GitHub Actions npm publishing with provenance, and an optional
34
+ Terraform example under `infra/examples/aws`.
35
+ - Updated repository metadata and links to `RobotEby/parry`.
36
+
37
+ ### Security
38
+
39
+ - **Breaking change:** `createParryAdminRouter` no longer permits
40
+ anonymous access by default. It fails during construction unless real auth is
41
+ configured or insecure access is explicitly selected outside production.
42
+ - In production, `allowInsecureAdminApi`, `auth.mode: "none"`, and legacy
43
+ `requireAuth: false` are always rejected. Empty tokens and invalid IP/CIDR
44
+ boundaries are rejected, and `verifyJwt: true` continues to fail explicitly.
45
+ - Removed stale monitor-only fixtures for Command Injection and SSRF because
46
+ Parry does not implement those detectors.
47
+
48
+ ## [1.1.1] - 2026-07-02
49
+
50
+ ### Changed
51
+
52
+ - Published `@roboteby/parry@1.1.1` to the stable `latest` npm dist-tag.
53
+
11
54
  ## [1.1.0-rc.1] - 2026-07-02
12
55
 
13
56
  ### Added
14
57
 
15
- - Published the first npm release candidate as `@roboteby/parry@1.1.0-rc.1`
16
- under the `rc` dist-tag.
17
- - Documented the release candidate installation path with
18
- `npm install @roboteby/parry@rc`.
19
- - Added package hardening, release workflow documentation, and package tarball
20
- validation scripts.
21
- - Included the current application-layer protection surface: route policies,
22
- distributed rate limiting, BruteForceGuard, Threat Events, metrics, and the
23
- read-only Admin API.
24
- - Added documentation for Admin API authentication modes, payload regression
25
- testing, Docker demo usage, AWS reference infrastructure, and CI/CD.
58
+ - Published the first release candidate under the `rc` npm dist-tag.
59
+ - Added route policies, distributed rate limiting, brute-force protection,
60
+ Threat Events, metrics, the read-only Admin API, Docker demonstration, and AWS
61
+ reference infrastructure.
26
62
 
27
63
  ### Security
28
64
 
29
- - Documented npm publishing and supply-chain hardening with Trusted
30
- Publishing/OIDC.
31
65
  - Clarified that Parry is application-layer Express middleware and does not
32
- replace CloudFront, AWS WAF, Shield, Cloudflare, CDN, ALB, or edge-layer
33
- volumetric DDoS protection.
66
+ replace CDN/WAF, load-balancer, or volumetric DDoS controls.
package/README.md CHANGED
@@ -1,299 +1,160 @@
1
1
  # Parry
2
2
 
3
- [![npm rc version](https://img.shields.io/npm/v/@roboteby/parry/rc?label=npm%20rc)](https://www.npmjs.com/package/@roboteby/parry)
4
- [![license](https://img.shields.io/npm/l/@roboteby/parry)](https://github.com/RobotEby/parry-express-security-middleware/blob/main/LICENSE)
3
+ [![npm version](https://img.shields.io/npm/v/@roboteby/parry)](https://www.npmjs.com/package/@roboteby/parry)
4
+ [![license](https://img.shields.io/npm/l/@roboteby/parry)](./LICENSE)
5
5
  [![node](https://img.shields.io/node/v/@roboteby/parry)](https://www.npmjs.com/package/@roboteby/parry)
6
6
 
7
- Application-layer security middleware for Express.js.
7
+ Application-layer security middleware for Express that combines abuse detection,
8
+ request guards, rate limiting, brute-force protection and security observability.
8
9
 
9
- ## Overview
10
+ The current stable release is `@roboteby/parry@2.0.0` on the npm `latest`
11
+ dist-tag.
10
12
 
11
- Parry is a CommonJS security middleware for Express applications. It helps block common application-layer abuse before requests reach route handlers, while keeping the public API small and compatible with standard Express middleware usage.
12
-
13
- Parry detects and blocks patterns associated with SQL injection, XSS, NoSQL injection, HTTP parameter pollution, prototype pollution, path traversal, rate abuse, and brute-force authentication attempts. It also emits structured Threat Events and metrics that can be inspected through an optional read-only Admin API.
14
-
15
- Parry is not a complete volumetric DDoS protection product. Network floods, L3/L4 abuse, TLS exhaustion, CDN filtering, and edge rate controls should be handled by CloudFront, AWS WAF, Shield, a CDN, an ALB/load balancer, or equivalent infrastructure controls.
16
-
17
- ## Why Parry
18
-
19
- Parry centralizes application-layer security policy in one Express middleware. That gives backend teams a consistent place to tune detector behavior, route-based limits, brute-force protection, event logging, and distributed rate limiting.
20
-
21
- It is designed for development and production-like deployments:
22
-
23
- - use `MemoryStore` for local development and single-process services;
24
- - use `RedisStore` for multiple instances, containers, ECS, Kubernetes, PM2 cluster, or load-balanced services;
25
- - expose the Admin API only behind authentication, private networking, VPN, or a trusted reverse proxy.
26
-
27
- ## Features
28
-
29
- - SQL injection detection
30
- - XSS detection
31
- - NoSQL injection detection
32
- - HTTP parameter pollution checks
33
- - Prototype pollution checks
34
- - Path traversal checks
35
- - Request shape guard
36
- - Global and route-based rate limiting
37
- - BruteForceGuard for authentication routes
38
- - `MemoryStore` and optional `RedisStore`
39
- - Structured Threat Events
40
- - Metrics and observability helpers
41
- - Optional read-only Admin API
42
- - Route-based policies and presets
43
- - Docker demo API
44
- - AWS reference infrastructure
45
-
46
- ## Installation
13
+ ## Install
47
14
 
48
15
  ```bash
49
- npm install @roboteby/parry@rc
16
+ npm install @roboteby/parry express@^5.2.1
50
17
  ```
51
18
 
52
- Parry expects Express to be installed by your application.
53
-
54
- ## Release Candidate
19
+ Parry 2 requires Node.js `>=22` and Express `^5.2.1`.
55
20
 
56
- `@roboteby/parry@1.1.0-rc.1` is published on npm under the `rc` tag for validation before promotion to the stable `latest` channel. Use `npm install @roboteby/parry@rc` to test the release candidate, review detector behavior and Admin API configuration in your environment, and pin the exact version if you need reproducible rollout testing.
57
-
58
- ## Quick Start
21
+ ## Minimal example
59
22
 
60
23
  ```js
61
24
  const express = require('express');
62
25
  const { createParry } = require('@roboteby/parry');
63
26
 
64
27
  const app = express();
28
+ app.use(express.json({ limit: '64kb' }));
65
29
 
66
- const parry = createParry({
67
- preset: 'recommended',
68
- });
69
-
70
- app.use(express.json());
30
+ const parry = createParry({ preset: 'recommended' });
71
31
  app.use(parry.middleware());
72
32
 
73
- app.get('/health', (_req, res) => {
74
- res.json({ ok: true });
75
- });
76
-
33
+ app.get('/health', (_req, res) => res.json({ ok: true }));
77
34
  app.listen(3000);
78
35
  ```
79
36
 
80
- Parse JSON and URL-encoded bodies before Parry when you want Parry to inspect request bodies. Keep Parry before routes that should be protected.
81
-
82
- The legacy `Parry_DDoS(options)` export remains available for existing CommonJS integrations:
83
-
84
- ```js
85
- const { Parry_DDoS } = require('@roboteby/parry');
86
-
87
- app.use(express.json());
88
- app.use(Parry_DDoS({ preset: 'recommended' }));
89
- ```
90
-
91
- ## Presets
37
+ Body parsers must run before Parry when request bodies should be inspected.
38
+ Mount Parry before the routes it protects.
92
39
 
93
- Parry supports conservative presets for common application-layer protection:
40
+ ## Main capabilities
94
41
 
95
- - `off`: no route-policy preset is added.
96
- - `recommended`: enables practical defaults for common auth routes and low-noise application-layer checks.
97
- - `strict`: uses more restrictive brute-force and route rate-limit defaults for sensitive environments.
42
+ - Request Shape limits for depth, key count, array length and string length.
43
+ - Heuristic SQL injection and XSS detection, plus NoSQL operator guards.
44
+ - HTTP parameter pollution, prototype pollution and path traversal guards.
45
+ - Global and route-specific rate limiting.
46
+ - Brute-force counters and temporary blocks for authentication routes.
47
+ - Sanitized Threat Events, process-local metrics and an optional read-only Admin
48
+ API.
49
+ - In-memory state by default, with an optional `RedisStore` for shared protection
50
+ state.
98
51
 
99
- Every option can still be configured explicitly. Prefer starting with `recommended`, reviewing logs and events, then tightening route policies where needed.
52
+ ## Security boundaries
100
53
 
101
- ## Stores
54
+ Parry is a defense-in-depth control for the Express application layer. It is not
55
+ a WAF and does not replace a CDN, reverse proxy, load balancer or volumetric
56
+ L3/L4 DDoS protection. It also does not replace authentication, authorization,
57
+ schema validation, parameterized queries, context-aware escaping/output
58
+ encoding or CSP.
102
59
 
103
- `MemoryStore` is the default store. It is suitable for tests, demos, local development, and single-process deployments.
60
+ SQLi and XSS detection is heuristic, so false positives and false negatives are
61
+ possible. Request Shape runs before heavier scans to bound their cost, but an
62
+ allowed request is not proof that its input is safe for downstream use.
104
63
 
105
- For distributed deployments, use `RedisStore` with a Redis client created by your application:
64
+ Trusted proxy handling depends on narrow, correct `trustedProxies`
65
+ configuration. See the [security model](./docs/security-model.md) before enabling
66
+ forwarded-header trust.
106
67
 
107
- ```js
108
- const { createClient } = require('redis');
109
- const { createParry, RedisStore } = require('@roboteby/parry');
68
+ ## Public API
110
69
 
111
- const redis = createClient({ url: process.env.REDIS_URL });
112
- await redis.connect();
70
+ The recommended root exports are:
113
71
 
114
- const parry = createParry({
115
- store: new RedisStore({ client: redis, prefix: 'parry' }),
116
- rateLimit: {
117
- enabled: true,
118
- max: 100,
119
- windowMs: 60_000,
120
- headers: true,
121
- },
122
- });
123
- ```
72
+ - `createParry(options)` creates the middleware and its observability context.
73
+ - `createParryAdminRouter(parry, options)` creates the optional Admin router.
74
+ - `MemoryStore` — keeps protection state in one process.
75
+ - `RedisStore` — uses an application-owned Redis client for shared protection
76
+ state.
124
77
 
125
- If your service runs behind multiple instances, containers, or load balancers, use a shared store. `MemoryStore` protects only the current Node.js process.
78
+ Existing exports remain available. `Parry_DDoS(options)` is a deprecated
79
+ compatibility wrapper around `createParry(options).middleware()`, and
80
+ `Parry_DDoSOptions` is a deprecated TypeScript alias for `ParryOptions`.
126
81
 
127
- ## Brute-Force Protection
82
+ Advanced typed exports are available from `/core`, `/detectors`, `/stores`,
83
+ `/policies`, `/brute-force`, `/events`, `/observability` and `/admin`.
128
84
 
129
- Route policies can protect sensitive authentication endpoints without making the global rate limit too aggressive:
85
+ ## Admin API
130
86
 
131
- ```js
132
- const parry = createParry({
133
- policies: [
134
- {
135
- name: 'auth-login',
136
- match: { method: 'POST', path: '/login' },
137
- rateLimit: {
138
- enabled: true,
139
- max: 20,
140
- windowMs: 60_000,
141
- key: 'ip',
142
- },
143
- bruteForce: {
144
- enabled: true,
145
- maxAttempts: 5,
146
- windowMs: 15 * 60_000,
147
- blockDurationMs: 10 * 60_000,
148
- keys: ['ip', 'body.email', 'ip+body.email'],
149
- failureStatusCodes: [400, 401, 403],
150
- resetOnSuccess: true,
151
- },
152
- },
153
- ],
154
- });
155
- ```
156
-
157
- Routes can also report authentication outcomes manually:
87
+ The Admin API exposes health, process metrics, recent sanitized events, active
88
+ bans and normalized policies. It is never mounted automatically and fails during
89
+ construction unless authentication is configured:
158
90
 
159
91
  ```js
160
- app.post('/login', async (req, res) => {
161
- const user = await authService.validate(req.body.email, req.body.password);
162
-
163
- if (!user) {
164
- req.parry?.recordAuthFailure('invalid_credentials');
165
- return res.status(200).json({ success: false });
166
- }
167
-
168
- req.parry?.recordAuthSuccess();
169
- return res.json({ success: true });
170
- });
171
- ```
92
+ const { createParryAdminRouter } = require('@roboteby/parry');
172
93
 
173
- ## Threat Events and Admin API
174
-
175
- Parry emits structured Threat Events for blocked requests, rate limits, brute-force blocks, store errors, and hook errors. Events are sanitized before reaching logs, hooks, metrics, or the Admin API.
176
-
177
- The optional Admin API is read-only and is never mounted automatically:
178
-
179
- ```js
180
- const { createParry, createParryAdminRouter } = require('@roboteby/parry');
181
-
182
- const parry = createParry({
183
- admin: {
184
- enabled: true,
94
+ app.use(
95
+ '/_parry',
96
+ createParryAdminRouter(parry, {
185
97
  auth: {
186
98
  mode: 'token',
187
99
  token: process.env.PARRY_ADMIN_TOKEN,
188
100
  },
189
- },
190
- });
191
-
192
- app.use(parry.middleware());
193
- app.use('/_parry', createParryAdminRouter(parry));
101
+ })
102
+ );
194
103
  ```
195
104
 
196
- Main endpoints:
197
-
198
- - `GET /_parry/health`
199
- - `GET /_parry/metrics`
200
- - `GET /_parry/events`
201
- - `GET /_parry/events/:id`
202
- - `GET /_parry/bans`
203
- - `GET /_parry/policies`
105
+ Anonymous Admin access is an explicit local-development opt-in and is rejected
106
+ in production. External identity modes depend on a configured trusted boundary;
107
+ Parry does not perform cryptographic JWT/JWKS verification, and `verifyJwt: true`
108
+ fails explicitly.
204
109
 
205
- Protect the Admin API with token auth for local demos, or with VPN, private networking, Cloudflare Access, AWS ALB/Cognito auth, trusted proxy auth, or IP allowlists in production.
110
+ See [Admin API](./docs/admin-api.md) for authentication modes and deployment
111
+ guidance.
206
112
 
207
- ## Parry Security Console
113
+ ## Redis
208
114
 
209
- The separate `parry-security-console` repository provides a read-only dashboard for the Parry Admin API. It displays health, metrics, Threat Events, bans/blocks, and route policies. It does not contain middleware logic, does not execute payloads, and is not a scanner.
115
+ The application creates and connects the Redis client. Parry does not install or
116
+ own a Redis package implicitly.
210
117
 
211
- For local development, the console can use Vite proxying with `VITE_PARRY_API_URL=/api/parry`.
212
-
213
- ## Docker Demo
214
-
215
- The repository includes a demo Express API with Redis:
216
-
217
- ```bash
218
- docker compose up --build
219
- ```
220
-
221
- Useful local checks:
222
-
223
- ```bash
224
- curl http://localhost:3000/health
118
+ ```js
119
+ const { createClient } = require('redis');
120
+ const { createParry, RedisStore } = require('@roboteby/parry');
225
121
 
226
- curl http://localhost:3000/_parry/health \
227
- -H "x-parry-admin-token: change-me"
122
+ const client = createClient({ url: process.env.REDIS_URL });
123
+ await client.connect();
228
124
 
229
- curl -X POST http://localhost:3000/echo \
230
- -H "Content-Type: application/json" \
231
- -d '{"message":"hello"}'
125
+ const parry = createParry({
126
+ store: new RedisStore({ client, prefix: 'parry' }),
127
+ storeFailureMode: 'fail-closed',
128
+ });
232
129
  ```
233
130
 
234
- The `change-me` token is for local demos only.
235
-
236
- ## cURL Guide
237
-
238
- The demo API can be validated with a set of local cURL examples covering health checks, Admin API authentication, Threat Events, brute-force blocks, active bans, CORS, request IDs, and frontend proxy checks.
239
-
240
- See [docs/curl-guide.md](https://github.com/RobotEby/parry-express-security-middleware/blob/main/docs/curl-guide.md).
241
-
242
- ## Security Model
243
-
244
- Parry operates inside the Express application. It helps identify and block suspicious application-layer requests, but it does not replace edge and infrastructure controls.
131
+ `RedisStore` shares rate-limit state, brute-force state, counters and related
132
+ bans/blocks across instances. Threat Events, the default event buffer and
133
+ metrics remain local to each process unless the application exports them.
245
134
 
246
- Production deployments should account for:
135
+ ## Runtime support
247
136
 
248
- - CloudFront, AWS WAF, Shield, CDN, ALB, or equivalent edge protection for volumetric DDoS and network-layer abuse.
249
- - RedisStore or another shared store for distributed rate limits and brute-force counters.
250
- - Admin API authentication and network restrictions.
251
- - Trusted proxy configuration before accepting `x-forwarded-for`, Cloudflare Access, ALB/Cognito, or reverse-proxy identity headers.
252
- - Generic authentication responses that do not reveal whether a username or email exists.
137
+ - Node.js `>=22`
138
+ - Express `^5.2.1`
139
+ - CommonJS
253
140
 
254
- Browser-visible Admin API tokens are appropriate only for local development and demos. Production consoles should be protected by VPN, private networking, Cloudflare Access, ALB/Cognito, reverse proxy auth, or a backend/admin gateway.
141
+ CI covers Node.js 22 and 24. Parry 2 does not imply or announce an ESM migration.
255
142
 
256
143
  ## Documentation
257
144
 
258
- Additional documentation is available in the repository:
259
-
260
- - [Admin API](https://github.com/RobotEby/parry-express-security-middleware/blob/main/docs/admin-api.md)
261
- - [Admin API authentication](https://github.com/RobotEby/parry-express-security-middleware/blob/main/docs/admin-api-auth.md)
262
- - [AWS Admin API authentication](https://github.com/RobotEby/parry-express-security-middleware/blob/main/docs/aws-admin-auth.md)
263
- - [Docker demo](https://github.com/RobotEby/parry-express-security-middleware/blob/main/docs/docker-demo.md)
264
- - [cURL guide](https://github.com/RobotEby/parry-express-security-middleware/blob/main/docs/curl-guide.md)
265
- - [AWS infrastructure notes](https://github.com/RobotEby/parry-express-security-middleware/blob/main/docs/aws-infra.md)
266
- - [CI/CD](https://github.com/RobotEby/parry-express-security-middleware/blob/main/docs/ci-cd.md)
267
- - [Release process](https://github.com/RobotEby/parry-express-security-middleware/blob/main/docs/release.md)
268
- - [Payload regression testing](https://github.com/RobotEby/parry-express-security-middleware/blob/main/docs/testing-payloads.md)
269
- - [Architecture](https://github.com/RobotEby/parry-express-security-middleware/blob/main/docs/architecture.md)
270
-
271
- The npm package keeps `docs/`, infrastructure, Docker demo files, and tests out of the published runtime package.
272
-
273
- ## Development
274
-
275
- ```bash
276
- npm ci
277
- npm test
278
- npm run test:fixtures
279
- npm run test:payload-regression
280
- npm run package:check
281
- npm pack --dry-run
282
- ```
283
-
284
- `npm test` uses local mocks and fake stores. It does not require Redis, AWS, Cloudflare, or external services.
285
-
286
- ## Roadmap
287
-
288
- Planned areas for future work:
145
+ - [Configuration](./docs/configuration.md)
146
+ - [Security model](./docs/security-model.md)
147
+ - [Admin API](./docs/admin-api.md)
148
+ - [Deployment](./docs/deployment.md)
149
+ - [Testing](./docs/testing.md)
150
+ - [Architecture](./docs/architecture.md)
151
+ - [Releasing](./docs/releasing.md)
152
+ - [OpenAPI contract](./docs/openapi/parry-admin-api.yaml)
153
+ - [Generated payload regression report](./docs/payload-regression-report.md)
289
154
 
290
- - Redis-backed event persistence
291
- - OpenTelemetry and Prometheus export
292
- - Express 4 compatibility matrix
293
- - Optional hashing/redaction for store keys
294
- - Additional detector tuning with benign counterexamples
295
- - Admin API hardening and deployment guides
155
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for the development workflow and
156
+ [SECURITY.md](./SECURITY.md) for vulnerability reporting.
296
157
 
297
158
  ## License
298
159
 
299
- MIT
160
+ [MIT](./LICENSE)
@@ -50,6 +50,9 @@ const DEFAULTS = {
50
50
  },
51
51
  trustProxyHeaders: false,
52
52
  trustedProxies: [],
53
+ headers: {
54
+ scan: ['user-agent', 'referer', 'x-forwarded-for', 'cookie'],
55
+ },
53
56
  debug: false,
54
57
 
55
58
  suspiciousThreshold: 5,
@@ -59,7 +62,7 @@ const DEFAULTS = {
59
62
 
60
63
  onThreat: null,
61
64
 
62
- maxObjectDepth: 5,
65
+ maxObjectDepth: 8,
63
66
  };
64
67
 
65
68
  module.exports = { DEFAULTS };