From a2a36426e0551bf0d32c2622bbb0ff1d32b81f0a Mon Sep 17 00:00:00 2001 From: Kazuki Sakamoto Date: Thu, 4 Feb 2021 00:56:19 -0800 Subject: [PATCH] Redirect DTD http request to the resource file (#850) * Redirect DTD http request to the resource file * Add JobSpec unittests * Remove Cuebot TCP 8080 port from Docker --- .../com/imageworks/spcue/service/JobSpec.java | 30 ++++++- .../src/main/resources/application.properties | 5 +- .../spcue/test/service/JobSpecTests.java | 85 +++++++++++++++++++ .../resources/conf/jobspec/jobspec_1_10.xml | 47 ++++++++++ .../conf/jobspec/jobspec_nonexistent_dtd.xml | 47 ++++++++++ sandbox/docker-compose.yml | 1 - 6 files changed, 209 insertions(+), 6 deletions(-) create mode 100644 cuebot/src/test/java/com/imageworks/spcue/test/service/JobSpecTests.java create mode 100644 cuebot/src/test/resources/conf/jobspec/jobspec_1_10.xml create mode 100644 cuebot/src/test/resources/conf/jobspec/jobspec_nonexistent_dtd.xml diff --git a/cuebot/src/main/java/com/imageworks/spcue/service/JobSpec.java b/cuebot/src/main/java/com/imageworks/spcue/service/JobSpec.java index d553456fd..3540c77a5 100644 --- a/cuebot/src/main/java/com/imageworks/spcue/service/JobSpec.java +++ b/cuebot/src/main/java/com/imageworks/spcue/service/JobSpec.java @@ -20,6 +20,8 @@ package com.imageworks.spcue.service; import java.io.File; +import java.io.IOException; +import java.io.InputStream; import java.io.StringReader; import java.util.ArrayList; import java.util.HashSet; @@ -36,6 +38,9 @@ import org.jdom.Element; import org.jdom.input.SAXBuilder; import org.springframework.dao.EmptyResultDataAccessException; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; import com.imageworks.spcue.BuildableDependency; import com.imageworks.spcue.BuildableJob; @@ -102,6 +107,8 @@ public class JobSpec { public static final String DEFAULT_SERVICE = "default"; + public static final String SPCUE_DTD_URL = "http://localhost:8080/spcue/dtd/"; + private List jobs = new ArrayList(); private List depends = new ArrayList(); @@ -836,9 +843,30 @@ public void parse(File file) { handleDependsTags(); } + private class DTDRedirector implements EntityResolver { + public InputSource resolveEntity(String publicId, + String systemId) throws SAXException, IOException { + if (systemId.startsWith(SPCUE_DTD_URL)) { + // Redirect to resource file. + try { + String filename = systemId.substring(SPCUE_DTD_URL.length()); + InputStream dtd = getClass().getResourceAsStream("/public/dtd/" + filename); + return new InputSource(dtd); + } catch (Exception e) { + throw new SpecBuilderException("Failed to redirect DTD " + systemId + ", " + e); + } + } else { + // Use default resolver. + return null; + } + } + } + public void parse(String cjsl) { try { - doc = new SAXBuilder(true).build(new StringReader(cjsl)); + SAXBuilder builder = new SAXBuilder(true); + builder.setEntityResolver(new DTDRedirector()); + doc = builder.build(new StringReader(cjsl)); } catch (Exception e) { throw new SpecBuilderException("Failed to parse job spec XML, " + e); diff --git a/cuebot/src/main/resources/application.properties b/cuebot/src/main/resources/application.properties index 79aa81236..cbc51e2fd 100644 --- a/cuebot/src/main/resources/application.properties +++ b/cuebot/src/main/resources/application.properties @@ -1,5 +1,2 @@ -server.contextPath=/spcue -server.port=8080 - spring.main.allow-bean-definition-overriding=true -spring.mvc.static-path-pattern=/spcue/** +spring.main.web-application-type=none diff --git a/cuebot/src/test/java/com/imageworks/spcue/test/service/JobSpecTests.java b/cuebot/src/test/java/com/imageworks/spcue/test/service/JobSpecTests.java new file mode 100644 index 000000000..d2aa142d8 --- /dev/null +++ b/cuebot/src/test/java/com/imageworks/spcue/test/service/JobSpecTests.java @@ -0,0 +1,85 @@ + +/* + * Copyright Contributors to the OpenCue Project + * + * Licensed 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 com.imageworks.spcue.test.service; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import javax.annotation.Resource; + +import org.junit.Test; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests; +import org.springframework.test.context.support.AnnotationConfigContextLoader; + +import com.imageworks.spcue.SpecBuilderException; +import com.imageworks.spcue.config.TestAppConfig; +import com.imageworks.spcue.service.JobLauncher; +import com.imageworks.spcue.service.JobSpec; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + + +@ContextConfiguration(classes=TestAppConfig.class, loader=AnnotationConfigContextLoader.class) +public class JobSpecTests extends AbstractTransactionalJUnit4SpringContextTests { + + @Resource + JobLauncher jobLauncher; + + private static String readJobSpec(String name) + { + String path = "src/test/resources/conf/jobspec/" + name; + byte[] encoded = null; + + try { + encoded = Files.readAllBytes(Paths.get(path)); + } catch (IOException e) { + fail("readJobSpec should succeed to read jobspec file"); + } + + return new String(encoded, StandardCharsets.UTF_8); + } + + @Test + public void testParseSuccess() { + String xml = readJobSpec("jobspec_1_10.xml"); + JobSpec spec = jobLauncher.parse(xml); + assertEquals(spec.getDoc().getDocType().getPublicID(), + "SPI Cue Specification Language"); + assertEquals(spec.getDoc().getDocType().getSystemID(), + "http://localhost:8080/spcue/dtd/cjsl-1.10.dtd"); + assertEquals(spec.getJobs().size(), 1); + assertEquals(spec.getJobs().get(0).detail.name, "testing-default-testuser_test"); + } + + @Test + public void testParseNonExistent() { + String xml = readJobSpec("jobspec_nonexistent_dtd.xml"); + try { + jobLauncher.parse(xml); + fail("Expected exception"); + } catch (SpecBuilderException e) { + assertEquals(e.getMessage(), + "Failed to parse job spec XML, java.net.MalformedURLException"); + } + } +} diff --git a/cuebot/src/test/resources/conf/jobspec/jobspec_1_10.xml b/cuebot/src/test/resources/conf/jobspec/jobspec_1_10.xml new file mode 100644 index 000000000..ce42deb16 --- /dev/null +++ b/cuebot/src/test/resources/conf/jobspec/jobspec_1_10.xml @@ -0,0 +1,47 @@ + + + + + + + + + local + testing + default + testuser + 9860 + + + False + 2 + False + + + + echo hello + 1 + 1 + + + shell + + + + + + diff --git a/cuebot/src/test/resources/conf/jobspec/jobspec_nonexistent_dtd.xml b/cuebot/src/test/resources/conf/jobspec/jobspec_nonexistent_dtd.xml new file mode 100644 index 000000000..870cec16d --- /dev/null +++ b/cuebot/src/test/resources/conf/jobspec/jobspec_nonexistent_dtd.xml @@ -0,0 +1,47 @@ + + + + + + + + + local + testing + default + testuser + 9860 + + + False + 2 + False + + + + echo hello + 1 + 1 + + + shell + + + + + + diff --git a/sandbox/docker-compose.yml b/sandbox/docker-compose.yml index f4fde39ec..33938c06f 100644 --- a/sandbox/docker-compose.yml +++ b/sandbox/docker-compose.yml @@ -34,7 +34,6 @@ services: - db ports: - "8443:8443" - - "8080:8080" depends_on: - db - flyway