001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.coep; 018 019import org.apache.wicket.coop.CrossOriginOpenerPolicyRequestCycleListener; 020import org.apache.wicket.request.IRequestHandler; 021import org.apache.wicket.request.cycle.IRequestCycleListener; 022import org.apache.wicket.request.cycle.RequestCycle; 023import org.apache.wicket.request.http.WebResponse; 024import org.apache.wicket.util.lang.Args; 025import org.slf4j.Logger; 026import org.slf4j.LoggerFactory; 027 028import javax.servlet.http.HttpServletRequest; 029 030/** 031 * Sets <a href="https://wicg.github.io/cross-origin-embedder-policy/">Cross-Origin Embedder 032 * Policy</a> (COEP) headers on the responses based on the mode specified by 033 * {@link CrossOriginEmbedderPolicyConfiguration}. COEP can be enabled in <code>REPORTING</code> 034 * mode which will set the headers as <code>Cross-Origin-Embedder-Policy-Report-Only</code> or 035 * <code>ENFORCING</code> mode which will set the header as 036 * <code>Cross-Origin-Embedder-Policy</code>. The header is not set for the paths that are exempted 037 * from COEP. The only valid value of COEP is <code>require-corp</code>, so if the listener is 038 * enabled the policy value will be specified as so. 039 * <p> 040 * COEP prevents a document from loading any non-same-origin resources which don't explicitly grant 041 * the document permission to be loaded. Using COEP and COOP together allows developers to safely 042 * use powerful features such as <code>SharedArrayBuffer</code>, 043 * <code>performance.measureMemory()</code>, and the JS Self-Profiling API.See 044 * {@link CrossOriginOpenerPolicyRequestCycleListener} for instructions on how to enable COOP. 045 * Read more about cross-origin isolation on 046 * <a href="https://web.dev/why-coop-coep/">https://web.dev/why-coop-coep/</a> 047 * <p> 048 * 049 * @author Santiago Diaz - saldiaz@google.com 050 * @author Ecenaz Jen Ozmen - ecenazo@google.com 051 * 052 * @see CrossOriginEmbedderPolicyConfiguration 053 * @see org.apache.wicket.settings.SecuritySettings 054 */ 055public class CrossOriginEmbedderPolicyRequestCycleListener implements IRequestCycleListener 056{ 057 private static final Logger log = LoggerFactory.getLogger(CrossOriginEmbedderPolicyRequestCycleListener.class); 058 059 static final String REQUIRE_CORP = "require-corp"; 060 061 private final CrossOriginEmbedderPolicyConfiguration coepConfig; 062 063 public CrossOriginEmbedderPolicyRequestCycleListener(CrossOriginEmbedderPolicyConfiguration coepConfig) 064 { 065 this.coepConfig = Args.notNull(coepConfig, "coepConfig"); 066 } 067 068 @Override 069 public void onRequestHandlerResolved(RequestCycle cycle, IRequestHandler handler) 070 { 071 // WICKET-7028- this is needed for redirect to buffer use case. 072 protect(cycle, handler); 073 } 074 075 @Override 076 public void onRequestHandlerExecuted(RequestCycle cycle, IRequestHandler handler) 077 { 078 protect(cycle, handler); 079 } 080 081 protected void protect(RequestCycle cycle, IRequestHandler handler) 082 { 083 final Object containerRequest = cycle.getRequest().getContainerRequest(); 084 if (containerRequest instanceof HttpServletRequest) 085 { 086 HttpServletRequest request = (HttpServletRequest) containerRequest; 087 String path = request.getContextPath(); 088 final String coepHeaderName = coepConfig.getCoepHeader(); 089 090 if (coepConfig.getExemptions().contains(path)) 091 { 092 log.debug("Request path {} is exempted from COEP, no '{}' header added", path, coepHeaderName); 093 return; 094 } 095 096 if (cycle.getResponse() instanceof WebResponse) 097 { 098 WebResponse webResponse = (WebResponse) cycle.getResponse(); 099 if (webResponse.isHeaderSupported()) 100 { 101 webResponse.setHeader(coepHeaderName, REQUIRE_CORP); 102 } 103 } 104 } 105 } 106 107}