This is an automated email from the ASF dual-hosted git repository.

mgrigorov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/wicket.git


The following commit(s) were added to refs/heads/master by this push:
     new ca8ec2797d WICKET-7149: Add Integrity and CrossOrigin values to 
ResourceReference and related code (#1102)
ca8ec2797d is described below

commit ca8ec2797d694bec701e001d66708338f8f9561e
Author: mpritt <[email protected]>
AuthorDate: Thu Feb 20 01:40:02 2025 -0700

    WICKET-7149: Add Integrity and CrossOrigin values to ResourceReference and 
related code (#1102)
    
    WICKET-7149:  Adding Integrity and CrossOrigin values to ResourceReference 
class and using values for creating JS/CSS reference header items
    
    Added two new variables to the ResourceReference class.  These new values 
allow for better JavaScriptReferenceHeaderItem and CssReferenceHeaderItem 
creation, allowing those classes to access the resource reference 
identity/CrossOrigin values if they are not already defined at the header item 
level.  Also fixed a bug with the AbstractCssReferenceHeaderItem class where it 
was directly accessing the identity and crossOrigin values instead of it going 
through the accessor methods like it should.
    
    * Create JsCssReferenceHeaderItemTest.java
    
    Added unit tests for ResourceReference, JavaScriptReferenceHeaderItem and 
CssReferenceHeaderItem classes.
    
    * WICKET-7149: Tidy-up the new JsCssReferenceHeaderItemTest
    
    Signed-off-by: Martin Tzvetanov Grigorov <[email protected]>
    
    ---------
    
    Signed-off-by: Martin Tzvetanov Grigorov <[email protected]>
    Co-authored-by: Martin Tzvetanov Grigorov <[email protected]>
---
 .../markup/html/JsCssReferenceHeaderItemTest.java  | 212 +++++++++++++++++++++
 .../head/AbstractCssReferenceHeaderItem.java       |   4 +-
 .../wicket/markup/head/CssReferenceHeaderItem.java |  16 +-
 .../markup/head/JavaScriptReferenceHeaderItem.java |  25 +++
 .../wicket/request/resource/ResourceReference.java |  45 +++++
 5 files changed, 297 insertions(+), 5 deletions(-)

diff --git 
a/wicket-core-tests/src/test/java/org/apache/wicket/markup/html/JsCssReferenceHeaderItemTest.java
 
b/wicket-core-tests/src/test/java/org/apache/wicket/markup/html/JsCssReferenceHeaderItemTest.java
new file mode 100644
index 0000000000..b1e25b1f8f
--- /dev/null
+++ 
b/wicket-core-tests/src/test/java/org/apache/wicket/markup/html/JsCssReferenceHeaderItemTest.java
@@ -0,0 +1,212 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.markup.html;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.Serial;
+
+import org.apache.wicket.MarkupContainer;
+import org.apache.wicket.markup.IMarkupResourceStreamProvider;
+import org.apache.wicket.markup.head.CssHeaderItem;
+import org.apache.wicket.markup.head.CssReferenceHeaderItem;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.markup.head.JavaScriptReferenceHeaderItem;
+import org.apache.wicket.markup.parser.XmlPullParser;
+import org.apache.wicket.markup.parser.XmlTag;
+import org.apache.wicket.request.resource.PackageResourceReference;
+import org.apache.wicket.util.resource.IResourceStream;
+import org.apache.wicket.util.resource.StringResourceStream;
+import org.apache.wicket.util.tester.WicketTestCase;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Michael Pritt
+ */
+class JsCssReferenceHeaderItemTest extends WicketTestCase
+{
+       private static final Logger log = 
LoggerFactory.getLogger(JsCssReferenceHeaderItemTest.class);
+       
+       private static final String JS_RESOURCE_HASH = 
"sha384-jsResourceHash2856816aw771";
+       private static final String JS_REFERENCE_HASH = 
"sha384-jssReferenceHash28546qt8725171";
+       private static final String CSS_RESOURCE_HASH = 
"sha384-cssResourceHash2512ab6wts23";
+       private static final String CSS_REFERENCE_HASH = 
"sha384-cssReferenceHash2awet512asd623";
+       
+       /**
+        * Basic ResourceReference integrity hash check
+        */
+       @Test
+       void resourceReferenceTest_basic() throws Exception
+       {
+               tester.startPage(TestPage.class);
+               XmlPullParser parser = new XmlPullParser();
+               parser.parse(tester.getLastResponseAsString());
+               XmlTag tag = parser.nextTag();
+               CharSequence integrity = null;
+               CharSequence crossOrigin = null;
+               do
+               {
+                       if (tag != null && tag.isOpen() && 
"script".equals(tag.getName()) && "jsref".contentEquals(tag.getAttribute("id")))
+                       {
+                               log.info(" SCRIPT TAG: " + tag.toDebugString() 
+ "\nExpect js resource hash: " + JS_RESOURCE_HASH
+                                               + "\nExpect crossOrigin: " + 
CrossOrigin.USE_CREDENTIALS.getRealName());
+                               integrity = tag.getAttribute("integrity");
+                               crossOrigin = tag.getAttribute("crossOrigin");
+                               break;
+                       }
+               }
+               while ((tag = parser.nextTag()) != null);
+               assertEquals(JS_RESOURCE_HASH, integrity);
+               assertEquals(CrossOrigin.USE_CREDENTIALS.getRealName(), 
crossOrigin);
+       }
+       
+
+       /**
+        * Basic JavaScriptReferenceHeaderItem integrity check - should 
override any integrity at resource level
+        */
+       @Test
+       void javaScriptReferenceIntegrityTest_override() throws Exception
+       {
+               tester.startPage(TestPage.class);
+               XmlPullParser parser = new XmlPullParser();
+               parser.parse(tester.getLastResponseAsString());
+               XmlTag tag = parser.nextTag();
+               CharSequence integrity = null;
+               CharSequence crossOrigin = null;
+               do
+               {
+                       if (tag != null && tag.isOpen() && 
"script".equals(tag.getName()) && 
"jsref2".contentEquals(tag.getAttribute("id")))
+                       {
+                               log.info(" SCRIPT TAG: " + tag.toDebugString() 
+ "\nExpect js reference hash: " + JS_REFERENCE_HASH
+                                               + "\nExpect crossOrigin: " + 
CrossOrigin.ANONYMOUS.getRealName());
+                               integrity = tag.getAttribute("integrity");
+                               crossOrigin = tag.getAttribute("crossOrigin");
+                               break;
+                       }
+               }
+               while ((tag = parser.nextTag()) != null);
+               assertEquals(JS_REFERENCE_HASH, integrity);
+               assertEquals(CrossOrigin.ANONYMOUS.getRealName(), crossOrigin);
+       }
+               
+       @Test
+       void cssReferenceIntegrityTest_basic() throws Exception
+       {
+               tester.startPage(TestPage.class);
+               XmlPullParser parser = new XmlPullParser();
+               parser.parse(tester.getLastResponseAsString());
+               XmlTag tag = parser.nextTag();
+               CharSequence integrity = null;
+               CharSequence crossOrigin = null;
+               do
+               {
+                       if (tag != null && "link".equals(tag.getName()) && 
tag.getAttribute("id").equals("cssref"))
+                       {
+                               log.debug(" LINK TAG: " + tag.toDebugString() + 
"\nExpect resource hash: " + CSS_RESOURCE_HASH
+                                       + "\nExpect crossOrigin: " + 
CrossOrigin.ANONYMOUS.getRealName());
+                               integrity = tag.getAttribute("integrity");
+                               crossOrigin = tag.getAttribute("crossOrigin");
+                               break;
+                       }
+               }
+               while ((tag = parser.nextTag()) != null);
+               assertEquals(CSS_RESOURCE_HASH, integrity);
+               assertEquals(CrossOrigin.ANONYMOUS.getRealName(), crossOrigin);
+       }
+
+       
+       @Test
+       void cssReferenceIntegrityTest_override() throws Exception
+       {
+               tester.startPage(TestPage.class);
+               XmlPullParser parser = new XmlPullParser();
+               parser.parse(tester.getLastResponseAsString());
+               XmlTag tag = parser.nextTag();
+               CharSequence integrity = null;
+               CharSequence crossOrigin = null;
+               do
+               {
+                       if (tag != null && "link".equals(tag.getName()) && 
tag.getAttribute("id").equals("cssref2"))
+                       {
+                               log.debug(" LINK TAG: " + tag.toDebugString() + 
"\nExpect reference hash: " + CSS_REFERENCE_HASH
+                                       + "\nExpect crossOrigin: " + 
CrossOrigin.USE_CREDENTIALS.getRealName());
+                               integrity = tag.getAttribute("integrity");
+                               crossOrigin = tag.getAttribute("crossOrigin");
+                               break;
+                       }
+               }
+               while ((tag = parser.nextTag()) != null);
+               assertEquals(CSS_REFERENCE_HASH, integrity);
+               assertEquals(CrossOrigin.USE_CREDENTIALS.getRealName(), 
crossOrigin);
+       }       
+
+       public static class TestPage extends WebPage implements 
IMarkupResourceStreamProvider
+       {
+               @Serial
+               private static final long serialVersionUID = 1L;
+
+               @Override
+               public void renderHead(IHeaderResponse response)
+               {
+                       super.renderHead(response);
+
+                       // JS Reference - Basic
+                       PackageResourceReference jsRef = new 
PackageResourceReference("jsres");
+                       jsRef.setIntegrity(JS_RESOURCE_HASH);
+                       jsRef.setCrossOrigin(CrossOrigin.USE_CREDENTIALS);
+                       JavaScriptReferenceHeaderItem jsHeaderItem = 
JavaScriptHeaderItem.forReference(jsRef, "jsref");
+                       response.render(jsHeaderItem);
+                       
+                       // JS Reference - Override
+                       jsRef = new PackageResourceReference("jsres2");
+                       jsRef.setIntegrity(JS_RESOURCE_HASH);
+                       jsRef.setCrossOrigin(CrossOrigin.USE_CREDENTIALS);
+                       jsHeaderItem = JavaScriptHeaderItem.forReference(jsRef, 
"jsref2");
+                       jsHeaderItem.setIntegrity(JS_REFERENCE_HASH);
+                       jsHeaderItem.setCrossOrigin(CrossOrigin.ANONYMOUS);
+                       response.render(jsHeaderItem);
+
+                       // CSS Reference - Basic
+                       PackageResourceReference cssRef = new 
PackageResourceReference("cssres");
+                       cssRef.setIntegrity(CSS_RESOURCE_HASH);
+                       cssRef.setCrossOrigin(CrossOrigin.ANONYMOUS);
+                       CssReferenceHeaderItem cssHeaderItem = 
CssHeaderItem.forReference(cssRef);
+                       cssHeaderItem.setId("cssref");
+                       response.render(cssHeaderItem);
+
+                       // CSS References - Override
+                       cssRef = new PackageResourceReference("cssres2");
+                       cssRef.setIntegrity(CSS_RESOURCE_HASH);
+                       cssRef.setCrossOrigin(CrossOrigin.ANONYMOUS);
+                       cssHeaderItem = CssHeaderItem.forReference(cssRef);
+                       cssHeaderItem.setId("cssref2");
+                       cssHeaderItem.setIntegrity(CSS_REFERENCE_HASH);
+                       
cssHeaderItem.setCrossOrigin(CrossOrigin.USE_CREDENTIALS);
+                       response.render(cssHeaderItem);
+               }
+
+               @Override
+               public IResourceStream getMarkupResourceStream(MarkupContainer 
container,
+                       Class<?> containerClass)
+               {
+                       return new 
StringResourceStream("<html><body></body></html>");
+               }
+       }
+}
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/markup/head/AbstractCssReferenceHeaderItem.java
 
b/wicket-core/src/main/java/org/apache/wicket/markup/head/AbstractCssReferenceHeaderItem.java
index cfe993b950..aa1ff06d41 100644
--- 
a/wicket-core/src/main/java/org/apache/wicket/markup/head/AbstractCssReferenceHeaderItem.java
+++ 
b/wicket-core/src/main/java/org/apache/wicket/markup/head/AbstractCssReferenceHeaderItem.java
@@ -93,8 +93,8 @@ public abstract class AbstractCssReferenceHeaderItem extends 
CssHeaderItem imple
                attributes.putAttribute(CssUtils.ATTR_ID, getId());
                attributes.putAttribute(CssUtils.ATTR_LINK_MEDIA, getMedia());
                attributes.putAttribute(CssUtils.ATTR_CROSS_ORIGIN,
-                       crossOrigin == null ? null : crossOrigin.getRealName());
-               attributes.putAttribute(CssUtils.ATTR_INTEGRITY, integrity);
+                       getCrossOrigin() == null ? null : 
getCrossOrigin().getRealName());
+               attributes.putAttribute(CssUtils.ATTR_INTEGRITY, 
getIntegrity());
                attributes.putAttribute(CssUtils.ATTR_CSP_NONCE, getNonce());
                CssUtils.writeLink(response, attributes);
 
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/markup/head/CssReferenceHeaderItem.java
 
b/wicket-core/src/main/java/org/apache/wicket/markup/head/CssReferenceHeaderItem.java
index a2ee043045..8cbd67335e 100644
--- 
a/wicket-core/src/main/java/org/apache/wicket/markup/head/CssReferenceHeaderItem.java
+++ 
b/wicket-core/src/main/java/org/apache/wicket/markup/head/CssReferenceHeaderItem.java
@@ -103,13 +103,23 @@ public class CssReferenceHeaderItem extends 
AbstractCssReferenceHeaderItem imple
        @Override
        public CrossOrigin getCrossOrigin()
        {
-               return null;
+               CrossOrigin tempOrigin = super.getCrossOrigin();
+               if (tempOrigin == null)
+               {
+                       tempOrigin = reference.getCrossOrigin();
+               }
+               return tempOrigin;
        }
        
-       @Override
+       @Override 
        public String getIntegrity()
        {
-               return null;
+               String tempIntegrity = super.getIntegrity();
+               if (tempIntegrity == null)
+               {
+                       tempIntegrity = reference.getIntegrity();
+               }
+               return tempIntegrity;
        }
        
        @Override
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/markup/head/JavaScriptReferenceHeaderItem.java
 
b/wicket-core/src/main/java/org/apache/wicket/markup/head/JavaScriptReferenceHeaderItem.java
index a04c6ed306..0292768b32 100644
--- 
a/wicket-core/src/main/java/org/apache/wicket/markup/head/JavaScriptReferenceHeaderItem.java
+++ 
b/wicket-core/src/main/java/org/apache/wicket/markup/head/JavaScriptReferenceHeaderItem.java
@@ -20,6 +20,7 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
+import org.apache.wicket.markup.html.CrossOrigin;
 import org.apache.wicket.request.IRequestHandler;
 import org.apache.wicket.request.Response;
 import org.apache.wicket.request.cycle.RequestCycle;
@@ -39,6 +40,8 @@ public class JavaScriptReferenceHeaderItem extends 
AbstractJavaScriptReferenceHe
        implements
                IReferenceHeaderItem
 {
+       private static final long serialVersionUID = 1L;
+       
        private final ResourceReference reference;
        private final PageParameters pageParameters;
 
@@ -140,4 +143,26 @@ public class JavaScriptReferenceHeaderItem extends 
AbstractJavaScriptReferenceHe
                return java.util.Objects.equals(reference, that.reference) &&
                                java.util.Objects.equals(pageParameters, 
that.pageParameters);
        }
+       
+       @Override 
+       public String getIntegrity()
+       {
+               String tempIntegrity = super.getIntegrity();
+               if (tempIntegrity == null)
+               {
+                       tempIntegrity = reference.getIntegrity();
+               }
+               return tempIntegrity;
+       }
+       
+       @Override
+       public CrossOrigin getCrossOrigin()
+       {
+               CrossOrigin tempOrigin = super.getCrossOrigin();
+               if (tempOrigin == null)
+               {
+                       tempOrigin = reference.getCrossOrigin();
+               }
+               return tempOrigin;
+       }       
 }
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/request/resource/ResourceReference.java
 
b/wicket-core/src/main/java/org/apache/wicket/request/resource/ResourceReference.java
index 9b96144ff2..8b7420f3a3 100644
--- 
a/wicket-core/src/main/java/org/apache/wicket/request/resource/ResourceReference.java
+++ 
b/wicket-core/src/main/java/org/apache/wicket/request/resource/ResourceReference.java
@@ -24,6 +24,7 @@ import java.util.Locale;
 import org.apache.wicket.Application;
 import org.apache.wicket.core.util.lang.WicketObjects;
 import org.apache.wicket.markup.head.HeaderItem;
+import org.apache.wicket.markup.html.CrossOrigin;
 import org.apache.wicket.util.io.IClusterable;
 import org.apache.wicket.util.lang.Args;
 import org.apache.wicket.util.lang.Objects;
@@ -47,6 +48,10 @@ public abstract class ResourceReference implements 
IClusterable
 
        private final Key data;
 
+       private String integrity;
+       
+       private CrossOrigin crossOrigin;
+       
        /**
         * Creates new {@link ResourceReference} instance.
         * 
@@ -243,6 +248,46 @@ public abstract class ResourceReference implements 
IClusterable
                return new UrlAttributes(getLocale(), getStyle(), 
getVariation());
        }
        
+       /**
+        * Returns the integrity value of the resource which is a string 
containing
+        * one or more base64 encoded hashes.
+        * 
+        * hashes are whitespace separated (see reference below): 
+        *     
https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity  
+        */
+       public String getIntegrity() {
+               return integrity;
+       }
+
+       /**
+        * Sets the integrity value of the resource which containes one or more 
+        * base64 encoded hashes 
+        * 
+        * hashes are whitespace separated (see reference below): 
+        *     
https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity  
+        */
+       public void setIntegrity(String integrity) {
+               this.integrity = integrity;
+       }
+
+       /**
+        * Returns the cross origin policy to use when creating the header 
reference 
+        * for the resource.
+        * 
+        *  @return cross origin policy
+        */
+       public CrossOrigin getCrossOrigin() {
+               return crossOrigin;
+       }
+
+       /**
+        * Sets the cross origin policy to use when creating the header 
reference 
+        * for the resource. 
+        */
+       public void setCrossOrigin(CrossOrigin crossOrigin) {
+               this.crossOrigin = crossOrigin;
+       }       
+       
        /**
         * Factory method to build a resource reference that uses the provided 
supplier to return
         * the resource.

Reply via email to