Repository: serverless/serverless (JavaScript)
Date: 2019-12-04 09:08:37+00:00
Task type: Bug Fix

Issue:
1.58 was causing aws access keys to return as undefined
# Bug Report

## Description

<!-- Please use https://forum.serverless.com, StackOverflow or other forums for Q&A -->
<!-- Please answer ALL the question below. Otherwise we probably have to close the issue due to missing information -->

1. What did you do?
I attempted to do a local invocation of a function that was calling aws secret management for postgres credentials
1. What happened?
Nothing was being returned and I was recieving a no password supplied postgres error
1. What should've happened?
It should have returned the password 

Ultimately, if I deleted the 'AWS_ACCESS_KEY_ID' environment variable in the python code, the code would execute - otherwise it aws returning as 'undefined'.  

The code worked in 1.57.

I believe it's related to plat-1798, potentially here: https://github.com/serverless/serverless/commit/ae36c256c0cdcfe5e0732dd6f502f9d05e109366

If no credentials are cached, it should not set the variables undefined.


Issue discussion:
See inline code comment that makes our issue go away, will try and spend some time with a more realistic fix, but not familiar enough with serverless to understand what's happening -- someone smarter than .me should address :) https://github.com/serverless/serverless/commit/ae36c256c0cdcfe5e0732dd6f502f9d05e109366#r36146664
Even merging the correct variables at this.provider.cachedCredentials.credentials is breaking for us.  Interesting to note that adding the access_key_id into the environment at this point is causing issues, regardless of whether it's accurate or not.

Solution patch:
diff --git a/lib/plugins/aws/invokeLocal/index.js b/lib/plugins/aws/invokeLocal/index.js
index 02b7341aeee..d68e027e386 100644
--- a/lib/plugins/aws/invokeLocal/index.js
+++ b/lib/plugins/aws/invokeLocal/index.js
@@ -128,13 +128,17 @@ class AwsInvokeLocal {
       NODE_PATH: '/var/runtime:/var/task:/var/runtime/node_modules',
     };
 
-    const credentialEnvVars = this.provider.cachedCredentials
-      ? {
-          AWS_ACCESS_KEY_ID: this.provider.cachedCredentials.accessKeyId,
-          AWS_SECRET_ACCESS_KEY: this.provider.cachedCredentials.secretAccessKey,
-          AWS_SESSION_TOKEN: this.provider.cachedCredentials.sessionToken,
-        }
-      : {};
+    const { cachedCredentials } = this.provider;
+    const credentialEnvVars = {};
+    if (cachedCredentials.accessKeyId) {
+      credentialEnvVars.AWS_ACCESS_KEY_ID = cachedCredentials.accessKeyId;
+    }
+    if (cachedCredentials.secretAccessKey) {
+      credentialEnvVars.AWS_SECRET_ACCESS_KEY = cachedCredentials.secretAccessKey;
+    }
+    if (cachedCredentials.sessionToken) {
+      credentialEnvVars.AWS_SESSION_TOKEN = cachedCredentials.sessionToken;
+    }
 
     // profile override from config
     const profileOverride = this.provider.getProfile();


Test patch:
diff --git a/lib/plugins/aws/invokeLocal/index.test.js b/lib/plugins/aws/invokeLocal/index.test.js
index 1cffadbcf41..ff42324977a 100644
--- a/lib/plugins/aws/invokeLocal/index.test.js
+++ b/lib/plugins/aws/invokeLocal/index.test.js
@@ -327,18 +327,31 @@ describe('AwsInvokeLocal', () => {
         expect(process.env.NODE_PATH).to.equal('/var/runtime:/var/task:/var/runtime/node_modules');
       }));
 
-    it('it should set credential env vars', () => {
-      provider.cachedCredentials.accessKeyId = 'ID';
-      provider.cachedCredentials.secretAccessKey = 'SECRET';
-      provider.cachedCredentials.sessionToken = 'TOKEN';
+    it('it should set credential env vars #1', () => {
+      provider.cachedCredentials = {
+        accessKeyId: 'ID',
+        secretAccessKey: 'SECRET',
+      };
 
       return awsInvokeLocal.loadEnvVars().then(() => {
         expect(process.env.AWS_ACCESS_KEY_ID).to.equal('ID');
         expect(process.env.AWS_SECRET_ACCESS_KEY).to.equal('SECRET');
-        expect(process.env.AWS_SESSION_TOKEN).to.equal('TOKEN');
+        expect('AWS_SESSION_TOKEN' in process.env).to.equal(false);
       });
     });
 
+    it('it should set credential env vars #2', () => {
+      provider.cachedCredentials = { sessionToken: 'TOKEN' };
+      return awsInvokeLocal
+        .loadEnvVars()
+
+        .then(() => {
+          expect(process.env.AWS_SESSION_TOKEN).to.equal('TOKEN');
+          expect('AWS_ACCESS_KEY_ID' in process.env).to.equal(false);
+          expect('AWS_SECRET_ACCESS_KEY' in process.env).to.equal(false);
+        });
+    });
+
     it('should fallback to service provider configuration when options are not available', () => {
       awsInvokeLocal.provider.options.region = null;
       awsInvokeLocal.serverless.service.provider.region = 'us-west-1';