From 4778ea0b65e1f22f3d85cfa8bad0e1b29f87b7d3 Mon Sep 17 00:00:00 2001
From: Justin Wind <justin.wind+git@gmail.com>
Date: Fri, 29 Oct 2021 13:41:40 -0700
Subject: [PATCH] obscure authorization header value when logging

---
 CHANGELOG.md       |  4 ++++
 lib/common.js      | 36 +++++++++++++++++++++++++++++++++++-
 test/lib/common.js | 40 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 79 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 33468ed..8e0e95d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,10 @@ Releases and notable changes to this project are documented here.
 
 ## [Unreleased]
 
+### Added
+
+- obscure sensitive request header data when logging
+
 ## [v1.2.1] - 2021-10-22
 
 ### Added
diff --git a/lib/common.js b/lib/common.js
index ff68e9d..21070b8 100644
--- a/lib/common.js
+++ b/lib/common.js
@@ -158,19 +158,51 @@ const pick = (obj, props) => {
 
 /**
  * Return a subset of a request object, suitable for logging.
+ * Obscures sensitive header values.
  * @param {http.ClientRequest} req 
  */
 const requestLogData = (req) => {
-  return pick(req, [
+  const data = pick(req, [
     'method',
     'url',
     'httpVersion',
     'headers',
     'trailers',
   ]);
+  scrubHeaderObject(data);
+  return data;
 };
 
 
+/**
+ * Remove sensitive header data.
+ * @param {Object} data
+ * @param {Object} data.headers
+ */
+const scrubHeaderObject = (data) => {
+  if (data && data.headers && 'authorization' in data.headers) {
+    data.headers = Object.assign({}, data.headers, {
+      authorization: obscureAuthorizationHeader(data.headers['authorization']),
+    });
+  }
+}
+
+
+/**
+ * Hide sensitive part of an Authorization header.
+ * @param {String} authHeader
+ * @returns {String}
+ */
+const obscureAuthorizationHeader = (authHeader) => {
+  if (!authHeader) {
+    return authHeader;
+  }
+  const space = authHeader.indexOf(' ');
+  // This blurs entire string if no space found, because -1.
+  return authHeader.slice(0, space + 1) + '*'.repeat(authHeader.length - (space + 1));
+}
+
+
 /**
  * Return a subset of a response object, suitable for logging.
  * @param {http.ServerResponse} res 
@@ -272,10 +304,12 @@ module.exports = {
   mergeEnum,
   nop,
   nullLogger,
+  obscureAuthorizationHeader,
   pick,
   requestId,
   requestLogData,
   responseLogData,
+  scrubHeaderObject,
   setOptions,
   splitFirst,
   unfoldHeaderLines,
diff --git a/test/lib/common.js b/test/lib/common.js
index a04bf61..bacd6be 100644
--- a/test/lib/common.js
+++ b/test/lib/common.js
@@ -141,6 +141,46 @@ describe('common', function () {
     });
   }); // requestLogData
 
+  describe('obscureAuthorizationHeader', function () {
+    it('obscures basic data', function () {
+      const authHeader = 'Basic Zm9vOmJhcg==';
+      const expected = 'Basic ************';
+      const result = common.obscureAuthorizationHeader(authHeader);
+      assert.strictEqual(result, expected);
+    });
+    it('obscures all of other types', function () {
+      const authHeader = 'someWeirdAuth';
+      const expected = '*************';
+      const result = common.obscureAuthorizationHeader(authHeader);
+      assert.strictEqual(result, expected);
+    });
+    it('does nothing when empty', function () {
+      const authHeader = undefined;
+      const expected = undefined;
+      const result = common.obscureAuthorizationHeader(authHeader);
+      assert.strictEqual(result, expected);
+    });
+  }); // obscureAuthorizationHeader
+
+  describe('scrubHeaderObject', function () {
+    it('', function () {
+      const data = {
+        headers: {
+          'foo': 'bar',
+          'authorization': 'Basic Zm9vOmJhcg==',
+        },
+      };
+      const expected = {
+        headers: {
+          'foo': 'bar',
+          'authorization': 'Basic ************',
+        },
+      };
+      common.scrubHeaderObject(data);
+      assert.deepStrictEqual(data, expected);
+    });
+  }); // scrubHeaderObject
+
   describe('responseLogData', function () {
     it('gives data', function () {
       const res = {
-- 
2.49.0