Skip to content

Commit

Permalink
Merge pull request from GHSA-j6qj-j888-vvgq
Browse files Browse the repository at this point in the history
Ensure that WebAppProvider Filter always canonicalises the file passed in from the Scanner. Thus, both the monitored directory is canonical as well as the file it is being compared against.

Signed-off-by: Greg Wilkins <gregw@webtide.com>

Co-authored-by: Greg Wilkins <gregw@webtide.com>
  • Loading branch information
joakime and gregw committed Mar 24, 2021
1 parent e412c8a commit 37fffb1
Show file tree
Hide file tree
Showing 4 changed files with 238 additions and 78 deletions.
Expand Up @@ -20,6 +20,7 @@

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
Expand Down Expand Up @@ -54,6 +55,7 @@ public abstract class ScanningAppProvider extends AbstractLifeCycle implements A
private DeploymentManager _deploymentManager;
protected FilenameFilter _filenameFilter;
private final List<Resource> _monitored = new CopyOnWriteArrayList<>();
private final List<Resource> _canonicalMonitored = new CopyOnWriteArrayList<>();
private boolean _recursive = false;
private int _scanInterval = 10;
private Scanner _scanner;
Expand Down Expand Up @@ -247,12 +249,34 @@ public void setMonitoredResources(List<Resource> resources)
{
_monitored.clear();
_monitored.addAll(resources);
_canonicalMonitored.clear();
for (Resource r : resources)
{
Resource canonical = r; //assume original is canonical
try
{
File f = r.getFile(); //if it has a file, ensure it is canonical
if (f != null)
canonical = Resource.newResource(f.getCanonicalFile());
}
catch (IOException e)
{
//use the original resource
LOG.ignore(e);
}
_canonicalMonitored.add(canonical);
}
}

public List<Resource> getMonitoredResources()
{
return Collections.unmodifiableList(_monitored);
}

List<Resource> getCanonicalMonitoredResources()
{
return Collections.unmodifiableList(_canonicalMonitored);
}

public void setMonitoredDirResource(Resource resource)
{
Expand Down
Expand Up @@ -20,6 +20,7 @@

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Locale;

import org.eclipse.jetty.deploy.App;
Expand All @@ -30,6 +31,8 @@
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.xml.XmlConfiguration;
Expand Down Expand Up @@ -62,6 +65,8 @@
@ManagedObject("Provider for start-up deployement of webapps based on presence in directory")
public class WebAppProvider extends ScanningAppProvider
{
private static final Logger LOG = Log.getLogger(WebAppProvider.class);

private boolean _extractWars = false;
private boolean _parentLoaderPriority = false;
private ConfigurationManager _configurationManager;
Expand All @@ -74,51 +79,62 @@ public class Filter implements FilenameFilter
@Override
public boolean accept(File dir, String name)
{
if (!dir.exists())
{
if (dir == null)
return false;
}
String lowername = name.toLowerCase(Locale.ENGLISH);

File file = new File(dir, name);
Resource r = Resource.newResource(file);
if (getMonitoredResources().contains(r) && r.isDirectory())
try
{
return false;
}

// ignore hidden files
if (lowername.startsWith("."))
return false;

// Ignore some directories
if (file.isDirectory())
{
// is it a nominated config directory
if (lowername.endsWith(".d"))
//Always work on canonical files because we need to
//check if the file passed in is one of the
//monitored resources
if (!dir.getCanonicalFile().exists())
return false;

String lowerName = name.toLowerCase(Locale.ENGLISH);

// is it an unpacked directory for an existing war file?
if (exists(name + ".war") || exists(name + ".WAR"))
File canonical = new File(dir, name).getCanonicalFile();
Resource r = Resource.newResource(canonical);
if (getCanonicalMonitoredResources().contains(r) && r.isDirectory())
return false;

// is it a directory for an existing xml file?
if (exists(name + ".xml") || exists(name + ".XML"))
// ignore hidden files
if (lowerName.startsWith("."))
return false;

//is it a sccs dir?
return !"cvs".equals(lowername) && !"cvsroot".equals(lowername); // OK to deploy it then
}
// Ignore some directories
if (canonical.isDirectory())
{
// is it a nominated config directory
if (lowerName.endsWith(".d"))
return false;

// else is it a war file
if (lowername.endsWith(".war"))
// is it an unpacked directory for an existing war file?
if (exists(name + ".war") || exists(name + ".WAR"))
return false;

// is it a directory for an existing xml file?
if (exists(name + ".xml") || exists(name + ".XML"))
return false;

//is it a sccs dir?
return !"cvs".equals(lowerName) && !"cvsroot".equals(lowerName); // OK to deploy it then
}

// else is it a war file
if (lowerName.endsWith(".war"))
{
//defer deployment decision to fileChanged()
return true;
}

// else is it a context XML file
return lowerName.endsWith(".xml");
}
catch (IOException e)
{
//defer deployment decision to fileChanged()
return true;
LOG.warn(e);
return false;
}

// else is it a context XML file
return lowername.endsWith(".xml");
}
}

Expand Down
Expand Up @@ -19,26 +19,35 @@
package org.eclipse.jetty.deploy.providers;

import java.io.File;
import java.net.URL;
import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.jetty.deploy.test.XmlConfiguredJetty;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.webapp.WebAppContext;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assumptions.assumeTrue;

@Disabled("See issue #1200")
@ExtendWith(WorkDirExtension.class)
public class WebAppProviderTest
{
Expand All @@ -49,7 +58,8 @@ public class WebAppProviderTest
@BeforeEach
public void setupEnvironment() throws Exception
{
jetty = new XmlConfiguredJetty(testdir.getEmptyPathDir());
Path p = testdir.getEmptyPathDir();
jetty = new XmlConfiguredJetty(p);
jetty.addConfiguration("jetty.xml");
jetty.addConfiguration("jetty-http.xml");
jetty.addConfiguration("jetty-deploy-wars.xml");
Expand Down Expand Up @@ -86,6 +96,7 @@ public void teardownEnvironment() throws Exception
jetty.stop();
}

@Disabled("See issue #1200")
@Test
public void testStartupContext()
{
Expand All @@ -101,7 +112,8 @@ public void testStartupContext()
// Test for correct behaviour
assertTrue(hasJettyGeneratedPath(workDir, "foo.war"), "Should have generated directory in work directory: " + workDir);
}


@Disabled("See issue #1200")
@Test
public void testStartupSymlinkContext()
{
Expand All @@ -119,7 +131,103 @@ public void testStartupSymlinkContext()
File workDir = jetty.getJettyDir("workish");
assertTrue(hasJettyGeneratedPath(workDir, "bar.war"), "Should have generated directory in work directory: " + workDir);
}

@Test
public void testWebappSymlinkDir() throws Exception
{
jetty.stop(); //reconfigure jetty

testdir.ensureEmpty();

jetty = new XmlConfiguredJetty(testdir.getEmptyPathDir());
jetty.addConfiguration("jetty.xml");
jetty.addConfiguration("jetty-http.xml");
jetty.addConfiguration("jetty-deploy-wars.xml");

assumeTrue(symlinkSupported);

//delete the existing webapps directory
File webapps = jetty.getJettyDir("webapps");
assertTrue(IO.delete(webapps));

//make a different directory to contain webapps
File x = jetty.getJettyDir("x");
Files.createDirectory(x.toPath());

//Put a webapp into it
File srcDir = MavenTestingUtils.getTestResourceDir("webapps");
File fooWar = new File(x, "foo.war");
IO.copy(new File(srcDir, "foo-webapp-1.war"), fooWar);
assertTrue(Files.exists(fooWar.toPath()));

//make a link from x to webapps
Files.createSymbolicLink(jetty.getJettyDir("webapps").toPath(), x.toPath());
assertTrue(Files.exists(jetty.getJettyDir("webapps").toPath()));

jetty.load();
jetty.start();

//only webapp in x should be deployed, not x itself
jetty.assertWebAppContextsExists("/foo");
}

@Test
public void testBaseDirSymlink() throws Exception
{
jetty.stop(); //reconfigure jetty

testdir.ensureEmpty();

Path realBase = testdir.getEmptyPathDir();

//set jetty up on the real base
jetty = new XmlConfiguredJetty(realBase);
jetty.addConfiguration("jetty.xml");
jetty.addConfiguration("jetty-http.xml");
jetty.addConfiguration("jetty-deploy-wars.xml");

//Put a webapp into the base
jetty.copyWebapp("foo-webapp-1.war", "foo.war");

//create the jetty structure
jetty.load();
jetty.start();
Path jettyHome = jetty.getJettyHome().toPath();

jetty.stop();

//Make a symbolic link to the real base
File testsDir = MavenTestingUtils.getTargetTestingDir();
Path symlinkBase = Files.createSymbolicLink(testsDir.toPath().resolve("basedirsymlink-" + System.currentTimeMillis()), jettyHome);
Map<String, String> properties = new HashMap<>();
properties.put("jetty.home", jettyHome.toString());
//Start jetty, but this time running from the symlinked base
System.setProperty("jetty.home", properties.get("jetty.home"));

List<URL> configurations = jetty.getConfigurations();
Server server = XmlConfiguredJetty.loadConfigurations(configurations, properties);

try
{
server.start();
HandlerCollection handlers = (HandlerCollection)server.getHandler();
Handler[] children = server.getChildHandlersByClass(WebAppContext.class);
assertEquals(1, children.length);
assertEquals("/foo", ((WebAppContext)children[0]).getContextPath());
}
finally
{
server.stop();
}
}

private Map<String, String> setupJettyProperties(Path jettyHome)
{
Map<String, String> properties = new HashMap<>();
properties.put("jetty.home", jettyHome.toFile().getAbsolutePath());
return properties;
}

private static boolean hasJettyGeneratedPath(File basedir, String expectedWarFilename)
{
File[] paths = basedir.listFiles();
Expand Down

0 comments on commit 37fffb1

Please sign in to comment.