@roboteby/parry 1.1.1 → 2.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/CHANGELOG.md CHANGED
@@ -1,33 +1,64 @@
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
+ - Made `createParry` and `ParryOptions` the recommended API names while retaining
23
+ every existing public export and the deprecated `Parry_DDoS`/
24
+ `Parry_DDoSOptions` aliases.
25
+ - Reordered scanning so Request Shape runs first, structured guards inspect each
26
+ surface once, scalar leaves are serialized once, duplicate findings are
27
+ removed, and aggregate severity uses the most severe finding.
28
+ - Resolved trusted proxy chains from right to left and changed generated request
29
+ IDs to `req_${crypto.randomUUID()}`.
30
+ - Migrated tests to `node:test`/`node:assert`, ESLint 9 flat configuration, full
31
+ Prettier checks, Node 18/20/22/24 CI, OIDC-only npm publishing, and an optional
32
+ Terraform example under `infra/examples/aws`.
33
+ - Updated repository metadata and links to `RobotEby/parry`.
34
+
35
+ ### Security
36
+
37
+ - **Intentional breaking behavior:** `createParryAdminRouter` no longer permits
38
+ anonymous access by default. It fails during construction unless real auth is
39
+ configured or insecure access is explicitly selected outside production.
40
+ - In production, `allowInsecureAdminApi`, `auth.mode: "none"`, and legacy
41
+ `requireAuth: false` are always rejected. Empty tokens and invalid IP/CIDR
42
+ boundaries are rejected, and `verifyJwt: true` continues to fail explicitly.
43
+ - Removed stale monitor-only fixtures for Command Injection and SSRF because
44
+ Parry 1.x does not implement those detectors.
45
+
46
+ ## [1.1.1] - 2026-07-02
47
+
48
+ ### Changed
49
+
50
+ - Published `@roboteby/parry@1.1.1` to the stable `latest` npm dist-tag.
51
+
11
52
  ## [1.1.0-rc.1] - 2026-07-02
12
53
 
13
54
  ### Added
14
55
 
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.
56
+ - Published the first release candidate under the `rc` npm dist-tag.
57
+ - Added route policies, distributed rate limiting, brute-force protection,
58
+ Threat Events, metrics, the read-only Admin API, Docker demonstration, and AWS
59
+ reference infrastructure.
26
60
 
27
61
  ### Security
28
62
 
29
- - Documented npm publishing and supply-chain hardening with Trusted
30
- Publishing/OIDC.
31
63
  - 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.
64
+ replace CDN/WAF, load-balancer, or volumetric DDoS controls.
package/README.md CHANGED
@@ -1,299 +1,148 @@
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 5.
8
8
 
9
- ## Overview
9
+ Parry combines request-shape limits, heuristic SQL injection/XSS/NoSQL checks,
10
+ HTTP parameter pollution and prototype/path traversal guards with rate limiting,
11
+ route policies, brute-force protection, sanitized Threat Events, and an optional
12
+ read-only Admin API. It is CommonJS and has no mandatory Redis dependency.
10
13
 
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.
14
+ The stable npm release is `1.1.1` on `latest`. The earlier `1.1.0-rc.1` remains
15
+ available on the `rc` dist-tag; it is not the recommended installation.
12
16
 
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
17
+ ## Install
47
18
 
48
19
  ```bash
49
- npm install @roboteby/parry@rc
20
+ npm install @roboteby/parry
50
21
  ```
51
22
 
52
- Parry expects Express to be installed by your application.
53
-
54
- ## Release Candidate
55
-
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
23
+ Express `^5.2.1` is a peer dependency.
59
24
 
60
25
  ```js
61
26
  const express = require('express');
62
27
  const { createParry } = require('@roboteby/parry');
63
28
 
64
29
  const app = express();
30
+ app.use(express.json({ limit: '64kb' }));
65
31
 
66
- const parry = createParry({
67
- preset: 'recommended',
68
- });
69
-
70
- app.use(express.json());
32
+ const parry = createParry({ preset: 'recommended' });
71
33
  app.use(parry.middleware());
72
34
 
73
- app.get('/health', (_req, res) => {
74
- res.json({ ok: true });
75
- });
76
-
35
+ app.get('/health', (_req, res) => res.json({ ok: true }));
77
36
  app.listen(3000);
78
37
  ```
79
38
 
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:
39
+ Body parsers must run before Parry if request bodies should be inspected. Mount
40
+ Parry before the routes it protects.
83
41
 
84
- ```js
85
- const { Parry_DDoS } = require('@roboteby/parry');
86
-
87
- app.use(express.json());
88
- app.use(Parry_DDoS({ preset: 'recommended' }));
89
- ```
42
+ ## Security boundaries
90
43
 
91
- ## Presets
44
+ Parry is one application-layer control. It does not replace validation and
45
+ parameterized queries in the application, output encoding and CSP in the browser,
46
+ authentication/authorization, a reverse proxy, CDN/WAF, load balancer, or
47
+ volumetric L3/L4 DDoS protection.
92
48
 
93
- Parry supports conservative presets for common application-layer protection:
49
+ SQLi and XSS detection is heuristic. The patterns are intentionally bounded and
50
+ the Request Shape guard runs before heavier scans, but applications still need
51
+ tests for their own traffic and false-positive profile. A clean Parry decision is
52
+ not proof that input is safe for every downstream interpreter.
94
53
 
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.
54
+ When `MemoryStore` is used, counters and bans exist only inside one Node.js
55
+ process. `RedisStore` shares rate-limit and brute-force state across instances.
56
+ Threat Events, the in-memory event buffer, and metrics remain local to each
57
+ process unless the application exports them through `onEvent` or another
58
+ observability integration.
98
59
 
99
- Every option can still be configured explicitly. Prefer starting with `recommended`, reviewing logs and events, then tightening route policies where needed.
100
-
101
- ## Stores
102
-
103
- `MemoryStore` is the default store. It is suitable for tests, demos, local development, and single-process deployments.
104
-
105
- For distributed deployments, use `RedisStore` with a Redis client created by your application:
106
-
107
- ```js
108
- const { createClient } = require('redis');
109
- const { createParry, RedisStore } = require('@roboteby/parry');
110
-
111
- const redis = createClient({ url: process.env.REDIS_URL });
112
- await redis.connect();
113
-
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
- ```
124
-
125
- If your service runs behind multiple instances, containers, or load balancers, use a shared store. `MemoryStore` protects only the current Node.js process.
126
-
127
- ## Brute-Force Protection
128
-
129
- Route policies can protect sensitive authentication endpoints without making the global rate limit too aggressive:
130
-
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
- ```
60
+ See [the security model](./docs/security-model.md) and [configuration](./docs/configuration.md).
156
61
 
157
- Routes can also report authentication outcomes manually:
62
+ ## Public API
158
63
 
159
- ```js
160
- app.post('/login', async (req, res) => {
161
- const user = await authService.validate(req.body.email, req.body.password);
64
+ The four recommended stable exports are:
162
65
 
163
- if (!user) {
164
- req.parry?.recordAuthFailure('invalid_credentials');
165
- return res.status(200).json({ success: false });
166
- }
66
+ - `createParry(options)` — create a middleware instance and observability context;
67
+ - `createParryAdminRouter(parry, options)` — create the optional read-only Admin router;
68
+ - `MemoryStore` single-process state store;
69
+ - `RedisStore` — adapter for an application-owned Redis client.
167
70
 
168
- req.parry?.recordAuthSuccess();
169
- return res.json({ success: true });
170
- });
171
- ```
71
+ All existing exports remain available. `Parry_DDoS(options)` is deprecated but
72
+ kept as a compatibility wrapper around `createParry(options).middleware()`.
73
+ `Parry_DDoSOptions` remains a deprecated TypeScript alias for `ParryOptions`.
172
74
 
173
- ## Threat Events and Admin API
75
+ Advanced APIs have typed subpaths: `/core`, `/detectors`, `/stores`, `/policies`,
76
+ `/brute-force`, `/events`, `/observability`, and `/admin`.
174
77
 
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.
78
+ ## Admin API is fail-closed
176
79
 
177
- The optional Admin API is read-only and is never mounted automatically:
80
+ The Admin router now throws during construction when no authentication strategy
81
+ is configured:
178
82
 
179
83
  ```js
180
- const { createParry, createParryAdminRouter } = require('@roboteby/parry');
84
+ const { createParryAdminRouter } = require('@roboteby/parry');
181
85
 
182
- const parry = createParry({
183
- admin: {
184
- enabled: true,
86
+ app.use(
87
+ '/_parry',
88
+ createParryAdminRouter(parry, {
185
89
  auth: {
186
90
  mode: 'token',
187
91
  token: process.env.PARRY_ADMIN_TOKEN,
188
92
  },
189
- },
190
- });
191
-
192
- app.use(parry.middleware());
193
- app.use('/_parry', createParryAdminRouter(parry));
93
+ })
94
+ );
194
95
  ```
195
96
 
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`
97
+ `allowInsecureAdminApi: true`, `auth.mode: 'none'`, and the legacy
98
+ `requireAuth: false` alias are explicit local-development opt-ins. They warn and
99
+ are rejected whenever `NODE_ENV=production`, even if an override is present.
100
+ An empty token is invalid. External Cloudflare/ALB modes validate a trusted
101
+ boundary but do not pretend that decoding a JWT is cryptographic verification;
102
+ `verifyJwt: true` fails explicitly in this 1.x implementation.
204
103
 
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.
104
+ Full configuration and deployment patterns are in [Admin API](./docs/admin-api.md).
206
105
 
207
- ## Parry Security Console
106
+ ## Redis
208
107
 
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.
108
+ Create and connect the Redis client in the application; Parry never installs or
109
+ owns a Redis package implicitly.
210
110
 
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
111
+ ```js
112
+ const { createClient } = require('redis');
113
+ const { createParry, RedisStore } = require('@roboteby/parry');
225
114
 
226
- curl http://localhost:3000/_parry/health \
227
- -H "x-parry-admin-token: change-me"
115
+ const client = createClient({ url: process.env.REDIS_URL });
116
+ await client.connect();
228
117
 
229
- curl -X POST http://localhost:3000/echo \
230
- -H "Content-Type: application/json" \
231
- -d '{"message":"hello"}'
118
+ const parry = createParry({
119
+ store: new RedisStore({ client, prefix: 'parry' }),
120
+ storeFailureMode: 'fail-closed',
121
+ });
232
122
  ```
233
123
 
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.
245
-
246
- Production deployments should account for:
247
-
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.
124
+ ## Runtime support
253
125
 
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.
126
+ The 1.x package keeps `engines.node >=18` and CI covers Node 18, 20, 22, and 24.
127
+ Node 18 and 20 are legacy/EOL compatibility targets. Use Node 22 or 24 for new
128
+ production deployments. Raising the minimum to Node 22 is reserved for v2. See
129
+ the [official Node.js release schedule](https://nodejs.org/en/about/previous-releases).
255
130
 
256
131
  ## Documentation
257
132
 
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:
133
+ - [Configuration](./docs/configuration.md)
134
+ - [Security model](./docs/security-model.md)
135
+ - [Admin API](./docs/admin-api.md)
136
+ - [Testing](./docs/testing.md)
137
+ - [Deployment](./docs/deployment.md)
138
+ - [Architecture](./docs/architecture.md)
139
+ - [Releasing](./docs/releasing.md)
140
+ - [OpenAPI contract](./docs/openapi/parry-admin-api.yaml)
141
+ - [Generated payload regression report](./docs/payload-regression-report.md)
289
142
 
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
143
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for development workflow and
144
+ [SECURITY.md](./SECURITY.md) for vulnerability reporting.
296
145
 
297
146
  ## License
298
147
 
299
- MIT
148
+ [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 };