# Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>

play.modules {
  enabled += "play.filters.csrf.CSRFModule"
  enabled += "play.filters.cors.CORSModule"
  enabled += "play.filters.csp.CSPModule"
  enabled += "play.filters.headers.SecurityHeadersModule"
  enabled += "play.filters.hosts.AllowedHostsModule"
  enabled += "play.filters.gzip.GzipFilterModule"
  enabled += "play.filters.https.RedirectHttpsModule"
  enabled += "play.filters.ip.IPFilterModule"
}

play.filters {

  # Default list of enabled filters, configured by play.api.http.EnabledFilters
  enabled += play.filters.csrf.CSRFFilter
  enabled += play.filters.headers.SecurityHeadersFilter
  enabled += play.filters.hosts.AllowedHostsFilter

  # CSRF config
  csrf {

    # Token configuration
    token {
      # The token name
      name = "csrfToken"

      # Whether tokens should be signed or not
      sign = true
    }

    # Cookie configuration
    cookie {
      # If non null, the CSRF token will be placed in a cookie with this name
      name = null

      # Whether the cookie should be set to secure
      secure = ${play.http.session.secure}

      # Whether the cookie should have the HTTP only flag set
      httpOnly = false

      # The value of the SameSite attribute of the cookie. Set to null for no SameSite attribute.
      # Possible values are "lax", "strict" and "none". If misconfigured it's set to null.
      sameSite = ${play.http.session.sameSite}
    }

    # How much of the body should be buffered when looking for the token in the request body
    body.bufferSize = ${play.http.parser.maxMemoryBuffer}

    # Bypass the CSRF check if this origin is trusted by the CORS filter
    bypassCorsTrustedOrigins = true

    # Header configuration
    header {

      # The name of the header to accept CSRF tokens from.
      name = "Csrf-Token"


      # Defines headers that must be present to perform the CSRF check. If any of these headers are present, the CSRF
      # check will be performed.
      #
      # By default, we only perform the CSRF check if there are Cookies or an Authorization header.
      # Generally, CSRF attacks use a user's browser to execute requests on the client's behalf. If the user does not
      # have an active session, there is no danger of this happening.
      #
      # Setting this to null or an empty object will protect all requests.
      protectHeaders {
        Cookie = "*"
        Authorization = "*"
      }

      # Defines headers that can be used to bypass the CSRF check if any are present. A value of "*" simply
      # checks for the presence of the header. A string value checks for a match on that string.
      bypassHeaders {}
    }

    # Method lists
    method {
      # If non empty, then requests will be checked if the method is not in this list.
      whiteList = ["GET", "HEAD", "OPTIONS"]

      # The black list is only used if the white list is empty.
      # Only check methods in this list.
      blackList = []
    }

    # Content type lists
    # If both white lists and black lists are empty, then all content types are checked.
    contentType {
      # If non empty, then requests will be checked if the content type is not in this list.
      whiteList = []

      # The black list is only used if the white list is empty.
      # Only check content types in this list.
      blackList = []
    }

    routeModifiers {
      # If non empty, then requests will be checked if the route does not have this modifier. This is how we enable the
      # nocsrf modifier, but you may choose to use a different modifier (such as "api") if you plan to check the
      # modifier in your code for other purposes.
      whiteList = ["nocsrf"]

      # If non empty, then requests will be checked if the route contains this modifier
      # The black list is used only if the white list is empty
      blackList = []
    }

    # The error handler.
    # Used by Play's built in DI support to locate and bind a request handler.  Must be one of the following:
    # - A FQCN that implements play.filters.csrf.CSRF.ErrorHandler (Scala).
    # - A FQCN that implements play.filters.csrf.CSRFErrorHandler (Java).
    # - provided, indicates that the application has bound an instance of play.filters.csrf.CSRF.ErrorHandler through some
    #   other mechanism.
    # If null, will attempt to load a class called CSRFErrorHandler in the root package, otherwise if that's
    # not found, will default to play.filters.csrf.CSRF.CSRFHttpErrorHandler, which delegates to the configured
    # HttpRequestHandler.
    errorHandler = null
  }

  # Security headers filter configuration
  headers {

    # The X-Frame-Options header. If null, the header is not set.
    frameOptions = "DENY"

    # The X-XSS-Protection header. If null, the header is not set.
    xssProtection = "1; mode=block"

    # The X-Content-Type-Options header. If null, the header is not set.
    contentTypeOptions = "nosniff"

    # The X-Permitted-Cross-Domain-Policies header. If null, the header is not set.
    permittedCrossDomainPolicies = "master-only"

    # DEPRECATED: Content Security Policy.  If null, the header is not set.
    # This config property is set to null deliberately as the CSPFilter replaces it.
    contentSecurityPolicy = null

    # The Referrer-Policy header. If null, the header is not set.
    referrerPolicy = "origin-when-cross-origin, strict-origin-when-cross-origin"

    # If true, allow an action to use .withHeaders to replace one or more of the above headers
    allowActionSpecificHeaders = false
  }

  # Content Security Policy filter configuration
  # Please see https://playframework.com/documentation/latest/CspFilter for more details.
  csp {
    # If true, the CSP output uses Content-Security-Policy-Report-Only header instead.
    reportOnly = false

    routeModifiers {
      # If non empty, then requests will be checked if the route does not have this modifier.
      whiteList = ["nocsp"]

      # If non empty, then requests will be checked if the route contains this modifier
      # The black list is used only if the white list is empty
      blackList = []
    }

    # #csp-nonce
    # Specify a nonce to be used in CSP security header
    # https://www.w3.org/TR/CSP3/#security-nonces
    #
    # Nonces are used in script and style elements to protect against XSS attacks.
    nonce {
      # Use nonce value (generated and passed in through request attribute)
      enabled = true

      # Pattern to use to replace with nonce
      pattern = "%CSP_NONCE_PATTERN%"

      # Add the nonce to "X-Content-Security-Policy-Nonce" header.  This is useful for debugging.
      header = false
    }
    # #csp-nonce

    # Specify hashes that are used internally in the content security policy.
    # The format of these hashes are as follows:
    #
    # {
    #   algorithm = sha256
    #   hash = "RpniQm4B6bHP0cNtv7w1p6pVcgpm5B/eu1DNEYyMFXc="
    #   pattern = "%CSP_MYSCRIPT_HASH%"
    # }
    #
    # and should be used inline the same way as the nonce pattern, i.e.
    #
    # script-src = "%CSP_MYSCRIPT_HASH% 'strict-dynamic' ..."
    hashes = []

    # #csp-directives
    # The directives here are set to the Google Strict CSP policy by default
    # https://csp.withgoogle.com/docs/strict-csp.html
    directives {
      # base-uri defaults to 'none' according to https://csp.withgoogle.com/docs/strict-csp.html
      # https://www.w3.org/TR/CSP3/#directive-base-uri
      base-uri = "'none'"

      # object-src defaults to 'none' according to https://csp.withgoogle.com/docs/strict-csp.html
      # https://www.w3.org/TR/CSP3/#directive-object-src
      object-src = "'none'"

      # script-src defaults according to https://csp.withgoogle.com/docs/strict-csp.html
      # https://www.w3.org/TR/CSP3/#directive-script-src
      script-src = ${play.filters.csp.nonce.pattern} "'unsafe-inline' 'unsafe-eval' 'strict-dynamic' https: http:"
    }
    # #csp-directives
  }

  # Allowed hosts filter configuration
  hosts {

    # A list of valid hosts (e.g. "example.com") or suffixes of valid hosts (e.g. ".example.com")
    # Note that ".example.com" will match example.com and any subdomain of example.com, with or without a trailing dot.
    # "." matches all domains, and "" matches an empty or nonexistent host.
    allowed = ["localhost", ".local", "127.0.0.1"]

    routeModifiers {
      # If non empty, then requests will be checked if the route does not have this modifier. This is how we enable the
      # anyhost modifier, but you may choose to use a different modifier (such as "api") if you plan to check the
      # modifier in your code for other purposes.
      whiteList = ["anyhost"]

      # If non empty, then requests will be checked if the route contains this modifier
      # The black list is used only if the white list is empty
      blackList = []
    }
  }

  # CORS filter configuration
  cors {

    # The path prefixes to filter.
    pathPrefixes = ["/"]

    # The allowed origins. If null, all origins are allowed.
    # The value `"*"` has a special meaning, it will send back `Access-Control-Allow-Origin: *`
    # if the request origin does not match any other origins in the list.
    allowedOrigins = null

    # The allowed HTTP methods. If null, all methods are allowed
    allowedHttpMethods = null

    # The allowed HTTP headers. If null, all headers are allowed.
    allowedHttpHeaders = null

    # The exposed headers
    exposedHeaders = []

    # Whether to support credentials
    supportsCredentials = true

    # The maximum amount of time the CORS meta data should be cached by the client
    preflightMaxAge = 1 hour

    # Whether to serve forbidden origins as non-CORS requests
    serveForbiddenOrigins = false
  }

  # GZip filter configuration
  gzip {

    # The maximum amount of data to send to the Gzip compressor in one go. Use `0` or `2147483647` to disable the buffering.
    #
    # In general, it is recommended to turn off the buffer and prevent generation of overlong chunks at the source.
    bufferSize = 8k

    # The maximum amount of content to buffer for gzipping in order to calculate the content length before falling back
    # to chunked encoding.
    chunkedThreshold = 100k

    contentType {

        # If non empty, then a response will only be compressed if its content type is in this list.
        whiteList = []

        # The black list is only used if the white list is empty.
        # Compress all responses except the ones whose content type is in this list.
        blackList = []
    }

    # The compression level to use, integer, -1 to 9, inclusive. See java.util.zip.Deflater.
    compressionLevel = -1

    # The byte threshold for the response body size which controls if a response should be gzipped (e.g. 1k).
    # If the body size cannot be determined, then it is assumed the response is over the threshold.
    # Set to 0 if you want to compress all responses, no matter how large the response body size is.
    threshold = 0
  }

  # Configuration for redirection to HTTPS and Strict-Transport-Security
  https {

    # A boolean defining whether the redirect to HTTPS is enabled.
    # A value of null means enabled only in Prod mode, but disabled in Dev/Test.
    redirectEnabled = null

    # The Strict-Transport-Security header is used to signal to browsers to always use https.
    # This header is added whenever a request is secure and redirectEnabled is true.
    # Set to null to disable the header.
    strictTransportSecurity = "max-age=31536000; includeSubDomains"

    # Configures the redirect status code used if the request is not secure.
    # By default, uses HTTP status code 308, which is a permanent redirect that does
    # not change the HTTP method according to [RFC 7238](https://tools.ietf.org/html/rfc7538).
    redirectStatusCode = 308

    # A boolean defining whether to only redirect if a x-forwarded-proto header is set to http.
    # This is a defacto standard that will be used by various proxys or load balancers to determine
    # if a redirect should happen.
    # [X-Forwarded-Proto](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto)
    xForwardedProtoEnabled = false

    # No redirect happens if path is in this list.
    # Query params don't matter, meaning if the list contains "/foo" the request "/foo?abc=xyz" will be excluded too.
    # Paths in this list are encoded, if you want to exclude "/foöbär" you have to add "/fo%C3%B6b%C3%A4r" to the list.
    excludePaths = []

    # The HTTPS port to use in the Redirect's Location URL.
    # e.g. port = 9443 results in https://playframework.com:9443/some/url
    port = null
    port = ${?play.server.https.port} # default to same HTTPS port as play server
  }

  # IP filter configuration
  ip {

    # An integer defining the default HTTP status code for denied IPs.
    # For example, if you want to hide an endpoint you could provide 404 ("Not Found").
    accessDeniedHttpStatusCode = 403

    # If not empty, a request will only be allowed if its IP address exists in the whitelist.
    whiteList = []

    # The black list is only used if the white list is empty.
    # If not empty, a request will be denied if its IP address exists in the blacklist.
    blackList = []

    routeModifiers {
      # If non empty, then requests will be checked if the route does not have this modifier. This is how we enable the
      # anyip modifier, but you may choose to use a different modifier (such as "noipcheck") if you plan to check the
      # modifier in your code for other purposes.
      whiteList = ["anyip"]

      # If non empty, then requests will be checked if the route contains this modifier
      # The black list is used only if the white list is empty
      blackList = []
    }
  }
}
