View Javadoc
1   /*
2    * This file is part of dependency-check-core.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   * Copyright (c) 2018 Paul Irwin. All Rights Reserved.
17   */
18  package org.owasp.dependencycheck.data.nuget;
19  
20  import org.owasp.dependencycheck.utils.XmlUtils;
21  import org.w3c.dom.Document;
22  import org.w3c.dom.NamedNodeMap;
23  import org.w3c.dom.Node;
24  import org.w3c.dom.NodeList;
25  import org.xml.sax.SAXException;
26  
27  import javax.xml.parsers.DocumentBuilder;
28  import javax.xml.parsers.ParserConfigurationException;
29  import javax.xml.xpath.XPath;
30  import javax.xml.xpath.XPathConstants;
31  import javax.xml.xpath.XPathExpressionException;
32  import javax.xml.xpath.XPathFactory;
33  import java.io.IOException;
34  import java.io.InputStream;
35  import java.util.HashMap;
36  import java.util.Map;
37  import java.util.Properties;
38  import org.owasp.dependencycheck.utils.InterpolationUtil;
39  import org.owasp.dependencycheck.utils.InterpolationUtil.SyntaxStyle;
40  
41  /**
42   * Parses a nuget's Directory.Packages.props file using XPath.
43   *
44   * @author Jeremy Long
45   */
46  public class DirectoryPackagesPropsParser {
47  
48      /**
49       * Parses the given stream for Directory.Packages.props elements.
50       *
51       * @param stream the input stream to parse
52       * @param props the Directory.Build.props properties
53       * @return a collection of discovered NuGet package references
54       * @throws MSBuildProjectParseException if an exception occurs
55       */
56      public Map<String, String> parse(InputStream stream, Properties props) throws MSBuildProjectParseException {
57          try {
58              final DocumentBuilder db = XmlUtils.buildSecureDocumentBuilder();
59              final Document d = db.parse(stream);
60  
61              final XPath xpath = XPathFactory.newInstance().newXPath();
62              final Map<String, String> packages = new HashMap<>();
63              final Node centralNode = (Node) xpath.evaluate("/Project/PropertyGroup/ManagePackageVersionsCentrally", d, XPathConstants.NODE);
64              if (centralNode != null && centralNode.getChildNodes().getLength() == 1) {
65                  final String val = centralNode.getChildNodes().item(0).getNodeValue();
66                  if (!"true".equalsIgnoreCase(val)) {
67                      return packages;
68                  }
69              } else {
70                  return packages;
71              }
72              final NodeList nodeList = (NodeList) xpath.evaluate("//PackageVersion", d, XPathConstants.NODESET);
73  
74              if (nodeList == null) {
75                  throw new MSBuildProjectParseException("Unable to parse Directory.Packages.props file");
76              }
77  
78              for (int i = 0; i < nodeList.getLength(); i++) {
79                  final Node node = nodeList.item(i);
80                  final NamedNodeMap attrs = node.getAttributes();
81  
82                  final Node includeAttr = attrs.getNamedItem("Include");
83                  if (includeAttr == null) {
84                      // Issue 5144 work-around for NPE on packageReferences other than includes
85                      continue;
86                  }
87                  final String include = includeAttr.getNodeValue();
88                  String version = null;
89  
90                  if (attrs.getNamedItem("Version") != null) {
91                      version = attrs.getNamedItem("Version").getNodeValue();
92                  } else if (xpath.evaluate("Version", node, XPathConstants.NODE) instanceof Node) {
93                      version = ((Node) xpath.evaluate("Version", node, XPathConstants.NODE)).getTextContent();
94                  }
95  
96                  if (include != null && version != null) {
97                      version = InterpolationUtil.interpolate(version, props, SyntaxStyle.MSBUILD);
98                      packages.put(include, version);
99                  }
100             }
101 
102             return packages;
103         } catch (ParserConfigurationException | SAXException | IOException | XPathExpressionException | MSBuildProjectParseException e) {
104             throw new MSBuildProjectParseException("Unable to parse Directory.Packages.props file", e);
105         }
106     }
107 
108 }