checkstyle/findbugs corrections

This commit is contained in:
Jeremy Long
2016-05-25 17:21:46 -04:00
parent c0384bb0ee
commit 6a807bc002
11 changed files with 368 additions and 258 deletions

View File

@@ -35,11 +35,14 @@ import org.slf4j.LoggerFactory;
/** /**
* <p> * <p>
* This analyzer ensures dependencies that should be grouped together, to remove excess noise from the report, are grouped. An * This analyzer ensures dependencies that should be grouped together, to remove
* example would be Spring, Spring Beans, Spring MVC, etc. If they are all for the same version and have the same relative path * excess noise from the report, are grouped. An example would be Spring, Spring
* then these should be grouped into a single dependency under the core/main library.</p> * Beans, Spring MVC, etc. If they are all for the same version and have the
* same relative path then these should be grouped into a single dependency
* under the core/main library.</p>
* <p> * <p>
* Note, this grouping only works on dependencies with identified CVE entries</p> * Note, this grouping only works on dependencies with identified CVE
* entries</p>
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
@@ -92,12 +95,14 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
//</editor-fold> //</editor-fold>
/** /**
* Analyzes a set of dependencies. If they have been found to have the same base path and the same set of identifiers they are * Analyzes a set of dependencies. If they have been found to have the same
* likely related. The related dependencies are bundled into a single reportable item. * base path and the same set of identifiers they are likely related. The
* related dependencies are bundled into a single reportable item.
* *
* @param ignore this analyzer ignores the dependency being analyzed * @param ignore this analyzer ignores the dependency being analyzed
* @param engine the engine that is scanning the dependencies * @param engine the engine that is scanning the dependencies
* @throws AnalysisException is thrown if there is an error reading the JAR file. * @throws AnalysisException is thrown if there is an error reading the JAR
* file.
*/ */
@Override @Override
public void analyze(Dependency ignore, Engine engine) throws AnalysisException { public void analyze(Dependency ignore, Engine engine) throws AnalysisException {
@@ -138,11 +143,11 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
mergeDependencies(nextDependency, dependency, dependenciesToRemove); mergeDependencies(nextDependency, dependency, dependenciesToRemove);
break; //since we merged into the next dependency - skip forward to the next in mainIterator break; //since we merged into the next dependency - skip forward to the next in mainIterator
} }
} else if ( isSameRubyGem(dependency, nextDependency) ) { } else if (isSameRubyGem(dependency, nextDependency)) {
Dependency main = getMainGemspecDependency(dependency, nextDependency); final Dependency main = getMainGemspecDependency(dependency, nextDependency);
if (main == dependency) { if (main == dependency) {
mergeDependencies(dependency, nextDependency, dependenciesToRemove); mergeDependencies(dependency, nextDependency, dependenciesToRemove);
} else { } else {
mergeDependencies(nextDependency, dependency, dependenciesToRemove); mergeDependencies(nextDependency, dependency, dependenciesToRemove);
break; //since we merged into the next dependency - skip forward to the next in mainIterator break; //since we merged into the next dependency - skip forward to the next in mainIterator
} }
@@ -160,10 +165,11 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
* Adds the relatedDependency to the dependency's related dependencies. * Adds the relatedDependency to the dependency's related dependencies.
* *
* @param dependency the main dependency * @param dependency the main dependency
* @param relatedDependency a collection of dependencies to be removed from the main analysis loop, this is the source of * @param relatedDependency a collection of dependencies to be removed from
* dependencies to remove * the main analysis loop, this is the source of dependencies to remove
* @param dependenciesToRemove a collection of dependencies that will be removed from the main analysis loop, this function * @param dependenciesToRemove a collection of dependencies that will be
* adds to this collection * removed from the main analysis loop, this function adds to this
* collection
*/ */
private void mergeDependencies(final Dependency dependency, final Dependency relatedDependency, final Set<Dependency> dependenciesToRemove) { private void mergeDependencies(final Dependency dependency, final Dependency relatedDependency, final Set<Dependency> dependenciesToRemove) {
dependency.addRelatedDependency(relatedDependency); dependency.addRelatedDependency(relatedDependency);
@@ -179,7 +185,8 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
} }
/** /**
* Attempts to trim a maven repo to a common base path. This is typically [drive]\[repo_location]\repository\[path1]\[path2]. * Attempts to trim a maven repo to a common base path. This is typically
* [drive]\[repo_location]\repository\[path1]\[path2].
* *
* @param path the path to trim * @param path the path to trim
* @return a string representing the base path. * @return a string representing the base path.
@@ -204,11 +211,13 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
} }
/** /**
* Returns true if the file names (and version if it exists) of the two dependencies are sufficiently similar. * Returns true if the file names (and version if it exists) of the two
* dependencies are sufficiently similar.
* *
* @param dependency1 a dependency2 to compare * @param dependency1 a dependency2 to compare
* @param dependency2 a dependency2 to compare * @param dependency2 a dependency2 to compare
* @return true if the identifiers in the two supplied dependencies are equal * @return true if the identifiers in the two supplied dependencies are
* equal
*/ */
private boolean fileNameMatch(Dependency dependency1, Dependency dependency2) { private boolean fileNameMatch(Dependency dependency1, Dependency dependency2) {
if (dependency1 == null || dependency1.getFileName() == null if (dependency1 == null || dependency1.getFileName() == null
@@ -236,11 +245,13 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
} }
/** /**
* Returns true if the CPE identifiers in the two supplied dependencies are equal. * Returns true if the CPE identifiers in the two supplied dependencies are
* equal.
* *
* @param dependency1 a dependency2 to compare * @param dependency1 a dependency2 to compare
* @param dependency2 a dependency2 to compare * @param dependency2 a dependency2 to compare
* @return true if the identifiers in the two supplied dependencies are equal * @return true if the identifiers in the two supplied dependencies are
* equal
*/ */
private boolean cpeIdentifiersMatch(Dependency dependency1, Dependency dependency2) { private boolean cpeIdentifiersMatch(Dependency dependency1, Dependency dependency2) {
if (dependency1 == null || dependency1.getIdentifiers() == null if (dependency1 == null || dependency1.getIdentifiers() == null
@@ -312,35 +323,51 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
} }
/** /**
* Bundling Ruby gems that are identified from different .gemspec files but denote the same package path. * Bundling Ruby gems that are identified from different .gemspec files but
* This happens when Ruby bundler installs an app's dependencies by running "bundle install". * denote the same package path. This happens when Ruby bundler installs an
* application's dependencies by running "bundle install".
*
* @param dependency1 dependency to compare
* @param dependency2 dependency to compare
* @return true if the the dependencies being analyzed appear to be the
* same; otherwise false
*/ */
private boolean isSameRubyGem(Dependency dependency1, Dependency dependency2) { private boolean isSameRubyGem(Dependency dependency1, Dependency dependency2) {
if (dependency1 == null || dependency2 == null || if (dependency1 == null || dependency2 == null
!dependency1.getFileName().endsWith(".gemspec") || || !dependency1.getFileName().endsWith(".gemspec")
!dependency2.getFileName().endsWith(".gemspec") || || !dependency2.getFileName().endsWith(".gemspec")
dependency1.getPackagePath() == null || || dependency1.getPackagePath() == null
dependency2.getPackagePath() == null) { || dependency2.getPackagePath() == null) {
return false; return false;
} }
if (dependency1.getPackagePath().equalsIgnoreCase(dependency2.getPackagePath())) if (dependency1.getPackagePath().equalsIgnoreCase(dependency2.getPackagePath())) {
return true; return true;
}
return false; return false;
} }
/** /**
* Ruby gems installed by "bundle install" can have zero or more *.gemspec files, all of which have the same packagePath and should be grouped. * Ruby gems installed by "bundle install" can have zero or more *.gemspec
* If one of these gemspec is from <parent>/specifications/*.gemspec, because it is a stub with fully resolved gem meta-data * files, all of which have the same packagePath and should be grouped. If
* created by Ruby bundler, this dependency should be the main one. Otherwise, use dependency2 as main. * one of these gemspec is from <parent>/specifications/*.gemspec, because
* it is a stub with fully resolved gem meta-data created by Ruby bundler,
* this dependency should be the main one. Otherwise, use dependency2 as
* main.
* *
* This method returns null if any dependency is not from *.gemspec, or the two do not have the same packagePath. * This method returns null if any dependency is not from *.gemspec, or the
* In this case, they should not be grouped. * two do not have the same packagePath. In this case, they should not be
* grouped.
*
* @param dependency1 dependency to compare
* @param dependency2 dependency to compare
* @return the main dependency; or null if a gemspec is not included in the
* analysis
*/ */
private Dependency getMainGemspecDependency(Dependency dependency1, Dependency dependency2) { private Dependency getMainGemspecDependency(Dependency dependency1, Dependency dependency2) {
if (isSameRubyGem(dependency1, dependency2)) { if (isSameRubyGem(dependency1, dependency2)) {
final File lFile = dependency1.getActualFile(); final File lFile = dependency1.getActualFile();
File left = lFile.getParentFile(); final File left = lFile.getParentFile();
if (left != null && left.getName().equalsIgnoreCase("specifications")) { if (left != null && left.getName().equalsIgnoreCase("specifications")) {
return dependency1; return dependency1;
} }
@@ -350,12 +377,13 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
} }
/** /**
* This is likely a very broken attempt at determining if the 'left' dependency is the 'core' library in comparison to the * This is likely a very broken attempt at determining if the 'left'
* 'right' library. * dependency is the 'core' library in comparison to the 'right' library.
* *
* @param left the dependency to test * @param left the dependency to test
* @param right the dependency to test against * @param right the dependency to test against
* @return a boolean indicating whether or not the left dependency should be considered the "core" version. * @return a boolean indicating whether or not the left dependency should be
* considered the "core" version.
*/ */
boolean isCore(Dependency left, Dependency right) { boolean isCore(Dependency left, Dependency right) {
final String leftName = left.getFileName().toLowerCase(); final String leftName = left.getFileName().toLowerCase();
@@ -391,11 +419,13 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
} }
/** /**
* Compares the SHA1 hashes of two dependencies to determine if they are equal. * Compares the SHA1 hashes of two dependencies to determine if they are
* equal.
* *
* @param dependency1 a dependency object to compare * @param dependency1 a dependency object to compare
* @param dependency2 a dependency object to compare * @param dependency2 a dependency object to compare
* @return true if the sha1 hashes of the two dependencies match; otherwise false * @return true if the sha1 hashes of the two dependencies match; otherwise
* false
*/ */
private boolean hashesMatch(Dependency dependency1, Dependency dependency2) { private boolean hashesMatch(Dependency dependency1, Dependency dependency2) {
if (dependency1 == null || dependency2 == null || dependency1.getSha1sum() == null || dependency2.getSha1sum() == null) { if (dependency1 == null || dependency2 == null || dependency1.getSha1sum() == null || dependency2.getSha1sum() == null) {
@@ -405,12 +435,13 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
} }
/** /**
* Determines if the jar is shaded and the created pom.xml identified the same CPE as the jar - if so, the pom.xml dependency * Determines if the jar is shaded and the created pom.xml identified the
* should be removed. * same CPE as the jar - if so, the pom.xml dependency should be removed.
* *
* @param dependency a dependency to check * @param dependency a dependency to check
* @param nextDependency another dependency to check * @param nextDependency another dependency to check
* @return true if on of the dependencies is a pom.xml and the identifiers between the two collections match; otherwise false * @return true if on of the dependencies is a pom.xml and the identifiers
* between the two collections match; otherwise false
*/ */
private boolean isShadedJar(Dependency dependency, Dependency nextDependency) { private boolean isShadedJar(Dependency dependency, Dependency nextDependency) {
final String mainName = dependency.getFileName().toLowerCase(); final String mainName = dependency.getFileName().toLowerCase();
@@ -424,12 +455,13 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
} }
/** /**
* Determines which path is shortest; if path lengths are equal then we use compareTo of the string method to determine if the * Determines which path is shortest; if path lengths are equal then we use
* first path is smaller. * compareTo of the string method to determine if the first path is smaller.
* *
* @param left the first path to compare * @param left the first path to compare
* @param right the second path to compare * @param right the second path to compare
* @return <code>true</code> if the leftPath is the shortest; otherwise <code>false</code> * @return <code>true</code> if the leftPath is the shortest; otherwise
* <code>false</code>
*/ */
protected boolean firstPathIsShortest(String left, String right) { protected boolean firstPathIsShortest(String left, String right) {
final String leftPath = left.replace('\\', '/'); final String leftPath = left.replace('\\', '/');

View File

@@ -70,7 +70,7 @@ public class FileNameAnalyzer extends AbstractAnalyzer implements Analyzer {
/** /**
* Python init files * Python init files
*/ */
private static final NameFileFilter IGNORED_FILES = new NameFileFilter(new String[] { private static final NameFileFilter IGNORED_FILES = new NameFileFilter(new String[]{
"__init__.py", "__init__.py",
"__init__.pyc", "__init__.pyc",
"__init__.pyo", "__init__.pyo",
@@ -81,7 +81,8 @@ public class FileNameAnalyzer extends AbstractAnalyzer implements Analyzer {
* *
* @param dependency the dependency to analyze. * @param dependency the dependency to analyze.
* @param engine the engine that is scanning the dependencies * @param engine the engine that is scanning the dependencies
* @throws AnalysisException is thrown if there is an error reading the JAR file. * @throws AnalysisException is thrown if there is an error reading the JAR
* file.
*/ */
@Override @Override
public void analyze(Dependency dependency, Engine engine) throws AnalysisException { public void analyze(Dependency dependency, Engine engine) throws AnalysisException {
@@ -107,13 +108,6 @@ public class FileNameAnalyzer extends AbstractAnalyzer implements Analyzer {
fileName, Confidence.MEDIUM); fileName, Confidence.MEDIUM);
} }
//add as vendor and product evidence
// if (fileName.contains("-")) {
// dependency.getProductEvidence().addEvidence("file", "name",
// fileName, Confidence.HIGHEST);
// dependency.getVendorEvidence().addEvidence("file", "name",
// fileName, Confidence.HIGHEST);
// } else
if (!IGNORED_FILES.accept(f)) { if (!IGNORED_FILES.accept(f)) {
dependency.getProductEvidence().addEvidence("file", "name", dependency.getProductEvidence().addEvidence("file", "name",
fileName, Confidence.HIGH); fileName, Confidence.HIGH);

View File

@@ -29,7 +29,6 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@@ -578,7 +577,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
addMatchingValues(classes, trimmedDescription, dependency.getProductEvidence()); addMatchingValues(classes, trimmedDescription, dependency.getProductEvidence());
} }
String projectURL = pom.getProjectURL(); final String projectURL = pom.getProjectURL();
if (projectURL != null && !projectURL.trim().isEmpty()) { if (projectURL != null && !projectURL.trim().isEmpty()) {
dependency.getVendorEvidence().addEvidence("pom", "url", projectURL, Confidence.HIGHEST); dependency.getVendorEvidence().addEvidence("pom", "url", projectURL, Confidence.HIGHEST);
} }

View File

@@ -39,7 +39,8 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
* Used to analyze a Python package, and collect information that can be used to determine the associated CPE. * Used to analyze a Python package, and collect information that can be used to
* determine the associated CPE.
* *
* @author Dale Visser * @author Dale Visser
*/ */
@@ -166,7 +167,8 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer {
* *
* @param dependency the dependency being analyzed * @param dependency the dependency being analyzed
* @param engine the engine being used to perform the scan * @param engine the engine being used to perform the scan
* @throws AnalysisException thrown if there is an unrecoverable error analyzing the dependency * @throws AnalysisException thrown if there is an unrecoverable error
* analyzing the dependency
*/ */
@Override @Override
protected void analyzeFileType(Dependency dependency, Engine engine) protected void analyzeFileType(Dependency dependency, Engine engine)
@@ -175,9 +177,9 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer {
final File parent = file.getParentFile(); final File parent = file.getParentFile();
final String parentName = parent.getName(); final String parentName = parent.getName();
if (INIT_PY_FILTER.accept(file)) { if (INIT_PY_FILTER.accept(file)) {
//by definition, the containing folder of __init__.py is considered the package, even the file is empty: //by definition, the containing folder of __init__.py is considered the package, even the file is empty:
//"The __init__.py files are required to make Python treat the directories as containing packages" //"The __init__.py files are required to make Python treat the directories as containing packages"
//see section "6.4 Packages" from https://docs.python.org/2/tutorial/modules.html; //see section "6.4 Packages" from https://docs.python.org/2/tutorial/modules.html;
dependency.setDisplayFileName(parentName + "/__init__.py"); dependency.setDisplayFileName(parentName + "/__init__.py");
dependency.getProductEvidence().addEvidence(file.getName(), dependency.getProductEvidence().addEvidence(file.getName(),
"PackageName", parentName, Confidence.HIGHEST); "PackageName", parentName, Confidence.HIGHEST);
@@ -188,8 +190,7 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer {
analyzeFileContents(dependency, sourceFile); analyzeFileContents(dependency, sourceFile);
} }
} }
} } else {
else {
// copy, alter and set in case some other thread is iterating over // copy, alter and set in case some other thread is iterating over
final List<Dependency> dependencies = new ArrayList<Dependency>( final List<Dependency> dependencies = new ArrayList<Dependency>(
engine.getDependencies()); engine.getDependencies());
@@ -199,8 +200,9 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* This should gather information from leading docstrings, file comments, and assignments to __version__, __title__, * This should gather information from leading docstrings, file comments,
* __summary__, __uri__, __url__, __home*page__, __author__, and their all caps equivalents. * and assignments to __version__, __title__, __summary__, __uri__, __url__,
* __home*page__, __author__, and their all caps equivalents.
* *
* @param dependency the dependency being analyzed * @param dependency the dependency being analyzed
* @param file the file name to analyze * @param file the file name to analyze
@@ -291,7 +293,8 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* Gather evidence from a Python source file using the given string assignment regex pattern. * Gather evidence from a Python source file using the given string
* assignment regex pattern.
* *
* @param pattern to scan contents with * @param pattern to scan contents with
* @param contents of Python source file * @param contents of Python source file

View File

@@ -61,8 +61,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
*/ */
private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.PRE_INFORMATION_COLLECTION; private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.PRE_INFORMATION_COLLECTION;
private static final FileFilter FILTER private static final FileFilter FILTER = FileFilterBuilder.newInstance().addFilenames("Gemfile.lock").build();
= FileFilterBuilder.newInstance().addFilenames("Gemfile.lock").build();
public static final String NAME = "Name: "; public static final String NAME = "Name: ";
public static final String VERSION = "Version: "; public static final String VERSION = "Version: ";
public static final String ADVISORY = "Advisory: "; public static final String ADVISORY = "Advisory: ";
@@ -81,7 +80,9 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
/** /**
* Launch bundle-audit. * Launch bundle-audit.
* *
* @param folder directory that contains bundle audit
* @return a handle to the process * @return a handle to the process
* @throws AnalysisException thrown when there is an issue launching bundle audit
*/ */
private Process launchBundleAudit(File folder) throws AnalysisException { private Process launchBundleAudit(File folder) throws AnalysisException {
if (!folder.isDirectory()) { if (!folder.isDirectory()) {
@@ -131,7 +132,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
throw ae; throw ae;
} }
int exitValue = process.waitFor(); final int exitValue = process.waitFor();
if (0 == exitValue) { if (0 == exitValue) {
LOGGER.warn("Unexpected exit code from bundle-audit process. Disabling {}: {}", ANALYZER_NAME, exitValue); LOGGER.warn("Unexpected exit code from bundle-audit process. Disabling {}: {}", ANALYZER_NAME, exitValue);
setEnabled(false); setEnabled(false);
@@ -236,7 +237,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
try { try {
errReader = new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8")); errReader = new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8"));
while (errReader.ready()) { while (errReader.ready()) {
String error = errReader.readLine(); final String error = errReader.readLine();
LOGGER.warn(error); LOGGER.warn(error);
} }
rdr = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8")); rdr = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
@@ -284,7 +285,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
dependency = map.get(gem); dependency = map.get(gem);
LOGGER.debug(String.format("bundle-audit (%s): %s", parentName, nextLine)); LOGGER.debug(String.format("bundle-audit (%s): %s", parentName, nextLine));
} else if (nextLine.startsWith(VERSION)) { } else if (nextLine.startsWith(VERSION)) {
vulnerability = createVulnerability(parentName, dependency, vulnerability, gem, nextLine); vulnerability = createVulnerability(parentName, dependency, gem, nextLine);
} else if (nextLine.startsWith(ADVISORY)) { } else if (nextLine.startsWith(ADVISORY)) {
setVulnerabilityName(parentName, dependency, vulnerability, nextLine); setVulnerabilityName(parentName, dependency, vulnerability, nextLine);
} else if (nextLine.startsWith(CRITICALITY)) { } else if (nextLine.startsWith(CRITICALITY)) {
@@ -318,7 +319,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
private void addReferenceToVulnerability(String parentName, Vulnerability vulnerability, String nextLine) { private void addReferenceToVulnerability(String parentName, Vulnerability vulnerability, String nextLine) {
final String url = nextLine.substring(("URL: ").length()); final String url = nextLine.substring(("URL: ").length());
if (null != vulnerability) { if (null != vulnerability) {
Reference ref = new Reference(); final Reference ref = new Reference();
ref.setName(vulnerability.getName()); ref.setName(vulnerability.getName());
ref.setSource("bundle-audit"); ref.setSource("bundle-audit");
ref.setUrl(url); ref.setUrl(url);
@@ -351,7 +352,8 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
LOGGER.debug(String.format("bundle-audit (%s): %s", parentName, nextLine)); LOGGER.debug(String.format("bundle-audit (%s): %s", parentName, nextLine));
} }
private Vulnerability createVulnerability(String parentName, Dependency dependency, Vulnerability vulnerability, String gem, String nextLine) { private Vulnerability createVulnerability(String parentName, Dependency dependency, String gem, String nextLine) {
Vulnerability vulnerability = null;
if (null != dependency) { if (null != dependency) {
final String version = nextLine.substring(VERSION.length()); final String version = nextLine.substring(VERSION.length());
dependency.getVersionEvidence().addEvidence( dependency.getVersionEvidence().addEvidence(

View File

@@ -25,20 +25,23 @@ import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Dependency;
/** /**
* This analyzer accepts the fully resolved .gemspec created by the Ruby bundler (http://bundler.io) * This analyzer accepts the fully resolved .gemspec created by the Ruby bundler
* for better evidence results. It also tries to resolve the dependency packagePath * (http://bundler.io) for better evidence results. It also tries to resolve the
* to where the gem is actually installed. Then during {@link AnalysisPhase.PRE_FINDING_ANALYSIS} * dependency packagePath to where the gem is actually installed. Then during {@link AnalysisPhase.PRE_FINDING_ANALYSIS}
* {@link DependencyBundlingAnalyzer} will merge two .gemspec dependencies together if * {@link DependencyBundlingAnalyzer} will merge two .gemspec dependencies
* <code>Dependency.getPackagePath()</code> are the same. * together if <code>Dependency.getPackagePath()</code> are the same.
* *
* Ruby bundler creates new .gemspec files under a folder called "specifications" at deploy time, * Ruby bundler creates new .gemspec files under a folder called
* in addition to the original .gemspec files from source. The bundler generated * "specifications" at deploy time, in addition to the original .gemspec files
* .gemspec files always contain fully resolved attributes thus provide more accurate * from source. The bundler generated .gemspec files always contain fully
* evidences, whereas the original .gemspec from source often contain variables for attributes * resolved attributes thus provide more accurate evidences, whereas the
* that can't be used for evidences. * original .gemspec from source often contain variables for attributes that
* can't be used for evidences.
* *
* Note this analyzer share the same {@link Settings.KEYS.ANALYZER_RUBY_GEMSPEC_ENABLED} as * Note this analyzer share the same
* {@link RubyGemspecAnalyzer}, so it will enabled/disabled with {@link RubyGemspecAnalyzer}. * {@link Settings.KEYS.ANALYZER_RUBY_GEMSPEC_ENABLED} as
* {@link RubyGemspecAnalyzer}, so it will enabled/disabled with
* {@link RubyGemspecAnalyzer}.
* *
* @author Bianca Jiang (biancajiang@gmail.com) * @author Bianca Jiang (biancajiang@gmail.com)
*/ */
@@ -49,11 +52,15 @@ public class RubyBundlerAnalyzer extends RubyGemspecAnalyzer {
*/ */
private static final String ANALYZER_NAME = "Ruby Bundler Analyzer"; private static final String ANALYZER_NAME = "Ruby Bundler Analyzer";
//Folder name that contains .gemspec files created by "bundle install" /**
private static final String SPECIFICATIONS = "specifications"; * Folder name that contains .gemspec files created by "bundle install"
*/
private static final String SPECIFICATIONS = "specifications";
//Folder name that contains the gems by "bundle install" /**
private static final String GEMS = "gems"; * Folder name that contains the gems by "bundle install"
*/
private static final String GEMS = "gems";
/** /**
* Returns the name of the analyzer. * Returns the name of the analyzer.
@@ -66,61 +73,66 @@ public class RubyBundlerAnalyzer extends RubyGemspecAnalyzer {
} }
/** /**
* Only accept *.gemspec files generated by "bundle install --deployment" under "specifications" folder. * Only accept *.gemspec files generated by "bundle install --deployment"
* under "specifications" folder.
*
* @param pathname the path name to test
* @return true if the analyzer can process the given file; otherwise false
*/ */
@Override @Override
public boolean accept(File pathname) { public boolean accept(File pathname) {
boolean accepted = super.accept(pathname); boolean accepted = super.accept(pathname);
if(accepted == true) { if (accepted) {
File parentDir = pathname.getParentFile(); final File parentDir = pathname.getParentFile();
accepted = parentDir != null && parentDir.getName().equals(SPECIFICATIONS); accepted = parentDir != null && parentDir.getName().equals(SPECIFICATIONS);
} }
return accepted; return accepted;
} }
@Override @Override
protected void analyzeFileType(Dependency dependency, Engine engine) protected void analyzeFileType(Dependency dependency, Engine engine)
throws AnalysisException { throws AnalysisException {
super.analyzeFileType(dependency, engine); super.analyzeFileType(dependency, engine);
//find the corresponding gem folder for this .gemspec stub by "bundle install --deployment" //find the corresponding gem folder for this .gemspec stub by "bundle install --deployment"
File gemspecFile = dependency.getActualFile(); final File gemspecFile = dependency.getActualFile();
String gemFileName = gemspecFile.getName(); final String gemFileName = gemspecFile.getName();
final String gemName = gemFileName.substring(0, gemFileName.lastIndexOf(".gemspec")); final String gemName = gemFileName.substring(0, gemFileName.lastIndexOf(".gemspec"));
File specificationsDir = gemspecFile.getParentFile(); final File specificationsDir = gemspecFile.getParentFile();
if(specificationsDir != null && specificationsDir.getName().equals(SPECIFICATIONS) && specificationsDir.exists()) { if (specificationsDir != null && specificationsDir.getName().equals(SPECIFICATIONS) && specificationsDir.exists()) {
File parentDir = specificationsDir.getParentFile(); final File parentDir = specificationsDir.getParentFile();
if(parentDir != null && parentDir.exists()) { if (parentDir != null && parentDir.exists()) {
File gemsDir = new File(parentDir, GEMS); final File gemsDir = new File(parentDir, GEMS);
if(gemsDir != null && gemsDir.exists()) { if (gemsDir.exists()) {
File[] matchingFiles = gemsDir.listFiles(new FilenameFilter() { final File[] matchingFiles = gemsDir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) { public boolean accept(File dir, String name) {
return name.equals(gemName); return name.equals(gemName);
} }
}); });
if(matchingFiles.length > 0) { if (matchingFiles != null && matchingFiles.length > 0) {
String gemPath = matchingFiles[0].getAbsolutePath(); final String gemPath = matchingFiles[0].getAbsolutePath();
if(dependency.getActualFilePath().equals(dependency.getFilePath())) { if (dependency.getActualFilePath().equals(dependency.getFilePath())) {
if(gemPath != null) if (gemPath != null) {
dependency.setPackagePath(gemPath); dependency.setPackagePath(gemPath);
} else { }
//.gemspec's actualFilePath and filePath are different when it's from a compressed file } else {
//in which case actualFilePath is the temp directory used by decompression. //.gemspec's actualFilePath and filePath are different when it's from a compressed file
//packagePath should use the filePath of the identified gem file in "gems" folder //in which case actualFilePath is the temp directory used by decompression.
File gemspecStub = new File(dependency.getFilePath()); //packagePath should use the filePath of the identified gem file in "gems" folder
File specDir = gemspecStub.getParentFile(); final File gemspecStub = new File(dependency.getFilePath());
if(specDir != null && specDir.getName().equals(SPECIFICATIONS)) { final File specDir = gemspecStub.getParentFile();
File gemsDir2 = new File(specDir.getParentFile(), GEMS); if (specDir != null && specDir.getName().equals(SPECIFICATIONS)) {
File packageDir = new File(gemsDir2, gemName); final File gemsDir2 = new File(specDir.getParentFile(), GEMS);
dependency.setPackagePath(packageDir.getAbsolutePath()); final File packageDir = new File(gemsDir2, gemName);
} dependency.setPackagePath(packageDir.getAbsolutePath());
} }
} }
} }
} }
} }
} }
}
} }

View File

@@ -34,15 +34,22 @@ import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.dependency.EvidenceCollection; import org.owasp.dependencycheck.dependency.EvidenceCollection;
import org.owasp.dependencycheck.utils.FileFilterBuilder; import org.owasp.dependencycheck.utils.FileFilterBuilder;
import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.Settings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* Used to analyze Ruby Gem specifications and collect information that can be used to determine the associated CPE. Regular * Used to analyze Ruby Gem specifications and collect information that can be
* expressions are used to parse the well-defined Ruby syntax that forms the specification. * used to determine the associated CPE. Regular expressions are used to parse
* the well-defined Ruby syntax that forms the specification.
* *
* @author Dale Visser * @author Dale Visser
*/ */
public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer { public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer {
/**
* The logger.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(RubyGemspecAnalyzer.class);
/** /**
* The name of the analyzer. * The name of the analyzer.
*/ */
@@ -53,13 +60,22 @@ public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer {
*/ */
private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION; private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
/**
* The gemspec file extension.
*/
private static final String GEMSPEC = "gemspec"; private static final String GEMSPEC = "gemspec";
private static final FileFilter FILTER /**
= FileFilterBuilder.newInstance().addExtensions(GEMSPEC).build(); * The file filter containing the list of file extensions that can be
//TODO: support Rakefile * analyzed.
//= FileFilterBuilder.newInstance().addExtensions(GEMSPEC).addFilenames("Rakefile").build(); */
private static final FileFilter FILTER = FileFilterBuilder.newInstance().addExtensions(GEMSPEC).build();
//TODO: support Rakefile
//= FileFilterBuilder.newInstance().addExtensions(GEMSPEC).addFilenames("Rakefile").build();
/**
* The name of the version file.
*/
private static final String VERSION_FILE_NAME = "VERSION"; private static final String VERSION_FILE_NAME = "VERSION";
/** /**
@@ -96,7 +112,8 @@ public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* Returns the key used in the properties file to reference the analyzer's enabled property. * Returns the key used in the properties file to reference the analyzer's
* enabled property.
* *
* @return the analyzer's enabled property setting key * @return the analyzer's enabled property setting key
*/ */
@@ -108,8 +125,7 @@ public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer {
/** /**
* The capture group #1 is the block variable. * The capture group #1 is the block variable.
*/ */
private static final Pattern GEMSPEC_BLOCK_INIT private static final Pattern GEMSPEC_BLOCK_INIT = Pattern.compile("Gem::Specification\\.new\\s+?do\\s+?\\|(.+?)\\|");
= Pattern.compile("Gem::Specification\\.new\\s+?do\\s+?\\|(.+?)\\|");
@Override @Override
protected void analyzeFileType(Dependency dependency, Engine engine) protected void analyzeFileType(Dependency dependency, Engine engine)
@@ -139,70 +155,89 @@ public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer {
addStringEvidence(vendor, contents, blockVariable, "homepage", "homepage", Confidence.HIGHEST); addStringEvidence(vendor, contents, blockVariable, "homepage", "homepage", Confidence.HIGHEST);
addStringEvidence(vendor, contents, blockVariable, "license", "licen[cs]es?", Confidence.HIGHEST); addStringEvidence(vendor, contents, blockVariable, "license", "licen[cs]es?", Confidence.HIGHEST);
String value = addStringEvidence(dependency.getVersionEvidence(), contents, blockVariable, "version", "version", Confidence.HIGHEST); final String value = addStringEvidence(dependency.getVersionEvidence(), contents,
if(value.length() < 1) blockVariable, "version", "version", Confidence.HIGHEST);
addEvidenceFromVersionFile(dependency.getActualFile(), dependency.getVersionEvidence()); if (value.length() < 1) {
addEvidenceFromVersionFile(dependency.getActualFile(), dependency.getVersionEvidence());
}
} }
setPackagePath(dependency); setPackagePath(dependency);
} }
/**
* Adds the specified evidence to the given evidence collection.
*
* @param evidences the collection to add the evidence to
* @param contents the evidence contents
* @param blockVariable the variable
* @param field the field
* @param fieldPattern the field pattern
* @param confidence the confidence of the evidence
* @return the evidence string value added
*/
private String addStringEvidence(EvidenceCollection evidences, String contents, private String addStringEvidence(EvidenceCollection evidences, String contents,
String blockVariable, String field, String fieldPattern, Confidence confidence) { String blockVariable, String field, String fieldPattern, Confidence confidence) {
String value = ""; String value = "";
//capture array value between [ ] //capture array value between [ ]
final Matcher arrayMatcher = Pattern.compile( final Matcher arrayMatcher = Pattern.compile(
String.format("\\s*?%s\\.%s\\s*?=\\s*?\\[(.*?)\\]", blockVariable, fieldPattern), Pattern.CASE_INSENSITIVE).matcher(contents); String.format("\\s*?%s\\.%s\\s*?=\\s*?\\[(.*?)\\]", blockVariable, fieldPattern), Pattern.CASE_INSENSITIVE).matcher(contents);
if(arrayMatcher.find()) { if (arrayMatcher.find()) {
String arrayValue = arrayMatcher.group(1); final String arrayValue = arrayMatcher.group(1);
value = arrayValue.replaceAll("['\"]", "").trim(); //strip quotes value = arrayValue.replaceAll("['\"]", "").trim(); //strip quotes
} } else { //capture single value between quotes
//capture single value between quotes final Matcher matcher = Pattern.compile(
else { String.format("\\s*?%s\\.%s\\s*?=\\s*?(['\"])(.*?)\\1", blockVariable, fieldPattern), Pattern.CASE_INSENSITIVE).matcher(contents);
final Matcher matcher = Pattern.compile( if (matcher.find()) {
String.format("\\s*?%s\\.%s\\s*?=\\s*?(['\"])(.*?)\\1", blockVariable, fieldPattern), Pattern.CASE_INSENSITIVE).matcher(contents); value = matcher.group(2);
if (matcher.find()) { }
value = matcher.group(2); }
} if (value.length() > 0) {
} evidences.addEvidence(GEMSPEC, field, value, confidence);
if(value.length() > 0) }
evidences.addEvidence(GEMSPEC, field, value, confidence);
return value; return value;
} }
private String addEvidenceFromVersionFile(File dependencyFile, EvidenceCollection versionEvidences) { /**
String value = null; * Adds evidence from the version file.
File parentDir = dependencyFile.getParentFile(); *
if(parentDir != null) { * @param dependencyFile the dependency being analyzed
File[] matchingFiles = parentDir.listFiles(new FilenameFilter() { * @param versionEvidences the version evidence
public boolean accept(File dir, String name) { */
return name.contains(VERSION_FILE_NAME); private void addEvidenceFromVersionFile(File dependencyFile, EvidenceCollection versionEvidences) {
} final File parentDir = dependencyFile.getParentFile();
}); if (parentDir != null) {
final File[] matchingFiles = parentDir.listFiles(new FilenameFilter() {
for(int i = 0; i < matchingFiles.length; i++) { public boolean accept(File dir, String name) {
try { return name.contains(VERSION_FILE_NAME);
List<String> lines = FileUtils.readLines(matchingFiles[i]); }
if(lines.size() == 1) { //TODO other checking? });
value = lines.get(0).trim(); for (File f : matchingFiles) {
versionEvidences.addEvidence(GEMSPEC, "version", value, Confidence.HIGH); try {
} final List<String> lines = FileUtils.readLines(f, Charset.defaultCharset());
if (lines.size() == 1) { //TODO other checking?
} catch (IOException e) { final String value = lines.get(0).trim();
e.printStackTrace(); versionEvidences.addEvidence(GEMSPEC, "version", value, Confidence.HIGH);
} }
} } catch (IOException e) {
} LOGGER.debug("Error reading gemspec", e);
}
return value; }
}
} }
/**
* Sets the package path on the dependency.
*
* @param dep the dependency to alter
*/
private void setPackagePath(Dependency dep) { private void setPackagePath(Dependency dep) {
File file = new File(dep.getFilePath()); final File file = new File(dep.getFilePath());
String parent = file.getParent(); final String parent = file.getParent();
if(parent != null) if (parent != null) {
dep.setPackagePath(parent); dep.setPackagePath(parent);
}
} }
} }

View File

@@ -36,9 +36,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* A program dependency. This object is one of the core components within DependencyCheck. It is used to collect information about * A program dependency. This object is one of the core components within
* the dependency in the form of evidence. The Evidence is then used to determine if there are any known, published, * DependencyCheck. It is used to collect information about the dependency in
* vulnerabilities associated with the program dependency. * the form of evidence. The Evidence is then used to determine if there are any
* known, published, vulnerabilities associated with the program dependency.
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
@@ -73,16 +74,30 @@ public class Dependency implements Serializable, Comparable<Dependency> {
*/ */
private String fileName; private String fileName;
/**
* The package path.
*/
private String packagePath; private String packagePath;
/**
* Returns the package path.
*
* @return the package path
*/
public String getPackagePath() { public String getPackagePath() {
return packagePath; return packagePath;
} }
public void setPackagePath(String packagePath) { /**
this.packagePath = packagePath; * Sets the package path.
} *
* @param packagePath the package path
*/
public void setPackagePath(String packagePath) {
this.packagePath = packagePath;
}
/** /**
* The md5 hash of the dependency. * The md5 hash of the dependency.
*/ */
private String md5sum; private String md5sum;
@@ -144,10 +159,12 @@ public class Dependency implements Serializable, Comparable<Dependency> {
} }
/** /**
* Returns the file name of the dependency with the backslash escaped for use in JavaScript. This is a complete hack as I * Returns the file name of the dependency with the backslash escaped for
* could not get the replace to work in the template itself. * use in JavaScript. This is a complete hack as I could not get the replace
* to work in the template itself.
* *
* @return the file name of the dependency with the backslash escaped for use in JavaScript * @return the file name of the dependency with the backslash escaped for
* use in JavaScript
*/ */
public String getFileNameForJavaScript() { public String getFileNameForJavaScript() {
return this.fileName.replace("\\", "\\\\"); return this.fileName.replace("\\", "\\\\");
@@ -199,9 +216,9 @@ public class Dependency implements Serializable, Comparable<Dependency> {
* @param filePath the file path of the dependency * @param filePath the file path of the dependency
*/ */
public void setFilePath(String filePath) { public void setFilePath(String filePath) {
if(this.packagePath == null || this.packagePath.equals(this.filePath)) if (this.packagePath == null || this.packagePath.equals(this.filePath)) {
this.packagePath = filePath; this.packagePath = filePath;
}
this.filePath = filePath; this.filePath = filePath;
} }
@@ -220,7 +237,8 @@ public class Dependency implements Serializable, Comparable<Dependency> {
} }
/** /**
* Returns the file name to display in reports; if no display file name has been set it will default to the actual file name. * Returns the file name to display in reports; if no display file name has
* been set it will default to the actual file name.
* *
* @return the file name to display * @return the file name to display
*/ */
@@ -235,8 +253,9 @@ public class Dependency implements Serializable, Comparable<Dependency> {
* <p> * <p>
* Gets the file path of the dependency.</p> * Gets the file path of the dependency.</p>
* <p> * <p>
* <b>NOTE:</b> This may not be the actual path of the file on disk. The actual path of the file on disk can be obtained via * <b>NOTE:</b> This may not be the actual path of the file on disk. The
* the getActualFilePath().</p> * actual path of the file on disk can be obtained via the
* getActualFilePath().</p>
* *
* @return the file path of the dependency * @return the file path of the dependency
*/ */
@@ -299,7 +318,8 @@ public class Dependency implements Serializable, Comparable<Dependency> {
} }
/** /**
* Adds an entry to the list of detected Identifiers for the dependency file. * Adds an entry to the list of detected Identifiers for the dependency
* file.
* *
* @param type the type of identifier (such as CPE) * @param type the type of identifier (such as CPE)
* @param value the value of the identifier * @param value the value of the identifier
@@ -311,7 +331,8 @@ public class Dependency implements Serializable, Comparable<Dependency> {
} }
/** /**
* Adds an entry to the list of detected Identifiers for the dependency file. * Adds an entry to the list of detected Identifiers for the dependency
* file.
* *
* @param type the type of identifier (such as CPE) * @param type the type of identifier (such as CPE)
* @param value the value of the identifier * @param value the value of the identifier
@@ -362,7 +383,8 @@ public class Dependency implements Serializable, Comparable<Dependency> {
} }
/** /**
* Adds an entry to the list of detected Identifiers for the dependency file. * Adds an entry to the list of detected Identifiers for the dependency
* file.
* *
* @param identifier the identifier to add * @param identifier the identifier to add
*/ */
@@ -594,8 +616,9 @@ public class Dependency implements Serializable, Comparable<Dependency> {
private Set<Dependency> relatedDependencies = new TreeSet<Dependency>(); private Set<Dependency> relatedDependencies = new TreeSet<Dependency>();
/** /**
* Get the value of {@link #relatedDependencies}. This field is used to collect other dependencies which really represent the * Get the value of {@link #relatedDependencies}. This field is used to
* same dependency, and may be presented as one item in reports. * collect other dependencies which really represent the same dependency,
* and may be presented as one item in reports.
* *
* @return the value of relatedDependencies * @return the value of relatedDependencies
*/ */
@@ -654,9 +677,11 @@ public class Dependency implements Serializable, Comparable<Dependency> {
} }
/** /**
* Adds a related dependency. The internal collection is normally a {@link java.util.TreeSet}, which relies on * Adds a related dependency. The internal collection is normally a
* {@link #compareTo(Dependency)}. A consequence of this is that if you attempt to add a dependency with the same file path * {@link java.util.TreeSet}, which relies on
* (modulo character case) as one that is already in the collection, it won't get added. * {@link #compareTo(Dependency)}. A consequence of this is that if you
* attempt to add a dependency with the same file path (modulo character
* case) as one that is already in the collection, it won't get added.
* *
* @param dependency a reference to the related dependency * @param dependency a reference to the related dependency
*/ */
@@ -706,7 +731,8 @@ public class Dependency implements Serializable, Comparable<Dependency> {
} }
/** /**
* Implementation of the Comparable&lt;Dependency&gt; interface. The comparison is solely based on the file path. * Implementation of the Comparable&lt;Dependency&gt; interface. The
* comparison is solely based on the file path.
* *
* @param o a dependency to compare * @param o a dependency to compare
* @return an integer representing the natural ordering * @return an integer representing the natural ordering
@@ -776,12 +802,14 @@ public class Dependency implements Serializable, Comparable<Dependency> {
} }
/** /**
* Standard toString() implementation showing the filename, actualFilePath, and filePath. * Standard toString() implementation showing the filename, actualFilePath,
* and filePath.
* *
* @return the string representation of the file * @return the string representation of the file
*/ */
@Override @Override
public String toString() { public String toString() {
return "Dependency{ fileName='" + fileName + "', actualFilePath='" + actualFilePath + "', filePath='" + filePath + "', packagePath='" + packagePath + "'}"; return "Dependency{ fileName='" + fileName + "', actualFilePath='" + actualFilePath
+ "', filePath='" + filePath + "', packagePath='" + packagePath + "'}";
} }
} }

View File

@@ -272,17 +272,17 @@ public class Model {
* @return the value of projectURL * @return the value of projectURL
*/ */
public String getProjectURL() { public String getProjectURL() {
return projectURL; return projectURL;
} }
/** /**
* Set the value of projectURL. * Set the value of projectURL.
* *
* @param parentVersion new value of projectURL * @param projectURL new value of projectURL
*/ */
public void setProjectURL(String projectURL) { public void setProjectURL(String projectURL) {
this.projectURL = projectURL; this.projectURL = projectURL;
} }
/** /**
* Process the Maven properties file and interpolate all properties. * Process the Maven properties file and interpolate all properties.
@@ -308,11 +308,14 @@ public class Model {
/** /**
* <p> * <p>
* A utility function that will interpolate strings based on values given in the properties file. It will also interpolate the * A utility function that will interpolate strings based on values given in
* strings contained within the properties file so that properties can reference other properties.</p> * the properties file. It will also interpolate the strings contained
* within the properties file so that properties can reference other
* properties.</p>
* <p> * <p>
* <b>Note:</b> if there is no property found the reference will be removed. In other words, if the interpolated string will * <b>Note:</b> if there is no property found the reference will be removed.
* be replaced with an empty string. * In other words, if the interpolated string will be replaced with an empty
* string.
* </p> * </p>
* <p> * <p>
* Example:</p> * Example:</p>
@@ -329,7 +332,8 @@ public class Model {
* </code> * </code>
* *
* @param text the string that contains references to properties. * @param text the string that contains references to properties.
* @param properties a collection of properties that may be referenced within the text. * @param properties a collection of properties that may be referenced
* within the text.
* @return the interpolated text. * @return the interpolated text.
*/ */
public static String interpolateString(String text, Properties properties) { public static String interpolateString(String text, Properties properties) {
@@ -340,8 +344,9 @@ public class Model {
return substitutor.replace(text); return substitutor.replace(text);
} }
/** /**
* Utility class that can provide values from a Properties object to a StrSubstitutor. * Utility class that can provide values from a Properties object to a
* StrSubstitutor.
*/ */
private static class PropertyLookup extends StrLookup { private static class PropertyLookup extends StrLookup {

View File

@@ -13,6 +13,6 @@
^ \* See the License for the specific language governing permissions and\s*$ ^ \* See the License for the specific language governing permissions and\s*$
^ \* limitations under the License\.\s*$ ^ \* limitations under the License\.\s*$
^ \*\s*$ ^ \*\s*$
^ \* Copyright \(c\) 201[0-9] (Jeremy Long|Steve Springett|The OWASP Foundation|Institute for Defense Analyses)\. All Rights Reserved\.\s*$ ^ \* Copyright \(c\) 201[0-9] (Jeremy Long|Steve Springett|Bianca Jiang|The OWASP Foundation|Institute for Defense Analyses)\. All Rights Reserved\.\s*$
^ \*/\s*$ ^ \*/\s*$
^package ^package