Skip to content

Commit

Permalink
Merge pull request #2051 from AkhtarAmir/F/GCP-FunctionVersion
Browse files Browse the repository at this point in the history
F/gcp function version
  • Loading branch information
alphadev4 committed Jun 19, 2024
2 parents 4329427 + b9f88be commit 0b3f2a3
Show file tree
Hide file tree
Showing 3 changed files with 234 additions and 0 deletions.
1 change: 1 addition & 0 deletions exports.js
Original file line number Diff line number Diff line change
Expand Up @@ -1575,6 +1575,7 @@ module.exports = {
'httpTriggerRequireHttps' : require(__dirname + '/plugins/google/cloudfunctions/httpTriggerRequireHttps.js'),
'ingressAllTrafficDisabled' : require(__dirname + '/plugins/google/cloudfunctions/ingressAllTrafficDisabled.js'),
'cloudFunctionLabelsAdded' : require(__dirname + '/plugins/google/cloudfunctions/cloudFunctionLabelsAdded.js'),
'cloudFunctionOldRuntime' : require(__dirname + '/plugins/google/cloudfunctions/cloudFunctionOldRuntime.js'),
'functionAllUsersPolicy' : require(__dirname + '/plugins/google/cloudfunctions/functionAllUsersPolicy.js'),

'serverlessVPCAccess' : require(__dirname + '/plugins/google/cloudfunctions/serverlessVPCAccess.js'),
Expand Down
118 changes: 118 additions & 0 deletions plugins/google/cloudfunctions/cloudFunctionOldRuntime.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
var async = require('async');
var helpers = require('../../../helpers/google');

module.exports = {
title: 'Cloud Function Old Runtimes',
category: 'Cloud Functions',
domain: 'Compute',
severity: 'Medium',
description: 'Ensure Cloud Functions are not using deprecated runtime versions.',
more_info: 'Cloud Functions runtimes should be kept current with recent versions of the underlying codebase. It is recommended to update to the latest supported versions to avoid potential security risks and ensure compatibility.',
link: 'https://cloud.google.com/functions/docs/concepts/execution-environment',
recommended_action: 'Modify Cloud Functions to use latest versions.',
apis: ['functions:list'],
settings: {
function_runtime_fail: {
name: 'Cloud Function Runtime Fail',
description: 'Return a failing result for cloud function runtime before this number of days for their end of life date.',
regex: '^[1-9]{1}[0-9]{0,3}$',
default: 0
}
},
realtime_triggers: ['functions.CloudFunctionsService.UpdateFunction', 'functions.CloudFunctionsService.CreateFunction', 'functions.CloudFunctionsService.DeleteFunction'],

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

var config = {
function_runtime_fail: parseInt(settings.function_runtime_fail || this.settings.function_runtime_fail.default)
};

var deprecatedRuntimes = [
{ 'id':'nodejs10', 'name': 'Node.js 10.x', 'endOfLifeDate': '2021-07-30' },
{ 'id':'nodejs12', 'name': 'Node.js 12', 'endOfLifeDate': '2024-01-30' },
{ 'id':'nodejs14', 'name': 'Node.js 14', 'endOfLifeDate': '2024-01-30' },
{ 'id':'nodejs16', 'name': 'Node.js 16', 'endOfLifeDate': '2024-01-30' },
{ 'id':'nodejs18', 'name': 'Node.js 18', 'endOfLifeDate': '2025-04-30' },
{ 'id':'nodejs20', 'name': 'Node.js 20', 'endOfLifeDate': '2026-04-30' },
{ 'id':'dotnet6', 'name': '.Net 6', 'endOfLifeDate': '2024-11-12' },
{ 'id':'dotnet7', 'name': '.Net 7', 'endOfLifeDate': '2024-05-14' },
{ 'id':'dotnet3', 'name': '.Net Core 3', 'endOfLifeDate': '2024-01-30' },
{ 'id':'python27', 'name': 'Python 2.7', 'endOfLifeDate': '2021-07-15' },
{ 'id':'python36', 'name': 'Python 3.6', 'endOfLifeDate': '2022-07-18' },
{ 'id':'python37', 'name': 'Python 3.7', 'endOfLifeDate': '2024-01-30' },
{ 'id':'python38', 'name': 'Python 3.8', 'endOfLifeDate': '2024-10-14' },
{ 'id':'python39', 'name': 'Python 3.9', 'endOfLifeDate': '2025-10-05' },
{ 'id':'python310', 'name': 'Python 3.10', 'endOfLifeDate': '2026-10-04' },
{ 'id':'python311', 'name': 'Python 3.11', 'endOfLifeDate': '2027-10-24' },
{ 'id':'python312', 'name': 'Python 3.12', 'endOfLifeDate': '2028-10-02' },
{ 'id':'ruby25', 'name': 'Ruby 2.5', 'endOfLifeDate': '2021-07-30' },
{ 'id':'ruby27', 'name': 'Ruby 2.7', 'endOfLifeDate': '2024-01-30' },
{ 'id':'ruby30', 'name': 'Ruby 3.0', 'endOfLifeDate': '2024-03-31' },
{ 'id':'ruby32', 'name': 'Ruby 3.2', 'endOfLifeDate': '2026-03-31' },
{ 'id':'go121', 'name': 'Go 1.21', 'endOfLifeDate': '2024-05-01' },
{ 'id':'go119', 'name': 'Go 1.19', 'endOfLifeDate': '2024-04-30' },
{ 'id':'go118', 'name': 'Go 1.18', 'endOfLifeDate': '2024-01-30' },
{ 'id':'go116', 'name': 'Go 1.16', 'endOfLifeDate': '2024-01-30' },
{ 'id':'go113', 'name': 'Go 1.13', 'endOfLifeDate': '2024-01-30' },
{ 'id':'java8', 'name': 'Java 8', 'endOfLifeDate': '2024-01-08' },
{ 'id':'java11', 'name': 'Java 11', 'endOfLifeDate': '2024-10-01' },
{ 'id':'java17', 'name': 'Java 17', 'endOfLifeDate': '2027-10-01' },
{ 'id':'php74', 'name': 'PHP 7.4', 'endOfLifeDate': '2024-01-30' },
{ 'id':'php81', 'name': 'PHP 8.1', 'endOfLifeDate': '2024-11-25' },
{ 'id':'php82', 'name': 'PHP 8.2', 'endOfLifeDate': '2025-12-08' },
];

async.each(regions.functions, (region, rcb) => {
var functions = helpers.addSource(cache, source,
['functions', 'list', region]);

if (!functions) return rcb();

if (functions.err || !functions.data) {
helpers.addResult(results, 3,
'Unable to query for Google Cloud Functions: ' + helpers.addError(functions), region, null, null, functions.err);
return rcb();
}

if (!functions.data.length) {
helpers.addResult(results, 0, 'No Google Cloud functions found', region);
return rcb();
}

functions.data.forEach(func => {
if (!func.name || !func.runtime) return;

var deprecatedRuntime = deprecatedRuntimes.filter((d) => {
return d.id == func.runtime;
});

var version = func.runtime;
var runtimeDeprecationDate = (deprecatedRuntime && deprecatedRuntime.length && deprecatedRuntime[0].endOfLifeDate) ? Date.parse(deprecatedRuntime[0].endOfLifeDate) : null;
let today = new Date();
today = Date.parse(`${today.getFullYear()}-${today.getMonth()+1}-${today.getDate()}`);
var difference = runtimeDeprecationDate? Math.round((runtimeDeprecationDate - today)/(1000 * 3600 * 24)): null;
if (runtimeDeprecationDate && today > runtimeDeprecationDate) {
helpers.addResult(results, 2,
'Cloud Function is using runtime: ' + deprecatedRuntime[0].name + ' which was deprecated on: ' + deprecatedRuntime[0].endOfLifeDate,
region, func.name);
} else if (difference && config.function_runtime_fail >= difference) {
helpers.addResult(results, 2,
'Cloud Function is using runtime: ' + version + ' which is deprecating in ' + Math.abs(difference) + ' days',
region, func.name);
} else {
helpers.addResult(results, 0,
'Cloud Function is running the current version: ' + version,
region, func.name);
}

});

rcb();
}, function() {
callback(null, results, source);
});
}
};
115 changes: 115 additions & 0 deletions plugins/google/cloudfunctions/cloudFunctionOldRuntime.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
var expect = require('chai').expect;
var plugin = require('./cloudFunctionOldRuntime');


const functions = [
{
"name": "projects/my-test-project/locations/us-central1/functions/function-1",
"status": "ACTIVE",
"entryPoint": "helloWorld",
"timeout": "60s",
"availableMemoryMb": 256,
"updateTime": "2021-09-24T06:18:15.265Z",
"runtime": "nodejs14",
"ingressSettings": "ALLOW_ALL"
},
{
"name": "projects/my-test-project/locations/us-central1/functions/function-2",
"status": "ACTIVE",
"entryPoint": "helloWorld",
"timeout": "60s",
"availableMemoryMb": 256,
"updateTime": "2021-09-24T06:18:15.265Z",
"versionId": "1",
"runtime": "python311",
"ingressSettings": "ALLOW_INTERNAL_AND_GCLB",
"labels": { 'deployment-tool': 'console-cloud' }

}
];

const createCache = (list, err) => {
return {
functions: {
list: {
'us-central1': {
err: err,
data: list
}
}
}
}
};

describe('cloudFunctionOldRuntime', function () {
describe('run', function () {
it('should give passing result if no cloud functions found', function (done) {
const callback = (err, results) => {
expect(results.length).to.be.above(0);
expect(results[0].status).to.equal(0);
expect(results[0].message).to.include('No Google Cloud functions found');
expect(results[0].region).to.equal('us-central1');
done()
};

const cache = createCache(
[],
null
);

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

it('should give unknown result if unable to query for Google Cloud functions', function (done) {
const callback = (err, results) => {
expect(results.length).to.be.above(0);
expect(results[0].status).to.equal(3);
expect(results[0].message).to.include('Unable to query for Google Cloud Functions');
expect(results[0].region).to.equal('us-central1');
done()
};

const cache = createCache(
[],
{message: 'error'},
);

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

it('should give passing result if google cloud function is using lasted runtime version', function (done) {
const callback = (err, results) => {
expect(results.length).to.be.above(0);
expect(results[0].status).to.equal(0);
expect(results[0].message).to.include('Cloud Function is running the current version: ');
expect(results[0].region).to.equal('us-central1');
done()
};

const cache = createCache(
[functions[1]],
null
);

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

it('should give failing result if google cloud function is using deprecated runtimeversion', function (done) {
const callback = (err, results) => {
expect(results.length).to.be.above(0);
expect(results[0].status).to.equal(2);
expect(results[0].message).to.include('which was deprecated on');
expect(results[0].region).to.equal('us-central1');
done();
};

const cache = createCache(
[functions[0]],
null );

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

})
});

0 comments on commit 0b3f2a3

Please sign in to comment.