Skip to content

Commit

Permalink
replace some hard coded clauses of the Content-Security-Policy with v…
Browse files Browse the repository at this point in the history
…alues from config to remove guardian specific stuff
  • Loading branch information
twrichards committed Apr 8, 2024
1 parent dd79340 commit 2effb99
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 16 deletions.
4 changes: 3 additions & 1 deletion dev/script/generate-config/service-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
| }
|]`;

Expand Down
44 changes: 31 additions & 13 deletions kahuna/app/KahunaComponents.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,20 @@ object KahunaSecurityConfig {
def apply(config: KahunaConfig, playConfig: Configuration): SecurityHeadersConfig = {
val base = SecurityHeadersConfig.fromConfiguration(playConfig)

val services = List(
def cspClause(directive: String, sources: Set[String]): String = s"$directive ${sources.mkString(" ")}"

val frameSources = cspClause("frame-src", Set(
config.services.authBaseUri,
config.services.kahunaBaseUri)
++ config.frameSources
++ config.scriptsToLoad.flatMap(_.cspFrameSources)
)
val frameAncestors = cspClause("frame-ancestors",
config.frameAncestors
)
val connectSources = cspClause("connect-src", Set(
"'self'",
config.imageOrigin,
config.services.apiBaseUri,
config.services.loaderBaseUri,
config.services.cropperBaseUri,
Expand All @@ -34,28 +47,33 @@ object KahunaSecurityConfig {
config.services.collectionsBaseUri,
config.services.leasesBaseUri,
config.services.authBaseUri,
config.services.guardianWitnessBaseUri
config.services.guardianWitnessBaseUri)
++ config.connectSources
)

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 frameAncestors = s"frame-ancestors ${config.frameAncestors.mkString(" ")}"
val connectSources = s"connect-src 'self' ${(services :+ config.imageOrigin).mkString(" ")} ${config.connectSources.mkString(" ")}"

val imageSources = s"img-src ${List(
val imageSources = cspClause("img-src", Set(
"'self'",
"data:",
"blob:",
URI.ensureSecure(config.services.imgopsBaseUri).toString,
URI.ensureSecure(config.fullOrigin).toString,
URI.ensureSecure(config.thumbOrigin).toString,
URI.ensureSecure(config.cropOrigin).toString,
URI.ensureSecure("app.getsentry.com").toString,
"https://*.googleusercontent.com",
"'self'"
).mkString(" ")} ${config.imageSources.mkString(" ")}"
URI.ensureSecure("app.getsentry.com").toString)
++ config.scriptsToLoad.flatMap(_.cspImageSources)
++ config.imageSources
)

val fontSources = s"font-src data: 'self' ${config.fontSources.mkString(" ")}"
val fontSources = cspClause("font-src", Set(
"'self'")
++ config.fontSources
)

val scriptSources = s"script-src 'self' 'unsafe-inline' ${config.scriptsToLoad.map(_.host).mkString(" ")}"
val scriptSources = cspClause("script-src", Set(
"'self'",
"'unsafe-inline'")
++ config.scriptsToLoad.flatMap(_.cspScriptSources)
)

base.copy(
// covered by frame-ancestors in contentSecurityPolicy
Expand Down
15 changes: 13 additions & 2 deletions kahuna/app/lib/KahunaConfig.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand All @@ -56,6 +65,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")
Expand Down

0 comments on commit 2effb99

Please sign in to comment.