@stonyx/rest-server 0.2.1-beta.90 → 0.2.1-beta.91

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 CHANGED
@@ -79,59 +79,82 @@ 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
+ | `caseSensitiveRoutes` | **Boolean** | `true` | Match route paths case-sensitively. Disable via `REST_CASE_SENSITIVE_ROUTES=false`. See [Route Matching Strictness](#route-matching-strictness) — **disabling this re-opens a security hole**. |
83
+ | `strictRoutes` | **Boolean** | `true` | Match route paths strictly, so a trailing slash does not match a route registered without one. Disable via `REST_STRICT_ROUTES=false`. See [Route Matching Strictness](#route-matching-strictness) — **disabling this re-opens a security hole**, and note `GET /health/` now 404s. |
83
84
  | `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. |
84
85
  | `statusMap` | **Object** | `{}` | Optional mapping of HTTP status codes to custom messages |
85
86
 
86
- ### Case-Sensitive Routing
87
+ ### Route Matching Strictness
87
88
 
88
- Routes match **case-sensitively by default**. `GET /users` reaches a route
89
- mounted at `/users`; `GET /Users` does not reach that mount, and
90
- `GET /users/Success` does not reach a `/success` handler registered inside it.
89
+ Routes match **case-sensitively and strictly by default**. Two settings, both
90
+ on, both applied at both express construction sites:
91
91
 
92
- Read [What this does not do](#what-this-does-not-do) before you rely on that
93
- sentence. Two things it does not say: "does not reach the handler" is not the
94
- same as "404", and casing is only one of the two ways express matches more
95
- loosely than the authorization predicates written against it.
92
+ | axis | setting | example that no longer matches |
93
+ |---|---|---|
94
+ | casing | `case sensitive routing` | `GET /users/Success` -> does not reach `/success` |
95
+ | trailing slash | `strict routing` | `GET /users/success/` -> does not reach `/success` |
96
96
 
97
- This is deliberate and security-relevant. Express matches case-insensitively by
98
- default, which means any authorization written against the request URL can be
99
- walked past by changing the case of the request:
97
+ Read [What this does not do](#what-this-does-not-do) and
98
+ [Upgrading](#upgrading-behaviour-changes) before you rely on that. Two things
99
+ the table does not say: "does not reach the handler" is not the same as "404",
100
+ and one edge of the trailing-slash axis is **not** closed and cannot be.
101
+
102
+ This is deliberate and security-relevant. Express matches both case-insensitively
103
+ and slash-insensitively by default, which means any authorization written
104
+ against the request URL can be walked past by changing the case of the request,
105
+ or by appending one character:
100
106
 
101
107
  ```
102
- GET /owners/angela -> 404 (correctly filtered)
103
- GET /OwNeRs/angela -> 200 (full record)
104
- DELETE /ANIMALS/22 -> 204 (record destroyed)
108
+ GET /owners/angela -> 404 (correctly filtered)
109
+ GET /OwNeRs/angela -> 200 (full record) <- closed by case sensitive routing
110
+ GET /owners/angela/ -> 200 (full record) <- closed by strict routing
111
+ DELETE /ANIMALS/22 -> 204 (record destroyed)
112
+ DELETE /animals/22/ -> 204 (record destroyed)
105
113
  ```
106
114
 
107
115
  The consumer's predicate is stricter than the router that dispatched the
108
116
  request, so the router hands the handler a request the predicate would have
109
- rejected. Case-sensitive matching closes the **casing** half of that asymmetry:
110
- the path a handler sees can only ever be the exact registered casing.
111
-
112
- It does not close the asymmetry itself. Express exposes `case sensitive
113
- routing` and `strict routing` as a pair of loose-by-default router settings and
114
- this change sets only the first, so the identical bypass is still reachable by
115
- appending a slash. Measured on this release against this repo's own fixture:
117
+ rejected. Measured against this repo's own fixture, before and after:
116
118
 
117
119
  ```
118
- GET /private/failure -> 505 (auth hook fires, request blocked)
119
- GET /private/failure/ -> 200 (auth hook never fires, handler runs)
120
+ before after
121
+ GET /private/failure 505 505 (auth hook fires, request blocked)
122
+ GET /private/failure/ 200 404 (auth hook never fired; now a miss)
123
+ GET /private/FAILURE 200 200 (absorbed by /:id — see below)
120
124
  ```
121
125
 
122
- That is the same defect, one character instead of a case shift translated to
123
- the example above, `DELETE /animals/22` is filtered and `DELETE /animals/22/`
124
- destroys the record. It is tracked as
125
- [#50](https://github.com/abofs/stonyx-rest-server/issues/50) and is not fixed
126
- here; it is a second consumer-visible behaviour change that needs its own flag
127
- and its own release note.
128
-
129
- **So do not drop a URL-normalizing defence you already have on the strength of
130
- this section.** If your authorization compares `req.path` or `req.originalUrl`,
131
- keep whatever normalization you have until #50 ships.
126
+ For a handler that authorizes on `req.path`, the path it sees can now only ever
127
+ be the exact registered spelling, in the exact registered casing, with no
128
+ trailing slash. That closes [#47](https://github.com/abofs/stonyx-rest-server/issues/47)
129
+ and [#50](https://github.com/abofs/stonyx-rest-server/issues/50).
132
130
 
133
131
  #### What this does not do
134
132
 
133
+ **It does not close the trailing slash on a mount root, and no setting can.**
134
+ This is the one edge that remains open, so do not read the section above as
135
+ closing the class outright:
136
+
137
+ ```
138
+ GET /public -> req.path '/' req.originalUrl '/public'
139
+ GET /public/ -> req.path '/' req.originalUrl '/public/'
140
+ ```
141
+
142
+ Express's router applies mount-prefix matching with `strict: false`
143
+ unconditionally (`router@2.2.0`; the file-and-line citation is in
144
+ [`docs/project-structure.md`](docs/project-structure.md) § *Strict routing
145
+ (#50)*), so both forms reach the mounted route class and both arrive with
146
+ `req.path === '/'`. A hook authorizing on `req.path` cannot tell them apart, so
147
+ for that hook there is no asymmetry to exploit. **A hook comparing
148
+ `req.originalUrl` still sees two different strings, and `strictRoutes` does not
149
+ change that.** If your authorization compares `req.originalUrl` rather than
150
+ `req.path`, keep whatever URL normalization you have.
151
+
152
+ No *setting* closes this, but the module can:
153
+ [#54](https://github.com/abofs/stonyx-rest-server/issues/54) tracks closing it
154
+ with a canonical-path check ahead of the `auth` hook. Until that ships, an
155
+ `originalUrl` hook is bypassed by one character — `GET /admin` denied,
156
+ `GET /admin/` reaching the route class's index handler.
157
+
135
158
  **It does not normalize path *parameter values*.** If your `auth()` hook rejects
136
159
  `params.id === 'restricted'`, then `GET /private/RESTRICTED` still reaches the
137
160
  handler — the router matched the route correctly, and `restricted` and
@@ -148,36 +171,64 @@ repo's AC5 asserts exactly that. A class exposing `/orders/summary` alongside
148
171
  database lookup. The param route's own `auth()` hook still runs, so this is an
149
172
  expectation defect rather than a bypass — but plan for a reroute, not a 404.
150
173
 
151
- **It does not cover trailing slashes.** See
152
- [#50](https://github.com/abofs/stonyx-rest-server/issues/50) above.
174
+ Note the two axes differ here. A *trailing slash* is not absorbed by `/:id`,
175
+ because `/:id` is equally strict: `GET /private/failure/` misses `/failure` and
176
+ misses `/:id`, and is a true 404.
177
+
178
+ **It does not redirect or rewrite** mixed-case or trailing-slash requests to
179
+ their canonical form. Whether `/Users` is a typo to forgive or an attack to
180
+ reject is an application policy decision, and encoding it here would mint
181
+ another variant of the bug above.
153
182
 
154
- **It does not redirect or rewrite** mixed-case requests to their canonical
155
- casing. Whether `/Users` is a typo to forgive or an attack to reject is an
156
- application policy decision, and encoding it here would mint another variant of
157
- the bug above.
183
+ #### Upgrading: behaviour changes
184
+
185
+ Both settings change which requests match, so both are consumer-visible.
186
+
187
+ **`GET /health/` now returns 404.** `GET /health` is unaffected. This is the
188
+ change most likely to page someone, and it is an **availability** problem rather
189
+ than a 404 you will read about in a log: if a Kubernetes liveness probe, an ELB
190
+ target-group health check or an uptime monitor is pointed at the trailing-slash
191
+ form, it starts failing and the deployment gets marked unhealthy and cycled.
192
+ This module emits no request logging, so the only symptom is the probe going
193
+ red. **Check your probe URLs before upgrading.**
194
+
195
+ Also affected:
196
+
197
+ - **Param routes.** `/resource/:id/` no longer matches. Any client calling
198
+ `/private/restricted/` gets a 404 where it previously got the param route.
199
+ - **Trailing-slash-normalizing proxies.** nginx `try_files`/`rewrite`, Apache
200
+ `DirectorySlash On` and some CDN edge rules append a slash; behind one of
201
+ those, every route stops matching at once.
202
+ - **Mount paths from filenames.** With `camelCaseRoutes` truthy, `phone-number.ts`
203
+ mounts at `/phoneNumber`, so `GET /phonenumber` returns 404; with it falsy,
204
+ `Users.ts` mounts at `/Users`, so `GET /users` returns 404.
205
+
206
+ A request that stops matching returns express's default `404 Cannot GET /x` with
207
+ no log line and no stack, so it looks like a deploy that dropped a route.
158
208
 
159
209
  #### Opting out
160
210
 
211
+ Two separate flags, one per axis:
212
+
161
213
  ```bash
162
- REST_CASE_SENSITIVE_ROUTES=false
214
+ REST_CASE_SENSITIVE_ROUTES=false # restores case-insensitive matching (#47)
215
+ REST_STRICT_ROUTES=false # restores trailing-slash tolerance (#50)
163
216
  ```
164
217
 
165
- **This restores the vulnerability described above** any URL-based
166
- authorization in your application becomes bypassable by changing case. It
167
- exists as a one-line remediation for an existing deployment, not as a
168
- configuration to run on.
218
+ or equivalently `restServer: { caseSensitiveRoutes: false, strictRoutes: false }`.
169
219
 
170
- You need it if clients call your endpoints with casing that does not match the
171
- mount path. Mount paths come from filenames, so this is not hypothetical:
220
+ **They are deliberately separate keys, and neither implies the other.** Slash
221
+ tolerance is a legitimate need a health-check URL you cannot change today is
222
+ the common case. Casing tolerance almost never is. Folding them into one flag
223
+ would force anyone who needs the first to accept the second, which is why a
224
+ consumer who took the `#47` opt-out still has to set `REST_STRICT_ROUTES=false`
225
+ separately to keep trailing slashes working.
172
226
 
173
- - with `camelCaseRoutes` truthy, `phone-number.ts` mounts at `/phoneNumber`, and
174
- `GET /phonenumber` now returns 404
175
- - with `camelCaseRoutes` falsy, filenames are used verbatim, so
176
- `Users.ts` mounts at `/Users` and `GET /users` now returns 404
177
-
178
- A request that stops matching returns express's default `404 Cannot GET /x` with
179
- no log line and no stack, so it looks like a deploy that dropped a route. Set
180
- the flag to restore service, then fix the client's casing and remove the flag.
227
+ **Each flag restores the corresponding vulnerability described above** — the
228
+ URL-based authorization in your application becomes bypassable along that axis
229
+ again. They exist as one-line remediations for an existing deployment, not as a
230
+ configuration to run on. Set the flag to restore service, then fix the client
231
+ and remove the flag.
181
232
 
182
233
  ### Running Behind a Load Balancer
183
234
 
@@ -5,6 +5,7 @@ const {
5
5
  REST_HEALTH_CHECK_DISABLE,
6
6
  REST_PORT,
7
7
  REST_REQUEST_PATH,
8
+ REST_STRICT_ROUTES,
8
9
  REST_TRUST_PROXY
9
10
  } = process.env;
10
11
 
@@ -31,6 +32,37 @@ const config = {
31
32
  // the subprocess-based env isolation this repo does not yet have; any fix
32
33
  // must keep a live assertion on this default.
33
34
  caseSensitiveRoutes: REST_CASE_SENSITIVE_ROUTES !== 'false',
35
+
36
+ // Secure by default, same polarity and same reasoning as caseSensitiveRoutes
37
+ // above: routes match strictly, so a trailing slash cannot walk past a
38
+ // consumer's URL-based authorization (abofs/stonyx-rest-server#50).
39
+ //
40
+ // BEHAVIOUR CHANGE for consumers upgrading: `/health/` now returns 404, and
41
+ // param routes like `/resource/:id/` no longer match. Opt out with
42
+ // REST_STRICT_ROUTES=false only as a temporary remediation. It is a separate
43
+ // key from REST_CASE_SENSITIVE_ROUTES on purpose -- opting out of slash
44
+ // strictness must not silently re-open #47's case bypass.
45
+ //
46
+ // DELIBERATELY NOT PINNED in test/config/environment.ts -- do not "fix" this
47
+ // as part of abofs/stonyx-rest-server#43. Same trap as the key above, and now
48
+ // measured for both: pin `strictRoutes: true` in test/config/environment.ts
49
+ // AND invert this line to `=== 'true'`, and the suite reports 31 pass /
50
+ // 0 fail. A naive pin makes an insecure published default completely
51
+ // invisible to a green suite.
52
+ //
53
+ // Unpinned, inverting this line alone turns #50's AC1 and AC2 red (29/2).
54
+ // AC3 stays GREEN under that mutation, because AC3 sets `strictRoutes` on the
55
+ // config object directly and so guards src/route-matching.ts's READ rather
56
+ // than this default -- the two assertions cover different halves and neither
57
+ // subsumes the other.
58
+ //
59
+ // The cost is that the suite is ambient-sensitive here
60
+ // (`REST_STRICT_ROUTES=false pnpm test` => 29 pass / 2 fail), but it fails
61
+ // LOUDLY, so there is no false green. Closing #43 for either key needs
62
+ // subprocess-based env isolation this repo does not have; any fix must keep a
63
+ // live assertion on this default.
64
+ strictRoutes: REST_STRICT_ROUTES !== 'false',
65
+
34
66
  enableHealthCheck: REST_HEALTH_CHECK_DISABLE !== 'true',
35
67
  origin: REST_CORS_ORIGIN ?? '*',
36
68
  methods: REST_CORS_METHODS ?? 'GET,POST,PATCH,PUT,DELETE',
@@ -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;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"}
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;;IAwBhB,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
@@ -29,11 +29,19 @@ export default class RestServer {
29
29
  return RestServer.instance;
30
30
  RestServer.instance = this;
31
31
  this.api = express();
32
- // Closes the mount segment (/PUBLIC/...) for abofs/stonyx-rest-server#47.
32
+ // Applies BOTH route-matching settings: case sensitive routing
33
+ // (abofs/stonyx-rest-server#47) and strict routing (#50). The two do not
34
+ // have the same role at this site:
35
+ // - #47: this call closes the mount segment (/PUBLIC/...). The matching
36
+ // call in Request's constructor closes sub-paths; both are required.
37
+ // - #50: this call closes exactly /health/, the only route registered
38
+ // directly on this app. It has NO security role for #50 -- do not
39
+ // describe it as having one. Router.prototype.use hardcodes
40
+ // `strict: false`, so mount segments are strict-immune, and the call in
41
+ // Request's constructor closes the trailing-slash bypass on its own.
33
42
  // Must stay in the constructor: the router is materialized lazily on first
34
43
  // 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.
44
+ // ineffective. See src/route-matching.ts for the per-site split.
37
45
  applyRouteMatching(this.api);
38
46
  }
39
47
  static close() {
package/dist/main.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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"}
@@ -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;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"}
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;;IAmB3B,aAAa,IAAI,IAAI;CAqDtB"}
package/dist/request.js CHANGED
@@ -28,10 +28,14 @@ export default class Request {
28
28
  constructor() {
29
29
  const api = express();
30
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() materializes 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.
31
+ // Applies BOTH route-matching settings: case sensitive routing
32
+ // (abofs/stonyx-rest-server#47) and strict routing (#50). For #47 this
33
+ // call closes sub-paths (/public/SUCCESS) and the parent's call closes the
34
+ // mount segment; for #50 THIS call closes the entire trailing-slash
35
+ // authorization bypass on its own, and the parent's call has no security
36
+ // role. Must stay in the constructor: registerCalls() materializes this
37
+ // router, and a set applied afterwards has no effect. The parent app's
38
+ // setting does not reach here -- see src/route-matching.ts.
35
39
  applyRouteMatching(api);
36
40
  this.expressInstance = api;
37
41
  }
@@ -1 +1 @@
1
- {"version":3,"file":"request.js","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"request.js","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"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"}
@@ -29,7 +29,62 @@ import type { Express } from 'express';
29
29
  *
30
30
  * Note `express({ caseSensitive: true })` does NOT work: express 5's
31
31
  * `createApplication()` takes zero arguments and forwards nothing. The app
32
- * setting is the only mechanism.
32
+ * setting is the only mechanism. The same is true of `strict`.
33
+ *
34
+ * ---
35
+ *
36
+ * `strict routing` (abofs/stonyx-rest-server#50) closes the same class of
37
+ * authorization bypass for a TRAILING SLASH: `GET /private/failure` was denied
38
+ * by a consumer's auth hook while `GET /private/failure/` reached the guarded
39
+ * handler, because the hook compares `req.path` against `/failure`.
40
+ *
41
+ * It is a SEPARATE key from `caseSensitiveRoutes`, not a rename and not a
42
+ * reuse. Coupling them would force any consumer who legitimately needs
43
+ * trailing-slash tolerance -- a load balancer probing `/health/`, a
44
+ * slash-normalizing proxy -- to re-open #47's case bypass to get it. Case
45
+ * insensitivity is almost never intentional; slash tolerance frequently is.
46
+ *
47
+ * The two settings do NOT share the #47 split above, and this is the one thing
48
+ * not to carry across from that fix. For `strict routing`:
49
+ *
50
+ * - The CHILD site (Request's constructor) closes the entire security
51
+ * defect on its own. The parent site does nothing for it.
52
+ * - The PARENT site (RestServer's constructor) closes exactly one thing in
53
+ * this repo: `/health/`, the only route registered directly on the parent
54
+ * app. It has no security role here; do not describe it as having one.
55
+ *
56
+ * The cause is concrete: `Router.prototype.use` hardcodes `strict: false` (and
57
+ * `end: false`), so mount segments are structurally strict-immune. That is the
58
+ * OPPOSITE of `sensitive`, which `use()` DOES forward, and which is why #47's
59
+ * parent site closed `/PUBLIC/...`. The version-pinned file-and-line citation
60
+ * for that upstream behaviour is deliberately kept in ONE place --
61
+ * `docs/project-structure.md`, section "Strict routing (#50)" -- so a router
62
+ * upgrade invalidates one line rather than five. Both sites still get both
63
+ * settings (they share this function), but the justification differs and the
64
+ * tests are built on the measured split, not on the analogy.
65
+ *
66
+ * Consequence worth stating so nobody expects this setting to cover it: the
67
+ * mount-segment trailing slash (`/public/`) cannot be closed by an express
68
+ * SETTING. For both `/public` and `/public/` the mounted sub-app receives
69
+ * `req.path === '/'`, so a `req.path` auth hook sees no difference, and a test
70
+ * asserting `/public/` -> 404 could never pass.
71
+ *
72
+ * That is NOT the same as saying the edge is harmless or unclosable.
73
+ * `req.originalUrl` does differ, and a hook authorizing on it is bypassed by
74
+ * one character -- measured: `GET /admin` -> 401, `GET /admin/` -> 200 with the
75
+ * guarded handler running unauthenticated. That is a live bypass of the same
76
+ * class as #47 and #50, and it IS closable by this module: a canonical-path
77
+ * check ahead of the `auth` call in `Request.registerCalls()`, or opt-in
78
+ * normalizing middleware. It is tracked as abofs/stonyx-rest-server#54. Do not
79
+ * close it here, and do not read this note as saying it cannot be closed.
80
+ *
81
+ * Both guards are `!== false` for the same reason: these flags default to the
82
+ * truthy direction, so a truthy check fails OPEN for a consumer whose shipped
83
+ * config predates the key. Both are also asserted at the unit tier for BOTH
84
+ * failure shapes -- key present-and-`undefined` and key absent as an own
85
+ * property -- in `test/unit/request-test.ts` (#47's AC6, #50's AC3). The
86
+ * integration tier cannot see either: with the shipped default `true`, a
87
+ * fail-open guard leaves every integration assertion green.
33
88
  */
34
89
  export default function applyRouteMatching(api: Express): void;
35
90
  //# sourceMappingURL=route-matching.d.ts.map
@@ -1 +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"}
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsFG;AACH,MAAM,CAAC,OAAO,UAAU,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAG7D"}
@@ -29,10 +29,67 @@ import config from 'stonyx/config';
29
29
  *
30
30
  * Note `express({ caseSensitive: true })` does NOT work: express 5's
31
31
  * `createApplication()` takes zero arguments and forwards nothing. The app
32
- * setting is the only mechanism.
32
+ * setting is the only mechanism. The same is true of `strict`.
33
+ *
34
+ * ---
35
+ *
36
+ * `strict routing` (abofs/stonyx-rest-server#50) closes the same class of
37
+ * authorization bypass for a TRAILING SLASH: `GET /private/failure` was denied
38
+ * by a consumer's auth hook while `GET /private/failure/` reached the guarded
39
+ * handler, because the hook compares `req.path` against `/failure`.
40
+ *
41
+ * It is a SEPARATE key from `caseSensitiveRoutes`, not a rename and not a
42
+ * reuse. Coupling them would force any consumer who legitimately needs
43
+ * trailing-slash tolerance -- a load balancer probing `/health/`, a
44
+ * slash-normalizing proxy -- to re-open #47's case bypass to get it. Case
45
+ * insensitivity is almost never intentional; slash tolerance frequently is.
46
+ *
47
+ * The two settings do NOT share the #47 split above, and this is the one thing
48
+ * not to carry across from that fix. For `strict routing`:
49
+ *
50
+ * - The CHILD site (Request's constructor) closes the entire security
51
+ * defect on its own. The parent site does nothing for it.
52
+ * - The PARENT site (RestServer's constructor) closes exactly one thing in
53
+ * this repo: `/health/`, the only route registered directly on the parent
54
+ * app. It has no security role here; do not describe it as having one.
55
+ *
56
+ * The cause is concrete: `Router.prototype.use` hardcodes `strict: false` (and
57
+ * `end: false`), so mount segments are structurally strict-immune. That is the
58
+ * OPPOSITE of `sensitive`, which `use()` DOES forward, and which is why #47's
59
+ * parent site closed `/PUBLIC/...`. The version-pinned file-and-line citation
60
+ * for that upstream behaviour is deliberately kept in ONE place --
61
+ * `docs/project-structure.md`, section "Strict routing (#50)" -- so a router
62
+ * upgrade invalidates one line rather than five. Both sites still get both
63
+ * settings (they share this function), but the justification differs and the
64
+ * tests are built on the measured split, not on the analogy.
65
+ *
66
+ * Consequence worth stating so nobody expects this setting to cover it: the
67
+ * mount-segment trailing slash (`/public/`) cannot be closed by an express
68
+ * SETTING. For both `/public` and `/public/` the mounted sub-app receives
69
+ * `req.path === '/'`, so a `req.path` auth hook sees no difference, and a test
70
+ * asserting `/public/` -> 404 could never pass.
71
+ *
72
+ * That is NOT the same as saying the edge is harmless or unclosable.
73
+ * `req.originalUrl` does differ, and a hook authorizing on it is bypassed by
74
+ * one character -- measured: `GET /admin` -> 401, `GET /admin/` -> 200 with the
75
+ * guarded handler running unauthenticated. That is a live bypass of the same
76
+ * class as #47 and #50, and it IS closable by this module: a canonical-path
77
+ * check ahead of the `auth` call in `Request.registerCalls()`, or opt-in
78
+ * normalizing middleware. It is tracked as abofs/stonyx-rest-server#54. Do not
79
+ * close it here, and do not read this note as saying it cannot be closed.
80
+ *
81
+ * Both guards are `!== false` for the same reason: these flags default to the
82
+ * truthy direction, so a truthy check fails OPEN for a consumer whose shipped
83
+ * config predates the key. Both are also asserted at the unit tier for BOTH
84
+ * failure shapes -- key present-and-`undefined` and key absent as an own
85
+ * property -- in `test/unit/request-test.ts` (#47's AC6, #50's AC3). The
86
+ * integration tier cannot see either: with the shipped default `true`, a
87
+ * fail-open guard leaves every integration assertion green.
33
88
  */
34
89
  export default function applyRouteMatching(api) {
35
90
  if (config.restServer?.caseSensitiveRoutes !== false)
36
91
  api.set('case sensitive routing', true);
92
+ if (config.restServer?.strictRoutes !== false)
93
+ api.set('strict routing', true);
37
94
  }
38
95
  //# sourceMappingURL=route-matching.js.map
@@ -1 +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"}
1
+ {"version":3,"file":"route-matching.js","sourceRoot":"","sources":["../src/route-matching.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,eAAe,CAAC;AAEnC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsFG;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;IAC9F,IAAI,MAAM,CAAC,UAAU,EAAE,YAAY,KAAK,KAAK;QAAE,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC;AACjF,CAAC"}
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "stonyx-async",
5
5
  "stonyx-module"
6
6
  ],
7
- "version": "0.2.1-beta.90",
7
+ "version": "0.2.1-beta.91",
8
8
  "description": "Rest Server Module for Stonyx Framework",
9
9
  "repository": {
10
10
  "type": "git",