Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 17 additions & 17 deletions .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,33 +22,33 @@ jobs:

steps:
- name: Checkout project
uses: actions/checkout@v3
uses: actions/checkout@a37ce9120846195fa4ece8f58b268e6043cb2f26 # v3
- name: Set up JDK 17
uses: actions/setup-java@v3
uses: actions/setup-java@e9fbacdec3bb3b6036605a3e6f7995d66773a8c6 # v3
with:
java-version: "17"
distribution: "corretto"
architecture: "x64"
- name: Setup Android SDK
uses: android-actions/setup-android@v3
uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3
with:
packages: "tools platform-tools build-tools;30.0.3"
packages: "platform-tools build-tools;30.0.3"
- name: Get project hash
run: |
git_hash=$(git rev-parse --short "$GITHUB_SHA")
echo "git_hash=$git_hash" >> $GITHUB_ENV
- name: Check format
uses: gradle/actions/setup-gradle@v3
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3
with:
build-root-directory: play-validations
arguments: spotlessCheck
- name: Build memory-footprint.jar
uses: gradle/actions/setup-gradle@v3
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3
with:
build-root-directory: play-validations
arguments: memory-footprint:jar -Dmemory-footprint.git_hash=${{ env.git_hash }}
- name: Test memory-footprint
uses: gradle/actions/setup-gradle@v3
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3
with:
build-root-directory: play-validations
arguments: memory-footprint:test
Expand All @@ -57,15 +57,15 @@ jobs:

steps:
- name: Checkout project
uses: actions/checkout@v3
uses: actions/checkout@a37ce9120846195fa4ece8f58b268e6043cb2f26 # v3
- name: Set up JDK 17
uses: actions/setup-java@v3
uses: actions/setup-java@e9fbacdec3bb3b6036605a3e6f7995d66773a8c6 # v3
with:
java-version: "17"
distribution: "corretto"
architecture: "x64"
- name: Build the validator
uses: gradle/actions/setup-gradle@v3
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3
with:
build-root-directory: third_party/wff
arguments: :specification:validator:build
Expand All @@ -74,33 +74,33 @@ jobs:

steps:
- name: Checkout project
uses: actions/checkout@v3
uses: actions/checkout@a37ce9120846195fa4ece8f58b268e6043cb2f26 # v3
- name: Set up JDK 17
uses: actions/setup-java@v3
uses: actions/setup-java@e9fbacdec3bb3b6036605a3e6f7995d66773a8c6 # v3
with:
java-version: "17"
distribution: "corretto"
architecture: "x64"
- name: Setup Android SDK
uses: android-actions/setup-android@v3
uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3
with:
packages: "tools platform-tools build-tools;30.0.3"
packages: "platform-tools build-tools;30.0.3"
- name: Get project hash
run: |
git_hash=$(git rev-parse --short "$GITHUB_SHA")
echo "git_hash=$git_hash" >> $GITHUB_ENV
- name: Check format
uses: gradle/actions/setup-gradle@v3
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3
with:
build-root-directory: tools
arguments: spotlessCheck
- name: Build wff-optimizer.jar
uses: gradle/actions/setup-gradle@v3
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3
with:
build-root-directory: tools
arguments: wff-optimizer:jar -Dmemory-footprint.git_hash=${{ env.git_hash }}
- name: Test wff-optimizer
uses: gradle/actions/setup-gradle@v3
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3
with:
build-root-directory: tools
arguments: wff-optimizer:test
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,19 @@

package com.google.wear.watchface.dfx.memory

import org.w3c.dom.Document
import java.io.ByteArrayInputStream
import java.util.stream.Stream
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.parsers.SAXParserFactory
import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.DOMResult
import javax.xml.transform.sax.SAXSource
import javax.xml.xpath.XPathConstants
import javax.xml.xpath.XPathFactory
import kotlin.streams.asSequence
import org.w3c.dom.Document
import org.xml.sax.InputSource
import org.xml.sax.SAXException

internal class WatchFaceData private constructor() {

Expand Down Expand Up @@ -86,8 +88,32 @@ internal class WatchFaceData private constructor() {
// DWF Runtime. See b/493476140 for details.
val docFactory = DocumentBuilderFactory.newInstance().apply { isNamespaceAware = true }
val doc = docFactory.newDocumentBuilder().newDocument()
val saxFactory =
try {
SAXParserFactory.newDefaultInstance()
} catch (_: NoSuchMethodError) {
SAXParserFactory.newInstance()
}.apply {
// Disable DTDs and external entities to prevent XML External Entity (XXE) and
// recursive entity expansion (e.g. Billion Laughs) attacks on untrusted inputs.
setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)
setFeature("http://xml.org/sax/features/external-general-entities", false)
setFeature("http://xml.org/sax/features/external-parameter-entities", false)
}
val xmlReader =
saxFactory.newSAXParser().xmlReader.apply {
try {
// Once DTDs and custom entities are disabled above, recursive entity
// expansion is impossible, so it is safe to disable these size limits to
// allow watch faces with a large number of built-in entities (like ").
setProperty("jdk.xml.maxGeneralEntitySizeLimit", 0)
setProperty("jdk.xml.totalEntitySizeLimit", 0)
} catch (_: SAXException) {
// Ignored on parsers that do not support JDK XML limit properties.
}
}
val inputStream = InputSource(ByteArrayInputStream(xmlData))
val sax = SAXSource(inputStream)
val sax = SAXSource(xmlReader, inputStream)
val transformer = TransformerFactory.newInstance().newTransformer()
transformer.transform(sax, DOMResult(doc))
return doc
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
package com.google.wear.watchface.dfx.memory;

import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
import org.junit.Test;
Expand Down Expand Up @@ -86,6 +89,44 @@ public void fromResourcesStream_createsPackageFromWindowsPaths() throws Exceptio
}
}

@Test
public void fromResourcesStream_parsesWatchFaceWithOver100kEntityReferences() {
String repeatedEntities = String.join("", Collections.nCopies(60000, ""a" "));
String xml =
"<WatchFace width=\"450\" height=\"450\">"
+ "<Scene><Group name=\""
+ repeatedEntities
+ "\"/></Scene>"
+ "</WatchFace>";
AndroidResource resource =
AndroidResource.fromPath(
Paths.get("base/res/raw/watchface.xml"),
xml.getBytes(StandardCharsets.UTF_8));

WatchFaceData watchFaceData =
WatchFaceData.fromResourcesStream(
Stream.of(resource), TEST_EVALUATION_SETTINGS, imageProcessor);

assertThat(watchFaceData.getWatchFaceDocuments()).hasSize(1);
}

@Test
public void fromResourcesStream_rejectsXmlWithDoctype() {
String xml =
"<!DOCTYPE lolz [ <!ENTITY lol \"lol\"> ]>"
+ "<WatchFace width=\"450\" height=\"450\"><Scene/></WatchFace>";
AndroidResource resource =
AndroidResource.fromPath(
Paths.get("base/res/raw/watchface.xml"),
xml.getBytes(StandardCharsets.UTF_8));

assertThrows(
Exception.class,
() ->
WatchFaceData.fromResourcesStream(
Stream.of(resource), TEST_EVALUATION_SETTINGS, imageProcessor));
}

private AndroidResource readPackageFile(String path) throws RuntimeException {
Path rootPath = Paths.get(TEST_PACKAGE_FILES_ROOT);
Path filePath = Paths.get(TEST_PACKAGE_FILES_ROOT, path);
Expand Down
Loading