From 94b144a020109d9559b19ab0786d05a066617dee Mon Sep 17 00:00:00 2001 From: Tom Richards Date: Mon, 25 Mar 2024 13:02:18 +0000 Subject: [PATCH] replace some hard coded clauses of the Content-Security-Policy with values from config to remove guardian specific stuff --- dev/script/generate-config/service-config.js | 4 +++- kahuna/app/KahunaComponents.scala | 6 +++--- kahuna/app/lib/KahunaConfig.scala | 15 +++++++++++++-- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/dev/script/generate-config/service-config.js b/dev/script/generate-config/service-config.js index 1bb84ad838..6e6f283f35 100644 --- a/dev/script/generate-config/service-config.js +++ b/dev/script/generate-config/service-config.js @@ -90,7 +90,9 @@ function getKahunaConfig(config){ | host: "https://pinboard.${config.DOMAIN}", | path: "pinboard.loader.js", | async: true, - | permission: "pinboard" + | permission: "pinboard", + | additionalFrameSourcesForCSP: ["https://www.youtube.com"] + | additionalImageSourcesForCSP: ["https://*.googleusercontent.com"] | } |]`; diff --git a/kahuna/app/KahunaComponents.scala b/kahuna/app/KahunaComponents.scala index 2335966d82..184e559f2d 100644 --- a/kahuna/app/KahunaComponents.scala +++ b/kahuna/app/KahunaComponents.scala @@ -37,7 +37,7 @@ object KahunaSecurityConfig { config.services.guardianWitnessBaseUri ) - val frameSources = s"frame-src ${config.services.authBaseUri} ${config.services.kahunaBaseUri} https://accounts.google.com https://www.youtube.com ${config.scriptsToLoad.map(_.host).mkString(" ")}" + val frameSources = s"frame-src ${config.services.authBaseUri} ${config.services.kahunaBaseUri} ${config.scriptsToLoad.flatMap(_.cspFrameSources).mkString(" ")}" val frameAncestors = s"frame-ancestors ${config.frameAncestors.mkString(" ")}" val connectSources = s"connect-src 'self' ${(services :+ config.imageOrigin).mkString(" ")} ${config.connectSources.mkString(" ")}" @@ -49,13 +49,13 @@ object KahunaSecurityConfig { URI.ensureSecure(config.thumbOrigin).toString, URI.ensureSecure(config.cropOrigin).toString, URI.ensureSecure("app.getsentry.com").toString, - "https://*.googleusercontent.com", + config.scriptsToLoad.flatMap(_.cspImageSources).mkString(" "), "'self'" ).mkString(" ")}" val fontSources = s"font-src data: 'self' ${config.fontSources.mkString(" ")}" - val scriptSources = s"script-src 'self' 'unsafe-inline' ${config.scriptsToLoad.map(_.host).mkString(" ")}" + val scriptSources = s"script-src 'self' 'unsafe-inline' ${config.scriptsToLoad.flatMap(_.cspScriptSources).mkString(" ")}" base.copy( // covered by frame-ancestors in contentSecurityPolicy diff --git a/kahuna/app/lib/KahunaConfig.scala b/kahuna/app/lib/KahunaConfig.scala index a9006ef139..a5da48f38c 100644 --- a/kahuna/app/lib/KahunaConfig.scala +++ b/kahuna/app/lib/KahunaConfig.scala @@ -4,13 +4,21 @@ import com.gu.mediaservice.lib.auth.Permissions.Pinboard import com.gu.mediaservice.lib.auth.SimplePermission import com.gu.mediaservice.lib.config.{CommonConfig, GridConfigResources} +import scala.jdk.CollectionConverters.iterableAsScalaIterableConverter + case class ScriptToLoad( host: String, path: String, async: Option[Boolean], permission: Option[SimplePermission], - shouldLoadWhenIFramed: Option[Boolean] -) + shouldLoadWhenIFramed: Option[Boolean], + additionalFrameSourcesForCSP: Option[Set[String]] = None, + additionalImageSourcesForCSP: Option[Set[String]] = None, +) { + def cspScriptSources: Set[String] = Set(host) + def cspFrameSources: Set[String] = additionalFrameSourcesForCSP.getOrElse(Set.empty) + host + def cspImageSources: Set[String] = additionalImageSourcesForCSP.getOrElse(Set.empty) +} class KahunaConfig(resources: GridConfigResources) extends CommonConfig(resources) { val rootUri: String = services.kahunaBaseUri @@ -41,6 +49,7 @@ class KahunaConfig(resources: GridConfigResources) extends CommonConfig(resource val showDenySyndicationWarning: Option[Boolean] = booleanOpt("showDenySyndicationWarning") + val frameSources: Set[String] = getStringSet("security.frameSources") val frameAncestors: Set[String] = getStringSet("security.frameAncestors") val connectSources: Set[String] = getStringSet("security.connectSources") ++ maybeIngestBucket.map { ingestBucket => if (isDev) "https://localstack.media.local.dev-gutools.co.uk" @@ -55,6 +64,8 @@ class KahunaConfig(resources: GridConfigResources) extends CommonConfig(resource // FIXME ideally the below would not hardcode reference to pinboard - hopefully future iterations of the pluggable authorisation will support evaluating permissions without a corresponding case object permission = if (entry.hasPath("permission") && entry.getString("permission") == "pinboard") Some(Pinboard) else None, shouldLoadWhenIFramed = if (entry.hasPath("shouldLoadWhenIFramed")) Some(entry.getBoolean("shouldLoadWhenIFramed")) else None, + additionalFrameSourcesForCSP = if (entry.hasPath("additionalFrameSourcesForCSP")) Some(entry.getStringList("additionalFrameSourcesForCSP").asScala.toSet) else None, + additionalImageSourcesForCSP = if (entry.hasPath("additionalImageSourcesForCSP")) Some(entry.getStringList("additionalImageSourcesForCSP").asScala.toSet) else None, )) val metadataTemplates: Seq[MetadataTemplate] = configuration.get[Seq[MetadataTemplate]]("metadata.templates")