From 49d78297e92c39bb88cacca7b727a034bdef34c6 Mon Sep 17 00:00:00 2001 From: fatima99s Date: Fri, 14 Jun 2024 15:00:58 +0500 Subject: [PATCH] F/GCP-FunctionVersion --- exports.js | 1 + .../cloudfunctions/cloudFunctionOldRuntime.js | 118 ++++++++++++++++++ .../cloudFunctionOldRuntime.spec.js | 115 +++++++++++++++++ 3 files changed, 234 insertions(+) create mode 100644 plugins/google/cloudfunctions/cloudFunctionOldRuntime.js create mode 100644 plugins/google/cloudfunctions/cloudFunctionOldRuntime.spec.js diff --git a/exports.js b/exports.js index 9ca097916d..6b0fb6717d 100644 --- a/exports.js +++ b/exports.js @@ -1554,6 +1554,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'), diff --git a/plugins/google/cloudfunctions/cloudFunctionOldRuntime.js b/plugins/google/cloudfunctions/cloudFunctionOldRuntime.js new file mode 100644 index 0000000000..79dbf9a9e8 --- /dev/null +++ b/plugins/google/cloudfunctions/cloudFunctionOldRuntime.js @@ -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 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); + }); + } +}; \ No newline at end of file diff --git a/plugins/google/cloudfunctions/cloudFunctionOldRuntime.spec.js b/plugins/google/cloudfunctions/cloudFunctionOldRuntime.spec.js new file mode 100644 index 0000000000..7f00884a49 --- /dev/null +++ b/plugins/google/cloudfunctions/cloudFunctionOldRuntime.spec.js @@ -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); + }); + + }) +}); +