Repository: pinojs/pino
Pull request #916 (2020-10-02 14:25:40)

#915 Adding path case sensitivity to docs
Added simple bullet in path syntax and emboldened case sensitivity.


Patch:
diff --git a/docs/api.md b/docs/api.md
index 2f99ec7..1930921 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -153,7 +153,7 @@ Each path must be a string using a syntax which corresponds to JavaScript dot an
 If an object is supplied, three options can be specified:
   * `paths` (array): Required. An array of paths. See [redaction - Path Syntax ⇗](/docs/redaction.md#paths) for specifics.
   * `censor` (String|Function|Undefined): Optional. When supplied as a String the `censor` option will overwrite keys which are to be redacted. When set to `undefined` the the key will be removed entirely from the object.
-    The `censor` option may also be a mapping function. The (synchronous) mapping function is called with the unredacted value. The value returned from the mapping function becomes the applied censor value. Default: `'[Redacted]'`
+    The `censor` option may also be a mapping function. The (synchronous) mapping function has the signature `(value, path) => redactedValue` and is called with the unredacted `value` and `path` to the key being redacted, as an array. For example given a redaction path of `a.b.c` the `path` argument would be `['a', 'b', 'c']`. The value returned from the mapping function becomes the applied censor value. Default: `'[Redacted]'`
     value synchronously.
     Default: `'[Redacted]'`
   * `remove` (Boolean): Optional. Instead of censoring the value, remove both the key and the value. Default: `false`
diff --git a/docs/redaction.md b/docs/redaction.md
index f0c79f9..d70508f 100644
--- a/docs/redaction.md
+++ b/docs/redaction.md
@@ -103,6 +103,7 @@ in standard EcmaScript, with two additions:
 
 * paths may start with bracket notation
 * paths may contain the asterisk `*` to denote a wildcard
+* paths are **case sensitive**
 
 By way of example, the following are all valid paths:
 
diff --git a/lib/redaction.js b/lib/redaction.js
index c1dd721..4ab7b28 100644
--- a/lib/redaction.js
+++ b/lib/redaction.js
@@ -76,13 +76,25 @@ function redaction (opts, serialize) {
     [redactFmtSym]: fastRedact({ paths, censor, serialize, strict })
   }
 
-  const topCensor = (...args) =>
-    typeof censor === 'function' ? serialize(censor(...args)) : serialize(censor)
+  const topCensor = (...args) => {
+    return typeof censor === 'function' ? serialize(censor(...args)) : serialize(censor)
+  }
 
   return [...Object.keys(shape), ...Object.getOwnPropertySymbols(shape)].reduce((o, k) => {
     // top level key:
-    if (shape[k] === null) o[k] = topCensor
-    else o[k] = fastRedact({ paths: shape[k], censor, serialize, strict })
+    if (shape[k] === null) {
+      o[k] = (value) => topCensor(value, [k])
+    } else {
+      const wrappedCensor = typeof censor === 'function' ? (value, path) => {
+        return censor(value, [k, ...path])
+      } : censor
+      o[k] = fastRedact({
+        paths: shape[k],
+        censor: wrappedCensor,
+        serialize,
+        strict
+      })
+    }
     return o
   }, result)
 }
diff --git a/package.json b/package.json
index 4a8b38b..8fd4e11 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "pino",
-  "version": "6.6.1",
+  "version": "6.7.0",
   "description": "super fast, all natural json logger",
   "main": "pino.js",
   "browser": "./browser.js",
@@ -85,7 +85,7 @@
     "winston": "^3.3.3"
   },
   "dependencies": {
-    "fast-redact": "^2.0.0",
+    "fast-redact": "^3.0.0",
     "fast-safe-stringify": "^2.0.7",
     "flatstr": "^1.0.12",
     "pino-std-serializers": "^2.4.2",


Test patch:
diff --git a/test/redact.test.js b/test/redact.test.js
index cab6618..d40e938 100644
--- a/test/redact.test.js
+++ b/test/redact.test.js
@@ -219,6 +219,37 @@ test('redact.censor option – sets the redact value', async ({ is }) => {
   is(req.headers.cookie, 'test')
 })
 
+test('redact.censor option – can be a function that accepts value and path arguments', async ({ is }) => {
+  const stream = sink()
+  const instance = pino({ redact: { paths: ['topLevel'], censor: (value, path) => value + ' ' + path.join('.') } }, stream)
+  instance.info({
+    topLevel: 'test'
+  })
+  const { topLevel } = await once(stream, 'data')
+  is(topLevel, 'test topLevel')
+})
+
+test('redact.censor option – can be a function that accepts value and path arguments (nested path)', async ({ is }) => {
+  const stream = sink()
+  const instance = pino({ redact: { paths: ['req.headers.cookie'], censor: (value, path) => value + ' ' + path.join('.') } }, stream)
+  instance.info({
+    req: {
+      id: 7915,
+      method: 'GET',
+      url: '/',
+      headers: {
+        host: 'localhost:3000',
+        connection: 'keep-alive',
+        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
+      },
+      remoteAddress: '::ffff:127.0.0.1',
+      remotePort: 58022
+    }
+  })
+  const { req } = await once(stream, 'data')
+  is(req.headers.cookie, 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1; req.headers.cookie')
+})
+
 test('redact.remove option – removes both key and value', async ({ is }) => {
   const stream = sink()
   const instance = pino({ redact: { paths: ['req.headers.cookie'], remove: true } }, stream)