Compare commits

...

422 Commits

Author SHA1 Message Date
Jeremy Long
5574f1c24f version 1.4.3 2016-09-06 07:04:34 -04:00
Jeremy Long
9457744571 using more robust check for windows os 2016-09-06 06:42:12 -04:00
Jeremy Long
19243c479c disabling batch support for mysql to fix issue #503 - more testing needs to be done 2016-09-06 06:36:08 -04:00
Jeremy Long
e868ce8328 cleaned up file deletion code slightly 2016-09-06 06:23:55 -04:00
Jeremy Long
ffa846c05a updated compareTo so that null values are handled properly 2016-09-06 05:48:12 -04:00
Jeremy Long
dde1791476 minor rewording of a log statement 2016-09-06 05:47:44 -04:00
Jeremy Long
45438a7f06 removed temporary test code 2016-09-05 06:46:06 -04:00
Jeremy Long
c980e77ea3 added assume to skip errors when mono is not installed 2016-09-04 20:50:14 -04:00
Jeremy Long
176d3ddefa temporary fix for issue #534 2016-09-04 19:09:08 -04:00
Jeremy Long
98d783d448 added todo for NPE reasons 2016-09-04 18:51:07 -04:00
Jeremy Long
bcd6634d8a fixed NPE issues 2016-09-04 18:41:58 -04:00
Jeremy Long
0b260cef2a removed duplicated test 2016-09-04 08:00:43 -04:00
Jeremy Long
6a68abbd67 fixed unit test on non-windows 2016-09-01 06:12:35 -04:00
Jeremy Long
9fcf23c802 coverity, checkstyle, pmd, and findbugs suggested corrections 2016-09-01 05:46:09 -04:00
Jeremy Long
5c2c08e051 suppressed false positive, see issue #540 2016-08-30 06:12:17 -04:00
Jeremy Long
1f254997e1 patch to resolve issue #547 2016-08-28 07:46:42 -04:00
Jeremy Long
4f95af0864 removed config 2016-08-27 13:52:05 -04:00
Jeremy Long
6ff39be9d2 initial config 2016-08-27 13:41:29 -04:00
Jeremy Long
6cf5a47971 re-added the check for https that was accidentally removed 2016-08-27 11:43:33 -04:00
Jeremy Long
56da53c700 update for issue #523 - removed specific algorithm list to support differences in JDKs (ibm); just setting the protocol resolves the issue 2016-08-27 07:26:59 -04:00
Jeremy Long
7091e10795 added coverity badge 2016-08-23 21:19:01 -04:00
Jeremy Long
34765c5741 coverity suggested corrections - removed dead local store 2016-08-23 19:24:25 -04:00
Jeremy Long
36c139872a coverity suggested corrections 2016-08-23 19:20:54 -04:00
Jeremy Long
1e77cec677 improved error reporting for issue #547 2016-08-23 19:12:04 -04:00
Jeremy Long
e95e3fb2d0 coverity suggested corrections 2016-08-21 18:40:28 -04:00
Jeremy Long
39c2234e38 coverity suggested corrections 2016-08-21 16:51:09 -04:00
Jeremy Long
f4fff5d9cb checkstyle and formating updates 2016-08-21 15:59:47 -04:00
Jeremy Long
659785f972 checkstyle correction 2016-08-21 15:28:55 -04:00
Jeremy Long
85c04f6e3e checkstyle correction 2016-08-21 15:28:49 -04:00
Jeremy Long
bef117cbe8 coverity correction 2016-08-21 15:28:10 -04:00
Jeremy Long
46dd7cf86e checkstyle correction 2016-08-21 15:27:34 -04:00
Jeremy Long
9ed5a97267 Merge branch 'master' of github.com:jeremylong/DependencyCheck 2016-08-21 14:41:08 -04:00
Jeremy Long
cc2da70db2 updated ignore list 2016-08-21 14:41:01 -04:00
Jeremy Long
cedd93e774 coverity suggested corrections 2016-08-21 14:40:07 -04:00
Jeremy Long
632e1692eb Merge pull request #541 from biancajiang/swift_support
cocoapods and swift package manager support
2016-08-21 08:03:52 -04:00
Jeremy Long
4861592d2a assume no NPE due to issue with mock and some versions of the JDK 2016-08-21 07:25:37 -04:00
Jeremy Long
22e6d4edf3 updated jdk used by travis 2016-08-21 07:24:54 -04:00
Jeremy Long
e9bd7ff72f Merge branch 'master' of github.com:jeremylong/DependencyCheck 2016-08-21 07:11:56 -04:00
Jeremy Long
e7228fb489 updated jdk used by travis 2016-08-21 07:11:17 -04:00
Jeremy Long
96c03a68f2 Merge pull request #545 from colezlaw/grokassemblyfix
Updated GrokAssembly to deal with non-UTF-8 chars in types
2016-08-20 12:24:21 -04:00
Jeremy Long
4f6f248421 reworked aggregation mojo to resolve issues #325, #386, and #531 2016-08-20 12:15:49 -04:00
Will Stranathan
a8f14c86fd Updated GrokAssembly to deal with non-UTF-8 chars in types 2016-08-20 09:34:15 -04:00
Jeremy Long
36de3d1e25 removed unnecassary stacktrace from logs per issue #544 2016-08-18 09:59:21 -04:00
Jeremy Long
48bc4570e1 Merge pull request #542 from bodewig/document_skip_and_scanConfigurations
document skipConfigurations and scanConfigurations for gradle plugin
2016-08-14 06:48:19 -04:00
Stefan Bodewig
94b272dbae document skipConfigurations and scanConfigurations
closes jeremylong/dependency-check-gradle/#12
2016-08-14 11:10:38 +02:00
bjiang
c093edf459 update copyright and javadoc 2016-08-12 17:12:12 -04:00
bjiang
0164feffcc Merge branch 'master' into swift_support 2016-08-12 16:35:12 -04:00
bjiang
8cd377b99f use value of specification-version as version from Manifest 2016-08-12 13:32:25 -04:00
bjiang
74282c8ac5 filter out version from jar filename for name 2016-08-12 13:15:29 -04:00
Jeremy Long
d2158e5e44 fixed typo 2016-08-11 21:12:47 -04:00
Jeremy Long
9ea16ad1d1 skipped patch for Java 1.6 & 1.7 if the JRE is at least 1.8 - see issue #523 2016-08-11 20:59:26 -04:00
Jeremy Long
45941adb71 fixed type per issue #533 2016-08-11 20:55:36 -04:00
Jeremy Long
c4d662fd2b patch for issue #536 2016-08-11 20:49:27 -04:00
Jeremy Long
d9ce3cda66 snapshot version 2016-08-11 20:09:34 -04:00
Jeremy Long
6bd7d6b078 version 1.4.2 2016-07-31 08:01:47 -04:00
Jeremy Long
84c6dd5dfa resolved gradle issue 14 - https://github.com/jeremylong/dependency-check-gradle/issues/14 2016-07-31 07:34:09 -04:00
Jeremy Long
71e7412f15 corrected example 2016-07-31 07:32:30 -04:00
Jeremy Long
d22c920b35 version 1.4.1 2016-07-30 06:52:48 -04:00
Jeremy Long
f7a0982ca0 checkstyle corrections 2016-07-29 06:12:40 -04:00
Jeremy Long
bed04150e1 reverted H2 upgrade due to issues with Jenkins and Java 6 compatability 2016-07-27 06:23:56 -04:00
Jeremy Long
ba15de2218 improved error handling 2016-07-27 06:04:56 -04:00
Jeremy Long
e9ec89dc9c improved error handling 2016-07-27 06:04:08 -04:00
Jeremy Long
d09f75658c minor formating correction 2016-07-24 08:47:27 -04:00
Jeremy Long
62f92db181 added issue template 2016-07-24 08:44:09 -04:00
Jeremy Long
27a98f4244 checckstyle corrections 2016-07-24 08:12:57 -04:00
Jeremy Long
f0a3482eda findbugs correction 2016-07-24 08:07:39 -04:00
Jeremy Long
5f76843c4a findbugs correction 2016-07-24 08:06:54 -04:00
Jeremy Long
c6ea92cff9 added links to the SBT plugin 2016-07-24 07:33:28 -04:00
Jeremy Long
c253308284 checkstyle corrections 2016-07-23 07:45:48 -04:00
Jeremy Long
9ae9c111e3 checkstyle corrections 2016-07-23 07:13:09 -04:00
Jeremy Long
4894372eee minor code quality issues corrected 2016-07-23 06:50:11 -04:00
Jeremy Long
7cf040653f upgraded h2 db version 2016-07-22 06:29:01 -04:00
Jeremy Long
034bd4dba0 testing fix to resolve connection issues with NVD 2016-07-19 07:04:24 -04:00
Jeremy Long
af12a2161c testing fix to resolve connection issues with NVD 2016-07-19 06:54:25 -04:00
Jeremy Long
57fcf6fde3 testing connection errors 2016-07-17 08:18:47 -04:00
Jeremy Long
c5757dc5f4 updates to resolve issue #215 2016-07-17 07:19:56 -04:00
Jeremy Long
6d5d5ceb7b Updated exception handling so that issue #215 can be resolved 2016-07-14 06:31:54 -04:00
bjiang
2fa8507d69 merge owasp 1.4.1 2016-07-12 16:22:05 -04:00
Jeremy Long
f23003ead3 fields can be final 2016-07-10 07:13:08 -04:00
Jeremy Long
c996f6b436 improved exception handling as part of resolution for #215 2016-07-10 07:12:43 -04:00
Jeremy Long
d2ee66a1c4 there was no need to extend IOException 2016-07-10 07:11:03 -04:00
Jeremy Long
26b0dd5ef5 updated javadoc 2016-07-10 06:56:26 -04:00
Jeremy Long
ad4149a259 updated documentation for PR #528 2016-07-10 06:27:40 -04:00
Jeremy Long
9611c3b478 Merge pull request #528 from felfert/master
Thanks for the PR!
2016-07-10 06:13:09 -04:00
Jeremy Long
cead88d221 reworked initialization exceptions as part of planned resolution for issue #215 2016-07-09 07:39:00 -04:00
Jeremy Long
c1e1a6bb4f cleaned up imports 2016-07-09 07:35:36 -04:00
Fritz Elfert
6212a5f740 Compatibility fixes for MariaDB JDBC driver 2016-07-08 22:27:10 +02:00
Jeremy Long
b3d9ea3c47 minor code reorg 2016-07-07 06:18:54 -04:00
Jeremy Long
cd51989354 Merge pull request #526 from nicolastrres/master
Updating gradle dependencyCheck documentation
2016-07-07 06:05:12 -04:00
nicolastrres
b705ae5f0c Updating gradle dependencyCheck documentation 2016-07-06 14:57:24 -03:00
Jeremy Long
13b53537fa incorrectly set quick query value during recheck - see issue #523 2016-07-06 06:48:10 -04:00
Jeremy Long
7d05aa6073 added logging for issue #523 2016-07-06 06:44:43 -04:00
Jeremy Long
85de173086 fixed StackOverflowError from issue #523 2016-07-06 06:32:57 -04:00
Jeremy Long
d264d804c8 patches and test case update for issue #522 2016-07-05 09:09:58 -04:00
Jeremy Long
8272da615e improved test cases to debug issue #522 2016-07-04 08:43:43 -04:00
Jeremy Long
857b993d51 ensured analyzers were correctly initialized and closed 2016-07-04 07:55:53 -04:00
Jeremy Long
a71edf584e additional testing added 2016-07-04 07:55:19 -04:00
Jeremy Long
461d7fec0e fixed typo 2016-07-04 07:54:57 -04:00
Jeremy Long
5e3da035dd resolved merge conflict with #525 2016-07-04 07:11:45 -04:00
Jeremy Long
ebb52995a5 converted hint analyzer to use an externalized configuration file to simplify the resolution of issue #522 2016-07-04 07:10:07 -04:00
Jeremy Long
519b82c620 minor cleanup of code/comments 2016-07-04 07:07:07 -04:00
Jeremy Long
84682d07c6 converted hint analyzer to use an externalized configuration file to simplify the resolution of issue #522 2016-07-04 07:06:17 -04:00
Jeremy Long
960eeb19af converted hint analyzer to use an externalized configuration file to simplify the resolution of issue #522 2016-07-04 07:05:31 -04:00
Hans Joachim Desserud
ab3920f8f1 Replace raw Iterator with for each 2016-07-02 16:29:32 +02:00
Hans Joachim Desserud
f5f5857897 Add missing @Overrides 2016-07-02 16:23:24 +02:00
Hans Joachim Desserud
1c400b410e Remove unused imports 2016-07-02 16:23:16 +02:00
Jeremy Long
cc751aa224 updated to skip custom scripts in executable scripts 2016-06-27 19:39:17 -04:00
Jeremy Long
c20892ee3e removed stack traces from build 2016-06-27 08:46:46 -04:00
Jeremy Long
32ab53c9e1 Merge branch 'master' of github.com:jeremylong/DependencyCheck 2016-06-26 07:32:23 -04:00
Jeremy Long
d0a7d9eb42 added test for issue #454 to ensure fully executable jar 2016-06-26 07:32:10 -04:00
Jeremy Long
a1a9602509 added support for fully executable jar files per issue #454 2016-06-26 07:31:17 -04:00
Jeremy Long
cf97c89fe0 fully exectuable jar for testing resolution for issue #454 2016-06-26 07:30:39 -04:00
Jeremy Long
8895bc85ea Merge pull request #521 from awhitford/Upg20160624
Plugin and Dependency Upgrades
2016-06-25 06:53:49 -04:00
Anthony Whitford
1a9976c6ca commons-compress 1.12, maven-jar-plugin 3.0.2, maven-source-plugin 3.0.1, maven-javadoc-plugin 2.10.4. 2016-06-24 23:51:12 -07:00
Anthony Whitford
f47ebf6145 jMockit 1.24, jSoup 1.9.2. 2016-06-24 23:35:24 -07:00
Jeremy Long
0380715311 resolved issue #514 2016-06-24 07:09:10 -04:00
Jeremy Long
80ad16c7fa updated to correctly label groovy's dependency scope 2016-06-22 06:48:11 -04:00
Jeremy Long
e56e9035b6 updated to correctly label groovy's dependency scope 2016-06-22 06:38:10 -04:00
Jeremy Long
73f22d32d2 fixed typo 2016-06-22 06:34:50 -04:00
Jeremy Long
c3bc56eebc additional suppressions 2016-06-18 07:33:30 -04:00
Jeremy Long
35cc14815e added property to solve issue #500 2016-06-18 07:32:57 -04:00
Jeremy Long
9be91474f6 staging 2016-06-18 06:46:28 -04:00
Jeremy Long
adf949bf08 added logging of URL 2016-06-18 06:18:16 -04:00
Jeremy Long
c6bf41b8ba staging 2016-06-18 06:17:55 -04:00
Jeremy Long
bc656c6218 version 1.4.0 2016-06-16 06:49:24 -04:00
Jeremy Long
f46226d055 updated documentation 2016-06-16 06:49:07 -04:00
bjiang
00d4ee47de merge upstream 2016-06-15 13:54:49 -04:00
Jeremy Long
c5ffc21660 Merge branch 'master' of github.com:jeremylong/DependencyCheck 2016-06-15 06:51:18 -04:00
Jeremy Long
d89b1fdc6a updated proxy configuration information 2016-06-15 06:51:05 -04:00
Jeremy Long
8324287bd6 updated proxy configuration information 2016-06-15 06:50:45 -04:00
Jeremy Long
6be161a546 updated experimental documentation 2016-06-15 06:19:06 -04:00
Jeremy Long
027350e1ba Merge pull request #516 from msrb/weightings-bug
Correctly apply weightings when searching for CPEs
2016-06-15 05:48:55 -04:00
Michal Srb
a2309e1c2e Correctly apply weightings when searching for CPEs 2016-06-14 21:34:04 +02:00
Jeremy Long
c34dc97bd4 updated snapshot version 2016-06-11 08:13:14 -04:00
Jeremy Long
7e8749146e updated documentation 2016-06-11 08:12:09 -04:00
Jeremy Long
8680ecd033 updated documentation for experimental analyzers 2016-06-08 06:40:07 -04:00
Jeremy Long
4e4417c7af checkstyle corrections 2016-06-06 18:45:39 -04:00
Jeremy Long
7909bbbbe9 corrected remaining merge conflicts that were missed earlier as they were in comments 2016-06-06 06:45:33 -04:00
Jeremy Long
6fd831e688 corrected JavaDoc links 2016-06-06 06:44:42 -04:00
Jeremy Long
59a4825c70 added license 2016-06-05 17:40:42 -04:00
Jeremy Long
1ba3681457 updated the ci 2016-06-05 17:32:57 -04:00
Jeremy Long
78becffb2e updated CI build status url 2016-06-05 17:29:29 -04:00
Jeremy Long
e7efd7070b Merge pull request #508 from albuch/h2-clean-orphans
Thanks for the PR!
2016-06-05 17:25:53 -04:00
Jeremy Long
ec6471e8c7 added notes for future enhancment 2016-06-05 17:17:38 -04:00
Jeremy Long
b01ae2c6d3 updated to speed-up the unit test 2016-06-05 17:16:43 -04:00
Jeremy Long
ef4a260615 fixed build issue with CveDB being closed before saving the property 2016-06-05 17:16:05 -04:00
Jeremy Long
f6b80630dd temporary travis debugging code 2016-06-05 08:30:22 -04:00
Jeremy Long
f43589589d fixed setup to call super 2016-06-05 08:04:45 -04:00
Jeremy Long
06b59cf79b initial 2016-06-05 07:49:59 -04:00
Jeremy Long
a2187205e0 only update last checked after updates were performed without errors 2016-06-05 06:45:13 -04:00
Jeremy Long
52f269a289 Merge branch 'jabbrwcky-batch-update' 2016-06-05 06:33:45 -04:00
Jeremy Long
310ca967a1 fixed compareTo in order to resolve issue #503 2016-06-05 06:32:49 -04:00
Jeremy Long
c4b423cb0f additional tests resources to fix issue #503 2016-06-05 06:32:11 -04:00
Alexander v. Buchholtz
8a6c940aaf Optimized CLEANUP_ORPHANS query for H2 1.4.x
Original query from dbStatements.properties writes millions of records from subselect to file system due to MAX_MEMORY_ROWS Setting http://www.h2database.com/html/grammar.html?highlight=max_memory_rows&search=MAX_MEM#set_max_memory_rows
Database maintenance task therefore takes forever.
The new query (copied from postgresql) works way faster.
2016-06-04 23:36:43 +02:00
Jeremy Long
b295e927b7 resolved merge conflict 2016-06-04 09:09:57 -04:00
Jeremy Long
63d24737dd Merge pull request #506 from jabbrwcky/issue-503
Thanks for the test cases
2016-06-04 07:47:44 -04:00
Jeremy Long
60ce02ba28 improved logging to assist in resoloving issue #503 2016-06-04 07:46:42 -04:00
Jeremy Long
95939ed66c added javadoc per checkstyle 2016-06-04 07:45:07 -04:00
Jeremy Long
7f609a35be added javadoc per checkstyle 2016-06-04 07:44:42 -04:00
Jeremy Long
f7b534f1ee checkstyle correction 2016-06-04 07:44:08 -04:00
Jeremy Long
cd5f9e2f13 findbugs correction 2016-06-04 07:42:58 -04:00
Jens Hausherr
e79da72711 Use batch update for references and vulnerable software if supported by DB. 2016-06-03 10:22:54 +02:00
Jens Hausherr
1ba081959b Accidentially dropped some imports 2016-06-03 10:09:28 +02:00
Jens Hausherr
578dc63652 Vulnerable Software: Compact toString() output; remove accessor calls for own properties 2016-06-03 09:54:25 +02:00
Jens Hausherr
fccd683b50 add toString() for Vulnerability 2016-06-03 09:52:35 +02:00
Jens Hausherr
f3d3a25856 Add more test cases 2016-06-03 09:50:28 +02:00
Jens Hausherr
6d70c92795 Add to String-Method to Reference 2016-06-03 09:41:48 +02:00
Jeremy Long
3c525d8e3a fixed issue #505 2016-06-02 19:30:38 -04:00
Jeremy Long
a6b47c7c43 clarified note 2016-06-02 19:23:51 -04:00
Jeremy Long
5b52f01f3d updated documentation for issue#498 2016-05-30 08:23:58 -04:00
Jeremy Long
d13bbd43f3 added experimental flag to force users to enable this and by doing so understand that these may not be as production ready as the Java analyzer (see issue #498) 2016-05-30 08:09:14 -04:00
Jeremy Long
0394d1a24f checkstyle correction - reduced method length 2016-05-30 07:59:53 -04:00
Jeremy Long
446222e127 removed unnecessary exclude 2016-05-30 07:59:18 -04:00
Jeremy Long
05d7aa898d minor reformatting to reduce line length (checkstyle) 2016-05-30 07:37:44 -04:00
Jeremy Long
73f7fc1d51 Merge branch 'master' of github.com:jeremylong/DependencyCheck 2016-05-30 07:19:15 -04:00
Jeremy Long
f0262466d4 Merge pull request #504 from awhitford/Upg052816
Plugin updates
2016-05-30 07:19:17 -04:00
Anthony Whitford
1ecde9bbc1 maven-jar-plugin 3.0.0 released; maven-resources-plugin 3.0.0 released. 2016-05-28 08:27:39 -07:00
Jens Hausherr
ae5a766092 Limit split to fix #503 2016-05-27 15:07:59 +02:00
Jeremy Long
6a807bc002 checkstyle/findbugs corrections 2016-05-25 17:21:46 -04:00
Jeremy Long
c0384bb0ee Merge pull request #502 from xthk/master
Update initialize_mysql.sql
2016-05-25 06:12:52 -04:00
Tilmann H
2906b315b3 Update initialize_mysql.sql
lower cased "properties" in UPDATE statement
2016-05-25 11:36:09 +02:00
Jeremy Long
425fd65bd8 added more false positive suppressions 2016-05-21 07:09:08 -04:00
Jeremy Long
7d83362a85 removed stack trace from build when ruby and bundle-audit are not installed 2016-05-15 07:49:17 -04:00
Jeremy Long
0b26894112 checkstyle/pmd/findbugs correction(s) 2016-05-15 07:48:26 -04:00
Jeremy Long
17f810a720 implement issue #498 2016-05-15 07:30:38 -04:00
Jeremy Long
71ef8061f9 merge conflict resolved 2016-05-15 07:29:17 -04:00
Jeremy Long
353b17690f checkstyle/pmd/findbugs correction(s) 2016-05-15 07:22:52 -04:00
Jeremy Long
6790727260 ensured resources are properly closed 2016-05-15 07:02:18 -04:00
Jeremy Long
e129f7db85 Merge branch 'biancajiang-ruby_dependency' 2016-05-15 06:46:18 -04:00
Jeremy Long
ea942398e3 updated test case to use the correct parent class that allows for use of the database during testing 2016-05-15 06:45:57 -04:00
Jeremy Long
5ad72cae3f Merge branch 'ruby_dependency' of git://github.com/biancajiang/DependencyCheck into biancajiang-ruby_dependency 2016-05-14 09:45:26 -04:00
Jeremy Long
5f945bc696 Merge branch 'master' of github.com:jeremylong/DependencyCheck 2016-05-14 07:21:07 -04:00
Jeremy Long
6f451736ba Add ability to flag analyzers as experimental so that they are not always enabled 2016-05-14 07:20:53 -04:00
Jeremy Long
30856f4a4f corrected doxia version 2016-05-14 07:19:12 -04:00
bjiang
413c71eb0a Merge branch 'ruby_dependency' into swift_support 2016-05-13 13:49:40 -04:00
bjiang
9d1408be20 do not use actual path for packagePath from compress case 2016-05-13 13:33:34 -04:00
Jeremy Long
f21f371751 Merge pull request #494 from erikerikson/master
Align documentation with current project name specification flag
2016-05-06 18:23:32 -04:00
bjiang
2b761279e4 Merge branch 'ruby_dependency' into swift_support 2016-05-06 17:59:28 -04:00
bjiang
d5e8f54214 fix RubyBundlerAnalyzer.accept 2016-05-06 17:55:21 -04:00
bjiang
83f83d4eee add RubyBundlerAnalyzerTest 2016-05-06 17:45:40 -04:00
bjiang
b0f4ab9ba5 cleanup & Rakefile support placeholder 2016-05-06 17:25:08 -04:00
bjiang
06dad8f79c javadoc 2016-05-06 17:22:39 -04:00
bjiang
83ab122ddf disable RubyBundlerAnalyzer if RubyBundleAuditAnalyzer 2016-05-06 16:42:39 -04:00
bjiang
8a42fe4ae1 javadoc 2016-05-06 16:19:59 -04:00
bjiang
94c6778b89 better javadoc 2016-05-06 16:14:16 -04:00
bjiang
c0e5973517 rename RubyBundlerAnalyzer and javadoc 2016-05-06 15:50:35 -04:00
bjiang
1e7bbfa7c1 bundle the same SWIFT package by different analyzers 2016-05-06 13:43:05 -04:00
bjiang
dc7245ff6e code cleanup 2016-05-06 12:55:59 -04:00
bjiang
ffaf7b40e9 merge from ruby_dependency 2016-05-06 10:31:17 -04:00
bjiang
4de3fb1f2a javadoc 2016-05-06 10:25:49 -04:00
bjiang
99355d993a code cleanup with more comments 2016-05-06 10:24:28 -04:00
bjiang
d25f6e813c new analyzer for Package.swift 2016-05-05 19:21:21 -04:00
bjiang
043f8e0523 cleanup 2016-05-03 15:45:08 -04:00
bjiang
5fcf2a2623 get authors field 2016-05-03 14:53:25 -04:00
Erik Erikson
ee77fccffd Align documentation with current project name specification flag
When using the "--app" flag, the following warning is produced:

 [WARN] The 'app' argument should no longer be used; use 'project' instead.

 This change updates the documentation from suggesting "--app" to "--project"
2016-05-03 10:31:00 -07:00
bjiang
f1422adf75 merge upstream 2016-05-03 13:12:05 -04:00
bjiang
189da08885 merge upstream 2016-05-03 13:05:56 -04:00
bjiang
c2b1742582 support cocoapods for swift 2016-05-03 12:41:39 -04:00
Jeremy Long
9e63ac6d5b Merge pull request #493 from awhitford/CommIO25
Commons-IO 2.5 upgrade
2016-05-02 19:26:52 -04:00
Jeremy Long
4d7ab8b187 Merge pull request #491 from mwieczorek/MSSQL_Support
MSSQL Support
2016-05-02 19:25:39 -04:00
Jeremy Long
4de9818bee original CVE used in test does not exist in the current default DB used for tests. 2016-05-01 20:16:30 -04:00
Jeremy Long
7a2e1fd221 updated bundle audit score to be more accurate 2016-05-01 15:39:12 -04:00
Jeremy Long
d0ca800a23 Merge branch 'geramirez-fix-cvss-for-bundle-audit' 2016-04-30 11:20:39 -04:00
Jeremy Long
35ffd56ea9 fixed compile issues in PR 2016-04-30 11:20:26 -04:00
Jeremy Long
84b992d3a1 Merge branch 'fix-cvss-for-bundle-audit' of git://github.com/geramirez/DependencyCheck into geramirez-fix-cvss-for-bundle-audit 2016-04-30 11:02:16 -04:00
Jeremy Long
9e46364759 updated test cases to track down build issue 2016-04-30 10:56:50 -04:00
Dave Goddard
0f37c2b59c Adding sinatra fixture
Signed-off-by: Gabriel Ramirez <gabriel.e.ramirez@gmail.com>
2016-04-29 16:17:51 -04:00
Michal Wieczorek
33852ea7e3 MSSQL Support 2016-04-27 23:35:05 +02:00
Anthony Whitford
4fbed1cdac Added Charset to avoid deprecated FileUtils methods. 2016-04-27 01:37:00 -07:00
Anthony Whitford
42c61ab457 commons-io 2.5 released; jsoup 1.9.1 released. 2016-04-27 01:22:20 -07:00
David Jahn
8c6b9f9c68 Fixed CVSS for Ruby.
this bug was discovered when scanning ruby applications and getting back
`-1` cvss. this turns out to be a problem with bundle-audit cve
database.

Our solution was to use the NVD database, which dependency check uses to
get the CVSS scores for Ruby only if the Criticality is missing from
bundle-audit output. Keep in mind there are compilation errors with the
commit atm.

Fixes #485

Signed-off-by: Gabriel Ramirez <gabriel.e.ramirez@gmail.com>
2016-04-25 09:40:54 -04:00
Jeremy Long
abebecac4a updated parser and tests to revert to old suppression schema if new schema fails 2016-04-24 09:06:00 -04:00
Jeremy Long
87efe429da fixed broken schema 2016-04-24 09:05:26 -04:00
Jeremy Long
35128b0bd4 updated 2016-04-24 09:04:22 -04:00
Jeremy Long
186cb2270f ensure updated schema is published to the site 2016-04-24 07:25:32 -04:00
Jeremy Long
deda02f879 updated suppression schema to require a CPE, CVE, or CVSS Below per issue #488 2016-04-24 07:20:11 -04:00
Jeremy Long
bcc2478ef7 snapshot version 2016-04-24 07:17:42 -04:00
Jeremy Long
8d54654482 Merge pull request #487 from awhitford/DepUpg160416
Upgraded plugins and dependencies
2016-04-17 21:02:54 -04:00
Jeremy Long
08318107c1 Merge pull request #486 from awhitford/MavenWarnings
Maven warnings
2016-04-17 21:02:35 -04:00
Anthony Whitford
a5e77c85a6 Maven Site Plugin 3.5.1, Doxia 1.7.1, Ant 1.9.7, Maven 3.3.9. 2016-04-16 11:21:24 -07:00
Anthony Whitford
1e8d2aff75 Added code to avoid an unchecked cast warning. 2016-04-16 11:08:13 -07:00
Anthony Whitford
bc0a0f9902 Added missing serialVersionUID. 2016-04-16 11:07:19 -07:00
bjiang
da82f975e4 Add test for project url from pom.xml 2016-04-15 12:30:14 -04:00
bjiang
48af120db8 add project URL evidence from pom 2016-04-15 11:28:33 -04:00
Jeremy Long
8722eae766 version 1.3.6 2016-04-10 07:06:07 -04:00
Jeremy Long
53776936ca fix FP per issue #469 2016-04-09 11:27:08 -04:00
Jeremy Long
dca465b801 fixed minor warning about file encoding during build 2016-04-09 07:31:40 -04:00
Jeremy Long
43cd115dc7 Merge pull request #482 from awhitford/DepUpg-160406
Dependency Updates
2016-04-09 06:59:29 -04:00
Jeremy Long
e7ba08e52c updated log message to assist in debugging an issue 2016-04-09 06:51:00 -04:00
Jeremy Long
9df12e6ff2 updated log message to assist in debugging an issue 2016-04-09 06:49:44 -04:00
Jeremy Long
b5c7fb747c updated log message to assist in debugging an issue 2016-04-09 06:38:37 -04:00
Anthony Whitford
a40a4afe80 SLF4J 1.7.21 released; commons-compress 1.11 released. 2016-04-06 21:39:27 -07:00
bjiang
739f595f13 improve python package identification 2016-04-05 16:12:14 -04:00
bjiang
e07e892969 Merge branch 'master' into ruby_dependency 2016-04-05 14:47:17 -04:00
Jeremy Long
d4a6c58cc8 upgrade the transitive dependency commons-collections 2016-04-05 12:08:16 -04:00
Jeremy Long
d644431a4e Merge pull request #479 from awhitford/SLF4J1720LB117
SLF4J 1.7.20 and Logback 1.1.7 released.
2016-04-03 07:41:54 -04:00
bjiang
33bbb50b43 Ruby .gemspec must before bundler analyzer to get proper package grouping 2016-04-02 13:51:15 -04:00
bjiang
f89d7df305 improve vendor evidences for .gemspec analyzer 2016-04-02 13:49:50 -04:00
bjiang
3b02cd0e39 bundling same Ruby packages from .gemspec and bundler analyzers 2016-04-02 13:48:25 -04:00
bjiang
52cd50e0a8 keep delimitor space in array value 2016-04-01 14:08:41 -04:00
bjiang
996a970081 fix version evidence name 2016-04-01 14:07:03 -04:00
bjiang
6c0b65acd4 capture licenses and homepage in Ruby bundler analyzer 2016-04-01 13:30:36 -04:00
Anthony Whitford
f4df263dfe SLF4J 1.7.20 and Logback 1.1.7 released. 2016-03-30 21:03:51 -07:00
bjiang
8c659acc82 new Ruby bundler analyzer 2016-03-30 20:20:10 -04:00
bjiang
7aba2429af merge from upstream 2016-03-28 14:23:09 -04:00
bjiang
ab48d2c2ff multiple improvements 2016-03-28 14:06:30 -04:00
Jeremy Long
0b699d45bf Merge pull request #467 from colezlaw/python-init
Patch for jeremylong/DependencyCheck/#466
2016-03-25 19:35:06 -04:00
Jeremy Long
54beafa262 Merge pull request #475 from biancajiang/master
Fix test to skip the proper test case when bundle-audit is not available
2016-03-25 19:34:34 -04:00
Jeremy Long
531d4923eb Merge pull request #470 from MrBerg/suppress-osvdb
Make it possible to suppress vulnerabilities from OSVDB
2016-03-25 19:33:43 -04:00
Jeremy Long
b160a4d1dd Merge pull request #478 from swapnilsm/master
Added primary key to "software" table
2016-03-25 19:32:45 -04:00
Swapnil S. Mahajan
ca54daf456 Added primary key to "software" table
"software" is a bridge table so there should always be only one record for a pair of cpeEntryId and cveid.
2016-03-25 16:55:53 +05:30
bjiang
a22fc550b3 #472 fix test to only skip the proper test case. 2016-03-21 11:38:52 -04:00
Jeremy Long
0650d93953 Merge pull request #474 from awhitford/SLF4J1719
SLF4J 1.7.19 released.
2016-03-21 08:18:41 -04:00
Jeremy Long
5633258fa7 Update README.md 2016-03-21 08:16:06 -04:00
Jeremy Long
12278cda58 Update README.md
Fixed broken link to documentation.
2016-03-21 08:12:39 -04:00
Jeremy Long
84d1f08fda updated documentation for NVD urls to match what is hosted by NIST 2016-03-21 07:58:02 -04:00
Jeremy Long
c184292a57 Merge pull request #473 from biancajiang/master
Handle bundle-audit not available case and fix RubyBundleAuditAnalyzer test cases
2016-03-21 07:52:17 -04:00
Anthony Whitford
4cdfcb9f9d SLF4J 1.7.19 released. 2016-03-20 20:47:07 -04:00
bjiang
343a78917c Fixed #472. Disable RubyBundleAuditAnalyzer if exception during initialize.
changes:
1. disable self during initialize before bubbling exception
2. new test case RubyBundleAuditAnalyzerTest#testMissingBundleAudit()
2016-03-20 17:06:03 -04:00
bjiang
ff7d0fdb9d #472 first fix and improve RubyBundleAuditAnalyzerTest.java
Test were failing b/c Gemfile.lock and Gemfile were missing.
The files were missing b/c parent .gitignore them.
Changes:
1. Force added new test files, and updated test with more result
validation.
2. Added error logging from bundle-audit.
3. place holder for bundle-audit install directory in test
dependencycheck.properties.
2016-03-20 15:54:24 -04:00
Jonas Berg
db26b46be0 Make it possible to suppress vulnerabilities from OSVDB 2016-03-16 13:59:23 +02:00
Will Stranathan
d77a70c360 Patch for jeremylong/DependencyCheck/#466
This does two things:
1) Updates the PythonPackageAnalyzer to HIGH evidence for __init__.py
2) Removes evidence from the FileNameAnalyzer for __init__.py[co]?

TODO: Need for the PythonPackageAnalyzer to still add evidence for
__init__.py[co] even though it won't be able to analyze the contents of
it. Also, need to work up the tree for __init__.py files to get the
parent folders (not sure why subfolders are not being inspected).
2016-03-12 15:09:43 -05:00
Jeremy Long
42f4ae65d1 Merge pull request #463 from chadjvw/master
Updated Oracle init script
2016-03-07 20:43:05 -05:00
Chad Van Wyhe
88daac31d2 Merge pull request #1 from chadjvw/oracle-init-fix
fixed trigger compilation and added version number
2016-03-07 12:59:02 -06:00
Chad Van Wyhe
ac04c173a8 fixed trigger compilation and added version number 2016-03-07 12:55:18 -06:00
Jeremy Long
8401494fbc Merge pull request #462 from thc202/issues-page-gradle-purge
Fix issues in Gradle's dependencyCheckPurge task site page
2016-03-06 18:54:03 -05:00
Jeremy Long
97af118cb9 Merge pull request #461 from thc202/broken-link-readme
Fix broken link in README.md file... Thanks!
2016-03-06 18:53:12 -05:00
thc202
091e6026bc Fix issues in Gradle's dependencyCheckPurge task site page
Fix broken link to dependencyCheckUpdate task page, remove repeated
closing character ']'.
Replace $H with # in the heading of the example.
2016-03-06 23:46:12 +00:00
thc202
c798ede7bf Fix broken link in README.md file
Correct the link to NOTICE.txt file, change from NOTICES.txt to
NOTICE.txt.
2016-03-06 23:46:04 +00:00
Jeremy Long
225851f067 Merge pull request #460 from awhitford/DepUpg160306
Dependency Upgrades
2016-03-06 18:01:03 -05:00
Jeremy Long
9dd65ecf70 Merge pull request #459 from awhitford/MPIR29
maven-project-info-reports-plugin 2.9 released.
2016-03-06 18:00:48 -05:00
Jeremy Long
1a9cc4b6be snapshot 2016-03-06 17:42:18 -05:00
Jeremy Long
a612f206bf version 1.3.5.1 2016-03-06 17:30:37 -05:00
Jeremy Long
e51031c62a fix bug in getLastProject for non-site executions 2016-03-06 17:28:40 -05:00
Anthony Whitford
e30c29ef50 SLF4J 1.7.18 released; Logback 1.1.6 released; jMockit 1.22 released. 2016-03-06 08:53:58 -08:00
Anthony Whitford
91ddcadbcd Removed maven-site-plugin from dependencyManagement. 2016-03-06 08:51:04 -08:00
Anthony Whitford
8c145860e5 maven-project-info-reports-plugin 2.9 released. 2016-03-06 08:35:09 -08:00
Jeremy Long
a19dd7687e v 1.3.6-SNAPSHOT 2016-03-05 16:13:29 -05:00
Jeremy Long
550d6ca083 v1.3.5 2016-03-05 16:08:59 -05:00
Jeremy Long
b425411357 doclint fixes 2016-03-05 13:18:42 -05:00
Jeremy Long
a1f0cf749d doclint fixes 2016-03-05 13:18:38 -05:00
Jeremy Long
22e0d1c74e doclint fixes 2016-03-05 13:18:37 -05:00
Jeremy Long
cdc07047aa doclint fixes 2016-03-05 13:18:37 -05:00
Jeremy Long
c832c2da28 doclint fixes 2016-03-05 13:18:37 -05:00
Jeremy Long
8daa713639 doclint fixes 2016-03-05 13:18:36 -05:00
Jeremy Long
e0a2966706 doclint fixes 2016-03-05 13:18:36 -05:00
Jeremy Long
354bfa14f9 doclint fixes 2016-03-05 13:18:35 -05:00
Jeremy Long
46b91702ba doclint fixes 2016-03-05 13:18:35 -05:00
Jeremy Long
de9516e368 doclint fixes 2016-03-05 13:18:35 -05:00
Jeremy Long
3924e07e5c doclint fixes 2016-03-05 13:18:34 -05:00
Jeremy Long
76bcbb5a7e doclint fixes 2016-03-05 13:18:34 -05:00
Jeremy Long
8022381d1c doclint fixes 2016-03-05 13:18:33 -05:00
Jeremy Long
feb1233081 doclint fixes 2016-03-05 13:18:33 -05:00
Jeremy Long
36eefd0836 doclint fixes 2016-03-05 13:18:32 -05:00
Jeremy Long
0e31e59759 doclint fixes 2016-03-05 13:18:32 -05:00
Jeremy Long
4a4c1e75da doclint fixes 2016-03-05 13:18:32 -05:00
Jeremy Long
b0bfd2292a doclint fixes 2016-03-05 13:18:31 -05:00
Jeremy Long
7214b24357 doclint fixes 2016-03-05 13:18:31 -05:00
Jeremy Long
24637f496f doclint fixes 2016-03-05 13:18:30 -05:00
Jeremy Long
d8ecde5265 doclint fixes 2016-03-05 13:18:30 -05:00
Jeremy Long
28840c6209 doclint fixes 2016-03-05 13:18:29 -05:00
Jeremy Long
1696213406 doclint fixes 2016-03-05 13:18:29 -05:00
Jeremy Long
6f315ac765 doclint fixes 2016-03-05 13:18:28 -05:00
Jeremy Long
a485307d92 doclint fixes 2016-03-05 13:18:28 -05:00
Jeremy Long
3d3b861ba0 doclint fixes 2016-03-05 13:18:28 -05:00
Jeremy Long
4b33ed25d5 doclint fixes 2016-03-05 13:18:27 -05:00
Jeremy Long
e264880c7b doclint fixes 2016-03-05 13:18:27 -05:00
Jeremy Long
ef8212701f doclint fixes 2016-03-05 13:18:26 -05:00
Jeremy Long
492157a502 doclint fixes 2016-03-05 13:18:26 -05:00
Jeremy Long
2605bc182e doclint fixes 2016-03-05 13:18:25 -05:00
Jeremy Long
fe8dfdd804 doclint fixes 2016-03-05 13:18:25 -05:00
Jeremy Long
bd917bc990 doclint fixes 2016-03-05 13:18:24 -05:00
Jeremy Long
c5c32f683f doclint fixes 2016-03-05 13:18:24 -05:00
Jeremy Long
5506e58c98 doclint fixes 2016-03-05 13:18:23 -05:00
Jeremy Long
5af2d49b18 doclint fixes 2016-03-05 13:18:23 -05:00
Jeremy Long
0fd35a4925 doclint fixes 2016-03-05 13:18:23 -05:00
Jeremy Long
7ed20b1244 doclint fixes 2016-03-05 13:18:22 -05:00
Jeremy Long
efa6a78255 doclint fixes 2016-03-05 13:18:22 -05:00
Jeremy Long
8b58df3b34 checkstyle/pmd/findbugs corrections 2016-03-05 07:07:53 -05:00
Jeremy Long
0d2a090e1f Merge pull request #456 from awhitford/Site35
Upgrade for Maven Site Plugin 3.5
2016-03-04 17:42:14 -05:00
Jeremy Long
7860d635a9 ensured deserialization is secure 2016-03-04 17:38:48 -05:00
Anthony Whitford
ba91c9fa9b Upgraded maven site plugin to 3.5, and doxia markdown module to 1.7. 2016-02-28 09:34:19 -08:00
Anthony Whitford
b3630e0d5e Upgraded the Fluido 1.5 skin and had to update site head for maven site plugin 3.5. See http://maven.apache.org/plugins/maven-site-plugin/examples/sitedescriptor.html#Inject_xhtml_into_head 2016-02-28 09:33:54 -08:00
Jeremy Long
f752285912 added test for parse manifest per issue #455 2016-02-27 07:14:27 -05:00
Jeremy Long
5a150d9b0e parsed additional entries in the manifest per issue #455 2016-02-27 07:13:57 -05:00
Jeremy Long
f0aa185832 added test dependency per issue #455 2016-02-27 07:12:30 -05:00
Jeremy Long
9592f058d4 add more false positives to the suppression list 2016-02-25 18:01:21 -05:00
Jeremy Long
f630794e22 added warning about site:stage with regards to the aggregate goal 2016-02-24 17:00:31 -05:00
Jeremy Long
93636e89c5 fixed broken hyperlinks 2016-02-23 20:54:24 -05:00
Jeremy Long
585002c25c resolution for issue #386 fixed the conditional so that execution occured on the last non-skipped project in the reactor 2016-02-23 20:42:10 -05:00
Jeremy Long
412ccc1be1 per issue #429 updates will only occur if the database schema and expected schema match exactly 2016-02-21 08:38:29 -05:00
Jeremy Long
8b1306a36c per issue #429 non-h2 databases may be used as long as the database schema is of the same major version and greater then or equal to the expected version. 2016-02-21 08:11:29 -05:00
Jeremy Long
81026e8dca isolate the analyze method to try and resolve multiple threads hitting the Lucene query parsers at the same time per issue #388 2016-02-20 08:18:00 -05:00
Jeremy Long
dd440c8f9f resolve issue #451 2016-02-20 08:12:14 -05:00
Jeremy Long
76f3e4b27e Merge pull request #449 from christiangalsterer/i444
Support nonProxyHosts parameter in settings.xml #444
2016-02-17 19:09:41 -05:00
Jeremy Long
5f5d3fdb66 Merge pull request #447 from kaimago/master
Oracle DB Support
2016-02-17 19:06:13 -05:00
Jeremy Long
853c92b87d Merge pull request #448 from awhitford/UpgFeb6-16
Upgraded SLF4J to 1.7.14 and the maven-compiler-plugin to 3.5.
2016-02-17 19:03:37 -05:00
Anthony Whitford
00080f2abc SLF4J 1.7.16 released; logback 1.1.5 released. 2016-02-15 10:23:07 -08:00
Anthony Whitford
55414208a3 SLF4J 1.7.15 released; maven-compiler-plugin 3.5.1 released. 2016-02-10 00:34:26 -08:00
Christian Galsterer
5091499563 [i444] Support nonProxyHosts parameter in settings.xml 2016-02-09 18:01:36 +01:00
Anthony Whitford
944b54d920 Upgraded SLF4J to 1.7.14 and the maven-compiler-plugin to 3.5. 2016-02-06 12:14:19 -08:00
Christian Galsterer
d023b2b2ff [i444] Support nonProxyHosts parameter in settings.xml 2016-02-06 16:13:01 +01:00
Jeremy Long
b45f9f514b base test case handles settings initialization 2016-02-06 08:40:33 -05:00
Jeremy Long
239a9383e0 fix for issue #446 2016-02-06 08:30:06 -05:00
Jeremy Long
2190c0229c added check to see if the file is xml prior to unzipping it per issue #441 2016-02-06 08:11:24 -05:00
I003306
01ef14dc92 Oracle DB Support 2016-02-04 15:23:57 +01:00
Jeremy Long
7b0784843c updated copyright 2016-01-31 17:23:13 -05:00
Jeremy Long
6fc805369e snapshot version 2016-01-31 17:11:37 -05:00
Jeremy Long
9e29939cd3 version 1.3.4 2016-01-31 16:50:34 -05:00
Jeremy Long
d750abca22 resolved issue with new databases not being created correctly if there was an intial download of the NVD data. 2016-01-31 08:26:23 -05:00
Jeremy Long
31df2fa131 findbugs/checkstyle corrections 2016-01-30 08:57:40 -05:00
Jeremy Long
6355a29a7a updated version to ensure there are no issues in the jenkins plugin per issue #445 2016-01-30 08:07:33 -05:00
Jeremy Long
86a2b38340 Merge pull request #440 from awhitford/DepUpg20160110
Upgrades
2016-01-24 08:40:51 -05:00
Jeremy Long
9cb2b58557 initial fix for issue #445 2016-01-24 08:35:44 -05:00
Jeremy Long
2b0e2e8d0d corrected link per issuue #443 2016-01-17 08:08:15 -05:00
Jeremy Long
cf46767196 resolution for issue #439 2016-01-16 07:39:48 -05:00
Anthony Whitford
ffc1034b5a findbugs-maven-plugin 3.0.3 released; JavaMail api 1.5.5 released; jMockit 1.21 released. 2016-01-10 10:01:33 -08:00
Jeremy Long
46bb19de9b supression rules to resolve issues #437 and #438 2016-01-10 07:45:29 -05:00
Jeremy Long
70bc7a6d01 Merge pull request #435 from awhitford/Surefire-2.19.1
Upgraded Surefire to 2.19.1 release.
2016-01-10 07:01:08 -05:00
Anthony Whitford
3164505273 Upgraded Surefire to 2.19.1 release. 2016-01-05 08:12:32 -08:00
Jeremy Long
3d84fcd037 resolves issue #433 2016-01-03 09:18:35 -05:00
Jeremy Long
578fa32243 updated to honor noupdate flag for version check and removed some complexity 2016-01-03 09:14:08 -05:00
Jeremy Long
fc00b7d1cc resolves issues #426 2016-01-03 08:51:03 -05:00
Jeremy Long
d7351bd3e5 Merge pull request #432 from awhitford/CodeTweaks20151228
Code tweaks 2015-12-28
2016-01-03 08:33:16 -05:00
Anthony Whitford
e7224c8f05 StringBuilder allocation more precise. 2015-12-31 09:25:44 -08:00
Anthony Whitford
b97622f45b Variables may be final. 2015-12-28 13:15:24 -08:00
Anthony Whitford
0e15f3b703 Add missing final keyword to local variables. 2015-12-28 13:14:31 -08:00
Anthony Whitford
6604c0da89 Default StringBuilder size should be larger than default 16. 2015-12-28 13:14:04 -08:00
Anthony Whitford
e0b8be20b3 Variable suppressionRules may be final. 2015-12-28 13:13:33 -08:00
Anthony Whitford
46965d8c96 Iterable does not need qualifying, and collection may be final. 2015-12-28 13:13:00 -08:00
Anthony Whitford
66e92f00ee Variable may be final. 2015-12-28 13:12:10 -08:00
Anthony Whitford
4a137b4e8e Use StringBuilder instead of String += concatenation. 2015-12-28 13:11:36 -08:00
Anthony Whitford
9d5ff28098 Variables can be final and the exception was unused so can be removed. 2015-12-28 13:10:37 -08:00
Anthony Whitford
313b114da5 Variables can be final. 2015-12-28 13:09:17 -08:00
Anthony Whitford
1b6bfc6338 Variables can be final. 2015-12-28 13:08:37 -08:00
Anthony Whitford
49fd89f34a Let's use logging parameters for lazy evaluation. 2015-12-28 13:07:34 -08:00
Anthony Whitford
a2e862886e Rather than an explicit StringBuilder, why not simply an implicit one? 2015-12-28 13:06:44 -08:00
Anthony Whitford
62f6c7c5a9 Rather than using instanceOf, just add a specific catch for AnalysisException. 2015-12-28 13:05:45 -08:00
Jeremy Long
2294ed1ce1 Merge pull request #430 from awhitford/PluginUpdates20151227
Plugin updates
2015-12-28 06:12:54 -05:00
Jeremy Long
c8a1c6a318 fixed issue #431 - missing dependency 2015-12-28 06:11:57 -05:00
Anthony Whitford
600ed66d5b maven-clean-plugin 3.0.0 released; maven-source-plugin 2.4 released; maven-javadoc-plugin 2.10.3 released. 2015-12-27 11:45:52 -08:00
Jeremy Long
512b17555c updated documentation for encrypted passwords per issue #417 2015-12-26 12:55:41 -05:00
Jeremy Long
dc7849c9e8 added support for encrypted passwords per issue #417 2015-12-26 07:13:40 -05:00
Jeremy Long
6a99a51b91 Merge pull request #425 from awhitford/PluginUpdate20151220
maven-pmd-plugin 3.6 released.
2015-12-25 07:07:22 -05:00
Anthony Whitford
8c7fa022a0 maven-pmd-plugin 3.6 released. 2015-12-20 10:10:55 -08:00
Jeremy Long
cca694a580 logs from issue #138 indicate multiple modules are calling a non-threadsafe operation; as such, the mojo is being marked as threadSafe=false 2015-12-18 06:36:14 -05:00
Jeremy Long
3a7f95b9b1 spelling correction 2015-12-18 06:28:11 -05:00
Jeremy Long
3a84dc3962 fixed casing per issue #418 2015-12-18 06:05:01 -05:00
Jeremy Long
5961a96a4c Merge pull request #424 from amandel/patch-1
Fix casing of properties table name.
2015-12-18 05:55:04 -05:00
Jeremy Long
a22382505f Merge pull request #420 from awhitford/Issue419
Issue #419 - Avoiding a duplicate CPE Index Created message
2015-12-18 05:51:26 -05:00
Jeremy Long
5faef75415 Merge pull request #422 from edgedalmacio/patch-1
added tomcat suppressions
2015-12-18 05:49:16 -05:00
Jeremy Long
fed60907dc snapshot version 2015-12-18 05:48:37 -05:00
Andreas Mandel
ce7e360b70 If casing of properties table name. 2015-12-17 14:06:16 +01:00
Edge Dalmacio
0b3def38b8 added tomcat suppressions
tomcat-embed-el
tomcat-jdbc
tomcat-juli
2015-12-17 15:27:17 +08:00
Anthony Whitford
25a15dea8c Issue #419 - Avoiding a duplicate CPE Index Created message and resource leak. 2015-12-14 00:52:48 -08:00
Jeremy Long
e204971a6c version 1.3.3 2015-12-10 19:44:38 -05:00
Jeremy Long
d5b3a118bc minor site tweaks 2015-12-10 19:44:26 -05:00
Jeremy Long
3396cb2887 fix for issue #416 2015-12-10 18:33:31 -05:00
Jeremy Long
3c5beea218 1.3.3-SNAPSHOT 2015-12-02 09:17:28 -05:00
Jeremy Long
e544384dd5 1.3.3-SNAPSHOT 2015-12-02 05:46:28 -05:00
Jeremy Long
0e90f460f4 reverted change, using the undocumented SERIALIZED file lock mode 2015-12-02 05:46:06 -05:00
Jeremy Long
921efc4d2b updated documentation 2015-11-30 06:50:15 -05:00
247 changed files with 9823 additions and 2935 deletions

18
.github/issue_template.md vendored Normal file
View File

@@ -0,0 +1,18 @@
### Reporting Bugs/Errors
When reporting errors, 99% of the time log file output is required. Please post the log file as a [gist](https://gist.github.com/) and provide a link in the new issue.
### Reporting False Positives
When reporting a false positive please include:
- The location of the dependency (Maven GAV, URL to download the dependency, etc.)
- The CPE that is believed to be false positive
- Please report the CPE not the CVE
#### Example
False positive on library foo.jar - reported as cpe:/a:apache:tomcat:7.0
```xml
<dependency>
<groupId>org.sample</groupId>
<artifactId>foo</artifactId>
<version>1.0</version>
</dependency>
```

2
.gitignore vendored
View File

@@ -26,3 +26,5 @@ _site/**
.LCKpom.xml~ .LCKpom.xml~
#coverity #coverity
/cov-int/ /cov-int/
/dependency-check-core/nbproject/
cov-scan.bat

2
.travis.yml Normal file
View File

@@ -0,0 +1,2 @@
language: java
jdk: oraclejdk7

View File

@@ -1,4 +1,5 @@
[![Build Status](https://dependency-check.ci.cloudbees.com/buildStatus/icon?job=dependency-check)](https://dependency-check.ci.cloudbees.com/job/dependency-check/) [![Build Status](https://travis-ci.org/jeremylong/DependencyCheck.svg?branch=master)](https://travis-ci.org/jeremylong/DependencyCheck) [![Apache 2.0 License](https://img.shields.io/badge/license-Apache%202-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0.txt) [![Coverity Scan Build Status](https://scan.coverity.com/projects/1654/badge.svg)](https://scan.coverity.com/projects/dependencycheck)
Dependency-Check Dependency-Check
================ ================
@@ -22,18 +23,18 @@ The latest CLI can be downloaded from bintray's
On *nix On *nix
``` ```
$ ./bin/dependency-check.sh -h $ ./bin/dependency-check.sh -h
$ ./bin/dependency-check.sh --app Testing --out . --scan [path to jar files to be scanned] $ ./bin/dependency-check.sh --project Testing --out . --scan [path to jar files to be scanned]
``` ```
On Windows On Windows
``` ```
> bin/dependency-check.bat -h > bin/dependency-check.bat -h
> bin/dependency-check.bat --app Testing --out . --scan [path to jar files to be scanned] > bin/dependency-check.bat --project Testing --out . --scan [path to jar files to be scanned]
``` ```
On Mac with [Homebrew](http://brew.sh) On Mac with [Homebrew](http://brew.sh)
``` ```
$ brew update && brew install dependency-check $ brew update && brew install dependency-check
$ dependency-check -h $ dependency-check -h
$ dependency-check --app Testing --out . --scan [path to jar files to be scanned] $ dependency-check --project Testing --out . --scan [path to jar files to be scanned]
``` ```
### Maven Plugin ### Maven Plugin
@@ -85,13 +86,13 @@ On *nix
``` ```
$ mvn install $ mvn install
$ ./dependency-check-cli/target/release/bin/dependency-check.sh -h $ ./dependency-check-cli/target/release/bin/dependency-check.sh -h
$ ./dependency-check-cli/target/release/bin/dependency-check.sh --app Testing --out . --scan ./src/test/resources $ ./dependency-check-cli/target/release/bin/dependency-check.sh --project Testing --out . --scan ./src/test/resources
``` ```
On Windows On Windows
``` ```
> mvn install > mvn install
> dependency-check-cli/target/release/bin/dependency-check.bat -h > dependency-check-cli/target/release/bin/dependency-check.bat -h
> dependency-check-cli/target/release/bin/dependency-check.bat --app Testing --out . --scan ./src/test/resources > dependency-check-cli/target/release/bin/dependency-check.bat --project Testing --out . --scan ./src/test/resources
``` ```
Then load the resulting 'DependencyCheck-Report.html' into your favorite browser. Then load the resulting 'DependencyCheck-Report.html' into your favorite browser.
@@ -108,7 +109,7 @@ Archive: [google group](https://groups.google.com/forum/#!forum/dependency-check
Copyright & License Copyright & License
- -
Dependency-Check is Copyright (c) 2012-2015 Jeremy Long. All Rights Reserved. Dependency-Check is Copyright (c) 2012-2016 Jeremy Long. All Rights Reserved.
Permission to modify and redistribute is granted under the terms of the Apache 2.0 license. See the [LICENSE.txt](https://raw.githubusercontent.com/jeremylong/DependencyCheck/master/LICENSE.txt) file for the full license. Permission to modify and redistribute is granted under the terms of the Apache 2.0 license. See the [LICENSE.txt](https://raw.githubusercontent.com/jeremylong/DependencyCheck/master/LICENSE.txt) file for the full license.
@@ -118,4 +119,4 @@ Dependency-Check makes use of several other open source libraries. Please see th
[wiki]: https://github.com/jeremylong/DependencyCheck/wiki [wiki]: https://github.com/jeremylong/DependencyCheck/wiki
[subscribe]: mailto:dependency-check+subscribe@googlegroups.com [subscribe]: mailto:dependency-check+subscribe@googlegroups.com
[post]: mailto:dependency-check@googlegroups.com [post]: mailto:dependency-check@googlegroups.com
[notices]: https://github.com/jeremylong/DependencyCheck/blob/master/NOTICES.txt [notices]: https://github.com/jeremylong/DependencyCheck/blob/master/NOTICE.txt

View File

@@ -1,134 +1,25 @@
Dependency-Check-Gradle Dependency-Check Ant Task
========= =========
**Working in progress** Dependency-Check Ant Task can be used to check the project dependencies for published security vulnerabilities. The checks
performed are a "best effort" and as such, there could be false positives as well as false negatives. However,
vulnerabilities in 3rd party components is a well-known problem and is currently documented in the 2013 OWASP
Top 10 as [A9 - Using Components with Known Vulnerabilities](https://www.owasp.org/index.php/Top_10_2013-A9-Using_Components_with_Known_Vulnerabilities).
This is a DependencyCheck gradle plugin designed for project which use Gradle as build script. Documentation and links to production binary releases can be found on the [github pages](http://jeremylong.github.io/DependencyCheck/dependency-check-ant/index.html).
Dependency-Check is a utility that attempts to detect publicly disclosed vulnerabilities contained within project dependencies. It does this by determining if there is a Common Platform Enumeration (CPE) identifier for a given dependency. If found, it will generate a report linking to the associated CVE entries. Mailing List
------------
========= Subscribe: [dependency-check+subscribe@googlegroups.com](mailto:dependency-check+subscribe@googlegroups.com)
## What's New Post: [dependency-check@googlegroups.com](mailto:dependency-check@googlegroups.com)
Current latest version is `0.0.8`
## Usage Copyright & License
-------------------
### Step 1, Apply dependency check gradle plugin Dependency-Check is Copyright (c) 2012-2014 Jeremy Long. All Rights Reserved.
Install from Maven central repo Permission to modify and redistribute is granted under the terms of the Apache 2.0 license. See the [LICENSE.txt](https://raw.githubusercontent.com/jeremylong/DependencyCheck/master/LICENSE.txt) file for the full license.
```groovy Dependency-Check-Ant makes use of other open source libraries. Please see the [NOTICE.txt](https://raw.githubusercontent.com/jeremylong/DependencyCheck/master/dependency-check-ant/NOTICE.txt) file for more information.
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.owasp:dependency-check-gradle:1.3.2'
}
}
apply plugin: 'dependency-check-gradle'
```
### Step 2, Run gradle task
Once gradle plugin applied, run following gradle task to check dependencies:
```
gradle dependencyCheck --info
```
The reports will be generated automatically under `./reports` folder.
If your project includes multiple sub-projects, the report will be generated for each sub-project in different sub-directory.
## FAQ
> **Questions List:**
> - What if I'm behind a proxy?
> - What if my project includes multiple sub-project? How can I use this plugin for each of them including the root project?
> - How to customize the report directory?
### What if I'm behind a proxy?
Maybe you have to use proxy to access internet, in this case, you could configure proxy settings for this plugin:
```groovy
dependencyCheck {
proxy {
server = "127.0.0.1" // required, the server name or IP address of the proxy
port = 3128 // required, the port number of the proxy
// optional, the proxy server might require username
// username = "username"
// optional, the proxy server might require password
// password = "password"
}
}
```
In addition, if the proxy only allow HTTP `GET` or `POST` methods, you will find that the update process will always fail,
the root cause is that every time you run `dependencyCheck` task, it will try to query the latest timestamp to determine whether need to perform an update action,
and for performance reason the HTTP method it uses by default is `HEAD`, which probably is disabled or not supported by the proxy. To avoid this problem, you can simply change the HTTP method by below configuration:
```groovy
dependencyCheck {
quickQueryTimestamp = false // when set to false, it means use HTTP GET method to query timestamp. (default value is true)
}
```
### What if my project includes multiple sub-project? How can I use this plugin for each of them including the root project?
Try put 'apply plugin: "dependency-check"' inside the 'allprojects' or 'subprojects' if you'd like to check all sub-projects only, see below:
(1) For all projects including root project:
```groovy
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "gradle.plugin.com.tools.security:dependency-check:0.0.8"
}
}
allprojects {
apply plugin: "dependency-check"
}
```
(2) For all sub-projects:
```groovy
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "gradle.plugin.com.tools.security:dependency-check:0.0.8"
}
}
subprojects {
apply plugin: "dependency-check"
}
```
In this way, the dependency check will be executed for all projects (including root project) or just sub projects.
### How to customize the report directory?
By default, all reports will be placed under `./reports` folder, to change the default directory, just modify it in the configuration section like this:
```groovy
subprojects {
apply plugin: "dependency-check"
dependencyCheck {
outputDirectory = "./customized-path/security-report"
}
}
```

View File

@@ -20,7 +20,7 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved.
<parent> <parent>
<groupId>org.owasp</groupId> <groupId>org.owasp</groupId>
<artifactId>dependency-check-parent</artifactId> <artifactId>dependency-check-parent</artifactId>
<version>1.3.2</version> <version>1.4.3</version>
</parent> </parent>
<artifactId>dependency-check-ant</artifactId> <artifactId>dependency-check-ant</artifactId>
@@ -256,6 +256,7 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved.
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<configuration> <configuration>
<argLine>-Dfile.encoding=UTF-8</argLine>
<systemProperties> <systemProperties>
<property> <property>
<name>data.directory</name> <name>data.directory</name>

View File

@@ -18,7 +18,6 @@
package org.owasp.dependencycheck.taskdefs; package org.owasp.dependencycheck.taskdefs;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.util.List; import java.util.List;
import org.apache.tools.ant.BuildException; import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project; import org.apache.tools.ant.Project;
@@ -32,9 +31,12 @@ import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.data.nvdcve.CveDB; import org.owasp.dependencycheck.data.nvdcve.CveDB;
import org.owasp.dependencycheck.data.nvdcve.DatabaseException; import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties; import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
import org.owasp.dependencycheck.data.update.exception.UpdateException;
import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.dependency.Identifier; import org.owasp.dependencycheck.dependency.Identifier;
import org.owasp.dependencycheck.dependency.Vulnerability; import org.owasp.dependencycheck.dependency.Vulnerability;
import org.owasp.dependencycheck.exception.ExceptionCollection;
import org.owasp.dependencycheck.exception.ReportException;
import org.owasp.dependencycheck.reporting.ReportGenerator; import org.owasp.dependencycheck.reporting.ReportGenerator;
import org.owasp.dependencycheck.reporting.ReportGenerator.Format; import org.owasp.dependencycheck.reporting.ReportGenerator.Format;
import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.Settings;
@@ -86,8 +88,8 @@ public class Check extends Update {
} }
/** /**
* Returns the path. If the path has not been initialized yet, this class is synchronized, and will instantiate the path * Returns the path. If the path has not been initialized yet, this class is
* object. * synchronized, and will instantiate the path object.
* *
* @return the path * @return the path
*/ */
@@ -109,7 +111,8 @@ public class Check extends Update {
} }
/** /**
* Add a reference to a Path, FileSet, DirSet, or FileList defined elsewhere. * Add a reference to a Path, FileSet, DirSet, or FileList defined
* elsewhere.
* *
* @param r the reference to a path, fileset, dirset or filelist. * @param r the reference to a path, fileset, dirset or filelist.
*/ */
@@ -121,7 +124,8 @@ public class Check extends Update {
} }
/** /**
* If this is a reference, this method will add the referenced resource collection to the collection of paths. * If this is a reference, this method will add the referenced resource
* collection to the collection of paths.
* *
* @throws BuildException if the reference is not to a resource collection * @throws BuildException if the reference is not to a resource collection
*/ */
@@ -196,7 +200,8 @@ public class Check extends Update {
} }
/** /**
* Specifies the destination directory for the generated Dependency-Check report. * Specifies the destination directory for the generated Dependency-Check
* report.
*/ */
private String reportOutputDirectory = "."; private String reportOutputDirectory = ".";
@@ -218,9 +223,11 @@ public class Check extends Update {
this.reportOutputDirectory = reportOutputDirectory; this.reportOutputDirectory = reportOutputDirectory;
} }
/** /**
* Specifies if the build should be failed if a CVSS score above a specified level is identified. The default is 11 which * Specifies if the build should be failed if a CVSS score above a specified
* means since the CVSS scores are 0-10, by default the build will never fail and the CVSS score is set to 11. The valid range * level is identified. The default is 11 which means since the CVSS scores
* for the fail build on CVSS is 0 to 11, where anything above 10 will not cause the build to fail. * are 0-10, by default the build will never fail and the CVSS score is set
* to 11. The valid range for the fail build on CVSS is 0 to 11, where
* anything above 10 will not cause the build to fail.
*/ */
private float failBuildOnCVSS = 11; private float failBuildOnCVSS = 11;
@@ -242,8 +249,8 @@ public class Check extends Update {
this.failBuildOnCVSS = failBuildOnCVSS; this.failBuildOnCVSS = failBuildOnCVSS;
} }
/** /**
* Sets whether auto-updating of the NVD CVE/CPE data is enabled. It is not recommended that this be turned to false. Default * Sets whether auto-updating of the NVD CVE/CPE data is enabled. It is not
* is true. * recommended that this be turned to false. Default is true.
*/ */
private Boolean autoUpdate; private Boolean autoUpdate;
@@ -295,7 +302,8 @@ public class Check extends Update {
} }
/** /**
* The report format to be generated (HTML, XML, VULN, ALL). Default is HTML. * The report format to be generated (HTML, XML, VULN, ALL). Default is
* HTML.
*/ */
private String reportFormat = "HTML"; private String reportFormat = "HTML";
@@ -361,6 +369,29 @@ public class Check extends Update {
this.showSummary = showSummary; this.showSummary = showSummary;
} }
/**
* Whether experimental analyzers are enabled.
*/
private Boolean enableExperimental;
/**
* Get the value of enableExperimental.
*
* @return the value of enableExperimental
*/
public Boolean isEnableExperimental() {
return enableExperimental;
}
/**
* Set the value of enableExperimental.
*
* @param enableExperimental new value of enableExperimental
*/
public void setEnableExperimental(Boolean enableExperimental) {
this.enableExperimental = enableExperimental;
}
/** /**
* Whether or not the Jar Analyzer is enabled. * Whether or not the Jar Analyzer is enabled.
*/ */
@@ -621,7 +652,8 @@ public class Check extends Update {
/** /**
* Set the value of pyDistributionAnalyzerEnabled. * Set the value of pyDistributionAnalyzerEnabled.
* *
* @param pyDistributionAnalyzerEnabled new value of pyDistributionAnalyzerEnabled * @param pyDistributionAnalyzerEnabled new value of
* pyDistributionAnalyzerEnabled
*/ */
public void setPyDistributionAnalyzerEnabled(Boolean pyDistributionAnalyzerEnabled) { public void setPyDistributionAnalyzerEnabled(Boolean pyDistributionAnalyzerEnabled) {
this.pyDistributionAnalyzerEnabled = pyDistributionAnalyzerEnabled; this.pyDistributionAnalyzerEnabled = pyDistributionAnalyzerEnabled;
@@ -674,7 +706,8 @@ public class Check extends Update {
} }
/** /**
* The URL of a Nexus server's REST API end point (http://domain/nexus/service/local). * The URL of a Nexus server's REST API end point
* (http://domain/nexus/service/local).
*/ */
private String nexusUrl; private String nexusUrl;
@@ -719,8 +752,8 @@ public class Check extends Update {
} }
/** /**
* Additional ZIP File extensions to add analyze. This should be a comma-separated list of file extensions to treat like ZIP * Additional ZIP File extensions to add analyze. This should be a
* files. * comma-separated list of file extensions to treat like ZIP files.
*/ */
private String zipExtensions; private String zipExtensions;
@@ -775,52 +808,67 @@ public class Check extends Update {
engine = new Engine(Check.class.getClassLoader()); engine = new Engine(Check.class.getClassLoader());
if (isUpdateOnly()) { if (isUpdateOnly()) {
log("Deprecated 'UpdateOnly' property set; please use the UpdateTask instead", Project.MSG_WARN); log("Deprecated 'UpdateOnly' property set; please use the UpdateTask instead", Project.MSG_WARN);
engine.doUpdates();
} else {
try { try {
for (Resource resource : path) { engine.doUpdates();
final FileProvider provider = resource.as(FileProvider.class); } catch (UpdateException ex) {
if (provider != null) { if (this.isFailOnError()) {
final File file = provider.getFile(); throw new BuildException(ex);
if (file != null && file.exists()) { }
engine.scan(file); log(ex.getMessage(), Project.MSG_ERR);
} }
} else {
for (Resource resource : path) {
final FileProvider provider = resource.as(FileProvider.class);
if (provider != null) {
final File file = provider.getFile();
if (file != null && file.exists()) {
engine.scan(file);
} }
} }
}
try {
engine.analyzeDependencies(); engine.analyzeDependencies();
DatabaseProperties prop = null; } catch (ExceptionCollection ex) {
CveDB cve = null; if (this.isFailOnError()) {
try { throw new BuildException(ex);
cve = new CveDB();
cve.open();
prop = cve.getDatabaseProperties();
} catch (DatabaseException ex) {
log("Unable to retrieve DB Properties", ex, Project.MSG_DEBUG);
} finally {
if (cve != null) {
cve.close();
}
} }
final ReportGenerator reporter = new ReportGenerator(getProjectName(), engine.getDependencies(), engine.getAnalyzers(), prop); }
reporter.generateReports(reportOutputDirectory, reportFormat); DatabaseProperties prop = null;
CveDB cve = null;
try {
cve = new CveDB();
cve.open();
prop = cve.getDatabaseProperties();
} catch (DatabaseException ex) {
log("Unable to retrieve DB Properties", ex, Project.MSG_DEBUG);
} finally {
if (cve != null) {
cve.close();
}
}
final ReportGenerator reporter = new ReportGenerator(getProjectName(), engine.getDependencies(), engine.getAnalyzers(), prop);
reporter.generateReports(reportOutputDirectory, reportFormat);
if (this.failBuildOnCVSS <= 10) { if (this.failBuildOnCVSS <= 10) {
checkForFailure(engine.getDependencies()); checkForFailure(engine.getDependencies());
} }
if (this.showSummary) { if (this.showSummary) {
showSummary(engine.getDependencies()); showSummary(engine.getDependencies());
}
} catch (IOException ex) {
log("Unable to generate dependency-check report", ex, Project.MSG_DEBUG);
throw new BuildException("Unable to generate dependency-check report", ex);
} catch (Exception ex) {
log("An exception occurred; unable to continue task", ex, Project.MSG_DEBUG);
throw new BuildException("An exception occurred; unable to continue task", ex);
} }
} }
} catch (DatabaseException ex) { } catch (DatabaseException ex) {
log("Unable to connect to the dependency-check database; analysis has stopped", ex, Project.MSG_ERR); final String msg = "Unable to connect to the dependency-check database; analysis has stopped";
if (this.isFailOnError()) {
throw new BuildException(msg, ex);
}
log(msg, ex, Project.MSG_ERR);
} catch (ReportException ex) {
final String msg = "Unable to generate the dependency-check report";
if (this.isFailOnError()) {
throw new BuildException(msg, ex);
}
log(msg, ex, Project.MSG_ERR);
} finally { } finally {
Settings.cleanup(true); Settings.cleanup(true);
if (engine != null) { if (engine != null) {
@@ -830,7 +878,8 @@ public class Check extends Update {
} }
/** /**
* Validate the configuration to ensure the parameters have been properly configured/initialized. * Validate the configuration to ensure the parameters have been properly
* configured/initialized.
* *
* @throws BuildException if the task was not configured correctly. * @throws BuildException if the task was not configured correctly.
*/ */
@@ -844,8 +893,9 @@ public class Check extends Update {
} }
/** /**
* Takes the properties supplied and updates the dependency-check settings. Additionally, this sets the system properties * Takes the properties supplied and updates the dependency-check settings.
* required to change the proxy server, port, and connection timeout. * Additionally, this sets the system properties required to change the
* proxy server, port, and connection timeout.
* *
* @throws BuildException thrown when an invalid setting is configured. * @throws BuildException thrown when an invalid setting is configured.
*/ */
@@ -854,6 +904,7 @@ public class Check extends Update {
super.populateSettings(); super.populateSettings();
Settings.setBooleanIfNotNull(Settings.KEYS.AUTO_UPDATE, autoUpdate); Settings.setBooleanIfNotNull(Settings.KEYS.AUTO_UPDATE, autoUpdate);
Settings.setStringIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE, suppressionFile); Settings.setStringIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE, suppressionFile);
Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_EXPERIMENTAL_ENABLED, enableExperimental);
Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_JAR_ENABLED, jarAnalyzerEnabled); Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_JAR_ENABLED, jarAnalyzerEnabled);
Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED, pyDistributionAnalyzerEnabled); Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED, pyDistributionAnalyzerEnabled);
Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_PYTHON_PACKAGE_ENABLED, pyPackageAnalyzerEnabled); Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_PYTHON_PACKAGE_ENABLED, pyPackageAnalyzerEnabled);
@@ -875,11 +926,12 @@ public class Check extends Update {
} }
/** /**
* Checks to see if a vulnerability has been identified with a CVSS score that is above the threshold set in the * Checks to see if a vulnerability has been identified with a CVSS score
* configuration. * that is above the threshold set in the configuration.
* *
* @param dependencies the list of dependency objects * @param dependencies the list of dependency objects
* @throws BuildException thrown if a CVSS score is found that is higher then the threshold set * @throws BuildException thrown if a CVSS score is found that is higher
* then the threshold set
*/ */
private void checkForFailure(List<Dependency> dependencies) throws BuildException { private void checkForFailure(List<Dependency> dependencies) throws BuildException {
final StringBuilder ids = new StringBuilder(); final StringBuilder ids = new StringBuilder();
@@ -903,7 +955,8 @@ public class Check extends Update {
} }
/** /**
* Generates a warning message listing a summary of dependencies and their associated CPE and CVE entries. * Generates a warning message listing a summary of dependencies and their
* associated CPE and CVE entries.
* *
* @param dependencies a list of dependency objects * @param dependencies a list of dependency objects
*/ */
@@ -943,7 +996,8 @@ public class Check extends Update {
} }
/** /**
* An enumeration of supported report formats: "ALL", "HTML", "XML", "VULN", etc.. * An enumeration of supported report formats: "ALL", "HTML", "XML", "VULN",
* etc..
*/ */
public static class ReportFormats extends EnumeratedAttribute { public static class ReportFormats extends EnumeratedAttribute {

View File

@@ -71,6 +71,30 @@ public class Purge extends Task {
this.dataDirectory = dataDirectory; this.dataDirectory = dataDirectory;
} }
/**
* Indicates if dependency-check should fail the build if an exception
* occurs.
*/
private boolean failOnError = true;
/**
* Get the value of failOnError.
*
* @return the value of failOnError
*/
public boolean isFailOnError() {
return failOnError;
}
/**
* Set the value of failOnError.
*
* @param failOnError new value of failOnError
*/
public void setFailOnError(boolean failOnError) {
this.failOnError = failOnError;
}
@Override @Override
public void execute() throws BuildException { public void execute() throws BuildException {
populateSettings(); populateSettings();
@@ -81,30 +105,49 @@ public class Purge extends Task {
if (db.delete()) { if (db.delete()) {
log("Database file purged; local copy of the NVD has been removed", Project.MSG_INFO); log("Database file purged; local copy of the NVD has been removed", Project.MSG_INFO);
} else { } else {
log(String.format("Unable to delete '%s'; please delete the file manually", db.getAbsolutePath()), Project.MSG_ERR); final String msg = String.format("Unable to delete '%s'; please delete the file manually", db.getAbsolutePath());
if (this.failOnError) {
throw new BuildException(msg);
}
log(msg, Project.MSG_ERR);
} }
} else { } else {
log(String.format("Unable to purge database; the database file does not exists: %s", db.getAbsolutePath()), Project.MSG_ERR); final String msg = String.format("Unable to purge database; the database file does not exists: %s", db.getAbsolutePath());
if (this.failOnError) {
throw new BuildException(msg);
}
log(msg, Project.MSG_ERR);
} }
} catch (IOException ex) { } catch (IOException ex) {
log("Unable to delete the database", Project.MSG_ERR); final String msg = "Unable to delete the database";
if (this.failOnError) {
throw new BuildException(msg);
}
log(msg, Project.MSG_ERR);
} finally { } finally {
Settings.cleanup(true); Settings.cleanup(true);
} }
} }
/** /**
* Takes the properties supplied and updates the dependency-check settings. Additionally, this sets the system properties * Takes the properties supplied and updates the dependency-check settings.
* required to change the proxy server, port, and connection timeout. * Additionally, this sets the system properties required to change the
* proxy server, port, and connection timeout.
*
* @throws BuildException thrown if the properties file cannot be read.
*/ */
protected void populateSettings() { protected void populateSettings() throws BuildException {
Settings.initialize(); Settings.initialize();
InputStream taskProperties = null; InputStream taskProperties = null;
try { try {
taskProperties = this.getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILE); taskProperties = this.getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILE);
Settings.mergeProperties(taskProperties); Settings.mergeProperties(taskProperties);
} catch (IOException ex) { } catch (IOException ex) {
log("Unable to load the dependency-check ant task.properties file.", ex, Project.MSG_WARN); final String msg = "Unable to load the dependency-check ant task.properties file.";
if (this.failOnError) {
throw new BuildException(msg, ex);
}
log(msg, ex, Project.MSG_WARN);
} finally { } finally {
if (taskProperties != null) { if (taskProperties != null) {
try { try {

View File

@@ -18,14 +18,17 @@
package org.owasp.dependencycheck.taskdefs; package org.owasp.dependencycheck.taskdefs;
import org.apache.tools.ant.BuildException; import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.data.nvdcve.DatabaseException; import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
import org.owasp.dependencycheck.data.update.exception.UpdateException;
import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.Settings;
import org.slf4j.impl.StaticLoggerBinder; import org.slf4j.impl.StaticLoggerBinder;
/** /**
* An Ant task definition to execute dependency-check update. This will download the latest data from the National Vulnerability * An Ant task definition to execute dependency-check update. This will download
* Database (NVD) and store a copy in the local database. * the latest data from the National Vulnerability Database (NVD) and store a
* copy in the local database.
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
@@ -381,10 +384,11 @@ public class Update extends Purge {
} }
/** /**
* Executes the update by initializing the settings, downloads the NVD XML data, and then processes the data storing it in the * Executes the update by initializing the settings, downloads the NVD XML
* local database. * data, and then processes the data storing it in the local database.
* *
* @throws BuildException thrown if a connection to the local database cannot be made. * @throws BuildException thrown if a connection to the local database
* cannot be made.
*/ */
@Override @Override
public void execute() throws BuildException { public void execute() throws BuildException {
@@ -392,9 +396,20 @@ public class Update extends Purge {
Engine engine = null; Engine engine = null;
try { try {
engine = new Engine(Update.class.getClassLoader()); engine = new Engine(Update.class.getClassLoader());
engine.doUpdates(); try {
engine.doUpdates();
} catch (UpdateException ex) {
if (this.isFailOnError()) {
throw new BuildException(ex);
}
log(ex.getMessage(), Project.MSG_ERR);
}
} catch (DatabaseException ex) { } catch (DatabaseException ex) {
throw new BuildException("Unable to connect to the dependency-check database; unable to update the NVD data", ex); final String msg = "Unable to connect to the dependency-check database; unable to update the NVD data";
if (this.isFailOnError()) {
throw new BuildException(msg, ex);
}
log(msg, Project.MSG_ERR);
} finally { } finally {
Settings.cleanup(true); Settings.cleanup(true);
if (engine != null) { if (engine != null) {
@@ -404,8 +419,9 @@ public class Update extends Purge {
} }
/** /**
* Takes the properties supplied and updates the dependency-check settings. Additionally, this sets the system properties * Takes the properties supplied and updates the dependency-check settings.
* required to change the proxy server, port, and connection timeout. * Additionally, this sets the system properties required to change the
* proxy server, port, and connection timeout.
* *
* @throws BuildException thrown when an invalid setting is configured. * @throws BuildException thrown when an invalid setting is configured.
*/ */

View File

@@ -23,16 +23,18 @@ import org.slf4j.ILoggerFactory;
import org.slf4j.spi.LoggerFactoryBinder; import org.slf4j.spi.LoggerFactoryBinder;
/** /**
* The binding of {@link LoggerFactory} class with an actual instance of {@link ILoggerFactory} is performed using information * The binding of org.slf4j.LoggerFactory class with an actual instance of
* returned by this class. * org.slf4j.ILoggerFactory is performed using information returned by this
* class.
* *
* @author colezlaw * @author colezlaw
*/ */
//CSOFF: FinalClass
public class StaticLoggerBinder implements LoggerFactoryBinder { public class StaticLoggerBinder implements LoggerFactoryBinder {
//CSON: FinalClass
/** /**
* The unique instance of this class * The unique instance of this class
*
*/ */
private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder(); private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
@@ -46,7 +48,8 @@ public class StaticLoggerBinder implements LoggerFactoryBinder {
} }
/** /**
* Ant tasks have the log method we actually want to call. So we hang onto the task as a delegate * Ant tasks have the log method we actually want to call. So we hang onto
* the task as a delegate
*/ */
private Task task = null; private Task task = null;
@@ -61,16 +64,24 @@ public class StaticLoggerBinder implements LoggerFactoryBinder {
} }
/** /**
* Declare the version of the SLF4J API this implementation is compiled against. The value of this filed is usually modified * Declare the version of the SLF4J API this implementation is compiled
* with each release. * against. The value of this filed is usually modified with each release.
*/ */
// to avoid constant folding by the compiler, this field must *not* be final // to avoid constant folding by the compiler, this field must *not* be final
//CSOFF: StaticVariableName
//CSOFF: VisibilityModifier
public static String REQUESTED_API_VERSION = "1.7.12"; // final public static String REQUESTED_API_VERSION = "1.7.12"; // final
//CSON: VisibilityModifier
//CSON: StaticVariableName
/**
* The logger factory class string.
*/
private static final String LOGGER_FACTORY_CLASS = AntLoggerFactory.class.getName(); private static final String LOGGER_FACTORY_CLASS = AntLoggerFactory.class.getName();
/** /**
* The ILoggerFactory instance returned by the {@link #getLoggerFactory} method should always be the smae object * The ILoggerFactory instance returned by the {@link #getLoggerFactory}
* method should always be the smae object
*/ */
private ILoggerFactory loggerFactory; private ILoggerFactory loggerFactory;

View File

@@ -1,2 +1,2 @@
# the path to the data directory # the path to the data directory
data.directory=data data.directory=data/3.0

View File

@@ -14,6 +14,7 @@ Configuration: dependency-check-purge Task
-------------------- --------------------
The following properties can be set on the dependency-check-purge task. The following properties can be set on the dependency-check-purge task.
Property | Description | Default Value Property | Description | Default Value
----------------------|----------------------------------------------------------------|------------------ ----------------------|------------------------------------------------------------------------|------------------
dataDirectory | Data directory that is used to store the local copy of the NVD | data dataDirectory | Data directory that is used to store the local copy of the NVD | data
failOnError | Whether the build should fail if there is an error executing the purge | true

View File

@@ -24,6 +24,7 @@ proxyPort | The Proxy Port. | &nbsp;
proxyUsername | Defines the proxy user name. | &nbsp; proxyUsername | Defines the proxy user name. | &nbsp;
proxyPassword | Defines the proxy password. | &nbsp; proxyPassword | Defines the proxy password. | &nbsp;
connectionTimeout | The URL Connection Timeout. | &nbsp; connectionTimeout | The URL Connection Timeout. | &nbsp;
failOnError | Whether the build should fail if there is an error executing the update | true
Advanced Configuration Advanced Configuration
==================== ====================

View File

@@ -27,22 +27,24 @@ the project's dependencies.
Configuration: dependency-check Task Configuration: dependency-check Task
-------------------- --------------------
The following properties can be set on the dependency-check-update task. The following properties can be set on the dependency-check task.
Property | Description | Default Value Property | Description | Default Value
----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------- ----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------
autoUpdate | Sets whether auto-updating of the NVD CVE/CPE data is enabled. It is not recommended that this be turned to false. | true autoUpdate | Sets whether auto-updating of the NVD CVE/CPE data is enabled. It is not recommended that this be turned to false. | true
cveValidForHours | Sets the number of hours to wait before checking for new updates from the NVD | 4 cveValidForHours | Sets the number of hours to wait before checking for new updates from the NVD | 4
failBuildOnCVSS | Specifies if the build should be failed if a CVSS score above a specified level is identified. The default is 11 which means since the CVSS scores are 0-10, by default the build will never fail. | 11 failBuildOnCVSS | Specifies if the build should be failed if a CVSS score above a specified level is identified. The default is 11 which means since the CVSS scores are 0-10, by default the build will never fail. | 11
failOnError | Whether the build should fail if there is an error executing the dependency-check analysis | true
projectName | The name of the project being scanned. | Dependency-Check projectName | The name of the project being scanned. | Dependency-Check
reportFormat | The report format to be generated (HTML, XML, VULN, ALL). This configuration option has no affect if using this within the Site plugin unless the externalReport is set to true. | HTML reportFormat | The report format to be generated (HTML, XML, VULN, ALL). This configuration option has no affect if using this within the Site plugin unless the externalReport is set to true. | HTML
reportOutputDirectory | The location to write the report(s). Note, this is not used if generating the report as part of a `mvn site` build | 'target' reportOutputDirectory | The location to write the report(s). Note, this is not used if generating the report as part of a `mvn site` build | 'target'
suppressionFile | The file path to the XML suppression file \- used to suppress [false positives](../general/suppression.html) | &nbsp; suppressionFile | The file path to the XML suppression file \- used to suppress [false positives](../general/suppression.html) | &nbsp;
proxyServer | The Proxy Server. | &nbsp; proxyServer | The Proxy Server; see the [proxy configuration](../data/proxy.html) page for more information. | &nbsp;
proxyPort | The Proxy Port. | &nbsp; proxyPort | The Proxy Port. | &nbsp;
proxyUsername | Defines the proxy user name. | &nbsp; proxyUsername | Defines the proxy user name. | &nbsp;
proxyPassword | Defines the proxy password. | &nbsp; proxyPassword | Defines the proxy password. | &nbsp;
connectionTimeout | The URL Connection Timeout. | &nbsp; connectionTimeout | The URL Connection Timeout. | &nbsp;
enableExperimental | Enable the [experimental analyzers](../analyzers/index.html). If not enabled the experimental analyzers (see below) will not be loaded or used. | false
Analyzer Configuration Analyzer Configuration
==================== ====================
@@ -52,26 +54,26 @@ Note, that specific analyzers will automatically disable themselves if no file
types that they support are detected - so specifically disabling them may not types that they support are detected - so specifically disabling them may not
be needed. be needed.
Property | Description | Default Value Property | Description | Default Value
------------------------------|---------------------------------------------------------------------------|------------------ ------------------------------|-----------------------------------------------------------------------------------|------------------
archiveAnalyzerEnabled | Sets whether the Archive Analyzer will be used. | true archiveAnalyzerEnabled | Sets whether the Archive Analyzer will be used. | true
zipExtensions | A comma-separated list of additional file extensions to be treated like a ZIP file, the contents will be extracted and analyzed. | &nbsp; zipExtensions | A comma-separated list of additional file extensions to be treated like a ZIP file, the contents will be extracted and analyzed. | &nbsp;
jarAnalyzer | Sets whether the Jar Analyzer will be used. | true jarAnalyzer | Sets whether the Jar Analyzer will be used. | true
centralAnalyzerEnabled | Sets whether the Central Analyzer will be used. **Disabling this analyzer is not recommended as it could lead to false negatives (e.g. libraries that have vulnerabilities may not be reported correctly).** If this analyzer is being disabled there is a good chance you also want to disable the Nexus Analyzer (see below). | true centralAnalyzerEnabled | Sets whether the Central Analyzer will be used. **Disabling this analyzer is not recommended as it could lead to false negatives (e.g. libraries that have vulnerabilities may not be reported correctly).** If this analyzer is being disabled there is a good chance you also want to disable the Nexus Analyzer (see below). | true
nexusAnalyzerEnabled | Sets whether Nexus Analyzer will be used. This analyzer is superceded by the Central Analyzer; however, you can configure this to run against a Nexus Pro installation. | true nexusAnalyzerEnabled | Sets whether Nexus Analyzer will be used. This analyzer is superceded by the Central Analyzer; however, you can configure this to run against a Nexus Pro installation. | true
nexusUrl | Defines the Nexus web service endpoint (example http://domain.enterprise/nexus/service/local/). If not set the Nexus Analyzer will be disabled. | &nbsp; nexusUrl | Defines the Nexus web service endpoint (example http://domain.enterprise/nexus/service/local/). If not set the Nexus Analyzer will be disabled. | &nbsp;
nexusUsesProxy | Whether or not the defined proxy should be used when connecting to Nexus. | true nexusUsesProxy | Whether or not the defined proxy should be used when connecting to Nexus. | true
pyDistributionAnalyzerEnabled | Sets whether the Python Distribution Analyzer will be used. | true pyDistributionAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) Python Distribution Analyzer will be used. | true
pyPackageAnalyzerEnabled | Sets whether the Python Package Analyzer will be used. | true pyPackageAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) Python Package Analyzer will be used. | true
rubygemsAnalyzerEnabled | Sets whether the Ruby Gemspec Analyzer will be used. | true rubygemsAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) Ruby Gemspec Analyzer will be used. | true
opensslAnalyzerEnabled | Sets whether or not the openssl Analyzer should be used. | true opensslAnalyzerEnabled | Sets whether the openssl Analyzer should be used. | true
cmakeAnalyzerEnabled | Sets whether or not the CMake Analyzer should be used. | true cmakeAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) CMake Analyzer should be used. | true
autoconfAnalyzerEnabled | Sets whether or not the autoconf Analyzer should be used. | true autoconfAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) autoconf Analyzer should be used. | true
composerAnalyzerEnabled | Sets whether or not the PHP Composer Lock File Analyzer should be used. | true composerAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) PHP Composer Lock File Analyzer should be used. | true
nodeAnalyzerEnabled | Sets whether or not the Node.js Analyzer should be used. | true nodeAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) Node.js Analyzer should be used. | true
nuspecAnalyzerEnabled | Sets whether or not the .NET Nuget Nuspec Analyzer will be used. | true nuspecAnalyzerEnabled | Sets whether the .NET Nuget Nuspec Analyzer will be used. | true
assemblyAnalyzerEnabled | Sets whether or not the .NET Assembly Analyzer should be used. | true assemblyAnalyzerEnabled | Sets whether the .NET Assembly Analyzer should be used. | true
pathToMono | The path to Mono for .NET assembly analysis on non-windows systems. | &nbsp; pathToMono | The path to Mono for .NET assembly analysis on non-windows systems. | &nbsp;
Advanced Configuration Advanced Configuration
==================== ====================

View File

@@ -16,8 +16,11 @@ Installation
<property name="dependency-check.home" value="C:/tools/dependency-check-ant"/> <property name="dependency-check.home" value="C:/tools/dependency-check-ant"/>
<path id="dependency-check.path"> <path id="dependency-check.path">
<pathelement location="${dependency-check.home}/dependency-check-ant.jar"/> <pathelement location="${dependency-check.home}/dependency-check-ant.jar"/>
<fileset dir="${dependency-check.home}/lib">
<include name="*.jar"/>
</fileset>
</path> </path>
<taskdef resource="dependency-check-taskdefs.properties"> <taskdef resource="dependency-check-taskdefs.properties">
<classpath refid="dependency-check.path" /> <classpath refid="dependency-check.path" />
</taskdef> </taskdef>
``` ```

View File

@@ -27,7 +27,7 @@ Copyright (c) 2013 Jeremy Long. All Rights Reserved.
<item name="dependency-check" href="../index.html"/> <item name="dependency-check" href="../index.html"/>
</breadcrumbs> </breadcrumbs>
<menu name="Getting Started"> <menu name="Getting Started">
<item name="Installation" href="installation.html"/> <item name="Installation" href="index.html"/>
<item name="Configuration" href="configuration.html"/> <item name="Configuration" href="configuration.html"/>
</menu> </menu>
<menu ref="reports" /> <menu ref="reports" />

View File

@@ -5,7 +5,7 @@ performed are a "best effort" and as such, there could be false positives as wel
vulnerabilities in 3rd party components is a well-known problem and is currently documented in the 2013 OWASP vulnerabilities in 3rd party components is a well-known problem and is currently documented in the 2013 OWASP
Top 10 as [A9 - Using Components with Known Vulnerabilities](https://www.owasp.org/index.php/Top_10_2013-A9-Using_Components_with_Known_Vulnerabilities). Top 10 as [A9 - Using Components with Known Vulnerabilities](https://www.owasp.org/index.php/Top_10_2013-A9-Using_Components_with_Known_Vulnerabilities).
Documentation and links to production binary releases can be found on the [github pages](http://jeremylong.github.io/DependencyCheck/dependency-check-cli/installation.html). Documentation and links to production binary releases can be found on the [github pages](http://jeremylong.github.io/DependencyCheck/dependency-check-cli/index.html).
Mailing List Mailing List
------------ ------------

View File

@@ -20,7 +20,7 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved.
<parent> <parent>
<groupId>org.owasp</groupId> <groupId>org.owasp</groupId>
<artifactId>dependency-check-parent</artifactId> <artifactId>dependency-check-parent</artifactId>
<version>1.3.2</version> <version>1.4.3</version>
</parent> </parent>
<artifactId>dependency-check-cli</artifactId> <artifactId>dependency-check-cli</artifactId>
@@ -110,6 +110,7 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved.
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<configuration> <configuration>
<argLine>-Dfile.encoding=UTF-8</argLine>
<systemProperties> <systemProperties>
<property> <property>
<name>cpe</name> <name>cpe</name>

View File

@@ -27,7 +27,6 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.apache.commons.cli.ParseException; import org.apache.commons.cli.ParseException;
import org.apache.commons.lang.StringUtils;
import org.owasp.dependencycheck.data.nvdcve.CveDB; import org.owasp.dependencycheck.data.nvdcve.CveDB;
import org.owasp.dependencycheck.data.nvdcve.DatabaseException; import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties; import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
@@ -38,6 +37,10 @@ import org.owasp.dependencycheck.utils.Settings;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import ch.qos.logback.core.FileAppender; import ch.qos.logback.core.FileAppender;
import org.owasp.dependencycheck.data.update.exception.UpdateException;
import org.owasp.dependencycheck.exception.ExceptionCollection;
import org.owasp.dependencycheck.exception.ReportException;
import org.owasp.dependencycheck.utils.InvalidSettingException;
import org.slf4j.impl.StaticLoggerBinder; import org.slf4j.impl.StaticLoggerBinder;
/** /**
@@ -58,21 +61,26 @@ public class App {
* @param args the command line arguments * @param args the command line arguments
*/ */
public static void main(String[] args) { public static void main(String[] args) {
int exitCode = 0;
try { try {
Settings.initialize(); Settings.initialize();
final App app = new App(); final App app = new App();
app.run(args); exitCode = app.run(args);
LOGGER.debug("Exit code: " + exitCode);
} finally { } finally {
Settings.cleanup(true); Settings.cleanup(true);
} }
System.exit(exitCode);
} }
/** /**
* Main CLI entry-point into the application. * Main CLI entry-point into the application.
* *
* @param args the command line arguments * @param args the command line arguments
* @return the exit code to return
*/ */
public void run(String[] args) { public int run(String[] args) {
int exitCode = 0;
final CliParser cli = new CliParser(); final CliParser cli = new CliParser();
try { try {
@@ -80,11 +88,11 @@ public class App {
} catch (FileNotFoundException ex) { } catch (FileNotFoundException ex) {
System.err.println(ex.getMessage()); System.err.println(ex.getMessage());
cli.printHelp(); cli.printHelp();
return; return -1;
} catch (ParseException ex) { } catch (ParseException ex) {
System.err.println(ex.getMessage()); System.err.println(ex.getMessage());
cli.printHelp(); cli.printHelp();
return; return -2;
} }
if (cli.getVerboseLog() != null) { if (cli.getVerboseLog() != null) {
@@ -94,8 +102,15 @@ public class App {
if (cli.isPurge()) { if (cli.isPurge()) {
if (cli.getConnectionString() != null) { if (cli.getConnectionString() != null) {
LOGGER.error("Unable to purge the database when using a non-default connection string"); LOGGER.error("Unable to purge the database when using a non-default connection string");
exitCode = -3;
} else { } else {
populateSettings(cli); try {
populateSettings(cli);
} catch (InvalidSettingException ex) {
LOGGER.error(ex.getMessage());
LOGGER.debug("Error loading properties file", ex);
exitCode = -4;
}
File db; File db;
try { try {
db = new File(Settings.getDataDirectory(), "dc.h2.db"); db = new File(Settings.getDataDirectory(), "dc.h2.db");
@@ -104,46 +119,101 @@ public class App {
LOGGER.info("Database file purged; local copy of the NVD has been removed"); LOGGER.info("Database file purged; local copy of the NVD has been removed");
} else { } else {
LOGGER.error("Unable to delete '{}'; please delete the file manually", db.getAbsolutePath()); LOGGER.error("Unable to delete '{}'; please delete the file manually", db.getAbsolutePath());
exitCode = -5;
} }
} else { } else {
LOGGER.error("Unable to purge database; the database file does not exists: {}", db.getAbsolutePath()); LOGGER.error("Unable to purge database; the database file does not exists: {}", db.getAbsolutePath());
exitCode = -6;
} }
} catch (IOException ex) { } catch (IOException ex) {
LOGGER.error("Unable to delete the database"); LOGGER.error("Unable to delete the database");
exitCode = -7;
} }
} }
} else if (cli.isGetVersion()) { } else if (cli.isGetVersion()) {
cli.printVersionInfo(); cli.printVersionInfo();
} else if (cli.isUpdateOnly()) { } else if (cli.isUpdateOnly()) {
populateSettings(cli);
runUpdateOnly();
} else if (cli.isRunScan()) {
populateSettings(cli);
try { try {
runScan(cli.getReportDirectory(), cli.getReportFormat(), cli.getProjectName(), cli.getScanFiles(), populateSettings(cli);
cli.getExcludeList(), cli.getSymLinkDepth()); } catch (InvalidSettingException ex) {
LOGGER.error(ex.getMessage());
LOGGER.debug("Error loading properties file", ex);
exitCode = -4;
}
try {
runUpdateOnly();
} catch (UpdateException ex) {
LOGGER.error(ex.getMessage());
exitCode = -8;
} catch (DatabaseException ex) {
LOGGER.error(ex.getMessage());
exitCode = -9;
}
} else if (cli.isRunScan()) {
try {
populateSettings(cli);
} catch (InvalidSettingException ex) {
LOGGER.error(ex.getMessage());
LOGGER.debug("Error loading properties file", ex);
exitCode = -4;
}
try {
final String[] scanFiles = cli.getScanFiles();
if (scanFiles != null) {
runScan(cli.getReportDirectory(), cli.getReportFormat(), cli.getProjectName(), scanFiles,
cli.getExcludeList(), cli.getSymLinkDepth());
} else {
LOGGER.error("No scan files configured");
}
} catch (InvalidScanPathException ex) { } catch (InvalidScanPathException ex) {
LOGGER.error("An invalid scan path was detected; unable to scan '//*' paths"); LOGGER.error("An invalid scan path was detected; unable to scan '//*' paths");
exitCode = -10;
} catch (DatabaseException ex) {
LOGGER.error(ex.getMessage());
exitCode = -11;
} catch (ReportException ex) {
LOGGER.error(ex.getMessage());
exitCode = -12;
} catch (ExceptionCollection ex) {
if (ex.isFatal()) {
exitCode = -13;
LOGGER.error("One or more fatal errors occured");
} else {
exitCode = -14;
}
for (Throwable e : ex.getExceptions()) {
LOGGER.error(e.getMessage());
}
} }
} else { } else {
cli.printHelp(); cli.printHelp();
} }
return exitCode;
} }
/** /**
* Scans the specified directories and writes the dependency reports to the reportDirectory. * Scans the specified directories and writes the dependency reports to the
* reportDirectory.
* *
* @param reportDirectory the path to the directory where the reports will be written * @param reportDirectory the path to the directory where the reports will
* be written
* @param outputFormat the output format of the report * @param outputFormat the output format of the report
* @param applicationName the application name for the report * @param applicationName the application name for the report
* @param files the files/directories to scan * @param files the files/directories to scan
* @param excludes the patterns for files/directories to exclude * @param excludes the patterns for files/directories to exclude
* @param symLinkDepth the depth that symbolic links will be followed * @param symLinkDepth the depth that symbolic links will be followed
* *
* @throws InvalidScanPathException thrown if the path to scan starts with "//" * @throws InvalidScanPathException thrown if the path to scan starts with
* "//"
* @throws ReportException thrown when the report cannot be generated
* @throws DatabaseException thrown when there is an error connecting to the
* database
* @throws ExceptionCollection thrown when an exception occurs during
* analysis; there may be multiple exceptions contained within the
* collection.
*/ */
private void runScan(String reportDirectory, String outputFormat, String applicationName, String[] files, private void runScan(String reportDirectory, String outputFormat, String applicationName, String[] files,
String[] excludes, int symLinkDepth) throws InvalidScanPathException { String[] excludes, int symLinkDepth) throws InvalidScanPathException, DatabaseException, ExceptionCollection, ReportException {
Engine engine = null; Engine engine = null;
try { try {
engine = new Engine(); engine = new Engine();
@@ -175,8 +245,6 @@ public class App {
include = "**/*"; include = "**/*";
} }
} }
//LOGGER.debug("baseDir: {}", baseDir);
//LOGGER.debug("include: {}", include);
scanner.setBasedir(baseDir); scanner.setBasedir(baseDir);
final String[] includes = {include}; final String[] includes = {include};
scanner.setIncludes(includes); scanner.setIncludes(includes);
@@ -198,7 +266,15 @@ public class App {
} }
engine.scan(paths); engine.scan(paths);
engine.analyzeDependencies(); ExceptionCollection exCol = null;
try {
engine.analyzeDependencies();
} catch (ExceptionCollection ex) {
if (ex.isFatal()) {
throw ex;
}
exCol = ex;
}
final List<Dependency> dependencies = engine.getDependencies(); final List<Dependency> dependencies = engine.getDependencies();
DatabaseProperties prop = null; DatabaseProperties prop = null;
CveDB cve = null; CveDB cve = null;
@@ -206,8 +282,6 @@ public class App {
cve = new CveDB(); cve = new CveDB();
cve.open(); cve.open();
prop = cve.getDatabaseProperties(); prop = cve.getDatabaseProperties();
} catch (DatabaseException ex) {
LOGGER.debug("Unable to retrieve DB Properties", ex);
} finally { } finally {
if (cve != null) { if (cve != null) {
cve.close(); cve.close();
@@ -216,34 +290,37 @@ public class App {
final ReportGenerator report = new ReportGenerator(applicationName, dependencies, engine.getAnalyzers(), prop); final ReportGenerator report = new ReportGenerator(applicationName, dependencies, engine.getAnalyzers(), prop);
try { try {
report.generateReports(reportDirectory, outputFormat); report.generateReports(reportDirectory, outputFormat);
} catch (IOException ex) { } catch (ReportException ex) {
LOGGER.error("There was an IO error while attempting to generate the report."); if (exCol != null) {
LOGGER.debug("", ex); exCol.addException(ex);
} catch (Throwable ex) { throw exCol;
LOGGER.error("There was an error while attempting to generate the report."); } else {
LOGGER.debug("", ex); throw ex;
}
}
if (exCol != null && exCol.getExceptions().size() > 0) {
throw exCol;
} }
} catch (DatabaseException ex) {
LOGGER.error("Unable to connect to the dependency-check database; analysis has stopped");
LOGGER.debug("", ex);
} finally { } finally {
if (engine != null) { if (engine != null) {
engine.cleanup(); engine.cleanup();
} }
} }
} }
/** /**
* Only executes the update phase of dependency-check. * Only executes the update phase of dependency-check.
*
* @throws UpdateException thrown if there is an error updating
* @throws DatabaseException thrown if a fatal error occurred and a
* connection to the database could not be established
*/ */
private void runUpdateOnly() { private void runUpdateOnly() throws UpdateException, DatabaseException {
Engine engine = null; Engine engine = null;
try { try {
engine = new Engine(); engine = new Engine();
engine.doUpdates(); engine.doUpdates();
} catch (DatabaseException ex) {
LOGGER.error("Unable to connect to the dependency-check database; analysis has stopped");
LOGGER.debug("", ex);
} finally { } finally {
if (engine != null) { if (engine != null) {
engine.cleanup(); engine.cleanup();
@@ -254,11 +331,13 @@ public class App {
/** /**
* Updates the global Settings. * Updates the global Settings.
* *
* @param cli a reference to the CLI Parser that contains the command line arguments used to set the corresponding settings in * @param cli a reference to the CLI Parser that contains the command line
* the core engine. * arguments used to set the corresponding settings in the core engine.
*
* @throws InvalidSettingException thrown when a user defined properties
* file is unable to be loaded.
*/ */
private void populateSettings(CliParser cli) { private void populateSettings(CliParser cli) throws InvalidSettingException {
final boolean autoUpdate = cli.isAutoUpdate(); final boolean autoUpdate = cli.isAutoUpdate();
final String connectionTimeout = cli.getConnectionTimeout(); final String connectionTimeout = cli.getConnectionTimeout();
final String proxyServer = cli.getProxyServer(); final String proxyServer = cli.getProxyServer();
@@ -281,16 +360,15 @@ public class App {
final String cveBase12 = cli.getBaseCve12Url(); final String cveBase12 = cli.getBaseCve12Url();
final String cveBase20 = cli.getBaseCve20Url(); final String cveBase20 = cli.getBaseCve20Url();
final Integer cveValidForHours = cli.getCveValidForHours(); final Integer cveValidForHours = cli.getCveValidForHours();
final boolean experimentalEnabled = cli.isExperimentalEnabled();
if (propertiesFile != null) { if (propertiesFile != null) {
try { try {
Settings.mergeProperties(propertiesFile); Settings.mergeProperties(propertiesFile);
} catch (FileNotFoundException ex) { } catch (FileNotFoundException ex) {
LOGGER.error("Unable to load properties file '{}'", propertiesFile.getPath()); throw new InvalidSettingException("Unable to find properties file '" + propertiesFile.getPath() + "'", ex);
LOGGER.debug("", ex);
} catch (IOException ex) { } catch (IOException ex) {
LOGGER.error("Unable to find properties file '{}'", propertiesFile.getPath()); throw new InvalidSettingException("Error reading properties file '" + propertiesFile.getPath() + "'", ex);
LOGGER.debug("", ex);
} }
} }
// We have to wait until we've merged the properties before attempting to set whether we use // We have to wait until we've merged the properties before attempting to set whether we use
@@ -319,6 +397,7 @@ public class App {
Settings.setIntIfNotNull(Settings.KEYS.CVE_CHECK_VALID_FOR_HOURS, cveValidForHours); Settings.setIntIfNotNull(Settings.KEYS.CVE_CHECK_VALID_FOR_HOURS, cveValidForHours);
//File Type Analyzer Settings //File Type Analyzer Settings
Settings.setBoolean(Settings.KEYS.ANALYZER_EXPERIMENTAL_ENABLED, experimentalEnabled);
Settings.setBoolean(Settings.KEYS.ANALYZER_JAR_ENABLED, !cli.isJarDisabled()); Settings.setBoolean(Settings.KEYS.ANALYZER_JAR_ENABLED, !cli.isJarDisabled());
Settings.setBoolean(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, !cli.isArchiveDisabled()); Settings.setBoolean(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, !cli.isArchiveDisabled());
Settings.setBoolean(Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED, !cli.isPythonDistributionDisabled()); Settings.setBoolean(Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED, !cli.isPythonDistributionDisabled());
@@ -384,15 +463,16 @@ public class App {
} }
/** /**
* Takes a path and resolves it to be a canonical &amp; absolute path. The caveats are that this method will take an Ant style * Takes a path and resolves it to be a canonical &amp; absolute path. The
* file selector path (../someDir/**\/*.jar) and convert it to an absolute/canonical path (at least to the left of the first * * caveats are that this method will take an Ant style file selector path
* or ?). * (../someDir/**\/*.jar) and convert it to an absolute/canonical path (at
* least to the left of the first * or ?).
* *
* @param path the path to canonicalize * @param path the path to canonicalize
* @return the canonical path * @return the canonical path
*/ */
protected String ensureCanonicalPath(String path) { protected String ensureCanonicalPath(String path) {
String basePath = null; String basePath;
String wildCards = null; String wildCards = null;
final String file = path.replace('\\', '/'); final String file = path.replace('\\', '/');
if (file.contains("*") || file.contains("?")) { if (file.contains("*") || file.contains("?")) {

View File

@@ -58,7 +58,8 @@ public final class CliParser {
* Parses the arguments passed in and captures the results for later use. * Parses the arguments passed in and captures the results for later use.
* *
* @param args the command line arguments * @param args the command line arguments
* @throws FileNotFoundException is thrown when a 'file' argument does not point to a file that exists. * @throws FileNotFoundException is thrown when a 'file' argument does not
* point to a file that exists.
* @throws ParseException is thrown when a Parse Exception occurs. * @throws ParseException is thrown when a Parse Exception occurs.
*/ */
public void parse(String[] args) throws FileNotFoundException, ParseException { public void parse(String[] args) throws FileNotFoundException, ParseException {
@@ -85,9 +86,10 @@ public final class CliParser {
/** /**
* Validates that the command line arguments are valid. * Validates that the command line arguments are valid.
* *
* @throws FileNotFoundException if there is a file specified by either the SCAN or CPE command line arguments that does not * @throws FileNotFoundException if there is a file specified by either the
* exist. * SCAN or CPE command line arguments that does not exist.
* @throws ParseException is thrown if there is an exception parsing the command line. * @throws ParseException is thrown if there is an exception parsing the
* command line.
*/ */
private void validateArgs() throws FileNotFoundException, ParseException { private void validateArgs() throws FileNotFoundException, ParseException {
if (isUpdateOnly() || isRunScan()) { if (isUpdateOnly() || isRunScan()) {
@@ -141,12 +143,14 @@ public final class CliParser {
} }
/** /**
* Validates whether or not the path(s) points at a file that exists; if the path(s) does not point to an existing file a * Validates whether or not the path(s) points at a file that exists; if the
* FileNotFoundException is thrown. * path(s) does not point to an existing file a FileNotFoundException is
* thrown.
* *
* @param paths the paths to validate if they exists * @param paths the paths to validate if they exists
* @param optType the option being validated (e.g. scan, out, etc.) * @param optType the option being validated (e.g. scan, out, etc.)
* @throws FileNotFoundException is thrown if one of the paths being validated does not exist. * @throws FileNotFoundException is thrown if one of the paths being
* validated does not exist.
*/ */
private void validatePathExists(String[] paths, String optType) throws FileNotFoundException { private void validatePathExists(String[] paths, String optType) throws FileNotFoundException {
for (String path : paths) { for (String path : paths) {
@@ -155,12 +159,14 @@ public final class CliParser {
} }
/** /**
* Validates whether or not the path points at a file that exists; if the path does not point to an existing file a * Validates whether or not the path points at a file that exists; if the
* FileNotFoundException is thrown. * path does not point to an existing file a FileNotFoundException is
* thrown.
* *
* @param path the paths to validate if they exists * @param path the paths to validate if they exists
* @param argumentName the argument being validated (e.g. scan, out, etc.) * @param argumentName the argument being validated (e.g. scan, out, etc.)
* @throws FileNotFoundException is thrown if the path being validated does not exist. * @throws FileNotFoundException is thrown if the path being validated does
* not exist.
*/ */
private void validatePathExists(String path, String argumentName) throws FileNotFoundException { private void validatePathExists(String path, String argumentName) throws FileNotFoundException {
if (path == null) { if (path == null) {
@@ -181,22 +187,25 @@ public final class CliParser {
throw new FileNotFoundException(msg); throw new FileNotFoundException(msg);
} }
} }
} else { } else if (!f.exists()) {
if (!f.exists()) { isValid = false;
isValid = false; final String msg = String.format("Invalid '%s' argument: '%s'", argumentName, path);
final String msg = String.format("Invalid '%s' argument: '%s'", argumentName, path); throw new FileNotFoundException(msg);
throw new FileNotFoundException(msg);
}
} }
} else if (path.startsWith("//") || path.startsWith("\\\\")) { } else if (path.startsWith("//") || path.startsWith("\\\\")) {
isValid = false; isValid = false;
final String msg = String.format("Invalid '%s' argument: '%s'%nUnable to scan paths that start with '//'.", argumentName, path); final String msg = String.format("Invalid '%s' argument: '%s'%nUnable to scan paths that start with '//'.", argumentName, path);
throw new FileNotFoundException(msg); throw new FileNotFoundException(msg);
} else if ((path.endsWith("/*") && !path.endsWith("**/*")) || (path.endsWith("\\*") && path.endsWith("**\\*"))) {
final String msg = String.format("Possibly incorrect path '%s' from argument '%s' because it ends with a slash star; "
+ "dependency-check uses ant-style paths", path, argumentName);
LOGGER.warn(msg);
} }
} }
/** /**
* Generates an Options collection that is used to parse the command line and to display the help message. * Generates an Options collection that is used to parse the command line
* and to display the help message.
* *
* @return the command line options used for parsing the command line * @return the command line options used for parsing the command line
*/ */
@@ -272,6 +281,10 @@ public final class CliParser {
.desc("The number of hours to wait before checking for new updates from the NVD.") .desc("The number of hours to wait before checking for new updates from the NVD.")
.build(); .build();
final Option experimentalEnabled = Option.builder().longOpt(ARGUMENT.EXPERIMENTAL)
.desc("Enables the experimental analzers.")
.build();
//This is an option group because it can be specified more then once. //This is an option group because it can be specified more then once.
final OptionGroup og = new OptionGroup(); final OptionGroup og = new OptionGroup();
og.addOption(path); og.addOption(path);
@@ -292,12 +305,14 @@ public final class CliParser {
.addOption(props) .addOption(props)
.addOption(verboseLog) .addOption(verboseLog)
.addOption(suppressionFile) .addOption(suppressionFile)
.addOption(cveValidForHours); .addOption(cveValidForHours)
.addOption(experimentalEnabled);
} }
/** /**
* Adds the advanced command line options to the given options collection. These are split out for purposes of being able to * Adds the advanced command line options to the given options collection.
* display two different help messages. * These are split out for purposes of being able to display two different
* help messages.
* *
* @param options a collection of command line arguments * @param options a collection of command line arguments
* @throws IllegalArgumentException thrown if there is an exception * @throws IllegalArgumentException thrown if there is an exception
@@ -344,7 +359,7 @@ public final class CliParser {
final Option pathToMono = Option.builder().argName("path").hasArg().longOpt(ARGUMENT.PATH_TO_MONO) final Option pathToMono = Option.builder().argName("path").hasArg().longOpt(ARGUMENT.PATH_TO_MONO)
.desc("The path to Mono for .NET Assembly analysis on non-windows systems.") .desc("The path to Mono for .NET Assembly analysis on non-windows systems.")
.build(); .build();
final Option pathToBundleAudit = Option.builder().argName("path").hasArg() final Option pathToBundleAudit = Option.builder().argName("path").hasArg()
.longOpt(ARGUMENT.PATH_TO_BUNDLE_AUDIT) .longOpt(ARGUMENT.PATH_TO_BUNDLE_AUDIT)
.desc("The path to bundle-audit for Gem bundle analysis.").build(); .desc("The path to bundle-audit for Gem bundle analysis.").build();
@@ -466,8 +481,10 @@ public final class CliParser {
} }
/** /**
* Adds the deprecated command line options to the given options collection. These are split out for purposes of not including * Adds the deprecated command line options to the given options collection.
* them in the help message. We need to add the deprecated options so as not to break existing scripts. * These are split out for purposes of not including them in the help
* message. We need to add the deprecated options so as not to break
* existing scripts.
* *
* @param options a collection of command line arguments * @param options a collection of command line arguments
* @throws IllegalArgumentException thrown if there is an exception * @throws IllegalArgumentException thrown if there is an exception
@@ -514,7 +531,8 @@ public final class CliParser {
} }
/** /**
* Returns the symbolic link depth (how deeply symbolic links will be followed). * Returns the symbolic link depth (how deeply symbolic links will be
* followed).
* *
* @return the symbolic link depth * @return the symbolic link depth
*/ */
@@ -534,7 +552,8 @@ public final class CliParser {
/** /**
* Returns true if the disableJar command line argument was specified. * Returns true if the disableJar command line argument was specified.
* *
* @return true if the disableJar command line argument was specified; otherwise false * @return true if the disableJar command line argument was specified;
* otherwise false
*/ */
public boolean isJarDisabled() { public boolean isJarDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_JAR); return (line != null) && line.hasOption(ARGUMENT.DISABLE_JAR);
@@ -543,7 +562,8 @@ public final class CliParser {
/** /**
* Returns true if the disableArchive command line argument was specified. * Returns true if the disableArchive command line argument was specified.
* *
* @return true if the disableArchive command line argument was specified; otherwise false * @return true if the disableArchive command line argument was specified;
* otherwise false
*/ */
public boolean isArchiveDisabled() { public boolean isArchiveDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_ARCHIVE); return (line != null) && line.hasOption(ARGUMENT.DISABLE_ARCHIVE);
@@ -552,7 +572,8 @@ public final class CliParser {
/** /**
* Returns true if the disableNuspec command line argument was specified. * Returns true if the disableNuspec command line argument was specified.
* *
* @return true if the disableNuspec command line argument was specified; otherwise false * @return true if the disableNuspec command line argument was specified;
* otherwise false
*/ */
public boolean isNuspecDisabled() { public boolean isNuspecDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_NUSPEC); return (line != null) && line.hasOption(ARGUMENT.DISABLE_NUSPEC);
@@ -561,26 +582,29 @@ public final class CliParser {
/** /**
* Returns true if the disableAssembly command line argument was specified. * Returns true if the disableAssembly command line argument was specified.
* *
* @return true if the disableAssembly command line argument was specified; otherwise false * @return true if the disableAssembly command line argument was specified;
* otherwise false
*/ */
public boolean isAssemblyDisabled() { public boolean isAssemblyDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_ASSEMBLY); return (line != null) && line.hasOption(ARGUMENT.DISABLE_ASSEMBLY);
} }
/** /**
* Returns true if the disableBundleAudit command line argument was specified. * Returns true if the disableBundleAudit command line argument was
* specified.
* *
* @return true if the disableBundleAudit command line argument was specified; otherwise false * @return true if the disableBundleAudit command line argument was
* specified; otherwise false
*/ */
public boolean isBundleAuditDisabled() { public boolean isBundleAuditDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_BUNDLE_AUDIT); return (line != null) && line.hasOption(ARGUMENT.DISABLE_BUNDLE_AUDIT);
} }
/** /**
* Returns true if the disablePyDist command line argument was specified. * Returns true if the disablePyDist command line argument was specified.
* *
* @return true if the disablePyDist command line argument was specified; otherwise false * @return true if the disablePyDist command line argument was specified;
* otherwise false
*/ */
public boolean isPythonDistributionDisabled() { public boolean isPythonDistributionDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_PY_DIST); return (line != null) && line.hasOption(ARGUMENT.DISABLE_PY_DIST);
@@ -589,7 +613,8 @@ public final class CliParser {
/** /**
* Returns true if the disablePyPkg command line argument was specified. * Returns true if the disablePyPkg command line argument was specified.
* *
* @return true if the disablePyPkg command line argument was specified; otherwise false * @return true if the disablePyPkg command line argument was specified;
* otherwise false
*/ */
public boolean isPythonPackageDisabled() { public boolean isPythonPackageDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_PY_PKG); return (line != null) && line.hasOption(ARGUMENT.DISABLE_PY_PKG);
@@ -598,7 +623,8 @@ public final class CliParser {
/** /**
* Returns whether the Ruby gemspec analyzer is disabled. * Returns whether the Ruby gemspec analyzer is disabled.
* *
* @return true if the {@link ARGUMENT#DISABLE_RUBYGEMS} command line argument was specified; otherwise false * @return true if the {@link ARGUMENT#DISABLE_RUBYGEMS} command line
* argument was specified; otherwise false
*/ */
public boolean isRubyGemspecDisabled() { public boolean isRubyGemspecDisabled() {
return (null != line) && line.hasOption(ARGUMENT.DISABLE_RUBYGEMS); return (null != line) && line.hasOption(ARGUMENT.DISABLE_RUBYGEMS);
@@ -607,7 +633,8 @@ public final class CliParser {
/** /**
* Returns true if the disableCmake command line argument was specified. * Returns true if the disableCmake command line argument was specified.
* *
* @return true if the disableCmake command line argument was specified; otherwise false * @return true if the disableCmake command line argument was specified;
* otherwise false
*/ */
public boolean isCmakeDisabled() { public boolean isCmakeDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_CMAKE); return (line != null) && line.hasOption(ARGUMENT.DISABLE_CMAKE);
@@ -616,7 +643,8 @@ public final class CliParser {
/** /**
* Returns true if the disableAutoconf command line argument was specified. * Returns true if the disableAutoconf command line argument was specified.
* *
* @return true if the disableAutoconf command line argument was specified; otherwise false * @return true if the disableAutoconf command line argument was specified;
* otherwise false
*/ */
public boolean isAutoconfDisabled() { public boolean isAutoconfDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_AUTOCONF); return (line != null) && line.hasOption(ARGUMENT.DISABLE_AUTOCONF);
@@ -625,7 +653,8 @@ public final class CliParser {
/** /**
* Returns true if the disableComposer command line argument was specified. * Returns true if the disableComposer command line argument was specified.
* *
* @return true if the disableComposer command line argument was specified; otherwise false * @return true if the disableComposer command line argument was specified;
* otherwise false
*/ */
public boolean isComposerDisabled() { public boolean isComposerDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_COMPOSER); return (line != null) && line.hasOption(ARGUMENT.DISABLE_COMPOSER);
@@ -634,7 +663,8 @@ public final class CliParser {
/** /**
* Returns true if the disableNexus command line argument was specified. * Returns true if the disableNexus command line argument was specified.
* *
* @return true if the disableNexus command line argument was specified; otherwise false * @return true if the disableNexus command line argument was specified;
* otherwise false
*/ */
public boolean isNexusDisabled() { public boolean isNexusDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_NEXUS); return (line != null) && line.hasOption(ARGUMENT.DISABLE_NEXUS);
@@ -643,7 +673,8 @@ public final class CliParser {
/** /**
* Returns true if the disableOpenSSL command line argument was specified. * Returns true if the disableOpenSSL command line argument was specified.
* *
* @return true if the disableOpenSSL command line argument was specified; otherwise false * @return true if the disableOpenSSL command line argument was specified;
* otherwise false
*/ */
public boolean isOpenSSLDisabled() { public boolean isOpenSSLDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_OPENSSL); return (line != null) && line.hasOption(ARGUMENT.DISABLE_OPENSSL);
@@ -652,7 +683,8 @@ public final class CliParser {
/** /**
* Returns true if the disableNodeJS command line argument was specified. * Returns true if the disableNodeJS command line argument was specified.
* *
* @return true if the disableNodeJS command line argument was specified; otherwise false * @return true if the disableNodeJS command line argument was specified;
* otherwise false
*/ */
public boolean isNodeJsDisabled() { public boolean isNodeJsDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_NODE_JS); return (line != null) && line.hasOption(ARGUMENT.DISABLE_NODE_JS);
@@ -661,7 +693,8 @@ public final class CliParser {
/** /**
* Returns true if the disableCentral command line argument was specified. * Returns true if the disableCentral command line argument was specified.
* *
* @return true if the disableCentral command line argument was specified; otherwise false * @return true if the disableCentral command line argument was specified;
* otherwise false
*/ */
public boolean isCentralDisabled() { public boolean isCentralDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_CENTRAL); return (line != null) && line.hasOption(ARGUMENT.DISABLE_CENTRAL);
@@ -670,7 +703,8 @@ public final class CliParser {
/** /**
* Returns the url to the nexus server if one was specified. * Returns the url to the nexus server if one was specified.
* *
* @return the url to the nexus server; if none was specified this will return null; * @return the url to the nexus server; if none was specified this will
* return null;
*/ */
public String getNexusUrl() { public String getNexusUrl() {
if (line == null || !line.hasOption(ARGUMENT.NEXUS_URL)) { if (line == null || !line.hasOption(ARGUMENT.NEXUS_URL)) {
@@ -681,9 +715,11 @@ public final class CliParser {
} }
/** /**
* Returns true if the Nexus Analyzer should use the configured proxy to connect to Nexus; otherwise false is returned. * Returns true if the Nexus Analyzer should use the configured proxy to
* connect to Nexus; otherwise false is returned.
* *
* @return true if the Nexus Analyzer should use the configured proxy to connect to Nexus; otherwise false * @return true if the Nexus Analyzer should use the configured proxy to
* connect to Nexus; otherwise false
*/ */
public boolean isNexusUsesProxy() { public boolean isNexusUsesProxy() {
// If they didn't specify whether Nexus needs to use the proxy, we should // If they didn't specify whether Nexus needs to use the proxy, we should
@@ -723,7 +759,8 @@ public final class CliParser {
} }
/** /**
* Retrieves the file command line parameter(s) specified for the 'scan' argument. * Retrieves the file command line parameter(s) specified for the 'scan'
* argument.
* *
* @return the file paths specified on the command line for scan * @return the file paths specified on the command line for scan
*/ */
@@ -732,7 +769,8 @@ public final class CliParser {
} }
/** /**
* Retrieves the list of excluded file patterns specified by the 'exclude' argument. * Retrieves the list of excluded file patterns specified by the 'exclude'
* argument.
* *
* @return the excluded file patterns * @return the excluded file patterns
*/ */
@@ -741,7 +779,8 @@ public final class CliParser {
} }
/** /**
* Returns the directory to write the reports to specified on the command line. * Returns the directory to write the reports to specified on the command
* line.
* *
* @return the path to the reports directory. * @return the path to the reports directory.
*/ */
@@ -750,7 +789,8 @@ public final class CliParser {
} }
/** /**
* Returns the path to Mono for .NET Assembly analysis on non-windows systems. * Returns the path to Mono for .NET Assembly analysis on non-windows
* systems.
* *
* @return the path to Mono * @return the path to Mono
*/ */
@@ -768,7 +808,8 @@ public final class CliParser {
} }
/** /**
* Returns the output format specified on the command line. Defaults to HTML if no format was specified. * Returns the output format specified on the command line. Defaults to HTML
* if no format was specified.
* *
* @return the output format name. * @return the output format name.
*/ */
@@ -929,15 +970,17 @@ public final class CliParser {
*/ */
public void printVersionInfo() { public void printVersionInfo() {
final String version = String.format("%s version %s", final String version = String.format("%s version %s",
Settings.getString(Settings.KEYS.APPLICATION_VAME, "dependency-check"), Settings.getString(Settings.KEYS.APPLICATION_NAME, "dependency-check"),
Settings.getString(Settings.KEYS.APPLICATION_VERSION, "Unknown")); Settings.getString(Settings.KEYS.APPLICATION_VERSION, "Unknown"));
System.out.println(version); System.out.println(version);
} }
/** /**
* Checks if the auto update feature has been disabled. If it has been disabled via the command line this will return false. * Checks if the auto update feature has been disabled. If it has been
* disabled via the command line this will return false.
* *
* @return <code>true</code> if auto-update is allowed; otherwise <code>false</code> * @return <code>true</code> if auto-update is allowed; otherwise
* <code>false</code>
*/ */
public boolean isAutoUpdate() { public boolean isAutoUpdate() {
return line != null && !line.hasOption(ARGUMENT.DISABLE_AUTO_UPDATE); return line != null && !line.hasOption(ARGUMENT.DISABLE_AUTO_UPDATE);
@@ -946,7 +989,8 @@ public final class CliParser {
/** /**
* Checks if the update only flag has been set. * Checks if the update only flag has been set.
* *
* @return <code>true</code> if the update only flag has been set; otherwise <code>false</code>. * @return <code>true</code> if the update only flag has been set; otherwise
* <code>false</code>.
*/ */
public boolean isUpdateOnly() { public boolean isUpdateOnly() {
return line != null && line.hasOption(ARGUMENT.UPDATE_ONLY); return line != null && line.hasOption(ARGUMENT.UPDATE_ONLY);
@@ -955,14 +999,16 @@ public final class CliParser {
/** /**
* Checks if the purge NVD flag has been set. * Checks if the purge NVD flag has been set.
* *
* @return <code>true</code> if the purge nvd flag has been set; otherwise <code>false</code>. * @return <code>true</code> if the purge nvd flag has been set; otherwise
* <code>false</code>.
*/ */
public boolean isPurge() { public boolean isPurge() {
return line != null && line.hasOption(ARGUMENT.PURGE_NVD); return line != null && line.hasOption(ARGUMENT.PURGE_NVD);
} }
/** /**
* Returns the database driver name if specified; otherwise null is returned. * Returns the database driver name if specified; otherwise null is
* returned.
* *
* @return the database driver name if specified; otherwise null is returned * @return the database driver name if specified; otherwise null is returned
*/ */
@@ -971,7 +1017,8 @@ public final class CliParser {
} }
/** /**
* Returns the database driver path if specified; otherwise null is returned. * Returns the database driver path if specified; otherwise null is
* returned.
* *
* @return the database driver name if specified; otherwise null is returned * @return the database driver name if specified; otherwise null is returned
*/ */
@@ -980,34 +1027,41 @@ public final class CliParser {
} }
/** /**
* Returns the database connection string if specified; otherwise null is returned. * Returns the database connection string if specified; otherwise null is
* returned.
* *
* @return the database connection string if specified; otherwise null is returned * @return the database connection string if specified; otherwise null is
* returned
*/ */
public String getConnectionString() { public String getConnectionString() {
return line.getOptionValue(ARGUMENT.CONNECTION_STRING); return line.getOptionValue(ARGUMENT.CONNECTION_STRING);
} }
/** /**
* Returns the database database user name if specified; otherwise null is returned. * Returns the database database user name if specified; otherwise null is
* returned.
* *
* @return the database database user name if specified; otherwise null is returned * @return the database database user name if specified; otherwise null is
* returned
*/ */
public String getDatabaseUser() { public String getDatabaseUser() {
return line.getOptionValue(ARGUMENT.DB_NAME); return line.getOptionValue(ARGUMENT.DB_NAME);
} }
/** /**
* Returns the database database password if specified; otherwise null is returned. * Returns the database database password if specified; otherwise null is
* returned.
* *
* @return the database database password if specified; otherwise null is returned * @return the database database password if specified; otherwise null is
* returned
*/ */
public String getDatabasePassword() { public String getDatabasePassword() {
return line.getOptionValue(ARGUMENT.DB_PASSWORD); return line.getOptionValue(ARGUMENT.DB_PASSWORD);
} }
/** /**
* Returns the additional Extensions if specified; otherwise null is returned. * Returns the additional Extensions if specified; otherwise null is
* returned.
* *
* @return the additional Extensions; otherwise null is returned * @return the additional Extensions; otherwise null is returned
*/ */
@@ -1029,7 +1083,17 @@ public final class CliParser {
} }
/** /**
* A collection of static final strings that represent the possible command line arguments. * Returns true if the experimental analyzers are enabled.
*
* @return true if the experimental analyzers are enabled; otherwise false
*/
public boolean isExperimentalEnabled() {
return line.hasOption(ARGUMENT.EXPERIMENTAL);
}
/**
* A collection of static final strings that represent the possible command
* line arguments.
*/ */
public static class ARGUMENT { public static class ARGUMENT {
@@ -1042,50 +1106,61 @@ public final class CliParser {
*/ */
public static final String SCAN_SHORT = "s"; public static final String SCAN_SHORT = "s";
/** /**
* The long CLI argument name specifying that the CPE/CVE/etc. data should not be automatically updated. * The long CLI argument name specifying that the CPE/CVE/etc. data
* should not be automatically updated.
*/ */
public static final String DISABLE_AUTO_UPDATE = "noupdate"; public static final String DISABLE_AUTO_UPDATE = "noupdate";
/** /**
* The short CLI argument name specifying that the CPE/CVE/etc. data should not be automatically updated. * The short CLI argument name specifying that the CPE/CVE/etc. data
* should not be automatically updated.
*/ */
public static final String DISABLE_AUTO_UPDATE_SHORT = "n"; public static final String DISABLE_AUTO_UPDATE_SHORT = "n";
/** /**
* The long CLI argument name specifying that only the update phase should be executed; no scan should be run. * The long CLI argument name specifying that only the update phase
* should be executed; no scan should be run.
*/ */
public static final String UPDATE_ONLY = "updateonly"; public static final String UPDATE_ONLY = "updateonly";
/** /**
* The long CLI argument name specifying that only the update phase should be executed; no scan should be run. * The long CLI argument name specifying that only the update phase
* should be executed; no scan should be run.
*/ */
public static final String PURGE_NVD = "purge"; public static final String PURGE_NVD = "purge";
/** /**
* The long CLI argument name specifying the directory to write the reports to. * The long CLI argument name specifying the directory to write the
* reports to.
*/ */
public static final String OUT = "out"; public static final String OUT = "out";
/** /**
* The short CLI argument name specifying the directory to write the reports to. * The short CLI argument name specifying the directory to write the
* reports to.
*/ */
public static final String OUT_SHORT = "o"; public static final String OUT_SHORT = "o";
/** /**
* The long CLI argument name specifying the output format to write the reports to. * The long CLI argument name specifying the output format to write the
* reports to.
*/ */
public static final String OUTPUT_FORMAT = "format"; public static final String OUTPUT_FORMAT = "format";
/** /**
* The short CLI argument name specifying the output format to write the reports to. * The short CLI argument name specifying the output format to write the
* reports to.
*/ */
public static final String OUTPUT_FORMAT_SHORT = "f"; public static final String OUTPUT_FORMAT_SHORT = "f";
/** /**
* The long CLI argument name specifying the name of the project to be scanned. * The long CLI argument name specifying the name of the project to be
* scanned.
*/ */
public static final String PROJECT = "project"; public static final String PROJECT = "project";
/** /**
* The long CLI argument name specifying the name of the application to be scanned. * The long CLI argument name specifying the name of the application to
* be scanned.
* *
* @deprecated project should be used instead * @deprecated project should be used instead
*/ */
@Deprecated @Deprecated
public static final String APP_NAME = "app"; public static final String APP_NAME = "app";
/** /**
* The short CLI argument name specifying the name of the application to be scanned. * The short CLI argument name specifying the name of the application to
* be scanned.
* *
* @deprecated project should be used instead * @deprecated project should be used instead
*/ */
@@ -1143,11 +1218,13 @@ public final class CliParser {
*/ */
public static final String CONNECTION_TIMEOUT = "connectiontimeout"; public static final String CONNECTION_TIMEOUT = "connectiontimeout";
/** /**
* The short CLI argument name for setting the location of an additional properties file. * The short CLI argument name for setting the location of an additional
* properties file.
*/ */
public static final String PROP_SHORT = "P"; public static final String PROP_SHORT = "P";
/** /**
* The CLI argument name for setting the location of an additional properties file. * The CLI argument name for setting the location of an additional
* properties file.
*/ */
public static final String PROP = "propertyfile"; public static final String PROP = "propertyfile";
/** /**
@@ -1171,7 +1248,8 @@ public final class CliParser {
*/ */
public static final String CVE_BASE_20 = "cveUrl20Base"; public static final String CVE_BASE_20 = "cveUrl20Base";
/** /**
* The short CLI argument name for setting the location of the data directory. * The short CLI argument name for setting the location of the data
* directory.
*/ */
public static final String DATA_DIRECTORY_SHORT = "d"; public static final String DATA_DIRECTORY_SHORT = "d";
/** /**
@@ -1179,20 +1257,24 @@ public final class CliParser {
*/ */
public static final String VERBOSE_LOG = "log"; public static final String VERBOSE_LOG = "log";
/** /**
* The short CLI argument name for setting the location of the data directory. * The short CLI argument name for setting the location of the data
* directory.
*/ */
public static final String VERBOSE_LOG_SHORT = "l"; public static final String VERBOSE_LOG_SHORT = "l";
/** /**
* The CLI argument name for setting the depth of symbolic links that will be followed. * The CLI argument name for setting the depth of symbolic links that
* will be followed.
*/ */
public static final String SYM_LINK_DEPTH = "symLink"; public static final String SYM_LINK_DEPTH = "symLink";
/** /**
* The CLI argument name for setting the location of the suppression file. * The CLI argument name for setting the location of the suppression
* file.
*/ */
public static final String SUPPRESSION_FILE = "suppression"; public static final String SUPPRESSION_FILE = "suppression";
/** /**
* The CLI argument name for setting the location of the suppression file. * The CLI argument name for setting the location of the suppression
* file.
*/ */
public static final String CVE_VALID_FOR_HOURS = "cveValidForHours"; public static final String CVE_VALID_FOR_HOURS = "cveValidForHours";
/** /**
@@ -1260,7 +1342,8 @@ public final class CliParser {
*/ */
public static final String NEXUS_URL = "nexus"; public static final String NEXUS_URL = "nexus";
/** /**
* Whether or not the defined proxy should be used when connecting to Nexus. * Whether or not the defined proxy should be used when connecting to
* Nexus.
*/ */
public static final String NEXUS_USES_PROXY = "nexusUsesProxy"; public static final String NEXUS_USES_PROXY = "nexusUsesProxy";
/** /**
@@ -1280,11 +1363,13 @@ public final class CliParser {
*/ */
public static final String DB_DRIVER = "dbDriverName"; public static final String DB_DRIVER = "dbDriverName";
/** /**
* The CLI argument name for setting the path to the database driver; in case it is not on the class path. * The CLI argument name for setting the path to the database driver; in
* case it is not on the class path.
*/ */
public static final String DB_DRIVER_PATH = "dbDriverPath"; public static final String DB_DRIVER_PATH = "dbDriverPath";
/** /**
* The CLI argument name for setting the path to mono for .NET Assembly analysis on non-windows systems. * The CLI argument name for setting the path to mono for .NET Assembly
* analysis on non-windows systems.
*/ */
public static final String PATH_TO_MONO = "mono"; public static final String PATH_TO_MONO = "mono";
/** /**
@@ -1296,8 +1381,13 @@ public final class CliParser {
*/ */
public static final String EXCLUDE = "exclude"; public static final String EXCLUDE = "exclude";
/** /**
* The CLI argument name for setting the path to bundle-audit for Ruby bundle analysis. * The CLI argument name for setting the path to bundle-audit for Ruby
* bundle analysis.
*/ */
public static final String PATH_TO_BUNDLE_AUDIT = "bundleAudit"; public static final String PATH_TO_BUNDLE_AUDIT = "bundleAudit";
/**
* The CLI argument to enable the experimental analyzers.
*/
private static final String EXPERIMENTAL = "enableExperimental";
} }
} }

View File

@@ -18,7 +18,7 @@ Short | Argument&nbsp;Name&nbsp;&nbsp; | Parameter | Description | Requir
| \-\-advancedHelp | | Print the advanced help message. | Optional | \-\-advancedHelp | | Print the advanced help message. | Optional
\-v | \-\-version | | Print the version information. | Optional \-v | \-\-version | | Print the version information. | Optional
| \-\-cveValidForHours | \<hours\> | The number of hours to wait before checking for new updates from the NVD. The default is 4 hours. | Optional | \-\-cveValidForHours | \<hours\> | The number of hours to wait before checking for new updates from the NVD. The default is 4 hours. | Optional
| \-\-experimental | | Enable the [experimental analyzers](../analyzers/index.html). If not set the analyzers marked as experimental below will not be loaded or used. | Optional
Advanced Options Advanced Options
================ ================
@@ -30,18 +30,18 @@ Short | Argument&nbsp;Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | Paramete
| \-\-cveUrl20Base | \<url\> | Base URL for each year's CVE 2.0, the %d will be replaced with the year | https://nvd.nist.gov/feeds/xml/cve/nvdcve-2.0-%d.xml.gz | \-\-cveUrl20Base | \<url\> | Base URL for each year's CVE 2.0, the %d will be replaced with the year | https://nvd.nist.gov/feeds/xml/cve/nvdcve-2.0-%d.xml.gz
\-P | \-\-propertyfile | \<file\> | Specifies a file that contains properties to use instead of applicaion defaults. | &nbsp; \-P | \-\-propertyfile | \<file\> | Specifies a file that contains properties to use instead of applicaion defaults. | &nbsp;
| \-\-updateonly | | If set only the update phase of dependency-check will be executed; no scan will be executed and no report will be generated. | &nbsp; | \-\-updateonly | | If set only the update phase of dependency-check will be executed; no scan will be executed and no report will be generated. | &nbsp;
| \-\-disablePyDist | | Sets whether the Python Distribution Analyzer will be used. | false | \-\-disablePyDist | | Sets whether the [experimental](../analyzers/index.html) Python Distribution Analyzer will be used. | false
| \-\-disablePyPkg | | Sets whether the Python Package Analyzer will be used. | false | \-\-disablePyPkg | | Sets whether the [experimental](../analyzers/index.html) Python Package Analyzer will be used. | false
| \-\-disableNodeJS | | Sets whether the Node.js Package Analyzer will be used. | false | \-\-disableNodeJS | | Sets whether the [experimental](../analyzers/index.html) Node.js Package Analyzer will be used. | false
| \-\-disableRubygems | | Sets whether the Ruby Gemspec Analyzer will be used. | false | \-\-disableRubygems | | Sets whether the [experimental](../analyzers/index.html) Ruby Gemspec Analyzer will be used. | false
| \-\-disableBundleAudit | | Sets whether the Ruby Bundler Audit Analyzer will be used. | false | \-\-disableBundleAudit | | Sets whether the [experimental](../analyzers/index.html) Ruby Bundler Audit Analyzer will be used. | false
| \-\-disableAutoconf | | Sets whether the Autoconf Analyzer will be used. | false | \-\-disableAutoconf | | Sets whether the [experimental](../analyzers/index.html) Autoconf Analyzer will be used. | false
| \-\-disableOpenSSL | | Sets whether the OpenSSL Analyzer will be used. | false | \-\-disableOpenSSL | | Sets whether the OpenSSL Analyzer will be used. | false
| \-\-disableCmake | | Sets whether the Cmake Analyzer will be disabled. | false | \-\-disableCmake | | Sets whether the [experimental](../analyzers/index.html) Cmake Analyzer will be disabled. | false
| \-\-disableArchive | | Sets whether the Archive Analyzer will be disabled. | false | \-\-disableArchive | | Sets whether the Archive Analyzer will be disabled. | false
| \-\-zipExtensions | \<strings\> | A comma-separated list of additional file extensions to be treated like a ZIP file, the contents will be extracted and analyzed. | &nbsp; | \-\-zipExtensions | \<strings\> | A comma-separated list of additional file extensions to be treated like a ZIP file, the contents will be extracted and analyzed. | &nbsp;
| \-\-disableJar | | Sets whether the Jar Analyzer will be disabled. | false | \-\-disableJar | | Sets whether the Jar Analyzer will be disabled. | false
| \-\-disableComposer | | Sets whether the PHP Composer Lock File Analyzer will be disabled. | false | \-\-disableComposer | | Sets whether the [experimental](../analyzers/index.html) PHP Composer Lock File Analyzer will be disabled. | false
| \-\-disableCentral | | Sets whether the Central Analyzer will be used. **Disabling this analyzer is not recommended as it could lead to false negatives (e.g. libraries that have vulnerabilities may not be reported correctly).** If this analyzer is being disabled there is a good chance you also want to disable the Nexus Analyzer. | false | \-\-disableCentral | | Sets whether the Central Analyzer will be used. **Disabling this analyzer is not recommended as it could lead to false negatives (e.g. libraries that have vulnerabilities may not be reported correctly).** If this analyzer is being disabled there is a good chance you also want to disable the Nexus Analyzer. | false
| \-\-disableNexus | | Sets whether the Nexus Analyzer will be used. Note, this has been superceded by the Central Analyzer. However, you can configure the Nexus URL to utilize an internally hosted Nexus Pro server. | false | \-\-disableNexus | | Sets whether the Nexus Analyzer will be used. Note, this has been superceded by the Central Analyzer. However, you can configure the Nexus URL to utilize an internally hosted Nexus Pro server. | false
| \-\-nexus | \<url\> | The url to the Nexus Server's web service end point (example: http://domain.enterprise/nexus/service/local/). If not set the Nexus Analyzer will be disabled. | &nbsp; | \-\-nexus | \<url\> | The url to the Nexus Server's web service end point (example: http://domain.enterprise/nexus/service/local/). If not set the Nexus Analyzer will be disabled. | &nbsp;
@@ -50,7 +50,7 @@ Short | Argument&nbsp;Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | Paramete
| \-\-disableAssembly | | Sets whether or not the .NET Assembly Analyzer should be used. | false | \-\-disableAssembly | | Sets whether or not the .NET Assembly Analyzer should be used. | false
| \-\-mono | \<path\> | The path to Mono for .NET Assembly analysis on non-windows systems. | &nbsp; | \-\-mono | \<path\> | The path to Mono for .NET Assembly analysis on non-windows systems. | &nbsp;
| \-\-bundleAudit | | The path to the bundle-audit executable. | &nbsp; | \-\-bundleAudit | | The path to the bundle-audit executable. | &nbsp;
| \-\-proxyserver | \<server\> | The proxy server to use when downloading resources. | &nbsp; | \-\-proxyserver | \<server\> | The proxy server to use when downloading resources; see the [proxy configuration](../data/proxy.html) page for more information. | &nbsp;
| \-\-proxyport | \<port\> | The proxy port to use when downloading resources. | &nbsp; | \-\-proxyport | \<port\> | The proxy port to use when downloading resources. | &nbsp;
| \-\-connectiontimeout | \<timeout\> | The connection timeout (in milliseconds) to use when downloading resources. | &nbsp; | \-\-connectiontimeout | \<timeout\> | The connection timeout (in milliseconds) to use when downloading resources. | &nbsp;
| \-\-proxypass | \<pass\> | The proxy password to use when downloading resources. | &nbsp; | \-\-proxypass | \<pass\> | The proxy password to use when downloading resources. | &nbsp;

View File

@@ -20,7 +20,7 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
<parent> <parent>
<groupId>org.owasp</groupId> <groupId>org.owasp</groupId>
<artifactId>dependency-check-parent</artifactId> <artifactId>dependency-check-parent</artifactId>
<version>1.3.2</version> <version>1.4.3</version>
</parent> </parent>
<artifactId>dependency-check-core</artifactId> <artifactId>dependency-check-core</artifactId>
@@ -83,9 +83,6 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
</testResource> </testResource>
<testResource> <testResource>
<directory>${basedir}/src/test/resources</directory> <directory>${basedir}/src/test/resources</directory>
<excludes>
<exclude>**/mysql-connector-java-5.1.27-bin.jar</exclude>
</excludes>
<filtering>false</filtering> <filtering>false</filtering>
</testResource> </testResource>
</testResources> </testResources>
@@ -178,6 +175,7 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<configuration> <configuration>
<argLine>-Dfile.encoding=UTF-8</argLine>
<systemProperties> <systemProperties>
<property> <property>
<name>data.directory</name> <name>data.directory</name>
@@ -454,6 +452,20 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
<scope>test</scope> <scope>test</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>xalan</groupId>
<artifactId>xalan</artifactId>
<version>2.7.0</version>
<scope>test</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.8</version>
<scope>test</scope>
<optional>true</optional>
</dependency>
</dependencies> </dependencies>
<profiles> <profiles>
<profile> <profile>
@@ -582,13 +594,6 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
<scope>test</scope> <scope>test</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.2</version>
<scope>test</scope>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>org.apache.ws.security</groupId> <groupId>org.apache.ws.security</groupId>
<artifactId>wss4j</artifactId> <artifactId>wss4j</artifactId>

View File

@@ -30,6 +30,8 @@ import org.owasp.dependencycheck.data.update.UpdateService;
import org.owasp.dependencycheck.data.update.exception.UpdateException; import org.owasp.dependencycheck.data.update.exception.UpdateException;
import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.exception.NoDataException; import org.owasp.dependencycheck.exception.NoDataException;
import org.owasp.dependencycheck.exception.ExceptionCollection;
import org.owasp.dependencycheck.exception.InitializationException;
import org.owasp.dependencycheck.utils.InvalidSettingException; import org.owasp.dependencycheck.utils.InvalidSettingException;
import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.Settings;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -47,8 +49,10 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
/** /**
* Scans files, directories, etc. for Dependencies. Analyzers are loaded and used to process the files found by the scan, if a * Scans files, directories, etc. for Dependencies. Analyzers are loaded and
* file is encountered and an Analyzer is associated with the file type then the file is turned into a dependency. * used to process the files found by the scan, if a file is encountered and an
* Analyzer is associated with the file type then the file is turned into a
* dependency.
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
@@ -61,15 +65,16 @@ public class Engine implements FileFilter {
/** /**
* A Map of analyzers grouped by Analysis phase. * A Map of analyzers grouped by Analysis phase.
*/ */
private Map<AnalysisPhase, List<Analyzer>> analyzers = new EnumMap<AnalysisPhase, List<Analyzer>>(AnalysisPhase.class); private final Map<AnalysisPhase, List<Analyzer>> analyzers = new EnumMap<AnalysisPhase, List<Analyzer>>(AnalysisPhase.class);
/** /**
* A Map of analyzers grouped by Analysis phase. * A Map of analyzers grouped by Analysis phase.
*/ */
private Set<FileTypeAnalyzer> fileTypeAnalyzers = new HashSet<FileTypeAnalyzer>(); private final Set<FileTypeAnalyzer> fileTypeAnalyzers = new HashSet<FileTypeAnalyzer>();
/** /**
* The ClassLoader to use when dynamically loading Analyzer and Update services. * The ClassLoader to use when dynamically loading Analyzer and Update
* services.
*/ */
private ClassLoader serviceClassLoader = Thread.currentThread().getContextClassLoader(); private ClassLoader serviceClassLoader = Thread.currentThread().getContextClassLoader();
/** /**
@@ -80,7 +85,8 @@ public class Engine implements FileFilter {
/** /**
* Creates a new Engine. * Creates a new Engine.
* *
* @throws DatabaseException thrown if there is an error connecting to the database * @throws DatabaseException thrown if there is an error connecting to the
* database
*/ */
public Engine() throws DatabaseException { public Engine() throws DatabaseException {
initializeEngine(); initializeEngine();
@@ -90,7 +96,8 @@ public class Engine implements FileFilter {
* Creates a new Engine. * Creates a new Engine.
* *
* @param serviceClassLoader a reference the class loader being used * @param serviceClassLoader a reference the class loader being used
* @throws DatabaseException thrown if there is an error connecting to the database * @throws DatabaseException thrown if there is an error connecting to the
* database
*/ */
public Engine(ClassLoader serviceClassLoader) throws DatabaseException { public Engine(ClassLoader serviceClassLoader) throws DatabaseException {
this.serviceClassLoader = serviceClassLoader; this.serviceClassLoader = serviceClassLoader;
@@ -98,9 +105,11 @@ public class Engine implements FileFilter {
} }
/** /**
* Creates a new Engine using the specified classloader to dynamically load Analyzer and Update services. * Creates a new Engine using the specified classloader to dynamically load
* Analyzer and Update services.
* *
* @throws DatabaseException thrown if there is an error connecting to the database * @throws DatabaseException thrown if there is an error connecting to the
* database
*/ */
protected final void initializeEngine() throws DatabaseException { protected final void initializeEngine() throws DatabaseException {
ConnectionFactory.initialize(); ConnectionFactory.initialize();
@@ -115,7 +124,8 @@ public class Engine implements FileFilter {
} }
/** /**
* Loads the analyzers specified in the configuration file (or system properties). * Loads the analyzers specified in the configuration file (or system
* properties).
*/ */
private void loadAnalyzers() { private void loadAnalyzers() {
if (!analyzers.isEmpty()) { if (!analyzers.isEmpty()) {
@@ -126,9 +136,8 @@ public class Engine implements FileFilter {
} }
final AnalyzerService service = new AnalyzerService(serviceClassLoader); final AnalyzerService service = new AnalyzerService(serviceClassLoader);
final Iterator<Analyzer> iterator = service.getAnalyzers(); final List<Analyzer> iterator = service.getAnalyzers();
while (iterator.hasNext()) { for (Analyzer a : iterator) {
final Analyzer a = iterator.next();
analyzers.get(a.getAnalysisPhase()).add(a); analyzers.get(a.getAnalysisPhase()).add(a);
if (a instanceof FileTypeAnalyzer) { if (a instanceof FileTypeAnalyzer) {
this.fileTypeAnalyzers.add((FileTypeAnalyzer) a); this.fileTypeAnalyzers.add((FileTypeAnalyzer) a);
@@ -165,8 +174,9 @@ public class Engine implements FileFilter {
} }
/** /**
* Scans an array of files or directories. If a directory is specified, it will be scanned recursively. Any dependencies * Scans an array of files or directories. If a directory is specified, it
* identified are added to the dependency collection. * will be scanned recursively. Any dependencies identified are added to the
* dependency collection.
* *
* @param paths an array of paths to files or directories to be analyzed * @param paths an array of paths to files or directories to be analyzed
* @return the list of dependencies scanned * @return the list of dependencies scanned
@@ -184,8 +194,9 @@ public class Engine implements FileFilter {
} }
/** /**
* Scans a given file or directory. If a directory is specified, it will be scanned recursively. Any dependencies identified * Scans a given file or directory. If a directory is specified, it will be
* are added to the dependency collection. * scanned recursively. Any dependencies identified are added to the
* dependency collection.
* *
* @param path the path to a file or directory to be analyzed * @param path the path to a file or directory to be analyzed
* @return the list of dependencies scanned * @return the list of dependencies scanned
@@ -196,8 +207,9 @@ public class Engine implements FileFilter {
} }
/** /**
* Scans an array of files or directories. If a directory is specified, it will be scanned recursively. Any dependencies * Scans an array of files or directories. If a directory is specified, it
* identified are added to the dependency collection. * will be scanned recursively. Any dependencies identified are added to the
* dependency collection.
* *
* @param files an array of paths to files or directories to be analyzed. * @param files an array of paths to files or directories to be analyzed.
* @return the list of dependencies * @return the list of dependencies
@@ -215,8 +227,9 @@ public class Engine implements FileFilter {
} }
/** /**
* Scans a collection of files or directories. If a directory is specified, it will be scanned recursively. Any dependencies * Scans a collection of files or directories. If a directory is specified,
* identified are added to the dependency collection. * it will be scanned recursively. Any dependencies identified are added to
* the dependency collection.
* *
* @param files a set of paths to files or directories to be analyzed * @param files a set of paths to files or directories to be analyzed
* @return the list of dependencies scanned * @return the list of dependencies scanned
@@ -234,8 +247,9 @@ public class Engine implements FileFilter {
} }
/** /**
* Scans a given file or directory. If a directory is specified, it will be scanned recursively. Any dependencies identified * Scans a given file or directory. If a directory is specified, it will be
* are added to the dependency collection. * scanned recursively. Any dependencies identified are added to the
* dependency collection.
* *
* @param file the path to a file or directory to be analyzed * @param file the path to a file or directory to be analyzed
* @return the list of dependencies scanned * @return the list of dependencies scanned
@@ -258,7 +272,8 @@ public class Engine implements FileFilter {
} }
/** /**
* Recursively scans files and directories. Any dependencies identified are added to the dependency collection. * Recursively scans files and directories. Any dependencies identified are
* added to the dependency collection.
* *
* @param dir the directory to scan * @param dir the directory to scan
* @return the list of Dependency objects scanned * @return the list of Dependency objects scanned
@@ -283,7 +298,8 @@ public class Engine implements FileFilter {
} }
/** /**
* Scans a specified file. If a dependency is identified it is added to the dependency collection. * Scans a specified file. If a dependency is identified it is added to the
* dependency collection.
* *
* @param file The file to scan * @param file The file to scan
* @return the scanned dependency * @return the scanned dependency
@@ -302,20 +318,38 @@ public class Engine implements FileFilter {
} }
/** /**
* Runs the analyzers against all of the dependencies. Since the mutable dependencies list is exposed via * Runs the analyzers against all of the dependencies. Since the mutable
* {@link #getDependencies()}, this method iterates over a copy of the dependencies list. Thus, the potential for * dependencies list is exposed via {@link #getDependencies()}, this method
* {@link java.util.ConcurrentModificationException}s is avoided, and analyzers may safely add or remove entries from the * iterates over a copy of the dependencies list. Thus, the potential for
* dependencies list. * {@link java.util.ConcurrentModificationException}s is avoided, and
* analyzers may safely add or remove entries from the dependencies list.
*
* Every effort is made to complete analysis on the dependencies. In some
* cases an exception will occur with part of the analysis being performed
* which may not affect the entire analysis. If an exception occurs it will
* be included in the thrown exception collection.
*
* @throws ExceptionCollection a collections of any exceptions that occurred
* during analysis
*/ */
public void analyzeDependencies() { public void analyzeDependencies() throws ExceptionCollection {
final List<Throwable> exceptions = new ArrayList<Throwable>();
boolean autoUpdate = true; boolean autoUpdate = true;
try { try {
autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE); autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE);
} catch (InvalidSettingException ex) { } catch (InvalidSettingException ex) {
LOGGER.debug("Invalid setting for auto-update; using true."); LOGGER.debug("Invalid setting for auto-update; using true.");
exceptions.add(ex);
} }
if (autoUpdate) { if (autoUpdate) {
doUpdates(); try {
doUpdates();
} catch (UpdateException ex) {
exceptions.add(ex);
LOGGER.warn("Unable to update Cached Web DataSource, using local "
+ "data instead. Results may not include recent vulnerabilities.");
LOGGER.debug("Update Error", ex);
}
} }
//need to ensure that data exists //need to ensure that data exists
@@ -324,16 +358,17 @@ public class Engine implements FileFilter {
} catch (NoDataException ex) { } catch (NoDataException ex) {
LOGGER.error("{}\n\nUnable to continue dependency-check analysis.", ex.getMessage()); LOGGER.error("{}\n\nUnable to continue dependency-check analysis.", ex.getMessage());
LOGGER.debug("", ex); LOGGER.debug("", ex);
return; exceptions.add(ex);
throw new ExceptionCollection("Unable to continue dependency-check analysis.", exceptions, true);
} catch (DatabaseException ex) { } catch (DatabaseException ex) {
LOGGER.error("{}\n\nUnable to continue dependency-check analysis.", ex.getMessage()); LOGGER.error("{}\n\nUnable to continue dependency-check analysis.", ex.getMessage());
LOGGER.debug("", ex); LOGGER.debug("", ex);
return; exceptions.add(ex);
throw new ExceptionCollection("Unable to connect to the dependency-check database", exceptions, true);
} }
LOGGER.debug("\n----------------------------------------------------\nBEGIN ANALYSIS\n----------------------------------------------------"); LOGGER.debug("\n----------------------------------------------------\nBEGIN ANALYSIS\n----------------------------------------------------");
LOGGER.info("Analysis Starting"); LOGGER.info("Analysis Started");
final long analysisStart = System.currentTimeMillis(); final long analysisStart = System.currentTimeMillis();
// analysis phases // analysis phases
@@ -341,7 +376,12 @@ public class Engine implements FileFilter {
final List<Analyzer> analyzerList = analyzers.get(phase); final List<Analyzer> analyzerList = analyzers.get(phase);
for (Analyzer a : analyzerList) { for (Analyzer a : analyzerList) {
a = initializeAnalyzer(a); try {
a = initializeAnalyzer(a);
} catch (InitializationException ex) {
exceptions.add(ex);
continue;
}
/* need to create a copy of the collection because some of the /* need to create a copy of the collection because some of the
* analyzers may modify it. This prevents ConcurrentModificationExceptions. * analyzers may modify it. This prevents ConcurrentModificationExceptions.
@@ -362,10 +402,12 @@ public class Engine implements FileFilter {
} catch (AnalysisException ex) { } catch (AnalysisException ex) {
LOGGER.warn("An error occurred while analyzing '{}'.", d.getActualFilePath()); LOGGER.warn("An error occurred while analyzing '{}'.", d.getActualFilePath());
LOGGER.debug("", ex); LOGGER.debug("", ex);
exceptions.add(ex);
} catch (Throwable ex) { } catch (Throwable ex) {
//final AnalysisException ax = new AnalysisException(axMsg, ex); //final AnalysisException ax = new AnalysisException(axMsg, ex);
LOGGER.warn("An unexpected error occurred during analysis of '{}'", d.getActualFilePath()); LOGGER.warn("An unexpected error occurred during analysis of '{}'", d.getActualFilePath());
LOGGER.debug("", ex); LOGGER.debug("", ex);
exceptions.add(ex);
} }
} }
} }
@@ -381,6 +423,9 @@ public class Engine implements FileFilter {
LOGGER.debug("\n----------------------------------------------------\nEND ANALYSIS\n----------------------------------------------------"); LOGGER.debug("\n----------------------------------------------------\nEND ANALYSIS\n----------------------------------------------------");
LOGGER.info("Analysis Complete ({} ms)", System.currentTimeMillis() - analysisStart); LOGGER.info("Analysis Complete ({} ms)", System.currentTimeMillis() - analysisStart);
if (exceptions.size() > 0) {
throw new ExceptionCollection("One or more exceptions occured during dependency-check analysis", exceptions);
}
} }
/** /**
@@ -388,12 +433,14 @@ public class Engine implements FileFilter {
* *
* @param analyzer the analyzer to initialize * @param analyzer the analyzer to initialize
* @return the initialized analyzer * @return the initialized analyzer
* @throws InitializationException thrown when there is a problem
* initializing the analyzer
*/ */
protected Analyzer initializeAnalyzer(Analyzer analyzer) { protected Analyzer initializeAnalyzer(Analyzer analyzer) throws InitializationException {
try { try {
LOGGER.debug("Initializing {}", analyzer.getName()); LOGGER.debug("Initializing {}", analyzer.getName());
analyzer.initialize(); analyzer.initialize();
} catch (Throwable ex) { } catch (InitializationException ex) {
LOGGER.error("Exception occurred initializing {}.", analyzer.getName()); LOGGER.error("Exception occurred initializing {}.", analyzer.getName());
LOGGER.debug("", ex); LOGGER.debug("", ex);
try { try {
@@ -401,6 +448,16 @@ public class Engine implements FileFilter {
} catch (Throwable ex1) { } catch (Throwable ex1) {
LOGGER.trace("", ex1); LOGGER.trace("", ex1);
} }
throw ex;
} catch (Throwable ex) {
LOGGER.error("Unexpected exception occurred initializing {}.", analyzer.getName());
LOGGER.debug("", ex);
try {
analyzer.close();
} catch (Throwable ex1) {
LOGGER.trace("", ex1);
}
throw new InitializationException("Unexpected Exception", ex);
} }
return analyzer; return analyzer;
} }
@@ -420,28 +477,26 @@ public class Engine implements FileFilter {
} }
/** /**
* Cycles through the cached web data sources and calls update on all of them. * Cycles through the cached web data sources and calls update on all of
* them.
*
* @throws UpdateException thrown if the operation fails
*/ */
public void doUpdates() { public void doUpdates() throws UpdateException {
LOGGER.info("Checking for updates"); LOGGER.info("Checking for updates");
final long updateStart = System.currentTimeMillis(); final long updateStart = System.currentTimeMillis();
final UpdateService service = new UpdateService(serviceClassLoader); final UpdateService service = new UpdateService(serviceClassLoader);
final Iterator<CachedWebDataSource> iterator = service.getDataSources(); final Iterator<CachedWebDataSource> iterator = service.getDataSources();
while (iterator.hasNext()) { while (iterator.hasNext()) {
final CachedWebDataSource source = iterator.next(); final CachedWebDataSource source = iterator.next();
try { source.update();
source.update();
} catch (UpdateException ex) {
LOGGER.warn(
"Unable to update Cached Web DataSource, using local data instead. Results may not include recent vulnerabilities.");
LOGGER.debug("Unable to update details for {}", source.getClass().getName(), ex);
}
} }
LOGGER.info("Check for updates complete ({} ms)", System.currentTimeMillis() - updateStart); LOGGER.info("Check for updates complete ({} ms)", System.currentTimeMillis() - updateStart);
} }
/** /**
* Returns a full list of all of the analyzers. This is useful for reporting which analyzers where used. * Returns a full list of all of the analyzers. This is useful for reporting
* which analyzers where used.
* *
* @return a list of Analyzers * @return a list of Analyzers
*/ */
@@ -458,7 +513,8 @@ public class Engine implements FileFilter {
* Checks all analyzers to see if an extension is supported. * Checks all analyzers to see if an extension is supported.
* *
* @param file a file extension * @param file a file extension
* @return true or false depending on whether or not the file extension is supported * @return true or false depending on whether or not the file extension is
* supported
*/ */
@Override @Override
public boolean accept(File file) { public boolean accept(File file) {
@@ -484,10 +540,12 @@ public class Engine implements FileFilter {
} }
/** /**
* Checks the CPE Index to ensure documents exists. If none exist a NoDataException is thrown. * Checks the CPE Index to ensure documents exists. If none exist a
* NoDataException is thrown.
* *
* @throws NoDataException thrown if no data exists in the CPE Index * @throws NoDataException thrown if no data exists in the CPE Index
* @throws DatabaseException thrown if there is an exception opening the database * @throws DatabaseException thrown if there is an exception opening the
* database
*/ */
private void ensureDataExists() throws NoDataException, DatabaseException { private void ensureDataExists() throws NoDataException, DatabaseException {
final CveDB cve = new CveDB(); final CveDB cve = new CveDB();

View File

@@ -27,6 +27,7 @@ import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.dependency.Identifier; import org.owasp.dependencycheck.dependency.Identifier;
import org.owasp.dependencycheck.dependency.Vulnerability; import org.owasp.dependencycheck.dependency.Vulnerability;
import org.owasp.dependencycheck.exception.ExceptionCollection;
import org.owasp.dependencycheck.exception.ScanAgentException; import org.owasp.dependencycheck.exception.ScanAgentException;
import org.owasp.dependencycheck.reporting.ReportGenerator; import org.owasp.dependencycheck.reporting.ReportGenerator;
import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.Settings;
@@ -34,14 +35,16 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* This class provides a way to easily conduct a scan solely based on existing evidence metadata rather than collecting evidence * This class provides a way to easily conduct a scan solely based on existing
* from the files themselves. This class is based on the Ant task and Maven plugin with the exception that it takes a list of * evidence metadata rather than collecting evidence from the files themselves.
* dependencies that can be programmatically added from data in a spreadsheet, database or some other datasource and conduct a * This class is based on the Ant task and Maven plugin with the exception that
* scan based on this pre-defined evidence. * it takes a list of dependencies that can be programmatically added from data
* in a spreadsheet, database or some other datasource and conduct a scan based
* on this pre-defined evidence.
* *
* <h2>Example:</h2> * <h2>Example:</h2>
* <pre> * <pre>
* List<Dependency> dependencies = new ArrayList<Dependency>(); * List&lt;Dependency&gt; dependencies = new ArrayList&lt;Dependency&gt;();
* Dependency dependency = new Dependency(new File(FileUtils.getBitBucket())); * Dependency dependency = new Dependency(new File(FileUtils.getBitBucket()));
* dependency.getProductEvidence().addEvidence("my-datasource", "name", "Jetty", Confidence.HIGH); * dependency.getProductEvidence().addEvidence("my-datasource", "name", "Jetty", Confidence.HIGH);
* dependency.getVersionEvidence().addEvidence("my-datasource", "version", "5.1.10", Confidence.HIGH); * dependency.getVersionEvidence().addEvidence("my-datasource", "version", "5.1.10", Confidence.HIGH);
@@ -55,7 +58,7 @@ import org.slf4j.LoggerFactory;
* scan.execute(); * scan.execute();
* </pre> * </pre>
* *
* @author Steve Springett <steve.springett@owasp.org> * @author Steve Springett
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class DependencyCheckScanAgent { public class DependencyCheckScanAgent {
@@ -138,7 +141,8 @@ public class DependencyCheckScanAgent {
} }
/** /**
* Specifies the destination directory for the generated Dependency-Check report. * Specifies the destination directory for the generated Dependency-Check
* report.
*/ */
private String reportOutputDirectory; private String reportOutputDirectory;
@@ -161,9 +165,11 @@ public class DependencyCheckScanAgent {
} }
/** /**
* Specifies if the build should be failed if a CVSS score above a specified level is identified. The default is 11 which * Specifies if the build should be failed if a CVSS score above a specified
* means since the CVSS scores are 0-10, by default the build will never fail and the CVSS score is set to 11. The valid range * level is identified. The default is 11 which means since the CVSS scores
* for the fail build on CVSS is 0 to 11, where anything above 10 will not cause the build to fail. * are 0-10, by default the build will never fail and the CVSS score is set
* to 11. The valid range for the fail build on CVSS is 0 to 11, where
* anything above 10 will not cause the build to fail.
*/ */
private float failBuildOnCVSS = 11; private float failBuildOnCVSS = 11;
@@ -186,8 +192,8 @@ public class DependencyCheckScanAgent {
} }
/** /**
* Sets whether auto-updating of the NVD CVE/CPE data is enabled. It is not recommended that this be turned to false. Default * Sets whether auto-updating of the NVD CVE/CPE data is enabled. It is not
* is true. * recommended that this be turned to false. Default is true.
*/ */
private boolean autoUpdate = true; private boolean autoUpdate = true;
@@ -233,8 +239,9 @@ public class DependencyCheckScanAgent {
} }
/** /**
* The report format to be generated (HTML, XML, VULN, ALL). This configuration option has no affect if using this within the * The report format to be generated (HTML, XML, VULN, ALL). This
* Site plugin unless the externalReport is set to true. Default is HTML. * configuration option has no affect if using this within the Site plugin
* unless the externalReport is set to true. Default is HTML.
*/ */
private ReportGenerator.Format reportFormat = ReportGenerator.Format.HTML; private ReportGenerator.Format reportFormat = ReportGenerator.Format.HTML;
@@ -283,7 +290,9 @@ public class DependencyCheckScanAgent {
* Get the value of proxyServer. * Get the value of proxyServer.
* *
* @return the value of proxyServer * @return the value of proxyServer
* @deprecated use {@link org.owasp.dependencycheck.agent.DependencyCheckScanAgent#getProxyServer()} instead * @deprecated use
* {@link org.owasp.dependencycheck.agent.DependencyCheckScanAgent#getProxyServer()}
* instead
*/ */
@Deprecated @Deprecated
public String getProxyUrl() { public String getProxyUrl() {
@@ -694,8 +703,8 @@ public class DependencyCheckScanAgent {
} }
/** /**
* Additional ZIP File extensions to add analyze. This should be a comma-separated list of file extensions to treat like ZIP * Additional ZIP File extensions to add analyze. This should be a
* files. * comma-separated list of file extensions to treat like ZIP files.
*/ */
private String zipExtensions; private String zipExtensions;
@@ -836,11 +845,17 @@ public class DependencyCheckScanAgent {
* Executes the Dependency-Check on the dependent libraries. * Executes the Dependency-Check on the dependent libraries.
* *
* @return the Engine used to scan the dependencies. * @return the Engine used to scan the dependencies.
* @throws org.owasp.dependencycheck.data.nvdcve.DatabaseException thrown if there is an exception connecting to the database * @throws ExceptionCollection a collection of one or more exceptions that
* occurred during analysis.
*/ */
private Engine executeDependencyCheck() throws DatabaseException { private Engine executeDependencyCheck() throws ExceptionCollection {
populateSettings(); populateSettings();
final Engine engine = new Engine(); final Engine engine;
try {
engine = new Engine();
} catch (DatabaseException ex) {
throw new ExceptionCollection(ex, true);
}
engine.setDependencies(this.dependencies); engine.setDependencies(this.dependencies);
engine.analyzeDependencies(); engine.analyzeDependencies();
return engine; return engine;
@@ -881,8 +896,9 @@ public class DependencyCheckScanAgent {
} }
/** /**
* Takes the properties supplied and updates the dependency-check settings. Additionally, this sets the system properties * Takes the properties supplied and updates the dependency-check settings.
* required to change the proxy server, port, and connection timeout. * Additionally, this sets the system properties required to change the
* proxy server, port, and connection timeout.
*/ */
private void populateSettings() { private void populateSettings() {
Settings.initialize(); Settings.initialize();
@@ -925,7 +941,8 @@ public class DependencyCheckScanAgent {
* Executes the dependency-check and generates the report. * Executes the dependency-check and generates the report.
* *
* @return a reference to the engine used to perform the scan. * @return a reference to the engine used to perform the scan.
* @throws org.owasp.dependencycheck.exception.ScanAgentException thrown if there is an exception executing the scan. * @throws org.owasp.dependencycheck.exception.ScanAgentException thrown if
* there is an exception executing the scan.
*/ */
public Engine execute() throws ScanAgentException { public Engine execute() throws ScanAgentException {
Engine engine = null; Engine engine = null;
@@ -940,10 +957,12 @@ public class DependencyCheckScanAgent {
if (this.failBuildOnCVSS <= 10) { if (this.failBuildOnCVSS <= 10) {
checkForFailure(engine.getDependencies()); checkForFailure(engine.getDependencies());
} }
} catch (DatabaseException ex) { } catch (ExceptionCollection ex) {
LOGGER.error( if (ex.isFatal()) {
"Unable to connect to the dependency-check database; analysis has stopped"); LOGGER.error("A fatal exception occurred during analysis; analysis has stopped. Please see the debug log for more details.");
LOGGER.debug("", ex); LOGGER.debug("", ex);
}
throw new ScanAgentException("One or more exceptions occurred during analysis; please see the debug log for more details.", ex);
} finally { } finally {
Settings.cleanup(true); Settings.cleanup(true);
if (engine != null) { if (engine != null) {
@@ -954,11 +973,12 @@ public class DependencyCheckScanAgent {
} }
/** /**
* Checks to see if a vulnerability has been identified with a CVSS score that is above the threshold set in the * Checks to see if a vulnerability has been identified with a CVSS score
* configuration. * that is above the threshold set in the configuration.
* *
* @param dependencies the list of dependency objects * @param dependencies the list of dependency objects
* @throws org.owasp.dependencycheck.exception.ScanAgentException thrown if there is an exception executing the scan. * @throws org.owasp.dependencycheck.exception.ScanAgentException thrown if
* there is an exception executing the scan.
*/ */
private void checkForFailure(List<Dependency> dependencies) throws ScanAgentException { private void checkForFailure(List<Dependency> dependencies) throws ScanAgentException {
final StringBuilder ids = new StringBuilder(); final StringBuilder ids = new StringBuilder();
@@ -986,7 +1006,8 @@ public class DependencyCheckScanAgent {
} }
/** /**
* Generates a warning message listing a summary of dependencies and their associated CPE and CVE entries. * Generates a warning message listing a summary of dependencies and their
* associated CPE and CVE entries.
* *
* @param dependencies a list of dependency objects * @param dependencies a list of dependency objects
*/ */

View File

@@ -17,7 +17,11 @@
*/ */
package org.owasp.dependencycheck.analyzer; package org.owasp.dependencycheck.analyzer;
import org.owasp.dependencycheck.exception.InitializationException;
/** /**
* Base class for analyzers to avoid code duplication of initialize and close
* as most analyzers do not need these methods.
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
@@ -26,10 +30,10 @@ public abstract class AbstractAnalyzer implements Analyzer {
/** /**
* The initialize method does nothing for this Analyzer. * The initialize method does nothing for this Analyzer.
* *
* @throws Exception thrown if there is an exception * @throws InitializationException thrown if there is an exception
*/ */
@Override @Override
public void initialize() throws Exception { public void initialize() throws InitializationException {
//do nothing //do nothing
} }

View File

@@ -30,9 +30,11 @@ import java.io.FileFilter;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import org.owasp.dependencycheck.exception.InitializationException;
/** /**
* The base FileTypeAnalyzer that all analyzers that have specific file types they analyze should extend. * The base FileTypeAnalyzer that all analyzers that have specific file types
* they analyze should extend.
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
@@ -40,7 +42,8 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen
//<editor-fold defaultstate="collapsed" desc="Constructor"> //<editor-fold defaultstate="collapsed" desc="Constructor">
/** /**
* Base constructor that all children must call. This checks the configuration to determine if the analyzer is enabled. * Base constructor that all children must call. This checks the
* configuration to determine if the analyzer is enabled.
*/ */
public AbstractFileTypeAnalyzer() { public AbstractFileTypeAnalyzer() {
reset(); reset();
@@ -58,7 +61,8 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen
private boolean filesMatched = false; private boolean filesMatched = false;
/** /**
* Get the value of filesMatched. A flag indicating whether the scan included any file types this analyzer supports. * Get the value of filesMatched. A flag indicating whether the scan
* included any file types this analyzer supports.
* *
* @return the value of filesMatched * @return the value of filesMatched
*/ */
@@ -67,7 +71,8 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen
} }
/** /**
* Set the value of filesMatched. A flag indicating whether the scan included any file types this analyzer supports. * Set the value of filesMatched. A flag indicating whether the scan
* included any file types this analyzer supports.
* *
* @param filesMatched new value of filesMatched * @param filesMatched new value of filesMatched
*/ */
@@ -102,27 +107,30 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen
//<editor-fold defaultstate="collapsed" desc="Abstract methods children must implement"> //<editor-fold defaultstate="collapsed" desc="Abstract methods children must implement">
/** /**
* <p> * <p>
* Returns the {@link java.io.FileFilter} used to determine which files are to be analyzed. An example would be an analyzer * Returns the {@link java.io.FileFilter} used to determine which files are
* that inspected Java jar files. Implementors may use {@link org.owasp.dependencycheck.utils.FileFilterBuilder}.</p> * to be analyzed. An example would be an analyzer that inspected Java jar
* files. Implementors may use
* {@link org.owasp.dependencycheck.utils.FileFilterBuilder}.</p>
* <p>
* If the analyzer returns null it will not cause additional files to be
* analyzed, but will be executed against every file loaded.</p>
* *
* @return the file filter used to determine which files are to be analyzed * @return the file filter used to determine which files are to be analyzed
* <p/>
* <p>
* If the analyzer returns null it will not cause additional files to be analyzed, but will be executed against every file
* loaded.</p>
*/ */
protected abstract FileFilter getFileFilter(); protected abstract FileFilter getFileFilter();
/** /**
* Initializes the file type analyzer. * Initializes the file type analyzer.
* *
* @throws Exception thrown if there is an exception during initialization * @throws InitializationException thrown if there is an exception during
* initialization
*/ */
protected abstract void initializeFileTypeAnalyzer() throws Exception; protected abstract void initializeFileTypeAnalyzer() throws InitializationException;
/** /**
* Analyzes a given dependency. If the dependency is an archive, such as a WAR or EAR, the contents are extracted, scanned, * Analyzes a given dependency. If the dependency is an archive, such as a
* and added to the list of dependencies within the engine. * WAR or EAR, the contents are extracted, scanned, and added to the list of
* dependencies within the engine.
* *
* @param dependency the dependency to analyze * @param dependency the dependency to analyze
* @param engine the engine scanning * @param engine the engine scanning
@@ -143,10 +151,11 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen
/** /**
* Initializes the analyzer. * Initializes the analyzer.
* *
* @throws Exception thrown if there is an exception during initialization * @throws InitializationException thrown if there is an exception during
* initialization
*/ */
@Override @Override
public final void initialize() throws Exception { public final void initialize() throws InitializationException {
if (filesMatched) { if (filesMatched) {
initializeFileTypeAnalyzer(); initializeFileTypeAnalyzer();
} else { } else {
@@ -170,8 +179,9 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen
} }
/** /**
* Analyzes a given dependency. If the dependency is an archive, such as a WAR or EAR, the contents are extracted, scanned, * Analyzes a given dependency. If the dependency is an archive, such as a
* and added to the list of dependencies within the engine. * WAR or EAR, the contents are extracted, scanned, and added to the list of
* dependencies within the engine.
* *
* @param dependency the dependency to analyze * @param dependency the dependency to analyze
* @param engine the engine scanning * @param engine the engine scanning
@@ -203,9 +213,8 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen
//<editor-fold defaultstate="collapsed" desc="Static utility methods"> //<editor-fold defaultstate="collapsed" desc="Static utility methods">
/** /**
* <p> * <p>
* Utility method to help in the creation of the extensions set. This constructs a new Set that can be used in a final static * Utility method to help in the creation of the extensions set. This
* declaration.</p> * constructs a new Set that can be used in a final static declaration.</p>
* <p/>
* <p> * <p>
* This implementation was copied from * This implementation was copied from
* http://stackoverflow.com/questions/2041778/initialize-java-hashset-values-by-construction</p> * http://stackoverflow.com/questions/2041778/initialize-java-hashset-values-by-construction</p>

View File

@@ -25,18 +25,21 @@ import java.net.URL;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.owasp.dependencycheck.suppression.SuppressionParseException; import org.owasp.dependencycheck.exception.InitializationException;
import org.owasp.dependencycheck.suppression.SuppressionParser; import org.owasp.dependencycheck.xml.suppression.SuppressionParseException;
import org.owasp.dependencycheck.suppression.SuppressionRule; import org.owasp.dependencycheck.xml.suppression.SuppressionParser;
import org.owasp.dependencycheck.xml.suppression.SuppressionRule;
import org.owasp.dependencycheck.utils.DownloadFailedException; import org.owasp.dependencycheck.utils.DownloadFailedException;
import org.owasp.dependencycheck.utils.Downloader; import org.owasp.dependencycheck.utils.Downloader;
import org.owasp.dependencycheck.utils.FileUtils; import org.owasp.dependencycheck.utils.FileUtils;
import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.Settings;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
/** /**
* Abstract base suppression analyzer that contains methods for parsing the suppression xml file. * Abstract base suppression analyzer that contains methods for parsing the
* suppression xml file.
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
@@ -61,12 +64,16 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
/** /**
* The initialize method loads the suppression XML file. * The initialize method loads the suppression XML file.
* *
* @throws Exception thrown if there is an exception * @throws InitializationException thrown if there is an exception
*/ */
@Override @Override
public void initialize() throws Exception { public void initialize() throws InitializationException {
super.initialize(); super.initialize();
loadSuppressionData(); try {
loadSuppressionData();
} catch (SuppressionParseException ex) {
throw new InitializationException("Error initializing the suppression analyzer", ex);
}
} }
/** /**
@@ -102,8 +109,8 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
File file = null; File file = null;
try { try {
rules = parser.parseSuppressionRules(this.getClass().getClassLoader().getResourceAsStream("dependencycheck-base-suppression.xml")); rules = parser.parseSuppressionRules(this.getClass().getClassLoader().getResourceAsStream("dependencycheck-base-suppression.xml"));
} catch (SuppressionParseException ex) { } catch (SAXException ex) {
LOGGER.debug("Unable to parse the base suppression data file", ex); throw new SuppressionParseException("Unable to parse the base suppression data file", ex);
} }
final String suppressionFilePath = Settings.getString(Settings.KEYS.SUPPRESSION_FILE); final String suppressionFilePath = Settings.getString(Settings.KEYS.SUPPRESSION_FILE);
if (suppressionFilePath == null) { if (suppressionFilePath == null) {
@@ -123,29 +130,37 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
} }
} else { } else {
file = new File(suppressionFilePath); file = new File(suppressionFilePath);
InputStream suppressionsFromClasspath = null;
if (!file.exists()) { if (!file.exists()) {
final InputStream suppressionsFromClasspath = this.getClass().getClassLoader().getResourceAsStream(suppressionFilePath); try {
if (suppressionsFromClasspath != null) { suppressionsFromClasspath = this.getClass().getClassLoader().getResourceAsStream(suppressionFilePath);
deleteTempFile = true; if (suppressionsFromClasspath != null) {
file = FileUtils.getTempFile("suppression", "xml"); deleteTempFile = true;
try { file = FileUtils.getTempFile("suppression", "xml");
org.apache.commons.io.FileUtils.copyInputStreamToFile(suppressionsFromClasspath, file); try {
} catch (IOException ex) { org.apache.commons.io.FileUtils.copyInputStreamToFile(suppressionsFromClasspath, file);
throwSuppressionParseException("Unable to locate suppressions file in classpath", ex); } catch (IOException ex) {
throwSuppressionParseException("Unable to locate suppressions file in classpath", ex);
}
}
} finally {
if (suppressionsFromClasspath != null) {
try {
suppressionsFromClasspath.close();
} catch (IOException ex) {
LOGGER.debug("Failed to close stream", ex);
}
} }
} }
} }
} }
if (file != null) { if (file != null) {
try { try {
//rules = parser.parseSuppressionRules(file);
rules.addAll(parser.parseSuppressionRules(file)); rules.addAll(parser.parseSuppressionRules(file));
LOGGER.debug("{} suppression rules were loaded.", rules.size()); LOGGER.debug("{} suppression rules were loaded.", rules.size());
} catch (SuppressionParseException ex) { } catch (SuppressionParseException ex) {
LOGGER.warn("Unable to parse suppression xml file '{}'", file.getPath()); LOGGER.warn("Unable to parse suppression xml file '{}'", file.getPath());
LOGGER.warn(ex.getMessage()); LOGGER.warn(ex.getMessage());
LOGGER.debug("", ex);
throw ex; throw ex;
} }
} }
@@ -167,7 +182,8 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
* *
* @param message the exception message * @param message the exception message
* @param exception the cause of the exception * @param exception the cause of the exception
* @throws SuppressionParseException throws the generated SuppressionParseException * @throws SuppressionParseException throws the generated
* SuppressionParseException
*/ */
private void throwSuppressionParseException(String message, Exception exception) throws SuppressionParseException { private void throwSuppressionParseException(String message, Exception exception) throws SuppressionParseException {
LOGGER.warn(message); LOGGER.warn(message);

View File

@@ -29,7 +29,7 @@ public enum AnalysisPhase {
*/ */
INITIAL, INITIAL,
/** /**
* Pre information collection phase * Pre information collection phase.
*/ */
PRE_INFORMATION_COLLECTION, PRE_INFORMATION_COLLECTION,
/** /**

View File

@@ -20,24 +20,28 @@ package org.owasp.dependencycheck.analyzer;
import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.exception.InitializationException;
/** /**
* An interface that defines an Analyzer that is used to identify Dependencies. An analyzer will collect information * An interface that defines an Analyzer that is used to identify Dependencies.
* about the dependency in the form of Evidence. * An analyzer will collect information about the dependency in the form of
* Evidence.
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
public interface Analyzer { public interface Analyzer {
/** /**
* Analyzes the given dependency. The analysis could be anything from identifying an Identifier for the dependency, * Analyzes the given dependency. The analysis could be anything from
* to finding vulnerabilities, etc. Additionally, if the analyzer collects enough information to add a description * identifying an Identifier for the dependency, to finding vulnerabilities,
* or license information for the dependency it should be added. * etc. Additionally, if the analyzer collects enough information to add a
* description or license information for the dependency it should be added.
* *
* @param dependency a dependency to analyze. * @param dependency a dependency to analyze.
* @param engine the engine that is scanning the dependencies - this is useful if we need to check other * @param engine the engine that is scanning the dependencies - this is
* dependencies * useful if we need to check other dependencies
* @throws AnalysisException is thrown if there is an error analyzing the dependency file * @throws AnalysisException is thrown if there is an error analyzing the
* dependency file
*/ */
void analyze(Dependency dependency, Engine engine) throws AnalysisException; void analyze(Dependency dependency, Engine engine) throws AnalysisException;
@@ -56,14 +60,17 @@ public interface Analyzer {
AnalysisPhase getAnalysisPhase(); AnalysisPhase getAnalysisPhase();
/** /**
* The initialize method is called (once) prior to the analyze method being called on all of the dependencies. * The initialize method is called (once) prior to the analyze method being
* called on all of the dependencies.
* *
* @throws Exception is thrown if an exception occurs initializing the analyzer. * @throws InitializationException is thrown if an exception occurs
* initializing the analyzer.
*/ */
void initialize() throws Exception; void initialize() throws InitializationException;
/** /**
* The close method is called after all of the dependencies have been analyzed. * The close method is called after all of the dependencies have been
* analyzed.
* *
* @throws Exception is thrown if an exception occurs closing the analyzer. * @throws Exception is thrown if an exception occurs closing the analyzer.
*/ */

View File

@@ -17,8 +17,13 @@
*/ */
package org.owasp.dependencycheck.analyzer; package org.owasp.dependencycheck.analyzer;
import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import org.owasp.dependencycheck.utils.InvalidSettingException;
import org.owasp.dependencycheck.utils.Settings;
import org.slf4j.LoggerFactory;
/** /**
* The Analyzer Service Loader. This class loads all services that implement * The Analyzer Service Loader. This class loads all services that implement
@@ -27,11 +32,15 @@ import java.util.ServiceLoader;
* @author Jeremy Long * @author Jeremy Long
*/ */
public class AnalyzerService { public class AnalyzerService {
/**
* The Logger for use throughout the class.
*/
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(AnalyzerService.class);
/** /**
* The service loader for analyzers. * The service loader for analyzers.
*/ */
private final ServiceLoader<Analyzer> loader; private final ServiceLoader<Analyzer> service;
/** /**
* Creates a new instance of AnalyzerService. * Creates a new instance of AnalyzerService.
@@ -39,15 +48,31 @@ public class AnalyzerService {
* @param classLoader the ClassLoader to use when dynamically loading Analyzer and Update services * @param classLoader the ClassLoader to use when dynamically loading Analyzer and Update services
*/ */
public AnalyzerService(ClassLoader classLoader) { public AnalyzerService(ClassLoader classLoader) {
loader = ServiceLoader.load(Analyzer.class, classLoader); service = ServiceLoader.load(Analyzer.class, classLoader);
} }
/** /**
* Returns an Iterator for all instances of the Analyzer interface. * Returns a list of all instances of the Analyzer interface.
* *
* @return an iterator of Analyzers. * @return a list of Analyzers.
*/ */
public Iterator<Analyzer> getAnalyzers() { public List<Analyzer> getAnalyzers() {
return loader.iterator(); final List<Analyzer> analyzers = new ArrayList<Analyzer>();
final Iterator<Analyzer> iterator = service.iterator();
boolean experimentalEnabled = false;
try {
experimentalEnabled = Settings.getBoolean(Settings.KEYS.ANALYZER_EXPERIMENTAL_ENABLED, false);
} catch (InvalidSettingException ex) {
LOGGER.error("invalide experimental setting", ex);
}
while (iterator.hasNext()) {
final Analyzer a = iterator.next();
if (!experimentalEnabled && a.getClass().isAnnotationPresent(Experimental.class)) {
continue;
}
LOGGER.debug("Loaded Analyzer {}", a.getName());
analyzers.add(a);
}
return analyzers;
} }
} }

View File

@@ -49,6 +49,7 @@ import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
import org.owasp.dependencycheck.analyzer.exception.ArchiveExtractionException; import org.owasp.dependencycheck.analyzer.exception.ArchiveExtractionException;
import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.exception.InitializationException;
import org.owasp.dependencycheck.utils.FileFilterBuilder; import org.owasp.dependencycheck.utils.FileFilterBuilder;
import org.owasp.dependencycheck.utils.FileUtils; import org.owasp.dependencycheck.utils.FileUtils;
import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.Settings;
@@ -58,8 +59,8 @@ import org.slf4j.LoggerFactory;
/** /**
* <p> * <p>
* An analyzer that extracts files from archives and ensures any supported files contained within the archive are added to the * An analyzer that extracts files from archives and ensures any supported files
* dependency list.</p> * contained within the archive are added to the dependency list.</p>
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
@@ -70,7 +71,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
*/ */
private static final Logger LOGGER = LoggerFactory.getLogger(ArchiveAnalyzer.class); private static final Logger LOGGER = LoggerFactory.getLogger(ArchiveAnalyzer.class);
/** /**
* The count of directories created during analysis. This is used for creating temporary directories. * The count of directories created during analysis. This is used for
* creating temporary directories.
*/ */
private static int dirCount = 0; private static int dirCount = 0;
/** /**
@@ -78,7 +80,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
*/ */
private File tempFileLocation = null; private File tempFileLocation = null;
/** /**
* The max scan depth that the analyzer will recursively extract nested archives. * The max scan depth that the analyzer will recursively extract nested
* archives.
*/ */
private static final int MAX_SCAN_DEPTH = Settings.getInt("archive.scan.depth", 3); private static final int MAX_SCAN_DEPTH = Settings.getInt("archive.scan.depth", 3);
/** /**
@@ -100,13 +103,15 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
*/ */
private static final Set<String> ZIPPABLES = newHashSet("zip", "ear", "war", "jar", "sar", "apk", "nupkg"); private static final Set<String> ZIPPABLES = newHashSet("zip", "ear", "war", "jar", "sar", "apk", "nupkg");
/** /**
* The set of file extensions supported by this analyzer. Note for developers, any additions to this list will need to be * The set of file extensions supported by this analyzer. Note for
* explicitly handled in {@link #extractFiles(File, File, Engine)}. * developers, any additions to this list will need to be explicitly handled
* in {@link #extractFiles(File, File, Engine)}.
*/ */
private static final Set<String> EXTENSIONS = newHashSet("tar", "gz", "tgz", "bz2", "tbz2"); private static final Set<String> EXTENSIONS = newHashSet("tar", "gz", "tgz", "bz2", "tbz2");
/** /**
* Detects files with extensions to remove from the engine's collection of dependencies. * Detects files with extensions to remove from the engine's collection of
* dependencies.
*/ */
private static final FileFilter REMOVE_FROM_ANALYSIS = FileFilterBuilder.newInstance().addExtensions("zip", "tar", "gz", "tgz", "bz2", "tbz2") private static final FileFilter REMOVE_FROM_ANALYSIS = FileFilterBuilder.newInstance().addExtensions("zip", "tar", "gz", "tgz", "bz2", "tbz2")
.build(); .build();
@@ -157,7 +162,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
//</editor-fold> //</editor-fold>
/** /**
* 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
*/ */
@@ -169,26 +175,36 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
/** /**
* The initialize method does nothing for this Analyzer. * The initialize method does nothing for this Analyzer.
* *
* @throws Exception is thrown if there is an exception deleting or creating temporary files * @throws InitializationException is thrown if there is an exception
* deleting or creating temporary files
*/ */
@Override @Override
public void initializeFileTypeAnalyzer() throws Exception { public void initializeFileTypeAnalyzer() throws InitializationException {
final File baseDir = Settings.getTempDirectory(); try {
tempFileLocation = File.createTempFile("check", "tmp", baseDir); final File baseDir = Settings.getTempDirectory();
if (!tempFileLocation.delete()) { tempFileLocation = File.createTempFile("check", "tmp", baseDir);
final String msg = String.format("Unable to delete temporary file '%s'.", tempFileLocation.getAbsolutePath()); if (!tempFileLocation.delete()) {
throw new AnalysisException(msg); setEnabled(false);
} final String msg = String.format("Unable to delete temporary file '%s'.", tempFileLocation.getAbsolutePath());
if (!tempFileLocation.mkdirs()) { throw new InitializationException(msg);
final String msg = String.format("Unable to create directory '%s'.", tempFileLocation.getAbsolutePath()); }
throw new AnalysisException(msg); if (!tempFileLocation.mkdirs()) {
setEnabled(false);
final String msg = String.format("Unable to create directory '%s'.", tempFileLocation.getAbsolutePath());
throw new InitializationException(msg);
}
} catch (IOException ex) {
setEnabled(false);
throw new InitializationException("Unable to create a temporary file", ex);
} }
} }
/** /**
* The close method deletes any temporary files and directories created during analysis. * The close method deletes any temporary files and directories created
* during analysis.
* *
* @throws Exception thrown if there is an exception deleting temporary files * @throws Exception thrown if there is an exception deleting temporary
* files
*/ */
@Override @Override
public void close() throws Exception { public void close() throws Exception {
@@ -205,8 +221,9 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* Analyzes a given dependency. If the dependency is an archive, such as a WAR or EAR, the contents are extracted, scanned, * Analyzes a given dependency. If the dependency is an archive, such as a
* and added to the list of dependencies within the engine. * WAR or EAR, the contents are extracted, scanned, and added to the list of
* dependencies within the engine.
* *
* @param dependency the dependency to analyze * @param dependency the dependency to analyze
* @param engine the engine scanning * @param engine the engine scanning
@@ -249,7 +266,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* If a zip file was identified as a possible JAR, this method will add the zip to the list of dependencies. * If a zip file was identified as a possible JAR, this method will add the
* zip to the list of dependencies.
* *
* @param dependency the zip file * @param dependency the zip file
* @param engine the engine * @param engine the engine
@@ -339,6 +357,12 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
*/ */
private void extractFiles(File archive, File destination, Engine engine) throws AnalysisException { private void extractFiles(File archive, File destination, Engine engine) throws AnalysisException {
if (archive != null && destination != null) { if (archive != null && destination != null) {
String archiveExt = FileUtils.getFileExtension(archive.getName());
if (archiveExt == null) {
return;
}
archiveExt = archiveExt.toLowerCase();
FileInputStream fis; FileInputStream fis;
try { try {
fis = new FileInputStream(archive); fis = new FileInputStream(archive);
@@ -346,10 +370,11 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
LOGGER.debug("", ex); LOGGER.debug("", ex);
throw new AnalysisException("Archive file was not found.", ex); throw new AnalysisException("Archive file was not found.", ex);
} }
final String archiveExt = FileUtils.getFileExtension(archive.getName()).toLowerCase();
try { try {
if (ZIPPABLES.contains(archiveExt)) { if (ZIPPABLES.contains(archiveExt)) {
extractArchive(new ZipArchiveInputStream(new BufferedInputStream(fis)), destination, engine); final BufferedInputStream in = new BufferedInputStream(fis);
ensureReadableJar(archiveExt, in);
extractArchive(new ZipArchiveInputStream(in), destination, engine);
} else if ("tar".equals(archiveExt)) { } else if ("tar".equals(archiveExt)) {
extractArchive(new TarArchiveInputStream(new BufferedInputStream(fis)), destination, engine); extractArchive(new TarArchiveInputStream(new BufferedInputStream(fis)), destination, engine);
} else if ("gz".equals(archiveExt) || "tgz".equals(archiveExt)) { } else if ("gz".equals(archiveExt) || "tgz".equals(archiveExt)) {
@@ -377,13 +402,65 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
} }
} }
/**
* Checks if the file being scanned is a JAR that begins with '#!/bin' which
* indicates it is a fully executable jar. If a fully executable JAR is
* identified the input stream will be advanced to the start of the actual
* JAR file ( skipping the script).
*
* @see
* <a href="http://docs.spring.io/spring-boot/docs/1.3.0.BUILD-SNAPSHOT/reference/htmlsingle/#deployment-install">Installing
* Spring Boot Applications</a>
* @param archiveExt the file extension
* @param in the input stream
* @throws IOException thrown if there is an error reading the stream
*/
private void ensureReadableJar(final String archiveExt, BufferedInputStream in) throws IOException {
if ("jar".equals(archiveExt) && in.markSupported()) {
in.mark(7);
final byte[] b = new byte[7];
final int read = in.read(b);
if (read == 7
&& b[0] == '#'
&& b[1] == '!'
&& b[2] == '/'
&& b[3] == 'b'
&& b[4] == 'i'
&& b[5] == 'n'
&& b[6] == '/') {
boolean stillLooking = true;
int chr, nxtChr;
while (stillLooking && (chr = in.read()) != -1) {
if (chr == '\n' || chr == '\r') {
in.mark(4);
if ((chr = in.read()) != -1) {
if (chr == 'P' && (chr = in.read()) != -1) {
if (chr == 'K' && (chr = in.read()) != -1) {
if ((chr == 3 || chr == 5 || chr == 7) && (nxtChr = in.read()) != -1) {
if (nxtChr == chr + 1) {
stillLooking = false;
in.reset();
}
}
}
}
}
}
}
} else {
in.reset();
}
}
}
/** /**
* Extracts files from an archive. * Extracts files from an archive.
* *
* @param input the archive to extract files from * @param input the archive to extract files from
* @param destination the location to write the files too * @param destination the location to write the files too
* @param engine the dependency-check engine * @param engine the dependency-check engine
* @throws ArchiveExtractionException thrown if there is an exception extracting files from the archive * @throws ArchiveExtractionException thrown if there is an exception
* extracting files from the archive
*/ */
private void extractArchive(ArchiveInputStream input, File destination, Engine engine) throws ArchiveExtractionException { private void extractArchive(ArchiveInputStream input, File destination, Engine engine) throws ArchiveExtractionException {
ArchiveEntry entry; ArchiveEntry entry;
@@ -442,7 +519,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
* *
* @param inputStream the compressed file * @param inputStream the compressed file
* @param outputFile the location to write the decompressed file * @param outputFile the location to write the decompressed file
* @throws ArchiveExtractionException thrown if there is an exception decompressing the file * @throws ArchiveExtractionException thrown if there is an exception
* decompressing the file
*/ */
private void decompressFile(CompressorInputStream inputStream, File outputFile) throws ArchiveExtractionException { private void decompressFile(CompressorInputStream inputStream, File outputFile) throws ArchiveExtractionException {
LOGGER.debug("Decompressing '{}'", outputFile.getPath()); LOGGER.debug("Decompressing '{}'", outputFile.getPath());
@@ -462,7 +540,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* Close the given {@link Closeable} instance, ignoring nulls, and logging any thrown {@link IOException}. * Close the given {@link Closeable} instance, ignoring nulls, and logging
* any thrown {@link IOException}.
* *
* @param closeable to be closed * @param closeable to be closed
*/ */

View File

@@ -43,9 +43,13 @@ import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory; import javax.xml.xpath.XPathFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import org.owasp.dependencycheck.exception.InitializationException;
import org.apache.commons.lang3.SystemUtils;
/** /**
* Analyzer for getting company, product, and version information from a .NET assembly. * Analyzer for getting company, product, and version information from a .NET
* assembly.
* *
* @author colezlaw * @author colezlaw
* *
@@ -82,18 +86,19 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
* *
* @return the list of arguments to begin populating the ProcessBuilder * @return the list of arguments to begin populating the ProcessBuilder
*/ */
private List<String> buildArgumentList() { protected List<String> buildArgumentList() {
// Use file.separator as a wild guess as to whether this is Windows // Use file.separator as a wild guess as to whether this is Windows
final List<String> args = new ArrayList<String>(); final List<String> args = new ArrayList<String>();
if (!"\\".equals(System.getProperty("file.separator"))) { if (!SystemUtils.IS_OS_WINDOWS) {
if (Settings.getString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH) != null) { if (Settings.getString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH) != null) {
args.add(Settings.getString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH)); args.add(Settings.getString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH));
} else { } else if (isInPath("mono")) {
args.add("mono"); args.add("mono");
} else {
return null;
} }
} }
args.add(grokAssemblyExe.getPath()); args.add(grokAssemblyExe.getPath());
return args; return args;
} }
@@ -113,6 +118,10 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
} }
final List<String> args = buildArgumentList(); final List<String> args = buildArgumentList();
if (args == null) {
LOGGER.warn("Assembly Analyzer was unable to execute");
return;
}
args.add(dependency.getActualFilePath()); args.add(dependency.getActualFilePath());
final ProcessBuilder pb = new ProcessBuilder(args); final ProcessBuilder pb = new ProcessBuilder(args);
Document doc = null; Document doc = null;
@@ -178,13 +187,20 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* Initialize the analyzer. In this case, extract GrokAssembly.exe to a temporary location. * Initialize the analyzer. In this case, extract GrokAssembly.exe to a
* temporary location.
* *
* @throws Exception if anything goes wrong * @throws InitializationException thrown if anything goes wrong
*/ */
@Override @Override
public void initializeFileTypeAnalyzer() throws Exception { public void initializeFileTypeAnalyzer() throws InitializationException {
final File tempFile = File.createTempFile("GKA", ".exe", Settings.getTempDirectory()); final File tempFile;
try {
tempFile = File.createTempFile("GKA", ".exe", Settings.getTempDirectory());
} catch (IOException ex) {
setEnabled(false);
throw new InitializationException("Unable to create temporary file for the assembly analyzerr", ex);
}
FileOutputStream fos = null; FileOutputStream fos = null;
InputStream is = null; InputStream is = null;
try { try {
@@ -193,13 +209,11 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
IOUtils.copy(is, fos); IOUtils.copy(is, fos);
grokAssemblyExe = tempFile; grokAssemblyExe = tempFile;
// Set the temp file to get deleted when we're done
grokAssemblyExe.deleteOnExit();
LOGGER.debug("Extracted GrokAssembly.exe to {}", grokAssemblyExe.getPath()); LOGGER.debug("Extracted GrokAssembly.exe to {}", grokAssemblyExe.getPath());
} catch (IOException ioe) { } catch (IOException ioe) {
this.setEnabled(false); this.setEnabled(false);
LOGGER.warn("Could not extract GrokAssembly.exe: {}", ioe.getMessage()); LOGGER.warn("Could not extract GrokAssembly.exe: {}", ioe.getMessage());
throw new AnalysisException("Could not extract GrokAssembly.exe", ioe); throw new InitializationException("Could not extract GrokAssembly.exe", ioe);
} finally { } finally {
if (fos != null) { if (fos != null) {
try { try {
@@ -219,6 +233,22 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
// Now, need to see if GrokAssembly actually runs from this location. // Now, need to see if GrokAssembly actually runs from this location.
final List<String> args = buildArgumentList(); final List<String> args = buildArgumentList();
//TODO this creaes an "unreported" error - if someone doesn't look
// at the command output this could easily be missed (especially in an
// Ant or Mmaven build.
//
// We need to create a non-fatal warning error type that will
// get added to the report.
//TOOD this idea needs to get replicated to the bundle audit analyzer.
if (args == null) {
setEnabled(false);
LOGGER.error("----------------------------------------------------");
LOGGER.error(".NET Assembly Analyzer could not be initialized and at least one "
+ "'exe' or 'dll' was scanned. The 'mono' executale could not be found on "
+ "the path; either disable the Assembly Analyzer or configure the path mono.");
LOGGER.error("----------------------------------------------------");
return;
}
try { try {
final ProcessBuilder pb = new ProcessBuilder(args); final ProcessBuilder pb = new ProcessBuilder(args);
final Process p = pb.start(); final Process p = pb.start();
@@ -232,21 +262,25 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
LOGGER.warn("An error occurred with the .NET AssemblyAnalyzer, please see the log for more details."); LOGGER.warn("An error occurred with the .NET AssemblyAnalyzer, please see the log for more details.");
LOGGER.debug("GrokAssembly.exe is not working properly"); LOGGER.debug("GrokAssembly.exe is not working properly");
grokAssemblyExe = null; grokAssemblyExe = null;
this.setEnabled(false); setEnabled(false);
throw new AnalysisException("Could not execute .NET AssemblyAnalyzer"); throw new InitializationException("Could not execute .NET AssemblyAnalyzer");
} }
} catch (InitializationException e) {
setEnabled(false);
throw e;
} catch (Throwable e) { } catch (Throwable e) {
if (e instanceof AnalysisException) { LOGGER.warn("An error occurred with the .NET AssemblyAnalyzer;\n"
throw (AnalysisException) e; + "this can be ignored unless you are scanning .NET DLLs. Please see the log for more details.");
} else { LOGGER.debug("Could not execute GrokAssembly {}", e.getMessage());
LOGGER.warn("An error occurred with the .NET AssemblyAnalyzer;\n" setEnabled(false);
+ "this can be ignored unless you are scanning .NET DLLs. Please see the log for more details."); throw new InitializationException("An error occurred with the .NET AssemblyAnalyzer", e);
LOGGER.debug("Could not execute GrokAssembly {}", e.getMessage()); }
this.setEnabled(false); try {
throw new AnalysisException("An error occured with the .NET AssemblyAnalyzer", e); builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
} } catch (ParserConfigurationException ex) {
setEnabled(false);
throw new InitializationException("Error initializing the assembly analyzer", ex);
} }
builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
} }
/** /**
@@ -259,10 +293,12 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
super.close(); super.close();
try { try {
if (grokAssemblyExe != null && !grokAssemblyExe.delete()) { if (grokAssemblyExe != null && !grokAssemblyExe.delete()) {
LOGGER.debug("Unable to delete temporary GrokAssembly.exe; attempting delete on exit");
grokAssemblyExe.deleteOnExit(); grokAssemblyExe.deleteOnExit();
} }
} catch (SecurityException se) { } catch (SecurityException se) {
LOGGER.debug("Can't delete temporary GrokAssembly.exe"); LOGGER.debug("Can't delete temporary GrokAssembly.exe");
grokAssemblyExe.deleteOnExit();
} }
} }
@@ -298,7 +334,8 @@ public class AssemblyAnalyzer 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
*/ */
@@ -306,4 +343,29 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
protected String getAnalyzerEnabledSettingKey() { protected String getAnalyzerEnabledSettingKey() {
return Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED; return Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED;
} }
/**
* Tests to see if a file is in the system path. <b>Note</b> - the current
* implementation only works on non-windows platforms. For purposes of the
* AssemblyAnalyzer this is okay as this is only needed on Mac/*nix.
*
* @param file the executable to look for
* @return <code>true</code> if the file exists; otherwise
* <code>false</code>
*/
private boolean isInPath(String file) {
final ProcessBuilder pb = new ProcessBuilder("which", file);
try {
final Process proc = pb.start();
final int retCode = proc.waitFor();
if (retCode == 0) {
return true;
}
} catch (IOException ex) {
LOGGER.debug("Path seach failed for " + file);
} catch (InterruptedException ex) {
LOGGER.debug("Path seach failed for " + file);
}
return false;
}
} }

View File

@@ -30,18 +30,23 @@ import org.owasp.dependencycheck.utils.UrlStringUtils;
import java.io.File; import java.io.File;
import java.io.FileFilter; import java.io.FileFilter;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.owasp.dependencycheck.exception.InitializationException;
/** /**
* Used to analyze Autoconf input files named configure.ac or configure.in. Files simply named "configure" are also analyzed, * Used to analyze Autoconf input files named configure.ac or configure.in.
* assuming they are generated by Autoconf, and contain certain special package descriptor variables. * Files simply named "configure" are also analyzed, assuming they are generated
* by Autoconf, and contain certain special package descriptor variables.
* *
* @author Dale Visser <dvisser@ida.org> * @author Dale Visser
* @see <a href="https://www.gnu.org/software/autoconf/">Autoconf - GNU Project - Free Software Foundation (FSF)</a> * @see <a href="https://www.gnu.org/software/autoconf/">Autoconf - GNU Project
* - Free Software Foundation (FSF)</a>
*/ */
@Experimental
public class AutoconfAnalyzer extends AbstractFileTypeAnalyzer { public class AutoconfAnalyzer extends AbstractFileTypeAnalyzer {
/** /**
@@ -140,7 +145,8 @@ public class AutoconfAnalyzer 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
*/ */
@@ -220,14 +226,12 @@ public class AutoconfAnalyzer extends AbstractFileTypeAnalyzer {
*/ */
private String getFileContents(final File actualFile) private String getFileContents(final File actualFile)
throws AnalysisException { throws AnalysisException {
String contents = "";
try { try {
contents = FileUtils.readFileToString(actualFile).trim(); return FileUtils.readFileToString(actualFile, Charset.defaultCharset()).trim();
} catch (IOException e) { } catch (IOException e) {
throw new AnalysisException( throw new AnalysisException(
"Problem occurred while reading dependency file.", e); "Problem occurred while reading dependency file.", e);
} }
return contents;
} }
/** /**
@@ -270,10 +274,11 @@ public class AutoconfAnalyzer extends AbstractFileTypeAnalyzer {
/** /**
* Initializes the file type analyzer. * Initializes the file type analyzer.
* *
* @throws Exception thrown if there is an exception during initialization * @throws InitializationException thrown if there is an exception during
* initialization
*/ */
@Override @Override
protected void initializeFileTypeAnalyzer() throws Exception { protected void initializeFileTypeAnalyzer() throws InitializationException {
// No initialization needed. // No initialization needed.
} }
} }

View File

@@ -32,23 +32,29 @@ import org.slf4j.LoggerFactory;
import java.io.File; import java.io.File;
import java.io.FileFilter; import java.io.FileFilter;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.owasp.dependencycheck.exception.InitializationException;
/** /**
* <p> * <p>
* Used to analyze CMake build files, and collect information that can be used to determine the associated CPE.</p> * Used to analyze CMake build files, and collect information that can be used
* <p/> * to determine the associated CPE.</p>
* <p> * <p>
* Note: This analyzer catches straightforward invocations of the project command, plus some other observed patterns of version * Note: This analyzer catches straightforward invocations of the project
* inclusion in real CMake projects. Many projects make use of older versions of CMake and/or use custom "homebrew" ways to insert * command, plus some other observed patterns of version inclusion in real CMake
* version information. Hopefully as the newer CMake call pattern grows in usage, this analyzer allow more CPEs to be * projects. Many projects make use of older versions of CMake and/or use custom
* "homebrew" ways to insert version information. Hopefully as the newer CMake
* call pattern grows in usage, this analyzer allow more CPEs to be
* identified.</p> * identified.</p>
* *
* @author Dale Visser <dvisser@ida.org> * @author Dale Visser
*/ */
@Experimental
public class CMakeAnalyzer extends AbstractFileTypeAnalyzer { public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
/** /**
@@ -133,10 +139,10 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
/** /**
* No-op initializer implementation. * No-op initializer implementation.
* *
* @throws Exception never thrown * @throws InitializationException never thrown
*/ */
@Override @Override
protected void initializeFileTypeAnalyzer() throws Exception { protected void initializeFileTypeAnalyzer() throws InitializationException {
// Nothing to do here. // Nothing to do here.
} }
@@ -145,7 +151,8 @@ public class CMakeAnalyzer 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)
@@ -156,7 +163,7 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
dependency.setDisplayFileName(String.format("%s%c%s", parentName, File.separatorChar, name)); dependency.setDisplayFileName(String.format("%s%c%s", parentName, File.separatorChar, name));
String contents; String contents;
try { try {
contents = FileUtils.readFileToString(file).trim(); contents = FileUtils.readFileToString(file, Charset.defaultCharset()).trim();
} catch (IOException e) { } catch (IOException e) {
throw new AnalysisException( throw new AnalysisException(
"Problem occurred while reading dependency file.", e); "Problem occurred while reading dependency file.", e);
@@ -181,13 +188,17 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* Extracts the version information from the contents. If more then one version is found additional dependencies are added to * Extracts the version information from the contents. If more then one
* the dependency list. * version is found additional dependencies are added to the dependency
* list.
* *
* @param dependency the dependency being analyzed * @param dependency the dependency being analyzed
* @param engine the dependency-check engine * @param engine the dependency-check engine
* @param contents the version information * @param contents the version information
*/ */
@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(
value = "DM_DEFAULT_ENCODING",
justification = "Default encoding is only used if UTF-8 is not available")
private void analyzeSetVersionCommand(Dependency dependency, Engine engine, String contents) { private void analyzeSetVersionCommand(Dependency dependency, Engine engine, String contents) {
Dependency currentDep = dependency; Dependency currentDep = dependency;
@@ -212,8 +223,13 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
final String filePath = String.format("%s:%s", dependency.getFilePath(), product); final String filePath = String.format("%s:%s", dependency.getFilePath(), product);
currentDep.setFilePath(filePath); currentDep.setFilePath(filePath);
// prevents coalescing into the dependency provided by engine byte[] path;
currentDep.setSha1sum(Checksum.getHex(sha1.digest(filePath.getBytes()))); try {
path = filePath.getBytes("UTF-8");
} catch (UnsupportedEncodingException ex) {
path = filePath.getBytes();
}
currentDep.setSha1sum(Checksum.getHex(sha1.digest(path)));
engine.getDependencies().add(currentDep); engine.getDependencies().add(currentDep);
} }
final String source = currentDep.getDisplayFileName(); final String source = currentDep.getDisplayFileName();

View File

@@ -25,6 +25,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import org.apache.commons.lang3.builder.CompareToBuilder;
import org.apache.lucene.document.Document; import org.apache.lucene.document.Document;
import org.apache.lucene.index.CorruptIndexException; import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.queryparser.classic.ParseException; import org.apache.lucene.queryparser.classic.ParseException;
@@ -45,14 +46,16 @@ import org.owasp.dependencycheck.dependency.Evidence;
import org.owasp.dependencycheck.dependency.EvidenceCollection; import org.owasp.dependencycheck.dependency.EvidenceCollection;
import org.owasp.dependencycheck.dependency.Identifier; import org.owasp.dependencycheck.dependency.Identifier;
import org.owasp.dependencycheck.dependency.VulnerableSoftware; import org.owasp.dependencycheck.dependency.VulnerableSoftware;
import org.owasp.dependencycheck.exception.InitializationException;
import org.owasp.dependencycheck.utils.DependencyVersion; import org.owasp.dependencycheck.utils.DependencyVersion;
import org.owasp.dependencycheck.utils.DependencyVersionUtil; import org.owasp.dependencycheck.utils.DependencyVersionUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* CPEAnalyzer is a utility class that takes a project dependency and attempts to discern if there is an associated CPE. It uses * CPEAnalyzer is a utility class that takes a project dependency and attempts
* the evidence contained within the dependency to search the Lucene index. * to discern if there is an associated CPE. It uses the evidence contained
* within the dependency to search the Lucene index.
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
@@ -71,15 +74,18 @@ public class CPEAnalyzer implements Analyzer {
*/ */
static final String WEIGHTING_BOOST = "^5"; static final String WEIGHTING_BOOST = "^5";
/** /**
* A string representation of a regular expression defining characters utilized within the CPE Names. * A string representation of a regular expression defining characters
* utilized within the CPE Names.
*/ */
static final String CLEANSE_CHARACTER_RX = "[^A-Za-z0-9 ._-]"; static final String CLEANSE_CHARACTER_RX = "[^A-Za-z0-9 ._-]";
/** /**
* A string representation of a regular expression used to remove all but alpha characters. * A string representation of a regular expression used to remove all but
* alpha characters.
*/ */
static final String CLEANSE_NONALPHA_RX = "[^A-Za-z]*"; static final String CLEANSE_NONALPHA_RX = "[^A-Za-z]*";
/** /**
* The additional size to add to a new StringBuilder to account for extra data that will be written into the string. * The additional size to add to a new StringBuilder to account for extra
* data that will be written into the string.
*/ */
static final int STRING_BUILDER_BUFFER = 20; static final int STRING_BUILDER_BUFFER = 20;
/** /**
@@ -119,32 +125,44 @@ public class CPEAnalyzer implements Analyzer {
/** /**
* Creates the CPE Lucene Index. * Creates the CPE Lucene Index.
* *
* @throws Exception is thrown if there is an issue opening the index. * @throws InitializationException is thrown if there is an issue opening
* the index.
*/ */
@Override @Override
public void initialize() throws Exception { public void initialize() throws InitializationException {
this.open(); try {
this.open();
} catch (IOException ex) {
LOGGER.debug("Exception initializing the Lucene Index", ex);
throw new InitializationException("An exception occurred initializing the Lucene Index", ex);
} catch (DatabaseException ex) {
LOGGER.debug("Exception accessing the database", ex);
throw new InitializationException("An exception occurred accessing the database", ex);
}
} }
/** /**
* Opens the data source. * Opens the data source.
* *
* @throws IOException when the Lucene directory to be queried does not exist or is corrupt. * @throws IOException when the Lucene directory to be queried does not
* @throws DatabaseException when the database throws an exception. This usually occurs when the database is in use by another * exist or is corrupt.
* process. * @throws DatabaseException when the database throws an exception. This
* usually occurs when the database is in use by another process.
*/ */
public void open() throws IOException, DatabaseException { public void open() throws IOException, DatabaseException {
cve = new CveDB(); if (!isOpen()) {
cve.open(); cve = new CveDB();
cpe = CpeMemoryIndex.getInstance(); cve.open();
try { cpe = CpeMemoryIndex.getInstance();
LOGGER.info("Creating the CPE Index"); try {
final long creationStart = System.currentTimeMillis(); LOGGER.info("Creating the CPE Index");
cpe.open(cve); final long creationStart = System.currentTimeMillis();
LOGGER.info("CPE Index Created ({} ms)", System.currentTimeMillis() - creationStart); cpe.open(cve);
} catch (IndexException ex) { LOGGER.info("CPE Index Created ({} ms)", System.currentTimeMillis() - creationStart);
LOGGER.debug("IndexException", ex); } catch (IndexException ex) {
throw new DatabaseException(ex); LOGGER.debug("IndexException", ex);
throw new DatabaseException(ex);
}
} }
} }
@@ -168,8 +186,9 @@ public class CPEAnalyzer implements Analyzer {
} }
/** /**
* Searches the data store of CPE entries, trying to identify the CPE for the given dependency based on the evidence contained * Searches the data store of CPE entries, trying to identify the CPE for
* within. The dependency passed in is updated with any identified CPE values. * the given dependency based on the evidence contained within. The
* dependency passed in is updated with any identified CPE values.
* *
* @param dependency the dependency to search for CPE entries on. * @param dependency the dependency to search for CPE entries on.
* @throws CorruptIndexException is thrown when the Lucene index is corrupt. * @throws CorruptIndexException is thrown when the Lucene index is corrupt.
@@ -190,8 +209,8 @@ public class CPEAnalyzer implements Analyzer {
LOGGER.debug("product search: {}", products); LOGGER.debug("product search: {}", products);
} }
if (!vendors.isEmpty() && !products.isEmpty()) { if (!vendors.isEmpty() && !products.isEmpty()) {
final List<IndexEntry> entries = searchCPE(vendors, products, dependency.getProductEvidence().getWeighting(), final List<IndexEntry> entries = searchCPE(vendors, products, dependency.getVendorEvidence().getWeighting(),
dependency.getVendorEvidence().getWeighting()); dependency.getProductEvidence().getWeighting());
if (entries == null) { if (entries == null) {
continue; continue;
} }
@@ -213,9 +232,10 @@ public class CPEAnalyzer implements Analyzer {
} }
/** /**
* Returns the text created by concatenating the text and the values from the EvidenceCollection (filtered for a specific * Returns the text created by concatenating the text and the values from
* confidence). This attempts to prevent duplicate terms from being added.<br/<br/> Note, if the evidence is longer then 200 * the EvidenceCollection (filtered for a specific confidence). This
* characters it will be truncated. * attempts to prevent duplicate terms from being added.<br/<br/> Note, if
* the evidence is longer then 200 characters it will be truncated.
* *
* @param text the base text. * @param text the base text.
* @param ec an EvidenceCollection * @param ec an EvidenceCollection
@@ -246,17 +266,19 @@ public class CPEAnalyzer implements Analyzer {
/** /**
* <p> * <p>
* Searches the Lucene CPE index to identify possible CPE entries associated with the supplied vendor, product, and * Searches the Lucene CPE index to identify possible CPE entries associated
* version.</p> * with the supplied vendor, product, and version.</p>
* *
* <p> * <p>
* If either the vendorWeightings or productWeightings lists have been populated this data is used to add weighting factors to * If either the vendorWeightings or productWeightings lists have been
* the search.</p> * populated this data is used to add weighting factors to the search.</p>
* *
* @param vendor the text used to search the vendor field * @param vendor the text used to search the vendor field
* @param product the text used to search the product field * @param product the text used to search the product field
* @param vendorWeightings a list of strings to use to add weighting factors to the vendor field * @param vendorWeightings a list of strings to use to add weighting factors
* @param productWeightings Adds a list of strings that will be used to add weighting factors to the product search * to the vendor field
* @param productWeightings Adds a list of strings that will be used to add
* weighting factors to the product search
* @return a list of possible CPE values * @return a list of possible CPE values
*/ */
protected List<IndexEntry> searchCPE(String vendor, String product, protected List<IndexEntry> searchCPE(String vendor, String product,
@@ -284,10 +306,10 @@ public class CPEAnalyzer implements Analyzer {
} }
return ret; return ret;
} catch (ParseException ex) { } catch (ParseException ex) {
LOGGER.warn("An error occured querying the CPE data. See the log for more details."); LOGGER.warn("An error occurred querying the CPE data. See the log for more details.");
LOGGER.info("Unable to parse: {}", searchString, ex); LOGGER.info("Unable to parse: {}", searchString, ex);
} catch (IOException ex) { } catch (IOException ex) {
LOGGER.warn("An error occured reading CPE data. See the log for more details."); LOGGER.warn("An error occurred reading CPE data. See the log for more details.");
LOGGER.info("IO Error with search string: {}", searchString, ex); LOGGER.info("IO Error with search string: {}", searchString, ex);
} }
return null; return null;
@@ -295,16 +317,20 @@ public class CPEAnalyzer implements Analyzer {
/** /**
* <p> * <p>
* Builds a Lucene search string by properly escaping data and constructing a valid search query.</p> * Builds a Lucene search string by properly escaping data and constructing
* a valid search query.</p>
* *
* <p> * <p>
* If either the possibleVendor or possibleProducts lists have been populated this data is used to add weighting factors to * If either the possibleVendor or possibleProducts lists have been
* the search string generated.</p> * populated this data is used to add weighting factors to the search string
* generated.</p>
* *
* @param vendor text to search the vendor field * @param vendor text to search the vendor field
* @param product text to search the product field * @param product text to search the product field
* @param vendorWeighting a list of strings to apply to the vendor to boost the terms weight * @param vendorWeighting a list of strings to apply to the vendor to boost
* @param productWeightings a list of strings to apply to the product to boost the terms weight * the terms weight
* @param productWeightings a list of strings to apply to the product to
* boost the terms weight
* @return the Lucene query * @return the Lucene query
*/ */
protected String buildSearch(String vendor, String product, protected String buildSearch(String vendor, String product,
@@ -325,13 +351,17 @@ public class CPEAnalyzer implements Analyzer {
} }
/** /**
* This method constructs a Lucene query for a given field. The searchText is split into separate words and if the word is * This method constructs a Lucene query for a given field. The searchText
* within the list of weighted words then an additional weighting is applied to the term as it is appended into the query. * is split into separate words and if the word is within the list of
* weighted words then an additional weighting is applied to the term as it
* is appended into the query.
* *
* @param sb a StringBuilder that the query text will be appended to. * @param sb a StringBuilder that the query text will be appended to.
* @param field the field within the Lucene index that the query is searching. * @param field the field within the Lucene index that the query is
* searching.
* @param searchText text used to construct the query. * @param searchText text used to construct the query.
* @param weightedText a list of terms that will be considered higher importance when searching. * @param weightedText a list of terms that will be considered higher
* importance when searching.
* @return if the append was successful. * @return if the append was successful.
*/ */
private boolean appendWeightedSearch(StringBuilder sb, String field, String searchText, Set<String> weightedText) { private boolean appendWeightedSearch(StringBuilder sb, String field, String searchText, Set<String> weightedText) {
@@ -377,7 +407,8 @@ public class CPEAnalyzer implements Analyzer {
} }
/** /**
* Removes characters from the input text that are not used within the CPE index. * Removes characters from the input text that are not used within the CPE
* index.
* *
* @param text is the text to remove the characters from. * @param text is the text to remove the characters from.
* @return the text having removed some characters. * @return the text having removed some characters.
@@ -387,7 +418,8 @@ public class CPEAnalyzer implements Analyzer {
} }
/** /**
* Compares two strings after lower casing them and removing the non-alpha characters. * Compares two strings after lower casing them and removing the non-alpha
* characters.
* *
* @param l string one to compare. * @param l string one to compare.
* @param r string two to compare. * @param r string two to compare.
@@ -404,8 +436,9 @@ public class CPEAnalyzer implements Analyzer {
} }
/** /**
* Ensures that the CPE Identified matches the dependency. This validates that the product, vendor, and version information * Ensures that the CPE Identified matches the dependency. This validates
* for the CPE are contained within the dependencies evidence. * that the product, vendor, and version information for the CPE are
* contained within the dependencies evidence.
* *
* @param entry a CPE entry. * @param entry a CPE entry.
* @param dependency the dependency that the CPE entries could be for. * @param dependency the dependency that the CPE entries could be for.
@@ -472,14 +505,16 @@ public class CPEAnalyzer implements Analyzer {
} }
/** /**
* Analyzes a dependency and attempts to determine if there are any CPE identifiers for this dependency. * Analyzes a dependency and attempts to determine if there are any CPE
* identifiers for this dependency.
* *
* @param dependency The Dependency to analyze. * @param dependency The Dependency to analyze.
* @param engine The analysis engine * @param engine The analysis engine
* @throws AnalysisException is thrown if there is an issue analyzing the dependency. * @throws AnalysisException is thrown if there is an issue analyzing the
* dependency.
*/ */
@Override @Override
public void analyze(Dependency dependency, Engine engine) throws AnalysisException { public synchronized void analyze(Dependency dependency, Engine engine) throws AnalysisException {
try { try {
determineCPE(dependency); determineCPE(dependency);
} catch (CorruptIndexException ex) { } catch (CorruptIndexException ex) {
@@ -492,15 +527,19 @@ public class CPEAnalyzer implements Analyzer {
} }
/** /**
* Retrieves a list of CPE values from the CveDB based on the vendor and product passed in. The list is then validated to find * Retrieves a list of CPE values from the CveDB based on the vendor and
* only CPEs that are valid for the given dependency. It is possible that the CPE identified is a best effort "guess" based on * product passed in. The list is then validated to find only CPEs that are
* the vendor, product, and version information. * valid for the given dependency. It is possible that the CPE identified is
* a best effort "guess" based on the vendor, product, and version
* information.
* *
* @param dependency the Dependency being analyzed * @param dependency the Dependency being analyzed
* @param vendor the vendor for the CPE being analyzed * @param vendor the vendor for the CPE being analyzed
* @param product the product for the CPE being analyzed * @param product the product for the CPE being analyzed
* @param currentConfidence the current confidence being used during analysis * @param currentConfidence the current confidence being used during
* @return <code>true</code> if an identifier was added to the dependency; otherwise <code>false</code> * analysis
* @return <code>true</code> if an identifier was added to the dependency;
* otherwise <code>false</code>
* @throws UnsupportedEncodingException is thrown if UTF-8 is not supported * @throws UnsupportedEncodingException is thrown if UTF-8 is not supported
*/ */
protected boolean determineIdentifiers(Dependency dependency, String vendor, String product, protected boolean determineIdentifiers(Dependency dependency, String vendor, String product,
@@ -510,10 +549,11 @@ public class CPEAnalyzer implements Analyzer {
Confidence bestGuessConf = null; Confidence bestGuessConf = null;
boolean hasBroadMatch = false; boolean hasBroadMatch = false;
final List<IdentifierMatch> collected = new ArrayList<IdentifierMatch>(); final List<IdentifierMatch> collected = new ArrayList<IdentifierMatch>();
//TODO the following algorithm incorrectly identifies things as a lower version
// if there lower confidence evidence when the current (highest) version number
// is newer then anything in the NVD.
for (Confidence conf : Confidence.values()) { for (Confidence conf : Confidence.values()) {
// if (conf.compareTo(currentConfidence) > 0) {
// break;
// }
for (Evidence evidence : dependency.getVersionEvidence().iterator(conf)) { for (Evidence evidence : dependency.getVersionEvidence().iterator(conf)) {
final DependencyVersion evVer = DependencyVersionUtil.parseVersion(evidence.getValue()); final DependencyVersion evVer = DependencyVersionUtil.parseVersion(evidence.getValue());
if (evVer == null) { if (evVer == null) {
@@ -535,15 +575,14 @@ public class CPEAnalyzer implements Analyzer {
final String url = String.format(NVD_SEARCH_URL, URLEncoder.encode(vs.getName(), "UTF-8")); final String url = String.format(NVD_SEARCH_URL, URLEncoder.encode(vs.getName(), "UTF-8"));
final IdentifierMatch match = new IdentifierMatch("cpe", vs.getName(), url, IdentifierConfidence.EXACT_MATCH, conf); final IdentifierMatch match = new IdentifierMatch("cpe", vs.getName(), url, IdentifierConfidence.EXACT_MATCH, conf);
collected.add(match); collected.add(match);
} else {
//TODO the following isn't quite right is it? need to think about this guessing game a bit more. //TODO the following isn't quite right is it? need to think about this guessing game a bit more.
if (evVer.getVersionParts().size() <= dbVer.getVersionParts().size() } else if (evVer.getVersionParts().size() <= dbVer.getVersionParts().size()
&& evVer.matchesAtLeastThreeLevels(dbVer)) { && evVer.matchesAtLeastThreeLevels(dbVer)) {
if (bestGuessConf == null || bestGuessConf.compareTo(conf) > 0) { if (bestGuessConf == null || bestGuessConf.compareTo(conf) > 0) {
if (bestGuess.getVersionParts().size() < dbVer.getVersionParts().size()) { if (bestGuess.getVersionParts().size() < dbVer.getVersionParts().size()) {
bestGuess = dbVer; bestGuess = dbVer;
bestGuessConf = conf; bestGuessConf = conf;
}
} }
} }
} }
@@ -602,14 +641,16 @@ public class CPEAnalyzer implements Analyzer {
*/ */
BEST_GUESS, BEST_GUESS,
/** /**
* The entire vendor/product group must be added (without a guess at version) because there is a CVE with a VS that only * The entire vendor/product group must be added (without a guess at
* specifies vendor/product. * version) because there is a CVE with a VS that only specifies
* vendor/product.
*/ */
BROAD_MATCH BROAD_MATCH
} }
/** /**
* A simple object to hold an identifier and carry information about the confidence in the identifier. * A simple object to hold an identifier and carry information about the
* confidence in the identifier.
*/ */
private static class IdentifierMatch implements Comparable<IdentifierMatch> { private static class IdentifierMatch implements Comparable<IdentifierMatch> {
@@ -619,8 +660,10 @@ public class CPEAnalyzer implements Analyzer {
* @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
* @param url the URL of the identifier * @param url the URL of the identifier
* @param identifierConfidence the confidence in the identifier: best guess or exact match * @param identifierConfidence the confidence in the identifier: best
* @param evidenceConfidence the confidence of the evidence used to find the identifier * guess or exact match
* @param evidenceConfidence the confidence of the evidence used to find
* the identifier
*/ */
IdentifierMatch(String type, String value, String url, IdentifierConfidence identifierConfidence, Confidence evidenceConfidence) { IdentifierMatch(String type, String value, String url, IdentifierConfidence identifierConfidence, Confidence evidenceConfidence) {
this.identifier = new Identifier(type, value, url); this.identifier = new Identifier(type, value, url);
@@ -751,13 +794,20 @@ public class CPEAnalyzer implements Analyzer {
//</editor-fold> //</editor-fold>
/** /**
* Standard implementation of compareTo that compares identifier confidence, evidence confidence, and then the identifier. * Standard implementation of compareTo that compares identifier
* confidence, evidence confidence, and then the identifier.
* *
* @param o the IdentifierMatch to compare to * @param o the IdentifierMatch to compare to
* @return the natural ordering of IdentifierMatch * @return the natural ordering of IdentifierMatch
*/ */
@Override @Override
public int compareTo(IdentifierMatch o) { public int compareTo(IdentifierMatch o) {
return new CompareToBuilder()
.append(confidence, o.confidence)
.append(evidenceConfidence, o.evidenceConfidence)
.append(identifier, o.identifier)
.toComparison();
/*
int conf = this.confidence.compareTo(o.confidence); int conf = this.confidence.compareTo(o.confidence);
if (conf == 0) { if (conf == 0) {
conf = this.evidenceConfidence.compareTo(o.evidenceConfidence); conf = this.evidenceConfidence.compareTo(o.evidenceConfidence);
@@ -766,6 +816,7 @@ public class CPEAnalyzer implements Analyzer {
} }
} }
return conf; return conf;
*/
} }
} }
} }

View File

@@ -33,8 +33,10 @@ import java.io.File;
import java.io.FileFilter; import java.io.FileFilter;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.util.List; import java.util.List;
import org.owasp.dependencycheck.exception.InitializationException;
import org.owasp.dependencycheck.utils.DownloadFailedException; import org.owasp.dependencycheck.utils.DownloadFailedException;
import org.owasp.dependencycheck.utils.Downloader; import org.owasp.dependencycheck.utils.Downloader;
import org.owasp.dependencycheck.utils.FileFilterBuilder; import org.owasp.dependencycheck.utils.FileFilterBuilder;
@@ -42,8 +44,8 @@ import org.owasp.dependencycheck.utils.InvalidSettingException;
import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.Settings;
/** /**
* Analyzer which will attempt to locate a dependency, and the GAV information, by querying Central for the dependency's SHA-1 * Analyzer which will attempt to locate a dependency, and the GAV information,
* digest. * by querying Central for the dependency's SHA-1 digest.
* *
* @author colezlaw * @author colezlaw
*/ */
@@ -70,7 +72,8 @@ public class CentralAnalyzer extends AbstractFileTypeAnalyzer {
private static final String SUPPORTED_EXTENSIONS = "jar"; private static final String SUPPORTED_EXTENSIONS = "jar";
/** /**
* The analyzer should be disabled if there are errors, so this is a flag to determine if such an error has occurred. * The analyzer should be disabled if there are errors, so this is a flag to
* determine if such an error has occurred.
*/ */
private boolean errorFlag = false; private boolean errorFlag = false;
@@ -96,7 +99,8 @@ public class CentralAnalyzer extends AbstractFileTypeAnalyzer {
/** /**
* Determines if this analyzer is enabled. * Determines if this analyzer is enabled.
* *
* @return <code>true</code> if the analyzer is enabled; otherwise <code>false</code> * @return <code>true</code> if the analyzer is enabled; otherwise
* <code>false</code>
*/ */
private boolean checkEnabled() { private boolean checkEnabled() {
boolean retval = false; boolean retval = false;
@@ -122,16 +126,21 @@ public class CentralAnalyzer extends AbstractFileTypeAnalyzer {
/** /**
* Initializes the analyzer once before any analysis is performed. * Initializes the analyzer once before any analysis is performed.
* *
* @throws Exception if there's an error during initialization * @throws InitializationException if there's an error during initialization
*/ */
@Override @Override
public void initializeFileTypeAnalyzer() throws Exception { public void initializeFileTypeAnalyzer() throws InitializationException {
LOGGER.debug("Initializing Central analyzer"); LOGGER.debug("Initializing Central analyzer");
LOGGER.debug("Central analyzer enabled: {}", isEnabled()); LOGGER.debug("Central analyzer enabled: {}", isEnabled());
if (isEnabled()) { if (isEnabled()) {
final String searchUrl = Settings.getString(Settings.KEYS.ANALYZER_CENTRAL_URL); final String searchUrl = Settings.getString(Settings.KEYS.ANALYZER_CENTRAL_URL);
LOGGER.debug("Central Analyzer URL: {}", searchUrl); LOGGER.debug("Central Analyzer URL: {}", searchUrl);
searcher = new CentralSearch(new URL(searchUrl)); try {
searcher = new CentralSearch(new URL(searchUrl));
} catch (MalformedURLException ex) {
setEnabled(false);
throw new InitializationException("The configured URL to Maven Central is malformed: " + searchUrl, ex);
}
} }
} }
@@ -146,7 +155,8 @@ public class CentralAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* Returns the key used in the properties file to to reference the analyzer's enabled property. * Returns the key used in the properties file to to reference the
* analyzer's enabled property.
* *
* @return the analyzer's enabled property setting key. * @return the analyzer's enabled property setting key.
*/ */
@@ -219,7 +229,8 @@ public class CentralAnalyzer extends AbstractFileTypeAnalyzer {
LOGGER.warn("Unable to download pom.xml for {} from Central; " LOGGER.warn("Unable to download pom.xml for {} from Central; "
+ "this could result in undetected CPE/CVEs.", dependency.getFileName()); + "this could result in undetected CPE/CVEs.", dependency.getFileName());
} finally { } finally {
if (pomFile != null && !FileUtils.deleteQuietly(pomFile)) { if (pomFile != null && pomFile.exists() && !FileUtils.deleteQuietly(pomFile)) {
LOGGER.debug("Failed to delete temporary pom file {}", pomFile.toString());
pomFile.deleteOnExit(); pomFile.deleteOnExit();
} }
} }

View File

@@ -0,0 +1,205 @@
/*
* This file is part of dependency-check-core.
*
* 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.
*
* Copyright (c) 2016 IBM Corporation. All Rights Reserved.
*/
package org.owasp.dependencycheck.analyzer;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.FileUtils;
import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
import org.owasp.dependencycheck.dependency.Confidence;
import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.dependency.EvidenceCollection;
import org.owasp.dependencycheck.utils.FileFilterBuilder;
import org.owasp.dependencycheck.utils.Settings;
/**
* This analyzer is used to analyze SWIFT and Objective-C packages by collecting
* information from .podspec files. CocoaPods dependency manager see
* https://cocoapods.org/.
*
* @author Bianca Jiang (https://twitter.com/biancajiang)
*/
@Experimental
public class CocoaPodsAnalyzer extends AbstractFileTypeAnalyzer {
/**
* The logger.
*/
// private static final Logger LOGGER = LoggerFactory.getLogger(CocoaPodsAnalyzer.class);
/**
* The name of the analyzer.
*/
private static final String ANALYZER_NAME = "CocoaPods Package Analyzer";
/**
* The phase that this analyzer is intended to run in.
*/
private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
/**
* The file name to scan.
*/
public static final String PODSPEC = "podspec";
/**
* Filter that detects files named "*.podspec".
*/
private static final FileFilter PODSPEC_FILTER = FileFilterBuilder.newInstance().addExtensions(PODSPEC).build();
/**
* The capture group #1 is the block variable. e.g. "Pod::Spec.new do
* |spec|"
*/
private static final Pattern PODSPEC_BLOCK_PATTERN = Pattern.compile("Pod::Spec\\.new\\s+?do\\s+?\\|(.+?)\\|");
/**
* Returns the FileFilter
*
* @return the FileFilter
*/
@Override
protected FileFilter getFileFilter() {
return PODSPEC_FILTER;
}
@Override
protected void initializeFileTypeAnalyzer() {
// NO-OP
}
/**
* Returns the name of the analyzer.
*
* @return the name of the analyzer.
*/
@Override
public String getName() {
return ANALYZER_NAME;
}
/**
* Returns the phase that the analyzer is intended to run in.
*
* @return the phase that the analyzer is intended to run in.
*/
@Override
public AnalysisPhase getAnalysisPhase() {
return ANALYSIS_PHASE;
}
/**
* Returns the key used in the properties file to reference the analyzer's
* enabled property.
*
* @return the analyzer's enabled property setting key
*/
@Override
protected String getAnalyzerEnabledSettingKey() {
return Settings.KEYS.ANALYZER_COCOAPODS_ENABLED;
}
@Override
protected void analyzeFileType(Dependency dependency, Engine engine)
throws AnalysisException {
String contents;
try {
contents = FileUtils.readFileToString(dependency.getActualFile(), Charset.defaultCharset());
} catch (IOException e) {
throw new AnalysisException(
"Problem occurred while reading dependency file.", e);
}
final Matcher matcher = PODSPEC_BLOCK_PATTERN.matcher(contents);
if (matcher.find()) {
contents = contents.substring(matcher.end());
final String blockVariable = matcher.group(1);
final EvidenceCollection vendor = dependency.getVendorEvidence();
final EvidenceCollection product = dependency.getProductEvidence();
final EvidenceCollection version = dependency.getVersionEvidence();
final String name = addStringEvidence(product, contents, blockVariable, "name", "name", Confidence.HIGHEST);
if (!name.isEmpty()) {
vendor.addEvidence(PODSPEC, "name_project", name, Confidence.HIGHEST);
}
addStringEvidence(product, contents, blockVariable, "summary", "summary", Confidence.HIGHEST);
addStringEvidence(vendor, contents, blockVariable, "author", "authors?", Confidence.HIGHEST);
addStringEvidence(vendor, contents, blockVariable, "homepage", "homepage", Confidence.HIGHEST);
addStringEvidence(vendor, contents, blockVariable, "license", "licen[cs]es?", Confidence.HIGHEST);
addStringEvidence(version, contents, blockVariable, "version", "version", Confidence.HIGHEST);
}
setPackagePath(dependency);
}
/**
* Extracts evidence from the contents and adds it to the given evidence
* collection.
*
* @param evidences the evidence collection to update
* @param contents the text to extract evidence from
* @param blockVariable the block variable within the content to search for
* @param field the name of the field being searched for
* @param fieldPattern the field pattern within the contents to search for
* @param confidence the confidence level of the evidence if found
* @return the string that was added as evidence
*/
private String addStringEvidence(EvidenceCollection evidences, String contents,
String blockVariable, String field, String fieldPattern, Confidence confidence) {
String value = "";
//capture array value between [ ]
final Matcher arrayMatcher = Pattern.compile(
String.format("\\s*?%s\\.%s\\s*?=\\s*?\\{\\s*?(.*?)\\s*?\\}", blockVariable, fieldPattern),
Pattern.CASE_INSENSITIVE).matcher(contents);
if (arrayMatcher.find()) {
value = arrayMatcher.group(1);
} else { //capture single value between quotes
final Matcher matcher = Pattern.compile(
String.format("\\s*?%s\\.%s\\s*?=\\s*?(['\"])(.*?)\\1", blockVariable, fieldPattern),
Pattern.CASE_INSENSITIVE).matcher(contents);
if (matcher.find()) {
value = matcher.group(2);
}
}
if (value.length() > 0) {
evidences.addEvidence(PODSPEC, field, value, confidence);
}
return value;
}
/**
* Sets the package path on the given dependency.
*
* @param dep the dependency to update
*/
private void setPackagePath(Dependency dep) {
final File file = new File(dep.getFilePath());
final String parent = file.getParent();
if (parent != null) {
dep.setPackagePath(parent);
}
}
}

View File

@@ -35,36 +35,39 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.owasp.dependencycheck.exception.InitializationException;
/** /**
* Used to analyze a composer.lock file for a composer PHP app. * Used to analyze a composer.lock file for a composer PHP app.
* *
* @author colezlaw * @author colezlaw
*/ */
@Experimental
public class ComposerLockAnalyzer extends AbstractFileTypeAnalyzer { public class ComposerLockAnalyzer extends AbstractFileTypeAnalyzer {
/** /**
* The logger * The logger.
*/ */
private static final Logger LOGGER = LoggerFactory.getLogger(ComposerLockAnalyzer.class); private static final Logger LOGGER = LoggerFactory.getLogger(ComposerLockAnalyzer.class);
/** /**
* The analyzer name * The analyzer name.
*/ */
private static final String ANALYZER_NAME = "Composer.lock analyzer"; private static final String ANALYZER_NAME = "Composer.lock analyzer";
/** /**
* composer.json * composer.json.
*/ */
private static final String COMPOSER_LOCK = "composer.lock"; private static final String COMPOSER_LOCK = "composer.lock";
/** /**
* The FileFilter * The FileFilter.
*/ */
private static final FileFilter FILE_FILTER = FileFilterBuilder.newInstance().addFilenames(COMPOSER_LOCK).build(); private static final FileFilter FILE_FILTER = FileFilterBuilder.newInstance().addFilenames(COMPOSER_LOCK).build();
/** /**
* Returns the FileFilter * Returns the FileFilter.
* *
* @return the FileFilter * @return the FileFilter
*/ */
@@ -74,17 +77,24 @@ public class ComposerLockAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* Initializes the analyzer * Initializes the analyzer.
* *
* @throws Exception * @throws InitializationException thrown if an exception occurs getting an
* instance of SHA1
*/ */
@Override @Override
protected void initializeFileTypeAnalyzer() throws Exception { protected void initializeFileTypeAnalyzer() throws InitializationException {
sha1 = MessageDigest.getInstance("SHA1"); try {
sha1 = MessageDigest.getInstance("SHA1");
} catch (NoSuchAlgorithmException ex) {
setEnabled(false);
throw new InitializationException("Unable to create SHA1 MmessageDigest", ex);
}
} }
/** /**
* The MessageDigest for calculating a new digest for the new dependencies added * The MessageDigest for calculating a new digest for the new dependencies
* added.
*/ */
private MessageDigest sha1 = null; private MessageDigest sha1 = null;

View File

@@ -20,7 +20,7 @@ package org.owasp.dependencycheck.analyzer;
import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.suppression.SuppressionRule; import org.owasp.dependencycheck.xml.suppression.SuppressionRule;
/** /**
* The suppression analyzer processes an externally defined XML document that complies with the suppressions.xsd schema. * The suppression analyzer processes an externally defined XML document that complies with the suppressions.xsd schema.

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 {
@@ -112,6 +117,7 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
final ListIterator<Dependency> subIterator = engine.getDependencies().listIterator(mainIterator.nextIndex()); final ListIterator<Dependency> subIterator = engine.getDependencies().listIterator(mainIterator.nextIndex());
while (subIterator.hasNext()) { while (subIterator.hasNext()) {
final Dependency nextDependency = subIterator.next(); final Dependency nextDependency = subIterator.next();
Dependency main = null;
if (hashesMatch(dependency, nextDependency) && !containedInWar(dependency.getFilePath()) if (hashesMatch(dependency, nextDependency) && !containedInWar(dependency.getFilePath())
&& !containedInWar(nextDependency.getFilePath())) { && !containedInWar(nextDependency.getFilePath())) {
if (firstPathIsShortest(dependency.getFilePath(), nextDependency.getFilePath())) { if (firstPathIsShortest(dependency.getFilePath(), nextDependency.getFilePath())) {
@@ -138,6 +144,20 @@ 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 ((main = getMainGemspecDependency(dependency, nextDependency)) != null) {
if (main == dependency) {
mergeDependencies(dependency, nextDependency, dependenciesToRemove);
} else {
mergeDependencies(nextDependency, dependency, dependenciesToRemove);
break; //since we merged into the next dependency - skip forward to the next in mainIterator
}
} else if ((main = getMainSwiftDependency(dependency, nextDependency)) != null) {
if (main == dependency) {
mergeDependencies(dependency, nextDependency, dependenciesToRemove);
} else {
mergeDependencies(nextDependency, dependency, dependenciesToRemove);
break; //since we merged into the next dependency - skip forward to the next in mainIterator
}
} }
} }
} }
@@ -152,10 +172,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);
@@ -171,7 +192,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.
@@ -196,11 +218,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
@@ -228,11 +252,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
@@ -283,10 +309,13 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
String right = rFile.getParent(); String right = rFile.getParent();
if (left == null) { if (left == null) {
return right == null; return right == null;
} else if (right == null) {
return false;
} }
if (left.equalsIgnoreCase(right)) { if (left.equalsIgnoreCase(right)) {
return true; return true;
} }
if (left.matches(".*[/\\\\]repository[/\\\\].*") && right.matches(".*[/\\\\]repository[/\\\\].*")) { if (left.matches(".*[/\\\\]repository[/\\\\].*") && right.matches(".*[/\\\\]repository[/\\\\].*")) {
left = getBaseRepoPath(left); left = getBaseRepoPath(left);
right = getBaseRepoPath(right); right = getBaseRepoPath(right);
@@ -304,12 +333,110 @@ 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 * Bundling Ruby gems that are identified from different .gemspec files but
* 'right' library. * 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) {
if (dependency1 == null || dependency2 == null
|| !dependency1.getFileName().endsWith(".gemspec")
|| !dependency2.getFileName().endsWith(".gemspec")
|| dependency1.getPackagePath() == null
|| dependency2.getPackagePath() == null) {
return false;
}
if (dependency1.getPackagePath().equalsIgnoreCase(dependency2.getPackagePath())) {
return true;
}
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. If
* 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. 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) {
if (isSameRubyGem(dependency1, dependency2)) {
final File lFile = dependency1.getActualFile();
final File left = lFile.getParentFile();
if (left != null && left.getName().equalsIgnoreCase("specifications")) {
return dependency1;
}
return dependency2;
}
return null;
}
/**
* Bundling same swift dependencies with the same packagePath but identified
* by different analyzers.
*
* @param dependency1 dependency to test
* @param dependency2 dependency to test
* @return <code>true</code> if the dependencies appear to be the same;
* otherwise <code>false</code>
*/
private boolean isSameSwiftPackage(Dependency dependency1, Dependency dependency2) {
if (dependency1 == null || dependency2 == null
|| (!dependency1.getFileName().endsWith(".podspec")
&& !dependency1.getFileName().equals("Package.swift"))
|| (!dependency2.getFileName().endsWith(".podspec")
&& !dependency2.getFileName().equals("Package.swift"))
|| dependency1.getPackagePath() == null
|| dependency2.getPackagePath() == null) {
return false;
}
if (dependency1.getPackagePath().equalsIgnoreCase(dependency2.getPackagePath())) {
return true;
}
return false;
}
/**
* Determines which of the swift dependencies should be considered the
* primary.
*
* @param dependency1 the first swift dependency to compare
* @param dependency2 the second swift dependency to compare
* @return the primary swift dependency
*/
private Dependency getMainSwiftDependency(Dependency dependency1, Dependency dependency2) {
if (isSameSwiftPackage(dependency1, dependency2)) {
if (dependency1.getFileName().endsWith(".podspec")) {
return dependency1;
}
return dependency2;
}
return null;
}
/**
* This is likely a very broken attempt at determining if the 'left'
* 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();
@@ -345,11 +472,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) {
@@ -359,12 +488,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();
@@ -378,12 +508,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

@@ -0,0 +1,34 @@
/*
* This file is part of dependency-check-core.
*
* 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.
*
* Copyright (c) 2016 Jeremy Long. All Rights Reserved.
*/
package org.owasp.dependencycheck.analyzer;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation used to flag an analyzer as experimental.
*
* @author jeremy long
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Experimental {
}

View File

@@ -18,7 +18,9 @@
package org.owasp.dependencycheck.analyzer; package org.owasp.dependencycheck.analyzer;
import java.io.File; import java.io.File;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.filefilter.NameFileFilter;
import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Confidence;
@@ -65,12 +67,23 @@ public class FileNameAnalyzer extends AbstractAnalyzer implements Analyzer {
} }
//</editor-fold> //</editor-fold>
/**
* Python init files
*/
//CSOFF: WhitespaceAfter
private static final NameFileFilter IGNORED_FILES = new NameFileFilter(new String[]{
"__init__.py",
"__init__.pyc",
"__init__.pyo",});
//CSON: WhitespaceAfter
/** /**
* Collects information about the file name. * Collects information about the file name.
* *
* @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 {
@@ -81,32 +94,27 @@ public class FileNameAnalyzer extends AbstractAnalyzer implements Analyzer {
//add version evidence //add version evidence
final DependencyVersion version = DependencyVersionUtil.parseVersion(fileName); final DependencyVersion version = DependencyVersionUtil.parseVersion(fileName);
final String packageName = DependencyVersionUtil.parsePreVersion(fileName);
if (version != null) { if (version != null) {
// If the version number is just a number like 2 or 23, reduce the confidence // If the version number is just a number like 2 or 23, reduce the confidence
// a shade. This should hopefully correct for cases like log4j.jar or // a shade. This should hopefully correct for cases like log4j.jar or
// struts2-core.jar // struts2-core.jar
if (version.getVersionParts() == null || version.getVersionParts().size() < 2) { if (version.getVersionParts() == null || version.getVersionParts().size() < 2) {
dependency.getVersionEvidence().addEvidence("file", "name", dependency.getVersionEvidence().addEvidence("file", "version",
version.toString(), Confidence.MEDIUM); version.toString(), Confidence.MEDIUM);
} else { } else {
dependency.getVersionEvidence().addEvidence("file", "name", dependency.getVersionEvidence().addEvidence("file", "version",
version.toString(), Confidence.HIGHEST); version.toString(), Confidence.HIGHEST);
} }
dependency.getVersionEvidence().addEvidence("file", "name", dependency.getVersionEvidence().addEvidence("file", "name",
fileName, Confidence.MEDIUM); packageName, Confidence.MEDIUM);
} }
//add as vendor and product evidence if (!IGNORED_FILES.accept(f)) {
if (fileName.contains("-")) {
dependency.getProductEvidence().addEvidence("file", "name", dependency.getProductEvidence().addEvidence("file", "name",
fileName, Confidence.HIGHEST); packageName, Confidence.HIGH);
dependency.getVendorEvidence().addEvidence("file", "name", dependency.getVendorEvidence().addEvidence("file", "name",
fileName, Confidence.HIGHEST); packageName, Confidence.HIGH);
} else {
dependency.getProductEvidence().addEvidence("file", "name",
fileName, Confidence.HIGH);
dependency.getVendorEvidence().addEvidence("file", "name",
fileName, Confidence.HIGH);
} }
} }
} }

View File

@@ -17,17 +17,37 @@
*/ */
package org.owasp.dependencycheck.analyzer; package org.owasp.dependencycheck.analyzer;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.regex.Pattern;
import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
import org.owasp.dependencycheck.dependency.Confidence;
import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.dependency.Evidence; import org.owasp.dependencycheck.dependency.Evidence;
import org.owasp.dependencycheck.exception.InitializationException;
import org.owasp.dependencycheck.xml.suppression.PropertyType;
import org.owasp.dependencycheck.utils.DownloadFailedException;
import org.owasp.dependencycheck.utils.Downloader;
import org.owasp.dependencycheck.utils.FileUtils;
import org.owasp.dependencycheck.utils.Settings;
import org.owasp.dependencycheck.xml.hints.VendorDuplicatingHintRule;
import org.owasp.dependencycheck.xml.hints.HintParseException;
import org.owasp.dependencycheck.xml.hints.HintParser;
import org.owasp.dependencycheck.xml.hints.HintRule;
import org.owasp.dependencycheck.xml.hints.Hints;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
/** /**
* This analyzer adds evidence to dependencies to enhance the accuracy of
* library identification.
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
@@ -62,18 +82,101 @@ public class HintAnalyzer extends AbstractAnalyzer implements Analyzer {
public AnalysisPhase getAnalysisPhase() { public AnalysisPhase getAnalysisPhase() {
return ANALYSIS_PHASE; return ANALYSIS_PHASE;
} }
/**
* The initialize method does nothing for this Analyzer.
*
* @throws InitializationException thrown if there is an exception
*/
@Override
public void initialize() throws InitializationException {
try {
super.initialize();
loadHintRules();
} catch (HintParseException ex) {
LOGGER.debug("Unable to parse hint file", ex);
throw new InitializationException("Unable to parse the hint file", ex);
}
}
//</editor-fold> //</editor-fold>
/** /**
* The HintAnalyzer uses knowledge about a dependency to add additional information to help in identification of identifiers * The Logger for use throughout the class
* or vulnerabilities. */
private static final Logger LOGGER = LoggerFactory.getLogger(HintAnalyzer.class);
/**
* The name of the hint rule file
*/
private static final String HINT_RULE_FILE_NAME = "dependencycheck-base-hint.xml";
/**
* The collection of hints.
*/
private Hints hints;
/**
* The HintAnalyzer uses knowledge about a dependency to add additional
* information to help in identification of identifiers or vulnerabilities.
* *
* @param dependency The dependency being analyzed * @param dependency The dependency being analyzed
* @param engine The scanning engine * @param engine The scanning engine
* @throws AnalysisException is thrown if there is an exception analyzing the dependency. * @throws AnalysisException is thrown if there is an exception analyzing
* the dependency.
*/ */
@Override @Override
public void analyze(Dependency dependency, Engine engine) throws AnalysisException { public void analyze(Dependency dependency, Engine engine) throws AnalysisException {
for (HintRule hint : hints.getHintRules()) {
boolean shouldAdd = false;
for (Evidence given : hint.getGivenVendor()) {
if (dependency.getVendorEvidence().getEvidence().contains(given)) {
shouldAdd = true;
break;
}
}
if (!shouldAdd) {
for (Evidence given : hint.getGivenProduct()) {
if (dependency.getProductEvidence().getEvidence().contains(given)) {
shouldAdd = true;
break;
}
}
}
if (!shouldAdd) {
for (PropertyType pt : hint.getFilenames()) {
if (pt.matches(dependency.getFileName())) {
shouldAdd = true;
}
}
}
if (shouldAdd) {
for (Evidence e : hint.getAddVendor()) {
dependency.getVendorEvidence().addEvidence(e);
}
for (Evidence e : hint.getAddProduct()) {
dependency.getProductEvidence().addEvidence(e);
}
for (Evidence e : hint.getAddVersion()) {
dependency.getVersionEvidence().addEvidence(e);
}
}
}
final Iterator<Evidence> itr = dependency.getVendorEvidence().iterator();
final List<Evidence> newEntries = new ArrayList<Evidence>();
while (itr.hasNext()) {
final Evidence e = itr.next();
for (VendorDuplicatingHintRule dhr : hints.getVendorDuplicatingHintRules()) {
if (dhr.getValue().equalsIgnoreCase(e.getValue(false))) {
newEntries.add(new Evidence(e.getSource() + " (hint)",
e.getName(), dhr.getDuplicate(), e.getConfidence()));
}
}
}
for (Evidence e : newEntries) {
dependency.getVendorEvidence().addEvidence(e);
}
//<editor-fold defaultstate="collapsed" desc="Old implementation">
/*
final Evidence springTest1 = new Evidence("Manifest", final Evidence springTest1 = new Evidence("Manifest",
"Implementation-Title", "Implementation-Title",
"Spring Framework", "Spring Framework",
@@ -171,6 +274,90 @@ public class HintAnalyzer extends AbstractAnalyzer implements Analyzer {
for (Evidence e : newEntries) { for (Evidence e : newEntries) {
dependency.getVendorEvidence().addEvidence(e); dependency.getVendorEvidence().addEvidence(e);
} }
*/
//</editor-fold>
}
/**
* Loads the hint rules file.
*
* @throws HintParseException thrown if the XML cannot be parsed.
*/
private void loadHintRules() throws HintParseException {
final HintParser parser = new HintParser();
File file = null;
try {
hints = parser.parseHints(this.getClass().getClassLoader().getResourceAsStream(HINT_RULE_FILE_NAME));
} catch (HintParseException ex) {
LOGGER.error("Unable to parse the base hint data file");
LOGGER.debug("Unable to parse the base hint data file", ex);
} catch (SAXException ex) {
LOGGER.error("Unable to parse the base hint data file");
LOGGER.debug("Unable to parse the base hint data file", ex);
}
final String filePath = Settings.getString(Settings.KEYS.HINTS_FILE);
if (filePath == null) {
return;
}
boolean deleteTempFile = false;
try {
final Pattern uriRx = Pattern.compile("^(https?|file)\\:.*", Pattern.CASE_INSENSITIVE);
if (uriRx.matcher(filePath).matches()) {
deleteTempFile = true;
file = FileUtils.getTempFile("hint", "xml");
final URL url = new URL(filePath);
try {
Downloader.fetchFile(url, file, false);
} catch (DownloadFailedException ex) {
Downloader.fetchFile(url, file, true);
}
} else {
file = new File(filePath);
if (!file.exists()) {
InputStream fromClasspath = null;
try {
fromClasspath = this.getClass().getClassLoader().getResourceAsStream(filePath);
if (fromClasspath != null) {
deleteTempFile = true;
file = FileUtils.getTempFile("hint", "xml");
try {
org.apache.commons.io.FileUtils.copyInputStreamToFile(fromClasspath, file);
} catch (IOException ex) {
throw new HintParseException("Unable to locate suppressions file in classpath", ex);
}
}
} finally {
if (fromClasspath != null) {
fromClasspath.close();
}
}
}
}
if (file != null) {
try {
final Hints newHints = parser.parseHints(file);
hints.getHintRules().addAll(newHints.getHintRules());
hints.getVendorDuplicatingHintRules().addAll(newHints.getVendorDuplicatingHintRules());
LOGGER.debug("{} hint rules were loaded.", hints.getHintRules().size());
LOGGER.debug("{} duplicating hint rules were loaded.", hints.getVendorDuplicatingHintRules().size());
} catch (HintParseException ex) {
LOGGER.warn("Unable to parse hint rule xml file '{}'", file.getPath());
LOGGER.warn(ex.getMessage());
LOGGER.debug("", ex);
throw ex;
}
}
} catch (DownloadFailedException ex) {
throw new HintParseException("Unable to fetch the configured hint file", ex);
} catch (MalformedURLException ex) {
throw new HintParseException("Configured hint file has an invalid URL", ex);
} catch (IOException ex) {
throw new HintParseException("Unable to create temp file for hints", ex);
} finally {
if (deleteTempFile && file != null) {
FileUtils.delete(file);
}
}
} }
} }

View File

@@ -49,6 +49,7 @@ import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Confidence;
import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.dependency.EvidenceCollection; import org.owasp.dependencycheck.dependency.EvidenceCollection;
import org.owasp.dependencycheck.exception.InitializationException;
import org.owasp.dependencycheck.utils.FileFilterBuilder; import org.owasp.dependencycheck.utils.FileFilterBuilder;
import org.owasp.dependencycheck.xml.pom.License; import org.owasp.dependencycheck.xml.pom.License;
import org.owasp.dependencycheck.xml.pom.PomUtils; import org.owasp.dependencycheck.xml.pom.PomUtils;
@@ -59,7 +60,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* Used to load a JAR file and collect information that can be used to determine the associated CPE. * Used to load a JAR file and collect information that can be used to determine
* the associated CPE.
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
@@ -71,7 +73,8 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
*/ */
private static final Logger LOGGER = LoggerFactory.getLogger(JarAnalyzer.class); private static final Logger LOGGER = LoggerFactory.getLogger(JarAnalyzer.class);
/** /**
* The count of directories created during analysis. This is used for creating temporary directories. * The count of directories created during analysis. This is used for
* creating temporary directories.
*/ */
private static int dirCount = 0; private static int dirCount = 0;
/** /**
@@ -79,7 +82,8 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
*/ */
private static final String NEWLINE = System.getProperty("line.separator"); private static final String NEWLINE = System.getProperty("line.separator");
/** /**
* A list of values in the manifest to ignore as they only result in false positives. * A list of values in the manifest to ignore as they only result in false
* positives.
*/ */
private static final Set<String> IGNORE_VALUES = newHashSet( private static final Set<String> IGNORE_VALUES = newHashSet(
"Sun Java System Application Server"); "Sun Java System Application Server");
@@ -122,7 +126,8 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
"ipojo-extension", "ipojo-extension",
"eclipse-sourcereferences"); "eclipse-sourcereferences");
/** /**
* Deprecated Jar manifest attribute, that is, nonetheless, useful for analysis. * Deprecated Jar manifest attribute, that is, nonetheless, useful for
* analysis.
*/ */
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
private static final String IMPLEMENTATION_VENDOR_ID = Attributes.Name.IMPLEMENTATION_VENDOR_ID private static final String IMPLEMENTATION_VENDOR_ID = Attributes.Name.IMPLEMENTATION_VENDOR_ID
@@ -202,7 +207,8 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
//</editor-fold> //</editor-fold>
/** /**
* 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
*/ */
@@ -212,12 +218,13 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* Loads a specified JAR file and collects information from the manifest and checksums to identify the correct CPE * Loads a specified JAR file and collects information from the manifest and
* information. * checksums to identify the correct CPE information.
* *
* @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 analyzeFileType(Dependency dependency, Engine engine) throws AnalysisException { public void analyzeFileType(Dependency dependency, Engine engine) throws AnalysisException {
@@ -241,13 +248,15 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* Attempts to find a pom.xml within the JAR file. If found it extracts information and adds it to the evidence. This will * Attempts to find a pom.xml within the JAR file. If found it extracts
* attempt to interpolate the strings contained within the pom.properties if one exists. * information and adds it to the evidence. This will attempt to interpolate
* the strings contained within the pom.properties if one exists.
* *
* @param dependency the dependency being analyzed * @param dependency the dependency being analyzed
* @param classes a collection of class name information * @param classes a collection of class name information
* @param engine the analysis engine, used to add additional dependencies * @param engine the analysis engine, used to add additional dependencies
* @throws AnalysisException is thrown if there is an exception parsing the pom * @throws AnalysisException is thrown if there is an exception parsing the
* pom
* @return whether or not evidence was added to the dependency * @return whether or not evidence was added to the dependency
*/ */
protected boolean analyzePOM(Dependency dependency, List<ClassNameInformation> classes, Engine engine) throws AnalysisException { protected boolean analyzePOM(Dependency dependency, List<ClassNameInformation> classes, Engine engine) throws AnalysisException {
@@ -316,11 +325,13 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
} else { } else {
pom = PomUtils.readPom(externalPom); pom = PomUtils.readPom(externalPom);
} }
pom.processProperties(pomProperties); if (pom != null) {
foundSomething |= setPomEvidence(dependency, pom, classes); pom.processProperties(pomProperties);
foundSomething |= setPomEvidence(dependency, pom, classes);
}
} }
} catch (AnalysisException ex) { } catch (AnalysisException ex) {
LOGGER.warn("An error occured while analyzing '{}'.", dependency.getActualFilePath()); LOGGER.warn("An error occurred while analyzing '{}'.", dependency.getActualFilePath());
LOGGER.trace("", ex); LOGGER.trace("", ex);
} }
} }
@@ -328,12 +339,14 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* Given a path to a pom.xml within a JarFile, this method attempts to load a sibling pom.properties if one exists. * Given a path to a pom.xml within a JarFile, this method attempts to load
* a sibling pom.properties if one exists.
* *
* @param path the path to the pom.xml within the JarFile * @param path the path to the pom.xml within the JarFile
* @param jar the JarFile to load the pom.properties from * @param jar the JarFile to load the pom.properties from
* @return a Properties object or null if no pom.properties was found * @return a Properties object or null if no pom.properties was found
* @throws IOException thrown if there is an exception reading the pom.properties * @throws IOException thrown if there is an exception reading the
* pom.properties
*/ */
private Properties retrievePomProperties(String path, final JarFile jar) throws IOException { private Properties retrievePomProperties(String path, final JarFile jar) throws IOException {
Properties pomProperties = null; Properties pomProperties = null;
@@ -360,7 +373,8 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* Searches a JarFile for pom.xml entries and returns a listing of these entries. * Searches a JarFile for pom.xml entries and returns a listing of these
* entries.
* *
* @param jar the JarFile to search * @param jar the JarFile to search
* @return a list of pom.xml entries * @return a list of pom.xml entries
@@ -387,8 +401,8 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
* @param jar the jar file to extract the pom from * @param jar the jar file to extract the pom from
* @param dependency the dependency being analyzed * @param dependency the dependency being analyzed
* @return returns the POM object * @return returns the POM object
* @throws AnalysisException is thrown if there is an exception extracting or parsing the POM * @throws AnalysisException is thrown if there is an exception extracting
* {@link org.owasp.dependencycheck.xml.pom.Model} object * or parsing the POM {@link org.owasp.dependencycheck.xml.pom.Model} object
*/ */
private Model extractPom(String path, JarFile jar, Dependency dependency) throws AnalysisException { private Model extractPom(String path, JarFile jar, Dependency dependency) throws AnalysisException {
InputStream input = null; InputStream input = null;
@@ -397,6 +411,9 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
final File file = new File(tmpDir, "pom.xml"); final File file = new File(tmpDir, "pom.xml");
try { try {
final ZipEntry entry = jar.getEntry(path); final ZipEntry entry = jar.getEntry(path);
if (entry == null) {
throw new AnalysisException(String.format("Pom (%s)does not exist in %s", path, jar.getName()));
}
input = jar.getInputStream(entry); input = jar.getInputStream(entry);
fos = new FileOutputStream(file); fos = new FileOutputStream(file);
IOUtils.copy(input, fos); IOUtils.copy(input, fos);
@@ -446,9 +463,10 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
* *
* @param dependency the dependency to set data on * @param dependency the dependency to set data on
* @param pom the information from the pom * @param pom the information from the pom
* @param classes a collection of ClassNameInformation - containing data about the fully qualified class names within the JAR * @param classes a collection of ClassNameInformation - containing data
* file being analyzed * about the fully qualified class names within the JAR file being analyzed
* @return true if there was evidence within the pom that we could use; otherwise false * @return true if there was evidence within the pom that we could use;
* otherwise false
*/ */
public static boolean setPomEvidence(Dependency dependency, Model pom, List<ClassNameInformation> classes) { public static boolean setPomEvidence(Dependency dependency, Model pom, List<ClassNameInformation> classes) {
boolean foundSomething = false; boolean foundSomething = false;
@@ -474,7 +492,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
} }
final String originalGroupID = groupid; final String originalGroupID = groupid;
if (groupid.startsWith("org.") || groupid.startsWith("com.")) { if (groupid != null && (groupid.startsWith("org.") || groupid.startsWith("com."))) {
groupid = groupid.substring(4); groupid = groupid.substring(4);
} }
@@ -483,7 +501,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
} }
final String originalArtifactID = artifactid; final String originalArtifactID = artifactid;
if (artifactid.startsWith("org.") || artifactid.startsWith("com.")) { if (artifactid != null && (artifactid.startsWith("org.") || artifactid.startsWith("com."))) {
artifactid = artifactid.substring(4); artifactid = artifactid.substring(4);
} }
@@ -565,17 +583,25 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
addMatchingValues(classes, trimmedDescription, dependency.getProductEvidence()); addMatchingValues(classes, trimmedDescription, dependency.getProductEvidence());
} }
final String projectURL = pom.getProjectURL();
if (projectURL != null && !projectURL.trim().isEmpty()) {
dependency.getVendorEvidence().addEvidence("pom", "url", projectURL, Confidence.HIGHEST);
}
extractLicense(pom, dependency); extractLicense(pom, dependency);
return foundSomething; return foundSomething;
} }
/** /**
* Analyzes the path information of the classes contained within the JarAnalyzer to try and determine possible vendor or * Analyzes the path information of the classes contained within the
* product names. If any are found they are stored in the packageVendor and packageProduct hashSets. * JarAnalyzer to try and determine possible vendor or product names. If any
* are found they are stored in the packageVendor and packageProduct
* hashSets.
* *
* @param classNames a list of class names * @param classNames a list of class names
* @param dependency a dependency to analyze * @param dependency a dependency to analyze
* @param addPackagesAsEvidence a flag indicating whether or not package names should be added as evidence. * @param addPackagesAsEvidence a flag indicating whether or not package
* names should be added as evidence.
*/ */
protected void analyzePackageNames(List<ClassNameInformation> classNames, protected void analyzePackageNames(List<ClassNameInformation> classNames,
Dependency dependency, boolean addPackagesAsEvidence) { Dependency dependency, boolean addPackagesAsEvidence) {
@@ -610,11 +636,13 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
/** /**
* <p> * <p>
* Reads the manifest from the JAR file and collects the entries. Some vendorKey entries are:</p> * Reads the manifest from the JAR file and collects the entries. Some
* vendorKey entries are:</p>
* <ul><li>Implementation Title</li> * <ul><li>Implementation Title</li>
* <li>Implementation Version</li> <li>Implementation Vendor</li> * <li>Implementation Version</li> <li>Implementation Vendor</li>
* <li>Implementation VendorId</li> <li>Bundle Name</li> <li>Bundle Version</li> <li>Bundle Vendor</li> <li>Bundle * <li>Implementation VendorId</li> <li>Bundle Name</li> <li>Bundle
* Description</li> <li>Main Class</li> </ul> * Version</li> <li>Bundle Vendor</li> <li>Bundle Description</li> <li>Main
* Class</li> </ul>
* However, all but a handful of specific entries are read in. * However, all but a handful of specific entries are read in.
* *
* @param dependency A reference to the dependency * @param dependency A reference to the dependency
@@ -627,11 +655,8 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
JarFile jar = null; JarFile jar = null;
try { try {
jar = new JarFile(dependency.getActualFilePath()); jar = new JarFile(dependency.getActualFilePath());
final Manifest manifest = jar.getManifest(); final Manifest manifest = jar.getManifest();
if (manifest == null) { if (manifest == null) {
//don't log this for javadoc or sources jar files
if (!dependency.getFileName().toLowerCase().endsWith("-sources.jar") if (!dependency.getFileName().toLowerCase().endsWith("-sources.jar")
&& !dependency.getFileName().toLowerCase().endsWith("-javadoc.jar") && !dependency.getFileName().toLowerCase().endsWith("-javadoc.jar")
&& !dependency.getFileName().toLowerCase().endsWith("-src.jar") && !dependency.getFileName().toLowerCase().endsWith("-src.jar")
@@ -641,17 +666,13 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
} }
return false; return false;
} }
final Attributes atts = manifest.getMainAttributes();
final EvidenceCollection vendorEvidence = dependency.getVendorEvidence(); final EvidenceCollection vendorEvidence = dependency.getVendorEvidence();
final EvidenceCollection productEvidence = dependency.getProductEvidence(); final EvidenceCollection productEvidence = dependency.getProductEvidence();
final EvidenceCollection versionEvidence = dependency.getVersionEvidence(); final EvidenceCollection versionEvidence = dependency.getVersionEvidence();
String source = "Manifest";
final String source = "Manifest";
String specificationVersion = null; String specificationVersion = null;
boolean hasImplementationVersion = false; boolean hasImplementationVersion = false;
Attributes atts = manifest.getMainAttributes();
for (Entry<Object, Object> entry : atts.entrySet()) { for (Entry<Object, Object> entry : atts.entrySet()) {
String key = entry.getKey().toString(); String key = entry.getKey().toString();
String value = atts.getValue(key); String value = atts.getValue(key);
@@ -669,7 +690,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
foundSomething = true; foundSomething = true;
versionEvidence.addEvidence(source, key, value, Confidence.HIGH); versionEvidence.addEvidence(source, key, value, Confidence.HIGH);
} else if ("specification-version".equalsIgnoreCase(key)) { } else if ("specification-version".equalsIgnoreCase(key)) {
specificationVersion = key; specificationVersion = value;
} else if (key.equalsIgnoreCase(Attributes.Name.IMPLEMENTATION_VENDOR.toString())) { } else if (key.equalsIgnoreCase(Attributes.Name.IMPLEMENTATION_VENDOR.toString())) {
foundSomething = true; foundSomething = true;
vendorEvidence.addEvidence(source, key, value, Confidence.HIGH); vendorEvidence.addEvidence(source, key, value, Confidence.HIGH);
@@ -681,7 +702,6 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
} else if (key.equalsIgnoreCase(BUNDLE_DESCRIPTION)) { } else if (key.equalsIgnoreCase(BUNDLE_DESCRIPTION)) {
foundSomething = true; foundSomething = true;
addDescription(dependency, value, "manifest", key); addDescription(dependency, value, "manifest", key);
//productEvidence.addEvidence(source, key, value, Confidence.MEDIUM);
addMatchingValues(classInformation, value, productEvidence); addMatchingValues(classInformation, value, productEvidence);
} else if (key.equalsIgnoreCase(BUNDLE_NAME)) { } else if (key.equalsIgnoreCase(BUNDLE_NAME)) {
foundSomething = true; foundSomething = true;
@@ -689,25 +709,14 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
addMatchingValues(classInformation, value, productEvidence); addMatchingValues(classInformation, value, productEvidence);
// //the following caused false positives. // //the following caused false positives.
// } else if (key.equalsIgnoreCase(BUNDLE_VENDOR)) { // } else if (key.equalsIgnoreCase(BUNDLE_VENDOR)) {
// foundSomething = true;
// vendorEvidence.addEvidence(source, key, value, Confidence.HIGH);
// addMatchingValues(classInformation, value, vendorEvidence);
} else if (key.equalsIgnoreCase(BUNDLE_VERSION)) { } else if (key.equalsIgnoreCase(BUNDLE_VERSION)) {
foundSomething = true; foundSomething = true;
versionEvidence.addEvidence(source, key, value, Confidence.HIGH); versionEvidence.addEvidence(source, key, value, Confidence.HIGH);
} else if (key.equalsIgnoreCase(Attributes.Name.MAIN_CLASS.toString())) { } else if (key.equalsIgnoreCase(Attributes.Name.MAIN_CLASS.toString())) {
continue; continue;
//skipping main class as if this has important information to add //skipping main class as if this has important information to add it will be added during class name analysis...
// it will be added during class name analysis... if other fields
// have the information from the class name then they will get added...
// foundSomething = true;
// productEvidence.addEvidence(source, key, value, Confidence.MEDIUM);
// vendorEvidence.addEvidence(source, key, value, Confidence.MEDIUM);
// addMatchingValues(classInformation, value, vendorEvidence);
// addMatchingValues(classInformation, value, productEvidence);
} else { } else {
key = key.toLowerCase(); key = key.toLowerCase();
if (!IGNORE_KEYS.contains(key) if (!IGNORE_KEYS.contains(key)
&& !key.endsWith("jdk") && !key.endsWith("jdk")
&& !key.contains("lastmodified") && !key.contains("lastmodified")
@@ -719,12 +728,9 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
&& !value.trim().startsWith("scm:") && !value.trim().startsWith("scm:")
&& !isImportPackage(key, value) && !isImportPackage(key, value)
&& !isPackage(key, value)) { && !isPackage(key, value)) {
foundSomething = true; foundSomething = true;
if (key.contains("version")) { if (key.contains("version")) {
if (!key.contains("specification")) { if (!key.contains("specification")) {
//versionEvidence.addEvidence(source, key, value, Confidence.LOW);
//} else {
versionEvidence.addEvidence(source, key, value, Confidence.MEDIUM); versionEvidence.addEvidence(source, key, value, Confidence.MEDIUM);
} }
} else if ("build-id".equals(key)) { } else if ("build-id".equals(key)) {
@@ -754,21 +760,19 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
addMatchingValues(classInformation, value, productEvidence); addMatchingValues(classInformation, value, productEvidence);
} else if (key.contains("license")) { } else if (key.contains("license")) {
addLicense(dependency, value); addLicense(dependency, value);
} else if (key.contains("description")) {
addDescription(dependency, value, "manifest", key);
} else { } else {
if (key.contains("description")) { productEvidence.addEvidence(source, key, value, Confidence.LOW);
addDescription(dependency, value, "manifest", key); vendorEvidence.addEvidence(source, key, value, Confidence.LOW);
} else { addMatchingValues(classInformation, value, vendorEvidence);
productEvidence.addEvidence(source, key, value, Confidence.LOW); addMatchingValues(classInformation, value, productEvidence);
vendorEvidence.addEvidence(source, key, value, Confidence.LOW); if (value.matches(".*\\d.*")) {
addMatchingValues(classInformation, value, vendorEvidence); final StringTokenizer tokenizer = new StringTokenizer(value, " ");
addMatchingValues(classInformation, value, productEvidence); while (tokenizer.hasMoreElements()) {
if (value.matches(".*\\d.*")) { final String s = tokenizer.nextToken();
final StringTokenizer tokenizer = new StringTokenizer(value, " "); if (s.matches("^[0-9.]+$")) {
while (tokenizer.hasMoreElements()) { versionEvidence.addEvidence(source, key, s, Confidence.LOW);
final String s = tokenizer.nextToken();
if (s.matches("^[0-9.]+$")) {
versionEvidence.addEvidence(source, key, s, Confidence.LOW);
}
} }
} }
} }
@@ -776,9 +780,34 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
} }
} }
} }
for (Map.Entry<String, Attributes> item : manifest.getEntries().entrySet()) {
final String name = item.getKey();
source = "manifest: " + name;
atts = item.getValue();
for (Entry<Object, Object> entry : atts.entrySet()) {
final String key = entry.getKey().toString();
final String value = atts.getValue(key);
if (key.equalsIgnoreCase(Attributes.Name.IMPLEMENTATION_TITLE.toString())) {
foundSomething = true;
productEvidence.addEvidence(source, key, value, Confidence.MEDIUM);
addMatchingValues(classInformation, value, productEvidence);
} else if (key.equalsIgnoreCase(Attributes.Name.IMPLEMENTATION_VERSION.toString())) {
foundSomething = true;
versionEvidence.addEvidence(source, key, value, Confidence.MEDIUM);
} else if (key.equalsIgnoreCase(Attributes.Name.IMPLEMENTATION_VENDOR.toString())) {
foundSomething = true;
vendorEvidence.addEvidence(source, key, value, Confidence.MEDIUM);
addMatchingValues(classInformation, value, vendorEvidence);
} else if (key.equalsIgnoreCase(Attributes.Name.SPECIFICATION_TITLE.toString())) {
foundSomething = true;
productEvidence.addEvidence(source, key, value, Confidence.MEDIUM);
addMatchingValues(classInformation, value, productEvidence);
}
}
}
if (specificationVersion != null && !hasImplementationVersion) { if (specificationVersion != null && !hasImplementationVersion) {
foundSomething = true; foundSomething = true;
versionEvidence.addEvidence(source, "specificationn-version", specificationVersion, Confidence.HIGH); versionEvidence.addEvidence(source, "specification-version", specificationVersion, Confidence.HIGH);
} }
} finally { } finally {
if (jar != null) { if (jar != null) {
@@ -789,15 +818,18 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* Adds a description to the given dependency. If the description contains one of the following strings beyond 100 characters, * Adds a description to the given dependency. If the description contains
* then the description used will be trimmed to that position: * one of the following strings beyond 100 characters, then the description
* <ul><li>"such as"</li><li>"like "</li><li>"will use "</li><li>"* uses "</li></ul> * used will be trimmed to that position:
* <ul><li>"such as"</li><li>"like "</li><li>"will use "</li><li>"* uses
* "</li></ul>
* *
* @param dependency a dependency * @param dependency a dependency
* @param description the description * @param description the description
* @param source the source of the evidence * @param source the source of the evidence
* @param key the "name" of the evidence * @param key the "name" of the evidence
* @return if the description is trimmed, the trimmed version is returned; otherwise the original description is returned * @return if the description is trimmed, the trimmed version is returned;
* otherwise the original description is returned
*/ */
public static String addDescription(Dependency dependency, String description, String source, String key) { public static String addDescription(Dependency dependency, String description, String source, String key) {
if (dependency.getDescription() == null) { if (dependency.getDescription() == null) {
@@ -835,10 +867,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
} }
if (pos > 0) { if (pos > 0) {
final StringBuilder sb = new StringBuilder(pos + 3); desc = desc.substring(0, pos) + "...";
sb.append(desc.substring(0, pos));
sb.append("...");
desc = sb.toString();
} }
dependency.getProductEvidence().addEvidence(source, key, desc, Confidence.LOW); dependency.getProductEvidence().addEvidence(source, key, desc, Confidence.LOW);
dependency.getVendorEvidence().addEvidence(source, key, desc, Confidence.LOW); dependency.getVendorEvidence().addEvidence(source, key, desc, Confidence.LOW);
@@ -871,19 +900,27 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
/** /**
* Initializes the JarAnalyzer. * Initializes the JarAnalyzer.
* *
* @throws Exception is thrown if there is an exception creating a temporary directory * @throws InitializationException is thrown if there is an exception
* creating a temporary directory
*/ */
@Override @Override
public void initializeFileTypeAnalyzer() throws Exception { public void initializeFileTypeAnalyzer() throws InitializationException {
final File baseDir = Settings.getTempDirectory(); try {
tempFileLocation = File.createTempFile("check", "tmp", baseDir); final File baseDir = Settings.getTempDirectory();
if (!tempFileLocation.delete()) { tempFileLocation = File.createTempFile("check", "tmp", baseDir);
final String msg = String.format("Unable to delete temporary file '%s'.", tempFileLocation.getAbsolutePath()); if (!tempFileLocation.delete()) {
throw new AnalysisException(msg); final String msg = String.format("Unable to delete temporary file '%s'.", tempFileLocation.getAbsolutePath());
} setEnabled(false);
if (!tempFileLocation.mkdirs()) { throw new InitializationException(msg);
final String msg = String.format("Unable to create directory '%s'.", tempFileLocation.getAbsolutePath()); }
throw new AnalysisException(msg); if (!tempFileLocation.mkdirs()) {
final String msg = String.format("Unable to create directory '%s'.", tempFileLocation.getAbsolutePath());
setEnabled(false);
throw new InitializationException(msg);
}
} catch (IOException ex) {
setEnabled(false);
throw new InitializationException("Unable to create a temporary file", ex);
} }
} }
@@ -902,11 +939,13 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* Determines if the key value pair from the manifest is for an "import" type entry for package names. * Determines if the key value pair from the manifest is for an "import"
* type entry for package names.
* *
* @param key the key from the manifest * @param key the key from the manifest
* @param value the value from the manifest * @param value the value from the manifest
* @return true or false depending on if it is believed the entry is an "import" entry * @return true or false depending on if it is believed the entry is an
* "import" entry
*/ */
private boolean isImportPackage(String key, String value) { private boolean isImportPackage(String key, String value) {
final Pattern packageRx = Pattern.compile("^([a-zA-Z0-9_#\\$\\*\\.]+\\s*[,;]\\s*)+([a-zA-Z0-9_#\\$\\*\\.]+\\s*)?$"); final Pattern packageRx = Pattern.compile("^([a-zA-Z0-9_#\\$\\*\\.]+\\s*[,;]\\s*)+([a-zA-Z0-9_#\\$\\*\\.]+\\s*)?$");
@@ -915,8 +954,9 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* Cycles through an enumeration of JarEntries, contained within the dependency, and returns a list of the class names. This * Cycles through an enumeration of JarEntries, contained within the
* does not include core Java package names (i.e. java.* or javax.*). * dependency, and returns a list of the class names. This does not include
* core Java package names (i.e. java.* or javax.*).
* *
* @param dependency the dependency being analyzed * @param dependency the dependency being analyzed
* @return an list of fully qualified class names * @return an list of fully qualified class names
@@ -952,12 +992,16 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* Cycles through the list of class names and places the package levels 0-3 into the provided maps for vendor and product. * Cycles through the list of class names and places the package levels 0-3
* This is helpful when analyzing vendor/product as many times this is included in the package name. * into the provided maps for vendor and product. This is helpful when
* analyzing vendor/product as many times this is included in the package
* name.
* *
* @param classNames a list of class names * @param classNames a list of class names
* @param vendor HashMap of possible vendor names from package names (e.g. owasp) * @param vendor HashMap of possible vendor names from package names (e.g.
* @param product HashMap of possible product names from package names (e.g. dependencycheck) * owasp)
* @param product HashMap of possible product names from package names (e.g.
* dependencycheck)
*/ */
private void analyzeFullyQualifiedClassNames(List<ClassNameInformation> classNames, private void analyzeFullyQualifiedClassNames(List<ClassNameInformation> classNames,
Map<String, Integer> vendor, Map<String, Integer> product) { Map<String, Integer> vendor, Map<String, Integer> product) {
@@ -984,8 +1028,9 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* Adds an entry to the specified collection and sets the Integer (e.g. the count) to 1. If the entry already exists in the * Adds an entry to the specified collection and sets the Integer (e.g. the
* collection then the Integer is incremented by 1. * count) to 1. If the entry already exists in the collection then the
* Integer is incremented by 1.
* *
* @param collection a collection of strings and their occurrence count * @param collection a collection of strings and their occurrence count
* @param key the key to add to the collection * @param key the key to add to the collection
@@ -999,9 +1044,10 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* Cycles through the collection of class name information to see if parts of the package names are contained in the provided * Cycles through the collection of class name information to see if parts
* value. If found, it will be added as the HIGHEST confidence evidence because we have more then one source corroborating the * of the package names are contained in the provided value. If found, it
* value. * will be added as the HIGHEST confidence evidence because we have more
* then one source corroborating the value.
* *
* @param classes a collection of class name information * @param classes a collection of class name information
* @param value the value to check to see if it contains a package name * @param value the value to check to see if it contains a package name
@@ -1014,7 +1060,9 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
final String text = value.toLowerCase(); final String text = value.toLowerCase();
for (ClassNameInformation cni : classes) { for (ClassNameInformation cni : classes) {
for (String key : cni.getPackageStructure()) { for (String key : cni.getPackageStructure()) {
if (text.contains(key)) { //note, package structure elements are already lowercase. final Pattern p = Pattern.compile("\b" + key + "\b");
if (p.matcher(text).find()) {
//if (text.contains(key)) { //note, package structure elements are already lowercase.
evidence.addEvidence("jar", "package name", key, Confidence.HIGHEST); evidence.addEvidence("jar", "package name", key, Confidence.HIGHEST);
} }
} }
@@ -1022,7 +1070,8 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* Simple check to see if the attribute from a manifest is just a package name. * Simple check to see if the attribute from a manifest is just a package
* name.
* *
* @param key the key of the value to check * @param key the key of the value to check
* @param value the value to check * @param value the value to check
@@ -1036,7 +1085,8 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* Extracts the license information from the pom and adds it to the dependency. * Extracts the license information from the pom and adds it to the
* dependency.
* *
* @param pom the pom object * @param pom the pom object
* @param dependency the dependency to add license information too * @param dependency the dependency to add license information too
@@ -1083,9 +1133,11 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
/** /**
* <p> * <p>
* Stores information about a given class name. This class will keep the fully qualified class name and a list of the * Stores information about a given class name. This class will keep the
* important parts of the package structure. Up to the first four levels of the package structure are stored, excluding a * fully qualified class name and a list of the important parts of the
* leading "org" or "com". Example:</p> * package structure. Up to the first four levels of the package
* structure are stored, excluding a leading "org" or "com".
* Example:</p>
* <code>ClassNameInformation obj = new ClassNameInformation("org.owasp.dependencycheck.analyzer.JarAnalyzer"); * <code>ClassNameInformation obj = new ClassNameInformation("org.owasp.dependencycheck.analyzer.JarAnalyzer");
* System.out.println(obj.getName()); * System.out.println(obj.getName());
* for (String p : obj.getPackageStructure()) * for (String p : obj.getPackageStructure())
@@ -1144,7 +1196,8 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
this.name = name; this.name = name;
} }
/** /**
* Up to the first four levels of the package structure, excluding a leading "org" or "com". * Up to the first four levels of the package structure, excluding a
* leading "org" or "com".
*/ */
private final ArrayList<String> packageStructure = new ArrayList<String>(); private final ArrayList<String> packageStructure = new ArrayList<String>();

View File

@@ -35,6 +35,7 @@ import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import org.owasp.dependencycheck.exception.InitializationException;
import org.owasp.dependencycheck.utils.DownloadFailedException; import org.owasp.dependencycheck.utils.DownloadFailedException;
import org.owasp.dependencycheck.utils.Downloader; import org.owasp.dependencycheck.utils.Downloader;
import org.owasp.dependencycheck.utils.FileFilterBuilder; import org.owasp.dependencycheck.utils.FileFilterBuilder;
@@ -42,15 +43,18 @@ import org.owasp.dependencycheck.utils.InvalidSettingException;
import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.Settings;
/** /**
* Analyzer which will attempt to locate a dependency on a Nexus service by SHA-1 digest of the dependency. * Analyzer which will attempt to locate a dependency on a Nexus service by
* SHA-1 digest of the dependency.
* *
* There are two settings which govern this behavior: * There are two settings which govern this behavior:
* *
* <ul> * <ul>
* <li>{@link org.owasp.dependencycheck.utils.Settings.KEYS#ANALYZER_NEXUS_ENABLED} determines whether this analyzer is even * <li>{@link org.owasp.dependencycheck.utils.Settings.KEYS#ANALYZER_NEXUS_ENABLED}
* enabled. This can be overridden by setting the system property.</li> * determines whether this analyzer is even enabled. This can be overridden by
* <li>{@link org.owasp.dependencycheck.utils.Settings.KEYS#ANALYZER_NEXUS_URL} the URL to a Nexus service to search by SHA-1. * setting the system property.</li>
* There is an expected <code>%s</code> in this where the SHA-1 will get entered.</li> * <li>{@link org.owasp.dependencycheck.utils.Settings.KEYS#ANALYZER_NEXUS_URL}
* the URL to a Nexus service to search by SHA-1. There is an expected
* <code>%s</code> in this where the SHA-1 will get entered.</li>
* </ul> * </ul>
* *
* @author colezlaw * @author colezlaw
@@ -58,7 +62,8 @@ import org.owasp.dependencycheck.utils.Settings;
public class NexusAnalyzer extends AbstractFileTypeAnalyzer { public class NexusAnalyzer extends AbstractFileTypeAnalyzer {
/** /**
* The default URL - this will be used by the CentralAnalyzer to determine whether to enable this. * The default URL - this will be used by the CentralAnalyzer to determine
* whether to enable this.
*/ */
public static final String DEFAULT_URL = "https://repository.sonatype.org/service/local/"; public static final String DEFAULT_URL = "https://repository.sonatype.org/service/local/";
@@ -95,7 +100,8 @@ public class NexusAnalyzer extends AbstractFileTypeAnalyzer {
/** /**
* Determines if this analyzer is enabled * Determines if this analyzer is enabled
* *
* @return <code>true</code> if the analyzer is enabled; otherwise <code>false</code> * @return <code>true</code> if the analyzer is enabled; otherwise
* <code>false</code>
*/ */
private boolean checkEnabled() { private boolean checkEnabled() {
/* Enable this analyzer ONLY if the Nexus URL has been set to something /* Enable this analyzer ONLY if the Nexus URL has been set to something
@@ -131,10 +137,10 @@ public class NexusAnalyzer extends AbstractFileTypeAnalyzer {
/** /**
* Initializes the analyzer once before any analysis is performed. * Initializes the analyzer once before any analysis is performed.
* *
* @throws Exception if there's an error during initialization * @throws InitializationException if there's an error during initialization
*/ */
@Override @Override
public void initializeFileTypeAnalyzer() throws Exception { public void initializeFileTypeAnalyzer() throws InitializationException {
LOGGER.debug("Initializing Nexus Analyzer"); LOGGER.debug("Initializing Nexus Analyzer");
LOGGER.debug("Nexus Analyzer enabled: {}", isEnabled()); LOGGER.debug("Nexus Analyzer enabled: {}", isEnabled());
if (isEnabled()) { if (isEnabled()) {
@@ -143,14 +149,12 @@ public class NexusAnalyzer extends AbstractFileTypeAnalyzer {
try { try {
searcher = new NexusSearch(new URL(searchUrl)); searcher = new NexusSearch(new URL(searchUrl));
if (!searcher.preflightRequest()) { if (!searcher.preflightRequest()) {
LOGGER.warn("There was an issue getting Nexus status. Disabling analyzer.");
setEnabled(false); setEnabled(false);
throw new InitializationException("There was an issue getting Nexus status. Disabling analyzer.");
} }
} catch (MalformedURLException mue) { } catch (MalformedURLException mue) {
// I know that initialize can throw an exception, but we'll
// just disable the analyzer if the URL isn't valid
LOGGER.warn("Property {} not a valid URL. Nexus Analyzer disabled", searchUrl);
setEnabled(false); setEnabled(false);
throw new InitializationException("Malformed URL to Nexus: " + searchUrl, mue);
} }
} }
} }
@@ -166,7 +170,8 @@ public class NexusAnalyzer 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
*/ */
@@ -240,7 +245,8 @@ public class NexusAnalyzer extends AbstractFileTypeAnalyzer {
LOGGER.warn("Unable to download pom.xml for {} from Nexus repository; " LOGGER.warn("Unable to download pom.xml for {} from Nexus repository; "
+ "this could result in undetected CPE/CVEs.", dependency.getFileName()); + "this could result in undetected CPE/CVEs.", dependency.getFileName());
} finally { } finally {
if (pomFile != null && !FileUtils.deleteQuietly(pomFile)) { if (pomFile != null && pomFile.exists() && !FileUtils.deleteQuietly(pomFile)) {
LOGGER.debug("Failed to delete temporary pom file {}", pomFile.toString());
pomFile.deleteOnExit(); pomFile.deleteOnExit();
} }
} }

View File

@@ -38,13 +38,15 @@ import javax.json.JsonObject;
import javax.json.JsonReader; import javax.json.JsonReader;
import javax.json.JsonString; import javax.json.JsonString;
import javax.json.JsonValue; import javax.json.JsonValue;
import org.owasp.dependencycheck.exception.InitializationException;
/** /**
* Used to analyze Node Package Manager (npm) package.json files, and collect information that can be used to determine the * Used to analyze Node Package Manager (npm) package.json files, and collect
* associated CPE. * information that can be used to determine the associated CPE.
* *
* @author Dale Visser <dvisser@ida.org> * @author Dale Visser
*/ */
@Experimental
public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer { public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer {
/** /**
@@ -83,7 +85,7 @@ public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer {
} }
@Override @Override
protected void initializeFileTypeAnalyzer() throws Exception { protected void initializeFileTypeAnalyzer() throws InitializationException {
// NO-OP // NO-OP
} }
@@ -108,7 +110,8 @@ public class NodePackageAnalyzer 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
*/ */
@@ -154,7 +157,8 @@ public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* Adds information to an evidence collection from the node json configuration. * Adds information to an evidence collection from the node json
* configuration.
* *
* @param json information from node.js * @param json information from node.js
* @param collection a set of evidence about a dependency * @param collection a set of evidence about a dependency

View File

@@ -34,6 +34,7 @@ import java.io.FileFilter;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import org.owasp.dependencycheck.exception.InitializationException;
/** /**
* Analyzer which will parse a Nuspec file to gather module information. * Analyzer which will parse a Nuspec file to gather module information.
@@ -65,10 +66,10 @@ public class NuspecAnalyzer extends AbstractFileTypeAnalyzer {
/** /**
* Initializes the analyzer once before any analysis is performed. * Initializes the analyzer once before any analysis is performed.
* *
* @throws Exception if there's an error during initialization * @throws InitializationException if there's an error during initialization
*/ */
@Override @Override
public void initializeFileTypeAnalyzer() throws Exception { public void initializeFileTypeAnalyzer() throws InitializationException {
} }
/** /**
@@ -82,7 +83,8 @@ public class NuspecAnalyzer 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
*/ */

View File

@@ -27,6 +27,8 @@ import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.dependency.Identifier; import org.owasp.dependencycheck.dependency.Identifier;
import org.owasp.dependencycheck.dependency.Vulnerability; import org.owasp.dependencycheck.dependency.Vulnerability;
import org.owasp.dependencycheck.exception.InitializationException;
import org.slf4j.LoggerFactory;
/** /**
* NvdCveAnalyzer is a utility class that takes a project dependency and attempts to discern if there is an associated * NvdCveAnalyzer is a utility class that takes a project dependency and attempts to discern if there is an associated
@@ -35,7 +37,10 @@ import org.owasp.dependencycheck.dependency.Vulnerability;
* @author Jeremy Long * @author Jeremy Long
*/ */
public class NvdCveAnalyzer implements Analyzer { public class NvdCveAnalyzer implements Analyzer {
/**
* The Logger for use throughout the class
*/
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(NvdCveAnalyzer.class);
/** /**
* The maximum number of query results to return. * The maximum number of query results to return.
*/ */
@@ -79,7 +84,7 @@ public class NvdCveAnalyzer implements Analyzer {
/** /**
* Ensures that the CVE Database is closed. * Ensures that the CVE Database is closed.
* *
* @throws Throwable when a throwable is thrown. * @throws Throwable an exception raised by this method
*/ */
@Override @Override
protected void finalize() throws Throwable { protected void finalize() throws Throwable {
@@ -94,7 +99,7 @@ public class NvdCveAnalyzer implements Analyzer {
* *
* @param dependency The Dependency to analyze * @param dependency The Dependency to analyze
* @param engine The analysis engine * @param engine The analysis engine
* @throws AnalysisException is thrown if there is an issue analyzing the dependency * @throws AnalysisException thrown if there is an issue analyzing the dependency
*/ */
@Override @Override
public void analyze(Dependency dependency, Engine engine) throws AnalysisException { public void analyze(Dependency dependency, Engine engine) throws AnalysisException {
@@ -145,10 +150,24 @@ public class NvdCveAnalyzer implements Analyzer {
/** /**
* Opens the database used to gather NVD CVE data. * Opens the database used to gather NVD CVE data.
* *
* @throws Exception is thrown if there is an issue opening the index. * @throws InitializationException is thrown if there is an issue opening the index.
*/ */
@Override @Override
public void initialize() throws Exception { public void initialize() throws InitializationException {
this.open(); try {
this.open();
} catch (SQLException ex) {
LOGGER.debug("SQL Exception initializing NvdCveAnalyzer", ex);
throw new InitializationException(ex);
} catch (IOException ex) {
LOGGER.debug("IO Exception initializing NvdCveAnalyzer", ex);
throw new InitializationException(ex);
} catch (DatabaseException ex) {
LOGGER.debug("Database Exception initializing NvdCveAnalyzer", ex);
throw new InitializationException(ex);
} catch (ClassNotFoundException ex) {
LOGGER.debug("Exception initializing NvdCveAnalyzer", ex);
throw new InitializationException(ex);
}
} }
} }

View File

@@ -28,16 +28,21 @@ import org.owasp.dependencycheck.utils.Settings;
import java.io.File; import java.io.File;
import java.io.FileFilter; import java.io.FileFilter;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.owasp.dependencycheck.exception.InitializationException;
/** /**
* Used to analyze OpenSSL source code present in the file system. * Used to analyze OpenSSL source code present in the file system.
* *
* @author Dale Visser <dvisser@ida.org> * @author Dale Visser
*/ */
public class OpenSSLAnalyzer extends AbstractFileTypeAnalyzer { public class OpenSSLAnalyzer extends AbstractFileTypeAnalyzer {
/**
* Hexadecimal.
*/
private static final int HEXADECIMAL = 16; private static final int HEXADECIMAL = 16;
/** /**
* Filename to analyze. All other .h files get removed from consideration. * Filename to analyze. All other .h files get removed from consideration.
@@ -48,17 +53,47 @@ public class OpenSSLAnalyzer extends AbstractFileTypeAnalyzer {
* Filter that detects files named "__init__.py". * Filter that detects files named "__init__.py".
*/ */
private static final FileFilter OPENSSLV_FILTER = FileFilterBuilder.newInstance().addFilenames(OPENSSLV_H).build(); private static final FileFilter OPENSSLV_FILTER = FileFilterBuilder.newInstance().addFilenames(OPENSSLV_H).build();
/**
* Open SSL Version number pattern.
*/
private static final Pattern VERSION_PATTERN = Pattern.compile( private static final Pattern VERSION_PATTERN = Pattern.compile(
"define\\s+OPENSSL_VERSION_NUMBER\\s+0x([0-9a-zA-Z]{8})L", Pattern.DOTALL "define\\s+OPENSSL_VERSION_NUMBER\\s+0x([0-9a-zA-Z]{8})L", Pattern.DOTALL
| Pattern.CASE_INSENSITIVE); | Pattern.CASE_INSENSITIVE);
/**
* The offset of the major version number.
*/
private static final int MAJOR_OFFSET = 28; private static final int MAJOR_OFFSET = 28;
/**
* The mask for the minor version number.
*/
private static final long MINOR_MASK = 0x0ff00000L; private static final long MINOR_MASK = 0x0ff00000L;
/**
* The offset of the minor version number.
*/
private static final int MINOR_OFFSET = 20; private static final int MINOR_OFFSET = 20;
/**
* The max for the fix version.
*/
private static final long FIX_MASK = 0x000ff000L; private static final long FIX_MASK = 0x000ff000L;
/**
* The offset for the fix version.
*/
private static final int FIX_OFFSET = 12; private static final int FIX_OFFSET = 12;
/**
* The mask for the patch version.
*/
private static final long PATCH_MASK = 0x00000ff0L; private static final long PATCH_MASK = 0x00000ff0L;
/**
* The offset for the patch version.
*/
private static final int PATCH_OFFSET = 4; private static final int PATCH_OFFSET = 4;
/**
* Number of letters.
*/
private static final int NUM_LETTERS = 26; private static final int NUM_LETTERS = 26;
/**
* The status mask.
*/
private static final int STATUS_MASK = 0x0000000f; private static final int STATUS_MASK = 0x0000000f;
/** /**
@@ -111,10 +146,10 @@ public class OpenSSLAnalyzer extends AbstractFileTypeAnalyzer {
/** /**
* No-op initializer implementation. * No-op initializer implementation.
* *
* @throws Exception never thrown * @throws InitializationException never thrown
*/ */
@Override @Override
protected void initializeFileTypeAnalyzer() throws Exception { protected void initializeFileTypeAnalyzer() throws InitializationException {
// Nothing to do here. // Nothing to do here.
} }
@@ -123,7 +158,8 @@ public class OpenSSLAnalyzer 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)
@@ -158,16 +194,19 @@ public class OpenSSLAnalyzer extends AbstractFileTypeAnalyzer {
*/ */
private String getFileContents(final File actualFile) private String getFileContents(final File actualFile)
throws AnalysisException { throws AnalysisException {
String contents;
try { try {
contents = FileUtils.readFileToString(actualFile).trim(); return FileUtils.readFileToString(actualFile, Charset.defaultCharset()).trim();
} catch (IOException e) { } catch (IOException e) {
throw new AnalysisException( throw new AnalysisException(
"Problem occurred while reading dependency file.", e); "Problem occurred while reading dependency file.", e);
} }
return contents;
} }
/**
* Returns the setting for the analyzer enabled setting key.
*
* @return the setting for the analyzer enabled setting key
*/
@Override @Override
protected String getAnalyzerEnabledSettingKey() { protected String getAnalyzerEnabledSettingKey() {
return Settings.KEYS.ANALYZER_OPENSSL_ENABLED; return Settings.KEYS.ANALYZER_OPENSSL_ENABLED;

View File

@@ -23,9 +23,10 @@ import java.io.FileFilter;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FilenameFilter; import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.io.filefilter.NameFileFilter; import org.apache.commons.io.filefilter.NameFileFilter;
import org.apache.commons.io.filefilter.SuffixFileFilter; import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.apache.commons.io.input.AutoCloseInputStream;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
@@ -37,6 +38,7 @@ import org.slf4j.LoggerFactory;
import javax.mail.MessagingException; import javax.mail.MessagingException;
import javax.mail.internet.InternetHeaders; import javax.mail.internet.InternetHeaders;
import org.owasp.dependencycheck.exception.InitializationException;
import org.owasp.dependencycheck.utils.ExtractionException; import org.owasp.dependencycheck.utils.ExtractionException;
import org.owasp.dependencycheck.utils.ExtractionUtil; import org.owasp.dependencycheck.utils.ExtractionUtil;
import org.owasp.dependencycheck.utils.FileFilterBuilder; import org.owasp.dependencycheck.utils.FileFilterBuilder;
@@ -45,11 +47,13 @@ import org.owasp.dependencycheck.utils.Settings;
import org.owasp.dependencycheck.utils.UrlStringUtils; import org.owasp.dependencycheck.utils.UrlStringUtils;
/** /**
* Used to analyze a Wheel or egg distribution files, or their contents in unzipped form, and collect information that can be used * Used to analyze a Wheel or egg distribution files, or their contents in
* to determine the associated CPE. * unzipped form, and collect information that can be used to determine the
* associated CPE.
* *
* @author Dale Visser <dvisser@ida.org> * @author Dale Visser
*/ */
@Experimental
public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer { public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
/** /**
@@ -69,7 +73,8 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
.getLogger(PythonDistributionAnalyzer.class); .getLogger(PythonDistributionAnalyzer.class);
/** /**
* The count of directories created during analysis. This is used for creating temporary directories. * The count of directories created during analysis. This is used for
* creating temporary directories.
*/ */
private static int dirCount = 0; private static int dirCount = 0;
@@ -103,7 +108,8 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
private File tempFileLocation; private File tempFileLocation;
/** /**
* Filter that detects *.dist-info files (but doesn't verify they are directories. * Filter that detects *.dist-info files (but doesn't verify they are
* directories.
*/ */
private static final FilenameFilter DIST_INFO_FILTER = new SuffixFileFilter( private static final FilenameFilter DIST_INFO_FILTER = new SuffixFileFilter(
".dist-info"); ".dist-info");
@@ -163,7 +169,8 @@ public class PythonDistributionAnalyzer 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
*/ */
@@ -205,7 +212,8 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
* @param dependency the archive being scanned * @param dependency the archive being scanned
* @param folderFilter the filter to apply to the folder * @param folderFilter the filter to apply to the folder
* @param metadataFilter the filter to apply to the meta data * @param metadataFilter the filter to apply to the meta data
* @throws AnalysisException thrown when there is a problem analyzing the dependency * @throws AnalysisException thrown when there is a problem analyzing the
* dependency
*/ */
private void collectMetadataFromArchiveFormat(Dependency dependency, private void collectMetadataFromArchiveFormat(Dependency dependency,
FilenameFilter folderFilter, FilenameFilter metadataFilter) FilenameFilter folderFilter, FilenameFilter metadataFilter)
@@ -220,32 +228,43 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
throw new AnalysisException(ex); throw new AnalysisException(ex);
} }
collectWheelMetadata( File matchingFile = getMatchingFile(temp, folderFilter);
dependency, if (matchingFile != null) {
getMatchingFile(getMatchingFile(temp, folderFilter), matchingFile = getMatchingFile(matchingFile, metadataFilter);
metadataFilter)); if (matchingFile != null) {
collectWheelMetadata(dependency, matchingFile);
}
}
} }
/** /**
* Makes sure a usable temporary directory is available. * Makes sure a usable temporary directory is available.
* *
* @throws Exception an AnalyzeException is thrown when the temp directory cannot be created * @throws InitializationException an AnalyzeException is thrown when the
* temp directory cannot be created
*/ */
@Override @Override
protected void initializeFileTypeAnalyzer() throws Exception { protected void initializeFileTypeAnalyzer() throws InitializationException {
final File baseDir = Settings.getTempDirectory(); try {
tempFileLocation = File.createTempFile("check", "tmp", baseDir); final File baseDir = Settings.getTempDirectory();
if (!tempFileLocation.delete()) { tempFileLocation = File.createTempFile("check", "tmp", baseDir);
final String msg = String.format( if (!tempFileLocation.delete()) {
"Unable to delete temporary file '%s'.", setEnabled(false);
tempFileLocation.getAbsolutePath()); final String msg = String.format(
throw new AnalysisException(msg); "Unable to delete temporary file '%s'.",
} tempFileLocation.getAbsolutePath());
if (!tempFileLocation.mkdirs()) { throw new InitializationException(msg);
final String msg = String.format( }
"Unable to create directory '%s'.", if (!tempFileLocation.mkdirs()) {
tempFileLocation.getAbsolutePath()); setEnabled(false);
throw new AnalysisException(msg); final String msg = String.format(
"Unable to create directory '%s'.",
tempFileLocation.getAbsolutePath());
throw new InitializationException(msg);
}
} catch (IOException ex) {
setEnabled(false);
throw new InitializationException("Unable to create a temporary file", ex);
} }
} }
@@ -311,7 +330,8 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* Returns a list of files that match the given filter, this does not recursively scan the directory. * Returns a list of files that match the given filter, this does not
* recursively scan the directory.
* *
* @param folder the folder to filter * @param folder the folder to filter
* @param filter the filter to apply to the files in the directory * @param filter the filter to apply to the files in the directory
@@ -337,20 +357,30 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
if (null == manifest) { if (null == manifest) {
LOGGER.debug("Manifest file not found."); LOGGER.debug("Manifest file not found.");
} else { } else {
InputStream in = null;
try { try {
result.load(new AutoCloseInputStream(new BufferedInputStream( in = new BufferedInputStream(new FileInputStream(manifest));
new FileInputStream(manifest)))); result.load(in);
} catch (MessagingException e) { } catch (MessagingException e) {
LOGGER.warn(e.getMessage(), e); LOGGER.warn(e.getMessage(), e);
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
LOGGER.warn(e.getMessage(), e); LOGGER.warn(e.getMessage(), e);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ex) {
LOGGER.debug("failed to close input stream", ex);
}
}
} }
} }
return result; return result;
} }
/** /**
* Retrieves the next temporary destination directory for extracting an archive. * Retrieves the next temporary destination directory for extracting an
* archive.
* *
* @return a directory * @return a directory
* @throws AnalysisException thrown if unable to create temporary directory * @throws AnalysisException thrown if unable to create temporary directory

View File

@@ -32,16 +32,20 @@ import org.owasp.dependencycheck.utils.UrlStringUtils;
import java.io.File; import java.io.File;
import java.io.FileFilter; import java.io.FileFilter;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.owasp.dependencycheck.exception.InitializationException;
/** /**
* 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 <dvisser@ida.org> * @author Dale Visser
*/ */
@Experimental
public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer { public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer {
/** /**
@@ -141,10 +145,10 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer {
/** /**
* No-op initializer implementation. * No-op initializer implementation.
* *
* @throws Exception never thrown * @throws InitializationException never thrown
*/ */
@Override @Override
protected void initializeFileTypeAnalyzer() throws Exception { protected void initializeFileTypeAnalyzer() throws InitializationException {
// Nothing to do here. // Nothing to do here.
} }
@@ -165,7 +169,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)
@@ -173,19 +178,20 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer {
final File file = dependency.getActualFile(); final File file = dependency.getActualFile();
final File parent = file.getParentFile(); final File parent = file.getParentFile();
final String parentName = parent.getName(); final String parentName = parent.getName();
boolean found = false;
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:
//"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;
dependency.setDisplayFileName(parentName + "/__init__.py");
dependency.getProductEvidence().addEvidence(file.getName(),
"PackageName", parentName, Confidence.HIGHEST);
final File[] fileList = parent.listFiles(PY_FILTER); final File[] fileList = parent.listFiles(PY_FILTER);
if (fileList != null) { if (fileList != null) {
for (final File sourceFile : fileList) { for (final File sourceFile : fileList) {
found |= analyzeFileContents(dependency, sourceFile); analyzeFileContents(dependency, sourceFile);
} }
} }
}
if (found) {
dependency.setDisplayFileName(parentName + "/__init__.py");
dependency.getProductEvidence().addEvidence(file.getName(),
"PackageName", parentName, Confidence.MEDIUM);
} 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>(
@@ -196,8 +202,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
@@ -208,7 +215,7 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer {
throws AnalysisException { throws AnalysisException {
String contents; String contents;
try { try {
contents = FileUtils.readFileToString(file).trim(); contents = FileUtils.readFileToString(file, Charset.defaultCharset()).trim();
} catch (IOException e) { } catch (IOException e) {
throw new AnalysisException( throw new AnalysisException(
"Problem occurred while reading dependency file.", e); "Problem occurred while reading dependency file.", e);
@@ -288,7 +295,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

@@ -17,28 +17,45 @@
*/ */
package org.owasp.dependencycheck.analyzer; package org.owasp.dependencycheck.analyzer;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
import org.owasp.dependencycheck.data.nvdcve.CveDB;
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Confidence;
import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.dependency.Reference; import org.owasp.dependencycheck.dependency.Reference;
import org.owasp.dependencycheck.dependency.Vulnerability; import org.owasp.dependencycheck.dependency.Vulnerability;
import org.owasp.dependencycheck.exception.InitializationException;
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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.*;
/** /**
* Used to analyze Ruby Bundler Gemspec.lock files utilizing the 3rd party bundle-audit tool. * Used to analyze Ruby Bundler Gemspec.lock files utilizing the 3rd party
* bundle-audit tool.
* *
* @author Dale Visser <dvisser@ida.org> * @author Dale Visser
*/ */
@Experimental
public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
/**
* The logger.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(RubyBundleAuditAnalyzer.class); private static final Logger LOGGER = LoggerFactory.getLogger(RubyBundleAuditAnalyzer.class);
/** /**
@@ -50,14 +67,32 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
* The phase that this analyzer is intended to run in. * The phase that this analyzer is intended to run in.
*/ */
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 = * The filter defining which files will be analyzed.
FileFilterBuilder.newInstance().addFilenames("Gemfile.lock").build(); */
private static final FileFilter FILTER = FileFilterBuilder.newInstance().addFilenames("Gemfile.lock").build();
/**
* Name.
*/
public static final String NAME = "Name: "; public static final String NAME = "Name: ";
/**
* Version.
*/
public static final String VERSION = "Version: "; public static final String VERSION = "Version: ";
/**
* Advisory.
*/
public static final String ADVISORY = "Advisory: "; public static final String ADVISORY = "Advisory: ";
/**
* Criticality.
*/
public static final String CRITICALITY = "Criticality: "; public static final String CRITICALITY = "Criticality: ";
/**
* The DAL.
*/
private CveDB cvedb;
/** /**
* @return a filter that accepts files named Gemfile.lock * @return a filter that accepts files named Gemfile.lock
*/ */
@@ -69,7 +104,10 @@ 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()) {
@@ -83,6 +121,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
final ProcessBuilder builder = new ProcessBuilder(args); final ProcessBuilder builder = new ProcessBuilder(args);
builder.directory(folder); builder.directory(folder);
try { try {
LOGGER.info("Launching: " + args + " from " + folder);
return builder.start(); return builder.start();
} catch (IOException ioe) { } catch (IOException ioe) {
throw new AnalysisException("bundle-audit failure", ioe); throw new AnalysisException("bundle-audit failure", ioe);
@@ -90,19 +129,50 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* Initialize the analyzer. In this case, extract GrokAssembly.exe to a temporary location. * Initialize the analyzer. In this case, extract GrokAssembly.exe to a
* temporary location.
* *
* @throws Exception if anything goes wrong * @throws InitializationException if anything goes wrong
*/ */
@Override @Override
public void initializeFileTypeAnalyzer() throws Exception { public void initializeFileTypeAnalyzer() throws InitializationException {
// Now, need to see if bundle-audit actually runs from this location. try {
Process process = launchBundleAudit(Settings.getTempDirectory()); cvedb = new CveDB();
int exitValue = process.waitFor(); cvedb.open();
if (0 == exitValue) { } catch (DatabaseException ex) {
LOGGER.warn("Unexpected exit code from bundle-audit process. Disabling {}: {}", ANALYZER_NAME, exitValue); LOGGER.warn("Exception opening the database");
LOGGER.debug("error", ex);
setEnabled(false); setEnabled(false);
throw new AnalysisException("Unexpected exit code from bundle-audit process."); throw new InitializationException("Error connecting to the database", ex);
}
// Now, need to see if bundle-audit actually runs from this location.
Process process = null;
try {
process = launchBundleAudit(Settings.getTempDirectory());
} catch (AnalysisException ae) {
setEnabled(false);
cvedb.close();
cvedb = null;
final String msg = String.format("Exception from bundle-audit process: %s. Disabling %s", ae.getCause(), ANALYZER_NAME);
throw new InitializationException(msg, ae);
} catch (IOException ex) {
setEnabled(false);
throw new InitializationException("Unable to create temporary file, the Ruby Bundle Audit Analyzer will be disabled", ex);
}
final int exitValue;
try {
exitValue = process.waitFor();
} catch (InterruptedException ex) {
setEnabled(false);
final String msg = String.format("Bundle-audit process was interupted. Disabling %s", ANALYZER_NAME);
throw new InitializationException(msg);
}
if (0 == exitValue) {
setEnabled(false);
final String msg = String.format("Unexpected exit code from bundle-audit process. Disabling %s: %s", ANALYZER_NAME, exitValue);
throw new InitializationException(msg);
} else { } else {
BufferedReader reader = null; BufferedReader reader = null;
try { try {
@@ -110,24 +180,35 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
if (!reader.ready()) { if (!reader.ready()) {
LOGGER.warn("Bundle-audit error stream unexpectedly not ready. Disabling " + ANALYZER_NAME); LOGGER.warn("Bundle-audit error stream unexpectedly not ready. Disabling " + ANALYZER_NAME);
setEnabled(false); setEnabled(false);
throw new AnalysisException("Bundle-audit error stream unexpectedly not ready."); throw new InitializationException("Bundle-audit error stream unexpectedly not ready.");
} else { } else {
final String line = reader.readLine(); final String line = reader.readLine();
if (!line.contains("Errno::ENOENT")) { if (line == null || !line.contains("Errno::ENOENT")) {
LOGGER.warn("Unexpected bundle-audit output. Disabling {}: {}", ANALYZER_NAME, line); LOGGER.warn("Unexpected bundle-audit output. Disabling {}: {}", ANALYZER_NAME, line);
setEnabled(false); setEnabled(false);
throw new AnalysisException("Unexpected bundle-audit output."); throw new InitializationException("Unexpected bundle-audit output.");
} }
} }
} catch (UnsupportedEncodingException ex) {
setEnabled(false);
throw new InitializationException("Unexpected bundle-audit encoding.", ex);
} catch (IOException ex) {
setEnabled(false);
throw new InitializationException("Unable to read bundle-audit output.", ex);
} finally { } finally {
if (null != reader) { if (null != reader) {
reader.close(); try {
reader.close();
} catch (IOException ex) {
LOGGER.debug("Error closing reader", ex);
}
} }
} }
} }
if (isEnabled()) { if (isEnabled()) {
LOGGER.info(ANALYZER_NAME + " is enabled. It is necessary to manually run \"bundle-audit update\" " + LOGGER.info(ANALYZER_NAME + " is enabled. It is necessary to manually run \"bundle-audit update\" "
"occasionally to keep its database up to date."); + "occasionally to keep its database up to date.");
} }
} }
@@ -152,7 +233,8 @@ public class RubyBundleAuditAnalyzer 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
*/ */
@@ -162,11 +244,19 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
} }
/** /**
* If {@link #analyzeFileType(Dependency, Engine)} is called, then we have successfully initialized, and it will * If {@link #analyzeFileType(Dependency, Engine)} is called, then we have
* be necessary to disable {@link RubyGemspecAnalyzer}. * successfully initialized, and it will be necessary to disable
* {@link RubyGemspecAnalyzer}.
*/ */
private boolean needToDisableGemspecAnalyzer = true; private boolean needToDisableGemspecAnalyzer = true;
/**
* Determines if the analyzer can analyze the given file type.
*
* @param dependency the dependency to determine if it can analyze
* @param engine the dependency-check engine
* @throws AnalysisException thrown if there is an analysis exception.
*/
@Override @Override
protected void analyzeFileType(Dependency dependency, Engine engine) protected void analyzeFileType(Dependency dependency, Engine engine)
throws AnalysisException { throws AnalysisException {
@@ -174,31 +264,52 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
boolean failed = true; boolean failed = true;
final String className = RubyGemspecAnalyzer.class.getName(); final String className = RubyGemspecAnalyzer.class.getName();
for (FileTypeAnalyzer analyzer : engine.getFileTypeAnalyzers()) { for (FileTypeAnalyzer analyzer : engine.getFileTypeAnalyzers()) {
if (analyzer instanceof RubyGemspecAnalyzer) { if (analyzer instanceof RubyBundlerAnalyzer) {
((RubyBundlerAnalyzer) analyzer).setEnabled(false);
LOGGER.info("Disabled " + RubyBundlerAnalyzer.class.getName() + " to avoid noisy duplicate results.");
} else if (analyzer instanceof RubyGemspecAnalyzer) {
((RubyGemspecAnalyzer) analyzer).setEnabled(false); ((RubyGemspecAnalyzer) analyzer).setEnabled(false);
LOGGER.info("Disabled " + className + " to avoid noisy duplicate results."); LOGGER.info("Disabled " + className + " to avoid noisy duplicate results.");
failed = false; failed = false;
} }
} }
if (failed) { if (failed) {
LOGGER.warn("Did not find" + className + '.'); LOGGER.warn("Did not find " + className + '.');
} }
needToDisableGemspecAnalyzer = false; needToDisableGemspecAnalyzer = false;
} }
final File parentFile = dependency.getActualFile().getParentFile(); final File parentFile = dependency.getActualFile().getParentFile();
final Process process = launchBundleAudit(parentFile); final Process process = launchBundleAudit(parentFile);
final int exitValue;
try { try {
process.waitFor(); exitValue = process.waitFor();
} catch (InterruptedException ie) { } catch (InterruptedException ie) {
throw new AnalysisException("bundle-audit process interrupted", ie); throw new AnalysisException("bundle-audit process interrupted", ie);
} }
if (exitValue != 0) {
final String msg = String.format("Unexpected exit code from bundle-audit process; exit code: %s", exitValue);
throw new AnalysisException(msg);
}
BufferedReader rdr = null; BufferedReader rdr = null;
BufferedReader errReader = null;
try { try {
errReader = new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8"));
while (errReader.ready()) {
final String error = errReader.readLine();
LOGGER.warn(error);
}
rdr = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8")); rdr = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
processBundlerAuditOutput(dependency, engine, rdr); processBundlerAuditOutput(dependency, engine, rdr);
} catch (IOException ioe) { } catch (IOException ioe) {
LOGGER.warn("bundle-audit failure", ioe); LOGGER.warn("bundle-audit failure", ioe);
} finally { } finally {
if (errReader != null) {
try {
errReader.close();
} catch (IOException ioe) {
LOGGER.warn("bundle-audit close failure", ioe);
}
}
if (null != rdr) { if (null != rdr) {
try { try {
rdr.close(); rdr.close();
@@ -210,9 +321,18 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
} }
/**
* Processes the bundler audit output.
*
* @param original the dependency
* @param engine the dependency-check engine
* @param rdr the reader of the report
* @throws IOException thrown if the report cannot be read.
*/
private void processBundlerAuditOutput(Dependency original, Engine engine, BufferedReader rdr) throws IOException { private void processBundlerAuditOutput(Dependency original, Engine engine, BufferedReader rdr) throws IOException {
final String parentName = original.getActualFile().getParentFile().getName(); final String parentName = original.getActualFile().getParentFile().getName();
final String fileName = original.getFileName(); final String fileName = original.getFileName();
final String filePath = original.getFilePath();
Dependency dependency = null; Dependency dependency = null;
Vulnerability vulnerability = null; Vulnerability vulnerability = null;
String gem = null; String gem = null;
@@ -226,12 +346,12 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
appendToDescription = false; appendToDescription = false;
gem = nextLine.substring(NAME.length()); gem = nextLine.substring(NAME.length());
if (!map.containsKey(gem)) { if (!map.containsKey(gem)) {
map.put(gem, createDependencyForGem(engine, parentName, fileName, gem)); map.put(gem, createDependencyForGem(engine, parentName, fileName, filePath, gem));
} }
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)) {
@@ -241,7 +361,9 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
} else if (nextLine.startsWith("Description:")) { } else if (nextLine.startsWith("Description:")) {
appendToDescription = true; appendToDescription = true;
if (null != vulnerability) { if (null != vulnerability) {
vulnerability.setDescription("*** Vulnerability obtained from bundle-audit verbose report. Title link may not work. CPE below is guessed. CVSS score is estimated (-1.0 indicates unknown). See link below for full details. *** "); vulnerability.setDescription("*** Vulnerability obtained from bundle-audit verbose report. "
+ "Title link may not work. CPE below is guessed. CVSS score is estimated (-1.0 "
+ " indicates unknown). See link below for full details. *** ");
} }
} else if (appendToDescription) { } else if (appendToDescription) {
if (null != vulnerability) { if (null != vulnerability) {
@@ -251,6 +373,14 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
} }
} }
/**
* Sets the vulnerability name.
*
* @param parentName the parent name
* @param dependency the dependency
* @param vulnerability the vulnerability
* @param nextLine the line to parse
*/
private void setVulnerabilityName(String parentName, Dependency dependency, Vulnerability vulnerability, String nextLine) { private void setVulnerabilityName(String parentName, Dependency dependency, Vulnerability vulnerability, String nextLine) {
final String advisory = nextLine.substring((ADVISORY.length())); final String advisory = nextLine.substring((ADVISORY.length()));
if (null != vulnerability) { if (null != vulnerability) {
@@ -262,10 +392,17 @@ 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));
} }
/**
* Adds a reference to the vulnerability.
*
* @param parentName the parent name
* @param vulnerability the vulnerability
* @param nextLine the line to parse
*/
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);
@@ -274,23 +411,48 @@ 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));
} }
/**
* Adds the criticality to the vulnerability
*
* @param parentName the parent name
* @param vulnerability the vulnerability
* @param nextLine the line to parse
*/
private void addCriticalityToVulnerability(String parentName, Vulnerability vulnerability, String nextLine) { private void addCriticalityToVulnerability(String parentName, Vulnerability vulnerability, String nextLine) {
if (null != vulnerability) { if (null != vulnerability) {
final String criticality = nextLine.substring(CRITICALITY.length()).trim(); final String criticality = nextLine.substring(CRITICALITY.length()).trim();
if ("High".equals(criticality)) { float score = -1.0f;
vulnerability.setCvssScore(8.5f); Vulnerability v = null;
} else if ("Medium".equals(criticality)) { try {
vulnerability.setCvssScore(5.5f); v = cvedb.getVulnerability(vulnerability.getName());
} else if ("Low".equals(criticality)) { } catch (DatabaseException ex) {
vulnerability.setCvssScore(2.0f); LOGGER.debug("Unable to look up vulnerability {}", vulnerability.getName());
} else {
vulnerability.setCvssScore(-1.0f);
} }
if (v != null) {
score = v.getCvssScore();
} else if ("High".equalsIgnoreCase(criticality)) {
score = 8.5f;
} else if ("Medium".equalsIgnoreCase(criticality)) {
score = 5.5f;
} else if ("Low".equalsIgnoreCase(criticality)) {
score = 2.0f;
}
vulnerability.setCvssScore(score);
} }
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) { /**
* Creates a vulnerability.
*
* @param parentName the parent name
* @param dependency the dependency
* @param gem the gem name
* @param nextLine the line to parse
* @return the vulnerability
*/
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(
@@ -313,13 +475,30 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
return vulnerability; return vulnerability;
} }
private Dependency createDependencyForGem(Engine engine, String parentName, String fileName, String gem) throws IOException { /**
final File tempFile = File.createTempFile("Gemfile-" + gem, ".lock", Settings.getTempDirectory()); * Creates the dependency based off of the gem.
*
* @param engine the engine used for scanning
* @param parentName the gem parent
* @param fileName the file name
* @param filePath the file path
* @param gem the gem name
* @return the dependency to add
* @throws IOException thrown if a temporary gem file could not be written
*/
private Dependency createDependencyForGem(Engine engine, String parentName, String fileName, String filePath, String gem) throws IOException {
final File gemFile = new File(Settings.getTempDirectory(), gem + "_Gemfile.lock");
if (!gemFile.createNewFile()) {
throw new IOException("Unable to create temporary gem file");
}
final String displayFileName = String.format("%s%c%s:%s", parentName, File.separatorChar, fileName, gem); final String displayFileName = String.format("%s%c%s:%s", parentName, File.separatorChar, fileName, gem);
FileUtils.write(tempFile, displayFileName); // unique contents to avoid dependency bundling
final Dependency dependency = new Dependency(tempFile); FileUtils.write(gemFile, displayFileName, Charset.defaultCharset()); // unique contents to avoid dependency bundling
final Dependency dependency = new Dependency(gemFile);
dependency.getProductEvidence().addEvidence("bundler-audit", "Name", gem, Confidence.HIGHEST); dependency.getProductEvidence().addEvidence("bundler-audit", "Name", gem, Confidence.HIGHEST);
dependency.setDisplayFileName(displayFileName); dependency.setDisplayFileName(displayFileName);
dependency.setFileName(fileName);
dependency.setFilePath(filePath);
engine.getDependencies().add(dependency); engine.getDependencies().add(dependency);
return dependency; return dependency;
} }

View File

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

View File

@@ -17,28 +17,41 @@
*/ */
package org.owasp.dependencycheck.analyzer; package org.owasp.dependencycheck.analyzer;
import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Confidence;
import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.dependency.EvidenceCollection; import org.owasp.dependencycheck.dependency.EvidenceCollection;
import org.owasp.dependencycheck.exception.InitializationException;
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 java.io.FileFilter; import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* Used to analyze Ruby Gem specifications and collect information that can be used to determine the associated CPE. * Used to analyze Ruby Gem specifications and collect information that can be
* Regular 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 <dvisser@ida.org> * @author Dale Visser
*/ */
@Experimental
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.
*/ */
@@ -49,15 +62,26 @@ 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).addFilenames("Rakefile").build(); * The file filter containing the list of file extensions that can be
* analyzed.
private static final String EMAIL = "email"; */
private static final FileFilter FILTER = FileFilterBuilder.newInstance().addExtensions(GEMSPEC).build();
//TODO: support Rakefile
//= FileFilterBuilder.newInstance().addExtensions(GEMSPEC).addFilenames("Rakefile").build();
/** /**
* @return a filter that accepts files named Rakefile or matching the glob pattern, *.gemspec * The name of the version file.
*/
private static final String VERSION_FILE_NAME = "VERSION";
/**
* @return a filter that accepts files matching the glob pattern, *.gemspec
*/ */
@Override @Override
protected FileFilter getFileFilter() { protected FileFilter getFileFilter() {
@@ -65,7 +89,7 @@ public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer {
} }
@Override @Override
protected void initializeFileTypeAnalyzer() throws Exception { protected void initializeFileTypeAnalyzer() throws InitializationException {
// NO-OP // NO-OP
} }
@@ -90,7 +114,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
*/ */
@@ -102,15 +127,14 @@ 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)
throws AnalysisException { throws AnalysisException {
String contents; String contents;
try { try {
contents = FileUtils.readFileToString(dependency.getActualFile()); contents = FileUtils.readFileToString(dependency.getActualFile(), Charset.defaultCharset());
} catch (IOException e) { } catch (IOException e) {
throw new AnalysisException( throw new AnalysisException(
"Problem occurred while reading dependency file.", e); "Problem occurred while reading dependency file.", e);
@@ -119,43 +143,107 @@ public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer {
if (matcher.find()) { if (matcher.find()) {
contents = contents.substring(matcher.end()); contents = contents.substring(matcher.end());
final String blockVariable = matcher.group(1); final String blockVariable = matcher.group(1);
final EvidenceCollection vendor = dependency.getVendorEvidence(); final EvidenceCollection vendor = dependency.getVendorEvidence();
addStringEvidence(vendor, contents, blockVariable, "author", Confidence.HIGHEST);
addListEvidence(vendor, contents, blockVariable, "authors", Confidence.HIGHEST);
final String email = addStringEvidence(vendor, contents, blockVariable, EMAIL, Confidence.MEDIUM);
if (email.isEmpty()) {
addListEvidence(vendor, contents, blockVariable, EMAIL, Confidence.MEDIUM);
}
addStringEvidence(vendor, contents, blockVariable, "homepage", Confidence.MEDIUM);
final EvidenceCollection product = dependency.getProductEvidence(); final EvidenceCollection product = dependency.getProductEvidence();
final String name = addStringEvidence(product, contents, blockVariable, "name", Confidence.HIGHEST); final String name = addStringEvidence(product, contents, blockVariable, "name", "name", Confidence.HIGHEST);
if (!name.isEmpty()) { if (!name.isEmpty()) {
vendor.addEvidence(GEMSPEC, "name_project", name + "_project", Confidence.LOW); vendor.addEvidence(GEMSPEC, "name_project", name + "_project", Confidence.LOW);
} }
addStringEvidence(product, contents, blockVariable, "summary", Confidence.LOW); addStringEvidence(product, contents, blockVariable, "summary", "summary", Confidence.LOW);
addStringEvidence(dependency.getVersionEvidence(), contents, blockVariable, "version", Confidence.HIGHEST);
} addStringEvidence(vendor, contents, blockVariable, "author", "authors?", Confidence.HIGHEST);
} addStringEvidence(vendor, contents, blockVariable, "email", "emails?", Confidence.MEDIUM);
addStringEvidence(vendor, contents, blockVariable, "homepage", "homepage", Confidence.HIGHEST);
private void addListEvidence(EvidenceCollection evidences, String contents, addStringEvidence(vendor, contents, blockVariable, "license", "licen[cs]es?", Confidence.HIGHEST);
String blockVariable, String field, Confidence confidence) {
final Matcher matcher = Pattern.compile( final String value = addStringEvidence(dependency.getVersionEvidence(), contents,
String.format("\\s+?%s\\.%s\\s*?=\\s*?\\[(.*?)\\]", blockVariable, field)).matcher(contents); blockVariable, "version", "version", Confidence.HIGHEST);
if (matcher.find()) { if (value.length() < 1) {
final String value = matcher.group(1).replaceAll("['\"]", " ").trim(); addEvidenceFromVersionFile(dependency.getActualFile(), dependency.getVersionEvidence());
evidences.addEvidence(GEMSPEC, field, value, confidence); }
} }
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, Confidence confidence) { String blockVariable, String field, String fieldPattern, Confidence confidence) {
final Matcher matcher = Pattern.compile(
String.format("\\s+?%s\\.%s\\s*?=\\s*?(['\"])(.*?)\\1", blockVariable, field)).matcher(contents);
String value = ""; String value = "";
if (matcher.find()) {
value = matcher.group(2); //capture array value between [ ]
final Matcher arrayMatcher = Pattern.compile(
String.format("\\s*?%s\\.%s\\s*?=\\s*?\\[(.*?)\\]", blockVariable, fieldPattern), Pattern.CASE_INSENSITIVE).matcher(contents);
if (arrayMatcher.find()) {
final String arrayValue = arrayMatcher.group(1);
value = arrayValue.replaceAll("['\"]", "").trim(); //strip quotes
} else { //capture single value between quotes
final Matcher matcher = Pattern.compile(
String.format("\\s*?%s\\.%s\\s*?=\\s*?(['\"])(.*?)\\1", blockVariable, fieldPattern), Pattern.CASE_INSENSITIVE).matcher(contents);
if (matcher.find()) {
value = matcher.group(2);
}
}
if (value.length() > 0) {
evidences.addEvidence(GEMSPEC, field, value, confidence); evidences.addEvidence(GEMSPEC, field, value, confidence);
} }
return value; return value;
} }
/**
* Adds evidence from the version file.
*
* @param dependencyFile the dependency being analyzed
* @param versionEvidences the version evidence
*/
private void addEvidenceFromVersionFile(File dependencyFile, EvidenceCollection versionEvidences) {
final File parentDir = dependencyFile.getParentFile();
if (parentDir != null) {
final File[] matchingFiles = parentDir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.contains(VERSION_FILE_NAME);
}
});
if (matchingFiles == null) {
return;
}
for (File f : matchingFiles) {
try {
final List<String> lines = FileUtils.readLines(f, Charset.defaultCharset());
if (lines.size() == 1) { //TODO other checking?
final String value = lines.get(0).trim();
versionEvidences.addEvidence(GEMSPEC, "version", value, Confidence.HIGH);
}
} catch (IOException e) {
LOGGER.debug("Error reading gemspec", e);
}
}
}
}
/**
* Sets the package path on the dependency.
*
* @param dep the dependency to alter
*/
private void setPackagePath(Dependency dep) {
final File file = new File(dep.getFilePath());
final String parent = file.getParent();
if (parent != null) {
dep.setPackagePath(parent);
}
}
} }

View File

@@ -0,0 +1,192 @@
/*
* This file is part of dependency-check-core.
*
* 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.
*
* Copyright (c) 2016 IBM Corporation. All Rights Reserved.
*/
package org.owasp.dependencycheck.analyzer;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.FileUtils;
import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
import org.owasp.dependencycheck.dependency.Confidence;
import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.dependency.EvidenceCollection;
import org.owasp.dependencycheck.utils.FileFilterBuilder;
import org.owasp.dependencycheck.utils.Settings;
/**
* This analyzer is used to analyze the SWIFT Package Manager
* (https://swift.org/package-manager/). It collects information about a package
* from Package.swift files.
*
* @author Bianca Jiang (https://twitter.com/biancajiang)
*/
@Experimental
public class SwiftPackageManagerAnalyzer extends AbstractFileTypeAnalyzer {
/**
* The name of the analyzer.
*/
private static final String ANALYZER_NAME = "SWIFT Package Manager Analyzer";
/**
* The phase that this analyzer is intended to run in.
*/
private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
/**
* The file name to scan.
*/
public static final String SPM_FILE_NAME = "Package.swift";
/**
* Filter that detects files named "package.json".
*/
private static final FileFilter SPM_FILE_FILTER = FileFilterBuilder.newInstance().addFilenames(SPM_FILE_NAME).build();
/**
* The capture group #1 is the block variable. e.g. "import
* PackageDescription let package = Package( name: "Gloss" )"
*/
private static final Pattern SPM_BLOCK_PATTERN = Pattern.compile("let[^=]+=\\s*Package\\s*\\(\\s*([^)]*)\\s*\\)", Pattern.DOTALL);
/**
* Returns the FileFilter
*
* @return the FileFilter
*/
@Override
protected FileFilter getFileFilter() {
return SPM_FILE_FILTER;
}
@Override
protected void initializeFileTypeAnalyzer() {
// NO-OP
}
/**
* Returns the name of the analyzer.
*
* @return the name of the analyzer.
*/
@Override
public String getName() {
return ANALYZER_NAME;
}
/**
* Returns the phase that the analyzer is intended to run in.
*
* @return the phase that the analyzer is intended to run in.
*/
@Override
public AnalysisPhase getAnalysisPhase() {
return ANALYSIS_PHASE;
}
/**
* Returns the key used in the properties file to reference the analyzer's
* enabled property.
*
* @return the analyzer's enabled property setting key
*/
@Override
protected String getAnalyzerEnabledSettingKey() {
return Settings.KEYS.ANALYZER_SWIFT_PACKAGE_MANAGER_ENABLED;
}
@Override
protected void analyzeFileType(Dependency dependency, Engine engine)
throws AnalysisException {
String contents;
try {
contents = FileUtils.readFileToString(dependency.getActualFile(), Charset.defaultCharset());
} catch (IOException e) {
throw new AnalysisException(
"Problem occurred while reading dependency file.", e);
}
final Matcher matcher = SPM_BLOCK_PATTERN.matcher(contents);
if (matcher.find()) {
final String packageDescription = matcher.group(1);
if (packageDescription.isEmpty()) {
return;
}
final EvidenceCollection product = dependency.getProductEvidence();
final EvidenceCollection vendor = dependency.getVendorEvidence();
//SPM is currently under development for SWIFT 3. Its current metadata includes package name and dependencies.
//Future interesting metadata: version, license, homepage, author, summary, etc.
final String name = addStringEvidence(product, packageDescription, "name", "name", Confidence.HIGHEST);
if (name != null && !name.isEmpty()) {
vendor.addEvidence(SPM_FILE_NAME, "name_project", name, Confidence.HIGHEST);
}
}
setPackagePath(dependency);
}
/**
* Extracts evidence from the package description and adds it to the given
* evidence collection.
*
* @param evidences the evidence collection to update
* @param packageDescription the text to extract evidence from
* @param field the name of the field being searched for
* @param fieldPattern the field pattern within the contents to search for
* @param confidence the confidence level of the evidence if found
* @return the string that was added as evidence
*/
private String addStringEvidence(EvidenceCollection evidences,
String packageDescription, String field, String fieldPattern, Confidence confidence) {
String value = "";
final Matcher matcher = Pattern.compile(
String.format("%s *:\\s*\"([^\"]*)", fieldPattern), Pattern.DOTALL).matcher(packageDescription);
if (matcher.find()) {
value = matcher.group(1);
}
if (value != null) {
value = value.trim();
if (value.length() > 0) {
evidences.addEvidence(SPM_FILE_NAME, field, value, confidence);
}
}
return value;
}
/**
* Sets the package path on the given dependency.
*
* @param dep the dependency to update
*/
private void setPackagePath(Dependency dep) {
final File file = new File(dep.getFilePath());
final String parent = file.getParent();
if (parent != null) {
dep.setPackagePath(parent);
}
}
}

View File

@@ -20,7 +20,7 @@ package org.owasp.dependencycheck.analyzer;
import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.suppression.SuppressionRule; import org.owasp.dependencycheck.xml.suppression.SuppressionRule;
/** /**
* The suppression analyzer processes an externally defined XML document that complies with the suppressions.xsd schema. * The suppression analyzer processes an externally defined XML document that complies with the suppressions.xsd schema.

View File

@@ -61,8 +61,8 @@ public class CentralSearch {
/** /**
* Creates a NexusSearch for the given repository URL. * Creates a NexusSearch for the given repository URL.
* *
* @param rootURL the URL of the repository on which searches should execute. Only parameters are added to this (so it should * @param rootURL the URL of the repository on which searches should
* end in /select) * execute. Only parameters are added to this (so it should end in /select)
*/ */
public CentralSearch(URL rootURL) { public CentralSearch(URL rootURL) {
this.rootURL = rootURL; this.rootURL = rootURL;
@@ -76,18 +76,20 @@ public class CentralSearch {
} }
/** /**
* Searches the configured Central URL for the given sha1 hash. If the artifact is found, a <code>MavenArtifact</code> is * Searches the configured Central URL for the given sha1 hash. If the
* populated with the GAV. * artifact is found, a <code>MavenArtifact</code> is populated with the
* GAV.
* *
* @param sha1 the SHA-1 hash string for which to search * @param sha1 the SHA-1 hash string for which to search
* @return the populated Maven GAV. * @return the populated Maven GAV.
* @throws IOException if it's unable to connect to the specified repository or if the specified artifact is not found. * @throws IOException if it's unable to connect to the specified repository
* or if the specified artifact is not found.
*/ */
public List<MavenArtifact> searchSha1(String sha1) throws IOException { public List<MavenArtifact> searchSha1(String sha1) throws IOException {
if (null == sha1 || !sha1.matches("^[0-9A-Fa-f]{40}$")) { if (null == sha1 || !sha1.matches("^[0-9A-Fa-f]{40}$")) {
throw new IllegalArgumentException("Invalid SHA1 format"); throw new IllegalArgumentException("Invalid SHA1 format");
} }
List<MavenArtifact> result = null;
final URL url = new URL(rootURL + String.format("?q=1:\"%s\"&wt=xml", sha1)); final URL url = new URL(rootURL + String.format("?q=1:\"%s\"&wt=xml", sha1));
LOGGER.debug("Searching Central url {}", url); LOGGER.debug("Searching Central url {}", url);
@@ -116,7 +118,7 @@ public class CentralSearch {
if ("0".equals(numFound)) { if ("0".equals(numFound)) {
missing = true; missing = true;
} else { } else {
final List<MavenArtifact> result = new ArrayList<MavenArtifact>(); result = new ArrayList<MavenArtifact>();
final NodeList docs = (NodeList) xpath.evaluate("/response/result/doc", doc, XPathConstants.NODESET); final NodeList docs = (NodeList) xpath.evaluate("/response/result/doc", doc, XPathConstants.NODESET);
for (int i = 0; i < docs.getLength(); i++) { for (int i = 0; i < docs.getLength(); i++) {
final String g = xpath.evaluate("./str[@name='g']", docs.item(i)); final String g = xpath.evaluate("./str[@name='g']", docs.item(i));
@@ -144,16 +146,12 @@ public class CentralSearch {
useHTTPS = true; useHTTPS = true;
} }
} }
LOGGER.trace("Version: {}", v); LOGGER.trace("Version: {}", v);
result.add(new MavenArtifact(g, a, v, jarAvailable, pomAvailable, useHTTPS)); result.add(new MavenArtifact(g, a, v, jarAvailable, pomAvailable, useHTTPS));
} }
return result;
} }
} catch (Throwable e) { } catch (Throwable e) {
// Anything else is jacked up XML stuff that we really can't recover // Anything else is jacked up XML stuff that we really can't recover from well
// from well
throw new IOException(e.getMessage(), e); throw new IOException(e.getMessage(), e);
} }
@@ -162,10 +160,9 @@ public class CentralSearch {
} }
} else { } else {
LOGGER.debug("Could not connect to Central received response code: {} {}", LOGGER.debug("Could not connect to Central received response code: {} {}",
conn.getResponseCode(), conn.getResponseMessage()); conn.getResponseCode(), conn.getResponseMessage());
throw new IOException("Could not connect to Central"); throw new IOException("Could not connect to Central");
} }
return result;
return null;
} }
} }

View File

@@ -1,6 +1,6 @@
/** /**
* *
* Contains classes related to searching Maven Central.<br/><br/> * Contains classes related to searching Maven Central.<br><br>
* *
* These are used to abstract Maven Central searching away from OWASP Dependency Check so they can be reused elsewhere. * These are used to abstract Maven Central searching away from OWASP Dependency Check so they can be reused elsewhere.
*/ */

View File

@@ -38,7 +38,6 @@ import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.search.TopDocs; import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.RAMDirectory; import org.apache.lucene.store.RAMDirectory;
import org.owasp.dependencycheck.data.lucene.FieldAnalyzer;
import org.owasp.dependencycheck.data.lucene.LuceneUtils; import org.owasp.dependencycheck.data.lucene.LuceneUtils;
import org.owasp.dependencycheck.data.lucene.SearchFieldAnalyzer; import org.owasp.dependencycheck.data.lucene.SearchFieldAnalyzer;
import org.owasp.dependencycheck.data.nvdcve.CveDB; import org.owasp.dependencycheck.data.nvdcve.CveDB;
@@ -48,8 +47,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* An in memory lucene index that contains the vendor/product combinations from the CPE (application) identifiers within the NVD * An in memory lucene index that contains the vendor/product combinations from
* CVE data. * the CPE (application) identifiers within the NVD CVE data.
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
@@ -101,11 +100,11 @@ public final class CpeMemoryIndex {
/** /**
* The search field analyzer for the product field. * The search field analyzer for the product field.
*/ */
private SearchFieldAnalyzer productSearchFieldAnalyzer; private SearchFieldAnalyzer productFieldAnalyzer;
/** /**
* The search field analyzer for the vendor field. * The search field analyzer for the vendor field.
*/ */
private SearchFieldAnalyzer vendorSearchFieldAnalyzer; private SearchFieldAnalyzer vendorFieldAnalyzer;
/** /**
* Creates and loads data into an in memory index. * Creates and loads data into an in memory index.
@@ -144,17 +143,6 @@ public final class CpeMemoryIndex {
return openState; return openState;
} }
/**
* Creates the indexing analyzer for the CPE Index.
*
* @return the CPE Analyzer.
*/
private Analyzer createIndexingAnalyzer() {
final Map<String, Analyzer> fieldAnalyzers = new HashMap<String, Analyzer>();
fieldAnalyzers.put(Fields.DOCUMENT_KEY, new KeywordAnalyzer());
return new PerFieldAnalyzerWrapper(new FieldAnalyzer(LuceneUtils.CURRENT_VERSION), fieldAnalyzers);
}
/** /**
* Creates an Analyzer for searching the CPE Index. * Creates an Analyzer for searching the CPE Index.
* *
@@ -163,12 +151,12 @@ public final class CpeMemoryIndex {
private Analyzer createSearchingAnalyzer() { private Analyzer createSearchingAnalyzer() {
final Map<String, Analyzer> fieldAnalyzers = new HashMap<String, Analyzer>(); final Map<String, Analyzer> fieldAnalyzers = new HashMap<String, Analyzer>();
fieldAnalyzers.put(Fields.DOCUMENT_KEY, new KeywordAnalyzer()); fieldAnalyzers.put(Fields.DOCUMENT_KEY, new KeywordAnalyzer());
productSearchFieldAnalyzer = new SearchFieldAnalyzer(LuceneUtils.CURRENT_VERSION); productFieldAnalyzer = new SearchFieldAnalyzer(LuceneUtils.CURRENT_VERSION);
vendorSearchFieldAnalyzer = new SearchFieldAnalyzer(LuceneUtils.CURRENT_VERSION); vendorFieldAnalyzer = new SearchFieldAnalyzer(LuceneUtils.CURRENT_VERSION);
fieldAnalyzers.put(Fields.PRODUCT, productSearchFieldAnalyzer); fieldAnalyzers.put(Fields.PRODUCT, productFieldAnalyzer);
fieldAnalyzers.put(Fields.VENDOR, vendorSearchFieldAnalyzer); fieldAnalyzers.put(Fields.VENDOR, vendorFieldAnalyzer);
return new PerFieldAnalyzerWrapper(new FieldAnalyzer(LuceneUtils.CURRENT_VERSION), fieldAnalyzers); return new PerFieldAnalyzerWrapper(new KeywordAnalyzer(), fieldAnalyzers);
} }
/** /**
@@ -206,7 +194,7 @@ public final class CpeMemoryIndex {
Analyzer analyzer = null; Analyzer analyzer = null;
IndexWriter indexWriter = null; IndexWriter indexWriter = null;
try { try {
analyzer = createIndexingAnalyzer(); analyzer = createSearchingAnalyzer();
final IndexWriterConfig conf = new IndexWriterConfig(LuceneUtils.CURRENT_VERSION, analyzer); final IndexWriterConfig conf = new IndexWriterConfig(LuceneUtils.CURRENT_VERSION, analyzer);
indexWriter = new IndexWriter(index, conf); indexWriter = new IndexWriter(index, conf);
try { try {
@@ -221,9 +209,13 @@ public final class CpeMemoryIndex {
final Set<Pair<String, String>> data = cve.getVendorProductList(); final Set<Pair<String, String>> data = cve.getVendorProductList();
for (Pair<String, String> pair : data) { for (Pair<String, String> pair : data) {
v.setStringValue(pair.getLeft()); //todo figure out why there are null products
p.setStringValue(pair.getRight()); if (pair.getLeft() != null && pair.getRight() != null) {
indexWriter.addDocument(doc); v.setStringValue(pair.getLeft());
p.setStringValue(pair.getRight());
indexWriter.addDocument(doc);
resetFieldAnalyzer();
}
} }
} catch (DatabaseException ex) { } catch (DatabaseException ex) {
LOGGER.debug("", ex); LOGGER.debug("", ex);
@@ -254,14 +246,14 @@ public final class CpeMemoryIndex {
} }
/** /**
* Resets the searching analyzers * Resets the product and vendor field analyzers.
*/ */
private void resetSearchingAnalyzer() { private void resetFieldAnalyzer() {
if (productSearchFieldAnalyzer != null) { if (productFieldAnalyzer != null) {
productSearchFieldAnalyzer.clear(); productFieldAnalyzer.clear();
} }
if (vendorSearchFieldAnalyzer != null) { if (vendorFieldAnalyzer != null) {
vendorSearchFieldAnalyzer.clear(); vendorFieldAnalyzer.clear();
} }
} }
@@ -272,7 +264,8 @@ public final class CpeMemoryIndex {
* @param maxQueryResults the maximum number of documents to return * @param maxQueryResults the maximum number of documents to return
* @return the TopDocs found by the search * @return the TopDocs found by the search
* @throws ParseException thrown when the searchString is invalid * @throws ParseException thrown when the searchString is invalid
* @throws IOException is thrown if there is an issue with the underlying Index * @throws IOException is thrown if there is an issue with the underlying
* Index
*/ */
public TopDocs search(String searchString, int maxQueryResults) throws ParseException, IOException { public TopDocs search(String searchString, int maxQueryResults) throws ParseException, IOException {
if (searchString == null || searchString.trim().isEmpty()) { if (searchString == null || searchString.trim().isEmpty()) {
@@ -293,7 +286,7 @@ public final class CpeMemoryIndex {
* @throws IOException thrown if there is an IOException * @throws IOException thrown if there is an IOException
*/ */
public TopDocs search(Query query, int maxQueryResults) throws CorruptIndexException, IOException { public TopDocs search(Query query, int maxQueryResults) throws CorruptIndexException, IOException {
resetSearchingAnalyzer(); resetFieldAnalyzer();
return indexSearcher.search(query, maxQueryResults); return indexSearcher.search(query, maxQueryResults);
} }

View File

@@ -37,7 +37,7 @@ public class CweHandler extends DefaultHandler {
/** /**
* Returns the HashMap of CWE entries (CWE-ID, Full CWE Name). * Returns the HashMap of CWE entries (CWE-ID, Full CWE Name).
* *
* @return a HashMap of CWE entries <String, String> * @return a HashMap of CWE entries &lt;String, String&gt;
*/ */
public HashMap<String, String> getCwe() { public HashMap<String, String> getCwe() {
return cwe; return cwe;

View File

@@ -29,11 +29,15 @@ import org.apache.lucene.util.Version;
/** /**
* <p> * <p>
* A Lucene Analyzer that utilizes the WhitespaceTokenizer, WordDelimiterFilter, LowerCaseFilter, and StopFilter. The intended * A Lucene Analyzer that utilizes the WhitespaceTokenizer, WordDelimiterFilter,
* purpose of this Analyzer is to index the CPE fields vendor and product.</p> * LowerCaseFilter, and StopFilter. The intended purpose of this Analyzer is to
* index the CPE fields vendor and product.</p>
* *
* @author Jeremy Long * @author Jeremy Long
* @deprecated the field analyzer should not be used, instead use the
* SearchFieldAnalyzer so that the token analyzing filter is used.
*/ */
@Deprecated
public class FieldAnalyzer extends Analyzer { public class FieldAnalyzer extends Analyzer {
/** /**

View File

@@ -27,7 +27,7 @@ import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
* <p> * <p>
* Takes a TokenStream and adds additional tokens by concatenating pairs of words.</p> * Takes a TokenStream and adds additional tokens by concatenating pairs of words.</p>
* <p> * <p>
* <b>Example:</b> "Spring Framework Core" -> "Spring SpringFramework Framework FrameworkCore Core".</p> * <b>Example:</b> "Spring Framework Core" -&gt; "Spring SpringFramework Framework FrameworkCore Core".</p>
* *
* @author Jeremy Long * @author Jeremy Long
*/ */

View File

@@ -31,15 +31,17 @@ import org.slf4j.LoggerFactory;
* <p> * <p>
* Takes a TokenStream and splits or adds tokens to correctly index version numbers.</p> * Takes a TokenStream and splits or adds tokens to correctly index version numbers.</p>
* <p> * <p>
* <b>Example:</b> "3.0.0.RELEASE" -> "3 3.0 3.0.0 RELEASE 3.0.0.RELEASE".</p> * <b>Example:</b> "3.0.0.RELEASE" -&gt; "3 3.0 3.0.0 RELEASE 3.0.0.RELEASE".</p>
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
public final class UrlTokenizingFilter extends AbstractTokenizingFilter { public final class UrlTokenizingFilter extends AbstractTokenizingFilter {
/** /**
* The logger. * The logger.
*/ */
private static final Logger LOGGER = LoggerFactory.getLogger(UrlTokenizingFilter.class); private static final Logger LOGGER = LoggerFactory.getLogger(UrlTokenizingFilter.class);
/** /**
* Constructs a new VersionTokenizingFilter. * Constructs a new VersionTokenizingFilter.
* *
@@ -50,8 +52,8 @@ public final class UrlTokenizingFilter extends AbstractTokenizingFilter {
} }
/** /**
* Increments the underlying TokenStream and sets CharTermAttributes to construct an expanded set of tokens by * Increments the underlying TokenStream and sets CharTermAttributes to construct an expanded set of tokens by concatenating
* concatenating tokens with the previous token. * tokens with the previous token.
* *
* @return whether or not we have hit the end of the TokenStream * @return whether or not we have hit the end of the TokenStream
* @throws IOException is thrown when an IOException occurs * @throws IOException is thrown when an IOException occurs

View File

@@ -1,5 +1,5 @@
/** /**
* Contains classes related to searching a Nexus repository.<br/><br/> * Contains classes related to searching a Nexus repository.<br><br>
* *
* These are used to abstract Nexus searching away from OWASP Dependency Check so they can be reused elsewhere. * These are used to abstract Nexus searching away from OWASP Dependency Check so they can be reused elsewhere.
*/ */

View File

@@ -1,5 +1,5 @@
/** /**
* Contains classes related to parsing Nuget related files<br/><br/> * Contains classes related to parsing Nuget related files<br><br>
* These are used to abstract away Nuget-related handling from Dependency Check so they can be used elsewhere. * These are used to abstract away Nuget-related handling from Dependency Check so they can be used elsewhere.
*/ */
package org.owasp.dependencycheck.data.nuget; package org.owasp.dependencycheck.data.nuget;

View File

@@ -20,7 +20,7 @@ package org.owasp.dependencycheck.data.nvdcve;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.sql.CallableStatement; import java.sql.PreparedStatement;
import java.sql.Connection; import java.sql.Connection;
import java.sql.Driver; import java.sql.Driver;
import java.sql.DriverManager; import java.sql.DriverManager;
@@ -36,8 +36,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* Loads the configured database driver and returns the database connection. If the embedded H2 database is used obtaining a * Loads the configured database driver and returns the database connection. If
* connection will ensure the database file exists and that the appropriate table structure has been created. * the embedded H2 database is used obtaining a connection will ensure the
* database file exists and that the appropriate table structure has been
* created.
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
@@ -87,12 +89,13 @@ public final class ConnectionFactory {
} }
/** /**
* Initializes the connection factory. Ensuring that the appropriate drivers are loaded and that a connection can be made * Initializes the connection factory. Ensuring that the appropriate drivers
* successfully. * are loaded and that a connection can be made successfully.
* *
* @throws DatabaseException thrown if we are unable to connect to the database * @throws DatabaseException thrown if we are unable to connect to the
* database
*/ */
public static synchronized void initialize() throws DatabaseException { public static void initialize() throws DatabaseException {
//this only needs to be called once. //this only needs to be called once.
if (connectionString != null) { if (connectionString != null) {
return; return;
@@ -188,11 +191,12 @@ public final class ConnectionFactory {
} }
/** /**
* Cleans up resources and unloads any registered database drivers. This needs to be called to ensure the driver is * Cleans up resources and unloads any registered database drivers. This
* unregistered prior to the finalize method being called as during shutdown the class loader used to load the driver may be * needs to be called to ensure the driver is unregistered prior to the
* unloaded prior to the driver being de-registered. * finalize method being called as during shutdown the class loader used to
* load the driver may be unloaded prior to the driver being de-registered.
*/ */
public static synchronized void cleanup() { public static void cleanup() {
if (driver != null) { if (driver != null) {
try { try {
DriverManager.deregisterDriver(driver); DriverManager.deregisterDriver(driver);
@@ -210,10 +214,12 @@ public final class ConnectionFactory {
} }
/** /**
* Constructs a new database connection object per the database configuration. * Constructs a new database connection object per the database
* configuration.
* *
* @return a database connection object * @return a database connection object
* @throws DatabaseException thrown if there is an exception loading the database connection * @throws DatabaseException thrown if there is an exception loading the
* database connection
*/ */
public static Connection getConnection() throws DatabaseException { public static Connection getConnection() throws DatabaseException {
initialize(); initialize();
@@ -228,10 +234,12 @@ public final class ConnectionFactory {
} }
/** /**
* Determines if the H2 database file exists. If it does not exist then the data structure will need to be created. * Determines if the H2 database file exists. If it does not exist then the
* data structure will need to be created.
* *
* @return true if the H2 database file does not exist; otherwise false * @return true if the H2 database file does not exist; otherwise false
* @throws IOException thrown if the data directory does not exist and cannot be created * @throws IOException thrown if the data directory does not exist and
* cannot be created
*/ */
private static boolean h2DataFileExists() throws IOException { private static boolean h2DataFileExists() throws IOException {
final File dir = Settings.getDataDirectory(); final File dir = Settings.getDataDirectory();
@@ -241,7 +249,8 @@ public final class ConnectionFactory {
} }
/** /**
* Creates the database structure (tables and indexes) to store the CVE data. * Creates the database structure (tables and indexes) to store the CVE
* data.
* *
* @param conn the database connection * @param conn the database connection
* @throws DatabaseException thrown if there is a Database Exception * @throws DatabaseException thrown if there is a Database Exception
@@ -271,15 +280,21 @@ public final class ConnectionFactory {
} }
/** /**
* Updates the database schema by loading the upgrade script for the version specified. The intended use is that if the * Updates the database schema by loading the upgrade script for the version
* current schema version is 2.9 then we would call updateSchema(conn, "2.9"). This would load the upgrade_2.9.sql file and * specified. The intended use is that if the current schema version is 2.9
* execute it against the database. The upgrade script must update the 'version' in the properties table. * then we would call updateSchema(conn, "2.9"). This would load the
* upgrade_2.9.sql file and execute it against the database. The upgrade
* script must update the 'version' in the properties table.
* *
* @param conn the database connection object * @param conn the database connection object
* @param schema the current schema version that is being upgraded * @param appExpectedVersion the schema version that the application expects
* @throws DatabaseException thrown if there is an exception upgrading the database schema * @param currentDbVersion the current schema version of the database
* @throws DatabaseException thrown if there is an exception upgrading the
* database schema
*/ */
private static void updateSchema(Connection conn, String schema) throws DatabaseException { private static void updateSchema(Connection conn, DependencyVersion appExpectedVersion, DependencyVersion currentDbVersion)
throws DatabaseException {
final String databaseProductName; final String databaseProductName;
try { try {
databaseProductName = conn.getMetaData().getDatabaseProductName(); databaseProductName = conn.getMetaData().getDatabaseProductName();
@@ -291,7 +306,7 @@ public final class ConnectionFactory {
InputStream is = null; InputStream is = null;
String updateFile = null; String updateFile = null;
try { try {
updateFile = String.format(DB_STRUCTURE_UPDATE_RESOURCE, schema); updateFile = String.format(DB_STRUCTURE_UPDATE_RESOURCE, currentDbVersion.toString());
is = ConnectionFactory.class.getClassLoader().getResourceAsStream(updateFile); is = ConnectionFactory.class.getClassLoader().getResourceAsStream(updateFile);
if (is == null) { if (is == null) {
throw new DatabaseException(String.format("Unable to load update file '%s'", updateFile)); throw new DatabaseException(String.format("Unable to load update file '%s'", updateFile));
@@ -303,7 +318,8 @@ public final class ConnectionFactory {
statement = conn.createStatement(); statement = conn.createStatement();
final boolean success = statement.execute(dbStructureUpdate); final boolean success = statement.execute(dbStructureUpdate);
if (!success && statement.getUpdateCount() <= 0) { if (!success && statement.getUpdateCount() <= 0) {
throw new DatabaseException(String.format("Unable to upgrade the database schema to %s", schema)); throw new DatabaseException(String.format("Unable to upgrade the database schema to %s",
currentDbVersion.toString()));
} }
} catch (SQLException ex) { } catch (SQLException ex) {
LOGGER.debug("", ex); LOGGER.debug("", ex);
@@ -318,36 +334,57 @@ public final class ConnectionFactory {
IOUtils.closeQuietly(is); IOUtils.closeQuietly(is);
} }
} else { } else {
LOGGER.error("The database schema must be upgraded to use this version of dependency-check. Please see {} for more information.", UPGRADE_HELP_URL); final int e0 = Integer.parseInt(appExpectedVersion.getVersionParts().get(0));
throw new DatabaseException("Database schema is out of date"); final int c0 = Integer.parseInt(currentDbVersion.getVersionParts().get(0));
final int e1 = Integer.parseInt(appExpectedVersion.getVersionParts().get(1));
final int c1 = Integer.parseInt(currentDbVersion.getVersionParts().get(1));
if (e0 == c0 && e1 < c1) {
LOGGER.warn("A new version of dependency-check is available; consider upgrading");
Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false);
} else if (e0 == c0 && e1 == c1) {
//do nothing - not sure how we got here, but just incase...
} else {
LOGGER.error("The database schema must be upgraded to use this version of dependency-check. Please see {} for more information.",
UPGRADE_HELP_URL);
throw new DatabaseException("Database schema is out of date");
}
} }
} }
/** /**
* Counter to ensure that calls to ensureSchemaVersion does not end up in an endless loop. * Counter to ensure that calls to ensureSchemaVersion does not end up in an
* endless loop.
*/ */
private static int callDepth = 0; private static int callDepth = 0;
/** /**
* Uses the provided connection to check the specified schema version within the database. * Uses the provided connection to check the specified schema version within
* the database.
* *
* @param conn the database connection object * @param conn the database connection object
* @throws DatabaseException thrown if the schema version is not compatible with this version of dependency-check * @throws DatabaseException thrown if the schema version is not compatible
* with this version of dependency-check
*/ */
private static void ensureSchemaVersion(Connection conn) throws DatabaseException { private static void ensureSchemaVersion(Connection conn) throws DatabaseException {
ResultSet rs = null; ResultSet rs = null;
CallableStatement cs = null; PreparedStatement ps = null;
try { try {
//TODO convert this to use DatabaseProperties //TODO convert this to use DatabaseProperties
cs = conn.prepareCall("SELECT value FROM properties WHERE id = 'version'"); ps = conn.prepareStatement("SELECT value FROM properties WHERE id = 'version'");
rs = cs.executeQuery(); rs = ps.executeQuery();
if (rs.next()) { if (rs.next()) {
final DependencyVersion current = DependencyVersionUtil.parseVersion(DB_SCHEMA_VERSION); final DependencyVersion appDbVersion = DependencyVersionUtil.parseVersion(DB_SCHEMA_VERSION);
if (appDbVersion == null) {
throw new DatabaseException("Invalid application database schema");
}
final DependencyVersion db = DependencyVersionUtil.parseVersion(rs.getString(1)); final DependencyVersion db = DependencyVersionUtil.parseVersion(rs.getString(1));
if (current.compareTo(db) > 0) { if (db == null) {
LOGGER.debug("Current Schema: " + DB_SCHEMA_VERSION); throw new DatabaseException("Invalid database schema");
LOGGER.debug("DB Schema: " + rs.getString(1)); }
updateSchema(conn, rs.getString(1)); if (appDbVersion.compareTo(db) > 0) {
LOGGER.debug("Current Schema: {}", DB_SCHEMA_VERSION);
LOGGER.debug("DB Schema: {}", rs.getString(1));
updateSchema(conn, appDbVersion, db);
if (++callDepth < 10) { if (++callDepth < 10) {
ensureSchemaVersion(conn); ensureSchemaVersion(conn);
} }
@@ -360,7 +397,7 @@ public final class ConnectionFactory {
throw new DatabaseException("Unable to check the database schema version"); throw new DatabaseException("Unable to check the database schema version");
} finally { } finally {
DBUtils.closeResultSet(rs); DBUtils.closeResultSet(rs);
DBUtils.closeStatement(cs); DBUtils.closeStatement(ps);
} }
} }
} }

View File

@@ -19,7 +19,6 @@ package org.owasp.dependencycheck.data.nvdcve;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.sql.CallableStatement;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
@@ -69,10 +68,18 @@ public class CveDB {
private ResourceBundle statementBundle = null; private ResourceBundle statementBundle = null;
/** /**
* Creates a new CveDB object and opens the database connection. Note, the connection must be closed by the caller by calling * Creates a new CveDB object and opens the database connection. Note, the
* the close method. * connection must be closed by the caller by calling the close method.
* ======= Does the underlying connection support batch operations?
*/
private boolean batchSupported;
/**
* Creates a new CveDB object and opens the database connection. Note, the
* connection must be closed by the caller by calling the close method.
* *
* @throws DatabaseException thrown if there is an exception opening the database. * @throws DatabaseException thrown if there is an exception opening the
* database.
*/ */
public CveDB() throws DatabaseException { public CveDB() throws DatabaseException {
super(); super();
@@ -83,6 +90,9 @@ public class CveDB {
LOGGER.debug("Database dialect: {}", databaseProductName); LOGGER.debug("Database dialect: {}", databaseProductName);
final Locale dbDialect = new Locale(databaseProductName); final Locale dbDialect = new Locale(databaseProductName);
statementBundle = ResourceBundle.getBundle("data/dbStatements", dbDialect); statementBundle = ResourceBundle.getBundle("data/dbStatements", dbDialect);
if ("mysql".equalsIgnoreCase(databaseProductName)) {
batchSupported = false;
}
} catch (SQLException se) { } catch (SQLException se) {
LOGGER.warn("Problem loading database specific dialect!", se); LOGGER.warn("Problem loading database specific dialect!", se);
statementBundle = ResourceBundle.getBundle("data/dbStatements"); statementBundle = ResourceBundle.getBundle("data/dbStatements");
@@ -103,9 +113,11 @@ public class CveDB {
} }
/** /**
* Opens the database connection. If the database does not exist, it will create a new one. * Opens the database connection. If the database does not exist, it will
* create a new one.
* *
* @throws DatabaseException thrown if there is an error opening the database connection * @throws DatabaseException thrown if there is an error opening the
* database connection
*/ */
public final void open() throws DatabaseException { public final void open() throws DatabaseException {
if (!isOpen()) { if (!isOpen()) {
@@ -114,7 +126,8 @@ public class CveDB {
} }
/** /**
* Closes the DB4O database. Close should be called on this object when it is done being used. * Closes the DB4O database. Close should be called on this object when it
* is done being used.
*/ */
public void close() { public void close() {
if (conn != null) { if (conn != null) {
@@ -165,7 +178,8 @@ public class CveDB {
super.finalize(); super.finalize();
} }
/** /**
* Database properties object containing the 'properties' from the database table. * Database properties object containing the 'properties' from the database
* table.
*/ */
private DatabaseProperties databaseProperties; private DatabaseProperties databaseProperties;
@@ -179,11 +193,13 @@ public class CveDB {
} }
/** /**
* Searches the CPE entries in the database and retrieves all entries for a given vendor and product combination. The returned * Searches the CPE entries in the database and retrieves all entries for a
* list will include all versions of the product that are registered in the NVD CVE data. * given vendor and product combination. The returned list will include all
* versions of the product that are registered in the NVD CVE data.
* *
* @param vendor the identified vendor name of the dependency being analyzed * @param vendor the identified vendor name of the dependency being analyzed
* @param product the identified name of the product of the dependency being analyzed * @param product the identified name of the product of the dependency being
* analyzed
* @return a set of vulnerable software * @return a set of vulnerable software
*/ */
public Set<VulnerableSoftware> getCPEs(String vendor, String product) { public Set<VulnerableSoftware> getCPEs(String vendor, String product) {
@@ -215,7 +231,8 @@ public class CveDB {
* Returns the entire list of vendor/product combinations. * Returns the entire list of vendor/product combinations.
* *
* @return the entire list of vendor/product combinations * @return the entire list of vendor/product combinations
* @throws DatabaseException thrown when there is an error retrieving the data from the DB * @throws DatabaseException thrown when there is an error retrieving the
* data from the DB
*/ */
public Set<Pair<String, String>> getVendorProductList() throws DatabaseException { public Set<Pair<String, String>> getVendorProductList() throws DatabaseException {
final Set<Pair<String, String>> data = new HashSet<Pair<String, String>>(); final Set<Pair<String, String>> data = new HashSet<Pair<String, String>>();
@@ -372,7 +389,7 @@ public class CveDB {
* @return a vulnerability object * @return a vulnerability object
* @throws DatabaseException if an exception occurs * @throws DatabaseException if an exception occurs
*/ */
private Vulnerability getVulnerability(String cve) throws DatabaseException { public Vulnerability getVulnerability(String cve) throws DatabaseException {
PreparedStatement psV = null; PreparedStatement psV = null;
PreparedStatement psR = null; PreparedStatement psR = null;
PreparedStatement psS = null; PreparedStatement psS = null;
@@ -380,6 +397,7 @@ public class CveDB {
ResultSet rsR = null; ResultSet rsR = null;
ResultSet rsS = null; ResultSet rsS = null;
Vulnerability vuln = null; Vulnerability vuln = null;
try { try {
psV = getConnection().prepareStatement(statementBundle.getString("SELECT_VULNERABILITY")); psV = getConnection().prepareStatement(statementBundle.getString("SELECT_VULNERABILITY"));
psV.setString(1, cve); psV.setString(1, cve);
@@ -438,7 +456,8 @@ public class CveDB {
} }
/** /**
* Updates the vulnerability within the database. If the vulnerability does not exist it will be added. * Updates the vulnerability within the database. If the vulnerability does
* not exist it will be added.
* *
* @param vuln the vulnerability to add to the database * @param vuln the vulnerability to add to the database
* @throws DatabaseException is thrown if the database * @throws DatabaseException is thrown if the database
@@ -484,6 +503,7 @@ public class CveDB {
} }
DBUtils.closeResultSet(rs); DBUtils.closeResultSet(rs);
rs = null; rs = null;
if (vulnerabilityId != 0) { if (vulnerabilityId != 0) {
if (vuln.getDescription().contains("** REJECT **")) { if (vuln.getDescription().contains("** REJECT **")) {
deleteVulnerability.setInt(1, vulnerabilityId); deleteVulnerability.setInt(1, vulnerabilityId);
@@ -525,13 +545,24 @@ public class CveDB {
rs = null; rs = null;
} }
} }
insertReference.setInt(1, vulnerabilityId);
for (Reference r : vuln.getReferences()) { for (Reference r : vuln.getReferences()) {
insertReference.setInt(1, vulnerabilityId);
insertReference.setString(2, r.getName()); insertReference.setString(2, r.getName());
insertReference.setString(3, r.getUrl()); insertReference.setString(3, r.getUrl());
insertReference.setString(4, r.getSource()); insertReference.setString(4, r.getSource());
insertReference.execute();
if (batchSupported) {
insertReference.addBatch();
} else {
insertReference.execute();
}
} }
if (batchSupported) {
insertReference.executeBatch();
}
for (VulnerableSoftware s : vuln.getVulnerableSoftware()) { for (VulnerableSoftware s : vuln.getVulnerableSoftware()) {
int cpeProductId = 0; int cpeProductId = 0;
selectCpeId.setString(1, s.getName()); selectCpeId.setString(1, s.getName());
@@ -560,17 +591,33 @@ public class CveDB {
insertSoftware.setInt(1, vulnerabilityId); insertSoftware.setInt(1, vulnerabilityId);
insertSoftware.setInt(2, cpeProductId); insertSoftware.setInt(2, cpeProductId);
if (s.getPreviousVersion() == null) { if (s.getPreviousVersion() == null) {
insertSoftware.setNull(3, java.sql.Types.VARCHAR); insertSoftware.setNull(3, java.sql.Types.VARCHAR);
} else { } else {
insertSoftware.setString(3, s.getPreviousVersion()); insertSoftware.setString(3, s.getPreviousVersion());
} }
insertSoftware.execute(); if (batchSupported) {
insertSoftware.addBatch();
} else {
try {
insertSoftware.execute();
} catch (SQLException ex) {
if (ex.getMessage().contains("Duplicate entry")) {
final String msg = String.format("Duplicate software key identified in '%s:%s'", vuln.getName(), s.getName());
LOGGER.debug(msg, ex);
} else {
throw ex;
}
}
}
}
if (batchSupported) {
insertSoftware.executeBatch();
} }
} catch (SQLException ex) { } catch (SQLException ex) {
final String msg = String.format("Error updating '%s'", vuln.getName()); final String msg = String.format("Error updating '%s'", vuln.getName());
LOGGER.debug("", ex); LOGGER.debug(msg, ex);
throw new DatabaseException(msg, ex); throw new DatabaseException(msg, ex);
} finally { } finally {
DBUtils.closeStatement(selectVulnerabilityId); DBUtils.closeStatement(selectVulnerabilityId);
@@ -613,7 +660,7 @@ public class CveDB {
+ "If the problem persist try deleting the files in '{}' and running {} again. If the problem continues, please " + "If the problem persist try deleting the files in '{}' and running {} again. If the problem continues, please "
+ "create a log file (see documentation at http://jeremylong.github.io/DependencyCheck/) and open a ticket at " + "create a log file (see documentation at http://jeremylong.github.io/DependencyCheck/) and open a ticket at "
+ "https://github.com/jeremylong/DependencyCheck/issues and include the log file.\n\n", + "https://github.com/jeremylong/DependencyCheck/issues and include the log file.\n\n",
dd, dd, Settings.getString(Settings.KEYS.APPLICATION_VAME)); dd, dd, Settings.getString(Settings.KEYS.APPLICATION_NAME));
LOGGER.debug("", ex); LOGGER.debug("", ex);
} finally { } finally {
DBUtils.closeResultSet(rs); DBUtils.closeResultSet(rs);
@@ -623,8 +670,9 @@ public class CveDB {
} }
/** /**
* It is possible that orphaned rows may be generated during database updates. This should be called after all updates have * It is possible that orphaned rows may be generated during database
* been completed to ensure orphan entries are removed. * updates. This should be called after all updates have been completed to
* ensure orphan entries are removed.
*/ */
public void cleanupDatabase() { public void cleanupDatabase() {
PreparedStatement ps = null; PreparedStatement ps = null;
@@ -642,13 +690,17 @@ public class CveDB {
} }
/** /**
* Determines if the given identifiedVersion is affected by the given cpeId and previous version flag. A non-null, non-empty * Determines if the given identifiedVersion is affected by the given cpeId
* string passed to the previous version argument indicates that all previous versions are affected. * and previous version flag. A non-null, non-empty string passed to the
* previous version argument indicates that all previous versions are
* affected.
* *
* @param vendor the vendor of the dependency being analyzed * @param vendor the vendor of the dependency being analyzed
* @param product the product name of the dependency being analyzed * @param product the product name of the dependency being analyzed
* @param vulnerableSoftware a map of the vulnerable software with a boolean indicating if all previous versions are affected * @param vulnerableSoftware a map of the vulnerable software with a boolean
* @param identifiedVersion the identified version of the dependency being analyzed * indicating if all previous versions are affected
* @param identifiedVersion the identified version of the dependency being
* analyzed
* @return true if the identified version is affected, otherwise false * @return true if the identified version is affected, otherwise false
*/ */
Entry<String, Boolean> getMatchingSoftware(Map<String, Boolean> vulnerableSoftware, String vendor, String product, Entry<String, Boolean> getMatchingSoftware(Map<String, Boolean> vulnerableSoftware, String vendor, String product,
@@ -715,7 +767,8 @@ public class CveDB {
} }
/** /**
* Parses the version (including revision) from a CPE identifier. If no version is identified then a '-' is returned. * Parses the version (including revision) from a CPE identifier. If no
* version is identified then a '-' is returned.
* *
* @param cpeStr a cpe identifier * @param cpeStr a cpe identifier
* @return a dependency version * @return a dependency version
@@ -732,7 +785,8 @@ public class CveDB {
} }
/** /**
* Takes a CPE and parses out the version number. If no version is identified then a '-' is returned. * Takes a CPE and parses out the version number. If no version is
* identified then a '-' is returned.
* *
* @param cpe a cpe object * @param cpe a cpe object
* @return a dependency version * @return a dependency version
@@ -759,19 +813,20 @@ public class CveDB {
* Deletes unused dictionary entries from the database. * Deletes unused dictionary entries from the database.
*/ */
public void deleteUnusedCpe() { public void deleteUnusedCpe() {
CallableStatement cs = null; PreparedStatement ps = null;
try { try {
cs = getConnection().prepareCall(statementBundle.getString("DELETE_UNUSED_DICT_CPE")); ps = getConnection().prepareStatement(statementBundle.getString("DELETE_UNUSED_DICT_CPE"));
cs.executeUpdate(); ps.executeUpdate();
} catch (SQLException ex) { } catch (SQLException ex) {
LOGGER.error("Unable to delete CPE dictionary entries", ex); LOGGER.error("Unable to delete CPE dictionary entries", ex);
} finally { } finally {
DBUtils.closeStatement(cs); DBUtils.closeStatement(ps);
} }
} }
/** /**
* This method is only referenced in unused code and will likely break on MySQL if ever used due to the MERGE statement. * This method is only referenced in unused code and will likely break on
* MySQL if ever used due to the MERGE statement.
* *
* Merges CPE entries into the database. * Merges CPE entries into the database.
* *
@@ -782,7 +837,7 @@ public class CveDB {
public void addCpe(String cpe, String vendor, String product) { public void addCpe(String cpe, String vendor, String product) {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
ps = getConnection().prepareCall(statementBundle.getString("ADD_DICT_CPE")); ps = getConnection().prepareStatement(statementBundle.getString("ADD_DICT_CPE"));
ps.setString(1, cpe); ps.setString(1, cpe);
ps.setString(2, vendor); ps.setString(2, vendor);
ps.setString(3, product); ps.setString(3, product);

View File

@@ -70,11 +70,11 @@ public class DatabaseProperties {
/** /**
* A collection of properties about the data. * A collection of properties about the data.
*/ */
private Properties properties; private final Properties properties;
/** /**
* A reference to the database. * A reference to the database.
*/ */
private CveDB cveDB; private final CveDB cveDB;
/** /**
* Constructs a new data properties object. * Constructs a new data properties object.
@@ -83,13 +83,6 @@ public class DatabaseProperties {
*/ */
DatabaseProperties(CveDB cveDB) { DatabaseProperties(CveDB cveDB) {
this.cveDB = cveDB; this.cveDB = cveDB;
loadProperties();
}
/**
* Loads the properties from the database.
*/
private void loadProperties() {
this.properties = cveDB.getProperties(); this.properties = cveDB.getProperties();
} }

View File

@@ -158,6 +158,7 @@ public class CpeUpdater extends BaseUpdater implements CachedWebDataSource {
final String originalPath = file.getPath(); final String originalPath = file.getPath();
final File gzip = new File(originalPath + ".gz"); final File gzip = new File(originalPath + ".gz");
if (gzip.isFile() && !gzip.delete()) { if (gzip.isFile() && !gzip.delete()) {
LOGGER.debug("Failed to delete intial temporary file {}", gzip.toString());
gzip.deleteOnExit(); gzip.deleteOnExit();
} }
if (!file.renameTo(gzip)) { if (!file.renameTo(gzip)) {
@@ -192,8 +193,9 @@ public class CpeUpdater extends BaseUpdater implements CachedWebDataSource {
LOGGER.trace("ignore", ex); LOGGER.trace("ignore", ex);
} }
} }
if (gzip.isFile()) { if (gzip.isFile() && !FileUtils.deleteQuietly(gzip)) {
FileUtils.deleteQuietly(gzip); LOGGER.debug("Failed to delete temporary file {}", gzip.toString());
gzip.deleteOnExit();
} }
} }
} }

View File

@@ -28,6 +28,7 @@ import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
import org.owasp.dependencycheck.data.update.exception.UpdateException; import org.owasp.dependencycheck.data.update.exception.UpdateException;
import org.owasp.dependencycheck.utils.DateUtil; import org.owasp.dependencycheck.utils.DateUtil;
import org.owasp.dependencycheck.utils.DependencyVersion; import org.owasp.dependencycheck.utils.DependencyVersion;
import org.owasp.dependencycheck.utils.InvalidSettingException;
import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.Settings;
import org.owasp.dependencycheck.utils.URLConnectionFactory; import org.owasp.dependencycheck.utils.URLConnectionFactory;
import org.owasp.dependencycheck.utils.URLConnectionFailureException; import org.owasp.dependencycheck.utils.URLConnectionFailureException;
@@ -35,6 +36,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* Checks the gh-pages dependency-check site to determine the current released
* version number. If the released version number is greater then the running
* version number a warning is printed recommending that an upgrade be
* performed.
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
@@ -58,12 +63,14 @@ public class EngineVersionCheck implements CachedWebDataSource {
private CveDB cveDB = null; private CveDB cveDB = null;
/** /**
* The version retrieved from the database properties or web to check against. * The version retrieved from the database properties or web to check
* against.
*/ */
private String updateToVersion; private String updateToVersion;
/** /**
* Getter for updateToVersion - only used for testing. Represents the version retrieved from the database. * Getter for updateToVersion - only used for testing. Represents the
* version retrieved from the database.
* *
* @return the version to test * @return the version to test
*/ */
@@ -72,7 +79,8 @@ public class EngineVersionCheck implements CachedWebDataSource {
} }
/** /**
* Setter for updateToVersion - only used for testing. Represents the version retrieved from the database. * Setter for updateToVersion - only used for testing. Represents the
* version retrieved from the database.
* *
* @param version the version to test * @param version the version to test
*/ */
@@ -80,50 +88,61 @@ public class EngineVersionCheck implements CachedWebDataSource {
updateToVersion = version; updateToVersion = version;
} }
/**
* Downloads the current released version number and compares it to the
* running engine's version number. If the released version number is newer
* a warning is printed recommending an upgrade.
*
* @throws UpdateException thrown if the local database properties could not
* be updated
*/
@Override @Override
public void update() throws UpdateException { public void update() throws UpdateException {
try { try {
openDatabase(); if (Settings.getBoolean(Settings.KEYS.AUTO_UPDATE)) {
LOGGER.debug("Begin Engine Version Check"); openDatabase();
final DatabaseProperties properties = cveDB.getDatabaseProperties(); LOGGER.debug("Begin Engine Version Check");
final long lastChecked = Long.parseLong(properties.getProperty(ENGINE_VERSION_CHECKED_ON, "0")); final DatabaseProperties properties = cveDB.getDatabaseProperties();
final long now = System.currentTimeMillis(); final long lastChecked = Long.parseLong(properties.getProperty(ENGINE_VERSION_CHECKED_ON, "0"));
updateToVersion = properties.getProperty(CURRENT_ENGINE_RELEASE, ""); final long now = System.currentTimeMillis();
final String currentVersion = Settings.getString(Settings.KEYS.APPLICATION_VERSION, "0.0.0"); updateToVersion = properties.getProperty(CURRENT_ENGINE_RELEASE, "");
LOGGER.debug("Last checked: {}", lastChecked); final String currentVersion = Settings.getString(Settings.KEYS.APPLICATION_VERSION, "0.0.0");
LOGGER.debug("Now: {}", now); LOGGER.debug("Last checked: {}", lastChecked);
LOGGER.debug("Current version: {}", currentVersion); LOGGER.debug("Now: {}", now);
final boolean updateNeeded = shouldUpdate(lastChecked, now, properties, currentVersion); LOGGER.debug("Current version: {}", currentVersion);
if (updateNeeded) { final boolean updateNeeded = shouldUpdate(lastChecked, now, properties, currentVersion);
LOGGER.warn("A new version of dependency-check is available. Consider updating to version {}.", if (updateNeeded) {
updateToVersion); LOGGER.warn("A new version of dependency-check is available. Consider updating to version {}.",
updateToVersion);
}
} }
} catch (DatabaseException ex) { } catch (DatabaseException ex) {
LOGGER.debug("Database Exception opening databases to retrieve properties", ex); LOGGER.debug("Database Exception opening databases to retrieve properties", ex);
throw new UpdateException("Error occured updating database properties."); throw new UpdateException("Error occured updating database properties.");
} catch (InvalidSettingException ex) {
LOGGER.debug("Unable to determine if autoupdate is enabled", ex);
} finally { } finally {
closeDatabase(); closeDatabase();
} }
} }
/** /**
* Determines if a new version of the dependency-check engine has been released. * Determines if a new version of the dependency-check engine has been
* released.
* *
* @param lastChecked the epoch time of the last version check * @param lastChecked the epoch time of the last version check
* @param now the current epoch time * @param now the current epoch time
* @param properties the database properties object * @param properties the database properties object
* @param currentVersion the current version of dependency-check * @param currentVersion the current version of dependency-check
* @return <code>true</code> if a newer version of the database has been released; otherwise <code>false</code> * @return <code>true</code> if a newer version of the database has been
* @throws UpdateException thrown if there is an error connecting to the github documentation site or accessing the local * released; otherwise <code>false</code>
* database. * @throws UpdateException thrown if there is an error connecting to the
* github documentation site or accessing the local database.
*/ */
protected boolean shouldUpdate(final long lastChecked, final long now, final DatabaseProperties properties, protected boolean shouldUpdate(final long lastChecked, final long now, final DatabaseProperties properties,
String currentVersion) throws UpdateException { String currentVersion) throws UpdateException {
//check every 30 days if we know there is an update, otherwise check every 7 days //check every 30 days if we know there is an update, otherwise check every 7 days
int checkRange = 30; final int checkRange = 30;
if (updateToVersion.isEmpty()) {
checkRange = 7;
}
if (!DateUtil.withinDateRange(lastChecked, now, checkRange)) { if (!DateUtil.withinDateRange(lastChecked, now, checkRange)) {
LOGGER.debug("Checking web for new version."); LOGGER.debug("Checking web for new version.");
final String currentRelease = getCurrentReleaseVersion(); final String currentRelease = getCurrentReleaseVersion();
@@ -133,14 +152,16 @@ public class EngineVersionCheck implements CachedWebDataSource {
updateToVersion = v.toString(); updateToVersion = v.toString();
if (!currentRelease.equals(updateToVersion)) { if (!currentRelease.equals(updateToVersion)) {
properties.save(CURRENT_ENGINE_RELEASE, updateToVersion); properties.save(CURRENT_ENGINE_RELEASE, updateToVersion);
} else {
properties.save(CURRENT_ENGINE_RELEASE, "");
} }
properties.save(ENGINE_VERSION_CHECKED_ON, Long.toString(now)); properties.save(ENGINE_VERSION_CHECKED_ON, Long.toString(now));
} }
} }
LOGGER.debug("Current Release: {}", updateToVersion); LOGGER.debug("Current Release: {}", updateToVersion);
} }
if (updateToVersion == null) {
LOGGER.debug("Unable to obtain current release");
return false;
}
final DependencyVersion running = new DependencyVersion(currentVersion); final DependencyVersion running = new DependencyVersion(currentVersion);
final DependencyVersion released = new DependencyVersion(updateToVersion); final DependencyVersion released = new DependencyVersion(updateToVersion);
if (running.compareTo(released) < 0) { if (running.compareTo(released) < 0) {
@@ -179,7 +200,8 @@ public class EngineVersionCheck implements CachedWebDataSource {
} }
/** /**
* Retrieves the current released version number from the github documentation site. * Retrieves the current released version number from the github
* documentation site.
* *
* @return the current released version number * @return the current released version number
*/ */
@@ -198,11 +220,11 @@ public class EngineVersionCheck implements CachedWebDataSource {
return releaseVersion.trim(); return releaseVersion.trim();
} }
} catch (MalformedURLException ex) { } catch (MalformedURLException ex) {
LOGGER.debug("unable to retrieve current release version of dependency-check", ex); LOGGER.debug("Unable to retrieve current release version of dependency-check - malformed url?");
} catch (URLConnectionFailureException ex) { } catch (URLConnectionFailureException ex) {
LOGGER.debug("unable to retrieve current release version of dependency-check", ex); LOGGER.debug("Unable to retrieve current release version of dependency-check - connection failed");
} catch (IOException ex) { } catch (IOException ex) {
LOGGER.debug("unable to retrieve current release version of dependency-check", ex); LOGGER.debug("Unable to retrieve current release version of dependency-check - i/o exception");
} finally { } finally {
if (conn != null) { if (conn != null) {
conn.disconnect(); conn.disconnect();

View File

@@ -25,6 +25,8 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import org.owasp.dependencycheck.data.nvdcve.CveDB;
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties; import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
import static org.owasp.dependencycheck.data.nvdcve.DatabaseProperties.MODIFIED; import static org.owasp.dependencycheck.data.nvdcve.DatabaseProperties.MODIFIED;
import org.owasp.dependencycheck.data.update.exception.InvalidDataException; import org.owasp.dependencycheck.data.update.exception.InvalidDataException;
@@ -48,7 +50,7 @@ import org.slf4j.LoggerFactory;
public class NvdCveUpdater extends BaseUpdater implements CachedWebDataSource { public class NvdCveUpdater extends BaseUpdater implements CachedWebDataSource {
/** /**
* The logger * The logger.
*/ */
private static final Logger LOGGER = LoggerFactory.getLogger(NvdCveUpdater.class); private static final Logger LOGGER = LoggerFactory.getLogger(NvdCveUpdater.class);
/** /**
@@ -57,25 +59,31 @@ public class NvdCveUpdater extends BaseUpdater implements CachedWebDataSource {
public static final int MAX_THREAD_POOL_SIZE = Settings.getInt(Settings.KEYS.MAX_DOWNLOAD_THREAD_POOL_SIZE, 3); public static final int MAX_THREAD_POOL_SIZE = Settings.getInt(Settings.KEYS.MAX_DOWNLOAD_THREAD_POOL_SIZE, 3);
/** /**
* <p> * Downloads the latest NVD CVE XML file from the web and imports it into
* Downloads the latest NVD CVE XML file from the web and imports it into the current CVE Database.</p> * the current CVE Database.
* *
* @throws UpdateException is thrown if there is an error updating the database * @throws UpdateException is thrown if there is an error updating the
* database
*/ */
@Override @Override
public void update() throws UpdateException { public void update() throws UpdateException {
try { try {
openDataStores(); openDataStores();
if (checkUpdate()) { boolean autoUpdate = true;
try {
autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE);
} catch (InvalidSettingException ex) {
LOGGER.debug("Invalid setting for auto-update; using true.");
}
if (autoUpdate && checkUpdate()) {
final UpdateableNvdCve updateable = getUpdatesNeeded(); final UpdateableNvdCve updateable = getUpdatesNeeded();
if (updateable.isUpdateNeeded()) { if (updateable.isUpdateNeeded()) {
performUpdate(updateable); performUpdate(updateable);
} }
getProperties().save(DatabaseProperties.LAST_CHECKED, Long.toString(System.currentTimeMillis()));
} }
} catch (MalformedURLException ex) { } catch (MalformedURLException ex) {
LOGGER.warn( throw new UpdateException("NVD CVE properties files contain an invalid URL, unable to update the data to use the most current data.", ex);
"NVD CVE properties files contain an invalid URL, unable to update the data to use the most current data.");
LOGGER.debug("", ex);
} catch (DownloadFailedException ex) { } catch (DownloadFailedException ex) {
LOGGER.warn( LOGGER.warn(
"Unable to download the NVD CVE data; the results may not include the most recent CPE/CVEs from the NVD."); "Unable to download the NVD CVE data; the results may not include the most recent CPE/CVEs from the NVD.");
@@ -83,33 +91,34 @@ public class NvdCveUpdater extends BaseUpdater implements CachedWebDataSource {
LOGGER.info( LOGGER.info(
"If you are behind a proxy you may need to configure dependency-check to use the proxy."); "If you are behind a proxy you may need to configure dependency-check to use the proxy.");
} }
LOGGER.debug("", ex); throw new UpdateException("Unable to download the NVD CVE data.", ex);
} finally { } finally {
closeDataStores(); closeDataStores();
} }
} }
/** /**
* Checks if the NVD CVE XML files were last checked recently. As an optimization, we can avoid repetitive checks against the * Checks if the NVD CVE XML files were last checked recently. As an
* NVD. Setting CVE_CHECK_VALID_FOR_HOURS determines the duration since last check before checking again. A database property * optimization, we can avoid repetitive checks against the NVD. Setting
* stores the timestamp of the last check. * CVE_CHECK_VALID_FOR_HOURS determines the duration since last check before
* checking again. A database property stores the timestamp of the last
* check.
* *
* @return true to proceed with the check, or false to skip. * @return true to proceed with the check, or false to skip
* @throws UpdateException thrown when there is an issue checking for updates. * @throws UpdateException thrown when there is an issue checking for
* updates
*/ */
private boolean checkUpdate() throws UpdateException { private boolean checkUpdate() throws UpdateException {
boolean proceed = true; boolean proceed = true;
// If the valid setting has not been specified, then we proceed to check... // If the valid setting has not been specified, then we proceed to check...
final int validForHours = Settings.getInt(Settings.KEYS.CVE_CHECK_VALID_FOR_HOURS, 0); final int validForHours = Settings.getInt(Settings.KEYS.CVE_CHECK_VALID_FOR_HOURS, 0);
if (0 < validForHours) { if (dataExists() && 0 < validForHours) {
// ms Valid = valid (hours) x 60 min/hour x 60 sec/min x 1000 ms/sec // ms Valid = valid (hours) x 60 min/hour x 60 sec/min x 1000 ms/sec
final long msValid = validForHours * 60L * 60L * 1000L; final long msValid = validForHours * 60L * 60L * 1000L;
final long lastChecked = Long.parseLong(getProperties().getProperty(DatabaseProperties.LAST_CHECKED, "0")); final long lastChecked = Long.parseLong(getProperties().getProperty(DatabaseProperties.LAST_CHECKED, "0"));
final long now = System.currentTimeMillis(); final long now = System.currentTimeMillis();
proceed = (now - lastChecked) > msValid; proceed = (now - lastChecked) > msValid;
if (proceed) { if (!proceed) {
getProperties().save(DatabaseProperties.LAST_CHECKED, Long.toString(now));
} else {
LOGGER.info("Skipping NVD check since last check was within {} hours.", validForHours); LOGGER.info("Skipping NVD check since last check was within {} hours.", validForHours);
LOGGER.debug("Last NVD was at {}, and now {} is within {} ms.", LOGGER.debug("Last NVD was at {}, and now {} is within {} ms.",
lastChecked, now, msValid); lastChecked, now, msValid);
@@ -119,111 +128,130 @@ public class NvdCveUpdater extends BaseUpdater implements CachedWebDataSource {
} }
/** /**
* Downloads the latest NVD CVE XML file from the web and imports it into the current CVE Database. * Checks the CVE Index to ensure data exists and analysis can continue.
* *
* @param updateable a collection of NVD CVE data file references that need to be downloaded and processed to update the * @return true if the database contains data
* database
* @throws UpdateException is thrown if there is an error updating the database
*/ */
public void performUpdate(UpdateableNvdCve updateable) throws UpdateException { private boolean dataExists() {
int maxUpdates = 0; CveDB cve = null;
try { try {
for (NvdCveInfo cve : updateable) { cve = new CveDB();
if (cve.getNeedsUpdate()) { cve.open();
maxUpdates += 1; return cve.dataExists();
} } catch (DatabaseException ex) {
} return false;
if (maxUpdates <= 0) {
return;
}
if (maxUpdates > 3) {
LOGGER.info(
"NVD CVE requires several updates; this could take a couple of minutes.");
}
if (maxUpdates > 0) {
openDataStores();
}
final int poolSize = (MAX_THREAD_POOL_SIZE < maxUpdates) ? MAX_THREAD_POOL_SIZE : maxUpdates;
final ExecutorService downloadExecutors = Executors.newFixedThreadPool(poolSize);
final ExecutorService processExecutor = Executors.newSingleThreadExecutor();
final Set<Future<Future<ProcessTask>>> downloadFutures = new HashSet<Future<Future<ProcessTask>>>(maxUpdates);
for (NvdCveInfo cve : updateable) {
if (cve.getNeedsUpdate()) {
final DownloadTask call = new DownloadTask(cve, processExecutor, getCveDB(), Settings.getInstance());
downloadFutures.add(downloadExecutors.submit(call));
}
}
downloadExecutors.shutdown();
//next, move the future future processTasks to just future processTasks
final Set<Future<ProcessTask>> processFutures = new HashSet<Future<ProcessTask>>(maxUpdates);
for (Future<Future<ProcessTask>> future : downloadFutures) {
Future<ProcessTask> task = null;
try {
task = future.get();
} catch (InterruptedException ex) {
downloadExecutors.shutdownNow();
processExecutor.shutdownNow();
LOGGER.debug("Thread was interrupted during download", ex);
throw new UpdateException("The download was interrupted", ex);
} catch (ExecutionException ex) {
downloadExecutors.shutdownNow();
processExecutor.shutdownNow();
LOGGER.debug("Thread was interrupted during download execution", ex);
throw new UpdateException("The execution of the download was interrupted", ex);
}
if (task == null) {
downloadExecutors.shutdownNow();
processExecutor.shutdownNow();
LOGGER.debug("Thread was interrupted during download");
throw new UpdateException("The download was interrupted; unable to complete the update");
} else {
processFutures.add(task);
}
}
for (Future<ProcessTask> future : processFutures) {
try {
final ProcessTask task = future.get();
if (task.getException() != null) {
throw task.getException();
}
} catch (InterruptedException ex) {
processExecutor.shutdownNow();
LOGGER.debug("Thread was interrupted during processing", ex);
throw new UpdateException(ex);
} catch (ExecutionException ex) {
processExecutor.shutdownNow();
LOGGER.debug("Execution Exception during process", ex);
throw new UpdateException(ex);
} finally {
processExecutor.shutdown();
}
}
if (maxUpdates >= 1) { //ensure the modified file date gets written (we may not have actually updated it)
getProperties().save(updateable.get(MODIFIED));
LOGGER.info("Begin database maintenance.");
getCveDB().cleanupDatabase();
LOGGER.info("End database maintenance.");
}
} finally { } finally {
closeDataStores(); if (cve != null) {
cve.close();
}
} }
} }
/** /**
* Determines if the index needs to be updated. This is done by fetching the NVD CVE meta data and checking the last update * Downloads the latest NVD CVE XML file from the web and imports it into
* date. If the data needs to be refreshed this method will return the NvdCveUrl for the files that need to be updated. * the current CVE Database.
*
* @param updateable a collection of NVD CVE data file references that need
* to be downloaded and processed to update the database
* @throws UpdateException is thrown if there is an error updating the
* database
*/
private void performUpdate(UpdateableNvdCve updateable) throws UpdateException {
int maxUpdates = 0;
for (NvdCveInfo cve : updateable) {
if (cve.getNeedsUpdate()) {
maxUpdates += 1;
}
}
if (maxUpdates <= 0) {
return;
}
if (maxUpdates > 3) {
LOGGER.info("NVD CVE requires several updates; this could take a couple of minutes.");
}
final int poolSize = (MAX_THREAD_POOL_SIZE < maxUpdates) ? MAX_THREAD_POOL_SIZE : maxUpdates;
final ExecutorService downloadExecutors = Executors.newFixedThreadPool(poolSize);
final ExecutorService processExecutor = Executors.newSingleThreadExecutor();
final Set<Future<Future<ProcessTask>>> downloadFutures = new HashSet<Future<Future<ProcessTask>>>(maxUpdates);
for (NvdCveInfo cve : updateable) {
if (cve.getNeedsUpdate()) {
final DownloadTask call = new DownloadTask(cve, processExecutor, getCveDB(), Settings.getInstance());
downloadFutures.add(downloadExecutors.submit(call));
}
}
downloadExecutors.shutdown();
//next, move the future future processTasks to just future processTasks
final Set<Future<ProcessTask>> processFutures = new HashSet<Future<ProcessTask>>(maxUpdates);
for (Future<Future<ProcessTask>> future : downloadFutures) {
Future<ProcessTask> task = null;
try {
task = future.get();
} catch (InterruptedException ex) {
downloadExecutors.shutdownNow();
processExecutor.shutdownNow();
LOGGER.debug("Thread was interrupted during download", ex);
throw new UpdateException("The download was interrupted", ex);
} catch (ExecutionException ex) {
downloadExecutors.shutdownNow();
processExecutor.shutdownNow();
LOGGER.debug("Thread was interrupted during download execution", ex);
throw new UpdateException("The execution of the download was interrupted", ex);
}
if (task == null) {
downloadExecutors.shutdownNow();
processExecutor.shutdownNow();
LOGGER.debug("Thread was interrupted during download");
throw new UpdateException("The download was interrupted; unable to complete the update");
} else {
processFutures.add(task);
}
}
for (Future<ProcessTask> future : processFutures) {
try {
final ProcessTask task = future.get();
if (task.getException() != null) {
throw task.getException();
}
} catch (InterruptedException ex) {
processExecutor.shutdownNow();
LOGGER.debug("Thread was interrupted during processing", ex);
throw new UpdateException(ex);
} catch (ExecutionException ex) {
processExecutor.shutdownNow();
LOGGER.debug("Execution Exception during process", ex);
throw new UpdateException(ex);
} finally {
processExecutor.shutdown();
}
}
if (maxUpdates >= 1) { //ensure the modified file date gets written (we may not have actually updated it)
getProperties().save(updateable.get(MODIFIED));
LOGGER.info("Begin database maintenance.");
getCveDB().cleanupDatabase();
LOGGER.info("End database maintenance.");
}
}
/**
* Determines if the index needs to be updated. This is done by fetching the
* NVD CVE meta data and checking the last update date. If the data needs to
* be refreshed this method will return the NvdCveUrl for the files that
* need to be updated.
* *
* @return the collection of files that need to be updated * @return the collection of files that need to be updated
* @throws MalformedURLException is thrown if the URL for the NVD CVE Meta data is incorrect * @throws MalformedURLException is thrown if the URL for the NVD CVE Meta
* @throws DownloadFailedException is thrown if there is an error. downloading the NVD CVE download data file * data is incorrect
* @throws UpdateException Is thrown if there is an issue with the last updated properties file * @throws DownloadFailedException is thrown if there is an error.
* downloading the NVD CVE download data file
* @throws UpdateException Is thrown if there is an issue with the last
* updated properties file
*/ */
protected final UpdateableNvdCve getUpdatesNeeded() throws MalformedURLException, DownloadFailedException, UpdateException { protected final UpdateableNvdCve getUpdatesNeeded() throws MalformedURLException, DownloadFailedException, UpdateException {
UpdateableNvdCve updates = null; UpdateableNvdCve updates = null;
@@ -287,9 +315,12 @@ public class NvdCveUpdater extends BaseUpdater implements CachedWebDataSource {
* Retrieves the timestamps from the NVD CVE meta data file. * Retrieves the timestamps from the NVD CVE meta data file.
* *
* @return the timestamp from the currently published nvdcve downloads page * @return the timestamp from the currently published nvdcve downloads page
* @throws MalformedURLException thrown if the URL for the NVD CCE Meta data is incorrect. * @throws MalformedURLException thrown if the URL for the NVD CCE Meta data
* @throws DownloadFailedException thrown if there is an error downloading the nvd cve meta data file * is incorrect.
* @throws InvalidDataException thrown if there is an exception parsing the timestamps * @throws DownloadFailedException thrown if there is an error downloading
* the nvd cve meta data file
* @throws InvalidDataException thrown if there is an exception parsing the
* timestamps
* @throws InvalidSettingException thrown if the settings are invalid * @throws InvalidSettingException thrown if the settings are invalid
*/ */
private UpdateableNvdCve retrieveCurrentTimestampsFromWeb() private UpdateableNvdCve retrieveCurrentTimestampsFromWeb()
@@ -311,5 +342,4 @@ public class NvdCveUpdater extends BaseUpdater implements CachedWebDataSource {
} }
return updates; return updates;
} }
} }

View File

@@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.owasp.dependencycheck.data.update.NvdCveUpdater; import org.owasp.dependencycheck.data.update.NvdCveUpdater;
import org.owasp.dependencycheck.data.update.exception.InvalidDataException; import org.owasp.dependencycheck.data.update.exception.InvalidDataException;
import org.owasp.dependencycheck.utils.Settings;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.xml.sax.Attributes; import org.xml.sax.Attributes;
@@ -40,13 +41,18 @@ public class CPEHandler extends DefaultHandler {
*/ */
private static final String CURRENT_SCHEMA_VERSION = "2.3"; private static final String CURRENT_SCHEMA_VERSION = "2.3";
/** /**
* The text content of the node being processed. This can be used during the end element event. * The Starts with expression to filter CVE entries by CPE.
*/
private static final String CPE_STARTS_WITH = Settings.getString(Settings.KEYS.CVE_CPE_STARTS_WITH_FILTER, "cpe:/a:");
/**
* The text content of the node being processed. This can be used during the
* end element event.
*/ */
private StringBuilder nodeText = null; private StringBuilder nodeText = null;
/** /**
* A reference to the current element. * A reference to the current element.
*/ */
private Element current = new Element(); private final Element current = new Element();
/** /**
* The logger. * The logger.
*/ */
@@ -54,7 +60,7 @@ public class CPEHandler extends DefaultHandler {
/** /**
* The list of CPE values. * The list of CPE values.
*/ */
private List<Cpe> data = new ArrayList<Cpe>(); private final List<Cpe> data = new ArrayList<Cpe>();
/** /**
* Returns the list of CPE values. * Returns the list of CPE values.
@@ -72,7 +78,8 @@ public class CPEHandler extends DefaultHandler {
* @param localName the local name * @param localName the local name
* @param qName the qualified name * @param qName the qualified name
* @param attributes the attributes * @param attributes the attributes
* @throws SAXException thrown if there is an exception processing the element * @throws SAXException thrown if there is an exception processing the
* element
*/ */
@Override @Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
@@ -82,7 +89,7 @@ public class CPEHandler extends DefaultHandler {
final String temp = attributes.getValue("deprecated"); final String temp = attributes.getValue("deprecated");
final String value = attributes.getValue("name"); final String value = attributes.getValue("name");
final boolean delete = "true".equalsIgnoreCase(temp); final boolean delete = "true".equalsIgnoreCase(temp);
if (!delete && value.startsWith("cpe:/a:") && value.length() > 7) { if (!delete && value.startsWith(CPE_STARTS_WITH) && value.length() > 7) {
try { try {
final Cpe cpe = new Cpe(value); final Cpe cpe = new Cpe(value);
data.add(cpe); data.add(cpe);
@@ -123,7 +130,8 @@ public class CPEHandler extends DefaultHandler {
* @param ch the char array * @param ch the char array
* @param start the start position of the data read * @param start the start position of the data read
* @param length the length of the data read * @param length the length of the data read
* @throws SAXException thrown if there is an exception processing the characters * @throws SAXException thrown if there is an exception processing the
* characters
*/ */
@Override @Override
public void characters(char[] ch, int start, int length) throws SAXException { public void characters(char[] ch, int start, int length) throws SAXException {
@@ -133,12 +141,14 @@ public class CPEHandler extends DefaultHandler {
} }
/** /**
* Handles the end element event. Stores the CPE data in the Cve Database if the cpe item node is ending. * Handles the end element event. Stores the CPE data in the Cve Database if
* the cpe item node is ending.
* *
* @param uri the element's uri * @param uri the element's uri
* @param localName the local name * @param localName the local name
* @param qName the qualified name * @param qName the qualified name
* @throws SAXException thrown if there is an exception processing the element * @throws SAXException thrown if there is an exception processing the
* element
*/ */
@Override @Override
public void endElement(String uri, String localName, String qName) throws SAXException { public void endElement(String uri, String localName, String qName) throws SAXException {
@@ -177,7 +187,8 @@ public class CPEHandler extends DefaultHandler {
// <editor-fold defaultstate="collapsed" desc="The Element Class that maintains state information about the current node"> // <editor-fold defaultstate="collapsed" desc="The Element Class that maintains state information about the current node">
/** /**
* A simple class to maintain information about the current element while parsing the CPE XML. * A simple class to maintain information about the current element while
* parsing the CPE XML.
*/ */
protected static final class Element { protected static final class Element {

View File

@@ -1,5 +1,5 @@
/** /**
* Contains classes used to parse the CPE XML file from NIST.<br/><br/> * Contains classes used to parse the CPE XML file from NIST.<br><br>
* *
* These classes are not used as they add no value over the existing CPE data contained within the CVE data from the NVD. However, * These classes are not used as they add no value over the existing CPE data contained within the CVE data from the NVD. However,
* we may consider pulling the more descriptive data from the CPE data in the future. * we may consider pulling the more descriptive data from the CPE data in the future.

View File

@@ -17,14 +17,12 @@
*/ */
package org.owasp.dependencycheck.data.update.exception; package org.owasp.dependencycheck.data.update.exception;
import java.io.IOException;
/** /**
* An exception used when an error occurs reading a setting. * An exception used when an error occurs reading a setting.
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
public class UpdateException extends IOException { public class UpdateException extends Exception {
/** /**
* The serial version uid. * The serial version uid.

View File

@@ -22,6 +22,7 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@@ -54,8 +55,9 @@ public class DownloadTask implements Callable<Future<ProcessTask>> {
* @param nvdCveInfo the NVD CVE info * @param nvdCveInfo the NVD CVE info
* @param processor the processor service to submit the downloaded files to * @param processor the processor service to submit the downloaded files to
* @param cveDB the CVE DB to use to store the vulnerability data * @param cveDB the CVE DB to use to store the vulnerability data
* @param settings a reference to the global settings object; this is necessary so that when the thread is started the * @param settings a reference to the global settings object; this is
* dependencies have a correct reference to the global settings. * necessary so that when the thread is started the dependencies have a
* correct reference to the global settings.
* @throws UpdateException thrown if temporary files could not be created * @throws UpdateException thrown if temporary files could not be created
*/ */
public DownloadTask(NvdCveInfo nvdCveInfo, ExecutorService processor, CveDB cveDB, Settings settings) throws UpdateException { public DownloadTask(NvdCveInfo nvdCveInfo, ExecutorService processor, CveDB cveDB, Settings settings) throws UpdateException {
@@ -80,11 +82,11 @@ public class DownloadTask implements Callable<Future<ProcessTask>> {
/** /**
* The CVE DB to use when processing the files. * The CVE DB to use when processing the files.
*/ */
private CveDB cveDB; private final CveDB cveDB;
/** /**
* The processor service to pass the results of the download to. * The processor service to pass the results of the download to.
*/ */
private ExecutorService processorService; private final ExecutorService processorService;
/** /**
* The NVD CVE Meta Data. * The NVD CVE Meta Data.
*/ */
@@ -92,7 +94,7 @@ public class DownloadTask implements Callable<Future<ProcessTask>> {
/** /**
* A reference to the global settings object. * A reference to the global settings object.
*/ */
private Settings settings; private final Settings settings;
/** /**
* Get the value of nvdCveInfo. * Get the value of nvdCveInfo.
@@ -155,28 +157,6 @@ public class DownloadTask implements Callable<Future<ProcessTask>> {
public void setSecond(File second) { public void setSecond(File second) {
this.second = second; this.second = second;
} }
/**
* A placeholder for an exception.
*/
private Exception exception = null;
/**
* Get the value of exception.
*
* @return the value of exception
*/
public Exception getException() {
return exception;
}
/**
* returns whether or not an exception occurred during download.
*
* @return whether or not an exception occurred during download
*/
public boolean hasException() {
return exception != null;
}
@Override @Override
public Future<ProcessTask> call() throws Exception { public Future<ProcessTask> call() throws Exception {
@@ -198,15 +178,15 @@ public class DownloadTask implements Callable<Future<ProcessTask>> {
LOGGER.debug("", ex); LOGGER.debug("", ex);
return null; return null;
} }
if (url1.toExternalForm().endsWith(".xml.gz")) { if (url1.toExternalForm().endsWith(".xml.gz") && !isXml(first)) {
extractGzip(first); extractGzip(first);
} }
if (url2.toExternalForm().endsWith(".xml.gz")) { if (url2.toExternalForm().endsWith(".xml.gz") && !isXml(second)) {
extractGzip(second); extractGzip(second);
} }
LOGGER.info("Download Complete for NVD CVE - {} ({} ms)", nvdCveInfo.getId(), LOGGER.info("Download Complete for NVD CVE - {} ({} ms)", nvdCveInfo.getId(),
System.currentTimeMillis() - startDownload); System.currentTimeMillis() - startDownload);
if (this.processorService == null) { if (this.processorService == null) {
return null; return null;
} }
@@ -226,30 +206,59 @@ public class DownloadTask implements Callable<Future<ProcessTask>> {
* Attempts to delete the files that were downloaded. * Attempts to delete the files that were downloaded.
*/ */
public void cleanup() { public void cleanup() {
boolean deleted = false; if (first != null && first.exists() && first.delete()) {
try { LOGGER.debug("Failed to delete first temporary file {}", second.toString());
if (first != null && first.exists()) { first.deleteOnExit();
deleted = first.delete();
}
} finally {
if (first != null && (first.exists() || !deleted)) {
first.deleteOnExit();
}
} }
if (second != null && second.exists() && !second.delete()) {
LOGGER.debug("Failed to delete second temporary file {}", second.toString());
second.deleteOnExit();
}
}
/**
* Checks the file header to see if it is an XML file.
*
* @param file the file to check
* @return true if the file is XML
*/
public static boolean isXml(File file) {
if (file == null || !file.isFile()) {
return false;
}
InputStream is = null;
try { try {
deleted = false; is = new FileInputStream(file);
if (second != null && second.exists()) {
deleted = second.delete(); final byte[] buf = new byte[5];
int read = 0;
try {
read = is.read(buf);
} catch (IOException ex) {
return false;
} }
return read == 5
&& buf[0] == '<'
&& (buf[1] == '?')
&& (buf[2] == 'x' || buf[2] == 'X')
&& (buf[3] == 'm' || buf[3] == 'M')
&& (buf[4] == 'l' || buf[4] == 'L');
} catch (FileNotFoundException ex) {
return false;
} finally { } finally {
if (second != null && (second.exists() || !deleted)) { if (is != null) {
second.deleteOnExit(); try {
is.close();
} catch (IOException ex) {
LOGGER.debug("Error closing stream", ex);
}
} }
} }
} }
/** /**
* Extracts the file contained in a gzip archive. The extracted file is placed in the exact same path as the file specified. * Extracts the file contained in a gzip archive. The extracted file is
* placed in the exact same path as the file specified.
* *
* @param file the archive file * @param file the archive file
* @throws FileNotFoundException thrown if the file does not exist * @throws FileNotFoundException thrown if the file does not exist
@@ -259,6 +268,7 @@ public class DownloadTask implements Callable<Future<ProcessTask>> {
final String originalPath = file.getPath(); final String originalPath = file.getPath();
final File gzip = new File(originalPath + ".gz"); final File gzip = new File(originalPath + ".gz");
if (gzip.isFile() && !gzip.delete()) { if (gzip.isFile() && !gzip.delete()) {
LOGGER.debug("Failed to delete initial temporary file when extracting 'gz' {}", gzip.toString());
gzip.deleteOnExit(); gzip.deleteOnExit();
} }
if (!file.renameTo(gzip)) { if (!file.renameTo(gzip)) {
@@ -293,8 +303,9 @@ public class DownloadTask implements Callable<Future<ProcessTask>> {
LOGGER.trace("ignore", ex); LOGGER.trace("ignore", ex);
} }
} }
if (gzip.isFile()) { if (gzip.isFile() && !FileUtils.deleteQuietly(gzip)) {
FileUtils.deleteQuietly(gzip); LOGGER.debug("Failed to delete temporary file when extracting 'gz' {}", gzip.toString());
gzip.deleteOnExit();
} }
} }
} }

View File

@@ -99,7 +99,6 @@ public class NvdCve12Handler extends DefaultHandler {
software = null; software = null;
} }
} else if (!skip && current.isProdNode()) { } else if (!skip && current.isProdNode()) {
vendor = attributes.getValue("vendor"); vendor = attributes.getValue("vendor");
product = attributes.getValue("name"); product = attributes.getValue("name");
} else if (!skip && current.isVersNode()) { } else if (!skip && current.isVersNode()) {
@@ -112,15 +111,19 @@ public class NvdCve12Handler extends DefaultHandler {
/*yes yes, this may not actually be an "a" - it could be an OS, etc. but for our /*yes yes, this may not actually be an "a" - it could be an OS, etc. but for our
purposes this is good enough as we won't use this if we don't find a corresponding "a" purposes this is good enough as we won't use this if we don't find a corresponding "a"
in the nvd cve 2.0. */ in the nvd cve 2.0. */
String cpe = "cpe:/a:" + vendor + ":" + product; final int cpeLen = 8 + vendor.length() + product.length()
+ (null != num ? (1 + num.length()) : 0)
+ (null != edition ? (1 + edition.length()) : 0);
final StringBuilder cpe = new StringBuilder(cpeLen);
cpe.append("cpe:/a:").append(vendor).append(':').append(product);
if (num != null) { if (num != null) {
cpe += ':' + num; cpe.append(':').append(num);
} }
if (edition != null) { if (edition != null) {
cpe += ':' + edition; cpe.append(':').append(edition);
} }
final VulnerableSoftware vs = new VulnerableSoftware(); final VulnerableSoftware vs = new VulnerableSoftware();
vs.setCpe(cpe); vs.setCpe(cpe.toString());
vs.setPreviousVersion(prev); vs.setPreviousVersion(prev);
software.add(vs); software.add(vs);
} }

View File

@@ -254,17 +254,16 @@ public class NvdCve20Handler extends DefaultHandler {
* @throws IOException thrown if there is an IOException with the CPE Index * @throws IOException thrown if there is an IOException with the CPE Index
*/ */
private void saveEntry(Vulnerability vuln) throws DatabaseException, CorruptIndexException, IOException { private void saveEntry(Vulnerability vuln) throws DatabaseException, CorruptIndexException, IOException {
if (cveDB == null) {
return;
}
final String cveName = vuln.getName(); final String cveName = vuln.getName();
if (prevVersionVulnMap.containsKey(cveName)) { if (prevVersionVulnMap != null && prevVersionVulnMap.containsKey(cveName)) {
final List<VulnerableSoftware> vulnSoftware = prevVersionVulnMap.get(cveName); final List<VulnerableSoftware> vulnSoftware = prevVersionVulnMap.get(cveName);
for (VulnerableSoftware vs : vulnSoftware) { for (VulnerableSoftware vs : vulnSoftware) {
vuln.updateVulnerableSoftware(vs); vuln.updateVulnerableSoftware(vs);
} }
} }
cveDB.updateVulnerability(vuln); if (cveDB != null) {
cveDB.updateVulnerability(vuln);
}
} }
// <editor-fold defaultstate="collapsed" desc="The Element Class that maintains state information about the current node"> // <editor-fold defaultstate="collapsed" desc="The Element Class that maintains state information about the current node">

View File

@@ -85,7 +85,7 @@ public class ProcessTask implements Callable<ProcessTask> {
/** /**
* A reference to the global settings object. * A reference to the global settings object.
*/ */
private Settings settings; private final Settings settings;
/** /**
* Constructs a new ProcessTask used to process an NVD CVE update. * Constructs a new ProcessTask used to process an NVD CVE update.

View File

@@ -25,6 +25,8 @@ import java.util.Map.Entry;
import java.util.TreeMap; import java.util.TreeMap;
import org.owasp.dependencycheck.utils.DownloadFailedException; import org.owasp.dependencycheck.utils.DownloadFailedException;
import org.owasp.dependencycheck.utils.Downloader; import org.owasp.dependencycheck.utils.Downloader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* Contains a collection of updateable NvdCveInfo objects. This is used to determine which files need to be downloaded and * Contains a collection of updateable NvdCveInfo objects. This is used to determine which files need to be downloaded and
@@ -32,12 +34,16 @@ import org.owasp.dependencycheck.utils.Downloader;
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
public class UpdateableNvdCve implements java.lang.Iterable<NvdCveInfo>, Iterator<NvdCveInfo> { public class UpdateableNvdCve implements Iterable<NvdCveInfo>, Iterator<NvdCveInfo> {
/**
* A reference to the logger.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(UpdateableNvdCve.class);
/** /**
* A collection of sources of data. * A collection of sources of data.
*/ */
private Map<String, NvdCveInfo> collection = new TreeMap<String, NvdCveInfo>(); private final Map<String, NvdCveInfo> collection = new TreeMap<String, NvdCveInfo>();
/** /**
* Returns the collection of NvdCveInfo objects. This method is mainly used for testing. * Returns the collection of NvdCveInfo objects. This method is mainly used for testing.
@@ -91,6 +97,7 @@ public class UpdateableNvdCve implements java.lang.Iterable<NvdCveInfo>, Iterato
item.setId(id); item.setId(id);
item.setUrl(url); item.setUrl(url);
item.setOldSchemaVersionUrl(oldUrl); item.setOldSchemaVersionUrl(oldUrl);
LOGGER.debug("Checking for updates from: {}", url);
item.setTimestamp(Downloader.getLastModified(new URL(url))); item.setTimestamp(Downloader.getLastModified(new URL(url)));
collection.put(id, item); collection.put(id, item);
} }

View File

@@ -1,4 +1,4 @@
/** /**
* Contains classes used to download, parse, and load the NVD CVE data from NIST into the local database.<br/><br/> * Contains classes used to download, parse, and load the NVD CVE data from NIST into the local database.<br><br>
*/ */
package org.owasp.dependencycheck.data.update.nvd; package org.owasp.dependencycheck.data.update.nvd;

View File

@@ -1,6 +1,6 @@
/** /**
* *
* Contains classes used to update the data stores.<br/><br/> * Contains classes used to update the data stores.<br><br>
* *
* The UpdateService will load, any correctly defined CachedWebDataSource(s) and call update() on them. The Cached Data Source * The UpdateService will load, any correctly defined CachedWebDataSource(s) and call update() on them. The Cached Data Source
* must determine if it needs to be updated and if so perform the update. The sub packages contain classes used to perform the * must determine if it needs to be updated and if so perform the update. The sub packages contain classes used to perform the

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
*/ */
@@ -72,6 +73,30 @@ public class Dependency implements Serializable, Comparable<Dependency> {
* The file name of the dependency. * The file name of the dependency.
*/ */
private String fileName; private String fileName;
/**
* The package path.
*/
private String packagePath;
/**
* Returns the package path.
*
* @return the package path
*/
public String getPackagePath() {
return 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.
*/ */
@@ -120,6 +145,7 @@ public class Dependency implements Serializable, Comparable<Dependency> {
this.actualFilePath = file.getAbsolutePath(); this.actualFilePath = file.getAbsolutePath();
this.filePath = this.actualFilePath; this.filePath = this.actualFilePath;
this.fileName = file.getName(); this.fileName = file.getName();
this.packagePath = filePath;
determineHashes(file); determineHashes(file);
} }
@@ -133,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("\\", "\\\\");
@@ -188,6 +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)) {
this.packagePath = filePath;
}
this.filePath = filePath; this.filePath = filePath;
} }
@@ -206,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
*/ */
@@ -221,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
*/ */
@@ -285,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
@@ -297,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
@@ -348,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
*/ */
@@ -580,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
*/ */
@@ -640,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
*/ */
@@ -692,7 +731,8 @@ public class Dependency implements Serializable, Comparable<Dependency> {
} }
/** /**
* Implementation of the Comparable<Dependency> 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
@@ -715,23 +755,24 @@ public class Dependency implements Serializable, Comparable<Dependency> {
} }
final Dependency other = (Dependency) obj; final Dependency other = (Dependency) obj;
return new EqualsBuilder() return new EqualsBuilder()
.appendSuper(super.equals(obj)) .appendSuper(super.equals(obj))
.append(this.actualFilePath, other.actualFilePath) .append(this.actualFilePath, other.actualFilePath)
.append(this.filePath, other.filePath) .append(this.filePath, other.filePath)
.append(this.fileName, other.fileName) .append(this.fileName, other.fileName)
.append(this.md5sum, other.md5sum) .append(this.packagePath, other.packagePath)
.append(this.sha1sum, other.sha1sum) .append(this.md5sum, other.md5sum)
.append(this.identifiers, other.identifiers) .append(this.sha1sum, other.sha1sum)
.append(this.vendorEvidence, other.vendorEvidence) .append(this.identifiers, other.identifiers)
.append(this.productEvidence, other.productEvidence) .append(this.vendorEvidence, other.vendorEvidence)
.append(this.versionEvidence, other.versionEvidence) .append(this.productEvidence, other.productEvidence)
.append(this.description, other.description) .append(this.versionEvidence, other.versionEvidence)
.append(this.license, other.license) .append(this.description, other.description)
.append(this.vulnerabilities, other.vulnerabilities) .append(this.license, other.license)
//.append(this.relatedDependencies, other.relatedDependencies) .append(this.vulnerabilities, other.vulnerabilities)
.append(this.projectReferences, other.projectReferences) //.append(this.relatedDependencies, other.relatedDependencies)
.append(this.availableVersions, other.availableVersions) .append(this.projectReferences, other.projectReferences)
.isEquals(); .append(this.availableVersions, other.availableVersions)
.isEquals();
} }
/** /**
@@ -742,31 +783,33 @@ public class Dependency implements Serializable, Comparable<Dependency> {
@Override @Override
public int hashCode() { public int hashCode() {
return new HashCodeBuilder(MAGIC_HASH_INIT_VALUE, MAGIC_HASH_MULTIPLIER) return new HashCodeBuilder(MAGIC_HASH_INIT_VALUE, MAGIC_HASH_MULTIPLIER)
.append(actualFilePath) .append(actualFilePath)
.append(filePath) .append(filePath)
.append(fileName) .append(fileName)
.append(md5sum) .append(md5sum)
.append(sha1sum) .append(sha1sum)
.append(identifiers) .append(identifiers)
.append(vendorEvidence) .append(vendorEvidence)
.append(productEvidence) .append(productEvidence)
.append(versionEvidence) .append(versionEvidence)
.append(description) .append(description)
.append(license) .append(license)
.append(vulnerabilities) .append(vulnerabilities)
//.append(relatedDependencies) //.append(relatedDependencies)
.append(projectReferences) .append(projectReferences)
.append(availableVersions) .append(availableVersions)
.toHashCode(); .toHashCode();
} }
/** /**
* 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 + "'}"; return "Dependency{ fileName='" + fileName + "', actualFilePath='" + actualFilePath
+ "', filePath='" + filePath + "', packagePath='" + packagePath + "'}";
} }
} }

View File

@@ -97,7 +97,7 @@ public class EvidenceCollection implements Serializable, Iterable<Evidence> {
* Used to iterate over evidence of the specified confidence. * Used to iterate over evidence of the specified confidence.
* *
* @param confidence the confidence level for the evidence to be iterated over. * @param confidence the confidence level for the evidence to be iterated over.
* @return Iterable<Evidence> an iterable collection of evidence * @return Iterable&lt;Evidence&gt; an iterable collection of evidence
*/ */
public final Iterable<Evidence> iterator(Confidence confidence) { public final Iterable<Evidence> iterator(Confidence confidence) {
if (confidence == Confidence.HIGHEST) { if (confidence == Confidence.HIGHEST) {
@@ -168,7 +168,7 @@ public class EvidenceCollection implements Serializable, Iterable<Evidence> {
* Returns a set of Weightings - a list of terms that are believed to be of higher confidence when also found in another * Returns a set of Weightings - a list of terms that are believed to be of higher confidence when also found in another
* location. * location.
* *
* @return Set<String> * @return Set&lt;String&gt;
*/ */
public Set<String> getWeighting() { public Set<String> getWeighting() {
return weightedStrings; return weightedStrings;
@@ -225,7 +225,7 @@ public class EvidenceCollection implements Serializable, Iterable<Evidence> {
/** /**
* Implements the iterator interface for the Evidence Collection. * Implements the iterator interface for the Evidence Collection.
* *
* @return an Iterator<Evidence>. * @return an Iterator&lt;Evidence&gt;
*/ */
@Override @Override
public Iterator<Evidence> iterator() { public Iterator<Evidence> iterator() {

View File

@@ -18,9 +18,11 @@
package org.owasp.dependencycheck.dependency; package org.owasp.dependencycheck.dependency;
import java.io.Serializable; import java.io.Serializable;
import org.apache.commons.lang3.builder.CompareToBuilder;
/** /**
* An external reference for a vulnerability. This contains a name, URL, and a source. * An external reference for a vulnerability. This contains a name, URL, and a
* source.
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
@@ -97,6 +99,11 @@ public class Reference implements Serializable, Comparable<Reference> {
this.source = source; this.source = source;
} }
@Override
public String toString() {
return "Reference: { name='" + this.name + "', url='" + this.url + "', source='" + this.source + "' }";
}
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj == null) { if (obj == null) {
@@ -135,18 +142,10 @@ public class Reference implements Serializable, Comparable<Reference> {
*/ */
@Override @Override
public int compareTo(Reference o) { public int compareTo(Reference o) {
if (source.equals(o.source)) { return new CompareToBuilder()
if (name.equals(o.name)) { .append(source, o.source)
if (url.equals(o.url)) { .append(name, o.name)
return 0; //they are equal .append(url, o.url)
} else { .toComparison();
return url.compareTo(o.url);
}
} else {
return name.compareTo(o.name);
}
} else {
return source.compareTo(o.source);
}
} }
} }

View File

@@ -21,6 +21,7 @@ import java.io.Serializable;
import java.util.Set; import java.util.Set;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
import org.apache.commons.lang3.builder.CompareToBuilder;
/** /**
* Contains the information about a vulnerability. * Contains the information about a vulnerability.
@@ -33,6 +34,7 @@ public class Vulnerability implements Serializable, Comparable<Vulnerability> {
* The serial version uid. * The serial version uid.
*/ */
private static final long serialVersionUID = 307319490326651052L; private static final long serialVersionUID = 307319490326651052L;
/** /**
* The name of the vulnerability. * The name of the vulnerability.
*/ */
@@ -160,7 +162,8 @@ public class Vulnerability implements Serializable, Comparable<Vulnerability> {
* Adds an entry for vulnerable software. * Adds an entry for vulnerable software.
* *
* @param cpe string representation of a cpe * @param cpe string representation of a cpe
* @param previousVersion the previous version (previousVersion - cpe would be considered vulnerable) * @param previousVersion the previous version (previousVersion - cpe would
* be considered vulnerable)
* @return if the add succeeded * @return if the add succeeded
*/ */
public boolean addVulnerableSoftware(String cpe, String previousVersion) { public boolean addVulnerableSoftware(String cpe, String previousVersion) {
@@ -383,16 +386,38 @@ public class Vulnerability implements Serializable, Comparable<Vulnerability> {
return hash; return hash;
} }
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Vulnerability ");
sb.append(this.name);
sb.append("\nReferences:\n");
for (Reference reference : this.references) {
sb.append("=> ");
sb.append(reference);
sb.append("\n");
}
sb.append("\nSoftware:\n");
for (VulnerableSoftware software : this.vulnerableSoftware) {
sb.append("=> ");
sb.append(software);
sb.append("\n");
}
return sb.toString();
}
/** /**
* Compares two vulnerabilities. * Compares two vulnerabilities.
* *
* @param v a vulnerability to be compared * @param v a vulnerability to be compared
* @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than * @return a negative integer, zero, or a positive integer as this object is
* the specified vulnerability * less than, equal to, or greater than the specified vulnerability
*/ */
@Override @Override
public int compareTo(Vulnerability v) { public int compareTo(Vulnerability v) {
return v.getName().compareTo(this.getName()); return new CompareToBuilder()
.append(this.name, v.name)
.toComparison();
//return v.getName().compareTo(this.getName());
} }
/** /**
@@ -408,8 +433,8 @@ public class Vulnerability implements Serializable, Comparable<Vulnerability> {
* Sets the CPE that caused this vulnerability to be flagged. * Sets the CPE that caused this vulnerability to be flagged.
* *
* @param cpeId a CPE identifier * @param cpeId a CPE identifier
* @param previous a flag indicating whether or not all previous versions were affected (any non-null value is * @param previous a flag indicating whether or not all previous versions
* considered true) * were affected (any non-null value is considered true)
*/ */
public void setMatchedCPE(String cpeId, String previous) { public void setMatchedCPE(String cpeId, String previous) {
matchedCPE = cpeId; matchedCPE = cpeId;

View File

@@ -138,7 +138,7 @@ public class VulnerableSoftware extends IndexEntry implements Serializable, Comp
return false; return false;
} }
final VulnerableSoftware other = (VulnerableSoftware) obj; final VulnerableSoftware other = (VulnerableSoftware) obj;
if ((this.getName() == null) ? (other.getName() != null) : !this.getName().equals(other.getName())) { if ((this.name == null) ? (other.getName() != null) : !this.name.equals(other.getName())) {
return false; return false;
} }
return true; return true;
@@ -152,7 +152,7 @@ public class VulnerableSoftware extends IndexEntry implements Serializable, Comp
@Override @Override
public int hashCode() { public int hashCode() {
int hash = 7; int hash = 7;
hash = 83 * hash + (this.getName() != null ? this.getName().hashCode() : 0); hash = 83 * hash + (this.name != null ? this.name.hashCode() : 0);
return hash; return hash;
} }
@@ -163,7 +163,7 @@ public class VulnerableSoftware extends IndexEntry implements Serializable, Comp
*/ */
@Override @Override
public String toString() { public String toString() {
return "VulnerableSoftware{ name=" + name + ", previousVersion=" + previousVersion + '}'; return "VulnerableSoftware{" + name + "[" + previousVersion + "]}";
} }
/** /**
@@ -175,28 +175,19 @@ public class VulnerableSoftware extends IndexEntry implements Serializable, Comp
@Override @Override
public int compareTo(VulnerableSoftware vs) { public int compareTo(VulnerableSoftware vs) {
int result = 0; int result = 0;
final String[] left = this.getName().split(":"); final String[] left = this.name.split(":");
final String[] right = vs.getName().split(":"); final String[] right = vs.getName().split(":");
final int max = (left.length <= right.length) ? left.length : right.length; final int max = (left.length <= right.length) ? left.length : right.length;
if (max > 0) { if (max > 0) {
for (int i = 0; result == 0 && i < max; i++) { for (int i = 0; result == 0 && i < max; i++) {
final String[] subLeft = left[i].split("\\."); final String[] subLeft = left[i].split("(\\.|-)");
final String[] subRight = right[i].split("\\."); final String[] subRight = right[i].split("(\\.|-)");
final int subMax = (subLeft.length <= subRight.length) ? subLeft.length : subRight.length; final int subMax = (subLeft.length <= subRight.length) ? subLeft.length : subRight.length;
if (subMax > 0) { if (subMax > 0) {
for (int x = 0; result == 0 && x < subMax; x++) { for (int x = 0; result == 0 && x < subMax; x++) {
if (isPositiveInteger(subLeft[x]) && isPositiveInteger(subRight[x])) { if (isPositiveInteger(subLeft[x]) && isPositiveInteger(subRight[x])) {
try { try {
result = Long.valueOf(subLeft[x]).compareTo(Long.valueOf(subRight[x])); result = Long.valueOf(subLeft[x]).compareTo(Long.valueOf(subRight[x]));
// final long iLeft = Long.parseLong(subLeft[x]);
// final long iRight = Long.parseLong(subRight[x]);
// if (iLeft != iRight) {
// if (iLeft > iRight) {
// result = 2;
// } else {
// result = -2;
// }
// }
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
//ignore the exception - they obviously aren't numbers //ignore the exception - they obviously aren't numbers
if (!subLeft[x].equalsIgnoreCase(subRight[x])) { if (!subLeft[x].equalsIgnoreCase(subRight[x])) {

View File

@@ -0,0 +1,227 @@
/*
* This file is part of dependency-check-core.
*
* 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.
*
* Copyright (c) 2016 Jeremy Long. All Rights Reserved.
*/
package org.owasp.dependencycheck.exception;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
/**
* A collection of several exceptions.
*
* @author Jeremy Lomg
*/
public class ExceptionCollection extends Exception {
/**
* Instantiates a new exception collection.
*
* @param exceptions a list of exceptions
*/
public ExceptionCollection(List<Throwable> exceptions) {
super();
this.exceptions = exceptions;
}
/**
* Instantiates a new exception collection.
*
* @param msg the exception message
* @param exceptions a list of exceptions
*/
public ExceptionCollection(String msg, List<Throwable> exceptions) {
super(msg);
this.exceptions = exceptions;
}
/**
* Instantiates a new exception collection.
*
* @param exceptions a list of exceptions
* @param fatal indicates if the exception that occurred is fatal - meaning
* that no analysis was performed.
*/
public ExceptionCollection(List<Throwable> exceptions, boolean fatal) {
super();
this.exceptions = exceptions;
this.fatal = fatal;
}
/**
* Instantiates a new exception collection.
*
* @param msg the exception message
* @param exceptions a list of exceptions
* @param fatal indicates if the exception that occurred is fatal - meaning
* that no analysis was performed.
*/
public ExceptionCollection(String msg, List<Throwable> exceptions, boolean fatal) {
super(msg);
this.exceptions = exceptions;
this.fatal = fatal;
}
/**
* Instantiates a new exception collection.
*
* @param exceptions a list of exceptions
* @param fatal indicates if the exception that occurred is fatal - meaning
* that no analysis was performed.
*/
public ExceptionCollection(Throwable exceptions, boolean fatal) {
super();
this.exceptions = new ArrayList<Throwable>();
this.exceptions.add(exceptions);
this.fatal = fatal;
}
/**
* Instantiates a new exception collection.
*
* @param msg the exception message
* @param exception a list of exceptions
*/
public ExceptionCollection(String msg, Throwable exception) {
super(msg);
this.exceptions = new ArrayList<Throwable>();
this.exceptions.add(exception);
this.fatal = false;
}
/**
* Instantiates a new exception collection.
*/
public ExceptionCollection() {
super();
this.exceptions = new ArrayList<Throwable>();
}
/**
* The serial version uid.
*/
private static final long serialVersionUID = 1L;
/**
* A collection of exceptions.
*/
private List<Throwable> exceptions;
/**
* Get the value of exceptions.
*
* @return the value of exceptions
*/
public List<Throwable> getExceptions() {
return exceptions;
}
/**
* Adds an exception to the collection.
*
* @param ex the exception to add
*/
public void addException(Throwable ex) {
this.exceptions.add(ex);
}
/**
* Adds an exception to the collection.
*
* @param ex the exception to add
* @param fatal flag indicating if this is a fatal error
*/
public void addException(Throwable ex, boolean fatal) {
addException(ex);
this.fatal = fatal;
}
/**
* Flag indicating if a fatal exception occurred that would prevent the
* attempt at completing the analysis even if exceptions occurred.
*/
private boolean fatal = false;
/**
* Get the value of fatal.
*
* @return the value of fatal
*/
public boolean isFatal() {
return fatal;
}
/**
* Set the value of fatal.
*
* @param fatal new value of fatal
*/
public void setFatal(boolean fatal) {
this.fatal = fatal;
}
/**
* Prints the stack trace.
*
* @param s the writer to print to
*/
@Override
public void printStackTrace(PrintWriter s) {
s.println("Multiple Exceptions Occured");
super.printStackTrace(s);
for (Throwable t : this.exceptions) {
s.println("Next Exception:");
t.printStackTrace(s);
}
}
/**
* Prints the stack trace.
*
* @param s the stream to write the stack trace to
*/
@Override
public void printStackTrace(PrintStream s) {
s.println("Multiple Exceptions Occurred");
super.printStackTrace(s);
for (Throwable t : this.exceptions) {
s.println("Next Exception:");
t.printStackTrace(s);
}
}
/**
* Returns the error message, including the message from all contained
* exceptions.
*
* @return the error message
*/
@Override
public String getMessage() {
final StringBuilder sb = new StringBuilder();
final String msg = super.getMessage();
if (msg == null || msg.isEmpty()) {
sb.append("One or more exceptions occured during analysis:");
} else {
sb.append(msg);
}
for (Throwable t : this.exceptions) {
sb.append("\n\t").append(t.getMessage());
}
return sb.toString();
}
}

View File

@@ -0,0 +1,66 @@
/*
* This file is part of dependency-check-core.
*
* 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.
*
* Copyright (c) 2016 Jeremy Long. All Rights Reserved.
*/
package org.owasp.dependencycheck.exception;
/**
* An exception used when initializing analyzers.
*
* @author Jeremy Long
*/
public class InitializationException extends Exception {
/**
* The serial version uid.
*/
private static final long serialVersionUID = 1L;
/**
* Creates a new InitializationException.
*/
public InitializationException() {
super();
}
/**
* Creates a new InitializationException.
*
* @param msg a message for the exception.
*/
public InitializationException(String msg) {
super(msg);
}
/**
* Creates a new InitializationException.
*
* @param ex the cause of the exception.
*/
public InitializationException(Throwable ex) {
super(ex);
}
/**
* Creates a new InitializationException.
*
* @param msg a message for the exception.
* @param ex the cause of the exception.
*/
public InitializationException(String msg, Throwable ex) {
super(msg, ex);
}
}

View File

@@ -0,0 +1,66 @@
/*
* This file is part of dependency-check-core.
*
* 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.
*
* Copyright (c) 2016 Jeremy Long. All Rights Reserved.
*/
package org.owasp.dependencycheck.exception;
/**
* An exception used when generating reports.
*
* @author Jeremy Long
*/
public class ReportException extends Exception {
/**
* The serial version uid.
*/
private static final long serialVersionUID = 1L;
/**
* Creates a new ReportException.
*/
public ReportException() {
super();
}
/**
* Creates a new ReportException.
*
* @param msg a message for the exception.
*/
public ReportException(String msg) {
super(msg);
}
/**
* Creates a new ReportException.
*
* @param ex the cause of the exception.
*/
public ReportException(Throwable ex) {
super(ex);
}
/**
* Creates a new ReportException.
*
* @param msg a message for the exception.
* @param ex the cause of the exception.
*/
public ReportException(String msg, Throwable ex) {
super(msg, ex);
}
}

View File

@@ -22,7 +22,7 @@ import java.io.IOException;
/** /**
* An exception used when using @{link DependencyCheckScanAgent} to conduct a scan and the scan fails. * An exception used when using @{link DependencyCheckScanAgent} to conduct a scan and the scan fails.
* *
* @author Steve Springett <steve.springett@owasp.org> * @author Steve Springett
*/ */
public class ScanAgentException extends IOException { public class ScanAgentException extends IOException {

View File

@@ -26,6 +26,7 @@ import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
@@ -37,13 +38,16 @@ import org.apache.velocity.runtime.RuntimeConstants;
import org.owasp.dependencycheck.analyzer.Analyzer; import org.owasp.dependencycheck.analyzer.Analyzer;
import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties; import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.exception.ReportException;
import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.Settings;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* The ReportGenerator is used to, as the name implies, generate reports. Internally the generator uses the Velocity * The ReportGenerator is used to, as the name implies, generate reports.
* Templating Engine. The ReportGenerator exposes a list of Dependencies to the template when generating the report. * Internally the generator uses the Velocity Templating Engine. The
* ReportGenerator exposes a list of Dependencies to the template when
* generating the report.
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
@@ -79,7 +83,7 @@ public class ReportGenerator {
/** /**
* The Velocity Engine. * The Velocity Engine.
*/ */
private final VelocityEngine engine; private final VelocityEngine velocityEngine;
/** /**
* The Velocity Engine Context. * The Velocity Engine Context.
*/ */
@@ -91,13 +95,14 @@ public class ReportGenerator {
* @param applicationName the application name being analyzed * @param applicationName the application name being analyzed
* @param dependencies the list of dependencies * @param dependencies the list of dependencies
* @param analyzers the list of analyzers used * @param analyzers the list of analyzers used
* @param properties the database properties (containing timestamps of the NVD CVE data) * @param properties the database properties (containing timestamps of the
* NVD CVE data)
*/ */
public ReportGenerator(String applicationName, List<Dependency> dependencies, List<Analyzer> analyzers, DatabaseProperties properties) { public ReportGenerator(String applicationName, List<Dependency> dependencies, List<Analyzer> analyzers, DatabaseProperties properties) {
engine = createVelocityEngine(); velocityEngine = createVelocityEngine();
context = createContext(); context = createContext();
engine.init(); velocityEngine.init();
final DateFormat dateFormat = new SimpleDateFormat("MMM d, yyyy 'at' HH:mm:ss z"); final DateFormat dateFormat = new SimpleDateFormat("MMM d, yyyy 'at' HH:mm:ss z");
final DateFormat dateFormatXML = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); final DateFormat dateFormatXML = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
@@ -119,19 +124,19 @@ public class ReportGenerator {
/** /**
* Creates a new Velocity Engine. * Creates a new Velocity Engine.
* *
* @return a velocity engine. * @return a velocity engine
*/ */
private VelocityEngine createVelocityEngine() { private VelocityEngine createVelocityEngine() {
final VelocityEngine engine = new VelocityEngine(); final VelocityEngine velocity = new VelocityEngine();
// Logging redirection for Velocity - Required by Jenkins and other server applications // Logging redirection for Velocity - Required by Jenkins and other server applications
engine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, VelocityLoggerRedirect.class.getName()); velocity.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, VelocityLoggerRedirect.class.getName());
return engine; return velocity;
} }
/** /**
* Creates a new Velocity Context. * Creates a new Velocity Context.
* *
* @return a Velocity Context. * @return a Velocity Context
*/ */
private Context createContext() { private Context createContext() {
return new VelocityContext(); return new VelocityContext();
@@ -143,7 +148,7 @@ public class ReportGenerator {
* @param outputStream the OutputStream to send the generated report to * @param outputStream the OutputStream to send the generated report to
* @param format the format the report should be written in * @param format the format the report should be written in
* @throws IOException is thrown when the template file does not exist * @throws IOException is thrown when the template file does not exist
* @throws Exception is thrown if there is an error writing out the reports. * @throws Exception is thrown if there is an error writing out the reports
*/ */
public void generateReports(OutputStream outputStream, Format format) throws IOException, Exception { public void generateReports(OutputStream outputStream, Format format) throws IOException, Exception {
if (format == Format.XML || format == Format.ALL) { if (format == Format.XML || format == Format.ALL) {
@@ -162,10 +167,9 @@ public class ReportGenerator {
* *
* @param outputDir the path where the reports should be written * @param outputDir the path where the reports should be written
* @param format the format the report should be written in * @param format the format the report should be written in
* @throws IOException is thrown when the template file does not exist * @throws ReportException is thrown if there is an error writing out the reports
* @throws Exception is thrown if there is an error writing out the reports.
*/ */
public void generateReports(String outputDir, Format format) throws IOException, Exception { public void generateReports(String outputDir, Format format) throws ReportException {
if (format == Format.XML || format == Format.ALL) { if (format == Format.XML || format == Format.ALL) {
generateReport("XmlReport", outputDir + File.separator + "dependency-check-report.xml"); generateReport("XmlReport", outputDir + File.separator + "dependency-check-report.xml");
} }
@@ -181,11 +185,12 @@ public class ReportGenerator {
* Generates the Dependency Reports for the identified dependencies. * Generates the Dependency Reports for the identified dependencies.
* *
* @param outputDir the path where the reports should be written * @param outputDir the path where the reports should be written
* @param outputFormat the format the report should be written in (XML, HTML, ALL) * @param outputFormat the format the report should be written in (XML,
* @throws IOException is thrown when the template file does not exist * HTML, ALL)
* @throws Exception is thrown if there is an error writing out the reports. * @throws ReportException is thrown if there is an error creating out the
* reports
*/ */
public void generateReports(String outputDir, String outputFormat) throws IOException, Exception { public void generateReports(String outputDir, String outputFormat) throws ReportException {
final String format = outputFormat.toUpperCase(); final String format = outputFormat.toUpperCase();
final String pathToCheck = outputDir.toLowerCase(); final String pathToCheck = outputDir.toLowerCase();
if (format.matches("^(XML|HTML|VULN|ALL)$")) { if (format.matches("^(XML|HTML|VULN|ALL)$")) {
@@ -217,16 +222,16 @@ public class ReportGenerator {
} }
/** /**
* Generates a report from a given Velocity Template. The template name provided can be the name of a template * Generates a report from a given Velocity Template. The template name
* contained in the jar file, such as 'XmlReport' or 'HtmlReport', or the template name can be the path to a * provided can be the name of a template contained in the jar file, such as
* 'XmlReport' or 'HtmlReport', or the template name can be the path to a
* template file. * template file.
* *
* @param templateName the name of the template to load. * @param templateName the name of the template to load
* @param outputStream the OutputStream to write the report to. * @param outputStream the OutputStream to write the report to
* @throws IOException is thrown when the template file does not exist. * @throws ReportException is thrown when an exception occurs
* @throws Exception is thrown when an exception occurs.
*/ */
protected void generateReport(String templateName, OutputStream outputStream) throws IOException, Exception { protected void generateReport(String templateName, OutputStream outputStream) throws ReportException {
InputStream input = null; InputStream input = null;
String templatePath = null; String templatePath = null;
final File f = new File(templateName); final File f = new File(templateName);
@@ -235,27 +240,30 @@ public class ReportGenerator {
templatePath = templateName; templatePath = templateName;
input = new FileInputStream(f); input = new FileInputStream(f);
} catch (FileNotFoundException ex) { } catch (FileNotFoundException ex) {
LOGGER.error("Unable to generate the report, the report template file could not be found."); throw new ReportException("Unable to locate template file: " + templateName, ex);
LOGGER.debug("", ex);
} }
} else { } else {
templatePath = "templates/" + templateName + ".vsl"; templatePath = "templates/" + templateName + ".vsl";
input = this.getClass().getClassLoader().getResourceAsStream(templatePath); input = this.getClass().getClassLoader().getResourceAsStream(templatePath);
} }
if (input == null) { if (input == null) {
throw new IOException("Template file doesn't exist"); throw new ReportException("Template file doesn't exist: " + templatePath);
} }
final InputStreamReader reader = new InputStreamReader(input, "UTF-8"); InputStreamReader reader = null;
OutputStreamWriter writer = null; OutputStreamWriter writer = null;
try { try {
reader = new InputStreamReader(input, "UTF-8");
writer = new OutputStreamWriter(outputStream, "UTF-8"); writer = new OutputStreamWriter(outputStream, "UTF-8");
if (!velocityEngine.evaluate(context, writer, templatePath, reader)) {
if (!engine.evaluate(context, writer, templatePath, reader)) { throw new ReportException("Failed to convert the template into html.");
throw new Exception("Failed to convert the template into html.");
} }
writer.flush(); writer.flush();
} catch (UnsupportedEncodingException ex) {
throw new ReportException("Unable to generate the report using UTF-8", ex);
} catch (IOException ex) {
throw new ReportException("Unable to write the report", ex);
} finally { } finally {
if (writer != null) { if (writer != null) {
try { try {
@@ -271,25 +279,27 @@ public class ReportGenerator {
LOGGER.trace("", ex); LOGGER.trace("", ex);
} }
} }
try { if (reader != null) {
reader.close(); try {
} catch (IOException ex) { reader.close();
LOGGER.trace("", ex); } catch (IOException ex) {
LOGGER.trace("", ex);
}
} }
} }
} }
/** /**
* Generates a report from a given Velocity Template. The template name provided can be the name of a template * Generates a report from a given Velocity Template. The template name
* contained in the jar file, such as 'XmlReport' or 'HtmlReport', or the template name can be the path to a * provided can be the name of a template contained in the jar file, such as
* 'XmlReport' or 'HtmlReport', or the template name can be the path to a
* template file. * template file.
* *
* @param templateName the name of the template to load. * @param templateName the name of the template to load
* @param outFileName the filename and path to write the report to. * @param outFileName the filename and path to write the report to
* @throws IOException is thrown when the template file does not exist. * @throws ReportException is thrown when the report cannot be generated
* @throws Exception is thrown when an exception occurs.
*/ */
protected void generateReport(String templateName, String outFileName) throws Exception { protected void generateReport(String templateName, String outFileName) throws ReportException {
File outFile = new File(outFileName); File outFile = new File(outFileName);
if (outFile.getParentFile() == null) { if (outFile.getParentFile() == null) {
outFile = new File(".", outFileName); outFile = new File(".", outFileName);
@@ -297,7 +307,7 @@ public class ReportGenerator {
if (!outFile.getParentFile().exists()) { if (!outFile.getParentFile().exists()) {
final boolean created = outFile.getParentFile().mkdirs(); final boolean created = outFile.getParentFile().mkdirs();
if (!created) { if (!created) {
throw new Exception("Unable to create directory '" + outFile.getParentFile().getAbsolutePath() + "'."); throw new ReportException("Unable to create directory '" + outFile.getParentFile().getAbsolutePath() + "'.");
} }
} }
@@ -305,6 +315,8 @@ public class ReportGenerator {
try { try {
outputSteam = new FileOutputStream(outFile); outputSteam = new FileOutputStream(outFile);
generateReport(templateName, outputSteam); generateReport(templateName, outputSteam);
} catch (FileNotFoundException ex) {
throw new ReportException("Unable to write to file: " + outFile, ex);
} finally { } finally {
if (outputSteam != null) { if (outputSteam != null) {
try { try {

View File

@@ -24,15 +24,14 @@ import org.slf4j.LoggerFactory;
/** /**
* <p> * <p>
* DependencyCheck uses {@link org.slf4j.Logger} as a logging framework, and Apache Velocity uses a custom * DependencyCheck uses {@link org.slf4j.Logger} as a logging framework, and Apache Velocity uses a custom logging implementation
* logging implementation that outputs to a file named velocity.log by default. This class is an implementation of a * that outputs to a file named velocity.log by default. This class is an implementation of a custom Velocity logger that
* custom Velocity logger that redirects all velocity logging to the Java Logger class. * redirects all velocity logging to the Java Logger class.
* </p><p> * </p><p>
* This class was written to address permission issues when using Dependency-Check in a server environment (such as the * This class was written to address permission issues when using Dependency-Check in a server environment (such as the Jenkins
* Jenkins plugin). In some circumstances, Velocity would attempt to create velocity.log in an un-writable * plugin). In some circumstances, Velocity would attempt to create velocity.log in an un-writable directory.</p>
* directory.</p>
* *
* @author Steve Springett <steve.springett@owasp.org> * @author Steve Springett
*/ */
public class VelocityLoggerRedirect implements LogChute { public class VelocityLoggerRedirect implements LogChute {
@@ -52,8 +51,7 @@ public class VelocityLoggerRedirect implements LogChute {
} }
/** /**
* Given a Velocity log level and message, this method will call the appropriate Logger level and log the specified * Given a Velocity log level and message, this method will call the appropriate Logger level and log the specified values.
* values.
* *
* @param level the logging level * @param level the logging level
* @param message the message to be logged * @param message the message to be logged
@@ -82,8 +80,8 @@ public class VelocityLoggerRedirect implements LogChute {
} }
/** /**
* Given a Velocity log level, message and Throwable, this method will call the appropriate Logger level and log the * Given a Velocity log level, message and Throwable, this method will call the appropriate Logger level and log the specified
* specified values. * values.
* *
* @param level the logging level * @param level the logging level
* @param message the message to be logged * @param message the message to be logged

View File

@@ -24,7 +24,8 @@ import java.util.regex.Pattern;
/** /**
* <p> * <p>
* A utility class to extract version numbers from file names (or other strings containing version numbers.</p> * A utility class to extract version numbers from file names (or other strings
* containing version numbers.</p>
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
@@ -35,11 +36,19 @@ public final class DependencyVersionUtil {
*/ */
private static final Pattern RX_VERSION = Pattern.compile("\\d+(\\.\\d{1,6})+(\\.?([_-](release|beta|alpha|\\d+)|[a-zA-Z_-]{1,3}\\d{0,8}))?"); private static final Pattern RX_VERSION = Pattern.compile("\\d+(\\.\\d{1,6})+(\\.?([_-](release|beta|alpha|\\d+)|[a-zA-Z_-]{1,3}\\d{0,8}))?");
/** /**
* Regular expression to extract a single version number without periods. This is a last ditch effort just to check in case we * Regular expression to extract a single version number without periods.
* are missing a version number using the previous regex. * This is a last ditch effort just to check in case we are missing a
* version number using the previous regex.
*/ */
private static final Pattern RX_SINGLE_VERSION = Pattern.compile("\\d+(\\.?([_-](release|beta|alpha)|[a-zA-Z_-]{1,3}\\d{1,8}))?"); private static final Pattern RX_SINGLE_VERSION = Pattern.compile("\\d+(\\.?([_-](release|beta|alpha)|[a-zA-Z_-]{1,3}\\d{1,8}))?");
/**
* Regular expression to extract the part before the version numbers if
* there are any based on RX_VERSION. In most cases, this part represents a
* more accurate name.
*/
private static final Pattern RX_PRE_VERSION = Pattern.compile("^(.+)[_-](\\d+\\.\\d{1,6})+");
/** /**
* Private constructor for utility class. * Private constructor for utility class.
*/ */
@@ -48,10 +57,12 @@ public final class DependencyVersionUtil {
/** /**
* <p> * <p>
* A utility class to extract version numbers from file names (or other strings containing version numbers.<br/> * A utility class to extract version numbers from file names (or other
* Example:<br/> * strings containing version numbers.</p>
* Give the file name: library-name-1.4.1r2-release.jar<br/> * <pre>
* This function would return: 1.4.1.r2</p> * Example:
* Give the file name: library-name-1.4.1r2-release.jar
* This function would return: 1.4.1.r2</pre>
* *
* @param text the text being analyzed * @param text the text being analyzed
* @return a DependencyVersion containing the version * @return a DependencyVersion containing the version
@@ -94,4 +105,30 @@ public final class DependencyVersionUtil {
} }
return new DependencyVersion(version); return new DependencyVersion(version);
} }
/**
* <p>
* A utility class to extract the part before version numbers from file
* names (or other strings containing version numbers. In most cases, this
* part represents a more accurate name than the full file name.</p>
* <pre>
* Example:
* Give the file name: library-name-1.4.1r2-release.jar
* This function would return: library-name</pre>
*
* @param text the text being analyzed
* @return the part before the version numbers if any, otherwise return the
* text itself.
*/
public static String parsePreVersion(String text) {
if (parseVersion(text) == null) {
return text;
}
final Matcher matcher = RX_PRE_VERSION.matcher(text);
if (matcher.find()) {
return matcher.group(1);
}
return text;
}
} }

View File

@@ -40,7 +40,7 @@ import java.util.Set;
* FileFilter filter = FileFilterBuilder.newInstance().addExtensions("jar", "war").build(); * FileFilter filter = FileFilterBuilder.newInstance().addExtensions("jar", "war").build();
* </pre> * </pre>
* *
* @author Dale Visser <dvisser@ida.org> * @author Dale Visser
* @see <a href="https://en.wikipedia.org/wiki/Builder_pattern">Builder pattern</a> * @see <a href="https://en.wikipedia.org/wiki/Builder_pattern">Builder pattern</a>
*/ */
public class FileFilterBuilder { public class FileFilterBuilder {

View File

@@ -50,7 +50,7 @@ public abstract class Filter<T> {
if (next == null) { if (next == null) {
throw new NoSuchElementException(); throw new NoSuchElementException();
} }
T returnValue = next; final T returnValue = next;
toNext(); toNext();
return returnValue; return returnValue;
} }
@@ -63,7 +63,7 @@ public abstract class Filter<T> {
private void toNext() { private void toNext() {
next = null; next = null;
while (iterator.hasNext()) { while (iterator.hasNext()) {
T item = iterator.next(); final T item = iterator.next();
if (item != null && passes(item)) { if (item != null && passes(item)) {
next = item; next = item;
break; break;

View File

@@ -0,0 +1,97 @@
/*
* This file is part of dependency-check-core.
*
* 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.
*
* Copyright (c) 2016 Jeremy Long. All Rights Reserved.
*/
package org.owasp.dependencycheck.xml.hints;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
/**
* An XML parsing error handler.
*
* @author Jeremy Long
*/
public class HintErrorHandler implements ErrorHandler {
/**
* The logger.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(HintErrorHandler.class);
/**
* Builds a prettier exception message.
*
* @param ex the SAXParseException
* @return an easier to read exception message
*/
private String getPrettyParseExceptionInfo(SAXParseException ex) {
final StringBuilder sb = new StringBuilder();
if (ex.getSystemId() != null) {
sb.append("systemId=").append(ex.getSystemId()).append(", ");
}
if (ex.getPublicId() != null) {
sb.append("publicId=").append(ex.getPublicId()).append(", ");
}
if (ex.getLineNumber() > 0) {
sb.append("Line=").append(ex.getLineNumber());
}
if (ex.getColumnNumber() > 0) {
sb.append(", Column=").append(ex.getColumnNumber());
}
sb.append(": ").append(ex.getMessage());
return sb.toString();
}
/**
* Logs warnings.
*
* @param ex the warning to log
* @throws SAXException is never thrown
*/
@Override
public void warning(SAXParseException ex) throws SAXException {
LOGGER.debug("", ex);
}
/**
* Handles errors.
*
* @param ex the error to handle
* @throws SAXException is always thrown
*/
@Override
public void error(SAXParseException ex) throws SAXException {
throw new SAXException(getPrettyParseExceptionInfo(ex));
}
/**
* Handles fatal exceptions.
*
* @param ex a fatal exception
* @throws SAXException is always
*/
@Override
public void fatalError(SAXParseException ex) throws SAXException {
throw new SAXException(getPrettyParseExceptionInfo(ex));
}
}

View File

@@ -0,0 +1,234 @@
/*
* This file is part of dependency-check-core.
*
* 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.
*
* Copyright (c) 2016 Jeremy Long. All Rights Reserved.
*/
package org.owasp.dependencycheck.xml.hints;
import java.util.ArrayList;
import java.util.List;
import org.owasp.dependencycheck.dependency.Confidence;
import org.owasp.dependencycheck.xml.suppression.PropertyType;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* A handler to load hint rules.
*
* @author Jeremy Long
*/
public class HintHandler extends DefaultHandler {
//<editor-fold defaultstate="collapsed" desc="Element and attribute names">
/**
* Element name.
*/
private static final String HINT = "hint";
/**
* Element name.
*/
private static final String GIVEN = "given";
/**
* Element name.
*/
private static final String ADD = "add";
/**
* Element name.
*/
private static final String EVIDENCE = "evidence";
/**
* Element name.
*/
private static final String FILE_NAME = "fileName";
/**
* Element name.
*/
private static final String VENDOR_DUPLICATING_RULE = "vendorDuplicatingHint";
/**
* Attribute name.
*/
private static final String DUPLICATE = "duplicate";
/**
* Attribute value.
*/
private static final String VENDOR = "vendor";
/**
* Attribute value.
*/
private static final String PRODUCT = "product";
/**
* Attribute value.
*/
private static final String VERSION = "version";
/**
* Attribute name.
*/
private static final String CONFIDENCE = "confidence";
/**
* Attribute name.
*/
private static final String VALUE = "value";
/**
* Attribute name.
*/
private static final String NAME = "name";
/**
* Attribute name.
*/
private static final String SOURCE = "source";
/**
* Attribute name.
*/
private static final String TYPE = "type";
/**
* Attribute name.
*/
private static final String CASE_SENSITIVE = "caseSensitive";
/**
* Attribute name.
*/
private static final String REGEX = "regex";
/**
* Attribute name.
*/
private static final String CONTAINS = "contains";
//</editor-fold>
/**
* The list of hint rules.
*/
private final List<HintRule> hintRules = new ArrayList<HintRule>();
/**
* Returns the list of hint rules.
*
* @return the value of hintRules
*/
public List<HintRule> getHintRules() {
return hintRules;
}
/**
* The list of vendor duplicating hint rules.
*/
private final List<VendorDuplicatingHintRule> vendorDuplicatingHintRules = new ArrayList<VendorDuplicatingHintRule>();
/**
* Returns the list of vendor duplicating hint rules.
*
* @return the list of vendor duplicating hint rules
*/
public List<VendorDuplicatingHintRule> getVendorDuplicatingHintRules() {
return vendorDuplicatingHintRules;
}
/**
* The current rule being read.
*/
private HintRule rule;
/**
* The current state of the parent node (to differentiate between 'add' and
* 'given').
*/
private boolean inAddNode = false;
/**
* Handles the start element event.
*
* @param uri the uri of the element being processed
* @param localName the local name of the element being processed
* @param qName the qName of the element being processed
* @param attr the attributes of the element being processed
* @throws SAXException thrown if there is an exception processing
*/
@Override
public void startElement(String uri, String localName, String qName, Attributes attr) throws SAXException {
if (HINT.equals(qName)) {
rule = new HintRule();
} else if (ADD.equals(qName)) {
inAddNode = true;
} else if (GIVEN.equals(qName)) {
inAddNode = false;
} else if (EVIDENCE.equals(qName)) {
final String hintType = attr.getValue(TYPE);
if (VENDOR.equals(hintType)) {
if (inAddNode) {
rule.addAddVendor(attr.getValue(SOURCE),
attr.getValue(NAME),
attr.getValue(VALUE),
Confidence.valueOf(attr.getValue(CONFIDENCE)));
} else {
rule.addGivenVendor(attr.getValue(SOURCE),
attr.getValue(NAME),
attr.getValue(VALUE),
Confidence.valueOf(attr.getValue(CONFIDENCE)));
}
} else if (PRODUCT.equals(hintType)) {
if (inAddNode) {
rule.addAddProduct(attr.getValue(SOURCE),
attr.getValue(NAME),
attr.getValue(VALUE),
Confidence.valueOf(attr.getValue(CONFIDENCE)));
} else {
rule.addGivenProduct(attr.getValue(SOURCE),
attr.getValue(NAME),
attr.getValue(VALUE),
Confidence.valueOf(attr.getValue(CONFIDENCE)));
}
} else if (VERSION.equals(hintType)) {
if (inAddNode) {
rule.addAddVersion(attr.getValue(SOURCE),
attr.getValue(NAME),
attr.getValue(VALUE),
Confidence.valueOf(attr.getValue(CONFIDENCE)));
}
}
} else if (FILE_NAME.equals(qName)) {
final PropertyType pt = new PropertyType();
pt.setValue(attr.getValue(CONTAINS));
if (attr.getLength() > 0) {
final String regex = attr.getValue(REGEX);
if (regex != null) {
pt.setRegex(Boolean.parseBoolean(regex));
}
final String caseSensitive = attr.getValue(CASE_SENSITIVE);
if (caseSensitive != null) {
pt.setCaseSensitive(Boolean.parseBoolean(caseSensitive));
}
}
rule.addFilename(pt);
} else if (VENDOR_DUPLICATING_RULE.equals(qName)) {
vendorDuplicatingHintRules.add(new VendorDuplicatingHintRule(attr.getValue(VALUE), attr.getValue(DUPLICATE)));
}
}
/**
* Handles the end element event.
*
* @param uri the element's URI
* @param localName the local name
* @param qName the qualified name
* @throws SAXException thrown if there is an exception processing the
* element
*/
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (HINT.equals(qName) && rule != null) {
hintRules.add(rule);
rule = null;
}
}
}

View File

@@ -0,0 +1,68 @@
/*
* This file is part of dependency-check-core.
*
* 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.
*
* Copyright (c) 2016 Jeremy Long. All Rights Reserved.
*/
package org.owasp.dependencycheck.xml.hints;
import java.io.IOException;
/**
* An exception used when parsing a suppression rule file fails.
*
* @author Jeremy Long
*/
public class HintParseException extends IOException {
/**
* The serial version UID for serialization.
*/
private static final long serialVersionUID = 1L;
/**
* Creates a new SuppressionParseException.
*/
public HintParseException() {
super();
}
/**
* Creates a new SuppressionParseException.
*
* @param msg a message for the exception.
*/
public HintParseException(String msg) {
super(msg);
}
/**
* Creates a new SuppressionParseException.
*
* @param ex the cause of the parse exception
*/
public HintParseException(Throwable ex) {
super(ex);
}
/**
* Creates a new SuppressionParseException.
*
* @param msg a message for the exception.
* @param ex the cause of the parse exception
*/
public HintParseException(String msg, Throwable ex) {
super(msg, ex);
}
}

View File

@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
* *
* Copyright (c) 2013 Jeremy Long. All Rights Reserved. * Copyright (c) 2016 Jeremy Long. All Rights Reserved.
*/ */
package org.owasp.dependencycheck.suppression; package org.owasp.dependencycheck.xml.hints;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@@ -24,7 +24,6 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.Reader; import java.io.Reader;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory; import javax.xml.parsers.SAXParserFactory;
@@ -36,44 +35,54 @@ import org.xml.sax.SAXException;
import org.xml.sax.XMLReader; import org.xml.sax.XMLReader;
/** /**
* A simple validating parser for XML Suppression Rules. * A simple validating parser for XML Hint Rules.
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
public class SuppressionParser { public class HintParser {
/** /**
* The logger. * The logger.
*/ */
private static final Logger LOGGER = LoggerFactory.getLogger(SuppressionParser.class); private static final Logger LOGGER = LoggerFactory.getLogger(HintParser.class);
/** /**
* JAXP Schema Language. Source: http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html * JAXP Schema Language. Source:
* http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html
*/ */
public static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage"; public static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
/** /**
* W3C XML Schema. Source: http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html * W3C XML Schema. Source:
* http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html
*/ */
public static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema"; public static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";
/** /**
* JAXP Schema Source. Source: http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html * JAXP Schema Source. Source:
* http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html
*/ */
public static final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource"; public static final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource";
/** /**
* Parses the given xml file and returns a list of the suppression rules contained. * The schema for the hint XML files.
*
* @param file an xml file containing suppression rules
* @return a list of suppression rules
* @throws SuppressionParseException thrown if the xml file cannot be parsed
*/ */
public List<SuppressionRule> parseSuppressionRules(File file) throws SuppressionParseException { private static final String HINT_SCHEMA = "schema/dependency-hint.1.1.xsd";
/**
* Parses the given XML file and returns a list of the hints contained.
*
* @param file an XML file containing hints
* @return a list of hint rules
* @throws HintParseException thrown if the XML file cannot be parsed
*/
public Hints parseHints(File file) throws HintParseException {
FileInputStream fis = null; FileInputStream fis = null;
try { try {
fis = new FileInputStream(file); fis = new FileInputStream(file);
return parseSuppressionRules(fis); return parseHints(fis);
} catch (IOException ex) { } catch (IOException ex) {
LOGGER.debug("", ex); LOGGER.debug("", ex);
throw new SuppressionParseException(ex); throw new HintParseException(ex);
} catch (SAXException ex) {
throw new HintParseException(ex);
} finally { } finally {
if (fis != null) { if (fis != null) {
try { try {
@@ -86,45 +95,61 @@ public class SuppressionParser {
} }
/** /**
* Parses the given xml stream and returns a list of the suppression rules contained. * Parses the given XML stream and returns a list of the hint rules
* contained.
* *
* @param inputStream an InputStream containing suppression rues * @param inputStream an InputStream containing hint rules
* @return a list of suppression rules * @return a list of hint rules
* @throws SuppressionParseException if the xml cannot be parsed * @throws HintParseException thrown if the XML cannot be parsed
* @throws SAXException thrown if the XML cannot be parsed
*/ */
public List<SuppressionRule> parseSuppressionRules(InputStream inputStream) throws SuppressionParseException { public Hints parseHints(InputStream inputStream) throws HintParseException, SAXException {
InputStream schemaStream = null;
try { try {
final InputStream schemaStream = this.getClass().getClassLoader().getResourceAsStream("schema/suppression.xsd"); schemaStream = this.getClass().getClassLoader().getResourceAsStream(HINT_SCHEMA);
final SuppressionHandler handler = new SuppressionHandler(); final HintHandler handler = new HintHandler();
final SAXParserFactory factory = SAXParserFactory.newInstance(); final SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true); factory.setNamespaceAware(true);
factory.setValidating(true); factory.setValidating(true);
final SAXParser saxParser = factory.newSAXParser(); final SAXParser saxParser = factory.newSAXParser();
saxParser.setProperty(SuppressionParser.JAXP_SCHEMA_LANGUAGE, SuppressionParser.W3C_XML_SCHEMA); saxParser.setProperty(HintParser.JAXP_SCHEMA_LANGUAGE, HintParser.W3C_XML_SCHEMA);
saxParser.setProperty(SuppressionParser.JAXP_SCHEMA_SOURCE, new InputSource(schemaStream)); saxParser.setProperty(HintParser.JAXP_SCHEMA_SOURCE, new InputSource(schemaStream));
final XMLReader xmlReader = saxParser.getXMLReader(); final XMLReader xmlReader = saxParser.getXMLReader();
xmlReader.setErrorHandler(new SuppressionErrorHandler()); xmlReader.setErrorHandler(new HintErrorHandler());
xmlReader.setContentHandler(handler); xmlReader.setContentHandler(handler);
final Reader reader = new InputStreamReader(inputStream, "UTF-8"); final Reader reader = new InputStreamReader(inputStream, "UTF-8");
final InputSource in = new InputSource(reader); final InputSource in = new InputSource(reader);
//in.setEncoding("UTF-8");
xmlReader.parse(in); xmlReader.parse(in);
final Hints hints = new Hints();
return handler.getSuppressionRules(); hints.setHintRules(handler.getHintRules());
hints.setVendorDuplicatingHintRules(handler.getVendorDuplicatingHintRules());
return hints;
} catch (ParserConfigurationException ex) { } catch (ParserConfigurationException ex) {
LOGGER.debug("", ex); LOGGER.debug("", ex);
throw new SuppressionParseException(ex); throw new HintParseException(ex);
} catch (SAXException ex) { } catch (SAXException ex) {
LOGGER.debug("", ex); if (ex.getMessage().contains("Cannot find the declaration of element 'hints'.")) {
throw new SuppressionParseException(ex); throw ex;
} else {
LOGGER.debug("", ex);
throw new HintParseException(ex);
}
} catch (FileNotFoundException ex) { } catch (FileNotFoundException ex) {
LOGGER.debug("", ex); LOGGER.debug("", ex);
throw new SuppressionParseException(ex); throw new HintParseException(ex);
} catch (IOException ex) { } catch (IOException ex) {
LOGGER.debug("", ex); LOGGER.debug("", ex);
throw new SuppressionParseException(ex); throw new HintParseException(ex);
} finally {
if (schemaStream != null) {
try {
schemaStream.close();
} catch (IOException ex) {
LOGGER.debug("Error closing hint file stream", ex);
}
}
} }
} }
} }

View File

@@ -0,0 +1,185 @@
/*
* This file is part of dependency-check-core.
*
* 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.
*
* Copyright (c) 2016 Jeremy Long. All Rights Reserved.
*/
package org.owasp.dependencycheck.xml.hints;
import java.util.ArrayList;
import java.util.List;
import org.owasp.dependencycheck.dependency.Confidence;
import org.owasp.dependencycheck.dependency.Evidence;
import org.owasp.dependencycheck.xml.suppression.PropertyType;
/**
* A collection of product and vendor evidence to match; if any evidence is
* matched the addVendor and addProduct evidence should be added to the
* dependency.
*
* @author Jeremy Long
*/
public class HintRule {
/**
* The list of file names to match.
*/
private final List<PropertyType> filenames = new ArrayList<PropertyType>();
/**
* Adds the filename evidence to the collection.
*
* @param filename the filename to add
*/
public void addFilename(PropertyType filename) {
this.filenames.add(filename);
}
/**
* Returns the list of filename evidence to match against.
*
* @return the list of filename evidence to match against
*/
public List<PropertyType> getFilenames() {
return filenames;
}
/**
* The list of product evidence that is being matched.
*/
private final List<Evidence> givenProduct = new ArrayList<Evidence>();
/**
* Adds a given product to the list of evidence to matched.
*
* @param source the source of the evidence
* @param name the name of the evidence
* @param value the value of the evidence
* @param confidence the confidence of the evidence
*/
public void addGivenProduct(String source, String name, String value, Confidence confidence) {
givenProduct.add(new Evidence(source, name, value, confidence));
}
/**
* Get the value of givenProduct.
*
* @return the value of givenProduct
*/
public List<Evidence> getGivenProduct() {
return givenProduct;
}
/**
* The list of vendor evidence that is being matched.
*/
private final List<Evidence> givenVendor = new ArrayList<Evidence>();
/**
* The list of product evidence to add.
*/
private final List<Evidence> addProduct = new ArrayList<Evidence>();
/**
* The list of version evidence to add.
*/
private final List<Evidence> addVersion = new ArrayList<Evidence>();
/**
* Adds a given vendors to the list of evidence to matched.
*
* @param source the source of the evidence
* @param name the name of the evidence
* @param value the value of the evidence
* @param confidence the confidence of the evidence
*/
public void addGivenVendor(String source, String name, String value, Confidence confidence) {
givenVendor.add(new Evidence(source, name, value, confidence));
}
/**
* Get the value of givenVendor.
*
* @return the value of givenVendor
*/
public List<Evidence> getGivenVendor() {
return givenVendor;
}
/**
* Adds a given product to the list of evidence to add when matched.
*
* @param source the source of the evidence
* @param name the name of the evidence
* @param value the value of the evidence
* @param confidence the confidence of the evidence
*/
public void addAddProduct(String source, String name, String value, Confidence confidence) {
addProduct.add(new Evidence(source, name, value, confidence));
}
/**
* Get the value of addProduct.
*
* @return the value of addProduct
*/
public List<Evidence> getAddProduct() {
return addProduct;
}
/**
* Adds a given version to the list of evidence to add when matched.
*
* @param source the source of the evidence
* @param name the name of the evidence
* @param value the value of the evidence
* @param confidence the confidence of the evidence
*/
public void addAddVersion(String source, String name, String value, Confidence confidence) {
addVersion.add(new Evidence(source, name, value, confidence));
}
/**
* Get the value of addVersion.
*
* @return the value of addVersion
*/
public List<Evidence> getAddVersion() {
return addVersion;
}
/**
* The list of vendor hints to add.
*/
private final List<Evidence> addVendor = new ArrayList<Evidence>();
/**
* Adds a given vendor to the list of evidence to add when matched.
*
* @param source the source of the evidence
* @param name the name of the evidence
* @param value the value of the evidence
* @param confidence the confidence of the evidence
*/
public void addAddVendor(String source, String name, String value, Confidence confidence) {
addVendor.add(new Evidence(source, name, value, confidence));
}
/**
* Get the value of addVendor.
*
* @return the value of addVendor
*/
public List<Evidence> getAddVendor() {
return addVendor;
}
}

Some files were not shown because too many files have changed in this diff Show More