@stonyx/rest-server 0.2.1-alpha.16 → 0.2.1-alpha.18
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 +57 -0
- package/config/environment.js +6 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +7 -0
- package/dist/main.js.map +1 -1
- package/dist/request.d.ts.map +1 -1
- package/dist/request.js +6 -0
- package/dist/request.js.map +1 -1
- package/dist/route-matching.d.ts +35 -0
- package/dist/route-matching.d.ts.map +1 -0
- package/dist/route-matching.js +38 -0
- package/dist/route-matching.js.map +1 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -79,9 +79,66 @@ Configuration is read from `stonyx/config` under `restServer`:
|
|
|
79
79
|
| `origin` | **String \| Array** | `'*'` | CORS origin(s) allowed |
|
|
80
80
|
| `methods` | **String** | `'GET,POST,PATCH,PUT,DELETE'` | CORS allowed methods |
|
|
81
81
|
| `enableHealthCheck` | **Boolean** | `true` | Register `GET /health` endpoint (disable via `REST_HEALTH_CHECK_DISABLE=true`) |
|
|
82
|
+
| `caseSensitiveRoutes` | **Boolean** | `true` | Match route paths case-sensitively. Disable via `REST_CASE_SENSITIVE_ROUTES=false`. See [Case-Sensitive Routing](#case-sensitive-routing) -- **disabling this re-opens a security hole**. |
|
|
82
83
|
| `trustProxy` | **Boolean** | `false` | Trust reverse proxy headers (e.g. `X-Forwarded-Proto`). Enable via `REST_TRUST_PROXY=true` when running behind a load balancer such as AWS ALB/ELB to ensure correct protocol detection. |
|
|
83
84
|
| `statusMap` | **Object** | `{}` | Optional mapping of HTTP status codes to custom messages |
|
|
84
85
|
|
|
86
|
+
### Case-Sensitive Routing
|
|
87
|
+
|
|
88
|
+
Routes match **case-sensitively by default**. `GET /users` reaches a route mounted
|
|
89
|
+
at `/users`; `GET /Users` and `GET /users/ID` do not.
|
|
90
|
+
|
|
91
|
+
This is deliberate and security-relevant. Express matches case-insensitively by
|
|
92
|
+
default, which means any authorization written against the request URL can be
|
|
93
|
+
walked past by changing the case of the request:
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
GET /owners/angela -> 404 (correctly filtered)
|
|
97
|
+
GET /OwNeRs/angela -> 200 (full record)
|
|
98
|
+
DELETE /ANIMALS/22 -> 204 (record destroyed)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The consumer's predicate is stricter than the router that dispatched the
|
|
102
|
+
request, so the router hands the handler a request the predicate would have
|
|
103
|
+
rejected. Case-sensitive matching removes that asymmetry: the path a handler
|
|
104
|
+
sees can only ever be the exact registered casing.
|
|
105
|
+
|
|
106
|
+
**What this does not do.** Case-sensitive routing does not normalise path
|
|
107
|
+
*parameter values*. If your `auth()` hook rejects `params.id === 'restricted'`,
|
|
108
|
+
then `GET /private/RESTRICTED` still reaches the handler -- the router matched
|
|
109
|
+
the route correctly and `restricted` and `RESTRICTED` are different values.
|
|
110
|
+
Record ids are legitimately case-sensitive, so this is a comparison your
|
|
111
|
+
application owns. Compare param values with the same case-handling you use when
|
|
112
|
+
you look them up.
|
|
113
|
+
|
|
114
|
+
The framework also does not redirect or rewrite mixed-case requests to their
|
|
115
|
+
canonical casing. Whether `/Users` is a typo to forgive or an attack to reject
|
|
116
|
+
is an application policy decision, and encoding it here would mint another
|
|
117
|
+
variant of the bug above.
|
|
118
|
+
|
|
119
|
+
#### Opting out
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
REST_CASE_SENSITIVE_ROUTES=false
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**This restores the vulnerability described above** -- any URL-based
|
|
126
|
+
authorization in your application becomes bypassable by changing case. It
|
|
127
|
+
exists as a one-line remediation for an existing deployment, not as a
|
|
128
|
+
configuration to run on.
|
|
129
|
+
|
|
130
|
+
You need it if clients call your endpoints with casing that does not match the
|
|
131
|
+
mount path. Mount paths come from filenames, so this is not hypothetical:
|
|
132
|
+
|
|
133
|
+
- with `camelCaseRoutes` truthy, `phone-number.ts` mounts at `/phoneNumber`, and
|
|
134
|
+
`GET /phonenumber` now returns 404
|
|
135
|
+
- with `camelCaseRoutes` falsy, filenames are used verbatim, so
|
|
136
|
+
`Users.ts` mounts at `/Users` and `GET /users` now returns 404
|
|
137
|
+
|
|
138
|
+
A request that stops matching returns express's default `404 Cannot GET /x` with
|
|
139
|
+
no log line and no stack, so it looks like a deploy that dropped a route. Set
|
|
140
|
+
the flag to restore service, then fix the client's casing and remove the flag.
|
|
141
|
+
|
|
85
142
|
### Running Behind a Load Balancer
|
|
86
143
|
|
|
87
144
|
When your application runs behind a reverse proxy or load balancer (e.g. AWS ALB/ELB), the load balancer terminates SSL and forwards requests to your server over HTTP internally. This means Express sees `http` as the protocol even though the original client request used `https`.
|
package/config/environment.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const {
|
|
2
|
+
REST_CASE_SENSITIVE_ROUTES,
|
|
2
3
|
REST_CORS_ORIGIN,
|
|
3
4
|
REST_CORS_METHODS,
|
|
4
5
|
REST_HEALTH_CHECK_DISABLE,
|
|
@@ -8,6 +9,11 @@ const {
|
|
|
8
9
|
} = process.env;
|
|
9
10
|
|
|
10
11
|
const config = {
|
|
12
|
+
// Secure by default: routes match case-sensitively so a consumer's
|
|
13
|
+
// URL-based authorization cannot be walked past by changing case
|
|
14
|
+
// (abofs/stonyx-rest-server#47). Opt out with REST_CASE_SENSITIVE_ROUTES=false
|
|
15
|
+
// only as a temporary remediation for a client that relies on loose casing.
|
|
16
|
+
caseSensitiveRoutes: REST_CASE_SENSITIVE_ROUTES !== 'false',
|
|
11
17
|
enableHealthCheck: REST_HEALTH_CHECK_DISABLE !== 'true',
|
|
12
18
|
origin: REST_CORS_ORIGIN ?? '*',
|
|
13
19
|
methods: REST_CORS_METHODS ?? 'GET,POST,PATCH,PUT,DELETE',
|
package/dist/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAiBA,OAAgB,EAAE,KAAK,OAAO,EAAoE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAiBA,OAAgB,EAAE,KAAK,OAAO,EAAoE,MAAM,SAAS,CAAC;AAKlH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,CAAC,OAAO,OAAO,UAAU;IAC7B,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC;IAE5B,GAAG,EAAG,OAAO,CAAC;IACd,MAAM,EAAG,MAAM,CAAC;;IAgBhB,MAAM,CAAC,KAAK,IAAI,IAAI;IAQd,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAerB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAelC,qBAAqB,IAAI,IAAI;IAW7B,UAAU,CAAC,iBAAiB,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI;CAarG"}
|
package/dist/main.js
CHANGED
|
@@ -18,6 +18,7 @@ import express from 'express';
|
|
|
18
18
|
import config from 'stonyx/config';
|
|
19
19
|
import log from 'stonyx/log';
|
|
20
20
|
import { forEachFileImport } from '@stonyx/utils/file';
|
|
21
|
+
import applyRouteMatching from './route-matching.js';
|
|
21
22
|
export { default as Request } from './request.js';
|
|
22
23
|
export default class RestServer {
|
|
23
24
|
static instance;
|
|
@@ -28,6 +29,12 @@ export default class RestServer {
|
|
|
28
29
|
return RestServer.instance;
|
|
29
30
|
RestServer.instance = this;
|
|
30
31
|
this.api = express();
|
|
32
|
+
// Closes the mount segment (/PUBLIC/...) for abofs/stonyx-rest-server#47.
|
|
33
|
+
// Must stay in the constructor: the router is materialised lazily on first
|
|
34
|
+
// route registration, so applying this after setupRouter() is silently
|
|
35
|
+
// ineffective. The matching call in Request's constructor is what closes
|
|
36
|
+
// sub-paths -- see src/route-matching.ts for why both are required.
|
|
37
|
+
applyRouteMatching(this.api);
|
|
31
38
|
}
|
|
32
39
|
static close() {
|
|
33
40
|
if (!RestServer.instance)
|
package/dist/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,OAA2F,MAAM,SAAS,CAAC;AAClH,OAAO,MAAM,MAAM,eAAe,CAAC;AACnC,OAAO,GAAG,MAAM,YAAY,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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"}
|
package/dist/request.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,KAAK,OAAO,IAAI,cAAc,EAAE,KAAK,QAAQ,IAAI,eAAe,EAAE,KAAK,OAAO,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,KAAK,OAAO,IAAI,cAAc,EAAE,KAAK,QAAQ,IAAI,eAAe,EAAE,KAAK,OAAO,EAAE,MAAM,SAAS,CAAC;AAOlH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACnD,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AACtG,MAAM,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,KAAK,MAAM,GAAG,SAAS,CAAC;AAC3F,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,EAAE,CAAC,CAAC,CAAC;AAE9F,MAAM,CAAC,OAAO,OAAO,OAAO;IAC1B,MAAM,CAAC,SAAS,SAAmB;IAEnC,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,cAAc,GAAG,YAAY;IASlD,MAAM,CAAC,kBAAkB,CAAC,GAAG,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAWrE,eAAe,EAAE,OAAO,CAAC;IACzB,QAAQ,EAAG,aAAa,CAAC;IACjB,IAAI,CAAC,EAAE,WAAW,CAAC;;IAe3B,aAAa,IAAI,IAAI;CAqDtB"}
|
package/dist/request.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
2
|
import config from 'stonyx/config';
|
|
3
3
|
import { makeArray } from '@stonyx/utils/object';
|
|
4
|
+
import applyRouteMatching from './route-matching.js';
|
|
4
5
|
const METHODS = new Set(['get', 'post', 'put', 'delete', 'patch']);
|
|
5
6
|
export default class Request {
|
|
6
7
|
static stateProp = '__stonyxState';
|
|
@@ -27,6 +28,11 @@ export default class Request {
|
|
|
27
28
|
constructor() {
|
|
28
29
|
const api = express();
|
|
29
30
|
api.disable('x-powered-by');
|
|
31
|
+
// Closes sub-paths (/public/SUCCESS) for abofs/stonyx-rest-server#47.
|
|
32
|
+
// Must stay in the constructor: registerCalls() materialises this router,
|
|
33
|
+
// and a set applied afterwards has no effect. The parent app's setting
|
|
34
|
+
// does not reach here -- see src/route-matching.ts.
|
|
35
|
+
applyRouteMatching(api);
|
|
30
36
|
this.expressInstance = api;
|
|
31
37
|
}
|
|
32
38
|
registerCalls() {
|
package/dist/request.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request.js","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"AAAA,OAAO,OAA2F,MAAM,SAAS,CAAC;AAClH,OAAO,MAAM,MAAM,eAAe,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"request.js","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Express } from 'express';
|
|
2
|
+
/**
|
|
3
|
+
* Applies this module's route-matching settings to an express app.
|
|
4
|
+
*
|
|
5
|
+
* Called from BOTH express construction sites (abofs/stonyx-rest-server#47):
|
|
6
|
+
* `RestServer`'s constructor closes the mount segment (`/PUBLIC/...`), and
|
|
7
|
+
* `Request`'s constructor closes sub-paths (`/public/SUCCESS`). Neither alone
|
|
8
|
+
* is sufficient -- settings are inherited on mount, but `mountRoute()` calls
|
|
9
|
+
* `registerCalls()` before `api.use()`, so each child router is already built
|
|
10
|
+
* by the time the parent's setting could reach it.
|
|
11
|
+
*
|
|
12
|
+
* Both callers invoke this from a constructor, and must keep doing so: express
|
|
13
|
+
* materialises a router lazily on first route registration, and a setting
|
|
14
|
+
* applied afterwards is silently ineffective -- no throw, no warning.
|
|
15
|
+
*
|
|
16
|
+
* The guard is `!== false`, not a plain truthy check, and that polarity is
|
|
17
|
+
* load-bearing. `trustProxy` and `enableHealthCheck` default to the falsy
|
|
18
|
+
* direction, so a missing key fails safe for them. This flag defaults to the
|
|
19
|
+
* truthy direction, so `if (config.restServer?.caseSensitiveRoutes)` would
|
|
20
|
+
* silently fail OPEN for a consumer whose shipped config predates the key.
|
|
21
|
+
*
|
|
22
|
+
* It lives here, in one place, rather than being written out at each call
|
|
23
|
+
* site, so that a single test can anchor it. The invariant is duplicated the
|
|
24
|
+
* moment the expression is: `test/unit/request-test.ts` AC6 reaches this
|
|
25
|
+
* function through `Request`, which means the same assertion now also covers
|
|
26
|
+
* the `RestServer` half. Two copies of the predicate left the parent's copy
|
|
27
|
+
* free to drift -- inverting it, or dropping the condition entirely, kept the
|
|
28
|
+
* suite green.
|
|
29
|
+
*
|
|
30
|
+
* Note `express({ caseSensitive: true })` does NOT work: express 5's
|
|
31
|
+
* `createApplication()` takes zero arguments and forwards nothing. The app
|
|
32
|
+
* setting is the only mechanism.
|
|
33
|
+
*/
|
|
34
|
+
export default function applyRouteMatching(api: Express): void;
|
|
35
|
+
//# sourceMappingURL=route-matching.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-matching.d.ts","sourceRoot":"","sources":["../src/route-matching.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,CAAC,OAAO,UAAU,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAE7D"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import config from 'stonyx/config';
|
|
2
|
+
/**
|
|
3
|
+
* Applies this module's route-matching settings to an express app.
|
|
4
|
+
*
|
|
5
|
+
* Called from BOTH express construction sites (abofs/stonyx-rest-server#47):
|
|
6
|
+
* `RestServer`'s constructor closes the mount segment (`/PUBLIC/...`), and
|
|
7
|
+
* `Request`'s constructor closes sub-paths (`/public/SUCCESS`). Neither alone
|
|
8
|
+
* is sufficient -- settings are inherited on mount, but `mountRoute()` calls
|
|
9
|
+
* `registerCalls()` before `api.use()`, so each child router is already built
|
|
10
|
+
* by the time the parent's setting could reach it.
|
|
11
|
+
*
|
|
12
|
+
* Both callers invoke this from a constructor, and must keep doing so: express
|
|
13
|
+
* materialises a router lazily on first route registration, and a setting
|
|
14
|
+
* applied afterwards is silently ineffective -- no throw, no warning.
|
|
15
|
+
*
|
|
16
|
+
* The guard is `!== false`, not a plain truthy check, and that polarity is
|
|
17
|
+
* load-bearing. `trustProxy` and `enableHealthCheck` default to the falsy
|
|
18
|
+
* direction, so a missing key fails safe for them. This flag defaults to the
|
|
19
|
+
* truthy direction, so `if (config.restServer?.caseSensitiveRoutes)` would
|
|
20
|
+
* silently fail OPEN for a consumer whose shipped config predates the key.
|
|
21
|
+
*
|
|
22
|
+
* It lives here, in one place, rather than being written out at each call
|
|
23
|
+
* site, so that a single test can anchor it. The invariant is duplicated the
|
|
24
|
+
* moment the expression is: `test/unit/request-test.ts` AC6 reaches this
|
|
25
|
+
* function through `Request`, which means the same assertion now also covers
|
|
26
|
+
* the `RestServer` half. Two copies of the predicate left the parent's copy
|
|
27
|
+
* free to drift -- inverting it, or dropping the condition entirely, kept the
|
|
28
|
+
* suite green.
|
|
29
|
+
*
|
|
30
|
+
* Note `express({ caseSensitive: true })` does NOT work: express 5's
|
|
31
|
+
* `createApplication()` takes zero arguments and forwards nothing. The app
|
|
32
|
+
* setting is the only mechanism.
|
|
33
|
+
*/
|
|
34
|
+
export default function applyRouteMatching(api) {
|
|
35
|
+
if (config.restServer?.caseSensitiveRoutes !== false)
|
|
36
|
+
api.set('case sensitive routing', true);
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=route-matching.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-matching.js","sourceRoot":"","sources":["../src/route-matching.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,eAAe,CAAC;AAEnC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,CAAC,OAAO,UAAU,kBAAkB,CAAC,GAAY;IACrD,IAAI,MAAM,CAAC,UAAU,EAAE,mBAAmB,KAAK,KAAK;QAAE,GAAG,CAAC,GAAG,CAAC,wBAAwB,EAAE,IAAI,CAAC,CAAC;AAChG,CAAC"}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"stonyx-async",
|
|
5
5
|
"stonyx-module"
|
|
6
6
|
],
|
|
7
|
-
"version": "0.2.1-alpha.
|
|
7
|
+
"version": "0.2.1-alpha.18",
|
|
8
8
|
"description": "Rest Server Module for Stonyx Framework",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
@@ -36,10 +36,10 @@
|
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"cors": "^2.8.5",
|
|
38
38
|
"express": "^5.1.0",
|
|
39
|
-
"stonyx": "0.2.3-beta.
|
|
39
|
+
"stonyx": "0.2.3-beta.77"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@stonyx/utils": "0.2.3-beta.
|
|
42
|
+
"@stonyx/utils": "0.2.3-beta.26",
|
|
43
43
|
"@types/cors": "^2.8.17",
|
|
44
44
|
"@types/express": "^5.0.6",
|
|
45
45
|
"@types/node": "^25.5.2",
|