Repository: aws/aws-sdk-js
Pull request #3350 (2020-07-15 20:51:46)

fix the logger ignoring the sensitive complexed shapes
<!--
Thank you for your pull request. Please provide a description below.
-->
Closes #3337 

SDK has a bug that the logger ignores the `sensitive` trait exists in `structure`, `map` or `list` shapes. It used to only redact the simple types like strings, blobs or numbers. With this change, the logger now redacts complex shapes that is sensitive.

/cc @trivikr @alexforsyth 

##### Checklist
<!-- Remove items that do not apply. For completed items, change [ ] to [x]. -->

- [x] `npm run test` passes
- [x] changelog is added, `npm run add-change`

aws-sdk-js-automation: 
### AWS CodeBuild CI Report

* CodeBuild project: sdk-v2-github
* Commit ID: fc24e89f6d1911b070a933a2f11069d54b317e5c
* Result: SUCCEEDED
* [Build Logs](https://r6vdd33du7.execute-api.us-west-2.amazonaws.com/Prod/buildlogs?key=d01160af-3b2e-4bd3-a0aa-416862bf20c2%2Fbuild.log) (available for 30 days)

*Powered by [github-codebuild-logs](https://serverlessrepo.aws.amazon.com/applications/arn:aws:serverlessrepo:us-east-1:277187709615:applications~github-codebuild-logs), available on the [AWS Serverless Application Repository](https://aws.amazon.com/serverless/serverlessrepo/)*


<!--
CREATED BY GITHUB-CODEBUILD-LOGS
-->


aws-sdk-js-automation: 
### AWS CodeBuild CI Report

* CodeBuild project: sdk-v2-github
* Commit ID: c954e589f43c916400494fcc6ed1aa54a8685683
* Result: SUCCEEDED
* [Build Logs](https://r6vdd33du7.execute-api.us-west-2.amazonaws.com/Prod/buildlogs?key=a6765527-3c61-4380-9f2d-dc83b97f12f1%2Fbuild.log) (available for 30 days)

*Powered by [github-codebuild-logs](https://serverlessrepo.aws.amazon.com/applications/arn:aws:serverlessrepo:us-east-1:277187709615:applications~github-codebuild-logs), available on the [AWS Serverless Application Repository](https://aws.amazon.com/serverless/serverlessrepo/)*


<!--
CREATED BY GITHUB-CODEBUILD-LOGS
-->



Patch:
diff --git a/.changes/next-release/bugfix-Logger-e94be0b3.json b/.changes/next-release/bugfix-Logger-e94be0b3.json
new file mode 100644
index 000000000..5516b89e5
--- /dev/null
+++ b/.changes/next-release/bugfix-Logger-e94be0b3.json
@@ -0,0 +1,5 @@
+{
+  "type": "bugfix",
+  "category": "Logger",
+  "description": "Fix the bug that SDK logs the sensitive data in structure, map, and list parameters"
+}
\ No newline at end of file
diff --git a/lib/event_listeners.js b/lib/event_listeners.js
index c6d4773b8..2d548e3c5 100644
--- a/lib/event_listeners.js
+++ b/lib/event_listeners.js
@@ -512,6 +512,9 @@ AWS.EventListeners = {
         if (!shape) {
           return shape;
         }
+        if (inputShape.isSensitive) {
+          return '***SensitiveInformation***';
+        }
         switch (inputShape.type) {
           case 'structure':
             var struct = {};
@@ -536,11 +539,7 @@ AWS.EventListeners = {
             });
             return map;
           default:
-            if (inputShape.isSensitive) {
-              return '***SensitiveInformation***';
-            } else {
-              return shape;
-            }
+            return shape;
         }
       }
 


Test patch:
diff --git a/test/event_listeners.spec.js b/test/event_listeners.spec.js
index 770550859..d1cb42e73 100644
--- a/test/event_listeners.spec.js
+++ b/test/event_listeners.spec.js
@@ -959,80 +959,81 @@
                 members: {
                   foo: {
                     type: 'string',
-                    sensitive: true
                   }
-                }
+                },
               },
               output: {}
             }
-          }
+          },
+          shapes: {}
         };
       });
 
-      it('with sensitive trait in shape\'s own property', function() {
+      it('from input shape of scalars', function() {
+        var allShapeTypes = ['boolean', 'timestamp', 'float','integer', 'string', 'base64', 'binary'];
+        Array.prototype.forEach.call(allShapeTypes, function(shapeType) {
+          apiJSON.operations.mockMethod.input.members.foo = {
+            type: shapeType,
+            sensitive: true
+          };
+          var api = new AWS.Model.Api(apiJSON);
+          var CustomMockService = MockServiceFromApi(api);
+          service = new CustomMockService({logger: logger});
+          helpers.mockHttpResponse(200, {}, []);
+          logger.log = logfn;
+          service.makeRequest('mockMethod', {
+            foo: '1234567'
+          }).send();
+          expect(data.indexOf('1234567')).to.equal(-1);
+        });
+      });
+
+      it('from input of undefined', function() {
+        apiJSON.operations.mockMethod.input.members.foo = {
+          type: 'list',
+          member: {
+            type: 'structure',
+            members: {
+              bar: { sensitive: true },
+              baz: {}
+            }
+          }
+        };
         var api = new AWS.Model.Api(apiJSON);
         var CustomMockService = MockServiceFromApi(api);
         service = new CustomMockService({logger: logger});
         helpers.mockHttpResponse(200, {}, []);
         logger.log = logfn;
-        var request = service.makeRequest('mockMethod', {
-          foo: 'secret_key_id'
-        });
-        request.send();
+        service.makeRequest('mockMethod', {
+          foo: [undefined, {bar: 'secret_key_id'}]
+        }).send();
+        console.log(data);
         expect(data.indexOf('secret_key_id')).to.equal(-1);
-        expect(JSON.stringify(request.params).indexOf('secret_key_id') >= 0).to.equal(true);
+        expect(data.indexOf('undefined')).to.be.greaterThan(-1);
       });
 
-      it('from input shape of structure and with un-inlined shape', function() {
-        var api = new AWS.Model.Api({
-          operations: {
-            mockMethod: {
-              input: {
-                type: 'structure',
-                members: {
-                  foo: {
-                    shape: 'S1'
-                  },
-                  baz: {
-                    type: 'structure',
-                    members: {
-                      bar: {}
-                    }
-                  }
-                }
-              },
-              output: {}
-            }
-          },
-          shapes: {
-            S1: {
-              type: 'blob',
-              sensitive: true
-            }
-          }
-        });
+      it('from structure shape with sensitive trait', function() {
+        apiJSON.operations.mockMethod.input.sensitive = true;
+        var api = new AWS.Model.Api(apiJSON);
         var CustomMockService = MockServiceFromApi(api);
         service = new CustomMockService({logger: logger});
         helpers.mockHttpResponse(200, {}, []);
         logger.log = logfn;
-        service.makeRequest('mockMethod', {
-          foo: 'secret_key_id',
-          baz: {
-            bar: 'should log'
-          },
-          qux: 'nonsense'
-        }).send();
+        var request = service.makeRequest('mockMethod', {
+          foo: 'secret_key_id'
+        });
+        request.send();
         expect(data.indexOf('secret_key_id')).to.equal(-1);
-        expect(data.indexOf('bar: \'should log\'') >= 0).to.equal(true);
-        expect(data.indexOf('qux: \'nonsense\'') >= 0).to.equal(true);
+        expect(data.indexOf('foo')).to.equal(-1);
       });
 
-      it('from input shape of list', function() {
+      it('from list shape with sensitive trait', function() {
         apiJSON.operations.mockMethod.input.members.foo = {
           type: 'list',
           member: {
-            sensitive: true
-          }
+            type: 'string'
+          },
+          sensitive: true
         };
         var api = new AWS.Model.Api(apiJSON);
         var CustomMockService = MockServiceFromApi(api);
@@ -1046,16 +1047,16 @@
         expect(data.indexOf('secret_access_key')).to.equal(-1);
       });
 
-      it('from input shape of map', function() {
+      it('from map shape with sensitive trait', function() {
         apiJSON.operations.mockMethod.input.members.foo = {
           type: 'map',
           key: {
             type: 'string'
           },
           value: {
-            type: 'string',
-            sensitive: true
-          }
+            type: 'string'
+          },
+          sensitive: true
         };
         var api = new AWS.Model.Api(apiJSON);
         var CustomMockService = MockServiceFromApi(api);
@@ -1068,23 +1069,20 @@
             key1: 'secret_key_id'
           }
         }).send();
+        expect(data.indexOf('key0')).to.equal(-1);
+        expect(data.indexOf('key1')).to.equal(-1);
         expect(data.indexOf('secret_key_id')).to.equal(-1);
       });
 
-      it('with recursive input shape', function() {
+      it('from array of array shapes with sensitive trait', function() {
         apiJSON.operations.mockMethod.input.members.foo = {
-          type: 'map',
-          key: {
-            type: 'string'
-          },
-          value: {
+          type: 'list',
+          member: {
             type: 'list',
             member: {
-              type: 'structure',
-              members: {
-                bar: {sensitive: true}
-              }
-            }
+              type: 'string',
+              sensitive: true
+            },
           }
         };
         var api = new AWS.Model.Api(apiJSON);
@@ -1093,55 +1091,174 @@
         helpers.mockHttpResponse(200, {}, []);
         logger.log = logfn;
         service.makeRequest('mockMethod', {
-          foo: {
-            key0: [{bar: 'secret_key_id'}, {bar: 'secret_access_key'}]
-          }
+          foo: [['secret_access_key_a', 'secret_access_key_b']]
         }).send();
-        expect(data.indexOf('secret_key_id')).to.equal(-1);
-        expect(data.indexOf('secret_access_key')).to.equal(-1);
-      });
-
-      it('from input shape of scalars', function() {
-        var allShapeTypes = ['boolean', 'timestamp', 'float','integer', 'string', 'base64', 'binary'];
-        Array.prototype.forEach.call(allShapeTypes, function(shapeType) {
-          apiJSON.operations.mockMethod.input.members.foo = {
-            type: shapeType,
-            sensitive: true
-          };
-          var api = new AWS.Model.Api(apiJSON);
-          var CustomMockService = MockServiceFromApi(api);
-          service = new CustomMockService({logger: logger});
-          helpers.mockHttpResponse(200, {}, []);
-          logger.log = logfn;
-          service.makeRequest('mockMethod', {
-            foo: '1234567'
-          }).send();
-          expect(data.indexOf('1234567')).to.equal(-1);
-        });
+        expect(data.indexOf('secret_access_key_a')).to.equal(-1);
+        expect(data.indexOf('secret_access_key_b')).to.equal(-1);
       });
 
-      it('from input of undefined', function() {
+      it('from array of structure shapes with sensitive trait', function() {
         apiJSON.operations.mockMethod.input.members.foo = {
           type: 'list',
           member: {
             type: 'structure',
             members: {
-              bar: {},
-              baz: {}
+              bar: {shape: 'S1'}
             }
           }
         };
+        apiJSON.shapes.S1 = {
+          type: 'string',
+          sensitive: true
+        };
+        var api = new AWS.Model.Api(apiJSON);
+        var CustomMockService = MockServiceFromApi(api);
+        service = new CustomMockService({logger: logger});
+        helpers.mockHttpResponse(200, {}, []);
+        logger.log = logfn;
+        service.makeRequest('mockMethod', {
+          foo: [{bar: 'secret_access_key_a'}, {bar: 'secret_access_key_b'}]
+        }).send();
+        expect(data.indexOf('bar')).to.be.greaterThan(-1);
+        expect(data.indexOf('secret_access_key_a')).to.equal(-1);
+        expect(data.indexOf('secret_access_key_b')).to.equal(-1);
+      });
+
+      it('from array of map shapes with sensitive trait', function() {
+        apiJSON.operations.mockMethod.input.members.foo = {
+          type: 'list',
+          member: {
+            type: 'map',
+            key: {},
+            value: { sensitive: true }
+          }
+        };
+        var api = new AWS.Model.Api(apiJSON);
+        var CustomMockService = MockServiceFromApi(api);
+        service = new CustomMockService({logger: logger});
+        helpers.mockHttpResponse(200, {}, []);
+        logger.log = logfn;
+        service.makeRequest('mockMethod', {
+          foo: [{
+            bar: 'secret_access_key_a',
+            qux: 'secret_access_key_b'
+          }]
+        }).send();
+        expect(data.indexOf('bar')).to.be.greaterThan(-1);
+        expect(data.indexOf('qux')).to.be.greaterThan(-1);
+        expect(data.indexOf('secret_access_key_a')).to.equal(-1);
+        expect(data.indexOf('secret_access_key_b')).to.equal(-1);
+      });
+
+      it('from structure of array shapes with sensitive trait', function() {
+        apiJSON.operations.mockMethod.input.members.foo = {
+          type: 'structure',
+          members: {
+            bar: { shape: 'S1' }
+          }
+        };
+        apiJSON.shapes.S1 = {
+          type: 'list',
+          member: {
+            type: 'string'
+          },
+          sensitive: true
+        };
+        var api = new AWS.Model.Api(apiJSON);
+        var CustomMockService = MockServiceFromApi(api);
+        service = new CustomMockService({logger: logger});
+        helpers.mockHttpResponse(200, {}, []);
+        logger.log = logfn;
+        service.makeRequest('mockMethod', {
+          foo: {bar: ['secret_access_key']}
+        }).send();
+        expect(data.indexOf('secret_access_key')).to.equal(-1);
+      });
+
+      it('from structure of structure shapes with sensitive trait', function() {
+        apiJSON.operations.mockMethod.input.members.foo = {
+          type: 'structure',
+          members: {
+            bar: { shape: 'S1' }
+          }
+        };
+        apiJSON.shapes.S1 = {
+          type: 'structure',
+          members: {
+            qux: { shape: 'S2'}
+          },
+        };
+        apiJSON.shapes.S2 = {
+          sensitive: true
+        };
+        var api = new AWS.Model.Api(apiJSON);
+        var CustomMockService = MockServiceFromApi(api);
+        service = new CustomMockService({logger: logger});
+        helpers.mockHttpResponse(200, {}, []);
+        logger.log = logfn;
+        service.makeRequest('mockMethod', {
+          foo: {bar: {qux: 'secret_access_key'}}
+        }).send();
+        expect(data.indexOf('bar')).to.be.greaterThan(-1);
+        expect(data.indexOf('qux')).to.be.greaterThan(-1);
+        expect(data.indexOf('secret_access_key')).to.equal(-1);
+      });
+
+      it('from structure of map shapes with sensitive trait', function() {
+        apiJSON.operations.mockMethod.input.members.foo = {
+          type: 'structure',
+          members: {
+            bar: { shape: 'S1' }
+          }
+        };
+        apiJSON.shapes.S1 = {
+          type: 'map',
+          key: {},
+          value: { shape: 'S2' },
+        };
+        apiJSON.shapes.S2 = { sensitive: true };
         var api = new AWS.Model.Api(apiJSON);
         var CustomMockService = MockServiceFromApi(api);
         service = new CustomMockService({logger: logger});
         helpers.mockHttpResponse(200, {}, []);
         logger.log = logfn;
         service.makeRequest('mockMethod', {
-          foo: [undefined, {bar: 'bar'}]
+          foo: {
+            bar: {key0: 'secret_access_key_0', key1: 'secret_access_key_1'}
+          }
+        }).send();
+        expect(data.indexOf('bar')).to.be.greaterThan(-1);
+        expect(data.indexOf('secret_access_key_0')).to.equal(-1);
+        expect(data.indexOf('secret_access_key_1')).to.equal(-1);
+      });
+
+      it('from map of structure shapes with sensitive trait', function() {
+        apiJSON.operations.mockMethod.input.members.foo = {
+          type: 'map',
+          key: {},
+          value: { shape: 'S1' }
+        };
+        apiJSON.shapes.S1 = {
+          type: 'structure',
+          members: {
+            bar: { shape: 'S2' }
+          }
+        };
+        apiJSON.shapes.S2 = { sensitive: true };
+        var api = new AWS.Model.Api(apiJSON);
+        var CustomMockService = MockServiceFromApi(api);
+        service = new CustomMockService({logger: logger});
+        helpers.mockHttpResponse(200, {}, []);
+        logger.log = logfn;
+        service.makeRequest('mockMethod', {
+          foo: {
+            key0: { bar: 'secret_access_key_0' },
+            key1: { bar: 'secret_access_key_1' }
+          }
         }).send();
-        expect(data.indexOf('bar: \'bar\'') >= 0).to.equal(true);
-        expect(data.indexOf('undefined') >= 0).to.equal(true);
-        expect(data.indexOf('{}')).to.equal(-1);
+        expect(data.indexOf('bar')).to.be.greaterThan(-1);
+        expect(data.indexOf('secret_access_key_0')).to.equal(-1);
+        expect(data.indexOf('secret_access_key_1')).to.equal(-1);
       });
     });