Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions exports.js
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,7 @@ module.exports = {
'lambdaLogGroups' : require(__dirname + '/plugins/aws/lambda/lambdaLogGroups.js'),
'lambdaTracingEnabled' : require(__dirname + '/plugins/aws/lambda/lambdaTracingEnabled.js'),
'lambdaHasTags' : require(__dirname + '/plugins/aws/lambda/lambdaHasTags.js'),
'lambdaFuncUrlNotInUse' : require(__dirname + '/plugins/aws/lambda/lambdaFuncUrlNotInUse.js'),
'lambdaUniqueExecutionRole' : require(__dirname + '/plugins/aws/lambda/lambdaUniqueExecutionRole.js'),

'webServerPublicAccess' : require(__dirname + '/plugins/aws/mwaa/webServerPublicAccess.js'),
Expand Down
6 changes: 6 additions & 0 deletions helpers/aws/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -2621,6 +2621,12 @@ var postcalls = [
filterKey: 'FunctionName',
filterValue: 'FunctionName',
},
listFunctionUrlConfigs : {
reliesOnService: 'lambda',
reliesOnCall: 'listFunctions',
filterKey: 'FunctionName',
filterValue: 'FunctionName',
},
sendIntegration: {
enabled: true
}
Expand Down
6 changes: 6 additions & 0 deletions helpers/aws/api_multipart.js
Original file line number Diff line number Diff line change
Expand Up @@ -1984,6 +1984,12 @@ var postcalls = [
filterKey: 'FunctionName',
filterValue: 'FunctionName',
},
listFunctionUrlConfigs : {
reliesOnService: 'lambda',
reliesOnCall: 'listFunctions',
filterKey: 'FunctionName',
filterValue: 'FunctionName',
},
sendIntegration: {
enabled: true
}
Expand Down
56 changes: 56 additions & 0 deletions plugins/aws/lambda/lambdaFuncUrlNotInUse.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
var async = require('async');
var helpers = require('../../../helpers/aws');

module.exports = {
title: 'Lambda Function URL Not in Use',
category: 'Lambda',
domain: 'Serverless',
severity: 'Medium',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
severity: 'Medium',
severity: 'Low',

description: 'Ensure that AWS Lambda functions are not configured with function URLs for HTTP(S) endpoints.',
more_info: 'A function URL creates a direct HTTP(S) endpoint to your function and this may pose a security risk depending on the security configuration and intention of the function.',
link: 'https://docs.aws.amazon.com/lambda/latest/dg/urls-configuration.html',
recommended_action: 'Modify Lambda function configurations and delete function url.',
apis: ['Lambda:listFunctions','Lambda:listFunctionUrlConfigs'],
realtime_triggers: ['lambda:CreateFunction','lambda:UpdateFunctionConfiguration','lambda:DeleteFunction'],

run: function(cache, settings, callback) {
var results = [];
var source = {};
var regions = helpers.regions(settings);

async.each(regions.lambda, function(region, rcb){
var listFunctions = helpers.addSource(cache, source,
['lambda', 'listFunctions', region]);

if (!listFunctions) return rcb();

if (listFunctions.err || !listFunctions.data) {
helpers.addResult(results, 3,
`Unable to query for Lambda functions: ${helpers.addError(listFunctions)}`, region);
return rcb();
}

if (!listFunctions.data.length) {
helpers.addResult(results, 0, 'No Lambda functions found', region);
return rcb();
}
for (var lambdaFunc of listFunctions.data) {
if (!lambdaFunc.FunctionName) continue;
var resource = lambdaFunc.FunctionName;
var urlConfigs = helpers.addSource(cache, source, ['lambda', 'listFunctionUrlConfigs', region, resource]);

if (urlConfigs && urlConfigs.data &&
urlConfigs.data.FunctionUrlConfigs &&
urlConfigs.data.FunctionUrlConfigs.length){
helpers.addResult(results, 2, 'Lambda function Url is configured', region, resource);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
helpers.addResult(results, 2, 'Lambda function Url is configured', region, resource);
helpers.addResult(results, 2, 'Lambda function url is configured', region, resource);

} else {
helpers.addResult(results, 0, 'Lambda function Url is not configured', region, resource);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
helpers.addResult(results, 0, 'Lambda function Url is not configured', region, resource);
helpers.addResult(results, 0, 'Lambda function url is not configured', region, resource);

}
}

rcb();
}, function(){
callback(null, results, source);
});
}
};
110 changes: 110 additions & 0 deletions plugins/aws/lambda/lambdaFuncUrlNotInUse.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
var expect = require('chai').expect;
var lambdaFunctionURLNotInUse = require('./lambdaFuncUrlNotInUse');

const createCache = (lambdaData, functionUrlConfigs) => {
return {
lambda: {
listFunctions: {
'us-east-1': {
err: null,
data: lambdaData
}
},
listFunctionUrlConfigs: functionUrlConfigs
}
};
};

describe('Lambda Function URL Not in Use', function () {
describe('run', function () {
it('should return unknown result if unable to list the lambda functions', function (done) {
const callback = (err, results) => {
expect(results.length).to.equal(1);
expect(results[0].status).to.equal(3);
expect(results[0].message).to.include('Unable to query for Lambda functions');
done();
};

const cache = createCache(null, {});

lambdaFunctionURLNotInUse.run(cache, {}, callback);
});

it('should return passing result if no lambda function found in region', function (done) {
const callback = (err, results) => {
expect(results.length).to.equal(1);
expect(results[0].status).to.equal(0);
expect(results[0].message).to.include('No Lambda functions found');
done();
};

const cache = createCache([], {});

lambdaFunctionURLNotInUse.run(cache, {}, callback);
});

it('should return passing result if lambda function URL is not configured', function (done) {
const lambdaData = [
{
"FunctionName": "test-lambda",
"FunctionArn": "arn:aws:lambda:us-east-1:000011112222:function:test-lambda"
}
];

const functionUrlConfigs = {
'us-east-1': {
'test-lambda': {
'err': null,
'data': {
'FunctionUrlConfigs': []
}
}
}
};

const callback = (err, results) => {
expect(results.length).to.equal(1);
expect(results[0].status).to.equal(0);
expect(results[0].message).to.include('Lambda function Url is not configured');
done();
};

const cache = createCache(lambdaData, functionUrlConfigs);

lambdaFunctionURLNotInUse.run(cache, {}, callback);
});

it('should return failing result if lambda function URL is configured', function (done) {
const lambdaData = [
{
"FunctionName": "test-lambda",
"FunctionArn": "arn:aws:lambda:us-east-1:000011112222:function:test-lambda"
}
];

const functionUrlConfigs = {
'us-east-1': {
'test-lambda': {
'err': null,
'data': {
'FunctionUrlConfigs': [{
FunctionUrl: "https://tetsuewfebwfweffesvvs.lambda-url.us-east-1.on.aws/",
}]
}
}
}
};

const callback = (err, results) => {
expect(results.length).to.equal(1);
expect(results[0].status).to.equal(2);
expect(results[0].message).to.include('Lambda function Url is configured');
done();
};

const cache = createCache(lambdaData, functionUrlConfigs);

lambdaFunctionURLNotInUse.run(cache, {}, callback);
});
});
});