Skip to content

Commit

Permalink
Merge branch 'master' of github.com:lichess-org/lila
Browse files Browse the repository at this point in the history
* 'master' of github.com:lichess-org/lila:
  scalafmt
  Fix HttpFilter env
  Disable coep:credentialless on PayPal patron page
  Add setting to disable site-wide coep:credentialless
  Cleanup
  Only enable site-wide coep:credentialless in Chrome 113+
  Allow coep:credentialless on broadcasts with embeds
  fix master merge
  fix scala syntax
  Update app/http/ResponseHeaders.scala
  Update app/controllers/RelayRound.scala
  fix call to embedderPolicy.supportsCoepCredentialless
  remove code duplication
  always add Cross-Origin-Embedder-Policy header, unless already set
  • Loading branch information
ornicar committed Apr 15, 2024
2 parents ff1dc4a + 9c749d2 commit 936a53e
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 21 deletions.
2 changes: 1 addition & 1 deletion app/LilaComponents.scala
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ final class LilaComponents(
lila.log("boot").info(s"Loaded lila modules in ${c.showDuration}")
c.result

val httpFilters = Seq(lila.web.HttpFilter(env.net, lila.security.Mobile.LichessMobileUa.parse))
val httpFilters = Seq(lila.web.HttpFilter(env.web, env.net, lila.security.Mobile.LichessMobileUa.parse))

override lazy val httpErrorHandler =
lila.app.http.ErrorHandler(
Expand Down
4 changes: 3 additions & 1 deletion app/controllers/Plan.scala
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ final class Plan(env: Env) extends LilaController(env):
bestIds = bestIds,
pricing = pricing
)
yield Ok(page)
yield Ok(page).withHeaders(crossOriginPolicy.unsafe*)

private def indexStripePatron(patron: lila.plan.Patron, customer: StripeCustomer)(using
ctx: Context,
Expand All @@ -98,6 +98,8 @@ final class Plan(env: Env) extends LilaController(env):
) =
Ok.pageAsync:
env.plan.api.giftsFrom(me).map { html.plan.indexPayPal(me, patron, sub, _) }
.map:
_.withHeaders(crossOriginPolicy.unsafe*)

private def myCurrency(using ctx: Context): Currency =
get("currency")
Expand Down
12 changes: 2 additions & 10 deletions modules/web/src/main/CtrlExtensions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import lila.core.i18n.Translate
import lila.core.perf.UserWithPerfs
import lila.core.config.BaseUrl

trait CtrlExtensions extends play.api.mvc.ControllerHelpers:
trait CtrlExtensions extends play.api.mvc.ControllerHelpers with ResponseHeaders:

def baseUrl: BaseUrl

Expand All @@ -34,15 +34,7 @@ trait CtrlExtensions extends play.api.mvc.ControllerHelpers:
result.withHeaders(LINK -> s"<${baseUrl}${url}>; rel=\"canonical\"")
def withCanonical(url: Call): Result = withCanonical(url.url)
def enforceCrossSiteIsolation(using req: RequestHeader): Result =
val coep =
if HTTPRequest.isChrome96Plus(req) ||
(HTTPRequest.isFirefox119Plus(req) && !HTTPRequest.isMobileBrowser(req))
then "credentialless"
else "require-corp"
result.withHeaders(
"Cross-Origin-Embedder-Policy" -> coep,
"Cross-Origin-Opener-Policy" -> "same-origin"
)
result.withHeaders(crossOriginPolicy.forReq(req)*)
def noCache: Result = result.withHeaders(
CACHE_CONTROL -> "no-cache, no-store, must-revalidate",
EXPIRES -> "0"
Expand Down
5 changes: 5 additions & 0 deletions modules/web/src/main/Env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,8 @@ final class Env(
default = false,
text = "Use external piece images".some
)
val sitewideCoepCredentiallessHeader = settingStore[Boolean](
"sitewideCoepCredentiallessHeader",
default = true,
text = "Enable COEP:credentialless header site-wide in supported browsers (Chromium)".some
)
14 changes: 11 additions & 3 deletions modules/web/src/main/HttpFilter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import lila.common.HTTPRequest
import lila.core.config.NetConfig
import lila.core.net.LichessMobileUa

final class HttpFilter(net: NetConfig, parseMobileUa: RequestHeader => Option[LichessMobileUa])(using
val mat: Materializer
final class HttpFilter(webEnv: Env, net: NetConfig, parseMobileUa: RequestHeader => Option[LichessMobileUa])(
using val mat: Materializer
)(using Executor)
extends Filter
with ResponseHeaders:
Expand All @@ -23,7 +23,8 @@ final class HttpFilter(net: NetConfig, parseMobileUa: RequestHeader => Option[Li
handle(req).map: result =>
monitoring(req, startTime):
addContextualResponseHeaders(req):
result
addEmbedderPolicyHeaders(req):
result
}

private def monitoring(req: RequestHeader, startTime: Long)(result: Result) =
Expand Down Expand Up @@ -54,3 +55,10 @@ final class HttpFilter(net: NetConfig, parseMobileUa: RequestHeader => Option[Li
if HTTPRequest.isApiOrApp(req)
then result.withHeaders(headersForApiOrApp(using req)*)
else result.withHeaders(permissionsPolicyHeader)

private def addEmbedderPolicyHeaders(req: RequestHeader)(result: Result) =
if !crossOriginPolicy.isSet(result)
&& crossOriginPolicy.supportsCredentiallessIFrames(req)
&& webEnv.settings.sitewideCoepCredentiallessHeader.get()
then result.withHeaders(crossOriginPolicy.credentialless*)
else result
32 changes: 27 additions & 5 deletions modules/web/src/main/ResponseHeaders.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,6 @@ trait ResponseHeaders extends HeaderNames:
"Cross-Origin-Embedder-Policy" -> "require-corp" // for Stockfish worker
)

val credentiallessHeaders = List(
"Cross-Origin-Opener-Policy" -> "same-origin",
"Cross-Origin-Embedder-Policy" -> "credentialless"
)

val permissionsPolicyHeader =
"Permissions-Policy" -> List(
"screen-wake-lock=(self \"https://lichess1.org\")",
Expand All @@ -58,3 +53,30 @@ trait ResponseHeaders extends HeaderNames:
def asAttachmentStream(name: String)(res: Result) = noProxyBuffer(asAttachment(name)(res))

def lastModified(date: Instant) = LAST_MODIFIED -> date.atZone(utcZone)

object crossOriginPolicy:

def isSet(result: Result) = result.header.headers.contains(embedderPolicyHeader)

def forReq(req: RequestHeader) =
if supportsCoepCredentialless(req) then credentialless else requireCorp

def supportsCoepCredentialless(req: RequestHeader) =
import HTTPRequest.*
isChrome96Plus(req) || (isFirefox119Plus(req) && !isMobileBrowser(req))

def supportsCredentiallessIFrames(req: RequestHeader) =
import HTTPRequest.*
isChrome113Plus(req)

def unsafe = headers("unsafe-none")
def credentialless = headers("credentialless")
def requireCorp = headers("require-corp")

private val openerPolicyHeader = "Cross-Origin-Opener-Policy"
private val embedderPolicyHeader = "Cross-Origin-Embedder-Policy"

private def headers(policy: "credentialless" | "require-corp" | "unsafe-none") = List(
openerPolicyHeader -> (if policy == "unsafe-none" then "unsafe-none" else "same-origin"),
embedderPolicyHeader -> policy
)
3 changes: 2 additions & 1 deletion ui/analyse/src/study/relay/videoPlayerView.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import RelayCtrl from './relayCtrl';
import { looseH as h, Redraw, VNode } from 'common/snabbdom';
import RelayCtrl from './relayCtrl';
import { allowVideo } from './relayView';

let player: VideoPlayer;
Expand Down Expand Up @@ -43,6 +43,7 @@ class VideoPlayer {
this.iframe.id = 'video-player';
this.iframe.src = relay.data.videoUrls![0];
this.iframe.allow = 'autoplay';
this.iframe.setAttribute('credentialless', 'credentialless');
this.close = document.createElement('img');
this.close.src = site.asset.flairSrc('symbols.cancel');
this.close.className = 'video-player-close';
Expand Down

0 comments on commit 936a53e

Please sign in to comment.