From 4ebb9d97bb8c55c40ca669aac62ac02cbd76bcd8 Mon Sep 17 00:00:00 2001
From: Josh Mock <joshua.mock@elastic.co>
Date: Thu, 7 Mar 2024 17:41:31 -0600
Subject: [PATCH] Redaction should recurse into arrays (#87)

---
 src/security.ts            | 21 +++++++++++++--------
 test/unit/security.test.ts | 13 +++++++++++++
 2 files changed, 26 insertions(+), 8 deletions(-)

diff --git a/src/security.ts b/src/security.ts
index 54dc17f..9268b1f 100644
--- a/src/security.ts
+++ b/src/security.ts
@@ -45,15 +45,20 @@ export function redactObject (obj: Record<string, any>, additionalKeys: string[]
         value = `${value.origin}${value.pathname}${value.search}`
       }
 
-      if (typeof value === 'object' && !Array.isArray(value) && value !== null) {
-        if (seen.get(value) !== true) {
-          // if this Object hasn't been seen, recursively redact it
-          seen.set(value, true)
-          value = doRedact(value)
+      if (typeof value === 'object' && value !== null) {
+        if (Array.isArray(value)) {
+          // if it's an array, redact each item
+          value = value.map(v => doRedact(v))
         } else {
-          // if it has been seen, set the value that goes in newObj to null
-          // this is what prevents the circular references
-          value = null
+          if (seen.get(value) !== true) {
+            // if this Object hasn't been seen, recursively redact it
+            seen.set(value, true)
+            value = doRedact(value)
+          } else {
+            // if it has been seen, set the value that goes in newObj to null
+            // this is what prevents the circular references
+            value = null
+          }
         }
       }
 
diff --git a/test/unit/security.test.ts b/test/unit/security.test.ts
index 35898ca..97d434d 100644
--- a/test/unit/security.test.ts
+++ b/test/unit/security.test.ts
@@ -192,5 +192,18 @@ test('redactObject', t => {
     t.equal(result.url, 'http://foo.com/path/to/endpoint?query=true')
   })
 
+  t.test('properly recurses into arrays', t => {
+    t.plan(2)
+    const result = redactObject({
+      foo: [
+        { authorization: 'foo' },
+        { password: 'bar' },
+      ]
+    })
+
+    t.notMatch(result.foo[0].authorization, 'foo')
+    t.notMatch(result.foo[1].password, 'bar')
+  })
+
   t.end()
 })