@63klabs/cache-data 1.3.10 → 1.3.11

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
@@ -8,6 +8,12 @@ To report an issue, or to see proposed and upcoming enhancements, check out [63K
8
8
 
9
9
  Report all vulnerabilities under the [Security menu](https://github.com/63Klabs/cache-data/security/advisories) in the Cache-Data GitHub repository.
10
10
 
11
+ ## v1.3.11 (2026-04-09)
12
+
13
+ ### Fixed
14
+
15
+ - **ClientRequest / RequestInfo: X-Forwarded-For and User-Agent header priority** - When API Gateway is behind CloudFront (or any reverse proxy), `getClientIp()` and `getClientUserAgent()` now return the original client values from the `x-forwarded-for` and `user-agent` headers instead of CloudFront's identity values. Falls back to `identity.sourceIp` and `identity.userAgent` when headers are absent. [Spec: 1-3-11-client-request-update-forwarded-for](.kiro/specs/1-3-11-client-request-update-forwarded-for/)
16
+
11
17
  ## v1.3.10 (2026-03-15)
12
18
 
13
19
  ### Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@63klabs/cache-data",
3
- "version": "1.3.10",
3
+ "version": "1.3.11",
4
4
  "description": "A package for AWS Lambda Node.js applications to access and cache data from remote API endpoints (or other sources) utilizing AWS S3 and DynamoDb for the cache storage. Also provides a simple request handling, routing, and response logging framework for running a web service with minimal dependencies.",
5
5
  "author": "Chad Kluck (https://chadkluck.me)",
6
6
  "license": "MIT",
@@ -28,7 +28,8 @@
28
28
  "@eslint/js": "^10.0.1",
29
29
  "eslint": "^10.0.1",
30
30
  "fast-check": "^4.5.3",
31
- "jest": "^30.2.0" },
31
+ "jest": "^30.2.0"
32
+ },
32
33
  "overrides": {
33
34
  "fast-xml-parser": ">=5.3.4",
34
35
  "glob": ">=13.0.6",
@@ -1400,8 +1400,16 @@ class ClientRequest extends RequestInfo {
1400
1400
  /**
1401
1401
  * Get the _processed_ request properties. These are the properties that
1402
1402
  * the ClientRequest object took from the event sent to Lambda, validated,
1403
- * supplemented, and makes available to controllers.
1404
- * @returns {{ method: string, path: string, pathArray: string[], resource: string, resourceArray[], pathParameters: {}, queryStringParameters: {}, headerParameters: {}, cookieParameters: {}, bodyParameters: {}, bodyPayload: string, client: {ip: string, userAgent: string, origin: string, referrer: string, isAuthenticated: boolean, isGuest: boolean, authorizations: string[], roles: string[]}, deadline: number, calcMsToDeadline: number}
1403
+ * supplemented, and makes available to controllers.
1404
+ *
1405
+ * Note When accessed behind CloudFront:
1406
+ * To ensure the user agent is passed along to API Gateway, use the AWS
1407
+ * managed Origin Request Policy named AllViewerExceptHostHeader. This
1408
+ * forwards the User-Agent along with most other viewer headers while
1409
+ * maintaining the correct Host header required for API Gateway to route
1410
+ * the request properly.
1411
+ *
1412
+ * @returns {{ method: string, path: string, pathArray: string[], resource: string, resourceArray: string[], pathParameters: {}, queryStringParameters: {}, headerParameters: {}, cookieParameters: {}, bodyParameters: {}, bodyPayload: string, client: {ip: string, userAgent: string, origin: string, referrer: string, isAuthenticated: boolean, isGuest: boolean, authorizations: string[], roles: string[]}, deadline: number, calcMsToDeadline: function}
1405
1413
  */
1406
1414
  getProps() {
1407
1415
  return this.#props;
@@ -130,6 +130,12 @@ class RequestInfo {
130
130
 
131
131
  /**
132
132
  * User Agent of client request
133
+ * Note When accessed behind CloudFront:
134
+ * To ensure the user agent is passed along to API Gateway, use the AWS
135
+ * managed Origin Request Policy named AllViewerExceptHostHeader. This
136
+ * forwards the User-Agent along with most other viewer headers while
137
+ * maintaining the correct Host header required for API Gateway to route
138
+ * the request properly.
133
139
  * @returns {string} The user agent string supplied by the client request
134
140
  */
135
141
  getClientUserAgent() {
@@ -259,9 +265,25 @@ class RequestInfo {
259
265
 
260
266
 
261
267
  /**
262
- * Obtain lambda event request details for logging
263
- * @param {*} event
264
- * @returns Information about the requesting client including IP and user agent
268
+ * Obtain lambda event request details for logging.
269
+ * Parses the API Gateway event to extract client information including IP,
270
+ * user agent, origin, referrer, headers, and query parameters.
271
+ *
272
+ * For `client.ip`, the `x-forwarded-for` header is preferred over
273
+ * `identity.sourceIp`. When `x-forwarded-for` is present and non-empty,
274
+ * the first IP from the comma-separated list is used (trimmed). This
275
+ * ensures the original client IP is returned when behind a proxy such
276
+ * as CloudFront. Falls back to `identity.sourceIp` when the header is
277
+ * absent or empty.
278
+ *
279
+ * For `client.userAgent`, the `user-agent` header is preferred over
280
+ * `identity.userAgent`. When the header is present and non-empty, its
281
+ * value is used directly. This ensures the original client user agent
282
+ * is returned instead of a proxy identifier (e.g. "Amazon CloudFront").
283
+ * Falls back to `identity.userAgent` when the header is absent or empty.
284
+ *
285
+ * @param {object} event - API Gateway Lambda event object
286
+ * @returns {{ip: string|null, userAgent: string|null, origin: string|null, referrer: string|null, ifModifiedSince: string|null, ifNoneMatch: string|null, accept: string|null, headers: object, parameters: object, body: string|null}} Client request information
265
287
  */
266
288
  _clientRequestInfo (event) {
267
289
 
@@ -292,11 +314,24 @@ class RequestInfo {
292
314
  client.ip = identity.sourceIp;
293
315
  }
294
316
 
317
+ // if x-forwarded-for header is present, prefer it over identity.sourceIp (first IP in the list is the original client)
318
+ if ( "x-forwarded-for" in headers && headers["x-forwarded-for"] !== null && headers["x-forwarded-for"] !== "" ) {
319
+ let firstIp = headers["x-forwarded-for"].split(",")[0].trim();
320
+ if ( firstIp !== "" ) {
321
+ client.ip = firstIp;
322
+ }
323
+ }
324
+
295
325
  // if there is a user-agent header, set it
296
326
  if ( "userAgent" in identity && identity.userAgent !== null ) {
297
327
  client.userAgent = identity.userAgent;
298
328
  }
299
329
 
330
+ // if user-agent header is present, prefer it over identity.userAgent (original client user agent)
331
+ if ( "user-agent" in headers && headers["user-agent"] !== null && headers["user-agent"] !== "" ) {
332
+ client.userAgent = headers["user-agent"];
333
+ }
334
+
300
335
  // if there is an origin header, set it
301
336
  if ( headers?.origin) {
302
337
  client.origin = headers.origin;