From 4944fa1e2d411a64845501a5207ac76e335aae8d Mon Sep 17 00:00:00 2001 From: Aleksandar Stojanov Date: Wed, 27 Dec 2023 12:39:29 +0100 Subject: [PATCH] add disallowEndOfLifeRuntime policy Signed-off-by: Aleksandar Stojanov --- __tests__/cloudfunctions/index.ts | 77 +++++++++++++++++++ .../disallowEndOfLifeRuntime.ts | 55 +++++++++++++ src/cloudfunctions/disallowPublicIngress.ts | 2 +- src/cloudfunctions/index.ts | 4 +- 4 files changed, 135 insertions(+), 3 deletions(-) diff --git a/__tests__/cloudfunctions/index.ts b/__tests__/cloudfunctions/index.ts index 380b5c6..b99c572 100644 --- a/__tests__/cloudfunctions/index.ts +++ b/__tests__/cloudfunctions/index.ts @@ -17,3 +17,80 @@ export const cloudfunctionsFunction = new gcp.cloudfunctions.Function("fail#1", } ], }); + +export const cloudfunctionsFunctionNodejs = new gcp.cloudfunctions.Function("fail#2", { + name: "my-function", + runtime: "nodejs16", + region: "europe-west1", + kmsKeyName: "my-key", + eventTrigger: { + eventType: "google.storage.object.finalize", + resource: "my-bucket", + }, +}); + +export const cloudfunctionsFunctionPython = new gcp.cloudfunctions.Function("fail#3", { + name: "my-function", + runtime: "python37", + region: "europe-west1", + kmsKeyName: "my-key", + eventTrigger: { + eventType: "google.storage.object.finalize", + resource: "my-bucket", + }, +}); + +export const cloudfunctionsFunctionGo = new gcp.cloudfunctions.Function("fail#4", { + name: "my-function", + runtime: "go119", + region: "europe-west1", + kmsKeyName: "my-key", + eventTrigger: { + eventType: "google.storage.object.finalize", + resource: "my-bucket", + }, +}); + +export const cloudfunctionsFunctionJava = new gcp.cloudfunctions.Function("fail#5", { + name: "my-function", + runtime: "java17", + region: "europe-west1", + kmsKeyName: "my-key", + eventTrigger: { + eventType: "google.storage.object.finalize", + resource: "my-bucket", + }, +}); + +export const cloudfunctionsFunctionRuby = new gcp.cloudfunctions.Function("fail#6", { + name: "my-function", + runtime: "ruby30", + region: "europe-west1", + kmsKeyName: "my-key", + eventTrigger: { + eventType: "google.storage.object.finalize", + resource: "my-bucket", + }, +}); + +export const cloudfunctionsFunctionPhp = new gcp.cloudfunctions.Function("fail#7", { + name: "my-function", + runtime: "php74", + region: "europe-west1", + kmsKeyName: "my-key", + eventTrigger: { + eventType: "google.storage.object.finalize", + resource: "my-bucket", + }, +}); + +export const cloudfunctionsFunctionDotNetCore = new gcp.cloudfunctions.Function("fail#8", { + name: "my-function", + runtime: "dotnet3", + region: "europe-west1", + kmsKeyName: "my-key", + eventTrigger: { + eventType: "google.storage.object.finalize", + resource: "my-bucket", + }, +}); diff --git a/src/cloudfunctions/disallowEndOfLifeRuntime.ts b/src/cloudfunctions/disallowEndOfLifeRuntime.ts index e69de29..d1a5fc1 100644 --- a/src/cloudfunctions/disallowEndOfLifeRuntime.ts +++ b/src/cloudfunctions/disallowEndOfLifeRuntime.ts @@ -0,0 +1,55 @@ +import { ResourceValidationArgs, ReportViolation, EnforcementLevel } from "@pulumi/policy"; + +export const disallowEndOfLifeRuntime = { + name: "cloudrunfunctions-function-disallow-end-of-life-runtime", + description: "Check that CloudFunctions function does not use end-of-life runtime.", + enforcementLevel: "advisory" as EnforcementLevel, + validateResource: (args: ResourceValidationArgs, reportViolation: ReportViolation) => { + if (args.type === "gcp:cloudfunctions/function:Function") { + const message = "CloudFunctions function should use runtime with active or security support." + const runtime = args.props.runtime; + const match = runtime.match(/^([a-z]+)(\d+)$/i); + if (match) { + let lang = match[1]; + let version = match[2]; + switch (lang) { + case "nodejs": // https://endoflife.date/nodejs + if (version < 18) { + reportViolation(message); + } + break; + case "python": // https://endoflife.date/python + if (version < 38) { + reportViolation(message); + } + break; + case "go": // https://endoflife.date/go + if (version < 120) { + reportViolation(message); + } + break; + case "java": // https://endoflife.date/openjdk-builds-from-oracle + if (version < 21) { + reportViolation(message); + } + break; + case "ruby": // https://endoflife.date/ruby + if (version < 31) { + reportViolation(message); + } + break; + case "php": + if (version < 81) { // https://endoflife.date/php + reportViolation(message); + } + break; + case "dotnet": + if (version < 6) { // https://endoflife.date/dotnet + reportViolation(message); + } + break; + } + } + } + }, +} diff --git a/src/cloudfunctions/disallowPublicIngress.ts b/src/cloudfunctions/disallowPublicIngress.ts index 1ebf9c3..1ac8857 100644 --- a/src/cloudfunctions/disallowPublicIngress.ts +++ b/src/cloudfunctions/disallowPublicIngress.ts @@ -6,8 +6,8 @@ export const disallowPublicIngress = { enforcementLevel: "advisory" as EnforcementLevel, validateResource: (args: ResourceValidationArgs, reportViolation: ReportViolation) => { if (args.type === "gcp:cloudfunctions/function:Function") { - const ingress = args.props.ingressSettings; const trigger = args.props.triggerHttp; + const ingress = args.props.ingressSettings; if (trigger && ingress === "ALLOW_ALL") { reportViolation( "CloudFunctions function should not allow public ingress from 'all'. Use a load balancer instead." diff --git a/src/cloudfunctions/index.ts b/src/cloudfunctions/index.ts index 5dfe7a7..8ea78fb 100644 --- a/src/cloudfunctions/index.ts +++ b/src/cloudfunctions/index.ts @@ -1,4 +1,4 @@ -// import { disallowEndOfLifeRuntime } from "./disallowEndOfLifeRuntime"; +import { disallowEndOfLifeRuntime } from "./disallowEndOfLifeRuntime"; import { disallowEnvsSecrets } from "./disallowEnvsSecrets"; import { disallowPlainHttp } from "./disallowPlainHttp"; import { disallowPublicIngress } from "./disallowPublicIngress"; @@ -6,7 +6,7 @@ import { disallowVpcConnectorPublicEgress } from "./disallowVpcConnectorPublicEg import { requireCmek } from "./requireCmek"; export const cloudfunctionsPolicies = [ - // disallowEndOfLifeRuntime, + disallowEndOfLifeRuntime, disallowEnvsSecrets, disallowPlainHttp, disallowPublicIngress,