You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by sv...@apache.org on 2020/01/09 07:01:46 UTC
[wicket] branch master updated: WICKET-6321 dynamic SRI integrity
calculation
This is an automated email from the ASF dual-hosted git repository.
svenmeier 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 5fb92a7 WICKET-6321 dynamic SRI integrity calculation
5fb92a7 is described below
commit 5fb92a71e28e22e0d358ddb4b7a36b97207339f2
Author: Sven Meier <sv...@apache.org>
AuthorDate: Wed Jan 8 09:18:04 2020 +0100
WICKET-6321 dynamic SRI integrity calculation
---
.../examples/sri/DynamicSubresourceIntegrity.java | 169 +++++++++++++++++++++
.../wicket/examples/sri/IntegrityDemoPage.html | 5 +-
.../wicket/examples/sri/IntegrityDemoPage.java | 5 +
.../apache/wicket/examples/sri/SriApplication.java | 25 +--
.../sri/{subresource.js => subresource.css} | 14 +-
.../org/apache/wicket/examples/sri/subresource.js | 7 +-
6 files changed, 188 insertions(+), 37 deletions(-)
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/sri/DynamicSubresourceIntegrity.java b/wicket-examples/src/main/java/org/apache/wicket/examples/sri/DynamicSubresourceIntegrity.java
new file mode 100644
index 0000000..3d92501
--- /dev/null
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/sri/DynamicSubresourceIntegrity.java
@@ -0,0 +1,169 @@
+/*
+ * 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.examples.sri;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Base64;
+import java.util.Base64.Encoder;
+import java.util.Map;
+
+import org.apache.commons.collections4.map.HashedMap;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.IReferenceHeaderItem;
+import org.apache.wicket.markup.head.ISubresourceHeaderItem;
+import org.apache.wicket.markup.head.filter.SubresourceHeaderResponse;
+import org.apache.wicket.request.Url;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.resource.IResource;
+import org.apache.wicket.request.resource.ResourceReference;
+import org.apache.wicket.request.resource.caching.IStaticCacheableResource;
+import org.apache.wicket.util.io.IOUtils;
+import org.apache.wicket.util.resource.IResourceStream;
+import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Dynamic calculation of SRI for {@link IStaticCacheableResource}s.
+ *
+ * @author svenmeier
+ */
+public class DynamicSubresourceIntegrity
+{
+ private static final Logger log = LoggerFactory.getLogger(DynamicSubresourceIntegrity.class);
+
+ private Map<Serializable, String> cache = new HashedMap<>();
+
+ /**
+ * Wrap the given response
+ *
+ * @param response
+ * response to add SRI to
+ * @return wrapper
+ */
+ public IHeaderResponse wrap(IHeaderResponse response)
+ {
+ return new SubresourceHeaderResponse(response)
+ {
+ @Override
+ protected void configure(ISubresourceHeaderItem item)
+ {
+ String integrity = getIntegrity(item);
+ if (integrity != null)
+ {
+ item.setIntegrity(integrity);
+ }
+ }
+ };
+ }
+
+ public String getIntegrity(ISubresourceHeaderItem item)
+ {
+ if (item instanceof IReferenceHeaderItem)
+ {
+ ResourceReference reference = ((IReferenceHeaderItem)item).getReference();
+
+ IResource resource = reference.getResource();
+ if (resource instanceof IStaticCacheableResource)
+ {
+ IStaticCacheableResource cacheableResource = (IStaticCacheableResource)resource;
+
+ return getIntegrity(reference, cacheableResource);
+ }
+ }
+
+ return null;
+ }
+
+ private String getIntegrity(ResourceReference reference, IStaticCacheableResource cacheableResource)
+ {
+ String integrity = cache.get(cacheableResource.getCacheKey());
+ if (integrity == null)
+ {
+ Url baseUrl = getBaseUrl(reference);
+ try
+ {
+ byte[] bytes = getBytes(cacheableResource, baseUrl);
+
+ integrity = "sha384-" + createHash(bytes);
+ cache.put(cacheableResource.getCacheKey(), integrity);
+ }
+ catch (Exception ex)
+ {
+ log.error("cannot calculate integrity", ex);
+ }
+ }
+ return integrity;
+ }
+
+ private Url getBaseUrl(ResourceReference reference)
+ {
+ RequestCycle cycle = RequestCycle.get();
+ Url url = Url.parse(cycle.urlFor(reference, null));
+ if (url.getSegments().get(0).equals("."))
+ {
+ // not sure why this is needed but leading dot must be removed,
+ // otherwise relative urls will differ from the actually served css
+ url.removeLeadingSegments(1);
+ }
+
+ return url;
+ }
+
+ /**
+ * Get bytes.
+ */
+ protected byte[] getBytes(IStaticCacheableResource cacheableResource, Url baseUrl)
+ throws IOException, ResourceStreamNotFoundException
+ {
+ byte[] bytes;
+
+ // base url has to be adjusted for relative images in CSS
+ RequestCycle cycle = RequestCycle.get();
+ Url originalBaseUrl = cycle.getUrlRenderer().setBaseUrl(baseUrl);
+
+ try (IResourceStream stream = cacheableResource.getResourceStream())
+ {
+ bytes = IOUtils.toByteArray(stream.getInputStream());
+ }
+ finally
+ {
+ cycle.getUrlRenderer().setBaseUrl(originalBaseUrl);
+ }
+
+ return bytes;
+ }
+
+ /**
+ * Create the hash.
+ *
+ * <pre>
+ * openssl dgst -sha384 -binary xy.js | openssl base64 -A
+ * </pre>
+ */
+ protected String createHash(byte[] bytes) throws NoSuchAlgorithmException
+ {
+ MessageDigest digest = MessageDigest.getInstance("SHA-384");
+
+ Encoder encoder = Base64.getEncoder();
+
+ return encoder.encodeToString(digest.digest(bytes));
+ }
+}
\ No newline at end of file
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/sri/IntegrityDemoPage.html b/wicket-examples/src/main/java/org/apache/wicket/examples/sri/IntegrityDemoPage.html
index 37815c5..3204a4e 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/sri/IntegrityDemoPage.html
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/sri/IntegrityDemoPage.html
@@ -6,7 +6,10 @@
<wicket:extend>
<h3>SRI Demo</h3>
- <p id="sri">Integrity test NOT passed</p>
+ <div id="sri">
+ <p class="sri-false" style="color: red;">Integrity test NOT passed</p>
+ <p class="sri-true" style="color: green; font-weight: bold; display: none;">Integrity test passed</p>
+ </div>
</wicket:extend>
</body>
</html>
\ No newline at end of file
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/sri/IntegrityDemoPage.java b/wicket-examples/src/main/java/org/apache/wicket/examples/sri/IntegrityDemoPage.java
index 0149275..e61b5fe 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/sri/IntegrityDemoPage.java
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/sri/IntegrityDemoPage.java
@@ -17,6 +17,7 @@
package org.apache.wicket.examples.sri;
import org.apache.wicket.examples.WicketExamplePage;
+import org.apache.wicket.markup.head.CssHeaderItem;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
import org.apache.wicket.request.resource.JavaScriptResourceReference;
@@ -31,6 +32,9 @@ public class IntegrityDemoPage extends WicketExamplePage
public static final ResourceReference JS = new JavaScriptResourceReference(
IntegrityDemoPage.class, "subresource.js");
+ public static final ResourceReference CSS = new JavaScriptResourceReference(
+ IntegrityDemoPage.class, "subresource.css");
+
public IntegrityDemoPage()
{
}
@@ -41,5 +45,6 @@ public class IntegrityDemoPage extends WicketExamplePage
super.renderHead(response);
response.render(JavaScriptHeaderItem.forReference(JS));
+ response.render(CssHeaderItem.forReference(CSS));
}
}
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/sri/SriApplication.java b/wicket-examples/src/main/java/org/apache/wicket/examples/sri/SriApplication.java
index b7ae7ab..8b9a4f4 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/sri/SriApplication.java
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/sri/SriApplication.java
@@ -18,15 +18,12 @@ package org.apache.wicket.examples.sri;
import org.apache.wicket.Page;
import org.apache.wicket.examples.WicketExampleApplication;
-import org.apache.wicket.markup.head.ISubresourceHeaderItem;
-import org.apache.wicket.markup.head.JavaScriptReferenceHeaderItem;
import org.apache.wicket.markup.head.ResourceAggregator;
-import org.apache.wicket.markup.head.filter.SubresourceHeaderResponse;
-import org.apache.wicket.request.resource.JavaScriptResourceReference;
-import org.apache.wicket.request.resource.ResourceReference;
public class SriApplication extends WicketExampleApplication
{
+ private DynamicSubresourceIntegrity integrity = new DynamicSubresourceIntegrity();
+
@Override
public Class<? extends Page> getHomePage()
{
@@ -38,23 +35,7 @@ public class SriApplication extends WicketExampleApplication
{
super.init();
- setHeaderResponseDecorator(
- response -> new ResourceAggregator(new SubresourceHeaderResponse(response)
- {
- @Override
- protected void configure(ISubresourceHeaderItem item)
- {
- if (item instanceof JavaScriptReferenceHeaderItem) {
- ResourceReference reference = ((JavaScriptReferenceHeaderItem)item).getReference();
-
- if (reference.equals(IntegrityDemoPage.JS)) {
- String algorithm = "sha384";
- String value = "yDSj1gWA4teUdCx2/5M0RsK1jovKR0RdUeeLXKU1gRpNWevoQDGhjHEd1R6Jb+FQ";
- item.setIntegrity(algorithm + "-" + value);
- }
- }
- }
- }));
+ setHeaderResponseDecorator(response -> new ResourceAggregator(integrity.wrap(response)));
mountPage("integritydemo", IntegrityDemoPage.class);
}
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/sri/subresource.js b/wicket-examples/src/main/java/org/apache/wicket/examples/sri/subresource.css
similarity index 71%
copy from wicket-examples/src/main/java/org/apache/wicket/examples/sri/subresource.js
copy to wicket-examples/src/main/java/org/apache/wicket/examples/sri/subresource.css
index e0e1fa6..e6718e3 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/sri/subresource.js
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/sri/subresource.css
@@ -14,11 +14,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/**
- * For any change in this file its hash has to be updated in SriApplication:
- *
- * openssl dgst -sha384 -binary subresource.js | openssl base64 -A
- */
-document.addEventListener('DOMContentLoaded', function(event) {
- document.querySelector("#sri").textContent = 'Integrity test passed';
-});
\ No newline at end of file
+.sri-passed .sri-false {
+ display: none;
+}
+.sri-passed .sri-true {
+ display: block !important;
+}
\ No newline at end of file
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/sri/subresource.js b/wicket-examples/src/main/java/org/apache/wicket/examples/sri/subresource.js
index e0e1fa6..ee130b1 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/sri/subresource.js
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/sri/subresource.js
@@ -14,11 +14,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/**
- * For any change in this file its hash has to be updated in SriApplication:
- *
- * openssl dgst -sha384 -binary subresource.js | openssl base64 -A
- */
document.addEventListener('DOMContentLoaded', function(event) {
- document.querySelector("#sri").textContent = 'Integrity test passed';
+ document.querySelector("#sri").classList.add("sri-passed");
});
\ No newline at end of file