Merge "Omaha #4491: Address security scan findings in IscReceiveSrv." into omaha_16.1.1
Former-commit-id: 51ecf21dc606759e9e70d2d67160a69517ccbcab
This commit is contained in:
commit
db2ce32c4b
1 changed files with 129 additions and 125 deletions
|
@ -19,15 +19,17 @@
|
|||
**/
|
||||
package com.raytheon.edex.plugin.gfe.isc;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
@ -51,8 +53,6 @@ import com.raytheon.uf.common.python.concurrent.PythonJobCoordinator;
|
|||
import com.raytheon.uf.common.status.IUFStatusHandler;
|
||||
import com.raytheon.uf.common.status.UFStatus;
|
||||
import com.raytheon.uf.common.status.UFStatus.Priority;
|
||||
import com.raytheon.uf.common.util.FileUtil;
|
||||
import com.raytheon.uf.common.util.file.FilenameFilters;
|
||||
|
||||
/**
|
||||
* ISC data receive service. Takes incoming request and executes iscDataRec
|
||||
|
@ -69,6 +69,7 @@ import com.raytheon.uf.common.util.file.FilenameFilters;
|
|||
* Mar 14, 2013 #1794 djohnson Consolidate common FilenameFilter implementations.
|
||||
* Dec 10, 2014 #4953 randerso Properly handle single file reception
|
||||
* May 06, 2015 #4383 dgilling Properly XML parse incoming XML file.
|
||||
* May 20, 2015 #4491 dgilling Remediate path manipulation possibilities.
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -81,12 +82,23 @@ public class IscReceiveSrv {
|
|||
private static final transient IUFStatusHandler statusHandler = UFStatus
|
||||
.getHandler(IscReceiveSrv.class);
|
||||
|
||||
private static final String ISC_REQUEST = "iscrequest";
|
||||
|
||||
private static final String METHOD_NAME = "main";
|
||||
|
||||
private static final FilenameFilter docFileFilter = FilenameFilters
|
||||
.byFileExtension(".doc");
|
||||
/*
|
||||
* TODO: determine if this constant and the cleanup of DOC files in the
|
||||
* finally block of prepareIscDataRec() is still necessary.
|
||||
*/
|
||||
private static final String DOC_FILE_FILTER = "*.doc";
|
||||
|
||||
private static final Path ISC_PRODUCTS_DIR = Paths.get("/awips2",
|
||||
"GFESuite", "products", "ISC");
|
||||
|
||||
private static final Collection<Path> WHITELISTED_PATHS = Arrays
|
||||
.asList(ISC_PRODUCTS_DIR);
|
||||
|
||||
private static final String COPY_ERROR_MSG = "Failed to copy: [%s] to %s. Unable to execute iscDataRec for %s.";
|
||||
|
||||
private static final String UNSAFE_PATH_MSG = "Skipping iscDataRec processing because file %s comes from an unsafe location.";
|
||||
|
||||
private static final IPythonJobListener<String> jobListener = new IPythonJobListener<String>() {
|
||||
|
||||
|
@ -150,131 +162,102 @@ public class IscReceiveSrv {
|
|||
throws IOException, InterruptedException,
|
||||
GfeConfigurationException, SAXException,
|
||||
ParserConfigurationException {
|
||||
Map<String, String[]> siteMap = new HashMap<String, String[]>();
|
||||
Map<String, String[]> siteMap = new HashMap<>();
|
||||
|
||||
String[] incomingFiles = args[2].split(",");
|
||||
String xmlFileName = "";
|
||||
String dataFileName = null;
|
||||
if (incomingFiles.length == 1) {
|
||||
xmlFileName = incomingFiles[0];
|
||||
} else {
|
||||
dataFileName = incomingFiles[0];
|
||||
xmlFileName = incomingFiles[1];
|
||||
}
|
||||
String xmlPathString = (incomingFiles.length == 1) ? incomingFiles[0]
|
||||
: incomingFiles[1];
|
||||
String dataPathString = (incomingFiles.length == 1) ? null
|
||||
: incomingFiles[0];
|
||||
Path xmlFilePath = Paths.get(xmlPathString);
|
||||
Path dataFilePath = (dataPathString != null) ? Paths
|
||||
.get(dataPathString) : null;
|
||||
|
||||
final File incomingXMLFile = new File(xmlFileName);
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder db = dbf.newDocumentBuilder();
|
||||
Document doc = db.parse(incomingXMLFile);
|
||||
doc.getDocumentElement().normalize();
|
||||
try {
|
||||
if (!isSafePathToProcess(xmlFilePath)) {
|
||||
statusHandler.warn(String.format(UNSAFE_PATH_MSG, xmlFilePath));
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
Collection<String> siteList = getXMLDestinations(doc);
|
||||
Set<String> activeSites = IFPServer.getActiveSites();
|
||||
if ((dataFilePath != null) && (!isSafePathToProcess(dataFilePath))) {
|
||||
statusHandler
|
||||
.warn(String.format(UNSAFE_PATH_MSG, dataFilePath));
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
if (ISC_REQUEST.equals(doc.getDocumentElement().getNodeName())) {
|
||||
/*
|
||||
* This case is for processing an ISC Request/Reply message
|
||||
* requesting our site's grids.
|
||||
*/
|
||||
for (String siteId : siteList) {
|
||||
if (activeSites.contains(siteId)) {
|
||||
if (IFPServerConfigManager.getServerConfig(siteId)
|
||||
.requestISC()) {
|
||||
String[] newArgs = new String[args.length];
|
||||
System.arraycopy(args, 0, newArgs, 0, args.length);
|
||||
String newXmlFileName = xmlFileName + "." + siteId;
|
||||
FileUtil.copyFile(incomingXMLFile, new File(
|
||||
newXmlFileName));
|
||||
newArgs[2] = newXmlFileName;
|
||||
siteMap.put(siteId, newArgs);
|
||||
String xmlFileName = xmlFilePath.getFileName().toString();
|
||||
String dataFileName = (dataPathString != null) ? dataFilePath
|
||||
.getFileName().toString() : null;
|
||||
|
||||
Collection<String> destinations = getXMLDestinations(xmlFilePath);
|
||||
Set<String> activeSites = IFPServer.getActiveSites();
|
||||
Set<String> activeDestinations = new HashSet<>(activeSites);
|
||||
activeDestinations.retainAll(destinations);
|
||||
|
||||
for (String siteId : activeDestinations) {
|
||||
if (IFPServerConfigManager.getServerConfig(siteId).requestISC()) {
|
||||
String[] modifiedArgs = new String[args.length];
|
||||
System.arraycopy(args, 0, modifiedArgs, 0, args.length);
|
||||
|
||||
if (dataFilePath != null) {
|
||||
String newDataFileName = dataFileName + "." + siteId;
|
||||
Path newDataFilePath = dataFilePath
|
||||
.resolveSibling(newDataFileName);
|
||||
|
||||
try {
|
||||
Files.copy(dataFilePath, newDataFilePath,
|
||||
StandardCopyOption.REPLACE_EXISTING);
|
||||
} catch (IOException e) {
|
||||
statusHandler.error(String.format(COPY_ERROR_MSG,
|
||||
dataFilePath, newDataFilePath, siteId), e);
|
||||
continue;
|
||||
}
|
||||
|
||||
modifiedArgs[2] = modifiedArgs[2].replace(
|
||||
dataPathString, newDataFilePath.toString());
|
||||
}
|
||||
|
||||
String newXmlFileName = xmlFileName + "." + siteId;
|
||||
Path newXmlFilePath = xmlFilePath
|
||||
.resolveSibling(newXmlFileName);
|
||||
|
||||
try {
|
||||
Files.copy(xmlFilePath, newXmlFilePath,
|
||||
StandardCopyOption.REPLACE_EXISTING);
|
||||
} catch (IOException e) {
|
||||
statusHandler.error(String.format(COPY_ERROR_MSG,
|
||||
xmlFilePath, newXmlFilePath, siteId), e);
|
||||
continue;
|
||||
}
|
||||
|
||||
modifiedArgs[2] = modifiedArgs[2].replace(xmlPathString,
|
||||
newXmlFilePath.toString());
|
||||
|
||||
siteMap.put(siteId, modifiedArgs);
|
||||
}
|
||||
}
|
||||
incomingXMLFile.delete();
|
||||
} else {
|
||||
try {
|
||||
for (String site : siteList) {
|
||||
if (activeSites.contains(site)
|
||||
&& IFPServerConfigManager.getServerConfig(site)
|
||||
.requestISC()) {
|
||||
String[] modifiedArgs = new String[args.length];
|
||||
System.arraycopy(args, 0, modifiedArgs, 0, args.length);
|
||||
} finally {
|
||||
Collection<Path> filesToDelete = new HashSet<>();
|
||||
|
||||
if (dataFileName != null) {
|
||||
String newFileName = dataFileName + "." + site;
|
||||
try {
|
||||
FileUtil.copyFile(new File(dataFileName),
|
||||
new File(newFileName));
|
||||
} catch (IOException e) {
|
||||
statusHandler
|
||||
.error("Failed to copy: ["
|
||||
+ dataFileName
|
||||
+ "] to "
|
||||
+ newFileName
|
||||
+ ". Unable to execute iscDataRec for "
|
||||
+ site, e);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!new File(newFileName).exists()) {
|
||||
statusHandler
|
||||
.error("Failed to copy: ["
|
||||
+ dataFileName
|
||||
+ "] to "
|
||||
+ newFileName
|
||||
+ ". Unable to execute iscDataRec for "
|
||||
+ site);
|
||||
continue;
|
||||
}
|
||||
modifiedArgs[2] = modifiedArgs[2].replace(
|
||||
dataFileName, newFileName);
|
||||
}
|
||||
|
||||
String newXmlFileName = xmlFileName + "." + site;
|
||||
try {
|
||||
FileUtil.copyFile(new File(xmlFileName), new File(
|
||||
newXmlFileName));
|
||||
} catch (IOException e) {
|
||||
statusHandler.error("Failed to copy: ["
|
||||
+ xmlFileName + "] to " + newXmlFileName
|
||||
+ ". Unable to execute iscDataRec for "
|
||||
+ site, e);
|
||||
continue;
|
||||
}
|
||||
if (!new File(newXmlFileName).exists()) {
|
||||
statusHandler.error("Failed to copy: ["
|
||||
+ xmlFileName + "] to " + newXmlFileName
|
||||
+ ". Unable to execute iscDataRec for "
|
||||
+ site);
|
||||
continue;
|
||||
}
|
||||
|
||||
modifiedArgs[2] = modifiedArgs[2].replace(xmlFileName,
|
||||
newXmlFileName);
|
||||
siteMap.put(site, modifiedArgs);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (dataFileName != null) {
|
||||
File dataFile = new File(dataFileName);
|
||||
if (dataFile.exists()) {
|
||||
if (!dataFile.delete()) {
|
||||
statusHandler.error("Unable to delete "
|
||||
+ dataFileName);
|
||||
}
|
||||
}
|
||||
filesToDelete.add(xmlFilePath);
|
||||
if (dataFilePath != null) {
|
||||
filesToDelete.add(dataFilePath);
|
||||
}
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(
|
||||
xmlFilePath.getParent(), DOC_FILE_FILTER)) {
|
||||
for (Path entry : stream) {
|
||||
filesToDelete.add(entry);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
statusHandler.error("Unable to list .doc files in directory "
|
||||
+ xmlFilePath.getParent(), e);
|
||||
}
|
||||
|
||||
File xmlFile = incomingXMLFile;
|
||||
if (xmlFile.exists()) {
|
||||
if (!xmlFile.delete()) {
|
||||
statusHandler.error("Unable to delete " + xmlFileName);
|
||||
}
|
||||
}
|
||||
List<File> docFiles = FileUtil.listFiles(
|
||||
xmlFile.getParentFile(), docFileFilter, false);
|
||||
for (File docFile : docFiles) {
|
||||
docFile.delete();
|
||||
for (Path toDelete : filesToDelete) {
|
||||
try {
|
||||
Files.deleteIfExists(toDelete);
|
||||
} catch (IOException e) {
|
||||
statusHandler.error("Unable to delete file " + toDelete, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -282,9 +265,29 @@ public class IscReceiveSrv {
|
|||
return siteMap;
|
||||
}
|
||||
|
||||
private Collection<String> getXMLDestinations(final Document doc)
|
||||
private boolean isSafePathToProcess(Path path) {
|
||||
try {
|
||||
Path realPath = path.toRealPath();
|
||||
for (Path safePath : WHITELISTED_PATHS) {
|
||||
if (realPath.startsWith(safePath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
statusHandler.error("Unable to resolve the real path for " + path,
|
||||
e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private Collection<String> getXMLDestinations(final Path xmlDocumentPath)
|
||||
throws SAXException, IOException, ParserConfigurationException {
|
||||
Collection<String> destinations = new HashSet<>();
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
dbf.setExpandEntityReferences(false);
|
||||
DocumentBuilder db = dbf.newDocumentBuilder();
|
||||
Document doc = db.parse(xmlDocumentPath.toFile());
|
||||
doc.getDocumentElement().normalize();
|
||||
|
||||
// Expected XML format:
|
||||
// <isc>
|
||||
|
@ -295,6 +298,7 @@ public class IscReceiveSrv {
|
|||
// </address>
|
||||
// </destinations>
|
||||
// </isc>
|
||||
Collection<String> destinations = new HashSet<>();
|
||||
NodeList destNodes = doc.getElementsByTagName("destinations");
|
||||
if (destNodes.getLength() > 0) {
|
||||
Node destNode = destNodes.item(0);
|
||||
|
|
Loading…
Add table
Reference in a new issue