aws-sam-cli icon indicating copy to clipboard operation
aws-sam-cli copied to clipboard

BUG: HttpApiCorsConfiguration AllowOrigins does not support CommaDelimitedList parameter locally

Open gauntface opened this issue 2 years ago • 11 comments

Description:

I've just hit this as well but spotted a slightly different behavior between cloudformation and SAM.

The following will not work when run via SAM locally

Parameters:
  GatewayCorsOrigins:
    Type: CommaDelimitedList
    Default: []

Resources:
 ApiGatewayApi:
  Type: AWS::Serverless::HttpApi
  Properties:
    CorsConfiguration:
      AllowMethods:
        - OPTIONS
        - GET
        - POST
      AllowOrigins: !Ref GatewayCorsOrigins
      AllowHeaders:
        - Authorization

The logs output CORS Property AllowOrigins was not fully resolved. Will proceed as if the Property was not defined.

However, if I change AllowOrigins: !Ref GatewayCorsOrigins for:

AllowOrigins:
  - !Ref GatewayCorsOrigins

Then SAM local will work with the CORs configuration BUT CloudFormation will not deploy anything.

Steps to reproduce:

Create a template file with a parameter defining the cors origins like so:

Parameters:
  GatewayCorsOrigins:
    Type: CommaDelimitedList
    Default: []

Resources:
 ApiGatewayApi:
  Type: AWS::Serverless::HttpApi
  Properties:
    CorsConfiguration:
      AllowMethods:
        - OPTIONS
        - GET
        - POST
      AllowOrigins: !Ref GatewayCorsOrigins
      AllowHeaders:
        - Authorization

Observed result:

CORs does not work locally: CORS Property AllowOrigins was not fully resolved. Will proceed as if the Property was not defined.

Expected result:

CORs would work for the defined origins.

Additional environment details (Ex: Windows, Mac, Amazon Linux etc)

  1. OS: Linux - Fedora
  2. sam --version: SAM CLI, version 1.50.0
  3. AWS region: N/A

gauntface avatar Jun 30 '22 22:06 gauntface

NOTE: This may be similar to #3719 but I raised a separate issue in case the use of parameters was the issue I'm facing vs support for multiple origins.

gauntface avatar Jun 30 '22 22:06 gauntface

Thanks for reporting this issue. I've tried to re-produce it with following template & function.

template

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Globals:
  Function:
    Timeout: 3

Parameters:
  GatewayCorsOrigins:
    Type: CommaDelimitedList
    Default: []

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.9
      Events:
        HelloWorld:
          Type: HttpApi
          Properties:
            ApiId: ApiGatewayApi
            Path: /hello
            Method: get

  ApiGatewayApi:
    Type: AWS::Serverless::HttpApi
    Properties:
      CorsConfiguration:
        AllowMethods:
          - OPTIONS
          - GET
          - POST
        AllowOrigins: !Ref GatewayCorsOrigins
        AllowHeaders:
          - Authorization

function code:

import json


def lambda_handler(event, context):

    return {
        "statusCode": 200,
        "body": json.dumps({
            "message": "hello world",
        }),
    }

And after running sam local start-api I am making the following curl call and I am getting following result;

curl 'http://localhost:3000/hello' -X 'OPTIONS' -i
HTTP/1.0 200 OK
Access-Control-Allow-Origin: 
Access-Control-Allow-Methods: GET,OPTIONS,POST
Access-Control-Allow-Headers: Authorization
Content-Length: 0
Server: Werkzeug/1.0.1 Python/3.8.13
Date: Thu, 07 Jul 2022 14:32:03 GMT

If I update default values for GatewayCorsOrigins I am getting list of origins in the header above.

Please let me know if I am missing something here.

Thanks!

mndeveci avatar Jul 07 '22 14:07 mndeveci

The only different I can imagine is that I'm using golang instead of python, but I wouldn't expect that to make a different to the gateway and CORs

gauntface avatar Jul 11 '22 21:07 gauntface

That shouldn't make a difference, what is the response you got when you try to invoke it locally?

mndeveci avatar Jul 12 '22 10:07 mndeveci

Closing as it's most likely that I'm holding sam cli wrong.

gauntface avatar Jul 18 '22 22:07 gauntface

Running into this still.

Template:

Resources:
  ApiGatewayApi:
    Type: AWS::Serverless::HttpApi
    Properties:
      StageName: "example-stage"
      CorsConfiguration:
        AllowMethods:
          - OPTIONS
          - GET
          - POST
        AllowOrigins: !Ref GatewayCorsOrigins
        AllowHeaders:
          - Authorization

cURL response:

curl 'http://localhost:3001/hello' -X 'OPTIONS' -i
HTTP/1.0 200 OK
Access-Control-Allow-Methods: GET,OPTIONS,POST
Access-Control-Allow-Headers: Authorization
Content-Length: 0
Server: Werkzeug/1.0.1 Python/3.7.10
Date: Fri, 22 Jul 2022 17:26:30 GMT

The main difference in curl output between https://github.com/aws/aws-sam-cli/issues/4026#issuecomment-1177717814 and mine is Access-Control-Allow-Origin: is not returned in my output.

gauntface avatar Jul 22 '22 17:07 gauntface

I can replicate the Access-Control-Allow-Origin: response if I don't include GatewayCorsOrigins parameter in the parameter overrides flag.

If I include --parameter-overrides="GatewayCorsOrigins='http://localhost:3000', then I get the behavior I originally described of no Access-Control-Allow-Origin header and the message CORS Property AllowOrigins was not fully resolved. Will proceed as if the Property was not defined. in the sam logs.

Changing the AllowOrigins: value will fix this but break the deployment.

gauntface avatar Jul 22 '22 18:07 gauntface

https://github.com/gauntface/aws-cli-cors-issue is an reduced case of what I'm trying to do.

If you run make serverdefault you'll get the empty Access Control Allow Origin header, if you run make serverparams you'll not get the header.

It seems like this may be a flag parsing issue, but I'm not sure how to debug this (i.e. can sam print the final values it uses)?

gauntface avatar Jul 22 '22 19:07 gauntface

I'm having this issue as well.

sam --version
SAM CLI, version 1.110.0

I think CommaDelimitedList combined with !Ref should transform a strings for instance "http://domain1, http://domain2" into ["http://domain1", "http://domain2"]. This doesn't work using sam local start-api.

https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html#parameters-section-structure-properties

CommaDelimitedList An array of literal strings that are separated by commas. The total number of strings should be one more than the total number of commas. Also, each member string is space trimmed.

For example, users could specify "test,dev,prod", and a Ref would result in ["test","dev","prod"].

Therefor the error CORS Property AllowOrigins was not fully resolved. Will proceed as if the Property was not defined. shows up. This more or less blocks me on local development because I'm dependent on CORS. If I deploy this it works as intended.

It's pretty easy to reproduce:

# Simple "sam init" 
# Output: https://app.warp.dev/block/j5SwgAAQCg2bv9GN7uJAxP
> git clone [email protected]:EloB/aws-sam-cli-command.git
> cd aws-sam-cli-command/
> sam local start-api

Initializing the lambda functions containers.
Local image is up-to-date
Using local image: public.ecr.aws/lambda/nodejs:18-rapid-x86_64.

Mounting /Users/ollebroms/Documents/sam-app/hello-world as /var/task:ro,delegated, inside runtime container
Containers Initialization is done.
CORS Property AllowOrigins was not fully resolved. Will proceed as if the Property was not defined.
# ...

Attempt 1, failed using parameters

> sam local start-api --parameter-overrides CorsAllowedOrigins="https://www.google.com"

Initializing the lambda functions containers.
Local image is up-to-date
Using local image: public.ecr.aws/lambda/nodejs:18-rapid-x86_64.

Mounting /Users/ollebroms/Documents/sam-app/hello-world as /var/task:ro,delegated, inside runtime container
Containers Initialization is done.
CORS Property AllowOrigins was not fully resolved. Will proceed as if the Property was not defined.
# ...

image

Attempt 2, failed using default value as string

Goto template.yaml file and uncomment Default: "https://www.google.com" or run provided git patch.

> git apply <<EOF
diff --git a/template.yaml b/template.yaml
index aeaa32a..ea43120 100644
--- a/template.yaml
+++ b/template.yaml
@@ -9,7 +9,7 @@ Parameters:
   CorsAllowedOrigins:
     Type: CommaDelimitedList
     # Uncomment the following to set the default value. Then rerun sam local start-api.
-    # Default: "https://www.google.com"
+    Default: "https://www.google.com"
     # Default: ["https://www.google.com"]
     Description: The list of allowed origins for CORS


EOF

> sam local start-api

Initializing the lambda functions containers.
Local image is up-to-date
Using local image: public.ecr.aws/lambda/nodejs:18-rapid-x86_64.

Mounting /Users/ollebroms/Documents/sam-app/hello-world as /var/task:ro,delegated, inside runtime container
Containers Initialization is done.
CORS Property AllowOrigins was not fully resolved. Will proceed as if the Property was not defined.
# ...

image

Attempt 3, works when providing default as array of strings

Goto template.yaml file and uncomment Default: ["https://www.google.com"] or run provided script.

I think this indicates that sam local start-api isn't transforming that !Ref correctly. According to the json schema for Cloudformation this shouldn't be allowed to enter but it makes the CORS to work.

> git checkout -- .
> git apply <<EOF
diff --git a/template.yaml b/template.yaml
index aeaa32a..af8d0b8 100644
--- a/template.yaml
+++ b/template.yaml
@@ -10,7 +10,7 @@ Parameters:
     Type: CommaDelimitedList
     # Uncomment the following to set the default value. Then rerun sam local start-api.
     # Default: "https://www.google.com"
-    # Default: ["https://www.google.com"]
+    Default: ["https://www.google.com"]
     Description: The list of allowed origins for CORS

 # More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst

EOF

# Also build if you want to be able to fetch. Don't forget to remove folder between attempts.

> sam local start-api

Initializing the lambda functions containers.
Local image is up-to-date
Using local image: public.ecr.aws/lambda/nodejs:18-rapid-x86_64.

Mounting /Users/ollebroms/Documents/sam-app/hello-world as /var/task:ro,delegated, inside runtime container
Containers Initialization is done.
Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET, OPTIONS]
# ...

image

EloB avatar Mar 01 '24 15:03 EloB

@jfuss @mildaniel I've made some testing regarding this. Any comments on my comment above? I'm a bit worried about this being stale for 2 years.

EloB avatar Mar 01 '24 15:03 EloB

Any Python developer who can help with my PR to fix this above? I have little to none experience with Python. I'm a JavaScript/TypeScript developer.

EloB avatar Mar 01 '24 18:03 EloB