Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Fixed detection of REST API type payloads in AWS Lambda #2543

Merged
merged 1 commit into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions lib/serverless/api-gateway.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ function normalizeHeaders(event, lowerCaseKey = false) {

/**
* Determines if Lambda event appears to be a valid Lambda Proxy event.
* There are multiple types of events possible. They are described at
* https://docs.aws.amazon.com/lambda/latest/dg/services-apigateway.html#services-apigateway-apitypes.
* Each type of event has its own event payload structure; some types have
* multiple versions of the payload structure.
*
* @param {object} event The event to inspect.
* @returns {boolean} Whether the given object contains fields necessary
Expand All @@ -106,7 +110,8 @@ function isLambdaProxyEvent(event) {
return isGatewayV1Event(event) || isGatewayV2Event(event)
}

const v1Keys = [
// See https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format
const restApiV1Keys = [
'body',
'headers',
'httpMethod',
Expand All @@ -118,16 +123,23 @@ const v1Keys = [
'queryStringParameters',
'requestContext',
'resource',
'stageVariables',
'version'
'stageVariables'
].join(',')

// See https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html
const httpApiV1Keys = [...restApiV1Keys.split(','), 'version'].join(',')

function isGatewayV1Event(event) {
const keys = Object.keys(event).sort().join(',')
return keys === v1Keys && event?.version === '1.0'
if (keys === httpApiV1Keys && event?.version === '1.0') {
return true
}

return keys === restApiV1Keys
}

const v2Keys = [
// See https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html
const httpApiV2Keys = [
'body',
'cookies',
'headers',
Expand All @@ -144,7 +156,7 @@ const v2Keys = [

function isGatewayV2Event(event) {
const keys = Object.keys(event).sort().join(',')
return keys === v2Keys && event?.version === '2.0'
return keys === httpApiV2Keys && event?.version === '2.0'
}

/**
Expand Down
2 changes: 1 addition & 1 deletion test/unit/serverless/api-gateway-v2.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const AwsLambda = require('../../../lib/serverless/aws-lambda')

const ATTR_DEST = require('../../../lib/config/attribute-filter').DESTINATIONS

const { gatewayV2Event: v2Event } = require('./fixtures')
const { httpApiGatewayV2Event: v2Event } = require('./fixtures')

tap.beforeEach((t) => {
// This env var suppresses console output we don't need to inspect.
Expand Down
81 changes: 76 additions & 5 deletions test/unit/serverless/fixtures.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@

'use strict'

// https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html
const gatewayV1Event = {
const httpApiGatewayV1Event = {
version: '1.0',
resource: '/my/path',
path: '/my/path',
Expand Down Expand Up @@ -77,8 +76,79 @@ const gatewayV1Event = {
isBase64Encoded: false
}

// https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format
const restApiGatewayV1Event = {
resource: '/my/path',
path: '/my/path',
httpMethod: 'GET',
headers: {
header1: 'value1',
header2: 'value2'
},
multiValueHeaders: {
header1: ['value1'],
header2: ['value1', 'value2']
},
queryStringParameters: {
parameter1: 'value1',
parameter2: 'value'
},
multiValueQueryStringParameters: {
parameter1: ['value1', 'value2'],
parameter2: ['value']
},
requestContext: {
accountId: '123456789012',
apiId: 'id',
authorizer: {
claims: null,
scopes: null
},
domainName: 'id.execute-api.us-east-1.amazonaws.com',
domainPrefix: 'id',
extendedRequestId: 'request-id',
httpMethod: 'GET',
identity: {
accessKey: null,
accountId: null,
caller: null,
cognitoAuthenticationProvider: null,
cognitoAuthenticationType: null,
cognitoIdentityId: null,
cognitoIdentityPoolId: null,
principalOrgId: null,
sourceIp: '192.0.2.1',
user: null,
userAgent: 'user-agent',
userArn: null,
clientCert: {
clientCertPem: 'CERT_CONTENT',
subjectDN: 'www.example.com',
issuerDN: 'Example issuer',
serialNumber: 'a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1',
validity: {
notBefore: 'May 28 12:30:02 2019 GMT',
notAfter: 'Aug 5 09:36:04 2021 GMT'
}
}
},
path: '/my/path',
protocol: 'HTTP/1.1',
requestId: 'id=',
requestTime: '04/Mar/2020:19:15:17 +0000',
requestTimeEpoch: 1583349317135,
resourceId: null,
resourcePath: '/my/path',
stage: '$default'
},
pathParameters: null,
stageVariables: null,
body: 'Hello from Lambda!',
isBase64Encoded: false
}

// https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html
const gatewayV2Event = {
const httpApiGatewayV2Event = {
version: '2.0',
routeKey: '$default',
rawPath: '/my/path',
Expand Down Expand Up @@ -175,7 +245,8 @@ const lambaV1InvocationEvent = {
}

module.exports = {
gatewayV1Event,
gatewayV2Event,
restApiGatewayV1Event,
httpApiGatewayV1Event,
httpApiGatewayV2Event,
lambaV1InvocationEvent
}
17 changes: 12 additions & 5 deletions test/unit/serverless/utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,23 @@ const test = require('node:test')
const assert = require('node:assert')

const { isGatewayV1Event, isGatewayV2Event } = require('../../../lib/serverless/api-gateway')
const { gatewayV1Event, gatewayV2Event, lambaV1InvocationEvent } = require('./fixtures')
const {
restApiGatewayV1Event,
httpApiGatewayV1Event,
httpApiGatewayV2Event,
lambaV1InvocationEvent
} = require('./fixtures')

test('isGatewayV1Event', () => {
assert.equal(isGatewayV1Event(gatewayV1Event), true)
assert.equal(isGatewayV1Event(gatewayV2Event), false)
assert.equal(isGatewayV1Event(restApiGatewayV1Event), true)
assert.equal(isGatewayV1Event(httpApiGatewayV1Event), true)
assert.equal(isGatewayV1Event(httpApiGatewayV2Event), false)
assert.equal(isGatewayV1Event(lambaV1InvocationEvent), false)
})

test('isGatewayV2Event', () => {
assert.equal(isGatewayV2Event(gatewayV1Event), false)
assert.equal(isGatewayV2Event(gatewayV2Event), true)
assert.equal(isGatewayV2Event(restApiGatewayV1Event), false)
assert.equal(isGatewayV2Event(httpApiGatewayV1Event), false)
assert.equal(isGatewayV2Event(httpApiGatewayV2Event), true)
assert.equal(isGatewayV2Event(lambaV1InvocationEvent), false)
})
Loading