mirror of
https://github.com/ysoftdevs/DependencyCheck.git
synced 2026-01-14 15:53:36 +01:00
Compare commits
941 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0b549e427 | ||
|
|
75207169e3 | ||
|
|
e07f568237 | ||
|
|
e2cd99d40d | ||
|
|
27f2682a98 | ||
|
|
34a2110e9a | ||
|
|
96ba51db4f | ||
|
|
9c6053a60a | ||
|
|
358367ef9e | ||
|
|
a12bc44ecd | ||
|
|
773ac019f8 | ||
|
|
e751b7b814 | ||
|
|
824aa23b9b | ||
|
|
b7b97960a6 | ||
|
|
40f0e907e1 | ||
|
|
5ff0dc885d | ||
|
|
e70a0ee238 | ||
|
|
9338697079 | ||
|
|
4018a4e1de | ||
|
|
e8788dd2a4 | ||
|
|
e70c2f2b05 | ||
|
|
5ed0583039 | ||
|
|
f76d7295f9 | ||
|
|
6e280c4958 | ||
|
|
48b4ef1944 | ||
|
|
9150df964f | ||
|
|
b2237394e1 | ||
|
|
b3a0f7ad26 | ||
|
|
782ba42abc | ||
|
|
74b93ce602 | ||
|
|
e907c40f17 | ||
|
|
13a9dedb1e | ||
|
|
b37698f245 | ||
|
|
d30d000346 | ||
|
|
446239a5bd | ||
|
|
ac25aa795b | ||
|
|
f117a9ded0 | ||
|
|
947d38ccd2 | ||
|
|
23f7996db8 | ||
|
|
9fdff51f26 | ||
|
|
9b43bf004a | ||
|
|
5d73faa1f0 | ||
|
|
9e70279b31 | ||
|
|
9e671d1065 | ||
|
|
7e2c4af0b3 | ||
|
|
11f9092a65 | ||
|
|
6017e5c217 | ||
|
|
b2149ff4b9 | ||
|
|
1a5177c576 | ||
|
|
7020c9931a | ||
|
|
9bc43e2e8e | ||
|
|
26a4e7451e | ||
|
|
3470d33bdc | ||
|
|
51c96894b4 | ||
|
|
7fc2be6a0a | ||
|
|
110c97bc15 | ||
|
|
8d51d8fa1f | ||
|
|
4b02a567e0 | ||
|
|
5a939ec108 | ||
|
|
d9c4480627 | ||
|
|
9388340e23 | ||
|
|
2285d2ef4b | ||
|
|
f84aea0040 | ||
|
|
452969cc92 | ||
|
|
128a600f18 | ||
|
|
7dd9a52e78 | ||
|
|
ff341b7228 | ||
|
|
92a8b4ca85 | ||
|
|
384199b28d | ||
|
|
44edcabe15 | ||
|
|
1a5e9884fc | ||
|
|
cda81315d2 | ||
|
|
d7100e54d1 | ||
|
|
989caead9c | ||
|
|
a9d3b627f1 | ||
|
|
99a1606df1 | ||
|
|
6326513c63 | ||
|
|
f6cfae595a | ||
|
|
0794efcf41 | ||
|
|
b9ea82f2c1 | ||
|
|
8b705b3370 | ||
|
|
c684607a4d | ||
|
|
b00833c2de | ||
|
|
0ca6bc6ab6 | ||
|
|
60faddff9b | ||
|
|
b35da8ad4b | ||
|
|
79887c148a | ||
|
|
1ae3457ee6 | ||
|
|
d2154c9d29 | ||
|
|
40ede24a99 | ||
|
|
5960ba919d | ||
|
|
f6aaaa8815 | ||
|
|
6f1b20c936 | ||
|
|
7734a50427 | ||
|
|
aef118d375 | ||
|
|
22cae71999 | ||
|
|
29d127303c | ||
|
|
5574f1c24f | ||
|
|
9457744571 | ||
|
|
19243c479c | ||
|
|
e868ce8328 | ||
|
|
ffa846c05a | ||
|
|
dde1791476 | ||
|
|
45438a7f06 | ||
|
|
c980e77ea3 | ||
|
|
176d3ddefa | ||
|
|
98d783d448 | ||
|
|
bcd6634d8a | ||
|
|
0b260cef2a | ||
|
|
6a68abbd67 | ||
|
|
9fcf23c802 | ||
|
|
5c2c08e051 | ||
|
|
1f254997e1 | ||
|
|
4f95af0864 | ||
|
|
6ff39be9d2 | ||
|
|
6cf5a47971 | ||
|
|
56da53c700 | ||
|
|
7091e10795 | ||
|
|
34765c5741 | ||
|
|
36c139872a | ||
|
|
1e77cec677 | ||
|
|
e95e3fb2d0 | ||
|
|
39c2234e38 | ||
|
|
f4fff5d9cb | ||
|
|
659785f972 | ||
|
|
85c04f6e3e | ||
|
|
bef117cbe8 | ||
|
|
46dd7cf86e | ||
|
|
9ed5a97267 | ||
|
|
cc2da70db2 | ||
|
|
cedd93e774 | ||
|
|
632e1692eb | ||
|
|
4861592d2a | ||
|
|
22e6d4edf3 | ||
|
|
e9bd7ff72f | ||
|
|
e7228fb489 | ||
|
|
96c03a68f2 | ||
|
|
4f6f248421 | ||
|
|
a8f14c86fd | ||
|
|
36de3d1e25 | ||
|
|
48bc4570e1 | ||
|
|
94b272dbae | ||
|
|
c093edf459 | ||
|
|
0164feffcc | ||
|
|
8cd377b99f | ||
|
|
74282c8ac5 | ||
|
|
d2158e5e44 | ||
|
|
9ea16ad1d1 | ||
|
|
45941adb71 | ||
|
|
c4d662fd2b | ||
|
|
d9ce3cda66 | ||
|
|
6bd7d6b078 | ||
|
|
84c6dd5dfa | ||
|
|
71e7412f15 | ||
|
|
d22c920b35 | ||
|
|
f7a0982ca0 | ||
|
|
bed04150e1 | ||
|
|
ba15de2218 | ||
|
|
e9ec89dc9c | ||
|
|
d09f75658c | ||
|
|
62f92db181 | ||
|
|
27a98f4244 | ||
|
|
f0a3482eda | ||
|
|
5f76843c4a | ||
|
|
c6ea92cff9 | ||
|
|
c253308284 | ||
|
|
9ae9c111e3 | ||
|
|
4894372eee | ||
|
|
7cf040653f | ||
|
|
034bd4dba0 | ||
|
|
af12a2161c | ||
|
|
57fcf6fde3 | ||
|
|
c5757dc5f4 | ||
|
|
6d5d5ceb7b | ||
|
|
2fa8507d69 | ||
|
|
f23003ead3 | ||
|
|
c996f6b436 | ||
|
|
d2ee66a1c4 | ||
|
|
26b0dd5ef5 | ||
|
|
ad4149a259 | ||
|
|
9611c3b478 | ||
|
|
cead88d221 | ||
|
|
c1e1a6bb4f | ||
|
|
6212a5f740 | ||
|
|
b3d9ea3c47 | ||
|
|
cd51989354 | ||
|
|
b705ae5f0c | ||
|
|
13b53537fa | ||
|
|
7d05aa6073 | ||
|
|
85de173086 | ||
|
|
d264d804c8 | ||
|
|
8272da615e | ||
|
|
857b993d51 | ||
|
|
a71edf584e | ||
|
|
461d7fec0e | ||
|
|
5e3da035dd | ||
|
|
ebb52995a5 | ||
|
|
519b82c620 | ||
|
|
84682d07c6 | ||
|
|
960eeb19af | ||
|
|
ab3920f8f1 | ||
|
|
f5f5857897 | ||
|
|
1c400b410e | ||
|
|
cc751aa224 | ||
|
|
c20892ee3e | ||
|
|
32ab53c9e1 | ||
|
|
d0a7d9eb42 | ||
|
|
a1a9602509 | ||
|
|
cf97c89fe0 | ||
|
|
8895bc85ea | ||
|
|
1a9976c6ca | ||
|
|
f47ebf6145 | ||
|
|
0380715311 | ||
|
|
80ad16c7fa | ||
|
|
e56e9035b6 | ||
|
|
73f22d32d2 | ||
|
|
c3bc56eebc | ||
|
|
35cc14815e | ||
|
|
9be91474f6 | ||
|
|
adf949bf08 | ||
|
|
c6bf41b8ba | ||
|
|
bc656c6218 | ||
|
|
f46226d055 | ||
|
|
00d4ee47de | ||
|
|
c5ffc21660 | ||
|
|
d89b1fdc6a | ||
|
|
8324287bd6 | ||
|
|
6be161a546 | ||
|
|
027350e1ba | ||
|
|
a2309e1c2e | ||
|
|
c34dc97bd4 | ||
|
|
7e8749146e | ||
|
|
8680ecd033 | ||
|
|
4e4417c7af | ||
|
|
7909bbbbe9 | ||
|
|
6fd831e688 | ||
|
|
59a4825c70 | ||
|
|
1ba3681457 | ||
|
|
78becffb2e | ||
|
|
e7efd7070b | ||
|
|
ec6471e8c7 | ||
|
|
b01ae2c6d3 | ||
|
|
ef4a260615 | ||
|
|
f6b80630dd | ||
|
|
f43589589d | ||
|
|
06b59cf79b | ||
|
|
a2187205e0 | ||
|
|
52f269a289 | ||
|
|
310ca967a1 | ||
|
|
c4b423cb0f | ||
|
|
8a6c940aaf | ||
|
|
b295e927b7 | ||
|
|
63d24737dd | ||
|
|
60ce02ba28 | ||
|
|
95939ed66c | ||
|
|
7f609a35be | ||
|
|
f7b534f1ee | ||
|
|
cd5f9e2f13 | ||
|
|
e79da72711 | ||
|
|
1ba081959b | ||
|
|
578dc63652 | ||
|
|
fccd683b50 | ||
|
|
f3d3a25856 | ||
|
|
6d70c92795 | ||
|
|
3c525d8e3a | ||
|
|
a6b47c7c43 | ||
|
|
5b52f01f3d | ||
|
|
d13bbd43f3 | ||
|
|
0394d1a24f | ||
|
|
446222e127 | ||
|
|
05d7aa898d | ||
|
|
73f7fc1d51 | ||
|
|
f0262466d4 | ||
|
|
1ecde9bbc1 | ||
|
|
ae5a766092 | ||
|
|
6a807bc002 | ||
|
|
c0384bb0ee | ||
|
|
2906b315b3 | ||
|
|
425fd65bd8 | ||
|
|
7d83362a85 | ||
|
|
0b26894112 | ||
|
|
17f810a720 | ||
|
|
71ef8061f9 | ||
|
|
353b17690f | ||
|
|
6790727260 | ||
|
|
e129f7db85 | ||
|
|
ea942398e3 | ||
|
|
5ad72cae3f | ||
|
|
5f945bc696 | ||
|
|
6f451736ba | ||
|
|
30856f4a4f | ||
|
|
413c71eb0a | ||
|
|
9d1408be20 | ||
|
|
f21f371751 | ||
|
|
2b761279e4 | ||
|
|
d5e8f54214 | ||
|
|
83f83d4eee | ||
|
|
b0f4ab9ba5 | ||
|
|
06dad8f79c | ||
|
|
83ab122ddf | ||
|
|
8a42fe4ae1 | ||
|
|
94c6778b89 | ||
|
|
c0e5973517 | ||
|
|
1e7bbfa7c1 | ||
|
|
dc7245ff6e | ||
|
|
ffaf7b40e9 | ||
|
|
4de3fb1f2a | ||
|
|
99355d993a | ||
|
|
d25f6e813c | ||
|
|
043f8e0523 | ||
|
|
5fcf2a2623 | ||
|
|
ee77fccffd | ||
|
|
f1422adf75 | ||
|
|
189da08885 | ||
|
|
c2b1742582 | ||
|
|
9e63ac6d5b | ||
|
|
4d7ab8b187 | ||
|
|
4de9818bee | ||
|
|
7a2e1fd221 | ||
|
|
d0ca800a23 | ||
|
|
35ffd56ea9 | ||
|
|
84b992d3a1 | ||
|
|
9e46364759 | ||
|
|
0f37c2b59c | ||
|
|
33852ea7e3 | ||
|
|
4fbed1cdac | ||
|
|
42c61ab457 | ||
|
|
8c6b9f9c68 | ||
|
|
abebecac4a | ||
|
|
87efe429da | ||
|
|
35128b0bd4 | ||
|
|
186cb2270f | ||
|
|
deda02f879 | ||
|
|
bcc2478ef7 | ||
|
|
8d54654482 | ||
|
|
08318107c1 | ||
|
|
a5e77c85a6 | ||
|
|
1e8d2aff75 | ||
|
|
bc0a0f9902 | ||
|
|
da82f975e4 | ||
|
|
48af120db8 | ||
|
|
8722eae766 | ||
|
|
53776936ca | ||
|
|
dca465b801 | ||
|
|
43cd115dc7 | ||
|
|
e7ba08e52c | ||
|
|
9df12e6ff2 | ||
|
|
b5c7fb747c | ||
|
|
a40a4afe80 | ||
|
|
739f595f13 | ||
|
|
e07e892969 | ||
|
|
d4a6c58cc8 | ||
|
|
d644431a4e | ||
|
|
33bbb50b43 | ||
|
|
f89d7df305 | ||
|
|
3b02cd0e39 | ||
|
|
52cd50e0a8 | ||
|
|
996a970081 | ||
|
|
6c0b65acd4 | ||
|
|
f4df263dfe | ||
|
|
8c659acc82 | ||
|
|
7aba2429af | ||
|
|
ab48d2c2ff | ||
|
|
0b699d45bf | ||
|
|
54beafa262 | ||
|
|
531d4923eb | ||
|
|
b160a4d1dd | ||
|
|
ca54daf456 | ||
|
|
a22fc550b3 | ||
|
|
0650d93953 | ||
|
|
5633258fa7 | ||
|
|
12278cda58 | ||
|
|
84d1f08fda | ||
|
|
c184292a57 | ||
|
|
4cdfcb9f9d | ||
|
|
343a78917c | ||
|
|
ff7d0fdb9d | ||
|
|
db26b46be0 | ||
|
|
d77a70c360 | ||
|
|
42f4ae65d1 | ||
|
|
88daac31d2 | ||
|
|
ac04c173a8 | ||
|
|
8401494fbc | ||
|
|
97af118cb9 | ||
|
|
091e6026bc | ||
|
|
c798ede7bf | ||
|
|
225851f067 | ||
|
|
9dd65ecf70 | ||
|
|
1a9cc4b6be | ||
|
|
a612f206bf | ||
|
|
e51031c62a | ||
|
|
e30c29ef50 | ||
|
|
91ddcadbcd | ||
|
|
8c145860e5 | ||
|
|
a19dd7687e | ||
|
|
550d6ca083 | ||
|
|
b425411357 | ||
|
|
a1f0cf749d | ||
|
|
22e0d1c74e | ||
|
|
cdc07047aa | ||
|
|
c832c2da28 | ||
|
|
8daa713639 | ||
|
|
e0a2966706 | ||
|
|
354bfa14f9 | ||
|
|
46b91702ba | ||
|
|
de9516e368 | ||
|
|
3924e07e5c | ||
|
|
76bcbb5a7e | ||
|
|
8022381d1c | ||
|
|
feb1233081 | ||
|
|
36eefd0836 | ||
|
|
0e31e59759 | ||
|
|
4a4c1e75da | ||
|
|
b0bfd2292a | ||
|
|
7214b24357 | ||
|
|
24637f496f | ||
|
|
d8ecde5265 | ||
|
|
28840c6209 | ||
|
|
1696213406 | ||
|
|
6f315ac765 | ||
|
|
a485307d92 | ||
|
|
3d3b861ba0 | ||
|
|
4b33ed25d5 | ||
|
|
e264880c7b | ||
|
|
ef8212701f | ||
|
|
492157a502 | ||
|
|
2605bc182e | ||
|
|
fe8dfdd804 | ||
|
|
bd917bc990 | ||
|
|
c5c32f683f | ||
|
|
5506e58c98 | ||
|
|
5af2d49b18 | ||
|
|
0fd35a4925 | ||
|
|
7ed20b1244 | ||
|
|
efa6a78255 | ||
|
|
8b58df3b34 | ||
|
|
0d2a090e1f | ||
|
|
7860d635a9 | ||
|
|
ba91c9fa9b | ||
|
|
b3630e0d5e | ||
|
|
f752285912 | ||
|
|
5a150d9b0e | ||
|
|
f0aa185832 | ||
|
|
9592f058d4 | ||
|
|
f630794e22 | ||
|
|
93636e89c5 | ||
|
|
585002c25c | ||
|
|
412ccc1be1 | ||
|
|
8b1306a36c | ||
|
|
81026e8dca | ||
|
|
dd440c8f9f | ||
|
|
76f3e4b27e | ||
|
|
5f5d3fdb66 | ||
|
|
853c92b87d | ||
|
|
00080f2abc | ||
|
|
55414208a3 | ||
|
|
5091499563 | ||
|
|
944b54d920 | ||
|
|
d023b2b2ff | ||
|
|
b45f9f514b | ||
|
|
239a9383e0 | ||
|
|
2190c0229c | ||
|
|
01ef14dc92 | ||
|
|
7b0784843c | ||
|
|
6fc805369e | ||
|
|
9e29939cd3 | ||
|
|
d750abca22 | ||
|
|
31df2fa131 | ||
|
|
6355a29a7a | ||
|
|
86a2b38340 | ||
|
|
9cb2b58557 | ||
|
|
2b0e2e8d0d | ||
|
|
cf46767196 | ||
|
|
ffc1034b5a | ||
|
|
46bb19de9b | ||
|
|
70bc7a6d01 | ||
|
|
3164505273 | ||
|
|
3d84fcd037 | ||
|
|
578fa32243 | ||
|
|
fc00b7d1cc | ||
|
|
d7351bd3e5 | ||
|
|
e7224c8f05 | ||
|
|
b97622f45b | ||
|
|
0e15f3b703 | ||
|
|
6604c0da89 | ||
|
|
e0b8be20b3 | ||
|
|
46965d8c96 | ||
|
|
66e92f00ee | ||
|
|
4a137b4e8e | ||
|
|
9d5ff28098 | ||
|
|
313b114da5 | ||
|
|
1b6bfc6338 | ||
|
|
49fd89f34a | ||
|
|
a2e862886e | ||
|
|
62f6c7c5a9 | ||
|
|
2294ed1ce1 | ||
|
|
c8a1c6a318 | ||
|
|
600ed66d5b | ||
|
|
512b17555c | ||
|
|
dc7849c9e8 | ||
|
|
6a99a51b91 | ||
|
|
8c7fa022a0 | ||
|
|
cca694a580 | ||
|
|
3a7f95b9b1 | ||
|
|
3a84dc3962 | ||
|
|
5961a96a4c | ||
|
|
a22382505f | ||
|
|
5faef75415 | ||
|
|
fed60907dc | ||
|
|
ce7e360b70 | ||
|
|
0b3def38b8 | ||
|
|
25a15dea8c | ||
|
|
e204971a6c | ||
|
|
d5b3a118bc | ||
|
|
3396cb2887 | ||
|
|
3c5beea218 | ||
|
|
e544384dd5 | ||
|
|
0e90f460f4 | ||
|
|
921efc4d2b | ||
|
|
1b3b4a5906 | ||
|
|
5c8b374352 | ||
|
|
e05cef6886 | ||
|
|
cb39ecacf9 | ||
|
|
e6816f94eb | ||
|
|
8b5dbeab44 | ||
|
|
29c21c3611 | ||
|
|
e05bed8d65 | ||
|
|
1b2210aba0 | ||
|
|
7fb1b1d57b | ||
|
|
a3adf71a1d | ||
|
|
51d81fab5d | ||
|
|
2ed5dc153a | ||
|
|
5f8f156bee | ||
|
|
eb03c90d7a | ||
|
|
fc05471086 | ||
|
|
b9db2dd89f | ||
|
|
de7fe21a4f | ||
|
|
56f9a7c4f9 | ||
|
|
df569a5ae2 | ||
|
|
acb9d04c51 | ||
|
|
09c4708a22 | ||
|
|
b346dfe0a3 | ||
|
|
5f259cb88c | ||
|
|
fb2aff3310 | ||
|
|
3c4c65c28c | ||
|
|
15885e3e8c | ||
|
|
5508c60e85 | ||
|
|
ffc341e4b9 | ||
|
|
41a68f7b25 | ||
|
|
041d3c5312 | ||
|
|
8e8b462bc8 | ||
|
|
efbc76e06f | ||
|
|
67a44d2adc | ||
|
|
92a35b929a | ||
|
|
e5744dd63f | ||
|
|
f2f3d050bd | ||
|
|
0cbecbe3a0 | ||
|
|
51a8b5a058 | ||
|
|
aaf716e54b | ||
|
|
209fcc7946 | ||
|
|
a5cb131806 | ||
|
|
8fbeb5f5d5 | ||
|
|
a92bdfe30d | ||
|
|
7f130ff036 | ||
|
|
b704f72854 | ||
|
|
e21f8a97ac | ||
|
|
a8ff403809 | ||
|
|
22097c0a25 | ||
|
|
92e7d9cf80 | ||
|
|
54d921f275 | ||
|
|
08d7b3dbce | ||
|
|
6949b3c229 | ||
|
|
b0ca38bd29 | ||
|
|
cf173ee9e7 | ||
|
|
aa9908b34a | ||
|
|
640674ef72 | ||
|
|
0c69ab80bb | ||
|
|
662557c2f3 | ||
|
|
346b2c31d2 | ||
|
|
62dbf99557 | ||
|
|
99b140adaa | ||
|
|
387d577d4f | ||
|
|
ab7eee7db9 | ||
|
|
487a45f01b | ||
|
|
60665c6bd8 | ||
|
|
8fc9a3d6d1 | ||
|
|
05a05f7e88 | ||
|
|
0c5bdfd7b7 | ||
|
|
626e93c7e3 | ||
|
|
b588c4c900 | ||
|
|
c52a0d88df | ||
|
|
84838d19d9 | ||
|
|
faf335a181 | ||
|
|
5c25351884 | ||
|
|
520f3cb09a | ||
|
|
e234246618 | ||
|
|
5d1d378f61 | ||
|
|
cef3bb7424 | ||
|
|
ccb03f2763 | ||
|
|
1f6168366b | ||
|
|
cd5bf85245 | ||
|
|
f2778e5d28 | ||
|
|
c2e6065ed7 | ||
|
|
fccba5f7fd | ||
|
|
3f230c5a05 | ||
|
|
dc849c3891 | ||
|
|
2770b58a20 | ||
|
|
37519acfb8 | ||
|
|
ad8c7b3cd2 | ||
|
|
04db8d3208 | ||
|
|
666e3b1e30 | ||
|
|
dc68781c06 | ||
|
|
a7f50d147e | ||
|
|
7e639db5de | ||
|
|
19a97a1706 | ||
|
|
cd66a9ef61 | ||
|
|
f121430a5d | ||
|
|
2f518dacfc | ||
|
|
fded8b6cd3 | ||
|
|
3b6c64dc9d | ||
|
|
d742985640 | ||
|
|
a13dd58989 | ||
|
|
622b3210ae | ||
|
|
90c97ed6aa | ||
|
|
53a4dfbf88 | ||
|
|
f488c57363 | ||
|
|
0ce830ca9d | ||
|
|
30ae418c2c | ||
|
|
3b976d211f | ||
|
|
cca49b5dc2 | ||
|
|
8c2b2070c6 | ||
|
|
24b8ff26db | ||
|
|
f0d93538ae | ||
|
|
02eab65c4e | ||
|
|
d941aa7df3 | ||
|
|
b5026a45f6 | ||
|
|
79fde3ebc9 | ||
|
|
031d648585 | ||
|
|
762b2fe7d6 | ||
|
|
5db377923e | ||
|
|
c3177df739 | ||
|
|
0dc36765f1 | ||
|
|
38e61ebd8d | ||
|
|
529b9739b5 | ||
|
|
a014ca7d8a | ||
|
|
83701f7d0d | ||
|
|
b2500939f3 | ||
|
|
1852b9dbb2 | ||
|
|
069474fc71 | ||
|
|
e7f518264a | ||
|
|
b0b096c3f5 | ||
|
|
bfa9d04d42 | ||
|
|
7dbe58469a | ||
|
|
41b36dabc2 | ||
|
|
4a685557d9 | ||
|
|
e7ef4b6906 | ||
|
|
67502fb9d3 | ||
|
|
960283bdcf | ||
|
|
39f30eab7a | ||
|
|
24b4741aaf | ||
|
|
64f373fb43 | ||
|
|
bc1830d8eb | ||
|
|
f2a2a91682 | ||
|
|
274ac339ad | ||
|
|
1d916286ee | ||
|
|
832d54300a | ||
|
|
ba6d3bbe15 | ||
|
|
c1d0789ac7 | ||
|
|
0573d0083e | ||
|
|
e57d62b682 | ||
|
|
bb6f27b322 | ||
|
|
86f424ad37 | ||
|
|
ad81bbc761 | ||
|
|
07e868e6f6 | ||
|
|
b45700df03 | ||
|
|
93ce2a8e3a | ||
|
|
fbc4d46962 | ||
|
|
d73d138b3f | ||
|
|
14ea21d53d | ||
|
|
2b3791b83e | ||
|
|
e04809f96b | ||
|
|
9203acff9c | ||
|
|
afc1a9f077 | ||
|
|
fc57851113 | ||
|
|
1f1d3f843f | ||
|
|
b389260dec | ||
|
|
1f37a5ff8f | ||
|
|
815d60eca2 | ||
|
|
877a584a26 | ||
|
|
0c60c9ff75 | ||
|
|
6a7a868b71 | ||
|
|
b5a070b228 | ||
|
|
39f13c6e5b | ||
|
|
8c98da09f0 | ||
|
|
ed70a7200c | ||
|
|
ea4410cd16 | ||
|
|
9d9b1cbcd5 | ||
|
|
f17d8f38fb | ||
|
|
0efc9d1cd2 | ||
|
|
4f5d5f1afd | ||
|
|
c4d8d7abf4 | ||
|
|
3fad29a709 | ||
|
|
665f204c1f | ||
|
|
362b651823 | ||
|
|
49b56588b8 | ||
|
|
c7a763ffdc | ||
|
|
5435ddad9f | ||
|
|
0ecd466c4c | ||
|
|
6117e25b97 | ||
|
|
ee10f09bc6 | ||
|
|
58512e302f | ||
|
|
ce564c209b | ||
|
|
7296d49693 | ||
|
|
290fdc4c0f | ||
|
|
1fa5ae695d | ||
|
|
b2ba6d38b8 | ||
|
|
620f1b94bc | ||
|
|
c8fb5d1a9a | ||
|
|
1f763aeb72 | ||
|
|
fcfb019555 | ||
|
|
d4c1acb126 | ||
|
|
862bf43685 | ||
|
|
f83139a9ee | ||
|
|
3d938b3edf | ||
|
|
6c6ae66e36 | ||
|
|
813e423bec | ||
|
|
a9a235fc87 | ||
|
|
6e1c6b4bed | ||
|
|
2214059a63 | ||
|
|
424cfcfa0c | ||
|
|
ce871dfa3e | ||
|
|
48a6eb1f86 | ||
|
|
fb85fb5b76 | ||
|
|
c39c3cfdae | ||
|
|
e2fa7c666a | ||
|
|
f49cc6fb1f | ||
|
|
69bef59473 | ||
|
|
9e931b9eb0 | ||
|
|
b26d9ea1e0 | ||
|
|
ca5607d79e | ||
|
|
903eaed250 | ||
|
|
0859eab2dc | ||
|
|
f0f84722ba | ||
|
|
17b8ba7069 | ||
|
|
eb91152cfa | ||
|
|
08c1b6879e | ||
|
|
0077a8f67c | ||
|
|
a89cc67bd2 | ||
|
|
388415ecc2 | ||
|
|
44c5ba208d | ||
|
|
d3a51857cb | ||
|
|
11a3db5d64 | ||
|
|
f3be8ae608 | ||
|
|
0577c9121c | ||
|
|
058f51e8c1 | ||
|
|
698444caec | ||
|
|
d575df4b19 | ||
|
|
bee98513a2 | ||
|
|
3746df49ee | ||
|
|
d98f67eab9 | ||
|
|
fde415e251 | ||
|
|
5702f39181 | ||
|
|
45658afd89 | ||
|
|
ece4a51b94 | ||
|
|
837d4918f2 | ||
|
|
57ae0f1676 | ||
|
|
48e644e007 | ||
|
|
49a04fa913 | ||
|
|
df943bcf75 | ||
|
|
fdbec176fa | ||
|
|
4b2b4e5482 | ||
|
|
96768d8529 | ||
|
|
2689a08026 | ||
|
|
54be70672e | ||
|
|
480fa50af5 | ||
|
|
b51731d15f | ||
|
|
c09650a136 | ||
|
|
769fcb20d8 | ||
|
|
537c4b3a50 | ||
|
|
a75c17ac5e | ||
|
|
85604e8afa | ||
|
|
9a45c9aa7c | ||
|
|
01450bacc2 | ||
|
|
af0255ee09 | ||
|
|
df25bbb6d2 | ||
|
|
444685bc05 | ||
|
|
115f63c330 | ||
|
|
f9dbc4f7bf | ||
|
|
83263f8dee | ||
|
|
a452ade957 | ||
|
|
1f48af024e | ||
|
|
0a643d7195 | ||
|
|
c3835b9da7 | ||
|
|
bb1a96cf7a | ||
|
|
56360301d7 | ||
|
|
435cccdeae | ||
|
|
b11b472933 | ||
|
|
514f8398e2 | ||
|
|
90935fef25 | ||
|
|
9b5ce1c3a6 | ||
|
|
8ad1639b02 | ||
|
|
6d70332cd6 | ||
|
|
717f6240e3 | ||
|
|
ab782054a1 | ||
|
|
b481f01217 | ||
|
|
d0f884f5b2 | ||
|
|
51e66354b0 | ||
|
|
1efe90f445 | ||
|
|
7b47b7549d | ||
|
|
7ccb77fb57 | ||
|
|
a32fa69823 | ||
|
|
ece4cb03ad | ||
|
|
5c53b6528f | ||
|
|
9b92007eff | ||
|
|
e433809f4d | ||
|
|
d74218004a | ||
|
|
c35276e3df | ||
|
|
064236ed5b | ||
|
|
2a50dcba9d | ||
|
|
f7974b324b | ||
|
|
3bb716b060 | ||
|
|
39e465261f | ||
|
|
784b78b17c | ||
|
|
22c68ed8ef | ||
|
|
c4c670a3b1 | ||
|
|
17a05cc1d4 | ||
|
|
d452c5fabb | ||
|
|
2cf974ef02 | ||
|
|
1f0e789575 | ||
|
|
92e1fd3f28 | ||
|
|
49736a87aa | ||
|
|
d009e39842 | ||
|
|
c2b8901537 | ||
|
|
dd910cb5ec | ||
|
|
4632753f02 | ||
|
|
3fdcd12b4f | ||
|
|
94d1d611c7 | ||
|
|
27b54a0bfa | ||
|
|
1b1ecd0748 | ||
|
|
1e29d2e751 | ||
|
|
a0437bf933 | ||
|
|
73e0292a4b | ||
|
|
c393e74160 | ||
|
|
80c4666198 | ||
|
|
ea7bd1f700 | ||
|
|
b3a55cc85d | ||
|
|
036200350d | ||
|
|
e630c484ff | ||
|
|
713e9658c5 | ||
|
|
782039810e | ||
|
|
b473d8ab9c | ||
|
|
2eb6918fb3 | ||
|
|
29626666a7 | ||
|
|
dc41c9a671 | ||
|
|
83af70bb59 | ||
|
|
b293873640 | ||
|
|
8bb92815cb | ||
|
|
7a8f7199c8 | ||
|
|
6f4ce34840 | ||
|
|
8853552161 | ||
|
|
95d3d17d83 | ||
|
|
3594280b04 | ||
|
|
1e447c6e3e | ||
|
|
c41a288280 | ||
|
|
a0492fe944 | ||
|
|
1fffebd497 | ||
|
|
62c05049a7 | ||
|
|
0fdf377d45 | ||
|
|
6ca8e2644a | ||
|
|
4a3061db6d | ||
|
|
61ac81518a | ||
|
|
acd4b4371d | ||
|
|
1d20291d44 | ||
|
|
c60245ea2b | ||
|
|
9e25480baa | ||
|
|
facd803943 | ||
|
|
05c8a6282d | ||
|
|
3f9ae34203 | ||
|
|
afb85309a2 | ||
|
|
2c5e64d0d5 | ||
|
|
ddb8c432be | ||
|
|
054be314f6 | ||
|
|
e484c5754e | ||
|
|
b36c4f65e5 | ||
|
|
4bdfbcc916 | ||
|
|
519198bb61 | ||
|
|
9a7c342f91 | ||
|
|
761a5ed3dd | ||
|
|
481e753ad4 | ||
|
|
271016f0fa | ||
|
|
4493f895c6 | ||
|
|
5c32ecd8e1 | ||
|
|
0b5244d321 | ||
|
|
b6f9715174 | ||
|
|
2db1f8d2b6 | ||
|
|
4c5957ae40 | ||
|
|
38cd19de15 | ||
|
|
f66ffbdd63 | ||
|
|
8d3f08e529 | ||
|
|
4209c1c406 | ||
|
|
1cd12d0a0c | ||
|
|
7eb18e1931 | ||
|
|
d308e50e1e | ||
|
|
88569cb369 | ||
|
|
235869fc79 | ||
|
|
89166e81fb | ||
|
|
2d109b81cf | ||
|
|
5c02b4dccb | ||
|
|
e7f154b58d | ||
|
|
c0752575c6 | ||
|
|
7eb2c89f39 | ||
|
|
bf4eb07342 | ||
|
|
c856d01b52 | ||
|
|
7a535b2576 | ||
|
|
4b17fd88a3 | ||
|
|
8d1f3f723f | ||
|
|
a543fbbec9 | ||
|
|
37f50db00e | ||
|
|
ccb87f43b7 | ||
|
|
d569f39f53 | ||
|
|
fb3951772f | ||
|
|
2c00bf4040 | ||
|
|
4f8772bd77 | ||
|
|
b4c3046ab5 | ||
|
|
2441b92bc6 | ||
|
|
c39eec32f2 | ||
|
|
7837718d04 | ||
|
|
7069671471 | ||
|
|
d1dbde2890 | ||
|
|
0472471ac9 | ||
|
|
62a0b8da90 | ||
|
|
810530fabd | ||
|
|
707de56612 | ||
|
|
921b0eb229 | ||
|
|
9afb92f0ed | ||
|
|
1ded88e089 | ||
|
|
1f074ff400 | ||
|
|
4764f61b48 | ||
|
|
56424924bb | ||
|
|
4c3831ec74 | ||
|
|
ba564a6aed |
18
.github/issue_template.md
vendored
Normal file
18
.github/issue_template.md
vendored
Normal 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
2
.gitignore
vendored
@@ -26,3 +26,5 @@ _site/**
|
||||
.LCKpom.xml~
|
||||
#coverity
|
||||
/cov-int/
|
||||
/dependency-check-core/nbproject/
|
||||
cov-scan.bat
|
||||
2
.travis.yml
Normal file
2
.travis.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
language: java
|
||||
jdk: oraclejdk7
|
||||
14
Dockerfile
Normal file
14
Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
||||
FROM java:8
|
||||
|
||||
MAINTAINER Timo Pagel <dependencycheckmaintainer@timo-pagel.de>
|
||||
|
||||
RUN wget -O /tmp/current.txt http://jeremylong.github.io/DependencyCheck/current.txt && current=$(cat /tmp/current.txt) && wget https://dl.bintray.com/jeremy-long/owasp/dependency-check-$current-release.zip && unzip dependency-check-$current-release.zip && mv dependency-check /usr/share/
|
||||
|
||||
RUN useradd -ms /bin/bash dockeruser && chown -R dockeruser:dockeruser /usr/share/dependency-check && mkdir /report && chown -R dockeruser:dockeruser /report
|
||||
USER dockeruser
|
||||
|
||||
VOLUME "/src /usr/share/dependency-check/data /report"
|
||||
|
||||
WORKDIR /report
|
||||
|
||||
ENTRYPOINT ["/usr/share/dependency-check/bin/dependency-check.sh", "--scan", "/src"]
|
||||
55
README.md
55
README.md
@@ -1,3 +1,5 @@
|
||||
[](https://travis-ci.org/jeremylong/DependencyCheck) [](https://www.apache.org/licenses/LICENSE-2.0.txt) [](https://scan.coverity.com/projects/dependencycheck)
|
||||
|
||||
Dependency-Check
|
||||
================
|
||||
|
||||
@@ -9,7 +11,7 @@ Current Releases
|
||||
-------------
|
||||
### Jenkins Plugin
|
||||
|
||||
For instructions on the use of the Jenkins plugin please see the [Jenkins dependency-check page](http://wiki.jenkins-ci.org/x/CwDgAQ).
|
||||
For instructions on the use of the Jenkins plugin please see the [OWASP Dependency-Check Plugin page](https://wiki.jenkins-ci.org/display/JENKINS/OWASP+Dependency-Check+Plugin).
|
||||
|
||||
### Command Line
|
||||
|
||||
@@ -21,23 +23,23 @@ The latest CLI can be downloaded from bintray's
|
||||
On *nix
|
||||
```
|
||||
$ ./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
|
||||
```
|
||||
> 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)
|
||||
```
|
||||
$ brew update && brew install dependency-check
|
||||
$ 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
|
||||
|
||||
More detailed instructions can be found on the [dependency-check-maven github pages](http://jeremylong.github.io/DependencyCheck/dependency-check-maven/usage.html).
|
||||
More detailed instructions can be found on the [dependency-check-maven github pages](http://jeremylong.github.io/DependencyCheck/dependency-check-maven).
|
||||
The plugin can be configured using the following:
|
||||
|
||||
```xml
|
||||
@@ -66,7 +68,7 @@ The plugin can be configured using the following:
|
||||
|
||||
### Ant Task
|
||||
|
||||
For instructions on the use of the Ant Task, please see the [dependency-check-ant github page](http://jeremylong.github.io/DependencyCheck/dependency-check-ant/installation.html).
|
||||
For instructions on the use of the Ant Task, please see the [dependency-check-ant github page](http://jeremylong.github.io/DependencyCheck/dependency-check-ant).
|
||||
|
||||
Development Usage
|
||||
-------------
|
||||
@@ -84,17 +86,48 @@ On *nix
|
||||
```
|
||||
$ mvn install
|
||||
$ ./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
|
||||
```
|
||||
> mvn install
|
||||
> 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.
|
||||
|
||||
### Docker
|
||||
|
||||
In the following example it is assumed that the source to be checked is in the actual directory. A persistent data directory and a persistent report directory is used so that the container can be destroyed after running it to make sure that you use the newst version, always.
|
||||
```
|
||||
# After the first run, feel free to change the owner of the directories to the owner of the creted files and the permissions to 744
|
||||
DATA_DIRECTORY=$HOME/OWASP-Dependency-Check/data
|
||||
REPORT_DIRECTORY=/$HOME/OWASP-Dependency-Check/reports
|
||||
|
||||
if [ ! -d $DATA_DIRECTORY ]; then
|
||||
echo "Initially creating persistent directories"
|
||||
mkdir -p $DATA_DIRECTORY
|
||||
chmod -R 777 $DATA_DIRECTORY
|
||||
|
||||
mkdir -p $REPORT_DIRECTORY
|
||||
chmod -R 777 $REPORT_DIRECTORY
|
||||
fi
|
||||
|
||||
docker pull owasp/dependency-check # Make sure it is the actual version
|
||||
|
||||
docker run --rm \
|
||||
--volume $(pwd):/src \
|
||||
--volume $DATA_DIRECTORY:/usr/share/dependency-check/data \
|
||||
--volume $REPORT_DIRECTORY:/report \
|
||||
--name dependency-check \
|
||||
dc \
|
||||
--suppression "/src/security/dependency-check-suppression.xml"\
|
||||
--format "ALL" \
|
||||
--project "My OWASP Dependency Check Projekt" \
|
||||
```
|
||||
|
||||
|
||||
Mailing List
|
||||
------------
|
||||
|
||||
@@ -107,9 +140,9 @@ Archive: [google group](https://groups.google.com/forum/#!forum/dependency-check
|
||||
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://github.com/jeremylong/DependencyCheck/dependency-check-cli/blob/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.
|
||||
|
||||
Dependency-Check makes use of several other open source libraries. Please see the [NOTICE.txt] [notices] file for more information.
|
||||
|
||||
@@ -117,4 +150,4 @@ Dependency-Check makes use of several other open source libraries. Please see th
|
||||
[wiki]: https://github.com/jeremylong/DependencyCheck/wiki
|
||||
[subscribe]: mailto:dependency-check+subscribe@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
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
-----------------------------
|
||||
---begin dependency-check----
|
||||
-----------------------------
|
||||
dependency-check
|
||||
OWASP dependency-check
|
||||
|
||||
Copyright (c) 2012-2013 Jeremy Long. All Rights Reserved.
|
||||
Copyright (c) 2012-2015 Jeremy Long. All Rights Reserved.
|
||||
|
||||
The licenses for the software listed below can be found in the META-INF/licenses/[dependency name].
|
||||
|
||||
@@ -19,11 +16,3 @@ An original copy of the license agreement can be found at: http://www.h2database
|
||||
This product includes data from the Common Weakness Enumeration (CWE): http://cwe.mitre.org/
|
||||
|
||||
This product downloads and utilizes data from the National Vulnerability Database hosted by NIST: http://nvd.nist.gov/download.cfm
|
||||
|
||||
-----------------------------
|
||||
---end dependency-check------
|
||||
-----------------------------
|
||||
|
||||
Notices below are from dependent libraries and have been included via maven-shade-plugin.
|
||||
|
||||
-----------------------------
|
||||
@@ -6,7 +6,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
|
||||
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-ant/installation.html).
|
||||
Documentation and links to production binary releases can be found on the [github pages](http://jeremylong.github.io/DependencyCheck/dependency-check-ant/index.html).
|
||||
|
||||
Mailing List
|
||||
------------
|
||||
@@ -20,6 +20,6 @@ Copyright & License
|
||||
|
||||
Dependency-Check is Copyright (c) 2012-2014 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://github.com/jeremylong/DependencyCheck/dependency-check-cli/blob/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.
|
||||
|
||||
Dependency-Check-Ant makes use of other open source libraries. Please see the [NOTICE.txt](https://github.com/jeremylong/DependencyCheck/dependency-check-ant/blob/master/NOTICES.txt) file for more information.
|
||||
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.
|
||||
|
||||
@@ -20,7 +20,7 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved.
|
||||
<parent>
|
||||
<groupId>org.owasp</groupId>
|
||||
<artifactId>dependency-check-parent</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.4.4</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dependency-check-ant</artifactId>
|
||||
@@ -190,38 +190,36 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved.
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<addClasspath>true</addClasspath>
|
||||
<classpathPrefix>lib/</classpathPrefix>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.3</version>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>META-INF/NOTICE.txt</resource>
|
||||
</transformer>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer">
|
||||
<resource>META-INF/NOTICE</resource>
|
||||
</transformer>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer">
|
||||
<resource>META-INF/LICENSE</resource>
|
||||
</transformer>
|
||||
</transformers>
|
||||
<attach>false</attach> <!-- don't install/deploy this archive -->
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>create-distribution</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>src/main/assembly/release.xml</descriptor>
|
||||
</descriptors>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
@@ -229,9 +227,6 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved.
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<!--instrumentation>
|
||||
<ignoreTrivial>true</ignoreTrivial>
|
||||
</instrumentation-->
|
||||
<check>
|
||||
<branchRate>85</branchRate>
|
||||
<lineRate>85</lineRate>
|
||||
@@ -261,6 +256,7 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved.
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<argLine>-Dfile.encoding=UTF-8</argLine>
|
||||
<systemProperties>
|
||||
<property>
|
||||
<name>data.directory</name>
|
||||
@@ -273,96 +269,6 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved.
|
||||
</build>
|
||||
<reporting>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||
<version>${reporting.project-info-reports-plugin.version}</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<reports>
|
||||
<report>summary</report>
|
||||
<report>license</report>
|
||||
<report>help</report>
|
||||
</reports>
|
||||
</reportSet>
|
||||
</reportSets>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>${reporting.javadoc-plugin.version}</version>
|
||||
<configuration>
|
||||
<failOnError>false</failOnError>
|
||||
<bottom>Copyright© 2012-15 Jeremy Long. All Rights Reserved.</bottom>
|
||||
</configuration>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<id>default</id>
|
||||
<reports>
|
||||
<report>javadoc</report>
|
||||
</reports>
|
||||
</reportSet>
|
||||
</reportSets>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>versions-maven-plugin</artifactId>
|
||||
<version>${reporting.versions-plugin.version}</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<reports>
|
||||
<report>dependency-updates-report</report>
|
||||
<report>plugin-updates-report</report>
|
||||
</reports>
|
||||
</reportSet>
|
||||
</reportSets>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jxr-plugin</artifactId>
|
||||
<version>${reporting.jxr-plugin.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<version>${reporting.cobertura-plugin.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-report-plugin</artifactId>
|
||||
<version>${reporting.surefire-report-plugin.version}</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<reports>
|
||||
<report>report-only</report>
|
||||
</reports>
|
||||
</reportSet>
|
||||
</reportSets>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>taglist-maven-plugin</artifactId>
|
||||
<version>${reporting.taglist-plugin.version}</version>
|
||||
<configuration>
|
||||
<tagListOptions>
|
||||
<tagClasses>
|
||||
<tagClass>
|
||||
<displayName>Todo Work</displayName>
|
||||
<tags>
|
||||
<tag>
|
||||
<matchString>todo</matchString>
|
||||
<matchType>ignoreCase</matchType>
|
||||
</tag>
|
||||
<tag>
|
||||
<matchString>FIXME</matchString>
|
||||
<matchType>exact</matchType>
|
||||
</tag>
|
||||
</tags>
|
||||
</tagClass>
|
||||
</tagClasses>
|
||||
</tagListOptions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
@@ -395,11 +301,6 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved.
|
||||
</rulesets>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<version>${reporting.findbugs-plugin.version}</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</reporting>
|
||||
<dependencies>
|
||||
@@ -423,6 +324,7 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved.
|
||||
<dependency>
|
||||
<groupId>org.apache.ant</groupId>
|
||||
<artifactId>ant</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.ant</groupId>
|
||||
|
||||
@@ -12,18 +12,25 @@
|
||||
<format>zip</format>
|
||||
</formats>
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
<fileSets>
|
||||
<!--fileSets>
|
||||
<fileSet>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<outputDirectory>dependency-check</outputDirectory>
|
||||
<directory>${project.build.directory}</directory>
|
||||
<includes>
|
||||
<include>dependency-check*.jar</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</fileSets-->
|
||||
<files>
|
||||
<file>
|
||||
<source>${project.build.directory}/${project.artifactId}-${project.version}.jar</source>
|
||||
<outputDirectory>dependency-check-ant</outputDirectory>
|
||||
<destName>dependency-check-ant.jar</destName>
|
||||
</file>
|
||||
</files>
|
||||
<dependencySets>
|
||||
<dependencySet>
|
||||
<outputDirectory>/lib</outputDirectory>
|
||||
<outputDirectory>dependency-check-ant/lib</outputDirectory>
|
||||
<scope>runtime</scope>
|
||||
</dependencySet>
|
||||
</dependencySets>
|
||||
|
||||
@@ -24,16 +24,21 @@ import org.slf4j.helpers.MarkerIgnoringBase;
|
||||
import org.slf4j.helpers.MessageFormatter;
|
||||
|
||||
/**
|
||||
* An instance of {@link org.slf4j.Logger} which simply calls the log method on the delegate Ant task
|
||||
* An instance of {@link org.slf4j.Logger} which simply calls the log method on
|
||||
* the delegate Ant task.
|
||||
*
|
||||
* @author colezlaw
|
||||
*/
|
||||
public class AntLoggerAdapter extends MarkerIgnoringBase {
|
||||
|
||||
/**
|
||||
* serialization UID.
|
||||
*/
|
||||
private static final long serialVersionUID = -1337;
|
||||
/**
|
||||
* A reference to the Ant task used for logging.
|
||||
*/
|
||||
private Task task;
|
||||
private transient Task task;
|
||||
|
||||
/**
|
||||
* Constructs an Ant Logger Adapter.
|
||||
@@ -63,7 +68,9 @@ public class AntLoggerAdapter extends MarkerIgnoringBase {
|
||||
|
||||
@Override
|
||||
public void trace(String msg) {
|
||||
task.log(msg, Project.MSG_VERBOSE);
|
||||
if (task != null) {
|
||||
task.log(msg, Project.MSG_VERBOSE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* This file is part of dependency-check-ant.
|
||||
*
|
||||
* 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) 2015 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.taskdefs;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.Project;
|
||||
import org.apache.tools.ant.Task;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.slf4j.impl.StaticLoggerBinder;
|
||||
|
||||
/**
|
||||
* An Ant task definition to execute dependency-check during an Ant build.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
public class Purge extends Task {
|
||||
|
||||
/**
|
||||
* The properties file location.
|
||||
*/
|
||||
private static final String PROPERTIES_FILE = "task.properties";
|
||||
|
||||
/**
|
||||
* Construct a new DependencyCheckTask.
|
||||
*/
|
||||
public Purge() {
|
||||
super();
|
||||
// Call this before Dependency Check Core starts logging anything - this way, all SLF4J messages from
|
||||
// core end up coming through this tasks logger
|
||||
StaticLoggerBinder.getSingleton().setTask(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* The location of the data directory that contains
|
||||
*/
|
||||
private String dataDirectory = null;
|
||||
|
||||
/**
|
||||
* Get the value of dataDirectory.
|
||||
*
|
||||
* @return the value of dataDirectory
|
||||
*/
|
||||
public String getDataDirectory() {
|
||||
return dataDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of dataDirectory.
|
||||
*
|
||||
* @param dataDirectory new value of dataDirectory
|
||||
*/
|
||||
public void setDataDirectory(String 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
|
||||
public void execute() throws BuildException {
|
||||
populateSettings();
|
||||
File db;
|
||||
try {
|
||||
db = new File(Settings.getDataDirectory(), "dc.h2.db");
|
||||
if (db.exists()) {
|
||||
if (db.delete()) {
|
||||
log("Database file purged; local copy of the NVD has been removed", Project.MSG_INFO);
|
||||
} else {
|
||||
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 {
|
||||
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) {
|
||||
final String msg = "Unable to delete the database";
|
||||
if (this.failOnError) {
|
||||
throw new BuildException(msg);
|
||||
}
|
||||
log(msg, Project.MSG_ERR);
|
||||
} finally {
|
||||
Settings.cleanup(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the properties supplied and updates the dependency-check settings.
|
||||
* 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() throws BuildException {
|
||||
Settings.initialize();
|
||||
InputStream taskProperties = null;
|
||||
try {
|
||||
taskProperties = this.getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILE);
|
||||
Settings.mergeProperties(taskProperties);
|
||||
} catch (IOException ex) {
|
||||
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 {
|
||||
if (taskProperties != null) {
|
||||
try {
|
||||
taskProperties.close();
|
||||
} catch (IOException ex) {
|
||||
log("", ex, Project.MSG_DEBUG);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dataDirectory != null) {
|
||||
Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDirectory);
|
||||
} else {
|
||||
final File jarPath = new File(Purge.class.getProtectionDomain().getCodeSource().getLocation().getPath());
|
||||
final File base = jarPath.getParentFile();
|
||||
final String sub = Settings.getString(Settings.KEYS.DATA_DIRECTORY);
|
||||
final File dataDir = new File(base, sub);
|
||||
Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,453 @@
|
||||
/*
|
||||
* This file is part of dependency-check-ant.
|
||||
*
|
||||
* 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) 2015 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.taskdefs;
|
||||
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.Project;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
|
||||
import org.owasp.dependencycheck.data.update.exception.UpdateException;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.slf4j.impl.StaticLoggerBinder;
|
||||
|
||||
/**
|
||||
* An Ant task definition to execute dependency-check update. This will download
|
||||
* the latest data from the National Vulnerability Database (NVD) and store a
|
||||
* copy in the local database.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
public class Update extends Purge {
|
||||
|
||||
/**
|
||||
* Construct a new UpdateTask.
|
||||
*/
|
||||
public Update() {
|
||||
super();
|
||||
// Call this before Dependency Check Core starts logging anything - this way, all SLF4J messages from
|
||||
// core end up coming through this tasks logger
|
||||
StaticLoggerBinder.getSingleton().setTask(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Proxy Server.
|
||||
*/
|
||||
private String proxyServer;
|
||||
|
||||
/**
|
||||
* Get the value of proxyServer.
|
||||
*
|
||||
* @return the value of proxyServer
|
||||
*/
|
||||
public String getProxyServer() {
|
||||
return proxyServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of proxyServer.
|
||||
*
|
||||
* @param server new value of proxyServer
|
||||
*/
|
||||
public void setProxyServer(String server) {
|
||||
this.proxyServer = server;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Proxy Port.
|
||||
*/
|
||||
private String proxyPort;
|
||||
|
||||
/**
|
||||
* Get the value of proxyPort.
|
||||
*
|
||||
* @return the value of proxyPort
|
||||
*/
|
||||
public String getProxyPort() {
|
||||
return proxyPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of proxyPort.
|
||||
*
|
||||
* @param proxyPort new value of proxyPort
|
||||
*/
|
||||
public void setProxyPort(String proxyPort) {
|
||||
this.proxyPort = proxyPort;
|
||||
}
|
||||
/**
|
||||
* The Proxy username.
|
||||
*/
|
||||
private String proxyUsername;
|
||||
|
||||
/**
|
||||
* Get the value of proxyUsername.
|
||||
*
|
||||
* @return the value of proxyUsername
|
||||
*/
|
||||
public String getProxyUsername() {
|
||||
return proxyUsername;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of proxyUsername.
|
||||
*
|
||||
* @param proxyUsername new value of proxyUsername
|
||||
*/
|
||||
public void setProxyUsername(String proxyUsername) {
|
||||
this.proxyUsername = proxyUsername;
|
||||
}
|
||||
/**
|
||||
* The Proxy password.
|
||||
*/
|
||||
private String proxyPassword;
|
||||
|
||||
/**
|
||||
* Get the value of proxyPassword.
|
||||
*
|
||||
* @return the value of proxyPassword
|
||||
*/
|
||||
public String getProxyPassword() {
|
||||
return proxyPassword;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of proxyPassword.
|
||||
*
|
||||
* @param proxyPassword new value of proxyPassword
|
||||
*/
|
||||
public void setProxyPassword(String proxyPassword) {
|
||||
this.proxyPassword = proxyPassword;
|
||||
}
|
||||
/**
|
||||
* The Connection Timeout.
|
||||
*/
|
||||
private String connectionTimeout;
|
||||
|
||||
/**
|
||||
* Get the value of connectionTimeout.
|
||||
*
|
||||
* @return the value of connectionTimeout
|
||||
*/
|
||||
public String getConnectionTimeout() {
|
||||
return connectionTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of connectionTimeout.
|
||||
*
|
||||
* @param connectionTimeout new value of connectionTimeout
|
||||
*/
|
||||
public void setConnectionTimeout(String connectionTimeout) {
|
||||
this.connectionTimeout = connectionTimeout;
|
||||
}
|
||||
/**
|
||||
* The database driver name; such as org.h2.Driver.
|
||||
*/
|
||||
private String databaseDriverName;
|
||||
|
||||
/**
|
||||
* Get the value of databaseDriverName.
|
||||
*
|
||||
* @return the value of databaseDriverName
|
||||
*/
|
||||
public String getDatabaseDriverName() {
|
||||
return databaseDriverName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of databaseDriverName.
|
||||
*
|
||||
* @param databaseDriverName new value of databaseDriverName
|
||||
*/
|
||||
public void setDatabaseDriverName(String databaseDriverName) {
|
||||
this.databaseDriverName = databaseDriverName;
|
||||
}
|
||||
|
||||
/**
|
||||
* The path to the database driver JAR file if it is not on the class path.
|
||||
*/
|
||||
private String databaseDriverPath;
|
||||
|
||||
/**
|
||||
* Get the value of databaseDriverPath.
|
||||
*
|
||||
* @return the value of databaseDriverPath
|
||||
*/
|
||||
public String getDatabaseDriverPath() {
|
||||
return databaseDriverPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of databaseDriverPath.
|
||||
*
|
||||
* @param databaseDriverPath new value of databaseDriverPath
|
||||
*/
|
||||
public void setDatabaseDriverPath(String databaseDriverPath) {
|
||||
this.databaseDriverPath = databaseDriverPath;
|
||||
}
|
||||
/**
|
||||
* The database connection string.
|
||||
*/
|
||||
private String connectionString;
|
||||
|
||||
/**
|
||||
* Get the value of connectionString.
|
||||
*
|
||||
* @return the value of connectionString
|
||||
*/
|
||||
public String getConnectionString() {
|
||||
return connectionString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of connectionString.
|
||||
*
|
||||
* @param connectionString new value of connectionString
|
||||
*/
|
||||
public void setConnectionString(String connectionString) {
|
||||
this.connectionString = connectionString;
|
||||
}
|
||||
/**
|
||||
* The user name for connecting to the database.
|
||||
*/
|
||||
private String databaseUser;
|
||||
|
||||
/**
|
||||
* Get the value of databaseUser.
|
||||
*
|
||||
* @return the value of databaseUser
|
||||
*/
|
||||
public String getDatabaseUser() {
|
||||
return databaseUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of databaseUser.
|
||||
*
|
||||
* @param databaseUser new value of databaseUser
|
||||
*/
|
||||
public void setDatabaseUser(String databaseUser) {
|
||||
this.databaseUser = databaseUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* The password to use when connecting to the database.
|
||||
*/
|
||||
private String databasePassword;
|
||||
|
||||
/**
|
||||
* Get the value of databasePassword.
|
||||
*
|
||||
* @return the value of databasePassword
|
||||
*/
|
||||
public String getDatabasePassword() {
|
||||
return databasePassword;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of databasePassword.
|
||||
*
|
||||
* @param databasePassword new value of databasePassword
|
||||
*/
|
||||
public void setDatabasePassword(String databasePassword) {
|
||||
this.databasePassword = databasePassword;
|
||||
}
|
||||
|
||||
/**
|
||||
* The url for the modified NVD CVE (1.2 schema).
|
||||
*/
|
||||
private String cveUrl12Modified;
|
||||
|
||||
/**
|
||||
* Get the value of cveUrl12Modified.
|
||||
*
|
||||
* @return the value of cveUrl12Modified
|
||||
*/
|
||||
public String getCveUrl12Modified() {
|
||||
return cveUrl12Modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of cveUrl12Modified.
|
||||
*
|
||||
* @param cveUrl12Modified new value of cveUrl12Modified
|
||||
*/
|
||||
public void setCveUrl12Modified(String cveUrl12Modified) {
|
||||
this.cveUrl12Modified = cveUrl12Modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* The url for the modified NVD CVE (2.0 schema).
|
||||
*/
|
||||
private String cveUrl20Modified;
|
||||
|
||||
/**
|
||||
* Get the value of cveUrl20Modified.
|
||||
*
|
||||
* @return the value of cveUrl20Modified
|
||||
*/
|
||||
public String getCveUrl20Modified() {
|
||||
return cveUrl20Modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of cveUrl20Modified.
|
||||
*
|
||||
* @param cveUrl20Modified new value of cveUrl20Modified
|
||||
*/
|
||||
public void setCveUrl20Modified(String cveUrl20Modified) {
|
||||
this.cveUrl20Modified = cveUrl20Modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base Data Mirror URL for CVE 1.2.
|
||||
*/
|
||||
private String cveUrl12Base;
|
||||
|
||||
/**
|
||||
* Get the value of cveUrl12Base.
|
||||
*
|
||||
* @return the value of cveUrl12Base
|
||||
*/
|
||||
public String getCveUrl12Base() {
|
||||
return cveUrl12Base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of cveUrl12Base.
|
||||
*
|
||||
* @param cveUrl12Base new value of cveUrl12Base
|
||||
*/
|
||||
public void setCveUrl12Base(String cveUrl12Base) {
|
||||
this.cveUrl12Base = cveUrl12Base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data Mirror URL for CVE 2.0.
|
||||
*/
|
||||
private String cveUrl20Base;
|
||||
|
||||
/**
|
||||
* Get the value of cveUrl20Base.
|
||||
*
|
||||
* @return the value of cveUrl20Base
|
||||
*/
|
||||
public String getCveUrl20Base() {
|
||||
return cveUrl20Base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of cveUrl20Base.
|
||||
*
|
||||
* @param cveUrl20Base new value of cveUrl20Base
|
||||
*/
|
||||
public void setCveUrl20Base(String cveUrl20Base) {
|
||||
this.cveUrl20Base = cveUrl20Base;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of hours to wait before re-checking for updates.
|
||||
*/
|
||||
private Integer cveValidForHours;
|
||||
|
||||
/**
|
||||
* Get the value of cveValidForHours.
|
||||
*
|
||||
* @return the value of cveValidForHours
|
||||
*/
|
||||
public Integer getCveValidForHours() {
|
||||
return cveValidForHours;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of cveValidForHours.
|
||||
*
|
||||
* @param cveValidForHours new value of cveValidForHours
|
||||
*/
|
||||
public void setCveValidForHours(Integer cveValidForHours) {
|
||||
this.cveValidForHours = cveValidForHours;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the update by initializing the settings, downloads the NVD XML
|
||||
* 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.
|
||||
*/
|
||||
@Override
|
||||
public void execute() throws BuildException {
|
||||
populateSettings();
|
||||
Engine engine = null;
|
||||
try {
|
||||
engine = new Engine(Update.class.getClassLoader());
|
||||
try {
|
||||
engine.doUpdates();
|
||||
} catch (UpdateException ex) {
|
||||
if (this.isFailOnError()) {
|
||||
throw new BuildException(ex);
|
||||
}
|
||||
log(ex.getMessage(), Project.MSG_ERR);
|
||||
}
|
||||
} catch (DatabaseException 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 {
|
||||
Settings.cleanup(true);
|
||||
if (engine != null) {
|
||||
engine.cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the properties supplied and updates the dependency-check settings.
|
||||
* 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.
|
||||
*/
|
||||
@Override
|
||||
protected void populateSettings() throws BuildException {
|
||||
super.populateSettings();
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_SERVER, proxyServer);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PORT, proxyPort);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_USERNAME, proxyUsername);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PASSWORD, proxyPassword);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_NAME, databaseDriverName);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_PATH, databaseDriverPath);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.DB_CONNECTION_STRING, connectionString);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.DB_USER, databaseUser);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.DB_PASSWORD, databasePassword);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.CVE_MODIFIED_12_URL, cveUrl12Modified);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.CVE_MODIFIED_20_URL, cveUrl20Modified);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.CVE_SCHEMA_1_2, cveUrl12Base);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.CVE_SCHEMA_2_0, cveUrl20Base);
|
||||
if (cveValidForHours != null) {
|
||||
if (cveValidForHours >= 0) {
|
||||
Settings.setInt(Settings.KEYS.CVE_CHECK_VALID_FOR_HOURS, cveValidForHours);
|
||||
} else {
|
||||
throw new BuildException("Invalid setting: `cpeValidForHours` must be 0 or greater");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,16 +23,18 @@ import org.slf4j.ILoggerFactory;
|
||||
import org.slf4j.spi.LoggerFactoryBinder;
|
||||
|
||||
/**
|
||||
* The binding of {@link LoggerFactory} class with an actual instance of {@link ILoggerFactory} is performed using information
|
||||
* returned by this class.
|
||||
* The binding of org.slf4j.LoggerFactory class with an actual instance of
|
||||
* org.slf4j.ILoggerFactory is performed using information returned by this
|
||||
* class.
|
||||
*
|
||||
* @author colezlaw
|
||||
*/
|
||||
//CSOFF: FinalClass
|
||||
public class StaticLoggerBinder implements LoggerFactoryBinder {
|
||||
//CSON: FinalClass
|
||||
|
||||
/**
|
||||
* The unique instance of this class
|
||||
*
|
||||
*/
|
||||
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;
|
||||
|
||||
@@ -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
|
||||
* with each release.
|
||||
* Declare the version of the SLF4J API this implementation is compiled
|
||||
* 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
|
||||
//CSOFF: StaticVariableName
|
||||
//CSOFF: VisibilityModifier
|
||||
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();
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
dependency-check=org.owasp.dependencycheck.taskdefs.Check
|
||||
dependency-check-purge=org.owasp.dependencycheck.taskdefs.Purge
|
||||
dependency-check-update=org.owasp.dependencycheck.taskdefs.Update
|
||||
@@ -1,2 +1,2 @@
|
||||
# the path to the data directory
|
||||
data.directory=dependency-check-data
|
||||
data.directory=data/3.0
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# define custom tasks here
|
||||
|
||||
dependencycheck=org.owasp.dependencycheck.taskdefs.DependencyCheckTask
|
||||
20
dependency-check-ant/src/site/markdown/config-purge.md
Normal file
20
dependency-check-ant/src/site/markdown/config-purge.md
Normal file
@@ -0,0 +1,20 @@
|
||||
Configuration
|
||||
====================
|
||||
The dependency-check-purge task deletes the local copy of the NVD. This task
|
||||
should rarely be used, if ever. This is included as a convenience method in
|
||||
the rare circumstance that the local H2 database becomes corrupt.
|
||||
|
||||
```xml
|
||||
<target name="dependency-check-purge" description="Dependency-Check purge">
|
||||
<dependency-check-purge />
|
||||
</target>
|
||||
```
|
||||
|
||||
Configuration: dependency-check-purge Task
|
||||
--------------------
|
||||
The following properties can be set on the dependency-check-purge task.
|
||||
|
||||
Property | Description | Default Value
|
||||
----------------------|------------------------------------------------------------------------|------------------
|
||||
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
|
||||
45
dependency-check-ant/src/site/markdown/config-update.md
Normal file
45
dependency-check-ant/src/site/markdown/config-update.md
Normal file
@@ -0,0 +1,45 @@
|
||||
Configuration
|
||||
====================
|
||||
The dependency-check-update task downloads and updates the local copy of the NVD.
|
||||
There are several reasons that one may want to use this task; primarily, creating
|
||||
an update that will be run only once a day or once every few days (but not greater
|
||||
then 7 days) and then use the `autoUpdate="false"` setting on individual
|
||||
dependency-check scans. See [Internet Access Required](https://jeremylong.github.io/DependencyCheck/data/index.html)
|
||||
for more information on why this task would be used.
|
||||
|
||||
```xml
|
||||
<target name="dependency-check-update" description="Dependency-Check Update">
|
||||
<dependency-check-update />
|
||||
</target>
|
||||
```
|
||||
|
||||
Configuration: dependency-check-update Task
|
||||
--------------------
|
||||
The following properties can be set on the dependency-check task.
|
||||
|
||||
Property | Description | Default Value
|
||||
----------------------|------------------------------------|------------------
|
||||
proxyServer | The Proxy Server. |
|
||||
proxyPort | The Proxy Port. |
|
||||
proxyUsername | Defines the proxy user name. |
|
||||
proxyPassword | Defines the proxy password. |
|
||||
connectionTimeout | The URL Connection Timeout. |
|
||||
failOnError | Whether the build should fail if there is an error executing the update | true
|
||||
|
||||
Advanced Configuration
|
||||
====================
|
||||
The following properties can be configured in the plugin. However, they are less frequently changed. One exception
|
||||
may be the cvedUrl properties, which can be used to host a mirror of the NVD within an enterprise environment.
|
||||
|
||||
Property | Description | Default Value
|
||||
---------------------|-------------------------------------------------------------------------------------------------------|------------------
|
||||
cveUrl12Modified | URL for the modified CVE 1.2. | https://nvd.nist.gov/download/nvdcve-Modified.xml.gz
|
||||
cveUrl20Modified | URL for the modified CVE 2.0. | https://nvd.nist.gov/feeds/xml/cve/nvdcve-2.0-Modified.xml.gz
|
||||
cveUrl12Base | Base URL for each year's CVE 1.2, the %d will be replaced with the year. | https://nvd.nist.gov/download/nvdcve-%d.xml.gz
|
||||
cveUrl20Base | 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
|
||||
dataDirectory | Data directory that is used to store the local copy of the NVD. This should generally not be changed. | data
|
||||
databaseDriverName | The name of the database driver. Example: org.h2.Driver. |
|
||||
databaseDriverPath | The path to the database driver JAR file; only used if the driver is not in the class path. |
|
||||
connectionString | The connection string used to connect to the database. |
|
||||
databaseUser | The username used when connecting to the database. |
|
||||
databasePassword | The password used when connecting to the database. |
|
||||
@@ -1,5 +1,11 @@
|
||||
Configuration
|
||||
====================
|
||||
Once dependency-check-ant has been [installed](index.html) the defined tasks can be used.
|
||||
|
||||
* dependency-check - the primary task used to check the project dependencies. Configuration options are below.
|
||||
* dependency-check-purge - deletes the local copy of the NVD; this should rarely be used (if ever). See the [purge configuration](config-purge.html) for more information.
|
||||
* dependency-check-update - downloads and updates the local copy of the NVD. See the [update configuration](config-update.html) for more information.
|
||||
|
||||
To configure the dependency-check task you can add it to a target and include a
|
||||
file based [resource collection](http://ant.apache.org/manual/Types/resources.html#collection)
|
||||
such as a [FileSet](http://ant.apache.org/manual/Types/fileset.html), [DirSet](http://ant.apache.org/manual/Types/dirset.html),
|
||||
@@ -8,7 +14,7 @@ the project's dependencies.
|
||||
|
||||
```xml
|
||||
<target name="dependency-check" description="Dependency-Check Analysis">
|
||||
<dependency-check applicationname="Hello World"
|
||||
<dependency-check projectname="Hello World"
|
||||
reportoutputdirectory="${basedir}"
|
||||
reportformat="ALL">
|
||||
|
||||
@@ -19,24 +25,27 @@ the project's dependencies.
|
||||
</target>
|
||||
```
|
||||
|
||||
Configuration
|
||||
====================
|
||||
The following properties can be set on the dependency-check-maven plugin.
|
||||
Configuration: dependency-check Task
|
||||
--------------------
|
||||
The following properties can be set on the dependency-check task.
|
||||
|
||||
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
|
||||
updateOnly | If set to true only the update phase of dependency-check will be executed; no scan will be executed and no report will be generated. | false
|
||||
externalReport | When using as a Site plugin this parameter sets whether or not the external report format should be used. | false
|
||||
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'
|
||||
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
|
||||
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
|
||||
suppressionFile | The file path to the XML suppression file \- used to suppress [false positives](../general/suppression.html) |
|
||||
proxyServer | The Proxy Server. |
|
||||
proxyPort | The Proxy Port. |
|
||||
proxyUsername | Defines the proxy user name. |
|
||||
proxyPassword | Defines the proxy password. |
|
||||
connectionTimeout | The URL Connection Timeout. |
|
||||
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
|
||||
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
|
||||
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
|
||||
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'
|
||||
suppressionFile | The file path to the XML suppression file \- used to suppress [false positives](../general/suppression.html) |
|
||||
hintsFile | The file path to the XML hints file \- used to resolve [false negatives](../general/hints.html) |
|
||||
proxyServer | The Proxy Server; see the [proxy configuration](../data/proxy.html) page for more information. |
|
||||
proxyPort | The Proxy Port. |
|
||||
proxyUsername | Defines the proxy user name. |
|
||||
proxyPassword | Defines the proxy password. |
|
||||
connectionTimeout | The URL Connection Timeout. |
|
||||
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
|
||||
====================
|
||||
@@ -46,18 +55,26 @@ Note, that specific analyzers will automatically disable themselves if no file
|
||||
types that they support are detected - so specifically disabling them may not
|
||||
be needed.
|
||||
|
||||
Property | Description | Default Value
|
||||
------------------------|---------------------------------------------------------------------------|------------------
|
||||
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. |
|
||||
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
|
||||
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. |
|
||||
nexusUsesProxy | Whether or not the defined proxy should be used when connecting to Nexus. | true
|
||||
nuspecAnalyzerEnabled | Sets whether or not the .NET Nuget Nuspec Analyzer will be used. | true
|
||||
assemblyAnalyzerEnabled | Sets whether or not the .NET Assembly Analyzer should be used. | true
|
||||
pathToMono | The path to Mono for .NET assembly analysis on non-windows systems. |
|
||||
Property | Description | Default Value
|
||||
------------------------------|-----------------------------------------------------------------------------------|------------------
|
||||
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. |
|
||||
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
|
||||
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. |
|
||||
nexusUsesProxy | Whether or not the defined proxy should be used when connecting to Nexus. | true
|
||||
pyDistributionAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) Python Distribution Analyzer will be used. | true
|
||||
pyPackageAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) Python Package Analyzer will be used. | true
|
||||
rubygemsAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) Ruby Gemspec Analyzer will be used. | true
|
||||
opensslAnalyzerEnabled | Sets whether the openssl Analyzer should be used. | true
|
||||
cmakeAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) CMake Analyzer should be used. | true
|
||||
autoconfAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) autoconf 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 the [experimental](../analyzers/index.html) Node.js Analyzer should be used. | true
|
||||
nuspecAnalyzerEnabled | Sets whether the .NET Nuget Nuspec Analyzer will 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. |
|
||||
|
||||
Advanced Configuration
|
||||
====================
|
||||
@@ -70,7 +87,7 @@ cveUrl12Modified | URL for the modified CVE 1.2.
|
||||
cveUrl20Modified | URL for the modified CVE 2.0. | http://static.nvd.nist.gov/feeds/xml/cve/nvdcve-2.0-modified.xml
|
||||
cveUrl12Base | Base URL for each year's CVE 1.2, the %d will be replaced with the year. | http://nvd.nist.gov/download/nvdcve-%d.xml
|
||||
cveUrl20Base | Base URL for each year's CVE 2.0, the %d will be replaced with the year. | http://static.nvd.nist.gov/feeds/xml/cve/nvdcve-2.0-%d.xml
|
||||
dataDirectory | Data directory to hold SQL CVEs contents. This should generally not be changed. |
|
||||
dataDirectory | Data directory that is used to store the local copy of the NVD. This should generally not be changed. | data
|
||||
databaseDriverName | The name of the database driver. Example: org.h2.Driver. |
|
||||
databaseDriverPath | The path to the database driver JAR file; only used if the driver is not in the class path. |
|
||||
connectionString | The connection string used to connect to the database. |
|
||||
|
||||
@@ -7,23 +7,28 @@ identifiers, and the associated Common Vulnerability and Exposure (CVE) entries.
|
||||
|
||||
Installation
|
||||
====================
|
||||
Download dependency-check-ant from [bintray here](http://dl.bintray.com/jeremy-long/owasp/dependency-check-ant-${project.version}.jar).
|
||||
To install dependency-check-ant place the dependency-check-ant-${project.version}.jar into
|
||||
the lib directory of your Ant instalation directory. Once installed you can add
|
||||
the taskdef to you build.xml and add the task to a new or existing target:
|
||||
1. Download dependency-check-ant from [bintray here](http://dl.bintray.com/jeremy-long/owasp/dependency-check-ant-${project.version}-release.zip).
|
||||
2. Unzip the archive
|
||||
3. Add the taskdef to your build.xml:
|
||||
|
||||
```xml
|
||||
<taskdef name="dependency-check" classname="org.owasp.dependencycheck.taskdefs.DependencyCheckTask"/>
|
||||
```
|
||||
```xml
|
||||
<!-- Set the value to the installation directory's path -->
|
||||
<property name="dependency-check.home" value="C:/tools/dependency-check-ant"/>
|
||||
<path id="dependency-check.path">
|
||||
<pathelement location="${dependency-check.home}/dependency-check-ant.jar"/>
|
||||
<fileset dir="${dependency-check.home}/lib">
|
||||
<include name="*.jar"/>
|
||||
</fileset>
|
||||
</path>
|
||||
<taskdef resource="dependency-check-taskdefs.properties">
|
||||
<classpath refid="dependency-check.path" />
|
||||
</taskdef>
|
||||
```
|
||||
4. Use the defined taskdefs:
|
||||
* [dependency-check](configuration.html) - the primary task used to check the project dependencies.
|
||||
* [dependency-check-purge](config-purge.html) - deletes the local copy of the NVD; this should rarely be used (if ever).
|
||||
* [dependency-check-update](config-update.html) - downloads and updates the local copy of the NVD.
|
||||
|
||||
If you do not want to install dependency-check-ant into your ant's lib directory when you define the task def you
|
||||
must add the classpath to the taskdef:
|
||||
|
||||
```xml
|
||||
<taskdef name="dependency-check" classname="org.owasp.dependencycheck.taskdefs.DependencyCheckTask">
|
||||
<classpath path="[path]/[to]/dependency-check-ant-${project.version}.jar"/>
|
||||
</taskdef>
|
||||
```
|
||||
|
||||
It is important to understand that the first time this task is executed it may
|
||||
take 10 minutes or more as it downloads and processes the data from the National
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
Usage
|
||||
====================
|
||||
First, add the dependency-check-ant taskdef to your build.xml (see the [installation guide](installation.html)):
|
||||
|
||||
```xml
|
||||
<taskdef name="dependency-check" classname="org.owasp.dependencycheck.taskdefs.DependencyCheckTask"/>
|
||||
```
|
||||
|
||||
Or
|
||||
|
||||
```xml
|
||||
<taskdef name="dependency-check" classname="org.owasp.dependencycheck.taskdefs.DependencyCheckTask">
|
||||
<classpath path="[path]/[to]/dependency-check-ant-${project.version}.jar"/>
|
||||
</taskdef>
|
||||
```
|
||||
|
||||
Next, add the task to a target of your choosing:
|
||||
|
||||
```xml
|
||||
<target name="dependency-check" description="Dependency-Check Analysis">
|
||||
<dependency-check applicationname="Hello World"
|
||||
autoupdate="true"
|
||||
reportoutputdirectory="${basedir}"
|
||||
reportformat="HTML">
|
||||
|
||||
<fileset dir="lib">
|
||||
<include name="**/*.jar"/>
|
||||
</fileset>
|
||||
</dependency-check>
|
||||
</target>
|
||||
```
|
||||
|
||||
See the [configuration guide](configuration.html) for more information.
|
||||
@@ -27,8 +27,7 @@ Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
<item name="dependency-check" href="../index.html"/>
|
||||
</breadcrumbs>
|
||||
<menu name="Getting Started">
|
||||
<item name="Installation" href="installation.html"/>
|
||||
<item name="Usage" href="usage.html"/>
|
||||
<item name="Installation" href="index.html"/>
|
||||
<item name="Configuration" href="configuration.html"/>
|
||||
</menu>
|
||||
<menu ref="reports" />
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.owasp.dependencycheck.data.nvdcve.BaseDBTestCase;
|
||||
import org.owasp.dependencycheck.BaseDBTestCase;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="Dependency-Check Test Build" default="test.fileset" basedir=".">
|
||||
|
||||
<taskdef name="dependency-check" classname="org.owasp.dependencycheck.taskdefs.DependencyCheckTask" />
|
||||
<taskdef name="dependency-check" classname="org.owasp.dependencycheck.taskdefs.Check" />
|
||||
|
||||
<target name="test.fileset">
|
||||
<dependency-check
|
||||
|
||||
@@ -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
|
||||
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
|
||||
------------
|
||||
@@ -19,6 +19,6 @@ Copyright & License
|
||||
|
||||
Dependency-Check is Copyright (c) 2012-2014 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://github.com/jeremylong/DependencyCheck/dependency-check-cli/blob/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.
|
||||
|
||||
Dependency-Check Command Line makes use of other open source libraries. Please see the [NOTICE.txt](https://github.com/jeremylong/DependencyCheck/dependency-check-cli/blob/master/NOTICES.txt) file for more information.
|
||||
Dependency-Check Command Line makes use of other open source libraries. Please see the [NOTICE.txt](https://raw.githubusercontent.com/jeremylong/DependencyCheck/master/dependency-check-cli/NOTICE.txt) file for more information.
|
||||
|
||||
@@ -20,7 +20,7 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved.
|
||||
<parent>
|
||||
<groupId>org.owasp</groupId>
|
||||
<artifactId>dependency-check-parent</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.4.4</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dependency-check-cli</artifactId>
|
||||
@@ -110,6 +110,7 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved.
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<argLine>-Dfile.encoding=UTF-8</argLine>
|
||||
<systemProperties>
|
||||
<property>
|
||||
<name>cpe</name>
|
||||
@@ -124,10 +125,6 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved.
|
||||
</systemProperties>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>appassembler-maven-plugin</artifactId>
|
||||
@@ -178,96 +175,6 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved.
|
||||
</build>
|
||||
<reporting>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||
<version>${reporting.project-info-reports-plugin.version}</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<reports>
|
||||
<report>summary</report>
|
||||
<report>license</report>
|
||||
<report>help</report>
|
||||
</reports>
|
||||
</reportSet>
|
||||
</reportSets>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>${reporting.javadoc-plugin.version}</version>
|
||||
<configuration>
|
||||
<failOnError>false</failOnError>
|
||||
<bottom>Copyright<EFBFBD> 2012-15 Jeremy Long. All Rights Reserved.</bottom>
|
||||
</configuration>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<id>default</id>
|
||||
<reports>
|
||||
<report>javadoc</report>
|
||||
</reports>
|
||||
</reportSet>
|
||||
</reportSets>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>versions-maven-plugin</artifactId>
|
||||
<version>${reporting.versions-plugin.version}</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<reports>
|
||||
<report>dependency-updates-report</report>
|
||||
<report>plugin-updates-report</report>
|
||||
</reports>
|
||||
</reportSet>
|
||||
</reportSets>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jxr-plugin</artifactId>
|
||||
<version>${reporting.jxr-plugin.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<version>${reporting.cobertura-plugin.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-report-plugin</artifactId>
|
||||
<version>${reporting.surefire-report-plugin.version}</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<reports>
|
||||
<report>report-only</report>
|
||||
</reports>
|
||||
</reportSet>
|
||||
</reportSets>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>taglist-maven-plugin</artifactId>
|
||||
<version>${reporting.taglist-plugin.version}</version>
|
||||
<configuration>
|
||||
<tagListOptions>
|
||||
<tagClasses>
|
||||
<tagClass>
|
||||
<displayName>Todo Work</displayName>
|
||||
<tags>
|
||||
<tag>
|
||||
<matchString>todo</matchString>
|
||||
<matchType>ignoreCase</matchType>
|
||||
</tag>
|
||||
<tag>
|
||||
<matchString>FIXME</matchString>
|
||||
<matchType>exact</matchType>
|
||||
</tag>
|
||||
</tags>
|
||||
</tagClass>
|
||||
</tagClasses>
|
||||
</tagListOptions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
@@ -300,11 +207,6 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved.
|
||||
</rulesets>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<version>${reporting.findbugs-plugin.version}</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</reporting>
|
||||
<dependencies>
|
||||
@@ -334,5 +236,15 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved.
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.ant</groupId>
|
||||
<artifactId>ant</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.apache.ant</groupId>
|
||||
<artifactId>ant-launcher</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.owasp.dependencycheck;
|
||||
|
||||
import ch.qos.logback.classic.LoggerContext;
|
||||
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
@@ -31,12 +32,16 @@ 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.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.org.apache.tools.ant.DirectoryScanner;
|
||||
import org.apache.tools.ant.DirectoryScanner;
|
||||
import org.owasp.dependencycheck.reporting.ReportGenerator;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -57,21 +62,26 @@ public class App {
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
int exitCode = 0;
|
||||
try {
|
||||
Settings.initialize();
|
||||
final App app = new App();
|
||||
app.run(args);
|
||||
exitCode = app.run(args);
|
||||
LOGGER.debug("Exit code: " + exitCode);
|
||||
} finally {
|
||||
Settings.cleanup(true);
|
||||
}
|
||||
System.exit(exitCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main CLI entry-point into the application.
|
||||
*
|
||||
* @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();
|
||||
|
||||
try {
|
||||
@@ -79,49 +89,132 @@ public class App {
|
||||
} catch (FileNotFoundException ex) {
|
||||
System.err.println(ex.getMessage());
|
||||
cli.printHelp();
|
||||
return;
|
||||
return -1;
|
||||
} catch (ParseException ex) {
|
||||
System.err.println(ex.getMessage());
|
||||
cli.printHelp();
|
||||
return;
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (cli.getVerboseLog() != null) {
|
||||
prepareLogger(cli.getVerboseLog());
|
||||
}
|
||||
|
||||
if (cli.isGetVersion()) {
|
||||
if (cli.isPurge()) {
|
||||
if (cli.getConnectionString() != null) {
|
||||
LOGGER.error("Unable to purge the database when using a non-default connection string");
|
||||
exitCode = -3;
|
||||
} else {
|
||||
try {
|
||||
populateSettings(cli);
|
||||
} catch (InvalidSettingException ex) {
|
||||
LOGGER.error(ex.getMessage());
|
||||
LOGGER.debug("Error loading properties file", ex);
|
||||
exitCode = -4;
|
||||
}
|
||||
File db;
|
||||
try {
|
||||
db = new File(Settings.getDataDirectory(), "dc.h2.db");
|
||||
if (db.exists()) {
|
||||
if (db.delete()) {
|
||||
LOGGER.info("Database file purged; local copy of the NVD has been removed");
|
||||
} else {
|
||||
LOGGER.error("Unable to delete '{}'; please delete the file manually", db.getAbsolutePath());
|
||||
exitCode = -5;
|
||||
}
|
||||
} else {
|
||||
LOGGER.error("Unable to purge database; the database file does not exists: {}", db.getAbsolutePath());
|
||||
exitCode = -6;
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
LOGGER.error("Unable to delete the database");
|
||||
exitCode = -7;
|
||||
}
|
||||
}
|
||||
} else if (cli.isGetVersion()) {
|
||||
cli.printVersionInfo();
|
||||
} else if (cli.isUpdateOnly()) {
|
||||
populateSettings(cli);
|
||||
runUpdateOnly();
|
||||
} else if (cli.isRunScan()) {
|
||||
populateSettings(cli);
|
||||
try {
|
||||
runScan(cli.getReportDirectory(), cli.getReportFormat(), cli.getApplicationName(), cli.getScanFiles(),
|
||||
cli.getExcludeList(), cli.getSymLinkDepth());
|
||||
populateSettings(cli);
|
||||
} 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) {
|
||||
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 occurred");
|
||||
} else {
|
||||
exitCode = -14;
|
||||
}
|
||||
for (Throwable e : ex.getExceptions()) {
|
||||
LOGGER.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
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 applicationName the application name for the report
|
||||
* @param files the files/directories to scan
|
||||
* @param excludes the patterns for files/directories to exclude
|
||||
* @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,
|
||||
String[] excludes, int symLinkDepth) throws InvalidScanPathException {
|
||||
String[] excludes, int symLinkDepth) throws InvalidScanPathException, DatabaseException, ExceptionCollection, ReportException {
|
||||
Engine engine = null;
|
||||
try {
|
||||
engine = new Engine();
|
||||
@@ -153,10 +246,9 @@ public class App {
|
||||
include = "**/*";
|
||||
}
|
||||
}
|
||||
//LOGGER.debug("baseDir: {}", baseDir);
|
||||
//LOGGER.debug("include: {}", include);
|
||||
scanner.setBasedir(baseDir);
|
||||
scanner.setIncludes(include);
|
||||
final String[] includes = {include};
|
||||
scanner.setIncludes(includes);
|
||||
scanner.setMaxLevelsOfSymlinks(symLinkDepth);
|
||||
if (symLinkDepth <= 0) {
|
||||
scanner.setFollowSymlinks(false);
|
||||
@@ -175,7 +267,15 @@ public class App {
|
||||
}
|
||||
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();
|
||||
DatabaseProperties prop = null;
|
||||
CveDB cve = null;
|
||||
@@ -183,8 +283,6 @@ public class App {
|
||||
cve = new CveDB();
|
||||
cve.open();
|
||||
prop = cve.getDatabaseProperties();
|
||||
} catch (DatabaseException ex) {
|
||||
LOGGER.debug("Unable to retrieve DB Properties", ex);
|
||||
} finally {
|
||||
if (cve != null) {
|
||||
cve.close();
|
||||
@@ -193,34 +291,37 @@ public class App {
|
||||
final ReportGenerator report = new ReportGenerator(applicationName, dependencies, engine.getAnalyzers(), prop);
|
||||
try {
|
||||
report.generateReports(reportDirectory, outputFormat);
|
||||
} catch (IOException ex) {
|
||||
LOGGER.error("There was an IO error while attempting to generate the report.");
|
||||
LOGGER.debug("", ex);
|
||||
} catch (Throwable ex) {
|
||||
LOGGER.error("There was an error while attempting to generate the report.");
|
||||
LOGGER.debug("", ex);
|
||||
} catch (ReportException ex) {
|
||||
if (exCol != null) {
|
||||
exCol.addException(ex);
|
||||
throw exCol;
|
||||
} else {
|
||||
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 {
|
||||
if (engine != null) {
|
||||
engine.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
try {
|
||||
engine = new Engine();
|
||||
engine.doUpdates();
|
||||
} catch (DatabaseException ex) {
|
||||
LOGGER.error("Unable to connect to the dependency-check database; analysis has stopped");
|
||||
LOGGER.debug("", ex);
|
||||
} finally {
|
||||
if (engine != null) {
|
||||
engine.cleanup();
|
||||
@@ -231,11 +332,13 @@ public class App {
|
||||
/**
|
||||
* 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
|
||||
* the core engine.
|
||||
* @param cli a reference to the CLI Parser that contains the command line
|
||||
* 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 String connectionTimeout = cli.getConnectionTimeout();
|
||||
final String proxyServer = cli.getProxyServer();
|
||||
@@ -245,16 +348,7 @@ public class App {
|
||||
final String dataDirectory = cli.getDataDirectory();
|
||||
final File propertiesFile = cli.getPropertiesFile();
|
||||
final String suppressionFile = cli.getSuppressionFile();
|
||||
final boolean jarDisabled = cli.isJarDisabled();
|
||||
final boolean archiveDisabled = cli.isArchiveDisabled();
|
||||
final boolean pyDistDisabled = cli.isPythonDistributionDisabled();
|
||||
final boolean cMakeDisabled = cli.isCmakeDisabled();
|
||||
final boolean pyPkgDisabled = cli.isPythonPackageDisabled();
|
||||
final boolean autoconfDisabled = cli.isAutoconfDisabled();
|
||||
final boolean assemblyDisabled = cli.isAssemblyDisabled();
|
||||
final boolean nuspecDisabled = cli.isNuspecDisabled();
|
||||
final boolean centralDisabled = cli.isCentralDisabled();
|
||||
final boolean nexusDisabled = cli.isNexusDisabled();
|
||||
final String hintsFile = cli.getHintsFile();
|
||||
final String nexusUrl = cli.getNexusUrl();
|
||||
final String databaseDriverName = cli.getDatabaseDriverName();
|
||||
final String databaseDriverPath = cli.getDatabaseDriverPath();
|
||||
@@ -267,16 +361,16 @@ public class App {
|
||||
final String cveMod20 = cli.getModifiedCve20Url();
|
||||
final String cveBase12 = cli.getBaseCve12Url();
|
||||
final String cveBase20 = cli.getBaseCve20Url();
|
||||
final Integer cveValidForHours = cli.getCveValidForHours();
|
||||
final boolean experimentalEnabled = cli.isExperimentalEnabled();
|
||||
|
||||
if (propertiesFile != null) {
|
||||
try {
|
||||
Settings.mergeProperties(propertiesFile);
|
||||
} catch (FileNotFoundException ex) {
|
||||
LOGGER.error("Unable to load properties file '{}'", propertiesFile.getPath());
|
||||
LOGGER.debug("", ex);
|
||||
throw new InvalidSettingException("Unable to find properties file '" + propertiesFile.getPath() + "'", ex);
|
||||
} catch (IOException ex) {
|
||||
LOGGER.error("Unable to find properties file '{}'", propertiesFile.getPath());
|
||||
LOGGER.debug("", ex);
|
||||
throw new InvalidSettingException("Error reading properties file '" + propertiesFile.getPath() + "'", ex);
|
||||
}
|
||||
}
|
||||
// We have to wait until we've merged the properties before attempting to set whether we use
|
||||
@@ -296,63 +390,43 @@ public class App {
|
||||
Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath());
|
||||
}
|
||||
Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate);
|
||||
if (proxyServer != null && !proxyServer.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.PROXY_SERVER, proxyServer);
|
||||
}
|
||||
if (proxyPort != null && !proxyPort.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.PROXY_PORT, proxyPort);
|
||||
}
|
||||
if (proxyUser != null && !proxyUser.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.PROXY_USERNAME, proxyUser);
|
||||
}
|
||||
if (proxyPass != null && !proxyPass.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.PROXY_PASSWORD, proxyPass);
|
||||
}
|
||||
if (connectionTimeout != null && !connectionTimeout.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout);
|
||||
}
|
||||
if (suppressionFile != null && !suppressionFile.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.SUPPRESSION_FILE, suppressionFile);
|
||||
}
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_SERVER, proxyServer);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PORT, proxyPort);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_USERNAME, proxyUser);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PASSWORD, proxyPass);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE, suppressionFile);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.HINTS_FILE, hintsFile);
|
||||
Settings.setIntIfNotNull(Settings.KEYS.CVE_CHECK_VALID_FOR_HOURS, cveValidForHours);
|
||||
|
||||
//File Type Analyzer Settings
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_JAR_ENABLED, !jarDisabled);
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, !archiveDisabled);
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED, !pyDistDisabled);
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_PYTHON_PACKAGE_ENABLED, !pyPkgDisabled);
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_AUTOCONF_ENABLED, !autoconfDisabled);
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_CMAKE_ENABLED, !cMakeDisabled);
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_NUSPEC_ENABLED, !nuspecDisabled);
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED, !assemblyDisabled);
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_EXPERIMENTAL_ENABLED, experimentalEnabled);
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_JAR_ENABLED, !cli.isJarDisabled());
|
||||
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_PACKAGE_ENABLED, !cli.isPythonPackageDisabled());
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_AUTOCONF_ENABLED, !cli.isAutoconfDisabled());
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_CMAKE_ENABLED, !cli.isCmakeDisabled());
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_NUSPEC_ENABLED, !cli.isNuspecDisabled());
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED, !cli.isAssemblyDisabled());
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_ENABLED, !cli.isBundleAuditDisabled());
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_OPENSSL_ENABLED, !cli.isOpenSSLDisabled());
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_COMPOSER_LOCK_ENABLED, !cli.isComposerDisabled());
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED, !cli.isNodeJsDisabled());
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_RUBY_GEMSPEC_ENABLED, !cli.isRubyGemspecDisabled());
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, !cli.isCentralDisabled());
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, !cli.isNexusDisabled());
|
||||
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, !centralDisabled);
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, !nexusDisabled);
|
||||
if (nexusUrl != null && !nexusUrl.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.ANALYZER_NEXUS_URL, nexusUrl);
|
||||
}
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_PROXY, nexusUsesProxy);
|
||||
if (databaseDriverName != null && !databaseDriverName.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.DB_DRIVER_NAME, databaseDriverName);
|
||||
}
|
||||
if (databaseDriverPath != null && !databaseDriverPath.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.DB_DRIVER_PATH, databaseDriverPath);
|
||||
}
|
||||
if (connectionString != null && !connectionString.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.DB_CONNECTION_STRING, connectionString);
|
||||
}
|
||||
if (databaseUser != null && !databaseUser.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.DB_USER, databaseUser);
|
||||
}
|
||||
if (databasePassword != null && !databasePassword.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.DB_PASSWORD, databasePassword);
|
||||
}
|
||||
if (additionalZipExtensions != null && !additionalZipExtensions.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS, additionalZipExtensions);
|
||||
}
|
||||
if (pathToMono != null && !pathToMono.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, pathToMono);
|
||||
}
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_PATH, cli.getPathToBundleAudit());
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_NEXUS_URL, nexusUrl);
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_USES_PROXY, nexusUsesProxy);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_NAME, databaseDriverName);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_PATH, databaseDriverPath);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.DB_CONNECTION_STRING, connectionString);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.DB_USER, databaseUser);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.DB_PASSWORD, databasePassword);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS, additionalZipExtensions);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, pathToMono);
|
||||
if (cveBase12 != null && !cveBase12.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.CVE_SCHEMA_1_2, cveBase12);
|
||||
Settings.setString(Settings.KEYS.CVE_SCHEMA_2_0, cveBase20);
|
||||
@@ -374,7 +448,7 @@ public class App {
|
||||
encoder.setPattern("%d %C:%L%n%-5level - %msg%n");
|
||||
encoder.setContext(context);
|
||||
encoder.start();
|
||||
final FileAppender fa = new FileAppender();
|
||||
final FileAppender<ILoggingEvent> fa = new FileAppender<ILoggingEvent>();
|
||||
fa.setAppend(true);
|
||||
fa.setEncoder(encoder);
|
||||
fa.setContext(context);
|
||||
@@ -392,15 +466,16 @@ public class App {
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a path and resolves it to be a canonical & absolute path. The caveats are that this method will take an Ant style
|
||||
* file selector path (../someDir/**\/*.jar) and convert it to an absolute/canonical path (at least to the left of the first *
|
||||
* or ?).
|
||||
* Takes a path and resolves it to be a canonical & absolute path. The
|
||||
* caveats are that this method will take an Ant style file selector path
|
||||
* (../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
|
||||
* @return the canonical path
|
||||
*/
|
||||
protected String ensureCanonicalPath(String path) {
|
||||
String basePath = null;
|
||||
String basePath;
|
||||
String wildCards = null;
|
||||
final String file = path.replace('\\', '/');
|
||||
if (file.contains("*") || file.contains("?")) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,7 +22,12 @@ package org.owasp.dependencycheck;
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
class InvalidScanPathException extends Exception {
|
||||
public class InvalidScanPathException extends Exception {
|
||||
|
||||
/**
|
||||
* The serial version UID for serialization.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates a new InvalidScanPathException.
|
||||
|
||||
@@ -5,7 +5,7 @@ The following table lists the command line arguments:
|
||||
|
||||
Short | Argument Name | Parameter | Description | Requirement
|
||||
-------|-----------------------|-----------------|-------------|------------
|
||||
\-a | \-\-app | \<name\> | The name of the application being scanned. This is a required argument. | Required
|
||||
| \-\-project | \<name\> | The name of the project being scanned. | Required
|
||||
\-s | \-\-scan | \<path\> | The path to scan \- this option can be specified multiple times. It is also possible to specify Ant style paths (e.g. directory/**/*.jar). | Required
|
||||
| \-\-exclude | \<pattern\> | The path patterns to exclude from the scan \- this option can be specified multiple times. This accepts Ant style path patterns (e.g. **/exclude/**). | Optional
|
||||
| \-\-symLink | \<depth\> | The depth that symbolic links will be followed; the default is 0 meaning symbolic links will not be followed. | Optional
|
||||
@@ -13,29 +13,35 @@ Short | Argument Name | Parameter | Description | Requir
|
||||
\-f | \-\-format | \<format\> | The output format to write to (XML, HTML, VULN, ALL). The default is HTML. | Required
|
||||
\-l | \-\-log | \<file\> | The file path to write verbose logging information. | Optional
|
||||
\-n | \-\-noupdate | | Disables the automatic updating of the CPE data. | Optional
|
||||
| \-\-suppression | \<file\> | The file path to the suppression XML file; used to suppress [false positives](../suppression.html). | Optional
|
||||
| \-\-suppression | \<file\> | The file path to the suppression XML file; used to suppress [false positives](../general/suppression.html). | Optional
|
||||
\-h | \-\-help | | Print the help message. | Optional
|
||||
| \-\-advancedHelp | | Print the advanced help message. | 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
|
||||
| \-\-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
|
||||
================
|
||||
Short | Argument Name | Parameter | Description | Default Value
|
||||
-------|-----------------------|-----------------|----------------------------------------------------------------------------------|-------------------
|
||||
| \-\-cveUrl12Modified | \<url\> | URL for the modified CVE 1.2 | http://nvd.nist.gov/download/nvdcve-modified.xml
|
||||
| \-\-cveUrl20Modified | \<url\> | URL for the modified CVE 2.0 | http://static.nvd.nist.gov/feeds/xml/cve/nvdcve-2.0-modified.xml
|
||||
| \-\-cveUrl12Base | \<url\> | Base URL for each year's CVE 1.2, the %d will be replaced with the year | http://nvd.nist.gov/download/nvdcve-%d.xml
|
||||
| \-\-cveUrl20Base | \<url\> | Base URL for each year's CVE 2.0, the %d will be replaced with the year | http://static.nvd.nist.gov/feeds/xml/cve/nvdcve-2.0-%d.xml
|
||||
| \-\-cveUrl12Modified | \<url\> | URL for the modified CVE 1.2 | https://nvd.nist.gov/download/nvdcve-Modified.xml.gz
|
||||
| \-\-cveUrl20Modified | \<url\> | URL for the modified CVE 2.0 | https://nvd.nist.gov/feeds/xml/cve/nvdcve-2.0-Modified.xml.gz
|
||||
| \-\-cveUrl12Base | \<url\> | Base URL for each year's CVE 1.2, the %d will be replaced with the year | https://nvd.nist.gov/download/nvdcve-%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. |
|
||||
| \-\-updateonly | | If set only the update phase of dependency-check will be executed; no scan will be executed and no report will be generated. |
|
||||
| \-\-disablePyDist | | Sets whether the Python Distribution Analyzer will be used. | false
|
||||
| \-\-disablePyPkg | | Sets whether the Python Package Analyzer will be used. | false
|
||||
| \-\-disableAutoconf | | Sets whether the Autoconf Analyzer will be used. | false
|
||||
| \-\-disablePyDist | | Sets whether the [experimental](../analyzers/index.html) Python Distribution Analyzer will be used. | false
|
||||
| \-\-disablePyPkg | | Sets whether the [experimental](../analyzers/index.html) Python 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 [experimental](../analyzers/index.html) Ruby Gemspec 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 [experimental](../analyzers/index.html) Autoconf Analyzer will be used. | false
|
||||
| \-\-disableOpenSSL | | Sets whether the OpenSSL Analyzer will be used. | false
|
||||
| \-\-disableCmake | | Sets whether the Cmake Analyzer will be used. | false
|
||||
| \-\-disableArchive | | Sets whether the Archive Analyzer will be used. | 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
|
||||
| \-\-zipExtensions | \<strings\> | A comma-separated list of additional file extensions to be treated like a ZIP file, the contents will be extracted and analyzed. |
|
||||
| \-\-disableJar | | Sets whether the Jar Analyzer will be used. | false
|
||||
| \-\-disableJar | | Sets whether the Jar 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
|
||||
| \-\-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. |
|
||||
@@ -43,7 +49,8 @@ Short | Argument Name | Paramete
|
||||
| \-\-disableNuspec | | Sets whether or not the .NET Nuget Nuspec Analyzer will 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. |
|
||||
| \-\-proxyserver | \<server\> | The proxy server to use when downloading resources. |
|
||||
| \-\-bundleAudit | | The path to the bundle-audit executable. |
|
||||
| \-\-proxyserver | \<server\> | The proxy server to use when downloading resources; see the [proxy configuration](../data/proxy.html) page for more information. |
|
||||
| \-\-proxyport | \<port\> | The proxy port to use when downloading resources. |
|
||||
| \-\-connectiontimeout | \<timeout\> | The connection timeout (in milliseconds) to use when downloading resources. |
|
||||
| \-\-proxypass | \<pass\> | The proxy password to use when downloading resources. |
|
||||
@@ -54,3 +61,4 @@ Short | Argument Name | Paramete
|
||||
| \-\-dbPassword | \<password\> | The password for connecting to the database. |
|
||||
| \-\-dbUser | \<user\> | The username used to connect to the database. |
|
||||
\-d | \-\-data | \<path\> | The location of the data directory used to store persistent data. This option should generally not be set. |
|
||||
| \-\-purge | | Delete the local copy of the NVD. This is used to force a refresh of the data. |
|
||||
@@ -9,19 +9,23 @@ Installation & Usage
|
||||
====================
|
||||
Download the dependency-check command line tool [here](http://dl.bintray.com/jeremy-long/owasp/dependency-check-${project.version}-release.zip).
|
||||
Extract the zip file to a location on your computer and put the 'bin' directory into the
|
||||
path environment variable. On \*nix systems you will likely need to make the shell
|
||||
script executable:
|
||||
path environment variable.
|
||||
|
||||
$ chmod +777 dependency-check.sh
|
||||
|
||||
To scan a folder on the system you can run:
|
||||
#set( $H = '#' )
|
||||
|
||||
$H$H$H Homebrew
|
||||
$ brew install dependency-check
|
||||
|
||||
This puts an executable `dependency-check` script in the `/bin` directory of
|
||||
your homebrew installation.
|
||||
|
||||
To scan a folder on the system you can run:
|
||||
|
||||
$H$H$H Windows
|
||||
dependency-check.bat --app "My App Name" --scan "c:\java\application\lib"
|
||||
dependency-check.bat --project "My App Name" --scan "c:\java\application\lib"
|
||||
|
||||
$H$H$H *nix
|
||||
dependency-check.sh --app "My App Name" --scan "/java/application/lib"
|
||||
dependency-check.sh --project "My App Name" --scan "/java/application/lib"
|
||||
|
||||
To view the command line arguments, see the <a href="arguments.html">arguments page</a>, or you can run:
|
||||
|
||||
@@ -29,4 +33,4 @@ $H$H$H Windows
|
||||
dependency-check.bat --help
|
||||
|
||||
$H$H$H *nix
|
||||
dependency-check.sh --help
|
||||
dependency-check.sh --help
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
/*
|
||||
* Copyright 2015 OWASP.
|
||||
* 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
|
||||
* 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) 2015 The OWASP Foundatio. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ Copyright & License
|
||||
|
||||
Dependency-Check is Copyright (c) 2012-2014 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://github.com/jeremylong/DependencyCheck/dependency-check-cli/blob/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.
|
||||
|
||||
Dependency-Check makes use of several other open source libraries. Please see the [NOTICE.txt] [notices] file for more information.
|
||||
|
||||
@@ -25,4 +25,4 @@ Dependency-Check makes use of several other open source libraries. Please see th
|
||||
[wiki]: https://github.com/jeremylong/DependencyCheck/wiki
|
||||
[subscribe]: mailto:dependency-check+subscribe@googlegroups.com
|
||||
[post]: mailto:dependency-check@googlegroups.com
|
||||
[notices]: https://github.com/jeremylong/DependencyCheck/blob/master/NOTICES.txt
|
||||
[notices]: https://raw.githubusercontent.com/jeremylong/DependencyCheck/master/NOTICE.txt
|
||||
@@ -20,7 +20,7 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
<parent>
|
||||
<groupId>org.owasp</groupId>
|
||||
<artifactId>dependency-check-parent</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.4.4</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dependency-check-core</artifactId>
|
||||
@@ -83,9 +83,6 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
</testResource>
|
||||
<testResource>
|
||||
<directory>${basedir}/src/test/resources</directory>
|
||||
<excludes>
|
||||
<exclude>**/mysql-connector-java-5.1.27-bin.jar</exclude>
|
||||
</excludes>
|
||||
<filtering>false</filtering>
|
||||
</testResource>
|
||||
</testResources>
|
||||
@@ -110,19 +107,17 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>jar</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>test-jar</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>test-jar</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/*.class</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
@@ -180,6 +175,7 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<argLine>-Dfile.encoding=UTF-8</argLine>
|
||||
<systemProperties>
|
||||
<property>
|
||||
<name>data.directory</name>
|
||||
@@ -212,81 +208,14 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
</systemProperties>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<compilerArgument>-Xlint:unchecked</compilerArgument>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<reporting>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||
<version>${reporting.project-info-reports-plugin.version}</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<reports>
|
||||
<report>summary</report>
|
||||
<report>license</report>
|
||||
<report>help</report>
|
||||
</reports>
|
||||
</reportSet>
|
||||
</reportSets>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>${reporting.javadoc-plugin.version}</version>
|
||||
<configuration>
|
||||
<failOnError>false</failOnError>
|
||||
<bottom>Copyright© 2012-15 Jeremy Long. All Rights Reserved.</bottom>
|
||||
</configuration>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<id>default</id>
|
||||
<reports>
|
||||
<report>javadoc</report>
|
||||
</reports>
|
||||
</reportSet>
|
||||
</reportSets>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>versions-maven-plugin</artifactId>
|
||||
<version>${reporting.versions-plugin.version}</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<reports>
|
||||
<report>dependency-updates-report</report>
|
||||
<report>plugin-updates-report</report>
|
||||
</reports>
|
||||
</reportSet>
|
||||
</reportSets>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jxr-plugin</artifactId>
|
||||
<version>${reporting.jxr-plugin.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<version>${reporting.cobertura-plugin.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-report-plugin</artifactId>
|
||||
<version>${reporting.surefire-report-plugin.version}</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<reports>
|
||||
<report>report-only</report>
|
||||
</reports>
|
||||
</reportSet>
|
||||
<reportSet>
|
||||
<id>integration-tests</id>
|
||||
<reports>
|
||||
@@ -296,30 +225,6 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
</reportSet>
|
||||
</reportSets>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>taglist-maven-plugin</artifactId>
|
||||
<version>${reporting.taglist-plugin.version}</version>
|
||||
<configuration>
|
||||
<tagListOptions>
|
||||
<tagClasses>
|
||||
<tagClass>
|
||||
<displayName>Todo Work</displayName>
|
||||
<tags>
|
||||
<tag>
|
||||
<matchString>todo</matchString>
|
||||
<matchType>ignoreCase</matchType>
|
||||
</tag>
|
||||
<tag>
|
||||
<matchString>FIXME</matchString>
|
||||
<matchType>exact</matchType>
|
||||
</tag>
|
||||
</tags>
|
||||
</tagClass>
|
||||
</tagClasses>
|
||||
</tagListOptions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
@@ -352,11 +257,6 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
</rulesets>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<version>${reporting.findbugs-plugin.version}</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</reporting>
|
||||
<dependencies>
|
||||
@@ -371,22 +271,11 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<!-- Set this to test so that each project that uses this has to have its own implementation of SLF4J -->
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- For the CAL10N support -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-ext</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.owasp</groupId>
|
||||
<artifactId>dependency-check-utils</artifactId>
|
||||
@@ -411,8 +300,8 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
<artifactId>commons-io</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.lucene</groupId>
|
||||
@@ -433,11 +322,15 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish</groupId>
|
||||
<artifactId>javax.json</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.mail</groupId>
|
||||
@@ -559,6 +452,20 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
<scope>test</scope>
|
||||
<optional>true</optional>
|
||||
</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>
|
||||
<profiles>
|
||||
<profile>
|
||||
@@ -573,7 +480,6 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.18.1</version>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
@@ -581,12 +487,68 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<version>2.18.1</version>
|
||||
<configuration>
|
||||
<systemProperties>
|
||||
<property>
|
||||
<name>data.driver_path</name>
|
||||
<value>${basedir}/${driver_path}</value>
|
||||
<value>${driver_path}</value>
|
||||
</property>
|
||||
<property>
|
||||
<name>data.driver_name</name>
|
||||
<value>${driver_name}</value>
|
||||
</property>
|
||||
<property>
|
||||
<name>data.connection_string</name>
|
||||
<value>${connection_string}</value>
|
||||
</property>
|
||||
</systemProperties>
|
||||
<includes>
|
||||
<include>**/*MySQLTest.java</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>integration-test</goal>
|
||||
<goal>verify</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>Postgresql-IntegrationTest</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>postgresql</name>
|
||||
</property>
|
||||
</activation>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>9.4-1204-jdbc42</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<configuration>
|
||||
<systemProperties>
|
||||
<property>
|
||||
<name>data.driver_path</name>
|
||||
<value>${driver_path}</value>
|
||||
</property>
|
||||
<property>
|
||||
<name>data.driver_name</name>
|
||||
@@ -632,13 +594,6 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
<scope>test</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.thoughtworks.xstream</groupId>
|
||||
<artifactId>xstream</artifactId>
|
||||
<version>1.4.2</version>
|
||||
<scope>test</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.ws.security</groupId>
|
||||
<artifactId>wss4j</artifactId>
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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 Stefan Neuhaus. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck;
|
||||
|
||||
import org.owasp.dependencycheck.analyzer.Analyzer;
|
||||
import org.owasp.dependencycheck.analyzer.FileTypeAnalyzer;
|
||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
* Task to support parallelism of dependency-check analysis.
|
||||
* Analyses a single {@link Dependency} by a specific {@link Analyzer}.
|
||||
*
|
||||
* @author Stefan Neuhaus
|
||||
*/
|
||||
class AnalysisTask implements Callable<Void> {
|
||||
|
||||
/**
|
||||
* Instance of the logger.
|
||||
*/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AnalysisTask.class);
|
||||
|
||||
/**
|
||||
* A reference to the analyzer.
|
||||
*/
|
||||
private final Analyzer analyzer;
|
||||
/**
|
||||
* The dependency to analyze.
|
||||
*/
|
||||
private final Dependency dependency;
|
||||
/**
|
||||
* A reference to the dependency-check engine.
|
||||
*/
|
||||
private final Engine engine;
|
||||
/**
|
||||
* The list of exceptions that may occur during analysis.
|
||||
*/
|
||||
private final List<Throwable> exceptions;
|
||||
|
||||
/**
|
||||
* Creates a new analysis task.
|
||||
*
|
||||
* @param analyzer a reference of the analyzer to execute
|
||||
* @param dependency the dependency to analyze
|
||||
* @param engine the dependency-check engine
|
||||
* @param exceptions exceptions that occur during analysis will be added to
|
||||
* this collection of exceptions
|
||||
*/
|
||||
AnalysisTask(Analyzer analyzer, Dependency dependency, Engine engine, List<Throwable> exceptions) {
|
||||
this.analyzer = analyzer;
|
||||
this.dependency = dependency;
|
||||
this.engine = engine;
|
||||
this.exceptions = exceptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the analysis task.
|
||||
*
|
||||
* @return null
|
||||
* @throws Exception thrown if unable to execute the analysis task
|
||||
*/
|
||||
@Override
|
||||
public Void call() {
|
||||
Settings.initialize();
|
||||
|
||||
if (shouldAnalyze()) {
|
||||
LOGGER.debug("Begin Analysis of '{}' ({})", dependency.getActualFilePath(), analyzer.getName());
|
||||
try {
|
||||
analyzer.analyze(dependency, engine);
|
||||
} catch (AnalysisException ex) {
|
||||
LOGGER.warn("An error occurred while analyzing '{}' ({}).", dependency.getActualFilePath(), analyzer.getName());
|
||||
LOGGER.debug("", ex);
|
||||
exceptions.add(ex);
|
||||
} catch (Throwable ex) {
|
||||
LOGGER.warn("An unexpected error occurred during analysis of '{}' ({}): {}",
|
||||
dependency.getActualFilePath(), analyzer.getName(), ex.getMessage());
|
||||
LOGGER.debug("", ex);
|
||||
exceptions.add(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the analyzer can analyze the given dependency.
|
||||
*
|
||||
* @return whether or not the analyzer can analyze the dependency
|
||||
*/
|
||||
boolean shouldAnalyze() {
|
||||
if (analyzer instanceof FileTypeAnalyzer) {
|
||||
final FileTypeAnalyzer fileTypeAnalyzer = (FileTypeAnalyzer) analyzer;
|
||||
return fileTypeAnalyzer.accept(dependency.getActualFile());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,6 @@ import org.owasp.dependencycheck.analyzer.AnalysisPhase;
|
||||
import org.owasp.dependencycheck.analyzer.Analyzer;
|
||||
import org.owasp.dependencycheck.analyzer.AnalyzerService;
|
||||
import org.owasp.dependencycheck.analyzer.FileTypeAnalyzer;
|
||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
import org.owasp.dependencycheck.data.nvdcve.ConnectionFactory;
|
||||
import org.owasp.dependencycheck.data.nvdcve.CveDB;
|
||||
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
|
||||
@@ -29,6 +28,8 @@ import org.owasp.dependencycheck.data.update.CachedWebDataSource;
|
||||
import org.owasp.dependencycheck.data.update.UpdateService;
|
||||
import org.owasp.dependencycheck.data.update.exception.UpdateException;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.exception.ExceptionCollection;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
import org.owasp.dependencycheck.exception.NoDataException;
|
||||
import org.owasp.dependencycheck.utils.InvalidSettingException;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
@@ -38,15 +39,26 @@ import org.slf4j.LoggerFactory;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Scans files, directories, etc. for Dependencies. Analyzers are loaded and 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.
|
||||
* Scans files, directories, etc. for Dependencies. Analyzers are loaded and
|
||||
* 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
|
||||
*/
|
||||
@@ -55,19 +67,20 @@ public class Engine implements FileFilter {
|
||||
/**
|
||||
* The list of dependencies.
|
||||
*/
|
||||
private List<Dependency> dependencies = new ArrayList<Dependency>();
|
||||
private final List<Dependency> dependencies = Collections.synchronizedList(new ArrayList<Dependency>());
|
||||
/**
|
||||
* A Map of analyzers grouped by Analysis phase.
|
||||
*/
|
||||
private EnumMap<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.
|
||||
*/
|
||||
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();
|
||||
/**
|
||||
@@ -78,7 +91,8 @@ public class Engine implements FileFilter {
|
||||
/**
|
||||
* 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 {
|
||||
initializeEngine();
|
||||
@@ -88,7 +102,8 @@ public class Engine implements FileFilter {
|
||||
* Creates a new Engine.
|
||||
*
|
||||
* @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 {
|
||||
this.serviceClassLoader = serviceClassLoader;
|
||||
@@ -96,9 +111,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 {
|
||||
ConnectionFactory.initialize();
|
||||
@@ -113,7 +130,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() {
|
||||
if (!analyzers.isEmpty()) {
|
||||
@@ -124,9 +142,8 @@ public class Engine implements FileFilter {
|
||||
}
|
||||
|
||||
final AnalyzerService service = new AnalyzerService(serviceClassLoader);
|
||||
final Iterator<Analyzer> iterator = service.getAnalyzers();
|
||||
while (iterator.hasNext()) {
|
||||
final Analyzer a = iterator.next();
|
||||
final List<Analyzer> iterator = service.getAnalyzers();
|
||||
for (Analyzer a : iterator) {
|
||||
analyzers.get(a.getAnalysisPhase()).add(a);
|
||||
if (a instanceof FileTypeAnalyzer) {
|
||||
this.fileTypeAnalyzers.add((FileTypeAnalyzer) a);
|
||||
@@ -145,9 +162,14 @@ public class Engine implements FileFilter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the dependencies identified.
|
||||
* Get the dependencies identified. The returned list is a reference to the
|
||||
* engine's synchronized list. You must synchronize on it, when you modify
|
||||
* and iterate over it from multiple threads. E.g. this holds for analyzers
|
||||
* supporting parallel processing during their analysis phase.
|
||||
*
|
||||
* @return the dependencies identified
|
||||
* @see Collections#synchronizedList(List)
|
||||
* @see Analyzer#supportsParallelProcessing()
|
||||
*/
|
||||
public List<Dependency> getDependencies() {
|
||||
return dependencies;
|
||||
@@ -159,22 +181,40 @@ public class Engine implements FileFilter {
|
||||
* @param dependencies the dependencies
|
||||
*/
|
||||
public void setDependencies(List<Dependency> dependencies) {
|
||||
this.dependencies = dependencies;
|
||||
synchronized (this.dependencies) {
|
||||
this.dependencies.clear();
|
||||
this.dependencies.addAll(dependencies);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans an array of files or directories. If a directory is specified, it will be scanned recursively. Any dependencies
|
||||
* identified are added to the dependency collection.
|
||||
* Scans an array of files or directories. If a directory is specified, it
|
||||
* 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
|
||||
* @return the list of dependencies scanned
|
||||
* @since v0.3.2.5
|
||||
*/
|
||||
public List<Dependency> scan(String[] paths) {
|
||||
return scan(paths, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans an array of files or directories. If a directory is specified, it
|
||||
* 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 projectReference the name of the project or scope in which the
|
||||
* dependency was identified
|
||||
* @return the list of dependencies scanned
|
||||
* @since v1.4.4
|
||||
*/
|
||||
public List<Dependency> scan(String[] paths, String projectReference) {
|
||||
final List<Dependency> deps = new ArrayList<Dependency>();
|
||||
for (String path : paths) {
|
||||
final File file = new File(path);
|
||||
final List<Dependency> d = scan(file);
|
||||
final List<Dependency> d = scan(path, projectReference);
|
||||
if (d != null) {
|
||||
deps.addAll(d);
|
||||
}
|
||||
@@ -183,29 +223,61 @@ public class Engine implements FileFilter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans a given file or directory. If a directory is specified, it will be scanned recursively. Any dependencies identified
|
||||
* are added to the dependency collection.
|
||||
* Scans a given file or directory. If a directory is specified, it will be
|
||||
* scanned recursively. Any dependencies identified are added to the
|
||||
* dependency collection.
|
||||
*
|
||||
* @param path the path to a file or directory to be analyzed
|
||||
* @return the list of dependencies scanned
|
||||
*/
|
||||
public List<Dependency> scan(String path) {
|
||||
final File file = new File(path);
|
||||
return scan(file);
|
||||
return scan(path, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans an array of files or directories. If a directory is specified, it will be scanned recursively. Any dependencies
|
||||
* identified are added to the dependency collection.
|
||||
* Scans a given file or directory. If a directory is specified, it will be
|
||||
* scanned recursively. Any dependencies identified are added to the
|
||||
* dependency collection.
|
||||
*
|
||||
* @param path the path to a file or directory to be analyzed
|
||||
* @param projectReference the name of the project or scope in which the
|
||||
* dependency was identified
|
||||
* @return the list of dependencies scanned
|
||||
* @since v1.4.4
|
||||
*/
|
||||
public List<Dependency> scan(String path, String projectReference) {
|
||||
final File file = new File(path);
|
||||
return scan(file, projectReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans an array of files or directories. If a directory is specified, it
|
||||
* 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.
|
||||
* @return the list of dependencies
|
||||
* @since v0.3.2.5
|
||||
*/
|
||||
public List<Dependency> scan(File[] files) {
|
||||
return scan(files, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans an array of files or directories. If a directory is specified, it
|
||||
* 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 projectReference the name of the project or scope in which the
|
||||
* dependency was identified
|
||||
* @return the list of dependencies
|
||||
* @since v1.4.4
|
||||
*/
|
||||
public List<Dependency> scan(File[] files, String projectReference) {
|
||||
final List<Dependency> deps = new ArrayList<Dependency>();
|
||||
for (File file : files) {
|
||||
final List<Dependency> d = scan(file);
|
||||
final List<Dependency> d = scan(file, projectReference);
|
||||
if (d != null) {
|
||||
deps.addAll(d);
|
||||
}
|
||||
@@ -214,36 +286,33 @@ public class Engine implements FileFilter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans a list of files or directories. If a directory is specified, it will be scanned recursively. Any dependencies
|
||||
* identified are added to the dependency collection.
|
||||
* Scans a collection of files or directories. If a directory is specified,
|
||||
* 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
|
||||
* @return the list of dependencies scanned
|
||||
* @since v0.3.2.5
|
||||
*/
|
||||
public List<Dependency> scan(Set<File> files) {
|
||||
final List<Dependency> deps = new ArrayList<Dependency>();
|
||||
for (File file : files) {
|
||||
final List<Dependency> d = scan(file);
|
||||
if (d != null) {
|
||||
deps.addAll(d);
|
||||
}
|
||||
}
|
||||
return deps;
|
||||
public List<Dependency> scan(Collection<File> files) {
|
||||
return scan(files, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans a list of files or directories. If a directory is specified, it will be scanned recursively. Any dependencies
|
||||
* identified are added to the dependency collection.
|
||||
* Scans a collection of files or directories. If a directory is specified,
|
||||
* 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 projectReference the name of the project or scope in which the
|
||||
* dependency was identified
|
||||
* @return the list of dependencies scanned
|
||||
* @since v0.3.2.5
|
||||
* @since v1.4.4
|
||||
*/
|
||||
public List<Dependency> scan(List<File> files) {
|
||||
public List<Dependency> scan(Collection<File> files, String projectReference) {
|
||||
final List<Dependency> deps = new ArrayList<Dependency>();
|
||||
for (File file : files) {
|
||||
final List<Dependency> d = scan(file);
|
||||
final List<Dependency> d = scan(file, projectReference);
|
||||
if (d != null) {
|
||||
deps.addAll(d);
|
||||
}
|
||||
@@ -252,19 +321,35 @@ public class Engine implements FileFilter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans a given file or directory. If a directory is specified, it will be scanned recursively. Any dependencies identified
|
||||
* are added to the dependency collection.
|
||||
* Scans a given file or directory. If a directory is specified, it will be
|
||||
* scanned recursively. Any dependencies identified are added to the
|
||||
* dependency collection.
|
||||
*
|
||||
* @param file the path to a file or directory to be analyzed
|
||||
* @return the list of dependencies scanned
|
||||
* @since v0.3.2.4
|
||||
*/
|
||||
public List<Dependency> scan(File file) {
|
||||
return scan(file, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans a given file or directory. If a directory is specified, it will be
|
||||
* scanned recursively. Any dependencies identified are added to the
|
||||
* dependency collection.
|
||||
*
|
||||
* @param file the path to a file or directory to be analyzed
|
||||
* @param projectReference the name of the project or scope in which the
|
||||
* dependency was identified
|
||||
* @return the list of dependencies scanned
|
||||
* @since v1.4.4
|
||||
*/
|
||||
public List<Dependency> scan(File file, String projectReference) {
|
||||
if (file.exists()) {
|
||||
if (file.isDirectory()) {
|
||||
return scanDirectory(file);
|
||||
return scanDirectory(file, projectReference);
|
||||
} else {
|
||||
final Dependency d = scanFile(file);
|
||||
final Dependency d = scanFile(file, projectReference);
|
||||
if (d != null) {
|
||||
final List<Dependency> deps = new ArrayList<Dependency>();
|
||||
deps.add(d);
|
||||
@@ -276,23 +361,38 @@ 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
|
||||
* @return the list of Dependency objects scanned
|
||||
*/
|
||||
protected List<Dependency> scanDirectory(File dir) {
|
||||
return scanDirectory(dir, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively scans files and directories. Any dependencies identified are
|
||||
* added to the dependency collection.
|
||||
*
|
||||
* @param dir the directory to scan
|
||||
* @param projectReference the name of the project or scope in which the
|
||||
* dependency was identified
|
||||
* @return the list of Dependency objects scanned
|
||||
* @since v1.4.4
|
||||
*/
|
||||
protected List<Dependency> scanDirectory(File dir, String projectReference) {
|
||||
final File[] files = dir.listFiles();
|
||||
final List<Dependency> deps = new ArrayList<Dependency>();
|
||||
if (files != null) {
|
||||
for (File f : files) {
|
||||
if (f.isDirectory()) {
|
||||
final List<Dependency> d = scanDirectory(f);
|
||||
final List<Dependency> d = scanDirectory(f, projectReference);
|
||||
if (d != null) {
|
||||
deps.addAll(d);
|
||||
}
|
||||
} else {
|
||||
final Dependency d = scanFile(f);
|
||||
final Dependency d = scanFile(f, projectReference);
|
||||
deps.add(d);
|
||||
}
|
||||
}
|
||||
@@ -301,92 +401,131 @@ 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
|
||||
* @return the scanned dependency
|
||||
*/
|
||||
protected Dependency scanFile(File file) {
|
||||
return scanFile(file, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans a specified file. If a dependency is identified it is added to the
|
||||
* dependency collection.
|
||||
*
|
||||
* @param file The file to scan
|
||||
* @param projectReference the name of the project or scope in which the
|
||||
* dependency was identified
|
||||
* @return the scanned dependency
|
||||
* @since v1.4.4
|
||||
*/
|
||||
protected Dependency scanFile(File file, String projectReference) {
|
||||
Dependency dependency = null;
|
||||
if (file.isFile()) {
|
||||
if (accept(file)) {
|
||||
dependency = new Dependency(file);
|
||||
dependencies.add(dependency);
|
||||
if (projectReference != null) {
|
||||
dependency.addProjectReference(projectReference);
|
||||
}
|
||||
final String sha1 = dependency.getSha1sum();
|
||||
boolean found = false;
|
||||
synchronized (dependencies) {
|
||||
if (sha1 != null) {
|
||||
for (Dependency existing : dependencies) {
|
||||
if (sha1.equals(existing.getSha1sum())) {
|
||||
found = true;
|
||||
if (projectReference != null) {
|
||||
existing.addProjectReference(projectReference);
|
||||
}
|
||||
if (existing.getActualFilePath() != null && dependency.getActualFilePath() != null
|
||||
&& !existing.getActualFilePath().equals(dependency.getActualFilePath())) {
|
||||
existing.addRelatedDependency(dependency);
|
||||
} else {
|
||||
dependency = existing;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
dependencies.add(dependency);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOGGER.debug("Path passed to scanFile(File) is not a file: {}. Skipping the file.", file);
|
||||
}
|
||||
} else {
|
||||
LOGGER.debug("Path passed to scanFile(File) is not a file: {}. Skipping the file.", file);
|
||||
}
|
||||
return dependency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the analyzers against all of the dependencies. Since the mutable dependencies list is exposed via
|
||||
* {@link #getDependencies()}, this method iterates over a copy of the dependencies list. Thus, the potential for
|
||||
* {@link java.util.ConcurrentModificationException}s is avoided, and analyzers may safely add or remove entries from the
|
||||
* dependencies list.
|
||||
* Runs the analyzers against all of the dependencies. Since the mutable
|
||||
* dependencies list is exposed via {@link #getDependencies()}, this method
|
||||
* iterates over a copy of the dependencies list. Thus, the potential for
|
||||
* {@link java.util.ConcurrentModificationException}s is avoided, and
|
||||
* analyzers may safely add or remove entries from the dependencies list.
|
||||
* <p>
|
||||
* 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 = Collections.synchronizedList(new ArrayList<Throwable>());
|
||||
boolean autoUpdate = true;
|
||||
try {
|
||||
autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE);
|
||||
} catch (InvalidSettingException ex) {
|
||||
LOGGER.debug("Invalid setting for auto-update; using true.");
|
||||
exceptions.add(ex);
|
||||
}
|
||||
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
|
||||
try {
|
||||
ensureDataExists();
|
||||
} catch (NoDataException ex) {
|
||||
LOGGER.error("{}\n\nUnable to continue dependency-check analysis.", ex.getMessage());
|
||||
LOGGER.debug("", ex);
|
||||
return;
|
||||
throwFatalExceptionCollection("Unable to continue dependency-check analysis.", ex, exceptions);
|
||||
} catch (DatabaseException ex) {
|
||||
LOGGER.error("{}\n\nUnable to continue dependency-check analysis.", ex.getMessage());
|
||||
LOGGER.debug("", ex);
|
||||
return;
|
||||
|
||||
throwFatalExceptionCollection("Unable to connect to the dependency-check database.", ex, exceptions);
|
||||
}
|
||||
|
||||
LOGGER.debug("\n----------------------------------------------------\nBEGIN ANALYSIS\n----------------------------------------------------");
|
||||
LOGGER.info("Analysis Starting");
|
||||
LOGGER.info("Analysis Started");
|
||||
final long analysisStart = System.currentTimeMillis();
|
||||
|
||||
// analysis phases
|
||||
for (AnalysisPhase phase : AnalysisPhase.values()) {
|
||||
final List<Analyzer> analyzerList = analyzers.get(phase);
|
||||
|
||||
for (Analyzer a : analyzerList) {
|
||||
a = initializeAnalyzer(a);
|
||||
|
||||
/* need to create a copy of the collection because some of the
|
||||
* analyzers may modify it. This prevents ConcurrentModificationExceptions.
|
||||
* This is okay for adds/deletes because it happens per analyzer.
|
||||
*/
|
||||
LOGGER.debug("Begin Analyzer '{}'", a.getName());
|
||||
final Set<Dependency> dependencySet = new HashSet<Dependency>();
|
||||
dependencySet.addAll(dependencies);
|
||||
for (Dependency d : dependencySet) {
|
||||
boolean shouldAnalyze = true;
|
||||
if (a instanceof FileTypeAnalyzer) {
|
||||
final FileTypeAnalyzer fAnalyzer = (FileTypeAnalyzer) a;
|
||||
shouldAnalyze = fAnalyzer.accept(d.getActualFile());
|
||||
}
|
||||
if (shouldAnalyze) {
|
||||
LOGGER.debug("Begin Analysis of '{}'", d.getActualFilePath());
|
||||
try {
|
||||
a.analyze(d, this);
|
||||
} catch (AnalysisException ex) {
|
||||
LOGGER.warn("An error occurred while analyzing '{}'.", d.getActualFilePath());
|
||||
LOGGER.debug("", ex);
|
||||
} catch (Throwable ex) {
|
||||
//final AnalysisException ax = new AnalysisException(axMsg, ex);
|
||||
LOGGER.warn("An unexpected error occurred during analysis of '{}'", d.getActualFilePath());
|
||||
LOGGER.debug("", ex);
|
||||
}
|
||||
}
|
||||
for (final Analyzer analyzer : analyzerList) {
|
||||
final long analyzerStart = System.currentTimeMillis();
|
||||
try {
|
||||
initializeAnalyzer(analyzer);
|
||||
} catch (InitializationException ex) {
|
||||
exceptions.add(ex);
|
||||
continue;
|
||||
}
|
||||
|
||||
executeAnalysisTasks(analyzer, exceptions);
|
||||
|
||||
final long analyzerDurationMillis = System.currentTimeMillis() - analyzerStart;
|
||||
final long analyzerDurationSeconds = TimeUnit.MILLISECONDS.toSeconds(analyzerDurationMillis);
|
||||
LOGGER.info("Finished {} ({} seconds)", analyzer.getName(), analyzerDurationSeconds);
|
||||
}
|
||||
}
|
||||
for (AnalysisPhase phase : AnalysisPhase.values()) {
|
||||
@@ -398,7 +537,81 @@ public class Engine implements FileFilter {
|
||||
}
|
||||
|
||||
LOGGER.debug("\n----------------------------------------------------\nEND ANALYSIS\n----------------------------------------------------");
|
||||
LOGGER.info("Analysis Complete");
|
||||
final long analysisDurationSeconds = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - analysisStart);
|
||||
LOGGER.info("Analysis Complete ({} seconds)", analysisDurationSeconds);
|
||||
if (exceptions.size() > 0) {
|
||||
throw new ExceptionCollection("One or more exceptions occurred during dependency-check analysis", exceptions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes executes the analyzer using multiple threads.
|
||||
*
|
||||
* @param exceptions a collection of exceptions that occurred during
|
||||
* analysis
|
||||
* @param analyzer the analyzer to execute
|
||||
* @throws ExceptionCollection thrown if exceptions occurred during analysis
|
||||
*/
|
||||
void executeAnalysisTasks(Analyzer analyzer, List<Throwable> exceptions) throws ExceptionCollection {
|
||||
LOGGER.debug("Starting {}", analyzer.getName());
|
||||
final List<AnalysisTask> analysisTasks = getAnalysisTasks(analyzer, exceptions);
|
||||
final ExecutorService executorService = getExecutorService(analyzer);
|
||||
|
||||
try {
|
||||
final List<Future<Void>> results = executorService.invokeAll(analysisTasks, 10, TimeUnit.MINUTES);
|
||||
|
||||
// ensure there was no exception during execution
|
||||
for (Future<Void> result : results) {
|
||||
try {
|
||||
result.get();
|
||||
} catch (ExecutionException e) {
|
||||
throwFatalExceptionCollection("Analysis task failed with a fatal exception.", e, exceptions);
|
||||
} catch (CancellationException e) {
|
||||
throwFatalExceptionCollection("Analysis task timed out.", e, exceptions);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throwFatalExceptionCollection("Analysis has been interrupted.", e, exceptions);
|
||||
} finally {
|
||||
executorService.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the analysis tasks for the dependencies.
|
||||
*
|
||||
* @param analyzer the analyzer to create tasks for
|
||||
* @param exceptions the collection of exceptions to collect
|
||||
* @return a collection of analysis tasks
|
||||
*/
|
||||
List<AnalysisTask> getAnalysisTasks(Analyzer analyzer, List<Throwable> exceptions) {
|
||||
final List<AnalysisTask> result = new ArrayList<AnalysisTask>();
|
||||
synchronized (dependencies) {
|
||||
for (final Dependency dependency : dependencies) {
|
||||
final AnalysisTask task = new AnalysisTask(analyzer, dependency, this, exceptions);
|
||||
result.add(task);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the executor service for a given analyzer.
|
||||
*
|
||||
* @param analyzer the analyzer to obtain an executor
|
||||
* @return the executor service
|
||||
*/
|
||||
ExecutorService getExecutorService(Analyzer analyzer) {
|
||||
if (analyzer.supportsParallelProcessing()) {
|
||||
// just a fair trade-off that should be reasonable for all analyzer types
|
||||
final int maximumNumberOfThreads = 4 * Runtime.getRuntime().availableProcessors();
|
||||
|
||||
LOGGER.debug("Parallel processing with up to {} threads: {}.", maximumNumberOfThreads, analyzer.getName());
|
||||
return Executors.newFixedThreadPool(maximumNumberOfThreads);
|
||||
} else {
|
||||
LOGGER.debug("Parallel processing is not supported: {}.", analyzer.getName());
|
||||
return Executors.newSingleThreadExecutor();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -406,12 +619,14 @@ public class Engine implements FileFilter {
|
||||
*
|
||||
* @param analyzer the analyzer to initialize
|
||||
* @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 {
|
||||
LOGGER.debug("Initializing {}", analyzer.getName());
|
||||
analyzer.initialize();
|
||||
} catch (Throwable ex) {
|
||||
} catch (InitializationException ex) {
|
||||
LOGGER.error("Exception occurred initializing {}.", analyzer.getName());
|
||||
LOGGER.debug("", ex);
|
||||
try {
|
||||
@@ -419,6 +634,16 @@ public class Engine implements FileFilter {
|
||||
} catch (Throwable 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;
|
||||
}
|
||||
@@ -438,27 +663,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");
|
||||
final long updateStart = System.currentTimeMillis();
|
||||
final UpdateService service = new UpdateService(serviceClassLoader);
|
||||
final Iterator<CachedWebDataSource> iterator = service.getDataSources();
|
||||
while (iterator.hasNext()) {
|
||||
final CachedWebDataSource source = iterator.next();
|
||||
try {
|
||||
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);
|
||||
}
|
||||
source.update();
|
||||
}
|
||||
LOGGER.info("Check for updates complete");
|
||||
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
|
||||
*/
|
||||
@@ -475,8 +699,10 @@ public class Engine implements FileFilter {
|
||||
* Checks all analyzers to see if an extension is supported.
|
||||
*
|
||||
* @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
|
||||
public boolean accept(File file) {
|
||||
if (file == null) {
|
||||
return false;
|
||||
@@ -500,10 +726,22 @@ public class Engine implements FileFilter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the CPE Index to ensure documents exists. If none exist a NoDataException is thrown.
|
||||
* Adds a file type analyzer. This has been added solely to assist in unit
|
||||
* testing the Engine.
|
||||
*
|
||||
* @param fta the file type analyzer to add
|
||||
*/
|
||||
protected void addFileTypeAnalyzer(FileTypeAnalyzer fta) {
|
||||
this.fileTypeAnalyzers.add(fta);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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 {
|
||||
final CveDB cve = new CveDB();
|
||||
@@ -518,4 +756,20 @@ public class Engine implements FileFilter {
|
||||
cve.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs and throws a fatal exception collection.
|
||||
*
|
||||
* @param message the exception message
|
||||
* @param throwable the cause
|
||||
* @param exceptions a collection of exception to include
|
||||
* @throws ExceptionCollection a collection of exceptions that occurred
|
||||
* during analysis
|
||||
*/
|
||||
private void throwFatalExceptionCollection(String message, Throwable throwable, List<Throwable> exceptions) throws ExceptionCollection {
|
||||
LOGGER.error("{}\n\n{}", throwable.getMessage(), message);
|
||||
LOGGER.debug("", throwable);
|
||||
exceptions.add(throwable);
|
||||
throw new ExceptionCollection(message, exceptions, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.dependency.Identifier;
|
||||
import org.owasp.dependencycheck.dependency.Vulnerability;
|
||||
import org.owasp.dependencycheck.exception.ExceptionCollection;
|
||||
import org.owasp.dependencycheck.exception.ScanAgentException;
|
||||
import org.owasp.dependencycheck.reporting.ReportGenerator;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
@@ -34,14 +35,16 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This class provides a way to easily conduct a scan solely based on existing evidence metadata rather than collecting evidence
|
||||
* from the files themselves. This class is based on the Ant task and Maven plugin with the exception that 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.
|
||||
* This class provides a way to easily conduct a scan solely based on existing
|
||||
* evidence metadata rather than collecting evidence from the files themselves.
|
||||
* This class is based on the Ant task and Maven plugin with the exception that
|
||||
* 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>
|
||||
* <pre>
|
||||
* List<Dependency> dependencies = new ArrayList<Dependency>();
|
||||
* List<Dependency> dependencies = new ArrayList<Dependency>();
|
||||
* Dependency dependency = new Dependency(new File(FileUtils.getBitBucket()));
|
||||
* dependency.getProductEvidence().addEvidence("my-datasource", "name", "Jetty", Confidence.HIGH);
|
||||
* dependency.getVersionEvidence().addEvidence("my-datasource", "version", "5.1.10", Confidence.HIGH);
|
||||
@@ -55,7 +58,7 @@ import org.slf4j.LoggerFactory;
|
||||
* scan.execute();
|
||||
* </pre>
|
||||
*
|
||||
* @author Steve Springett <steve.springett@owasp.org>
|
||||
* @author Steve Springett
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
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;
|
||||
|
||||
@@ -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
|
||||
* 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
|
||||
* for the fail build on CVSS is 0 to 11, where anything above 10 will not cause the build to fail.
|
||||
* 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 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;
|
||||
|
||||
@@ -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
|
||||
* is true.
|
||||
* Sets whether auto-updating of the NVD CVE/CPE data is enabled. It is not
|
||||
* recommended that this be turned to false. Default is 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
|
||||
* Site plugin unless the externalReport is set to true. Default is HTML.
|
||||
* 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. Default is HTML.
|
||||
*/
|
||||
private ReportGenerator.Format reportFormat = ReportGenerator.Format.HTML;
|
||||
|
||||
@@ -283,7 +290,9 @@ public class DependencyCheckScanAgent {
|
||||
* Get 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
|
||||
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
|
||||
* files.
|
||||
* Additional ZIP File extensions to add analyze. This should be a
|
||||
* comma-separated list of file extensions to treat like ZIP files.
|
||||
*/
|
||||
private String zipExtensions;
|
||||
|
||||
@@ -836,12 +845,17 @@ public class DependencyCheckScanAgent {
|
||||
* Executes the Dependency-Check on the dependent libraries.
|
||||
*
|
||||
* @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();
|
||||
Engine engine = null;
|
||||
engine = new Engine();
|
||||
final Engine engine;
|
||||
try {
|
||||
engine = new Engine();
|
||||
} catch (DatabaseException ex) {
|
||||
throw new ExceptionCollection(ex, true);
|
||||
}
|
||||
engine.setDependencies(this.dependencies);
|
||||
engine.analyzeDependencies();
|
||||
return engine;
|
||||
@@ -882,8 +896,9 @@ public class DependencyCheckScanAgent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the properties supplied and updates the dependency-check settings. Additionally, this sets the system properties
|
||||
* required to change the proxy server, port, and connection timeout.
|
||||
* Takes the properties supplied and updates the dependency-check settings.
|
||||
* Additionally, this sets the system properties required to change the
|
||||
* proxy server, port, and connection timeout.
|
||||
*/
|
||||
private void populateSettings() {
|
||||
Settings.initialize();
|
||||
@@ -898,74 +913,36 @@ public class DependencyCheckScanAgent {
|
||||
}
|
||||
|
||||
Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate);
|
||||
|
||||
if (proxyServer != null && !proxyServer.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.PROXY_SERVER, proxyServer);
|
||||
}
|
||||
if (proxyPort != null && !proxyPort.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.PROXY_PORT, proxyPort);
|
||||
}
|
||||
if (proxyUsername != null && !proxyUsername.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.PROXY_USERNAME, proxyUsername);
|
||||
}
|
||||
if (proxyPassword != null && !proxyPassword.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.PROXY_PASSWORD, proxyPassword);
|
||||
}
|
||||
if (connectionTimeout != null && !connectionTimeout.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout);
|
||||
}
|
||||
if (suppressionFile != null && !suppressionFile.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.SUPPRESSION_FILE, suppressionFile);
|
||||
}
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_SERVER, proxyServer);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PORT, proxyPort);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_USERNAME, proxyUsername);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PASSWORD, proxyPassword);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE, suppressionFile);
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, centralAnalyzerEnabled);
|
||||
if (centralUrl != null && !centralUrl.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.ANALYZER_CENTRAL_URL, centralUrl);
|
||||
}
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_CENTRAL_URL, centralUrl);
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, nexusAnalyzerEnabled);
|
||||
if (nexusUrl != null && !nexusUrl.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.ANALYZER_NEXUS_URL, nexusUrl);
|
||||
}
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_PROXY, nexusUsesProxy);
|
||||
if (databaseDriverName != null && !databaseDriverName.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.DB_DRIVER_NAME, databaseDriverName);
|
||||
}
|
||||
if (databaseDriverPath != null && !databaseDriverPath.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.DB_DRIVER_PATH, databaseDriverPath);
|
||||
}
|
||||
if (connectionString != null && !connectionString.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.DB_CONNECTION_STRING, connectionString);
|
||||
}
|
||||
if (databaseUser != null && !databaseUser.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.DB_USER, databaseUser);
|
||||
}
|
||||
if (databasePassword != null && !databasePassword.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.DB_PASSWORD, databasePassword);
|
||||
}
|
||||
if (zipExtensions != null && !zipExtensions.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS, zipExtensions);
|
||||
}
|
||||
if (cveUrl12Modified != null && !cveUrl12Modified.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.CVE_MODIFIED_12_URL, cveUrl12Modified);
|
||||
}
|
||||
if (cveUrl20Modified != null && !cveUrl20Modified.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.CVE_MODIFIED_20_URL, cveUrl20Modified);
|
||||
}
|
||||
if (cveUrl12Base != null && !cveUrl12Base.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.CVE_SCHEMA_1_2, cveUrl12Base);
|
||||
}
|
||||
if (cveUrl20Base != null && !cveUrl20Base.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.CVE_SCHEMA_2_0, cveUrl20Base);
|
||||
}
|
||||
if (pathToMono != null && !pathToMono.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, pathToMono);
|
||||
}
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_NEXUS_URL, nexusUrl);
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_USES_PROXY, nexusUsesProxy);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_NAME, databaseDriverName);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_PATH, databaseDriverPath);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.DB_CONNECTION_STRING, connectionString);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.DB_USER, databaseUser);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.DB_PASSWORD, databasePassword);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS, zipExtensions);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.CVE_MODIFIED_12_URL, cveUrl12Modified);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.CVE_MODIFIED_20_URL, cveUrl20Modified);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.CVE_SCHEMA_1_2, cveUrl12Base);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.CVE_SCHEMA_2_0, cveUrl20Base);
|
||||
Settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, pathToMono);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the dependency-check and generates the report.
|
||||
*
|
||||
* @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 {
|
||||
Engine engine = null;
|
||||
@@ -980,10 +957,12 @@ public class DependencyCheckScanAgent {
|
||||
if (this.failBuildOnCVSS <= 10) {
|
||||
checkForFailure(engine.getDependencies());
|
||||
}
|
||||
} catch (DatabaseException ex) {
|
||||
LOGGER.error(
|
||||
"Unable to connect to the dependency-check database; analysis has stopped");
|
||||
LOGGER.debug("", ex);
|
||||
} catch (ExceptionCollection ex) {
|
||||
if (ex.isFatal()) {
|
||||
LOGGER.error("A fatal exception occurred during analysis; analysis has stopped. Please see the debug log for more details.");
|
||||
LOGGER.debug("", ex);
|
||||
}
|
||||
throw new ScanAgentException("One or more exceptions occurred during analysis; please see the debug log for more details.", ex);
|
||||
} finally {
|
||||
Settings.cleanup(true);
|
||||
if (engine != null) {
|
||||
@@ -994,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
|
||||
* configuration.
|
||||
* Checks to see if a vulnerability has been identified with a CVSS score
|
||||
* that is above the threshold set in the configuration.
|
||||
*
|
||||
* @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 {
|
||||
final StringBuilder ids = new StringBuilder();
|
||||
@@ -1026,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
|
||||
*/
|
||||
|
||||
@@ -17,7 +17,11 @@
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@@ -26,10 +30,10 @@ public abstract class AbstractAnalyzer implements 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
|
||||
public void initialize() throws Exception {
|
||||
public void initialize() throws InitializationException {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@@ -42,4 +46,12 @@ public abstract class AbstractAnalyzer implements Analyzer {
|
||||
public void close() throws Exception {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* The default is to support parallel processing.
|
||||
*/
|
||||
@Override
|
||||
public boolean supportsParallelProcessing() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,9 +30,11 @@ import java.io.FileFilter;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
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
|
||||
*/
|
||||
@@ -40,7 +42,8 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen
|
||||
|
||||
//<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() {
|
||||
reset();
|
||||
@@ -58,7 +61,8 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen
|
||||
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
|
||||
*/
|
||||
@@ -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
|
||||
*/
|
||||
@@ -78,7 +83,7 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen
|
||||
/**
|
||||
* A flag indicating whether or not the analyzer is enabled.
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
private volatile boolean enabled = true;
|
||||
|
||||
/**
|
||||
* Get the value of enabled.
|
||||
@@ -102,27 +107,30 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen
|
||||
//<editor-fold defaultstate="collapsed" desc="Abstract methods children must implement">
|
||||
/**
|
||||
* <p>
|
||||
* Returns the {@link java.io.FileFilter} used to determine which files are to be analyzed. An example would be an analyzer
|
||||
* that inspected Java jar files. Implementors may use {@link org.owasp.dependencycheck.utils.FileFilterBuilder}.</p>
|
||||
* Returns the {@link java.io.FileFilter} used to determine which files are
|
||||
* 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
|
||||
* <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();
|
||||
|
||||
/**
|
||||
* 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,
|
||||
* and added to the list of dependencies within the engine.
|
||||
* Analyzes a given dependency. If the dependency is an archive, such as a
|
||||
* 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 engine the engine scanning
|
||||
@@ -143,10 +151,11 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen
|
||||
/**
|
||||
* Initializes the analyzer.
|
||||
*
|
||||
* @throws Exception thrown if there is an exception during initialization
|
||||
* @throws InitializationException thrown if there is an exception during
|
||||
* initialization
|
||||
*/
|
||||
@Override
|
||||
public final void initialize() throws Exception {
|
||||
public final void initialize() throws InitializationException {
|
||||
if (filesMatched) {
|
||||
initializeFileTypeAnalyzer();
|
||||
} 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,
|
||||
* and added to the list of dependencies within the engine.
|
||||
* Analyzes a given dependency. If the dependency is an archive, such as a
|
||||
* 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 engine the engine scanning
|
||||
@@ -203,9 +213,8 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen
|
||||
//<editor-fold defaultstate="collapsed" desc="Static utility methods">
|
||||
/**
|
||||
* <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
|
||||
* declaration.</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 declaration.</p>
|
||||
* <p>
|
||||
* This implementation was copied from
|
||||
* http://stackoverflow.com/questions/2041778/initialize-java-hashset-values-by-construction</p>
|
||||
@@ -214,7 +223,7 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen
|
||||
* @return a Set of strings.
|
||||
*/
|
||||
protected static Set<String> newHashSet(String... strings) {
|
||||
final Set<String> set = new HashSet<String>();
|
||||
final Set<String> set = new HashSet<String>(strings.length);
|
||||
Collections.addAll(set, strings);
|
||||
return set;
|
||||
}
|
||||
|
||||
@@ -25,18 +25,21 @@ import java.net.URL;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import org.owasp.dependencycheck.suppression.SuppressionParseException;
|
||||
import org.owasp.dependencycheck.suppression.SuppressionParser;
|
||||
import org.owasp.dependencycheck.suppression.SuppressionRule;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
import org.owasp.dependencycheck.xml.suppression.SuppressionParseException;
|
||||
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.Downloader;
|
||||
import org.owasp.dependencycheck.utils.FileUtils;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.slf4j.Logger;
|
||||
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
|
||||
*/
|
||||
@@ -61,12 +64,16 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
|
||||
/**
|
||||
* 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
|
||||
public void initialize() throws Exception {
|
||||
public void initialize() throws InitializationException {
|
||||
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;
|
||||
try {
|
||||
rules = parser.parseSuppressionRules(this.getClass().getClassLoader().getResourceAsStream("dependencycheck-base-suppression.xml"));
|
||||
} catch (SuppressionParseException ex) {
|
||||
LOGGER.debug("Unable to parse the base suppression data file", ex);
|
||||
} catch (SAXException ex) {
|
||||
throw new SuppressionParseException("Unable to parse the base suppression data file", ex);
|
||||
}
|
||||
final String suppressionFilePath = Settings.getString(Settings.KEYS.SUPPRESSION_FILE);
|
||||
if (suppressionFilePath == null) {
|
||||
@@ -123,29 +130,42 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
|
||||
}
|
||||
} else {
|
||||
file = new File(suppressionFilePath);
|
||||
InputStream suppressionsFromClasspath = null;
|
||||
if (!file.exists()) {
|
||||
final InputStream suppressionsFromClasspath = this.getClass().getClassLoader().getResourceAsStream(suppressionFilePath);
|
||||
if (suppressionsFromClasspath != null) {
|
||||
deleteTempFile = true;
|
||||
file = FileUtils.getTempFile("suppression", "xml");
|
||||
try {
|
||||
org.apache.commons.io.FileUtils.copyInputStreamToFile(suppressionsFromClasspath, file);
|
||||
} catch (IOException ex) {
|
||||
throwSuppressionParseException("Unable to locate suppressions file in classpath", ex);
|
||||
try {
|
||||
suppressionsFromClasspath = this.getClass().getClassLoader().getResourceAsStream(suppressionFilePath);
|
||||
if (suppressionsFromClasspath != null) {
|
||||
deleteTempFile = true;
|
||||
file = FileUtils.getTempFile("suppression", "xml");
|
||||
try {
|
||||
org.apache.commons.io.FileUtils.copyInputStreamToFile(suppressionsFromClasspath, file);
|
||||
} 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.exists()) {
|
||||
final String msg = String.format("Suppression file '%s' does not exists", file.getPath());
|
||||
LOGGER.warn(msg);
|
||||
throw new SuppressionParseException(msg);
|
||||
}
|
||||
try {
|
||||
//rules = parser.parseSuppressionRules(file);
|
||||
rules.addAll(parser.parseSuppressionRules(file));
|
||||
LOGGER.debug("{} suppression rules were loaded.", rules.size());
|
||||
} catch (SuppressionParseException ex) {
|
||||
LOGGER.warn("Unable to parse suppression xml file '{}'", file.getPath());
|
||||
LOGGER.warn(ex.getMessage());
|
||||
LOGGER.debug("", ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
@@ -153,6 +173,8 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
|
||||
throwSuppressionParseException("Unable to fetch the configured suppression file", ex);
|
||||
} catch (MalformedURLException ex) {
|
||||
throwSuppressionParseException("Configured suppression file has an invalid URL", ex);
|
||||
} catch (SuppressionParseException ex) {
|
||||
throw ex;
|
||||
} catch (IOException ex) {
|
||||
throwSuppressionParseException("Unable to create temp file for suppressions", ex);
|
||||
} finally {
|
||||
@@ -167,7 +189,8 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
|
||||
*
|
||||
* @param message the exception message
|
||||
* @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 {
|
||||
LOGGER.warn(message);
|
||||
|
||||
@@ -28,6 +28,10 @@ public enum AnalysisPhase {
|
||||
* Initialization phase.
|
||||
*/
|
||||
INITIAL,
|
||||
/**
|
||||
* Pre information collection phase.
|
||||
*/
|
||||
PRE_INFORMATION_COLLECTION,
|
||||
/**
|
||||
* Information collection phase.
|
||||
*/
|
||||
|
||||
@@ -20,24 +20,28 @@ package org.owasp.dependencycheck.analyzer;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
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
|
||||
* about the dependency in the form of Evidence.
|
||||
* An interface that defines an Analyzer that is used to identify Dependencies.
|
||||
* An analyzer will collect information about the dependency in the form of
|
||||
* Evidence.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
public interface Analyzer {
|
||||
|
||||
/**
|
||||
* Analyzes the given dependency. The analysis could be anything from identifying an Identifier for the dependency,
|
||||
* to finding vulnerabilities, etc. Additionally, if the analyzer collects enough information to add a description
|
||||
* or license information for the dependency it should be added.
|
||||
* Analyzes the given dependency. The analysis could be anything from
|
||||
* identifying an Identifier for the dependency, to finding vulnerabilities,
|
||||
* 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 engine the engine that is scanning the dependencies - this is useful if we need to check other
|
||||
* dependencies
|
||||
* @throws AnalysisException is thrown if there is an error analyzing the dependency file
|
||||
* @param engine the engine that is scanning the dependencies - this is
|
||||
* useful if we need to check other dependencies
|
||||
* @throws AnalysisException is thrown if there is an error analyzing the
|
||||
* dependency file
|
||||
*/
|
||||
void analyze(Dependency dependency, Engine engine) throws AnalysisException;
|
||||
|
||||
@@ -56,16 +60,27 @@ public interface Analyzer {
|
||||
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.
|
||||
*/
|
||||
void close() throws Exception;
|
||||
|
||||
/**
|
||||
* Returns whether multiple instances of the same type of analyzer can run in parallel.
|
||||
* Note that running analyzers of different types in parallel is not supported at all.
|
||||
*
|
||||
* @return {@code true} if the analyzer supports parallel processing, {@code false} else
|
||||
*/
|
||||
boolean supportsParallelProcessing();
|
||||
}
|
||||
|
||||
@@ -17,8 +17,13 @@
|
||||
*/
|
||||
package org.owasp.dependencycheck.analyzer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
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
|
||||
@@ -27,11 +32,15 @@ import java.util.ServiceLoader;
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
private final ServiceLoader<Analyzer> loader;
|
||||
private final ServiceLoader<Analyzer> service;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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() {
|
||||
return loader.iterator();
|
||||
public List<Analyzer> getAnalyzers() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,18 +18,15 @@
|
||||
package org.owasp.dependencycheck.analyzer;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -40,22 +37,28 @@ import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import org.apache.commons.compress.compressors.CompressorInputStream;
|
||||
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
|
||||
import org.apache.commons.compress.compressors.bzip2.BZip2Utils;
|
||||
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
|
||||
import org.apache.commons.compress.compressors.gzip.GzipUtils;
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
import org.owasp.dependencycheck.analyzer.exception.ArchiveExtractionException;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
import org.owasp.dependencycheck.utils.FileFilterBuilder;
|
||||
import org.owasp.dependencycheck.utils.FileUtils;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* An analyzer that extracts files from archives and ensures any supported files contained within the archive are added to the
|
||||
* dependency list.</p>
|
||||
* An analyzer that extracts files from archives and ensures any supported files
|
||||
* contained within the archive are added to the dependency list.</p>
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
@@ -66,11 +69,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
*/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ArchiveAnalyzer.class);
|
||||
/**
|
||||
* The buffer size to use when extracting files from the archive.
|
||||
*/
|
||||
private static final int BUFFER_SIZE = 4096;
|
||||
/**
|
||||
* 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;
|
||||
/**
|
||||
@@ -78,7 +78,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
*/
|
||||
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);
|
||||
/**
|
||||
@@ -100,21 +101,24 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
*/
|
||||
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
|
||||
* explicitly handled in extractFiles().
|
||||
* The set of file extensions supported by this analyzer. Note for
|
||||
* 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");
|
||||
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").build();
|
||||
private static final FileFilter REMOVE_FROM_ANALYSIS = FileFilterBuilder.newInstance().addExtensions("zip", "tar", "gz", "tgz", "bz2", "tbz2")
|
||||
.build();
|
||||
|
||||
static {
|
||||
final String additionalZipExt = Settings.getString(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS);
|
||||
if (additionalZipExt != null) {
|
||||
final Set<String> ext = new HashSet<String>(Arrays.asList(additionalZipExt));
|
||||
ZIPPABLES.addAll(ext);
|
||||
final String[] ext = additionalZipExt.split("\\s*,\\s*");
|
||||
Collections.addAll(ZIPPABLES, ext);
|
||||
}
|
||||
EXTENSIONS.addAll(ZIPPABLES);
|
||||
}
|
||||
@@ -156,7 +160,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
//</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
|
||||
*/
|
||||
@@ -168,41 +173,67 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
/**
|
||||
* 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
|
||||
public void initializeFileTypeAnalyzer() throws Exception {
|
||||
final File baseDir = Settings.getTempDirectory();
|
||||
tempFileLocation = File.createTempFile("check", "tmp", baseDir);
|
||||
if (!tempFileLocation.delete()) {
|
||||
final String msg = String.format("Unable to delete temporary file '%s'.", tempFileLocation.getAbsolutePath());
|
||||
throw new AnalysisException(msg);
|
||||
}
|
||||
if (!tempFileLocation.mkdirs()) {
|
||||
final String msg = String.format("Unable to create directory '%s'.", tempFileLocation.getAbsolutePath());
|
||||
throw new AnalysisException(msg);
|
||||
public void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
try {
|
||||
final File baseDir = Settings.getTempDirectory();
|
||||
tempFileLocation = File.createTempFile("check", "tmp", baseDir);
|
||||
if (!tempFileLocation.delete()) {
|
||||
setEnabled(false);
|
||||
final String msg = String.format("Unable to delete temporary file '%s'.", tempFileLocation.getAbsolutePath());
|
||||
throw new InitializationException(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
|
||||
public void close() throws Exception {
|
||||
if (tempFileLocation != null && tempFileLocation.exists()) {
|
||||
LOGGER.debug("Attempting to delete temporary files");
|
||||
final boolean success = FileUtils.delete(tempFileLocation);
|
||||
if (!success && tempFileLocation != null && tempFileLocation.exists() && tempFileLocation.list().length > 0) {
|
||||
LOGGER.warn("Failed to delete some temporary files, see the log for more details");
|
||||
if (!success && tempFileLocation.exists()) {
|
||||
final String[] l = tempFileLocation.list();
|
||||
if (l != null && l.length > 0) {
|
||||
LOGGER.warn("Failed to delete some temporary files, see the log for more details");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyzes a given dependency. If the dependency is an archive, such as a WAR or EAR, the contents are extracted, scanned,
|
||||
* and added to the list of dependencies within the engine.
|
||||
* Does not support parallel processing as it both modifies and iterates
|
||||
* over the engine's list of dependencies.
|
||||
*
|
||||
* @see #analyzeFileType(Dependency, Engine)
|
||||
* @see #findMoreDependencies(Engine, File)
|
||||
*/
|
||||
@Override
|
||||
public boolean supportsParallelProcessing() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyzes a given dependency. If the dependency is an archive, such as a
|
||||
* 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 engine the engine scanning
|
||||
@@ -215,71 +246,110 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
extractFiles(f, tmpDir, engine);
|
||||
|
||||
//make a copy
|
||||
List<Dependency> dependencies = new ArrayList<Dependency>(engine.getDependencies());
|
||||
engine.scan(tmpDir);
|
||||
List<Dependency> newDependencies = engine.getDependencies();
|
||||
if (dependencies.size() != newDependencies.size()) {
|
||||
//get the new dependencies
|
||||
final Set<Dependency> dependencySet = new HashSet<Dependency>();
|
||||
dependencySet.addAll(newDependencies);
|
||||
dependencySet.removeAll(dependencies);
|
||||
final List<Dependency> dependencySet = findMoreDependencies(engine, tmpDir);
|
||||
|
||||
if (!dependencySet.isEmpty()) {
|
||||
for (Dependency d : dependencySet) {
|
||||
//fix the dependency's display name and path
|
||||
final String displayPath = String.format("%s%s",
|
||||
dependency.getFilePath(),
|
||||
d.getActualFilePath().substring(tmpDir.getAbsolutePath().length()));
|
||||
final String displayName = String.format("%s: %s",
|
||||
dependency.getFileName(),
|
||||
d.getFileName());
|
||||
d.setFilePath(displayPath);
|
||||
d.setFileName(displayName);
|
||||
if (d.getFilePath().startsWith(tmpDir.getAbsolutePath())) {
|
||||
//fix the dependency's display name and path
|
||||
final String displayPath = String.format("%s%s",
|
||||
dependency.getFilePath(),
|
||||
d.getActualFilePath().substring(tmpDir.getAbsolutePath().length()));
|
||||
final String displayName = String.format("%s: %s",
|
||||
dependency.getFileName(),
|
||||
d.getFileName());
|
||||
d.setFilePath(displayPath);
|
||||
d.setFileName(displayName);
|
||||
d.setProjectReferences(dependency.getProjectReferences());
|
||||
|
||||
//TODO - can we get more evidence from the parent? EAR contains module name, etc.
|
||||
//analyze the dependency (i.e. extract files) if it is a supported type.
|
||||
if (this.accept(d.getActualFile()) && scanDepth < MAX_SCAN_DEPTH) {
|
||||
scanDepth += 1;
|
||||
analyze(d, engine);
|
||||
scanDepth -= 1;
|
||||
//TODO - can we get more evidence from the parent? EAR contains module name, etc.
|
||||
//analyze the dependency (i.e. extract files) if it is a supported type.
|
||||
if (this.accept(d.getActualFile()) && scanDepth < MAX_SCAN_DEPTH) {
|
||||
scanDepth += 1;
|
||||
analyze(d, engine);
|
||||
scanDepth -= 1;
|
||||
}
|
||||
} else {
|
||||
for (Dependency sub : dependencySet) {
|
||||
if (sub.getFilePath().startsWith(tmpDir.getAbsolutePath())) {
|
||||
final String displayPath = String.format("%s%s",
|
||||
dependency.getFilePath(),
|
||||
sub.getActualFilePath().substring(tmpDir.getAbsolutePath().length()));
|
||||
final String displayName = String.format("%s: %s",
|
||||
dependency.getFileName(),
|
||||
sub.getFileName());
|
||||
sub.setFilePath(displayPath);
|
||||
sub.setFileName(displayName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (REMOVE_FROM_ANALYSIS.accept(dependency.getActualFile())) {
|
||||
if (ZIP_FILTER.accept(dependency.getActualFile()) && isZipFileActuallyJarFile(dependency)) {
|
||||
final File tdir = getNextTempDirectory();
|
||||
final String fileName = dependency.getFileName();
|
||||
|
||||
LOGGER.info(String.format("The zip file '%s' appears to be a JAR file, making a copy and analyzing it as a JAR.", fileName));
|
||||
|
||||
final File tmpLoc = new File(tdir, fileName.substring(0, fileName.length() - 3) + "jar");
|
||||
try {
|
||||
org.apache.commons.io.FileUtils.copyFile(tdir, tmpLoc);
|
||||
dependencies = new ArrayList<Dependency>(engine.getDependencies());
|
||||
engine.scan(tmpLoc);
|
||||
newDependencies = engine.getDependencies();
|
||||
if (dependencies.size() != newDependencies.size()) {
|
||||
//get the new dependencies
|
||||
final Set<Dependency> dependencySet = new HashSet<Dependency>();
|
||||
dependencySet.addAll(newDependencies);
|
||||
dependencySet.removeAll(dependencies);
|
||||
if (dependencySet.size() != 1) {
|
||||
LOGGER.info("Deep copy of ZIP to JAR file resulted in more then one dependency?");
|
||||
}
|
||||
for (Dependency d : dependencySet) {
|
||||
//fix the dependency's display name and path
|
||||
d.setFilePath(dependency.getFilePath());
|
||||
d.setDisplayFileName(dependency.getFileName());
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
LOGGER.debug("Unable to perform deep copy on '{}'", dependency.getActualFile().getPath(), ex);
|
||||
}
|
||||
}
|
||||
addDisguisedJarsToDependencies(dependency, engine);
|
||||
engine.getDependencies().remove(dependency);
|
||||
}
|
||||
Collections.sort(engine.getDependencies());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 engine the engine
|
||||
* @throws AnalysisException thrown if there is an issue
|
||||
*/
|
||||
private void addDisguisedJarsToDependencies(Dependency dependency, Engine engine) throws AnalysisException {
|
||||
if (ZIP_FILTER.accept(dependency.getActualFile()) && isZipFileActuallyJarFile(dependency)) {
|
||||
final File tdir = getNextTempDirectory();
|
||||
final String fileName = dependency.getFileName();
|
||||
|
||||
LOGGER.info("The zip file '{}' appears to be a JAR file, making a copy and analyzing it as a JAR.", fileName);
|
||||
final File tmpLoc = new File(tdir, fileName.substring(0, fileName.length() - 3) + "jar");
|
||||
//store the archives sha1 and change it so that the engine doesn't think the zip and jar file are the same
|
||||
// and add it is a related dependency.
|
||||
final String archiveSha1 = dependency.getSha1sum();
|
||||
try {
|
||||
dependency.setSha1sum("");
|
||||
org.apache.commons.io.FileUtils.copyFile(dependency.getActualFile(), tmpLoc);
|
||||
final List<Dependency> dependencySet = findMoreDependencies(engine, tmpLoc);
|
||||
if (!dependencySet.isEmpty()) {
|
||||
for (Dependency d : dependencySet) {
|
||||
//fix the dependency's display name and path
|
||||
if (d.getActualFile().equals(tmpLoc)) {
|
||||
d.setFilePath(dependency.getFilePath());
|
||||
d.setDisplayFileName(dependency.getFileName());
|
||||
} else {
|
||||
for (Dependency sub : d.getRelatedDependencies()) {
|
||||
if (sub.getActualFile().equals(tmpLoc)) {
|
||||
sub.setFilePath(dependency.getFilePath());
|
||||
sub.setDisplayFileName(dependency.getFileName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
LOGGER.debug("Unable to perform deep copy on '{}'", dependency.getActualFile().getPath(), ex);
|
||||
} finally {
|
||||
dependency.setSha1sum(archiveSha1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the given file/folder, and return any new dependencies found.
|
||||
*
|
||||
* @param engine used to scan
|
||||
* @param file target of scanning
|
||||
* @return any dependencies that weren't known to the engine before
|
||||
*/
|
||||
private static List<Dependency> findMoreDependencies(Engine engine, File file) {
|
||||
final List<Dependency> added = engine.scan(file);
|
||||
return added;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the next temporary directory to extract an archive too.
|
||||
*
|
||||
@@ -309,41 +379,118 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
* @throws AnalysisException thrown if the archive is not found
|
||||
*/
|
||||
private void extractFiles(File archive, File destination, Engine engine) throws AnalysisException {
|
||||
if (archive == null || destination == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(archive);
|
||||
} catch (FileNotFoundException ex) {
|
||||
LOGGER.debug("", ex);
|
||||
throw new AnalysisException("Archive file was not found.", ex);
|
||||
}
|
||||
final String archiveExt = FileUtils.getFileExtension(archive.getName()).toLowerCase();
|
||||
try {
|
||||
if (ZIPPABLES.contains(archiveExt)) {
|
||||
extractArchive(new ZipArchiveInputStream(new BufferedInputStream(fis)), destination, engine);
|
||||
} else if ("tar".equals(archiveExt)) {
|
||||
extractArchive(new TarArchiveInputStream(new BufferedInputStream(fis)), destination, engine);
|
||||
} else if ("gz".equals(archiveExt) || "tgz".equals(archiveExt)) {
|
||||
final String uncompressedName = GzipUtils.getUncompressedFilename(archive.getName());
|
||||
final File f = new File(destination, uncompressedName);
|
||||
if (engine.accept(f)) {
|
||||
decompressFile(new GzipCompressorInputStream(new BufferedInputStream(fis)), f);
|
||||
}
|
||||
if (archive != null && destination != null) {
|
||||
String archiveExt = FileUtils.getFileExtension(archive.getName());
|
||||
if (archiveExt == null) {
|
||||
return;
|
||||
}
|
||||
} catch (ArchiveExtractionException ex) {
|
||||
LOGGER.warn("Exception extracting archive '{}'.", archive.getName());
|
||||
LOGGER.debug("", ex);
|
||||
} catch (IOException ex) {
|
||||
LOGGER.warn("Exception reading archive '{}'.", archive.getName());
|
||||
LOGGER.debug("", ex);
|
||||
} finally {
|
||||
archiveExt = archiveExt.toLowerCase();
|
||||
|
||||
final FileInputStream fis;
|
||||
try {
|
||||
fis.close();
|
||||
} catch (IOException ex) {
|
||||
fis = new FileInputStream(archive);
|
||||
} catch (FileNotFoundException ex) {
|
||||
LOGGER.debug("", ex);
|
||||
throw new AnalysisException("Archive file was not found.", ex);
|
||||
}
|
||||
BufferedInputStream in = null;
|
||||
ZipArchiveInputStream zin = null;
|
||||
TarArchiveInputStream tin = null;
|
||||
GzipCompressorInputStream gin = null;
|
||||
BZip2CompressorInputStream bzin = null;
|
||||
try {
|
||||
if (ZIPPABLES.contains(archiveExt)) {
|
||||
in = new BufferedInputStream(fis);
|
||||
ensureReadableJar(archiveExt, in);
|
||||
zin = new ZipArchiveInputStream(in);
|
||||
extractArchive(zin, destination, engine);
|
||||
} else if ("tar".equals(archiveExt)) {
|
||||
in = new BufferedInputStream(fis);
|
||||
tin = new TarArchiveInputStream(in);
|
||||
extractArchive(tin, destination, engine);
|
||||
} else if ("gz".equals(archiveExt) || "tgz".equals(archiveExt)) {
|
||||
final String uncompressedName = GzipUtils.getUncompressedFilename(archive.getName());
|
||||
final File f = new File(destination, uncompressedName);
|
||||
if (engine.accept(f)) {
|
||||
in = new BufferedInputStream(fis);
|
||||
gin = new GzipCompressorInputStream(in);
|
||||
decompressFile(gin, f);
|
||||
}
|
||||
} else if ("bz2".equals(archiveExt) || "tbz2".equals(archiveExt)) {
|
||||
final String uncompressedName = BZip2Utils.getUncompressedFilename(archive.getName());
|
||||
final File f = new File(destination, uncompressedName);
|
||||
if (engine.accept(f)) {
|
||||
in = new BufferedInputStream(fis);
|
||||
bzin = new BZip2CompressorInputStream(in);
|
||||
decompressFile(bzin, f);
|
||||
}
|
||||
}
|
||||
} catch (ArchiveExtractionException ex) {
|
||||
LOGGER.warn("Exception extracting archive '{}'.", archive.getName());
|
||||
LOGGER.debug("", ex);
|
||||
} catch (IOException ex) {
|
||||
LOGGER.warn("Exception reading archive '{}'.", archive.getName());
|
||||
LOGGER.debug("", ex);
|
||||
} finally {
|
||||
//overly verbose and not needed... but keeping it anyway due to
|
||||
//having issue with file handles being left open
|
||||
close(fis);
|
||||
close(in);
|
||||
close(zin);
|
||||
close(tin);
|
||||
close(gin);
|
||||
close(bzin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -354,81 +501,58 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
* @param input the archive to extract files from
|
||||
* @param destination the location to write the files too
|
||||
* @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 {
|
||||
ArchiveEntry entry;
|
||||
try {
|
||||
while ((entry = input.getNextEntry()) != null) {
|
||||
final File file = new File(destination, entry.getName());
|
||||
if (entry.isDirectory()) {
|
||||
final File d = new File(destination, entry.getName());
|
||||
if (!d.exists()) {
|
||||
if (!d.mkdirs()) {
|
||||
final String msg = String.format("Unable to create directory '%s'.", d.getAbsolutePath());
|
||||
throw new AnalysisException(msg);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
final File file = new File(destination, entry.getName());
|
||||
if (engine.accept(file)) {
|
||||
LOGGER.debug("Extracting '{}'", file.getPath());
|
||||
BufferedOutputStream bos = null;
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
final File parent = file.getParentFile();
|
||||
if (!parent.isDirectory()) {
|
||||
if (!parent.mkdirs()) {
|
||||
final String msg = String.format("Unable to build directory '%s'.", parent.getAbsolutePath());
|
||||
throw new AnalysisException(msg);
|
||||
}
|
||||
}
|
||||
fos = new FileOutputStream(file);
|
||||
bos = new BufferedOutputStream(fos, BUFFER_SIZE);
|
||||
int count;
|
||||
final byte[] data = new byte[BUFFER_SIZE];
|
||||
while ((count = input.read(data, 0, BUFFER_SIZE)) != -1) {
|
||||
bos.write(data, 0, count);
|
||||
}
|
||||
bos.flush();
|
||||
} catch (FileNotFoundException ex) {
|
||||
LOGGER.debug("", ex);
|
||||
final String msg = String.format("Unable to find file '%s'.", file.getName());
|
||||
throw new AnalysisException(msg, ex);
|
||||
} catch (IOException ex) {
|
||||
LOGGER.debug("", ex);
|
||||
final String msg = String.format("IO Exception while parsing file '%s'.", file.getName());
|
||||
throw new AnalysisException(msg, ex);
|
||||
} finally {
|
||||
if (bos != null) {
|
||||
try {
|
||||
bos.close();
|
||||
} catch (IOException ex) {
|
||||
LOGGER.trace("", ex);
|
||||
}
|
||||
}
|
||||
if (fos != null) {
|
||||
try {
|
||||
fos.close();
|
||||
} catch (IOException ex) {
|
||||
LOGGER.trace("", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!file.exists() && !file.mkdirs()) {
|
||||
final String msg = String.format("Unable to create directory '%s'.", file.getAbsolutePath());
|
||||
throw new AnalysisException(msg);
|
||||
}
|
||||
} else if (engine.accept(file)) {
|
||||
extractAcceptedFile(input, file);
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new ArchiveExtractionException(ex);
|
||||
} catch (Throwable ex) {
|
||||
throw new ArchiveExtractionException(ex);
|
||||
} finally {
|
||||
if (input != null) {
|
||||
try {
|
||||
input.close();
|
||||
} catch (IOException ex) {
|
||||
LOGGER.trace("", ex);
|
||||
}
|
||||
close(input);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a file from an archive.
|
||||
*
|
||||
* @param input the archives input stream
|
||||
* @param file the file to extract
|
||||
* @throws AnalysisException thrown if there is an error
|
||||
*/
|
||||
private static void extractAcceptedFile(ArchiveInputStream input, File file) throws AnalysisException {
|
||||
LOGGER.debug("Extracting '{}'", file.getPath());
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
final File parent = file.getParentFile();
|
||||
if (!parent.isDirectory() && !parent.mkdirs()) {
|
||||
final String msg = String.format("Unable to build directory '%s'.", parent.getAbsolutePath());
|
||||
throw new AnalysisException(msg);
|
||||
}
|
||||
fos = new FileOutputStream(file);
|
||||
IOUtils.copy(input, fos);
|
||||
} catch (FileNotFoundException ex) {
|
||||
LOGGER.debug("", ex);
|
||||
final String msg = String.format("Unable to find file '%s'.", file.getName());
|
||||
throw new AnalysisException(msg, ex);
|
||||
} catch (IOException ex) {
|
||||
LOGGER.debug("", ex);
|
||||
final String msg = String.format("IO Exception while parsing file '%s'.", file.getName());
|
||||
throw new AnalysisException(msg, ex);
|
||||
} finally {
|
||||
close(fos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,18 +561,15 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
*
|
||||
* @param inputStream the compressed 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 {
|
||||
LOGGER.debug("Decompressing '{}'", outputFile.getPath());
|
||||
FileOutputStream out = null;
|
||||
try {
|
||||
out = new FileOutputStream(outputFile);
|
||||
final byte[] buffer = new byte[BUFFER_SIZE];
|
||||
int n = 0;
|
||||
while (-1 != (n = inputStream.read(buffer))) {
|
||||
out.write(buffer, 0, n);
|
||||
}
|
||||
IOUtils.copy(inputStream, out);
|
||||
} catch (FileNotFoundException ex) {
|
||||
LOGGER.debug("", ex);
|
||||
throw new ArchiveExtractionException(ex);
|
||||
@@ -456,12 +577,22 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
LOGGER.debug("", ex);
|
||||
throw new ArchiveExtractionException(ex);
|
||||
} finally {
|
||||
if (out != null) {
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException ex) {
|
||||
LOGGER.trace("", ex);
|
||||
}
|
||||
close(out);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the given {@link Closeable} instance, ignoring nulls, and logging
|
||||
* any thrown {@link IOException}.
|
||||
*
|
||||
* @param closeable to be closed
|
||||
*/
|
||||
private static void close(Closeable closeable) {
|
||||
if (null != closeable) {
|
||||
try {
|
||||
closeable.close();
|
||||
} catch (IOException ex) {
|
||||
LOGGER.trace("", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,15 +17,13 @@
|
||||
*/
|
||||
package org.owasp.dependencycheck.analyzer;
|
||||
|
||||
import ch.qos.cal10n.IMessageConveyor;
|
||||
import ch.qos.cal10n.MessageConveyor;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.io.output.NullOutputStream;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
import org.owasp.dependencycheck.dependency.Confidence;
|
||||
@@ -45,10 +43,13 @@ import javax.xml.xpath.XPathExpressionException;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
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
|
||||
*
|
||||
@@ -71,14 +72,6 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
* The temp value for GrokAssembly.exe
|
||||
*/
|
||||
private File grokAssemblyExe = null;
|
||||
/**
|
||||
* The DocumentBuilder for parsing the XML
|
||||
*/
|
||||
private DocumentBuilder builder;
|
||||
/**
|
||||
* Message Conveyer
|
||||
*/
|
||||
private static final IMessageConveyor MESSAGE_CONVERYOR = new MessageConveyor(Locale.getDefault());
|
||||
/**
|
||||
* Logger
|
||||
*/
|
||||
@@ -89,18 +82,19 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
*
|
||||
* @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
|
||||
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) {
|
||||
args.add(Settings.getString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH));
|
||||
} else {
|
||||
} else if (isInPath("mono")) {
|
||||
args.add("mono");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
args.add(grokAssemblyExe.getPath());
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
@@ -120,23 +114,26 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
|
||||
final List<String> args = buildArgumentList();
|
||||
if (args == null) {
|
||||
LOGGER.warn("Assembly Analyzer was unable to execute");
|
||||
return;
|
||||
}
|
||||
args.add(dependency.getActualFilePath());
|
||||
final ProcessBuilder pb = new ProcessBuilder(args);
|
||||
BufferedReader rdr = null;
|
||||
Document doc = null;
|
||||
try {
|
||||
final Process proc = pb.start();
|
||||
// Try evacuating the error stream
|
||||
rdr = new BufferedReader(new InputStreamReader(proc.getErrorStream(), "UTF-8"));
|
||||
String line = null;
|
||||
// CHECKSTYLE:OFF
|
||||
while (rdr.ready() && (line = rdr.readLine()) != null) {
|
||||
LOGGER.warn("Error from GrokAssembly: {}", line);
|
||||
}
|
||||
// CHECKSTYLE:ON
|
||||
int rc = 0;
|
||||
|
||||
final DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
||||
doc = builder.parse(proc.getInputStream());
|
||||
|
||||
// Try evacuating the error stream
|
||||
final String errorStream = IOUtils.toString(proc.getErrorStream(), "UTF-8");
|
||||
if (null != errorStream && !errorStream.isEmpty()) {
|
||||
LOGGER.warn("Error from GrokAssembly: {}", errorStream);
|
||||
}
|
||||
|
||||
int rc = 0;
|
||||
try {
|
||||
rc = proc.waitFor();
|
||||
} catch (InterruptedException ie) {
|
||||
@@ -154,7 +151,7 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
|
||||
// First, see if there was an error
|
||||
final String error = xpath.evaluate("/assembly/error", doc);
|
||||
if (error != null && !"".equals(error)) {
|
||||
if (error != null && !error.isEmpty()) {
|
||||
throw new AnalysisException(error);
|
||||
}
|
||||
|
||||
@@ -176,6 +173,8 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
product, Confidence.HIGH));
|
||||
}
|
||||
|
||||
} catch (ParserConfigurationException pce) {
|
||||
throw new AnalysisException("Error initializing the assembly analyzer", pce);
|
||||
} catch (IOException ioe) {
|
||||
throw new AnalysisException(ioe);
|
||||
} catch (SAXException saxe) {
|
||||
@@ -183,43 +182,37 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
} catch (XPathExpressionException xpe) {
|
||||
// This shouldn't happen
|
||||
throw new AnalysisException(xpe);
|
||||
} finally {
|
||||
if (rdr != null) {
|
||||
try {
|
||||
rdr.close();
|
||||
} catch (IOException ex) {
|
||||
LOGGER.debug("ignore", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
public void initializeFileTypeAnalyzer() throws Exception {
|
||||
final File tempFile = File.createTempFile("GKA", ".exe", Settings.getTempDirectory());
|
||||
public void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
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;
|
||||
InputStream is = null;
|
||||
try {
|
||||
fos = new FileOutputStream(tempFile);
|
||||
is = AssemblyAnalyzer.class.getClassLoader().getResourceAsStream("GrokAssembly.exe");
|
||||
final byte[] buff = new byte[4096];
|
||||
int bread = -1;
|
||||
while ((bread = is.read(buff)) >= 0) {
|
||||
fos.write(buff, 0, bread);
|
||||
}
|
||||
IOUtils.copy(is, fos);
|
||||
|
||||
grokAssemblyExe = tempFile;
|
||||
// Set the temp file to get deleted when we're done
|
||||
grokAssemblyExe.deleteOnExit();
|
||||
LOGGER.debug("Extracted GrokAssembly.exe to {}", grokAssemblyExe.getPath());
|
||||
} catch (IOException ioe) {
|
||||
this.setEnabled(false);
|
||||
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 {
|
||||
if (fos != null) {
|
||||
try {
|
||||
@@ -239,47 +232,51 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
|
||||
// Now, need to see if GrokAssembly actually runs from this location.
|
||||
final List<String> args = buildArgumentList();
|
||||
BufferedReader rdr = null;
|
||||
//TODO this creates an "unreported" error - if someone doesn't look
|
||||
// at the command output this could easily be missed (especially in an
|
||||
// Ant or Maven 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' executable could not be found on "
|
||||
+ "the path; either disable the Assembly Analyzer or configure the path mono.");
|
||||
LOGGER.error("----------------------------------------------------");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final ProcessBuilder pb = new ProcessBuilder(args);
|
||||
final Process p = pb.start();
|
||||
// Try evacuating the error stream
|
||||
rdr = new BufferedReader(new InputStreamReader(p.getErrorStream(), "UTF-8"));
|
||||
// CHECKSTYLE:OFF
|
||||
while (rdr.ready() && rdr.readLine() != null) {
|
||||
// We expect this to complain
|
||||
}
|
||||
// CHECKSTYLE:ON
|
||||
final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(p.getInputStream());
|
||||
IOUtils.copy(p.getErrorStream(), NullOutputStream.NULL_OUTPUT_STREAM);
|
||||
|
||||
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
final DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
final Document doc = builder.parse(p.getInputStream());
|
||||
final XPath xpath = XPathFactory.newInstance().newXPath();
|
||||
final String error = xpath.evaluate("/assembly/error", doc);
|
||||
if (p.waitFor() != 1 || error == null || "".equals(error)) {
|
||||
if (p.waitFor() != 1 || error == null || error.isEmpty()) {
|
||||
LOGGER.warn("An error occurred with the .NET AssemblyAnalyzer, please see the log for more details.");
|
||||
LOGGER.debug("GrokAssembly.exe is not working properly");
|
||||
grokAssemblyExe = null;
|
||||
this.setEnabled(false);
|
||||
throw new AnalysisException("Could not execute .NET AssemblyAnalyzer");
|
||||
setEnabled(false);
|
||||
throw new InitializationException("Could not execute .NET AssemblyAnalyzer");
|
||||
}
|
||||
} catch (InitializationException e) {
|
||||
setEnabled(false);
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
if (e instanceof AnalysisException) {
|
||||
throw (AnalysisException) e;
|
||||
} else {
|
||||
LOGGER.warn("An error occurred with the .NET AssemblyAnalyzer;\n"
|
||||
+ "this can be ignored unless you are scanning .NET DLLs. Please see the log for more details.");
|
||||
LOGGER.debug("Could not execute GrokAssembly {}", e.getMessage());
|
||||
this.setEnabled(false);
|
||||
throw new AnalysisException("An error occured with the .NET AssemblyAnalyzer", e);
|
||||
}
|
||||
} finally {
|
||||
if (rdr != null) {
|
||||
try {
|
||||
rdr.close();
|
||||
} catch (IOException ex) {
|
||||
LOGGER.trace("ignore", ex);
|
||||
}
|
||||
}
|
||||
LOGGER.warn("An error occurred with the .NET AssemblyAnalyzer;\n"
|
||||
+ "this can be ignored unless you are scanning .NET DLLs. Please see the log for more details.");
|
||||
LOGGER.debug("Could not execute GrokAssembly {}", e.getMessage());
|
||||
setEnabled(false);
|
||||
throw new InitializationException("An error occurred with the .NET AssemblyAnalyzer", e);
|
||||
}
|
||||
builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -292,10 +289,12 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
super.close();
|
||||
try {
|
||||
if (grokAssemblyExe != null && !grokAssemblyExe.delete()) {
|
||||
LOGGER.debug("Unable to delete temporary GrokAssembly.exe; attempting delete on exit");
|
||||
grokAssemblyExe.deleteOnExit();
|
||||
}
|
||||
} catch (SecurityException se) {
|
||||
LOGGER.debug("Can't delete temporary GrokAssembly.exe");
|
||||
grokAssemblyExe.deleteOnExit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,7 +330,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
|
||||
*/
|
||||
@@ -339,4 +339,29 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
protected String getAnalyzerEnabledSettingKey() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,18 +30,21 @@ import org.owasp.dependencycheck.utils.UrlStringUtils;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.regex.Matcher;
|
||||
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,
|
||||
* assuming they are generated by Autoconf, and contain certain special package descriptor variables.
|
||||
* Used to analyze Autoconf input files named configure.ac or configure.in.
|
||||
* 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>
|
||||
* @see <a href="https://www.gnu.org/software/autoconf/">Autoconf - GNU Project - Free Software Foundation (FSF)</a>
|
||||
* @author Dale Visser
|
||||
* @see <a href="https://www.gnu.org/software/autoconf/">Autoconf - GNU Project
|
||||
* - Free Software Foundation (FSF)</a>
|
||||
*/
|
||||
@Experimental
|
||||
public class AutoconfAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
|
||||
/**
|
||||
@@ -140,7 +143,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
|
||||
*/
|
||||
@@ -172,11 +176,7 @@ public class AutoconfAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// copy, alter and set in case some other thread is iterating over
|
||||
final List<Dependency> deps = new ArrayList<Dependency>(
|
||||
engine.getDependencies());
|
||||
deps.remove(dependency);
|
||||
engine.setDependencies(deps);
|
||||
engine.getDependencies().remove(dependency);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,14 +220,12 @@ public class AutoconfAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
*/
|
||||
private String getFileContents(final File actualFile)
|
||||
throws AnalysisException {
|
||||
String contents = "";
|
||||
try {
|
||||
contents = FileUtils.readFileToString(actualFile).trim();
|
||||
return FileUtils.readFileToString(actualFile, Charset.defaultCharset()).trim();
|
||||
} catch (IOException e) {
|
||||
throw new AnalysisException(
|
||||
"Problem occured while reading dependency file.", e);
|
||||
"Problem occurred while reading dependency file.", e);
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -270,10 +268,11 @@ public class AutoconfAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
/**
|
||||
* 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
|
||||
protected void initializeFileTypeAnalyzer() throws Exception {
|
||||
protected void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
// No initialization needed.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
package org.owasp.dependencycheck.analyzer;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
import org.owasp.dependencycheck.dependency.Confidence;
|
||||
@@ -32,23 +32,29 @@ import org.slf4j.LoggerFactory;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Used to analyze CMake build files, and collect information that can be used to determine the associated CPE.</p>
|
||||
* <p/>
|
||||
* Used to analyze CMake build files, and collect information that can be used
|
||||
* to determine the associated CPE.</p>
|
||||
* <p>
|
||||
* Note: This analyzer catches straightforward invocations of the project command, plus some other observed patterns of version
|
||||
* inclusion in real CMake 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
|
||||
* Note: This analyzer catches straightforward invocations of the project
|
||||
* command, plus some other observed patterns of version inclusion in real CMake
|
||||
* 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>
|
||||
*
|
||||
* @author Dale Visser <dvisser@ida.org>
|
||||
* @author Dale Visser
|
||||
*/
|
||||
@Experimental
|
||||
public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
|
||||
/**
|
||||
@@ -62,11 +68,19 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
private static final int REGEX_OPTIONS = Pattern.DOTALL
|
||||
| Pattern.CASE_INSENSITIVE | Pattern.MULTILINE;
|
||||
|
||||
/**
|
||||
* Regex to extract the product information.
|
||||
*/
|
||||
private static final Pattern PROJECT = Pattern.compile(
|
||||
"^ *project *\\([ \\n]*(\\w+)[ \\n]*.*?\\)", REGEX_OPTIONS);
|
||||
|
||||
// Group 1: Product
|
||||
// Group 2: Version
|
||||
/**
|
||||
* Regex to extract product and version information.
|
||||
*
|
||||
* Group 1: Product
|
||||
*
|
||||
* Group 2: Version
|
||||
*/
|
||||
private static final Pattern SET_VERSION = Pattern
|
||||
.compile(
|
||||
"^ *set\\s*\\(\\s*(\\w+)_version\\s+\"?(\\d+(?:\\.\\d+)+)[\\s\"]?\\)",
|
||||
@@ -78,24 +92,10 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
private static final FileFilter FILTER = FileFilterBuilder.newInstance().addExtensions(".cmake")
|
||||
.addFilenames("CMakeLists.txt").build();
|
||||
|
||||
/**
|
||||
* A reference to SHA1 message digest.
|
||||
*/
|
||||
private static MessageDigest sha1 = null;
|
||||
|
||||
static {
|
||||
try {
|
||||
sha1 = MessageDigest.getInstance("SHA1");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
LOGGER.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the CMake analyzer.
|
||||
*
|
||||
* @return the name of the analyzer
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
@@ -123,13 +123,19 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
|
||||
/**
|
||||
* No-op initializer implementation.
|
||||
* Initializes the analyzer.
|
||||
*
|
||||
* @throws Exception never thrown
|
||||
* @throws InitializationException thrown if an exception occurs getting an
|
||||
* instance of SHA1
|
||||
*/
|
||||
@Override
|
||||
protected void initializeFileTypeAnalyzer() throws Exception {
|
||||
// Nothing to do here.
|
||||
protected void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
try {
|
||||
getSha1MessageDigest();
|
||||
} catch (IllegalStateException ex) {
|
||||
setEnabled(false);
|
||||
throw new InitializationException("Unable to create SHA1 MessageDigest", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,7 +143,8 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
*
|
||||
* @param dependency the dependency being analyzed
|
||||
* @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
|
||||
protected void analyzeFileType(Dependency dependency, Engine engine)
|
||||
@@ -148,7 +155,7 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
dependency.setDisplayFileName(String.format("%s%c%s", parentName, File.separatorChar, name));
|
||||
String contents;
|
||||
try {
|
||||
contents = FileUtils.readFileToString(file).trim();
|
||||
contents = FileUtils.readFileToString(file, Charset.defaultCharset()).trim();
|
||||
} catch (IOException e) {
|
||||
throw new AnalysisException(
|
||||
"Problem occurred while reading dependency file.", e);
|
||||
@@ -167,20 +174,32 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
dependency.getProductEvidence().addEvidence(name, "Project",
|
||||
group, Confidence.HIGH);
|
||||
}
|
||||
LOGGER.debug(String.format("Found %d matches.", count));
|
||||
LOGGER.debug("Found {} matches.", count);
|
||||
analyzeSetVersionCommand(dependency, engine, contents);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the version information from the contents. If more then one
|
||||
* version is found additional dependencies are added to the dependency
|
||||
* list.
|
||||
*
|
||||
* @param dependency the dependency being analyzed
|
||||
* @param engine the dependency-check engine
|
||||
* @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) {
|
||||
final Dependency orig = dependency;
|
||||
Dependency currentDep = dependency;
|
||||
|
||||
final Matcher m = SET_VERSION.matcher(contents);
|
||||
int count = 0;
|
||||
while (m.find()) {
|
||||
count++;
|
||||
LOGGER.debug(String.format(
|
||||
"Found project command match with %d groups: %s",
|
||||
m.groupCount(), m.group(0)));
|
||||
LOGGER.debug("Found project command match with {} groups: {}",
|
||||
m.groupCount(), m.group(0));
|
||||
String product = m.group(1);
|
||||
final String version = m.group(2);
|
||||
LOGGER.debug("Group 1: " + product);
|
||||
@@ -191,19 +210,25 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
if (count > 1) {
|
||||
//TODO - refactor so we do not assign to the parameter (checkstyle)
|
||||
dependency = new Dependency(orig.getActualFile());
|
||||
dependency.setDisplayFileName(String.format("%s:%s", orig.getDisplayFileName(), product));
|
||||
final String filePath = String.format("%s:%s", orig.getFilePath(), product);
|
||||
dependency.setFilePath(filePath);
|
||||
currentDep = new Dependency(dependency.getActualFile());
|
||||
currentDep.setDisplayFileName(String.format("%s:%s", dependency.getDisplayFileName(), product));
|
||||
final String filePath = String.format("%s:%s", dependency.getFilePath(), product);
|
||||
currentDep.setFilePath(filePath);
|
||||
|
||||
// prevents coalescing into the dependency provided by engine
|
||||
dependency.setSha1sum(Checksum.getHex(sha1.digest(filePath.getBytes())));
|
||||
engine.getDependencies().add(dependency);
|
||||
byte[] path;
|
||||
try {
|
||||
path = filePath.getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
path = filePath.getBytes();
|
||||
}
|
||||
final MessageDigest sha1 = getSha1MessageDigest();
|
||||
currentDep.setSha1sum(Checksum.getHex(sha1.digest(path)));
|
||||
engine.getDependencies().add(currentDep);
|
||||
}
|
||||
final String source = dependency.getDisplayFileName();
|
||||
dependency.getProductEvidence().addEvidence(source, "Product",
|
||||
final String source = currentDep.getDisplayFileName();
|
||||
currentDep.getProductEvidence().addEvidence(source, "Product",
|
||||
product, Confidence.MEDIUM);
|
||||
dependency.getVersionEvidence().addEvidence(source, "Version",
|
||||
currentDep.getVersionEvidence().addEvidence(source, "Version",
|
||||
version, Confidence.MEDIUM);
|
||||
}
|
||||
LOGGER.debug(String.format("Found %d matches.", count));
|
||||
@@ -213,4 +238,18 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
protected String getAnalyzerEnabledSettingKey() {
|
||||
return Settings.KEYS.ANALYZER_CMAKE_ENABLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sha1 message digest.
|
||||
*
|
||||
* @return the sha1 message digest
|
||||
*/
|
||||
private MessageDigest getSha1MessageDigest() {
|
||||
try {
|
||||
return MessageDigest.getInstance("SHA1");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
LOGGER.error(e.getMessage());
|
||||
throw new IllegalStateException("Failed to obtain the SHA1 message digest.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.apache.commons.lang3.builder.CompareToBuilder;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.index.CorruptIndexException;
|
||||
import org.apache.lucene.queryparser.classic.ParseException;
|
||||
@@ -45,18 +47,20 @@ import org.owasp.dependencycheck.dependency.Evidence;
|
||||
import org.owasp.dependencycheck.dependency.EvidenceCollection;
|
||||
import org.owasp.dependencycheck.dependency.Identifier;
|
||||
import org.owasp.dependencycheck.dependency.VulnerableSoftware;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
import org.owasp.dependencycheck.utils.DependencyVersion;
|
||||
import org.owasp.dependencycheck.utils.DependencyVersionUtil;
|
||||
import org.slf4j.Logger;
|
||||
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
|
||||
* the evidence contained within the dependency to search the Lucene index.
|
||||
* CPEAnalyzer is a utility class that takes a project dependency and attempts
|
||||
* to discern if there is an associated CPE. It uses the evidence contained
|
||||
* within the dependency to search the Lucene index.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
public class CPEAnalyzer implements Analyzer {
|
||||
public class CPEAnalyzer extends AbstractAnalyzer {
|
||||
|
||||
/**
|
||||
* The Logger.
|
||||
@@ -71,15 +75,18 @@ public class CPEAnalyzer implements Analyzer {
|
||||
*/
|
||||
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 ._-]";
|
||||
/**
|
||||
* 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]*";
|
||||
/**
|
||||
* 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;
|
||||
/**
|
||||
@@ -119,31 +126,44 @@ public class CPEAnalyzer implements Analyzer {
|
||||
/**
|
||||
* 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
|
||||
public void initialize() throws Exception {
|
||||
this.open();
|
||||
public void initialize() throws InitializationException {
|
||||
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.
|
||||
*
|
||||
* @throws IOException when the Lucene directory to be queried does not exist or is corrupt.
|
||||
* @throws DatabaseException when the database throws an exception. This usually occurs when the database is in use by another
|
||||
* process.
|
||||
* @throws IOException when the Lucene directory to be queried does not
|
||||
* exist or is corrupt.
|
||||
* @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 {
|
||||
LOGGER.debug("Opening the CVE Database");
|
||||
cve = new CveDB();
|
||||
cve.open();
|
||||
LOGGER.debug("Creating the Lucene CPE Index");
|
||||
cpe = CpeMemoryIndex.getInstance();
|
||||
try {
|
||||
cpe.open(cve);
|
||||
} catch (IndexException ex) {
|
||||
LOGGER.debug("IndexException", ex);
|
||||
throw new DatabaseException(ex);
|
||||
if (!isOpen()) {
|
||||
cve = new CveDB();
|
||||
cve.open();
|
||||
cpe = CpeMemoryIndex.getInstance();
|
||||
try {
|
||||
final long creationStart = System.currentTimeMillis();
|
||||
cpe.open(cve);
|
||||
final long creationSeconds = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - creationStart);
|
||||
LOGGER.info("Created CPE Index ({} seconds)", creationSeconds);
|
||||
} catch (IndexException ex) {
|
||||
LOGGER.debug("IndexException", ex);
|
||||
throw new DatabaseException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,8 +187,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
|
||||
* within. The dependency passed in is updated with any identified CPE values.
|
||||
* Searches the data store of CPE entries, trying to identify the CPE for
|
||||
* 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.
|
||||
* @throws CorruptIndexException is thrown when the Lucene index is corrupt.
|
||||
@@ -189,8 +210,8 @@ public class CPEAnalyzer implements Analyzer {
|
||||
LOGGER.debug("product search: {}", products);
|
||||
}
|
||||
if (!vendors.isEmpty() && !products.isEmpty()) {
|
||||
final List<IndexEntry> entries = searchCPE(vendors, products, dependency.getProductEvidence().getWeighting(),
|
||||
dependency.getVendorEvidence().getWeighting());
|
||||
final List<IndexEntry> entries = searchCPE(vendors, products, dependency.getVendorEvidence().getWeighting(),
|
||||
dependency.getProductEvidence().getWeighting());
|
||||
if (entries == null) {
|
||||
continue;
|
||||
}
|
||||
@@ -212,9 +233,10 @@ public class CPEAnalyzer implements Analyzer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text created by concatenating the text and the values from the EvidenceCollection (filtered for a specific
|
||||
* confidence). This attempts to prevent duplicate terms from being added.<br/<br/> Note, if the evidence is longer then 200
|
||||
* characters it will be truncated.
|
||||
* Returns the text created by concatenating the text and the values from
|
||||
* the EvidenceCollection (filtered for a specific confidence). This
|
||||
* 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 ec an EvidenceCollection
|
||||
@@ -245,17 +267,19 @@ public class CPEAnalyzer implements Analyzer {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Searches the Lucene CPE index to identify possible CPE entries associated with the supplied vendor, product, and
|
||||
* version.</p>
|
||||
* Searches the Lucene CPE index to identify possible CPE entries associated
|
||||
* with the supplied vendor, product, and version.</p>
|
||||
*
|
||||
* <p>
|
||||
* If either the vendorWeightings or productWeightings lists have been populated this data is used to add weighting factors to
|
||||
* the search.</p>
|
||||
* If either the vendorWeightings or productWeightings lists have been
|
||||
* populated this data is used to add weighting factors to the search.</p>
|
||||
*
|
||||
* @param vendor the text used to search the vendor 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 productWeightings Adds a list of strings that will be used to add weighting factors to the product search
|
||||
* @param vendorWeightings a list of strings to use to add weighting factors
|
||||
* 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
|
||||
*/
|
||||
protected List<IndexEntry> searchCPE(String vendor, String product,
|
||||
@@ -283,10 +307,10 @@ public class CPEAnalyzer implements Analyzer {
|
||||
}
|
||||
return ret;
|
||||
} 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);
|
||||
} 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);
|
||||
}
|
||||
return null;
|
||||
@@ -294,16 +318,20 @@ public class CPEAnalyzer implements Analyzer {
|
||||
|
||||
/**
|
||||
* <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>
|
||||
* If either the possibleVendor or possibleProducts lists have been populated this data is used to add weighting factors to
|
||||
* the search string generated.</p>
|
||||
* If either the possibleVendor or possibleProducts lists have been
|
||||
* populated this data is used to add weighting factors to the search string
|
||||
* generated.</p>
|
||||
*
|
||||
* @param vendor text to search the vendor 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 productWeightings a list of strings to apply to the product to boost the terms weight
|
||||
* @param vendorWeighting a list of strings to apply to the vendor to boost
|
||||
* the terms weight
|
||||
* @param productWeightings a list of strings to apply to the product to
|
||||
* boost the terms weight
|
||||
* @return the Lucene query
|
||||
*/
|
||||
protected String buildSearch(String vendor, String product,
|
||||
@@ -324,21 +352,25 @@ 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
|
||||
* within the list of weighted words then an additional weighting is applied to the term as it is appended into the query.
|
||||
* This method constructs a Lucene query for a given field. The searchText
|
||||
* 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 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 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.
|
||||
*/
|
||||
private boolean appendWeightedSearch(StringBuilder sb, String field, String searchText, Set<String> weightedText) {
|
||||
sb.append(" ").append(field).append(":( ");
|
||||
sb.append(' ').append(field).append(":( ");
|
||||
|
||||
final String cleanText = cleanseText(searchText);
|
||||
|
||||
if ("".equals(cleanText)) {
|
||||
if (cleanText.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -348,20 +380,27 @@ public class CPEAnalyzer implements Analyzer {
|
||||
final StringTokenizer tokens = new StringTokenizer(cleanText);
|
||||
while (tokens.hasMoreElements()) {
|
||||
final String word = tokens.nextToken();
|
||||
String temp = null;
|
||||
StringBuilder temp = null;
|
||||
for (String weighted : weightedText) {
|
||||
final String weightedStr = cleanseText(weighted);
|
||||
if (equalsIgnoreCaseAndNonAlpha(word, weightedStr)) {
|
||||
temp = LuceneUtils.escapeLuceneQuery(word) + WEIGHTING_BOOST;
|
||||
temp = new StringBuilder(word.length() + 2);
|
||||
LuceneUtils.appendEscapedLuceneQuery(temp, word);
|
||||
temp.append(WEIGHTING_BOOST);
|
||||
if (!word.equalsIgnoreCase(weightedStr)) {
|
||||
temp += " " + LuceneUtils.escapeLuceneQuery(weightedStr) + WEIGHTING_BOOST;
|
||||
temp.append(' ');
|
||||
LuceneUtils.appendEscapedLuceneQuery(temp, weightedStr);
|
||||
temp.append(WEIGHTING_BOOST);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
sb.append(' ');
|
||||
if (temp == null) {
|
||||
temp = LuceneUtils.escapeLuceneQuery(word);
|
||||
LuceneUtils.appendEscapedLuceneQuery(sb, word);
|
||||
} else {
|
||||
sb.append(temp);
|
||||
}
|
||||
sb.append(" ").append(temp);
|
||||
}
|
||||
}
|
||||
sb.append(" ) ");
|
||||
@@ -369,7 +408,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.
|
||||
* @return the text having removed some characters.
|
||||
@@ -379,7 +419,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 r string two to compare.
|
||||
@@ -396,8 +437,9 @@ public class CPEAnalyzer implements Analyzer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the CPE Identified matches the dependency. This validates that the product, vendor, and version information
|
||||
* for the CPE are contained within the dependencies evidence.
|
||||
* Ensures that the CPE Identified matches the dependency. This validates
|
||||
* that the product, vendor, and version information for the CPE are
|
||||
* contained within the dependencies evidence.
|
||||
*
|
||||
* @param entry a CPE entry.
|
||||
* @param dependency the dependency that the CPE entries could be for.
|
||||
@@ -464,14 +506,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 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
|
||||
public void analyze(Dependency dependency, Engine engine) throws AnalysisException {
|
||||
public synchronized void analyze(Dependency dependency, Engine engine) throws AnalysisException {
|
||||
try {
|
||||
determineCPE(dependency);
|
||||
} catch (CorruptIndexException ex) {
|
||||
@@ -484,15 +528,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
|
||||
* only CPEs that are 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.
|
||||
* Retrieves a list of CPE values from the CveDB based on the vendor and
|
||||
* product passed in. The list is then validated to find only CPEs that are
|
||||
* 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 vendor the vendor for the CPE being analyzed
|
||||
* @param product the product for the CPE being analyzed
|
||||
* @param currentConfidence the current confidence being used during analysis
|
||||
* @return <code>true</code> if an identifier was added to the dependency; otherwise <code>false</code>
|
||||
* @param currentConfidence the current confidence being used during
|
||||
* 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
|
||||
*/
|
||||
protected boolean determineIdentifiers(Dependency dependency, String vendor, String product,
|
||||
@@ -502,10 +550,11 @@ public class CPEAnalyzer implements Analyzer {
|
||||
Confidence bestGuessConf = null;
|
||||
boolean hasBroadMatch = false;
|
||||
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()) {
|
||||
// if (conf.compareTo(currentConfidence) > 0) {
|
||||
// break;
|
||||
// }
|
||||
for (Evidence evidence : dependency.getVersionEvidence().iterator(conf)) {
|
||||
final DependencyVersion evVer = DependencyVersionUtil.parseVersion(evidence.getValue());
|
||||
if (evVer == null) {
|
||||
@@ -514,7 +563,7 @@ public class CPEAnalyzer implements Analyzer {
|
||||
for (VulnerableSoftware vs : cpes) {
|
||||
DependencyVersion dbVer;
|
||||
if (vs.getUpdate() != null && !vs.getUpdate().isEmpty()) {
|
||||
dbVer = DependencyVersionUtil.parseVersion(vs.getVersion() + "." + vs.getUpdate());
|
||||
dbVer = DependencyVersionUtil.parseVersion(vs.getVersion() + '.' + vs.getUpdate());
|
||||
} else {
|
||||
dbVer = DependencyVersionUtil.parseVersion(vs.getVersion());
|
||||
}
|
||||
@@ -527,15 +576,14 @@ public class CPEAnalyzer implements Analyzer {
|
||||
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);
|
||||
collected.add(match);
|
||||
} else {
|
||||
|
||||
//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()
|
||||
&& evVer.matchesAtLeastThreeLevels(dbVer)) {
|
||||
if (bestGuessConf == null || bestGuessConf.compareTo(conf) > 0) {
|
||||
if (bestGuess.getVersionParts().size() < dbVer.getVersionParts().size()) {
|
||||
bestGuess = dbVer;
|
||||
bestGuessConf = conf;
|
||||
}
|
||||
} else if (evVer.getVersionParts().size() <= dbVer.getVersionParts().size()
|
||||
&& evVer.matchesAtLeastThreeLevels(dbVer)) {
|
||||
if (bestGuessConf == null || bestGuessConf.compareTo(conf) > 0) {
|
||||
if (bestGuess.getVersionParts().size() < dbVer.getVersionParts().size()) {
|
||||
bestGuess = dbVer;
|
||||
bestGuessConf = conf;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -594,14 +642,16 @@ public class CPEAnalyzer implements Analyzer {
|
||||
*/
|
||||
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
|
||||
* specifies vendor/product.
|
||||
* The entire vendor/product group must be added (without a guess at
|
||||
* version) because there is a CVE with a VS that only specifies
|
||||
* vendor/product.
|
||||
*/
|
||||
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> {
|
||||
|
||||
@@ -611,8 +661,10 @@ public class CPEAnalyzer implements Analyzer {
|
||||
* @param type the type of identifier (such as CPE)
|
||||
* @param value the value of the identifier
|
||||
* @param url the URL of the identifier
|
||||
* @param identifierConfidence the confidence in the identifier: best guess or exact match
|
||||
* @param evidenceConfidence the confidence of the evidence used to find the identifier
|
||||
* @param identifierConfidence the confidence in the identifier: best
|
||||
* 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) {
|
||||
this.identifier = new Identifier(type, value, url);
|
||||
@@ -743,13 +795,20 @@ public class CPEAnalyzer implements Analyzer {
|
||||
//</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
|
||||
* @return the natural ordering of IdentifierMatch
|
||||
*/
|
||||
@Override
|
||||
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);
|
||||
if (conf == 0) {
|
||||
conf = this.evidenceConfidence.compareTo(o.evidenceConfidence);
|
||||
@@ -758,6 +817,7 @@ public class CPEAnalyzer implements Analyzer {
|
||||
}
|
||||
}
|
||||
return conf;
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,8 +33,10 @@ import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
import org.owasp.dependencycheck.utils.DownloadFailedException;
|
||||
import org.owasp.dependencycheck.utils.Downloader;
|
||||
import org.owasp.dependencycheck.utils.FileFilterBuilder;
|
||||
@@ -42,8 +44,8 @@ import org.owasp.dependencycheck.utils.InvalidSettingException;
|
||||
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
|
||||
* digest.
|
||||
* Analyzer which will attempt to locate a dependency, and the GAV information,
|
||||
* by querying Central for the dependency's SHA-1 digest.
|
||||
*
|
||||
* @author colezlaw
|
||||
*/
|
||||
@@ -70,9 +72,10 @@ public class CentralAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
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 volatile boolean errorFlag = false;
|
||||
|
||||
/**
|
||||
* The searcher itself.
|
||||
@@ -96,7 +99,8 @@ public class CentralAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
/**
|
||||
* 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() {
|
||||
boolean retval = false;
|
||||
@@ -122,16 +126,21 @@ public class CentralAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
/**
|
||||
* 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
|
||||
public void initializeFileTypeAnalyzer() throws Exception {
|
||||
public void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
LOGGER.debug("Initializing Central analyzer");
|
||||
LOGGER.debug("Central analyzer enabled: {}", isEnabled());
|
||||
if (isEnabled()) {
|
||||
final String searchUrl = Settings.getString(Settings.KEYS.ANALYZER_CENTRAL_URL);
|
||||
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.
|
||||
*/
|
||||
@@ -192,7 +202,7 @@ public class CentralAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
final List<MavenArtifact> mas = searcher.searchSha1(dependency.getSha1sum());
|
||||
final Confidence confidence = mas.size() > 1 ? Confidence.HIGH : Confidence.HIGHEST;
|
||||
for (MavenArtifact ma : mas) {
|
||||
LOGGER.debug("Central analyzer found artifact ({}) for dependency ({})", ma.toString(), dependency.getFileName());
|
||||
LOGGER.debug("Central analyzer found artifact ({}) for dependency ({})", ma, dependency.getFileName());
|
||||
dependency.addAsEvidence("central", ma, confidence);
|
||||
boolean pomAnalyzed = false;
|
||||
for (Evidence e : dependency.getVendorEvidence()) {
|
||||
@@ -219,7 +229,8 @@ public class CentralAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
LOGGER.warn("Unable to download pom.xml for {} from Central; "
|
||||
+ "this could result in undetected CPE/CVEs.", dependency.getFileName());
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* 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) 2015 The OWASP Foundation. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.analyzer;
|
||||
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
import org.owasp.dependencycheck.data.composer.ComposerDependency;
|
||||
import org.owasp.dependencycheck.data.composer.ComposerException;
|
||||
import org.owasp.dependencycheck.data.composer.ComposerLockParser;
|
||||
import org.owasp.dependencycheck.dependency.Confidence;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
import org.owasp.dependencycheck.utils.Checksum;
|
||||
import org.owasp.dependencycheck.utils.FileFilterBuilder;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
/**
|
||||
* Used to analyze a composer.lock file for a composer PHP app.
|
||||
*
|
||||
* @author colezlaw
|
||||
*/
|
||||
@Experimental
|
||||
public class ComposerLockAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ComposerLockAnalyzer.class);
|
||||
|
||||
/**
|
||||
* The analyzer name.
|
||||
*/
|
||||
private static final String ANALYZER_NAME = "Composer.lock analyzer";
|
||||
|
||||
/**
|
||||
* composer.json.
|
||||
*/
|
||||
private static final String COMPOSER_LOCK = "composer.lock";
|
||||
|
||||
/**
|
||||
* The FileFilter.
|
||||
*/
|
||||
private static final FileFilter FILE_FILTER = FileFilterBuilder.newInstance().addFilenames(COMPOSER_LOCK).build();
|
||||
|
||||
/**
|
||||
* Returns the FileFilter.
|
||||
*
|
||||
* @return the FileFilter
|
||||
*/
|
||||
@Override
|
||||
protected FileFilter getFileFilter() {
|
||||
return FILE_FILTER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the analyzer.
|
||||
*
|
||||
* @throws InitializationException thrown if an exception occurs getting an
|
||||
* instance of SHA1
|
||||
*/
|
||||
@Override
|
||||
protected void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
try {
|
||||
getSha1MessageDigest();
|
||||
} catch (IllegalStateException ex) {
|
||||
setEnabled(false);
|
||||
throw new InitializationException("Unable to create SHA1 MessageDigest", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point for the analyzer.
|
||||
*
|
||||
* @param dependency the dependency to analyze
|
||||
* @param engine the engine scanning
|
||||
* @throws AnalysisException if there's a failure during analysis
|
||||
*/
|
||||
@Override
|
||||
protected void analyzeFileType(Dependency dependency, Engine engine) throws AnalysisException {
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(dependency.getActualFile());
|
||||
final ComposerLockParser clp = new ComposerLockParser(fis);
|
||||
LOGGER.info("Checking composer.lock file {}", dependency.getActualFilePath());
|
||||
clp.process();
|
||||
for (ComposerDependency dep : clp.getDependencies()) {
|
||||
final Dependency d = new Dependency(dependency.getActualFile());
|
||||
d.setDisplayFileName(String.format("%s:%s/%s", dependency.getDisplayFileName(), dep.getGroup(), dep.getProject()));
|
||||
final String filePath = String.format("%s:%s/%s", dependency.getFilePath(), dep.getGroup(), dep.getProject());
|
||||
final MessageDigest sha1 = getSha1MessageDigest();
|
||||
d.setFilePath(filePath);
|
||||
d.setSha1sum(Checksum.getHex(sha1.digest(filePath.getBytes(Charset.defaultCharset()))));
|
||||
d.getVendorEvidence().addEvidence(COMPOSER_LOCK, "vendor", dep.getGroup(), Confidence.HIGHEST);
|
||||
d.getProductEvidence().addEvidence(COMPOSER_LOCK, "product", dep.getProject(), Confidence.HIGHEST);
|
||||
d.getVersionEvidence().addEvidence(COMPOSER_LOCK, "version", dep.getVersion(), Confidence.HIGHEST);
|
||||
LOGGER.info("Adding dependency {}", d);
|
||||
engine.getDependencies().add(d);
|
||||
}
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
LOGGER.warn("Error opening dependency {}", dependency.getActualFilePath());
|
||||
} catch (ComposerException ce) {
|
||||
LOGGER.warn("Error parsing composer.json {}", dependency.getActualFilePath(), ce);
|
||||
} finally {
|
||||
if (fis != null) {
|
||||
try {
|
||||
fis.close();
|
||||
} catch (Exception e) {
|
||||
LOGGER.debug("Unable to close file", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the key to determine whether the analyzer is enabled.
|
||||
*
|
||||
* @return the key specifying whether the analyzer is enabled
|
||||
*/
|
||||
@Override
|
||||
protected String getAnalyzerEnabledSettingKey() {
|
||||
return Settings.KEYS.ANALYZER_COMPOSER_LOCK_ENABLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the analyzer's name.
|
||||
*
|
||||
* @return the analyzer's name
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return ANALYZER_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the phase this analyzer should run under.
|
||||
*
|
||||
* @return the analysis phase
|
||||
*/
|
||||
@Override
|
||||
public AnalysisPhase getAnalysisPhase() {
|
||||
return AnalysisPhase.INFORMATION_COLLECTION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sha1 message digest.
|
||||
*
|
||||
* @return the sha1 message digest
|
||||
*/
|
||||
private MessageDigest getSha1MessageDigest() {
|
||||
try {
|
||||
return MessageDigest.getInstance("SHA1");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
LOGGER.error(e.getMessage());
|
||||
throw new IllegalStateException("Failed to obtain the SHA1 message digest.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ package org.owasp.dependencycheck.analyzer;
|
||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
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.
|
||||
|
||||
@@ -35,15 +35,18 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This analyzer ensures dependencies that should be grouped together, to remove excess noise from the report, are grouped. An
|
||||
* example would be Spring, Spring 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>
|
||||
* This analyzer ensures dependencies that should be grouped together, to remove
|
||||
* excess noise from the report, are grouped. An example would be Spring, Spring
|
||||
* 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>
|
||||
* 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
|
||||
*/
|
||||
public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
public class DependencyBundlingAnalyzer extends AbstractAnalyzer {
|
||||
|
||||
/**
|
||||
* The Logger.
|
||||
@@ -55,10 +58,23 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
|
||||
* A pattern for obtaining the first part of a filename.
|
||||
*/
|
||||
private static final Pattern STARTING_TEXT_PATTERN = Pattern.compile("^[a-zA-Z0-9]*");
|
||||
|
||||
/**
|
||||
* a flag indicating if this analyzer has run. This analyzer only runs once.
|
||||
*/
|
||||
private boolean analyzed = false;
|
||||
|
||||
/**
|
||||
* Returns a flag indicating if this analyzer has run. This analyzer only
|
||||
* runs once. Note this is currently only used in the unit tests.
|
||||
*
|
||||
* @return a flag indicating if this analyzer has run. This analyzer only
|
||||
* runs once
|
||||
*/
|
||||
protected boolean getAnalyzed() {
|
||||
return analyzed;
|
||||
}
|
||||
|
||||
//</editor-fold>
|
||||
//<editor-fold defaultstate="collapsed" desc="All standard implementation details of Analyzer">
|
||||
/**
|
||||
@@ -75,6 +91,7 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
|
||||
*
|
||||
* @return the name of the analyzer.
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return ANALYZER_NAME;
|
||||
}
|
||||
@@ -84,18 +101,33 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
|
||||
*
|
||||
* @return the phase that the analyzer is intended to run in.
|
||||
*/
|
||||
@Override
|
||||
public AnalysisPhase getAnalysisPhase() {
|
||||
return ANALYSIS_PHASE;
|
||||
}
|
||||
//</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
|
||||
* likely related. The related dependencies are bundled into a single reportable item.
|
||||
* Does not support parallel processing as it only runs once and then
|
||||
* operates on <em>all</em> dependencies.
|
||||
*
|
||||
* @return whether or not parallel processing is enabled
|
||||
* @see #analyze(Dependency, Engine)
|
||||
*/
|
||||
@Override
|
||||
public boolean supportsParallelProcessing() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyzes a set of dependencies. If they have been found to have the same
|
||||
* 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 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
|
||||
public void analyze(Dependency ignore, Engine engine) throws AnalysisException {
|
||||
@@ -110,6 +142,7 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
|
||||
final ListIterator<Dependency> subIterator = engine.getDependencies().listIterator(mainIterator.nextIndex());
|
||||
while (subIterator.hasNext()) {
|
||||
final Dependency nextDependency = subIterator.next();
|
||||
Dependency main = null;
|
||||
if (hashesMatch(dependency, nextDependency) && !containedInWar(dependency.getFilePath())
|
||||
&& !containedInWar(nextDependency.getFilePath())) {
|
||||
if (firstPathIsShortest(dependency.getFilePath(), nextDependency.getFilePath())) {
|
||||
@@ -136,6 +169,20 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
|
||||
mergeDependencies(nextDependency, dependency, dependenciesToRemove);
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,10 +197,11 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
|
||||
* Adds the relatedDependency to the dependency's related dependencies.
|
||||
*
|
||||
* @param dependency the main dependency
|
||||
* @param relatedDependency a collection of dependencies to be removed from 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
|
||||
* adds to this collection
|
||||
* @param relatedDependency a collection of dependencies to be removed from
|
||||
* 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 adds to this
|
||||
* collection
|
||||
*/
|
||||
private void mergeDependencies(final Dependency dependency, final Dependency relatedDependency, final Set<Dependency> dependenciesToRemove) {
|
||||
dependency.addRelatedDependency(relatedDependency);
|
||||
@@ -169,7 +217,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
|
||||
* @return a string representing the base path.
|
||||
@@ -194,11 +243,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 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) {
|
||||
if (dependency1 == null || dependency1.getFileName() == null
|
||||
@@ -211,10 +262,8 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
|
||||
//version check
|
||||
final DependencyVersion version1 = DependencyVersionUtil.parseVersion(fileName1);
|
||||
final DependencyVersion version2 = DependencyVersionUtil.parseVersion(fileName2);
|
||||
if (version1 != null && version2 != null) {
|
||||
if (!version1.equals(version2)) {
|
||||
return false;
|
||||
}
|
||||
if (version1 != null && version2 != null && !version1.equals(version2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//filename check
|
||||
@@ -228,11 +277,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 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) {
|
||||
if (dependency1 == null || dependency1.getIdentifiers() == null
|
||||
@@ -283,10 +334,13 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
|
||||
String right = rFile.getParent();
|
||||
if (left == null) {
|
||||
return right == null;
|
||||
} else if (right == null) {
|
||||
return false;
|
||||
}
|
||||
if (left.equalsIgnoreCase(right)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (left.matches(".*[/\\\\]repository[/\\\\].*") && right.matches(".*[/\\\\]repository[/\\\\].*")) {
|
||||
left = getBaseRepoPath(left);
|
||||
right = getBaseRepoPath(right);
|
||||
@@ -304,12 +358,103 @@ 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
|
||||
* 'right' library.
|
||||
* Bundling Ruby gems that are identified from different .gemspec files but
|
||||
* 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;
|
||||
}
|
||||
return dependency1.getPackagePath().equalsIgnoreCase(dependency2.getPackagePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
return dependency1.getPackagePath().equalsIgnoreCase(dependency2.getPackagePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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) {
|
||||
final String leftName = left.getFileName().toLowerCase();
|
||||
@@ -345,11 +490,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 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) {
|
||||
if (dependency1 == null || dependency2 == null || dependency1.getSha1sum() == null || dependency2.getSha1sum() == null) {
|
||||
@@ -359,12 +506,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
|
||||
* should be removed.
|
||||
* Determines if the jar is shaded and the created pom.xml identified the
|
||||
* same CPE as the jar - if so, the pom.xml dependency should be removed.
|
||||
*
|
||||
* @param dependency a 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) {
|
||||
final String mainName = dependency.getFileName().toLowerCase();
|
||||
@@ -378,14 +526,18 @@ 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
|
||||
* first path is smaller.
|
||||
* Determines which path is shortest; if path lengths are equal then we use
|
||||
* compareTo of the string method to determine if the first path is smaller.
|
||||
*
|
||||
* @param left the first 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) {
|
||||
if (left.contains("dctemp")) {
|
||||
return false;
|
||||
}
|
||||
final String leftPath = left.replace('\\', '/');
|
||||
final String rightPath = right.replace('\\', '/');
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* This file is part of dependency-check-gradle.
|
||||
* 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.
|
||||
@@ -13,14 +13,22 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright (c) 2015 Wei Ma. All Rights Reserved.
|
||||
* Copyright (c) 2016 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.analyzer;
|
||||
|
||||
//Local debug use only
|
||||
uploadArchives {
|
||||
repositories {
|
||||
mavenDeployer {
|
||||
repository(url: uri('../../../repo'))
|
||||
}
|
||||
}
|
||||
}
|
||||
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 {
|
||||
|
||||
}
|
||||
@@ -69,6 +69,7 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer {
|
||||
*
|
||||
* @return the name of the analyzer.
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return ANALYZER_NAME;
|
||||
}
|
||||
@@ -78,6 +79,7 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer {
|
||||
*
|
||||
* @return the phase that the analyzer is intended to run in.
|
||||
*/
|
||||
@Override
|
||||
public AnalysisPhase getAnalysisPhase() {
|
||||
return ANALYSIS_PHASE;
|
||||
}
|
||||
@@ -111,7 +113,7 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer {
|
||||
for (Identifier i : dependency.getIdentifiers()) {
|
||||
if ("maven".contains(i.getType())) {
|
||||
if (i.getValue() != null && i.getValue().startsWith("org.springframework.")) {
|
||||
final int endPoint = i.getValue().indexOf(":", 19);
|
||||
final int endPoint = i.getValue().indexOf(':', 19);
|
||||
if (endPoint >= 0) {
|
||||
mustContain = i.getValue().substring(19, endPoint).toLowerCase();
|
||||
break;
|
||||
@@ -154,8 +156,7 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer {
|
||||
*/
|
||||
@SuppressWarnings("null")
|
||||
private void removeSpuriousCPE(Dependency dependency) {
|
||||
final List<Identifier> ids = new ArrayList<Identifier>();
|
||||
ids.addAll(dependency.getIdentifiers());
|
||||
final List<Identifier> ids = new ArrayList<Identifier>(dependency.getIdentifiers());
|
||||
Collections.sort(ids);
|
||||
final ListIterator<Identifier> mainItr = ids.listIterator();
|
||||
while (mainItr.hasNext()) {
|
||||
@@ -379,18 +380,16 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer {
|
||||
*/
|
||||
private void addFalseNegativeCPEs(Dependency dependency) {
|
||||
//TODO move this to the hint analyzer
|
||||
final Iterator<Identifier> itr = dependency.getIdentifiers().iterator();
|
||||
while (itr.hasNext()) {
|
||||
final Identifier i = itr.next();
|
||||
if ("cpe".equals(i.getType()) && i.getValue() != null
|
||||
&& (i.getValue().startsWith("cpe:/a:oracle:opensso:")
|
||||
|| i.getValue().startsWith("cpe:/a:oracle:opensso_enterprise:")
|
||||
|| i.getValue().startsWith("cpe:/a:sun:opensso_enterprise:")
|
||||
|| i.getValue().startsWith("cpe:/a:sun:opensso:"))) {
|
||||
final String newCpe = String.format("cpe:/a:sun:opensso_enterprise:%s", i.getValue().substring(22));
|
||||
final String newCpe2 = String.format("cpe:/a:oracle:opensso_enterprise:%s", i.getValue().substring(22));
|
||||
final String newCpe3 = String.format("cpe:/a:sun:opensso:%s", i.getValue().substring(22));
|
||||
final String newCpe4 = String.format("cpe:/a:oracle:opensso:%s", i.getValue().substring(22));
|
||||
for (final Identifier identifier : dependency.getIdentifiers()) {
|
||||
if ("cpe".equals(identifier.getType()) && identifier.getValue() != null
|
||||
&& (identifier.getValue().startsWith("cpe:/a:oracle:opensso:")
|
||||
|| identifier.getValue().startsWith("cpe:/a:oracle:opensso_enterprise:")
|
||||
|| identifier.getValue().startsWith("cpe:/a:sun:opensso_enterprise:")
|
||||
|| identifier.getValue().startsWith("cpe:/a:sun:opensso:"))) {
|
||||
final String newCpe = String.format("cpe:/a:sun:opensso_enterprise:%s", identifier.getValue().substring(22));
|
||||
final String newCpe2 = String.format("cpe:/a:oracle:opensso_enterprise:%s", identifier.getValue().substring(22));
|
||||
final String newCpe3 = String.format("cpe:/a:sun:opensso:%s", identifier.getValue().substring(22));
|
||||
final String newCpe4 = String.format("cpe:/a:oracle:opensso:%s", identifier.getValue().substring(22));
|
||||
try {
|
||||
dependency.addIdentifier("cpe",
|
||||
newCpe,
|
||||
@@ -424,28 +423,30 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer {
|
||||
String parentPath = dependency.getFilePath().toLowerCase();
|
||||
if (parentPath.contains(".jar")) {
|
||||
parentPath = parentPath.substring(0, parentPath.indexOf(".jar") + 4);
|
||||
final Dependency parent = findDependency(parentPath, engine.getDependencies());
|
||||
if (parent != null) {
|
||||
boolean remove = false;
|
||||
for (Identifier i : dependency.getIdentifiers()) {
|
||||
if ("cpe".equals(i.getType())) {
|
||||
final String trimmedCPE = trimCpeToVendor(i.getValue());
|
||||
for (Identifier parentId : parent.getIdentifiers()) {
|
||||
if ("cpe".equals(parentId.getType()) && parentId.getValue().startsWith(trimmedCPE)) {
|
||||
remove |= true;
|
||||
final List<Dependency> dependencies = engine.getDependencies();
|
||||
synchronized (dependencies) {
|
||||
final Dependency parent = findDependency(parentPath, dependencies);
|
||||
if (parent != null) {
|
||||
boolean remove = false;
|
||||
for (Identifier i : dependency.getIdentifiers()) {
|
||||
if ("cpe".equals(i.getType())) {
|
||||
final String trimmedCPE = trimCpeToVendor(i.getValue());
|
||||
for (Identifier parentId : parent.getIdentifiers()) {
|
||||
if ("cpe".equals(parentId.getType()) && parentId.getValue().startsWith(trimmedCPE)) {
|
||||
remove |= true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!remove) { //we can escape early
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!remove) { //we can escape early
|
||||
return;
|
||||
if (remove) {
|
||||
dependencies.remove(dependency);
|
||||
}
|
||||
}
|
||||
if (remove) {
|
||||
engine.getDependencies().remove(dependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -473,8 +474,8 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer {
|
||||
*/
|
||||
private String trimCpeToVendor(String value) {
|
||||
//cpe:/a:jruby:jruby:1.0.8
|
||||
final int pos1 = value.indexOf(":", 7); //right of vendor
|
||||
final int pos2 = value.indexOf(":", pos1 + 1); //right of product
|
||||
final int pos1 = value.indexOf(':', 7); //right of vendor
|
||||
final int pos2 = value.indexOf(':', pos1 + 1); //right of product
|
||||
if (pos2 < 0) {
|
||||
return value;
|
||||
} else {
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
package org.owasp.dependencycheck.analyzer;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.io.filefilter.NameFileFilter;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
import org.owasp.dependencycheck.dependency.Confidence;
|
||||
@@ -31,7 +34,7 @@ import org.owasp.dependencycheck.utils.DependencyVersionUtil;
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
public class FileNameAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
public class FileNameAnalyzer extends AbstractAnalyzer {
|
||||
|
||||
//<editor-fold defaultstate="collapsed" desc="All standard implementation details of Analyzer">
|
||||
/**
|
||||
@@ -48,6 +51,7 @@ public class FileNameAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
*
|
||||
* @return the name of the analyzer.
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return ANALYZER_NAME;
|
||||
}
|
||||
@@ -57,59 +61,60 @@ public class FileNameAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
*
|
||||
* @return the phase that the analyzer is intended to run in.
|
||||
*/
|
||||
@Override
|
||||
public AnalysisPhase getAnalysisPhase() {
|
||||
return ANALYSIS_PHASE;
|
||||
}
|
||||
//</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.
|
||||
*
|
||||
* @param dependency the dependency to analyze.
|
||||
* @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
|
||||
public void analyze(Dependency dependency, Engine engine) throws AnalysisException {
|
||||
|
||||
//strip any path information that may get added by ArchiveAnalyzer, etc.
|
||||
final File f = dependency.getActualFile();
|
||||
String fileName = f.getName();
|
||||
|
||||
//remove file extension
|
||||
final int pos = fileName.lastIndexOf(".");
|
||||
if (pos > 0) {
|
||||
fileName = fileName.substring(0, pos);
|
||||
}
|
||||
final String fileName = FilenameUtils.removeExtension(f.getName());
|
||||
|
||||
//add version evidence
|
||||
final DependencyVersion version = DependencyVersionUtil.parseVersion(fileName);
|
||||
final String packageName = DependencyVersionUtil.parsePreVersion(fileName);
|
||||
if (version != null) {
|
||||
// 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
|
||||
// struts2-core.jar
|
||||
if (version.getVersionParts() == null || version.getVersionParts().size() < 2) {
|
||||
dependency.getVersionEvidence().addEvidence("file", "name",
|
||||
dependency.getVersionEvidence().addEvidence("file", "version",
|
||||
version.toString(), Confidence.MEDIUM);
|
||||
} else {
|
||||
dependency.getVersionEvidence().addEvidence("file", "name",
|
||||
dependency.getVersionEvidence().addEvidence("file", "version",
|
||||
version.toString(), Confidence.HIGHEST);
|
||||
}
|
||||
dependency.getVersionEvidence().addEvidence("file", "name",
|
||||
fileName, Confidence.MEDIUM);
|
||||
packageName, Confidence.MEDIUM);
|
||||
}
|
||||
|
||||
//add as vendor and product evidence
|
||||
if (fileName.contains("-")) {
|
||||
if (!IGNORED_FILES.accept(f)) {
|
||||
dependency.getProductEvidence().addEvidence("file", "name",
|
||||
fileName, Confidence.HIGHEST);
|
||||
packageName, Confidence.HIGH);
|
||||
dependency.getVendorEvidence().addEvidence("file", "name",
|
||||
fileName, Confidence.HIGHEST);
|
||||
} else {
|
||||
dependency.getProductEvidence().addEvidence("file", "name",
|
||||
fileName, Confidence.HIGH);
|
||||
dependency.getVendorEvidence().addEvidence("file", "name",
|
||||
fileName, Confidence.HIGH);
|
||||
packageName, Confidence.HIGH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,21 +17,41 @@
|
||||
*/
|
||||
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.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
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.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
|
||||
*/
|
||||
public class HintAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
public class HintAnalyzer extends AbstractAnalyzer {
|
||||
|
||||
//<editor-fold defaultstate="collapsed" desc="All standard implementation details of Analyzer">
|
||||
/**
|
||||
@@ -62,18 +82,101 @@ public class HintAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
public AnalysisPhase getAnalysisPhase() {
|
||||
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>
|
||||
|
||||
/**
|
||||
* The HintAnalyzer uses knowledge about a dependency to add additional information to help in identification of identifiers
|
||||
* or vulnerabilities.
|
||||
* The Logger for use throughout the class
|
||||
*/
|
||||
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 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
|
||||
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",
|
||||
"Implementation-Title",
|
||||
"Spring Framework",
|
||||
@@ -104,6 +207,21 @@ public class HintAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
"spring-security-core",
|
||||
Confidence.HIGH);
|
||||
|
||||
final Evidence symfony = new Evidence("composer.lock",
|
||||
"vendor",
|
||||
"symfony",
|
||||
Confidence.HIGHEST);
|
||||
|
||||
final Evidence zendframeworkVendor = new Evidence("composer.lock",
|
||||
"vendor",
|
||||
"zendframework",
|
||||
Confidence.HIGHEST);
|
||||
|
||||
final Evidence zendframeworkProduct = new Evidence("composer.lock",
|
||||
"product",
|
||||
"zendframework",
|
||||
Confidence.HIGHEST);
|
||||
|
||||
//springsource/vware problem
|
||||
final Set<Evidence> product = dependency.getProductEvidence().getEvidence();
|
||||
final Set<Evidence> vendor = dependency.getVendorEvidence().getEvidence();
|
||||
@@ -128,6 +246,18 @@ public class HintAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
dependency.getVendorEvidence().addEvidence("hint analyzer", "vendor", "vmware", Confidence.HIGH);
|
||||
}
|
||||
|
||||
if (vendor.contains(symfony)) {
|
||||
dependency.getVendorEvidence().addEvidence("hint analyzer", "vendor", "sensiolabs", Confidence.HIGHEST);
|
||||
}
|
||||
|
||||
if (vendor.contains(zendframeworkVendor)) {
|
||||
dependency.getVendorEvidence().addEvidence("hint analyzer", "vendor", "zend", Confidence.HIGHEST);
|
||||
}
|
||||
|
||||
if (product.contains(zendframeworkProduct)) {
|
||||
dependency.getProductEvidence().addEvidence("hint analyzer", "vendor", "zend_framework", Confidence.HIGHEST);
|
||||
}
|
||||
|
||||
//sun/oracle problem
|
||||
final Iterator<Evidence> itr = dependency.getVendorEvidence().iterator();
|
||||
final List<Evidence> newEntries = new ArrayList<Evidence>();
|
||||
@@ -144,6 +274,90 @@ public class HintAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
for (Evidence e : newEntries) {
|
||||
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 hints 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
package org.owasp.dependencycheck.analyzer;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileOutputStream;
|
||||
@@ -27,7 +26,6 @@ import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -36,18 +34,22 @@ import java.util.Map.Entry;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.ZipEntry;
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.jsoup.Jsoup;
|
||||
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.exception.InitializationException;
|
||||
import org.owasp.dependencycheck.utils.FileFilterBuilder;
|
||||
import org.owasp.dependencycheck.xml.pom.License;
|
||||
import org.owasp.dependencycheck.xml.pom.PomUtils;
|
||||
@@ -58,7 +60,8 @@ import org.slf4j.Logger;
|
||||
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
|
||||
*/
|
||||
@@ -70,19 +73,17 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
*/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(JarAnalyzer.class);
|
||||
/**
|
||||
* The buffer size to use when extracting files from the archive.
|
||||
* The count of directories created during analysis. This is used for
|
||||
* creating temporary directories.
|
||||
*/
|
||||
private static final int BUFFER_SIZE = 4096;
|
||||
/**
|
||||
* The count of directories created during analysis. This is used for creating temporary directories.
|
||||
*/
|
||||
private static int dirCount = 0;
|
||||
private static final AtomicInteger DIR_COUNT = new AtomicInteger(0);
|
||||
/**
|
||||
* The system independent newline character.
|
||||
*/
|
||||
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(
|
||||
"Sun Java System Application Server");
|
||||
@@ -125,7 +126,8 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
"ipojo-extension",
|
||||
"eclipse-sourcereferences");
|
||||
/**
|
||||
* Deprecated Jar manifest attribute, that is, nonetheless, useful for analysis.
|
||||
* Deprecated Jar manifest attribute, that is, nonetheless, useful for
|
||||
* analysis.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
private static final String IMPLEMENTATION_VENDOR_ID = Attributes.Name.IMPLEMENTATION_VENDOR_ID
|
||||
@@ -198,13 +200,15 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
*
|
||||
* @return the phase that the analyzer is intended to run in.
|
||||
*/
|
||||
@Override
|
||||
public AnalysisPhase getAnalysisPhase() {
|
||||
return ANALYSIS_PHASE;
|
||||
}
|
||||
//</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
|
||||
*/
|
||||
@@ -214,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
|
||||
* information.
|
||||
* Loads a specified JAR file and collects information from the manifest and
|
||||
* checksums to identify the correct CPE information.
|
||||
*
|
||||
* @param dependency the dependency to analyze.
|
||||
* @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
|
||||
public void analyzeFileType(Dependency dependency, Engine engine) throws AnalysisException {
|
||||
@@ -243,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
|
||||
* attempt to interpolate the strings contained within the pom.properties if one exists.
|
||||
* Attempts to find a pom.xml within the JAR file. If found it extracts
|
||||
* 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 classes a collection of class name information
|
||||
* @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
|
||||
*/
|
||||
protected boolean analyzePOM(Dependency dependency, List<ClassNameInformation> classes, Engine engine) throws AnalysisException {
|
||||
@@ -272,8 +279,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
File externalPom = null;
|
||||
if (pomEntries.isEmpty()) {
|
||||
String pomPath = dependency.getActualFilePath();
|
||||
pomPath = pomPath.substring(0, pomPath.lastIndexOf('.')) + ".pom";
|
||||
final String pomPath = FilenameUtils.removeExtension(dependency.getActualFilePath()) + ".pom";
|
||||
externalPom = new File(pomPath);
|
||||
if (externalPom.isFile()) {
|
||||
pomEntries.add(pomPath);
|
||||
@@ -312,18 +318,19 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
pom.processProperties(pomProperties);
|
||||
setPomEvidence(newDependency, pom, null);
|
||||
engine.getDependencies().add(newDependency);
|
||||
Collections.sort(engine.getDependencies());
|
||||
} else {
|
||||
if (externalPom == null) {
|
||||
pom = PomUtils.readPom(path, jar);
|
||||
} else {
|
||||
pom = PomUtils.readPom(externalPom);
|
||||
}
|
||||
pom.processProperties(pomProperties);
|
||||
foundSomething |= setPomEvidence(dependency, pom, classes);
|
||||
if (pom != null) {
|
||||
pom.processProperties(pomProperties);
|
||||
foundSomething |= setPomEvidence(dependency, pom, classes);
|
||||
}
|
||||
}
|
||||
} catch (AnalysisException ex) {
|
||||
LOGGER.warn("An error occured while analyzing '{}'.", dependency.getActualFilePath());
|
||||
LOGGER.warn("An error occurred while analyzing '{}'.", dependency.getActualFilePath());
|
||||
LOGGER.trace("", ex);
|
||||
}
|
||||
}
|
||||
@@ -331,12 +338,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 jar the JarFile to load the pom.properties from
|
||||
* @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 {
|
||||
Properties pomProperties = null;
|
||||
@@ -363,7 +372,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
|
||||
* @return a list of pom.xml entries
|
||||
@@ -390,32 +400,27 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
* @param jar the jar file to extract the pom from
|
||||
* @param dependency the dependency being analyzed
|
||||
* @return returns the POM object
|
||||
* @throws AnalysisException is thrown if there is an exception extracting or parsing the POM
|
||||
* {@link org.owasp.dependencycheck.xml.pom.Model} object
|
||||
* @throws AnalysisException is thrown if there is an exception extracting
|
||||
* or parsing the POM {@link org.owasp.dependencycheck.xml.pom.Model} object
|
||||
*/
|
||||
private Model extractPom(String path, JarFile jar, Dependency dependency) throws AnalysisException {
|
||||
InputStream input = null;
|
||||
FileOutputStream fos = null;
|
||||
BufferedOutputStream bos = null;
|
||||
final File tmpDir = getNextTempDirectory();
|
||||
final File file = new File(tmpDir, "pom.xml");
|
||||
try {
|
||||
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);
|
||||
fos = new FileOutputStream(file);
|
||||
bos = new BufferedOutputStream(fos, BUFFER_SIZE);
|
||||
int count;
|
||||
final byte[] data = new byte[BUFFER_SIZE];
|
||||
while ((count = input.read(data, 0, BUFFER_SIZE)) != -1) {
|
||||
bos.write(data, 0, count);
|
||||
}
|
||||
bos.flush();
|
||||
IOUtils.copy(input, fos);
|
||||
dependency.setActualFilePath(file.getAbsolutePath());
|
||||
} catch (IOException ex) {
|
||||
LOGGER.warn("An error occurred reading '{}' from '{}'.", path, dependency.getFilePath());
|
||||
LOGGER.error("", ex);
|
||||
} finally {
|
||||
closeStream(bos);
|
||||
closeStream(fos);
|
||||
closeStream(input);
|
||||
}
|
||||
@@ -457,9 +462,10 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
*
|
||||
* @param dependency the dependency to set data on
|
||||
* @param pom the information from the pom
|
||||
* @param classes a collection of ClassNameInformation - containing data 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
|
||||
* @param classes a collection of ClassNameInformation - containing data
|
||||
* 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
|
||||
*/
|
||||
public static boolean setPomEvidence(Dependency dependency, Model pom, List<ClassNameInformation> classes) {
|
||||
boolean foundSomething = false;
|
||||
@@ -485,7 +491,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
|
||||
final String originalGroupID = groupid;
|
||||
if (groupid.startsWith("org.") || groupid.startsWith("com.")) {
|
||||
if (groupid != null && (groupid.startsWith("org.") || groupid.startsWith("com."))) {
|
||||
groupid = groupid.substring(4);
|
||||
}
|
||||
|
||||
@@ -494,7 +500,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
|
||||
final String originalArtifactID = artifactid;
|
||||
if (artifactid.startsWith("org.") || artifactid.startsWith("com.")) {
|
||||
if (artifactid != null && (artifactid.startsWith("org.") || artifactid.startsWith("com."))) {
|
||||
artifactid = artifactid.substring(4);
|
||||
}
|
||||
|
||||
@@ -576,17 +582,25 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
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);
|
||||
return foundSomething;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyzes the path information of the classes contained within the JarAnalyzer to try and determine possible vendor or
|
||||
* product names. If any are found they are stored in the packageVendor and packageProduct hashSets.
|
||||
* Analyzes the path information of the classes contained within the
|
||||
* 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 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,
|
||||
Dependency dependency, boolean addPackagesAsEvidence) {
|
||||
@@ -621,11 +635,13 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
|
||||
/**
|
||||
* <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>
|
||||
* <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
|
||||
* Description</li> <li>Main Class</li> </ul>
|
||||
* <li>Implementation VendorId</li> <li>Bundle Name</li> <li>Bundle
|
||||
* 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.
|
||||
*
|
||||
* @param dependency A reference to the dependency
|
||||
@@ -638,11 +654,8 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
JarFile jar = null;
|
||||
try {
|
||||
jar = new JarFile(dependency.getActualFilePath());
|
||||
|
||||
final Manifest manifest = jar.getManifest();
|
||||
|
||||
if (manifest == null) {
|
||||
//don't log this for javadoc or sources jar files
|
||||
if (!dependency.getFileName().toLowerCase().endsWith("-sources.jar")
|
||||
&& !dependency.getFileName().toLowerCase().endsWith("-javadoc.jar")
|
||||
&& !dependency.getFileName().toLowerCase().endsWith("-src.jar")
|
||||
@@ -652,17 +665,13 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
final Attributes atts = manifest.getMainAttributes();
|
||||
|
||||
final EvidenceCollection vendorEvidence = dependency.getVendorEvidence();
|
||||
final EvidenceCollection productEvidence = dependency.getProductEvidence();
|
||||
final EvidenceCollection versionEvidence = dependency.getVersionEvidence();
|
||||
|
||||
final String source = "Manifest";
|
||||
|
||||
String source = "Manifest";
|
||||
String specificationVersion = null;
|
||||
boolean hasImplementationVersion = false;
|
||||
|
||||
Attributes atts = manifest.getMainAttributes();
|
||||
for (Entry<Object, Object> entry : atts.entrySet()) {
|
||||
String key = entry.getKey().toString();
|
||||
String value = atts.getValue(key);
|
||||
@@ -680,7 +689,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
foundSomething = true;
|
||||
versionEvidence.addEvidence(source, key, value, Confidence.HIGH);
|
||||
} else if ("specification-version".equalsIgnoreCase(key)) {
|
||||
specificationVersion = key;
|
||||
specificationVersion = value;
|
||||
} else if (key.equalsIgnoreCase(Attributes.Name.IMPLEMENTATION_VENDOR.toString())) {
|
||||
foundSomething = true;
|
||||
vendorEvidence.addEvidence(source, key, value, Confidence.HIGH);
|
||||
@@ -692,7 +701,6 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
} else if (key.equalsIgnoreCase(BUNDLE_DESCRIPTION)) {
|
||||
foundSomething = true;
|
||||
addDescription(dependency, value, "manifest", key);
|
||||
//productEvidence.addEvidence(source, key, value, Confidence.MEDIUM);
|
||||
addMatchingValues(classInformation, value, productEvidence);
|
||||
} else if (key.equalsIgnoreCase(BUNDLE_NAME)) {
|
||||
foundSomething = true;
|
||||
@@ -700,25 +708,14 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
addMatchingValues(classInformation, value, productEvidence);
|
||||
// //the following caused false positives.
|
||||
// } 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)) {
|
||||
foundSomething = true;
|
||||
versionEvidence.addEvidence(source, key, value, Confidence.HIGH);
|
||||
} else if (key.equalsIgnoreCase(Attributes.Name.MAIN_CLASS.toString())) {
|
||||
continue;
|
||||
//skipping main class as if this has important information to add
|
||||
// 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);
|
||||
//skipping main class as if this has important information to add it will be added during class name analysis...
|
||||
} else {
|
||||
key = key.toLowerCase();
|
||||
|
||||
if (!IGNORE_KEYS.contains(key)
|
||||
&& !key.endsWith("jdk")
|
||||
&& !key.contains("lastmodified")
|
||||
@@ -730,12 +727,9 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
&& !value.trim().startsWith("scm:")
|
||||
&& !isImportPackage(key, value)
|
||||
&& !isPackage(key, value)) {
|
||||
|
||||
foundSomething = true;
|
||||
if (key.contains("version")) {
|
||||
if (!key.contains("specification")) {
|
||||
//versionEvidence.addEvidence(source, key, value, Confidence.LOW);
|
||||
//} else {
|
||||
versionEvidence.addEvidence(source, key, value, Confidence.MEDIUM);
|
||||
}
|
||||
} else if ("build-id".equals(key)) {
|
||||
@@ -765,21 +759,19 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
addMatchingValues(classInformation, value, productEvidence);
|
||||
} else if (key.contains("license")) {
|
||||
addLicense(dependency, value);
|
||||
} else if (key.contains("description")) {
|
||||
addDescription(dependency, value, "manifest", key);
|
||||
} else {
|
||||
if (key.contains("description")) {
|
||||
addDescription(dependency, value, "manifest", key);
|
||||
} else {
|
||||
productEvidence.addEvidence(source, key, value, Confidence.LOW);
|
||||
vendorEvidence.addEvidence(source, key, value, Confidence.LOW);
|
||||
addMatchingValues(classInformation, value, vendorEvidence);
|
||||
addMatchingValues(classInformation, value, productEvidence);
|
||||
if (value.matches(".*\\d.*")) {
|
||||
final StringTokenizer tokenizer = new StringTokenizer(value, " ");
|
||||
while (tokenizer.hasMoreElements()) {
|
||||
final String s = tokenizer.nextToken();
|
||||
if (s.matches("^[0-9.]+$")) {
|
||||
versionEvidence.addEvidence(source, key, s, Confidence.LOW);
|
||||
}
|
||||
productEvidence.addEvidence(source, key, value, Confidence.LOW);
|
||||
vendorEvidence.addEvidence(source, key, value, Confidence.LOW);
|
||||
addMatchingValues(classInformation, value, vendorEvidence);
|
||||
addMatchingValues(classInformation, value, productEvidence);
|
||||
if (value.matches(".*\\d.*")) {
|
||||
final StringTokenizer tokenizer = new StringTokenizer(value, " ");
|
||||
while (tokenizer.hasMoreElements()) {
|
||||
final String s = tokenizer.nextToken();
|
||||
if (s.matches("^[0-9.]+$")) {
|
||||
versionEvidence.addEvidence(source, key, s, Confidence.LOW);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -787,9 +779,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) {
|
||||
foundSomething = true;
|
||||
versionEvidence.addEvidence(source, "specificationn-version", specificationVersion, Confidence.HIGH);
|
||||
versionEvidence.addEvidence(source, "specification-version", specificationVersion, Confidence.HIGH);
|
||||
}
|
||||
} finally {
|
||||
if (jar != null) {
|
||||
@@ -800,15 +817,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,
|
||||
* then the description used will be trimmed to that position:
|
||||
* <ul><li>"such as"</li><li>"like "</li><li>"will use "</li><li>"* uses "</li></ul>
|
||||
* Adds a description to the given dependency. If the description contains
|
||||
* one of the following strings beyond 100 characters, then the description
|
||||
* 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 description the description
|
||||
* @param source the source 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) {
|
||||
if (dependency.getDescription() == null) {
|
||||
@@ -846,10 +866,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
|
||||
if (pos > 0) {
|
||||
final StringBuilder sb = new StringBuilder(pos + 3);
|
||||
sb.append(desc.substring(0, pos));
|
||||
sb.append("...");
|
||||
desc = sb.toString();
|
||||
desc = desc.substring(0, pos) + "...";
|
||||
}
|
||||
dependency.getProductEvidence().addEvidence(source, key, desc, Confidence.LOW);
|
||||
dependency.getVendorEvidence().addEvidence(source, key, desc, Confidence.LOW);
|
||||
@@ -882,19 +899,27 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
/**
|
||||
* 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
|
||||
public void initializeFileTypeAnalyzer() throws Exception {
|
||||
final File baseDir = Settings.getTempDirectory();
|
||||
tempFileLocation = File.createTempFile("check", "tmp", baseDir);
|
||||
if (!tempFileLocation.delete()) {
|
||||
final String msg = String.format("Unable to delete temporary file '%s'.", tempFileLocation.getAbsolutePath());
|
||||
throw new AnalysisException(msg);
|
||||
}
|
||||
if (!tempFileLocation.mkdirs()) {
|
||||
final String msg = String.format("Unable to create directory '%s'.", tempFileLocation.getAbsolutePath());
|
||||
throw new AnalysisException(msg);
|
||||
public void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
try {
|
||||
final File baseDir = Settings.getTempDirectory();
|
||||
tempFileLocation = File.createTempFile("check", "tmp", baseDir);
|
||||
if (!tempFileLocation.delete()) {
|
||||
final String msg = String.format("Unable to delete temporary file '%s'.", tempFileLocation.getAbsolutePath());
|
||||
setEnabled(false);
|
||||
throw new InitializationException(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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -906,18 +931,23 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
if (tempFileLocation != null && tempFileLocation.exists()) {
|
||||
LOGGER.debug("Attempting to delete temporary files");
|
||||
final boolean success = FileUtils.delete(tempFileLocation);
|
||||
if (!success) {
|
||||
LOGGER.warn("Failed to delete some temporary files, see the log for more details");
|
||||
if (!success && tempFileLocation.exists()) {
|
||||
final String[] l = tempFileLocation.list();
|
||||
if (l != null && l.length > 0) {
|
||||
LOGGER.warn("Failed to delete some temporary files, see the log for more details");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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) {
|
||||
final Pattern packageRx = Pattern.compile("^([a-zA-Z0-9_#\\$\\*\\.]+\\s*[,;]\\s*)+([a-zA-Z0-9_#\\$\\*\\.]+\\s*)?$");
|
||||
@@ -926,8 +956,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
|
||||
* does not include core Java package names (i.e. java.* or javax.*).
|
||||
* Cycles through an enumeration of JarEntries, contained within the
|
||||
* 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
|
||||
* @return an list of fully qualified class names
|
||||
@@ -963,12 +994,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.
|
||||
* This is helpful when analyzing vendor/product as many times this is included in the package name.
|
||||
* Cycles through the list of class names and places the package levels 0-3
|
||||
* 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 vendor HashMap of possible vendor names from package names (e.g. owasp)
|
||||
* @param product HashMap of possible product names from package names (e.g. dependencycheck)
|
||||
* @param vendor HashMap of possible vendor names from package names (e.g.
|
||||
* owasp)
|
||||
* @param product HashMap of possible product names from package names (e.g.
|
||||
* dependencycheck)
|
||||
*/
|
||||
private void analyzeFullyQualifiedClassNames(List<ClassNameInformation> classNames,
|
||||
Map<String, Integer> vendor, Map<String, Integer> product) {
|
||||
@@ -995,8 +1030,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
|
||||
* collection then the Integer is incremented by 1.
|
||||
* Adds an entry to the specified collection and sets the Integer (e.g. the
|
||||
* 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 key the key to add to the collection
|
||||
@@ -1010,9 +1046,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
|
||||
* value. If found, it will be added as the HIGHEST confidence evidence because we have more then one source corroborating the
|
||||
* value.
|
||||
* Cycles through the collection of class name information to see if parts
|
||||
* of the package names are contained in the provided value. If found, it
|
||||
* 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 value the value to check to see if it contains a package name
|
||||
@@ -1025,7 +1062,9 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
final String text = value.toLowerCase();
|
||||
for (ClassNameInformation cni : classes) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1033,7 +1072,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 value the value to check
|
||||
@@ -1047,7 +1087,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 dependency the dependency to add license information too
|
||||
@@ -1094,9 +1135,11 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Stores information about a given class name. This class will keep the fully qualified class name and a list of the
|
||||
* important parts of the package structure. Up to the first four levels of the package structure are stored, excluding a
|
||||
* leading "org" or "com". Example:</p>
|
||||
* Stores information about a given class name. This class will keep the
|
||||
* fully qualified class name and a list of the important parts of the
|
||||
* 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");
|
||||
* System.out.println(obj.getName());
|
||||
* for (String p : obj.getPackageStructure())
|
||||
@@ -1155,7 +1198,8 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
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>();
|
||||
|
||||
@@ -1176,7 +1220,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
* @throws AnalysisException thrown if unable to create temporary directory
|
||||
*/
|
||||
private File getNextTempDirectory() throws AnalysisException {
|
||||
dirCount += 1;
|
||||
final int dirCount = DIR_COUNT.incrementAndGet();
|
||||
final File directory = new File(tempFileLocation, String.valueOf(dirCount));
|
||||
//getting an exception for some directories not being able to be created; might be because the directory already exists?
|
||||
if (directory.exists()) {
|
||||
|
||||
@@ -35,6 +35,7 @@ import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
import org.owasp.dependencycheck.utils.DownloadFailedException;
|
||||
import org.owasp.dependencycheck.utils.Downloader;
|
||||
import org.owasp.dependencycheck.utils.FileFilterBuilder;
|
||||
@@ -42,15 +43,18 @@ import org.owasp.dependencycheck.utils.InvalidSettingException;
|
||||
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:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link org.owasp.dependencycheck.utils.Settings.KEYS#ANALYZER_NEXUS_ENABLED} determines whether this analyzer is even
|
||||
* enabled. This can be overridden by setting the system property.</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>
|
||||
* <li>{@link org.owasp.dependencycheck.utils.Settings.KEYS#ANALYZER_NEXUS_ENABLED}
|
||||
* determines whether this analyzer is even enabled. This can be overridden by
|
||||
* setting the system property.</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>
|
||||
*
|
||||
* @author colezlaw
|
||||
@@ -58,7 +62,8 @@ import org.owasp.dependencycheck.utils.Settings;
|
||||
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/";
|
||||
|
||||
@@ -82,6 +87,7 @@ public class NexusAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
*/
|
||||
private static final String SUPPORTED_EXTENSIONS = "jar";
|
||||
|
||||
private boolean useProxy;
|
||||
/**
|
||||
* The Nexus Search to be set up for this analyzer.
|
||||
*/
|
||||
@@ -95,7 +101,8 @@ public class NexusAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
/**
|
||||
* 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() {
|
||||
/* Enable this analyzer ONLY if the Nexus URL has been set to something
|
||||
@@ -104,7 +111,7 @@ public class NexusAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
*/
|
||||
boolean retval = false;
|
||||
try {
|
||||
if ((!DEFAULT_URL.equals(Settings.getString(Settings.KEYS.ANALYZER_NEXUS_URL)))
|
||||
if (!DEFAULT_URL.equals(Settings.getString(Settings.KEYS.ANALYZER_NEXUS_URL))
|
||||
&& Settings.getBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED)) {
|
||||
LOGGER.info("Enabling Nexus analyzer");
|
||||
retval = true;
|
||||
@@ -131,26 +138,25 @@ public class NexusAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
/**
|
||||
* 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
|
||||
public void initializeFileTypeAnalyzer() throws Exception {
|
||||
public void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
LOGGER.debug("Initializing Nexus Analyzer");
|
||||
LOGGER.debug("Nexus Analyzer enabled: {}", isEnabled());
|
||||
if (isEnabled()) {
|
||||
useProxy = useProxy();
|
||||
final String searchUrl = Settings.getString(Settings.KEYS.ANALYZER_NEXUS_URL);
|
||||
LOGGER.debug("Nexus Analyzer URL: {}", searchUrl);
|
||||
try {
|
||||
searcher = new NexusSearch(new URL(searchUrl));
|
||||
searcher = new NexusSearch(new URL(searchUrl), useProxy);
|
||||
if (!searcher.preflightRequest()) {
|
||||
LOGGER.warn("There was an issue getting Nexus status. Disabling analyzer.");
|
||||
setEnabled(false);
|
||||
throw new InitializationException("There was an issue getting Nexus status. Disabling analyzer.");
|
||||
}
|
||||
} 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);
|
||||
throw new InitializationException("Malformed URL to Nexus: " + searchUrl, mue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -166,7 +172,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
|
||||
*/
|
||||
@@ -240,14 +247,15 @@ public class NexusAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
LOGGER.warn("Unable to download pom.xml for {} from Nexus repository; "
|
||||
+ "this could result in undetected CPE/CVEs.", dependency.getFileName());
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IllegalArgumentException iae) {
|
||||
//dependency.addAnalysisException(new AnalysisException("Invalid SHA-1"));
|
||||
LOGGER.info(String.format("invalid sha-1 hash on %s", dependency.getFileName()));
|
||||
LOGGER.info("invalid sha-1 hash on {}", dependency.getFileName());
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
//dependency.addAnalysisException(new AnalysisException("Artifact not found on repository"));
|
||||
LOGGER.debug("Artifact not found in repository '{}'", dependency.getFileName());
|
||||
@@ -257,4 +265,19 @@ public class NexusAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
LOGGER.debug("Could not connect to nexus repository", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a proxy should be used.
|
||||
*
|
||||
* @return {@code true} if a proxy should be used
|
||||
*/
|
||||
public static boolean useProxy() {
|
||||
try {
|
||||
return Settings.getString(Settings.KEYS.PROXY_SERVER) != null
|
||||
&& Settings.getBoolean(Settings.KEYS.ANALYZER_NEXUS_USES_PROXY);
|
||||
} catch (InvalidSettingException ise) {
|
||||
LOGGER.warn("Failed to parse proxy settings.", ise);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* 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) 2015 Institute for Defense Analyses. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.analyzer;
|
||||
|
||||
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;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import javax.json.Json;
|
||||
import javax.json.JsonException;
|
||||
import javax.json.JsonObject;
|
||||
import javax.json.JsonReader;
|
||||
import javax.json.JsonString;
|
||||
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 associated CPE.
|
||||
*
|
||||
* @author Dale Visser
|
||||
*/
|
||||
@Experimental
|
||||
public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(NodePackageAnalyzer.class);
|
||||
|
||||
/**
|
||||
* The name of the analyzer.
|
||||
*/
|
||||
private static final String ANALYZER_NAME = "Node.js 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 PACKAGE_JSON = "package.json";
|
||||
/**
|
||||
* Filter that detects files named "package.json".
|
||||
*/
|
||||
private static final FileFilter PACKAGE_JSON_FILTER = FileFilterBuilder.newInstance()
|
||||
.addFilenames(PACKAGE_JSON).build();
|
||||
|
||||
/**
|
||||
* Returns the FileFilter
|
||||
*
|
||||
* @return the FileFilter
|
||||
*/
|
||||
@Override
|
||||
protected FileFilter getFileFilter() {
|
||||
return PACKAGE_JSON_FILTER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
// 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_NODE_PACKAGE_ENABLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void analyzeFileType(Dependency dependency, Engine engine)
|
||||
throws AnalysisException {
|
||||
final File file = dependency.getActualFile();
|
||||
JsonReader jsonReader;
|
||||
try {
|
||||
jsonReader = Json.createReader(FileUtils.openInputStream(file));
|
||||
} catch (IOException e) {
|
||||
throw new AnalysisException(
|
||||
"Problem occurred while reading dependency file.", e);
|
||||
}
|
||||
try {
|
||||
final JsonObject json = jsonReader.readObject();
|
||||
final EvidenceCollection productEvidence = dependency.getProductEvidence();
|
||||
final EvidenceCollection vendorEvidence = dependency.getVendorEvidence();
|
||||
if (json.containsKey("name")) {
|
||||
final Object value = json.get("name");
|
||||
if (value instanceof JsonString) {
|
||||
final String valueString = ((JsonString) value).getString();
|
||||
productEvidence.addEvidence(PACKAGE_JSON, "name", valueString, Confidence.HIGHEST);
|
||||
vendorEvidence.addEvidence(PACKAGE_JSON, "name_project", String.format("%s_project", valueString), Confidence.LOW);
|
||||
} else {
|
||||
LOGGER.warn("JSON value not string as expected: {}", value);
|
||||
}
|
||||
}
|
||||
addToEvidence(json, productEvidence, "description");
|
||||
addToEvidence(json, vendorEvidence, "author");
|
||||
addToEvidence(json, dependency.getVersionEvidence(), "version");
|
||||
dependency.setDisplayFileName(String.format("%s/%s", file.getParentFile().getName(), file.getName()));
|
||||
} catch (JsonException e) {
|
||||
LOGGER.warn("Failed to parse package.json file.", e);
|
||||
} finally {
|
||||
jsonReader.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds information to an evidence collection from the node json
|
||||
* configuration.
|
||||
*
|
||||
* @param json information from node.js
|
||||
* @param collection a set of evidence about a dependency
|
||||
* @param key the key to obtain the data from the json information
|
||||
*/
|
||||
private void addToEvidence(JsonObject json, EvidenceCollection collection, String key) {
|
||||
if (json.containsKey(key)) {
|
||||
final JsonValue value = json.get(key);
|
||||
if (value instanceof JsonString) {
|
||||
collection.addEvidence(PACKAGE_JSON, key, ((JsonString) value).getString(), Confidence.HIGHEST);
|
||||
} else if (value instanceof JsonObject) {
|
||||
final JsonObject jsonObject = (JsonObject) value;
|
||||
for (final Map.Entry<String, JsonValue> entry : jsonObject.entrySet()) {
|
||||
final String property = entry.getKey();
|
||||
final JsonValue subValue = entry.getValue();
|
||||
if (subValue instanceof JsonString) {
|
||||
collection.addEvidence(PACKAGE_JSON,
|
||||
String.format("%s.%s", key, property),
|
||||
((JsonString) subValue).getString(),
|
||||
Confidence.HIGHEST);
|
||||
} else {
|
||||
LOGGER.warn("JSON sub-value not string as expected: {}", subValue);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOGGER.warn("JSON value not string or JSON object as expected: {}", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ import java.io.FileFilter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @throws Exception if there's an error during initialization
|
||||
* @throws InitializationException if there's an error during initialization
|
||||
*/
|
||||
@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
|
||||
*/
|
||||
@@ -126,7 +128,7 @@ public class NuspecAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
*/
|
||||
@Override
|
||||
public void analyzeFileType(Dependency dependency, Engine engine) throws AnalysisException {
|
||||
LOGGER.debug("Checking Nuspec file {}", dependency.toString());
|
||||
LOGGER.debug("Checking Nuspec file {}", dependency);
|
||||
try {
|
||||
final NuspecParser parser = new XPathNuspecParser();
|
||||
NugetPackage np = null;
|
||||
|
||||
@@ -27,6 +27,8 @@ import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.dependency.Identifier;
|
||||
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
|
||||
@@ -34,8 +36,11 @@ import org.owasp.dependencycheck.dependency.Vulnerability;
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
public class NvdCveAnalyzer implements Analyzer {
|
||||
|
||||
public class NvdCveAnalyzer extends AbstractAnalyzer {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@@ -73,13 +78,13 @@ public class NvdCveAnalyzer implements Analyzer {
|
||||
* @return true or false.
|
||||
*/
|
||||
public boolean isOpen() {
|
||||
return (cveDB != null);
|
||||
return cveDB != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the CVE Database is closed.
|
||||
*
|
||||
* @throws Throwable when a throwable is thrown.
|
||||
* @throws Throwable an exception raised by this method
|
||||
*/
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
@@ -94,7 +99,7 @@ public class NvdCveAnalyzer implements Analyzer {
|
||||
*
|
||||
* @param dependency The Dependency to analyze
|
||||
* @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
|
||||
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.
|
||||
*
|
||||
* @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
|
||||
public void initialize() throws Exception {
|
||||
this.open();
|
||||
public void initialize() throws InitializationException {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,16 +28,21 @@ import org.owasp.dependencycheck.utils.Settings;
|
||||
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.owasp.dependencycheck.exception.InitializationException;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
/**
|
||||
* Hexadecimal.
|
||||
*/
|
||||
private static final int HEXADECIMAL = 16;
|
||||
/**
|
||||
* 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".
|
||||
*/
|
||||
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(
|
||||
"define\\s+OPENSSL_VERSION_NUMBER\\s+0x([0-9a-zA-Z]{8})L", Pattern.DOTALL
|
||||
| Pattern.CASE_INSENSITIVE);
|
||||
/**
|
||||
* The offset of the major version number.
|
||||
*/
|
||||
private static final int MAJOR_OFFSET = 28;
|
||||
/**
|
||||
* The mask for the minor version number.
|
||||
*/
|
||||
private static final long MINOR_MASK = 0x0ff00000L;
|
||||
/**
|
||||
* The offset of the minor version number.
|
||||
*/
|
||||
private static final int MINOR_OFFSET = 20;
|
||||
/**
|
||||
* The max for the fix version.
|
||||
*/
|
||||
private static final long FIX_MASK = 0x000ff000L;
|
||||
/**
|
||||
* The offset for the fix version.
|
||||
*/
|
||||
private static final int FIX_OFFSET = 12;
|
||||
/**
|
||||
* The mask for the patch version.
|
||||
*/
|
||||
private static final long PATCH_MASK = 0x00000ff0L;
|
||||
/**
|
||||
* The offset for the patch version.
|
||||
*/
|
||||
private static final int PATCH_OFFSET = 4;
|
||||
/**
|
||||
* Number of letters.
|
||||
*/
|
||||
private static final int NUM_LETTERS = 26;
|
||||
/**
|
||||
* The status mask.
|
||||
*/
|
||||
private static final int STATUS_MASK = 0x0000000f;
|
||||
|
||||
/**
|
||||
@@ -111,10 +146,10 @@ public class OpenSSLAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
/**
|
||||
* No-op initializer implementation.
|
||||
*
|
||||
* @throws Exception never thrown
|
||||
* @throws InitializationException never thrown
|
||||
*/
|
||||
@Override
|
||||
protected void initializeFileTypeAnalyzer() throws Exception {
|
||||
protected void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
@@ -123,7 +158,8 @@ public class OpenSSLAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
*
|
||||
* @param dependency the dependency being analyzed
|
||||
* @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
|
||||
protected void analyzeFileType(Dependency dependency, Engine engine)
|
||||
@@ -158,16 +194,19 @@ public class OpenSSLAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
*/
|
||||
private String getFileContents(final File actualFile)
|
||||
throws AnalysisException {
|
||||
String contents;
|
||||
try {
|
||||
contents = FileUtils.readFileToString(actualFile).trim();
|
||||
return FileUtils.readFileToString(actualFile, Charset.defaultCharset()).trim();
|
||||
} catch (IOException e) {
|
||||
throw new AnalysisException(
|
||||
"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
|
||||
protected String getAnalyzerEnabledSettingKey() {
|
||||
return Settings.KEYS.ANALYZER_OPENSSL_ENABLED;
|
||||
|
||||
@@ -23,10 +23,11 @@ import java.io.FileFilter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
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.SuffixFileFilter;
|
||||
import org.apache.commons.io.input.AutoCloseInputStream;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
import org.owasp.dependencycheck.dependency.Confidence;
|
||||
@@ -37,23 +38,27 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.internet.InternetHeaders;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
import org.owasp.dependencycheck.utils.ExtractionException;
|
||||
import org.owasp.dependencycheck.utils.ExtractionUtil;
|
||||
import org.owasp.dependencycheck.utils.FileFilterBuilder;
|
||||
import org.owasp.dependencycheck.utils.FileUtils;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.owasp.dependencycheck.utils.UrlStringUtils;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Used to analyze a Wheel or egg distribution files, or their contents in unzipped form, and collect information that can be used
|
||||
* to determine the associated CPE.
|
||||
* Used to analyze a Wheel or egg distribution files, or their contents in
|
||||
* 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 {
|
||||
|
||||
/**
|
||||
* Name of egg metatdata files to analyze.
|
||||
* Name of egg metadata files to analyze.
|
||||
*/
|
||||
private static final String PKG_INFO = "PKG-INFO";
|
||||
|
||||
@@ -69,9 +74,10 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
.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 final AtomicInteger DIR_COUNT = new AtomicInteger(0);
|
||||
|
||||
/**
|
||||
* The name of the analyzer.
|
||||
@@ -103,7 +109,8 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
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(
|
||||
".dist-info");
|
||||
@@ -163,7 +170,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
|
||||
*/
|
||||
@@ -205,7 +213,8 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
* @param dependency the archive being scanned
|
||||
* @param folderFilter the filter to apply to the folder
|
||||
* @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,
|
||||
FilenameFilter folderFilter, FilenameFilter metadataFilter)
|
||||
@@ -220,32 +229,43 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
throw new AnalysisException(ex);
|
||||
}
|
||||
|
||||
collectWheelMetadata(
|
||||
dependency,
|
||||
getMatchingFile(getMatchingFile(temp, folderFilter),
|
||||
metadataFilter));
|
||||
File matchingFile = getMatchingFile(temp, folderFilter);
|
||||
if (matchingFile != null) {
|
||||
matchingFile = getMatchingFile(matchingFile, metadataFilter);
|
||||
if (matchingFile != null) {
|
||||
collectWheelMetadata(dependency, matchingFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
protected void initializeFileTypeAnalyzer() throws Exception {
|
||||
final File baseDir = Settings.getTempDirectory();
|
||||
tempFileLocation = File.createTempFile("check", "tmp", baseDir);
|
||||
if (!tempFileLocation.delete()) {
|
||||
final String msg = String.format(
|
||||
"Unable to delete temporary file '%s'.",
|
||||
tempFileLocation.getAbsolutePath());
|
||||
throw new AnalysisException(msg);
|
||||
}
|
||||
if (!tempFileLocation.mkdirs()) {
|
||||
final String msg = String.format(
|
||||
"Unable to create directory '%s'.",
|
||||
tempFileLocation.getAbsolutePath());
|
||||
throw new AnalysisException(msg);
|
||||
protected void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
try {
|
||||
final File baseDir = Settings.getTempDirectory();
|
||||
tempFileLocation = File.createTempFile("check", "tmp", baseDir);
|
||||
if (!tempFileLocation.delete()) {
|
||||
setEnabled(false);
|
||||
final String msg = String.format(
|
||||
"Unable to delete temporary file '%s'.",
|
||||
tempFileLocation.getAbsolutePath());
|
||||
throw new InitializationException(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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,9 +277,11 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
if (tempFileLocation != null && tempFileLocation.exists()) {
|
||||
LOGGER.debug("Attempting to delete temporary files");
|
||||
final boolean success = FileUtils.delete(tempFileLocation);
|
||||
if (!success) {
|
||||
LOGGER.warn(
|
||||
"Failed to delete some temporary files, see the log for more details");
|
||||
if (!success && tempFileLocation.exists()) {
|
||||
final String[] l = tempFileLocation.list();
|
||||
if (l != null && l.length > 0) {
|
||||
LOGGER.warn("Failed to delete some temporary files, see the log for more details");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -269,10 +291,8 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
*
|
||||
* @param dependency the dependency being analyzed
|
||||
* @param file a reference to the manifest/properties file
|
||||
* @throws AnalysisException thrown when there is an error
|
||||
*/
|
||||
private static void collectWheelMetadata(Dependency dependency, File file)
|
||||
throws AnalysisException {
|
||||
private static void collectWheelMetadata(Dependency dependency, File file) {
|
||||
final InternetHeaders headers = getManifestProperties(file);
|
||||
addPropertyToEvidence(headers, dependency.getVersionEvidence(),
|
||||
"Version", Confidence.HIGHEST);
|
||||
@@ -313,7 +333,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 filter the filter to apply to the files in the directory
|
||||
@@ -339,20 +360,30 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
if (null == manifest) {
|
||||
LOGGER.debug("Manifest file not found.");
|
||||
} else {
|
||||
InputStream in = null;
|
||||
try {
|
||||
result.load(new AutoCloseInputStream(new BufferedInputStream(
|
||||
new FileInputStream(manifest))));
|
||||
in = new BufferedInputStream(new FileInputStream(manifest));
|
||||
result.load(in);
|
||||
} catch (MessagingException e) {
|
||||
LOGGER.warn(e.getMessage(), e);
|
||||
} catch (FileNotFoundException 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the next temporary destingation directory for extracting an archive.
|
||||
* Retrieves the next temporary destination directory for extracting an
|
||||
* archive.
|
||||
*
|
||||
* @return a directory
|
||||
* @throws AnalysisException thrown if unable to create temporary directory
|
||||
@@ -363,7 +394,7 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
// getting an exception for some directories not being able to be
|
||||
// created; might be because the directory already exists?
|
||||
do {
|
||||
dirCount += 1;
|
||||
final int dirCount = DIR_COUNT.incrementAndGet();
|
||||
directory = new File(tempFileLocation, String.valueOf(dirCount));
|
||||
} while (directory.exists());
|
||||
if (!directory.mkdirs()) {
|
||||
|
||||
@@ -28,23 +28,22 @@ import org.owasp.dependencycheck.dependency.EvidenceCollection;
|
||||
import org.owasp.dependencycheck.utils.FileFilterBuilder;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.owasp.dependencycheck.utils.UrlStringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.regex.Matcher;
|
||||
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 {
|
||||
|
||||
/**
|
||||
@@ -53,12 +52,6 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
private static final int REGEX_OPTIONS = Pattern.DOTALL
|
||||
| Pattern.CASE_INSENSITIVE;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*/
|
||||
private static final Logger LOGGER = LoggerFactory
|
||||
.getLogger(PythonPackageAnalyzer.class);
|
||||
|
||||
/**
|
||||
* Filename extensions for files to be analyzed.
|
||||
*/
|
||||
@@ -150,10 +143,10 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
/**
|
||||
* No-op initializer implementation.
|
||||
*
|
||||
* @throws Exception never thrown
|
||||
* @throws InitializationException never thrown
|
||||
*/
|
||||
@Override
|
||||
protected void initializeFileTypeAnalyzer() throws Exception {
|
||||
protected void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
@@ -174,7 +167,8 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
*
|
||||
* @param dependency the dependency being analyzed
|
||||
* @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
|
||||
protected void analyzeFileType(Dependency dependency, Engine engine)
|
||||
@@ -182,28 +176,29 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
final File file = dependency.getActualFile();
|
||||
final File parent = file.getParentFile();
|
||||
final String parentName = parent.getName();
|
||||
boolean found = false;
|
||||
if (INIT_PY_FILTER.accept(file)) {
|
||||
for (final File sourcefile : parent.listFiles(PY_FILTER)) {
|
||||
found |= analyzeFileContents(dependency, sourcefile);
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
//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.MEDIUM);
|
||||
"PackageName", parentName, Confidence.HIGHEST);
|
||||
|
||||
final File[] fileList = parent.listFiles(PY_FILTER);
|
||||
if (fileList != null) {
|
||||
for (final File sourceFile : fileList) {
|
||||
analyzeFileContents(dependency, sourceFile);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// copy, alter and set in case some other thread is iterating over
|
||||
final List<Dependency> deps = new ArrayList<Dependency>(
|
||||
engine.getDependencies());
|
||||
deps.remove(dependency);
|
||||
engine.setDependencies(deps);
|
||||
engine.getDependencies().remove(dependency);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This should gather information from leading docstrings, file comments, and assignments to __version__, __title__,
|
||||
* __summary__, __uri__, __url__, __home*page__, __author__, and their all caps equivalents.
|
||||
* This should gather information from leading docstrings, file comments,
|
||||
* and assignments to __version__, __title__, __summary__, __uri__, __url__,
|
||||
* __home*page__, __author__, and their all caps equivalents.
|
||||
*
|
||||
* @param dependency the dependency being analyzed
|
||||
* @param file the file name to analyze
|
||||
@@ -214,7 +209,7 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
throws AnalysisException {
|
||||
String contents;
|
||||
try {
|
||||
contents = FileUtils.readFileToString(file).trim();
|
||||
contents = FileUtils.readFileToString(file, Charset.defaultCharset()).trim();
|
||||
} catch (IOException e) {
|
||||
throw new AnalysisException(
|
||||
"Problem occurred while reading dependency file.", e);
|
||||
@@ -238,14 +233,10 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
.getVendorEvidence();
|
||||
found |= gatherEvidence(AUTHOR_PATTERN, contents, source,
|
||||
vendorEvidence, "SourceAuthor", Confidence.MEDIUM);
|
||||
try {
|
||||
found |= gatherHomePageEvidence(URI_PATTERN, vendorEvidence,
|
||||
source, "URL", contents);
|
||||
found |= gatherHomePageEvidence(HOMEPAGE_PATTERN,
|
||||
vendorEvidence, source, "HomePage", contents);
|
||||
} catch (MalformedURLException e) {
|
||||
LOGGER.warn(e.getMessage());
|
||||
}
|
||||
found |= gatherHomePageEvidence(URI_PATTERN, vendorEvidence,
|
||||
source, "URL", contents);
|
||||
found |= gatherHomePageEvidence(HOMEPAGE_PATTERN,
|
||||
vendorEvidence, source, "HomePage", contents);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
@@ -281,11 +272,10 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
* @param name the name of the evidence
|
||||
* @param contents the home page URL
|
||||
* @return true if evidence was collected; otherwise false
|
||||
* @throws MalformedURLException thrown if the URL is malformed
|
||||
*/
|
||||
private boolean gatherHomePageEvidence(Pattern pattern,
|
||||
EvidenceCollection evidence, String source, String name,
|
||||
String contents) throws MalformedURLException {
|
||||
String contents) {
|
||||
final Matcher matcher = pattern.matcher(contents);
|
||||
boolean found = false;
|
||||
if (matcher.find()) {
|
||||
@@ -299,7 +289,8 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gather evidence from a Python source file usin 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 contents of Python source file
|
||||
|
||||
@@ -0,0 +1,505 @@
|
||||
/*
|
||||
* 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) 2015 Institute for Defense Analyses. All Rights Reserved.
|
||||
*/
|
||||
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.owasp.dependencycheck.Engine;
|
||||
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.Dependency;
|
||||
import org.owasp.dependencycheck.dependency.Reference;
|
||||
import org.owasp.dependencycheck.dependency.Vulnerability;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
import org.owasp.dependencycheck.utils.FileFilterBuilder;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Used to analyze Ruby Bundler Gemspec.lock files utilizing the 3rd party
|
||||
* bundle-audit tool.
|
||||
*
|
||||
* @author Dale Visser
|
||||
*/
|
||||
@Experimental
|
||||
public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(RubyBundleAuditAnalyzer.class);
|
||||
|
||||
/**
|
||||
* The name of the analyzer.
|
||||
*/
|
||||
private static final String ANALYZER_NAME = "Ruby Bundle Audit Analyzer";
|
||||
|
||||
/**
|
||||
* The phase that this analyzer is intended to run in.
|
||||
*/
|
||||
private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.PRE_INFORMATION_COLLECTION;
|
||||
/**
|
||||
* The filter defining which files will be analyzed.
|
||||
*/
|
||||
private static final FileFilter FILTER = FileFilterBuilder.newInstance().addFilenames("Gemfile.lock").build();
|
||||
/**
|
||||
* Name.
|
||||
*/
|
||||
public static final String NAME = "Name: ";
|
||||
/**
|
||||
* Version.
|
||||
*/
|
||||
public static final String VERSION = "Version: ";
|
||||
/**
|
||||
* Advisory.
|
||||
*/
|
||||
public static final String ADVISORY = "Advisory: ";
|
||||
/**
|
||||
* Criticality.
|
||||
*/
|
||||
public static final String CRITICALITY = "Criticality: ";
|
||||
|
||||
/**
|
||||
* The DAL.
|
||||
*/
|
||||
private CveDB cvedb;
|
||||
|
||||
/**
|
||||
* @return a filter that accepts files named Gemfile.lock
|
||||
*/
|
||||
@Override
|
||||
protected FileFilter getFileFilter() {
|
||||
return FILTER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch bundle-audit.
|
||||
*
|
||||
* @param folder directory that contains bundle audit
|
||||
* @return a handle to the process
|
||||
* @throws AnalysisException thrown when there is an issue launching bundle
|
||||
* audit
|
||||
*/
|
||||
private Process launchBundleAudit(File folder) throws AnalysisException {
|
||||
if (!folder.isDirectory()) {
|
||||
throw new AnalysisException(String.format("%s should have been a directory.", folder.getAbsolutePath()));
|
||||
}
|
||||
final List<String> args = new ArrayList<String>();
|
||||
final String bundleAuditPath = Settings.getString(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_PATH);
|
||||
args.add(null == bundleAuditPath ? "bundle-audit" : bundleAuditPath);
|
||||
args.add("check");
|
||||
args.add("--verbose");
|
||||
final ProcessBuilder builder = new ProcessBuilder(args);
|
||||
builder.directory(folder);
|
||||
try {
|
||||
LOGGER.info("Launching: " + args + " from " + folder);
|
||||
return builder.start();
|
||||
} catch (IOException ioe) {
|
||||
throw new AnalysisException("bundle-audit failure", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the analyzer. In this case, extract GrokAssembly.exe to a
|
||||
* temporary location.
|
||||
*
|
||||
* @throws InitializationException if anything goes wrong
|
||||
*/
|
||||
@Override
|
||||
public void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
try {
|
||||
cvedb = new CveDB();
|
||||
cvedb.open();
|
||||
} catch (DatabaseException ex) {
|
||||
LOGGER.warn("Exception opening the database");
|
||||
LOGGER.debug("error", ex);
|
||||
setEnabled(false);
|
||||
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 {
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
reader = new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8"));
|
||||
if (!reader.ready()) {
|
||||
LOGGER.warn("Bundle-audit error stream unexpectedly not ready. Disabling " + ANALYZER_NAME);
|
||||
setEnabled(false);
|
||||
throw new InitializationException("Bundle-audit error stream unexpectedly not ready.");
|
||||
} else {
|
||||
final String line = reader.readLine();
|
||||
if (line == null || !line.contains("Errno::ENOENT")) {
|
||||
LOGGER.warn("Unexpected bundle-audit output. Disabling {}: {}", ANALYZER_NAME, line);
|
||||
setEnabled(false);
|
||||
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 {
|
||||
if (null != reader) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException ex) {
|
||||
LOGGER.debug("Error closing reader", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isEnabled()) {
|
||||
LOGGER.info(ANALYZER_NAME + " is enabled. It is necessary to manually run \"bundle-audit update\" "
|
||||
+ "occasionally to keep its database up to date.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_BUNDLE_AUDIT_ENABLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* If {@link #analyzeFileType(Dependency, Engine)} is called, then we have
|
||||
* successfully initialized, and it will be necessary to disable
|
||||
* {@link RubyGemspecAnalyzer}.
|
||||
*/
|
||||
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
|
||||
protected void analyzeFileType(Dependency dependency, Engine engine)
|
||||
throws AnalysisException {
|
||||
if (needToDisableGemspecAnalyzer) {
|
||||
boolean failed = true;
|
||||
final String className = RubyGemspecAnalyzer.class.getName();
|
||||
for (FileTypeAnalyzer analyzer : engine.getFileTypeAnalyzers()) {
|
||||
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);
|
||||
LOGGER.info("Disabled " + className + " to avoid noisy duplicate results.");
|
||||
failed = false;
|
||||
}
|
||||
}
|
||||
if (failed) {
|
||||
LOGGER.warn("Did not find " + className + '.');
|
||||
}
|
||||
needToDisableGemspecAnalyzer = false;
|
||||
}
|
||||
final File parentFile = dependency.getActualFile().getParentFile();
|
||||
final Process process = launchBundleAudit(parentFile);
|
||||
final int exitValue;
|
||||
try {
|
||||
exitValue = process.waitFor();
|
||||
} catch (InterruptedException ie) {
|
||||
throw new AnalysisException("bundle-audit process interrupted", ie);
|
||||
}
|
||||
if (exitValue < 0 || exitValue > 1) {
|
||||
final String msg = String.format("Unexpected exit code from bundle-audit process; exit code: %s", exitValue);
|
||||
throw new AnalysisException(msg);
|
||||
}
|
||||
BufferedReader rdr = null;
|
||||
BufferedReader errReader = null;
|
||||
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"));
|
||||
processBundlerAuditOutput(dependency, engine, rdr);
|
||||
} catch (IOException ioe) {
|
||||
LOGGER.warn("bundle-audit failure", ioe);
|
||||
} finally {
|
||||
if (errReader != null) {
|
||||
try {
|
||||
errReader.close();
|
||||
} catch (IOException ioe) {
|
||||
LOGGER.warn("bundle-audit close failure", ioe);
|
||||
}
|
||||
}
|
||||
if (null != rdr) {
|
||||
try {
|
||||
rdr.close();
|
||||
} catch (IOException ioe) {
|
||||
LOGGER.warn("bundle-audit close failure", ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
final String parentName = original.getActualFile().getParentFile().getName();
|
||||
final String fileName = original.getFileName();
|
||||
final String filePath = original.getFilePath();
|
||||
Dependency dependency = null;
|
||||
Vulnerability vulnerability = null;
|
||||
String gem = null;
|
||||
final Map<String, Dependency> map = new HashMap<String, Dependency>();
|
||||
boolean appendToDescription = false;
|
||||
while (rdr.ready()) {
|
||||
final String nextLine = rdr.readLine();
|
||||
if (null == nextLine) {
|
||||
break;
|
||||
} else if (nextLine.startsWith(NAME)) {
|
||||
appendToDescription = false;
|
||||
gem = nextLine.substring(NAME.length());
|
||||
if (!map.containsKey(gem)) {
|
||||
map.put(gem, createDependencyForGem(engine, parentName, fileName, filePath, gem));
|
||||
}
|
||||
dependency = map.get(gem);
|
||||
LOGGER.debug(String.format("bundle-audit (%s): %s", parentName, nextLine));
|
||||
} else if (nextLine.startsWith(VERSION)) {
|
||||
vulnerability = createVulnerability(parentName, dependency, gem, nextLine);
|
||||
} else if (nextLine.startsWith(ADVISORY)) {
|
||||
setVulnerabilityName(parentName, dependency, vulnerability, nextLine);
|
||||
} else if (nextLine.startsWith(CRITICALITY)) {
|
||||
addCriticalityToVulnerability(parentName, vulnerability, nextLine);
|
||||
} else if (nextLine.startsWith("URL: ")) {
|
||||
addReferenceToVulnerability(parentName, vulnerability, nextLine);
|
||||
} else if (nextLine.startsWith("Description:")) {
|
||||
appendToDescription = true;
|
||||
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. *** ");
|
||||
}
|
||||
} else if (appendToDescription) {
|
||||
if (null != vulnerability) {
|
||||
vulnerability.setDescription(vulnerability.getDescription() + nextLine + "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
final String advisory = nextLine.substring((ADVISORY.length()));
|
||||
if (null != vulnerability) {
|
||||
vulnerability.setName(advisory);
|
||||
}
|
||||
if (null != dependency) {
|
||||
dependency.getVulnerabilities().add(vulnerability); // needed to wait for vulnerability name to avoid NPE
|
||||
}
|
||||
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) {
|
||||
final String url = nextLine.substring(("URL: ").length());
|
||||
if (null != vulnerability) {
|
||||
final Reference ref = new Reference();
|
||||
ref.setName(vulnerability.getName());
|
||||
ref.setSource("bundle-audit");
|
||||
ref.setUrl(url);
|
||||
vulnerability.getReferences().add(ref);
|
||||
}
|
||||
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) {
|
||||
if (null != vulnerability) {
|
||||
final String criticality = nextLine.substring(CRITICALITY.length()).trim();
|
||||
float score = -1.0f;
|
||||
Vulnerability v = null;
|
||||
try {
|
||||
v = cvedb.getVulnerability(vulnerability.getName());
|
||||
} catch (DatabaseException ex) {
|
||||
LOGGER.debug("Unable to look up vulnerability {}", vulnerability.getName());
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
final String version = nextLine.substring(VERSION.length());
|
||||
dependency.getVersionEvidence().addEvidence(
|
||||
"bundler-audit",
|
||||
"Version",
|
||||
version,
|
||||
Confidence.HIGHEST);
|
||||
vulnerability = new Vulnerability(); // don't add to dependency until we have name set later
|
||||
vulnerability.setMatchedCPE(
|
||||
String.format("cpe:/a:%1$s_project:%1$s:%2$s::~~~ruby~~", gem, version),
|
||||
null);
|
||||
vulnerability.setCvssAccessVector("-");
|
||||
vulnerability.setCvssAccessComplexity("-");
|
||||
vulnerability.setCvssAuthentication("-");
|
||||
vulnerability.setCvssAvailabilityImpact("-");
|
||||
vulnerability.setCvssConfidentialityImpact("-");
|
||||
vulnerability.setCvssIntegrityImpact("-");
|
||||
}
|
||||
LOGGER.debug(String.format("bundle-audit (%s): %s", parentName, nextLine));
|
||||
return vulnerability;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
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.setDisplayFileName(displayFileName);
|
||||
dependency.setFileName(fileName);
|
||||
dependency.setFilePath(filePath);
|
||||
engine.getDependencies().add(dependency);
|
||||
return dependency;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* 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) 2015 Institute for Defense Analyses. All Rights Reserved.
|
||||
*/
|
||||
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.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.exception.InitializationException;
|
||||
import org.owasp.dependencycheck.utils.FileFilterBuilder;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Used to analyze Ruby Gem specifications and collect information that can be
|
||||
* used to determine the associated CPE. Regular expressions are used to parse
|
||||
* the well-defined Ruby syntax that forms the specification.
|
||||
*
|
||||
* @author Dale Visser
|
||||
*/
|
||||
@Experimental
|
||||
public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(RubyGemspecAnalyzer.class);
|
||||
/**
|
||||
* The name of the analyzer.
|
||||
*/
|
||||
private static final String ANALYZER_NAME = "Ruby Gemspec Analyzer";
|
||||
|
||||
/**
|
||||
* The phase that this analyzer is intended to run in.
|
||||
*/
|
||||
private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
|
||||
|
||||
/**
|
||||
* The gemspec file extension.
|
||||
*/
|
||||
private static final String GEMSPEC = "gemspec";
|
||||
|
||||
/**
|
||||
* The file filter containing the list of file extensions that can be
|
||||
* analyzed.
|
||||
*/
|
||||
private static final FileFilter FILTER = FileFilterBuilder.newInstance().addExtensions(GEMSPEC).build();
|
||||
//TODO: support Rakefile
|
||||
//= FileFilterBuilder.newInstance().addExtensions(GEMSPEC).addFilenames("Rakefile").build();
|
||||
|
||||
/**
|
||||
* The name of the version file.
|
||||
*/
|
||||
private static final String VERSION_FILE_NAME = "VERSION";
|
||||
|
||||
/**
|
||||
* @return a filter that accepts files matching the glob pattern, *.gemspec
|
||||
*/
|
||||
@Override
|
||||
protected FileFilter getFileFilter() {
|
||||
return FILTER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
// 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_RUBY_GEMSPEC_ENABLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* The capture group #1 is the block variable.
|
||||
*/
|
||||
private static final Pattern GEMSPEC_BLOCK_INIT = Pattern.compile("Gem::Specification\\.new\\s+?do\\s+?\\|(.+?)\\|");
|
||||
|
||||
@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 = GEMSPEC_BLOCK_INIT.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 String name = addStringEvidence(product, contents, blockVariable, "name", "name", Confidence.HIGHEST);
|
||||
if (!name.isEmpty()) {
|
||||
vendor.addEvidence(GEMSPEC, "name_project", name + "_project", Confidence.LOW);
|
||||
}
|
||||
addStringEvidence(product, contents, blockVariable, "summary", "summary", Confidence.LOW);
|
||||
|
||||
addStringEvidence(vendor, contents, blockVariable, "author", "authors?", Confidence.HIGHEST);
|
||||
addStringEvidence(vendor, contents, blockVariable, "email", "emails?", Confidence.MEDIUM);
|
||||
addStringEvidence(vendor, contents, blockVariable, "homepage", "homepage", Confidence.HIGHEST);
|
||||
addStringEvidence(vendor, contents, blockVariable, "license", "licen[cs]es?", Confidence.HIGHEST);
|
||||
|
||||
final String value = addStringEvidence(dependency.getVersionEvidence(), contents,
|
||||
blockVariable, "version", "version", Confidence.HIGHEST);
|
||||
if (value.length() < 1) {
|
||||
addEvidenceFromVersionFile(dependency.getActualFile(), dependency.getVersionEvidence());
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
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*?\\[(.*?)\\]", 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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ package org.owasp.dependencycheck.analyzer;
|
||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
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.
|
||||
|
||||
@@ -51,7 +51,7 @@ public class CentralSearch {
|
||||
/**
|
||||
* Whether to use the Proxy when making requests
|
||||
*/
|
||||
private boolean useProxy;
|
||||
private final boolean useProxy;
|
||||
|
||||
/**
|
||||
* Used for logging.
|
||||
@@ -61,8 +61,8 @@ public class CentralSearch {
|
||||
/**
|
||||
* 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
|
||||
* end in /select)
|
||||
* @param rootURL the URL of the repository on which searches should
|
||||
* execute. Only parameters are added to this (so it should end in /select)
|
||||
*/
|
||||
public CentralSearch(URL rootURL) {
|
||||
this.rootURL = rootURL;
|
||||
@@ -76,21 +76,23 @@ public class CentralSearch {
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the configured Central URL for the given sha1 hash. If the artifact is found, a <code>MavenArtifact</code> is
|
||||
* populated with the GAV.
|
||||
* Searches the configured Central URL for the given sha1 hash. If the
|
||||
* artifact is found, a <code>MavenArtifact</code> is populated with the
|
||||
* GAV.
|
||||
*
|
||||
* @param sha1 the SHA-1 hash string for which to search
|
||||
* @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 {
|
||||
if (null == sha1 || !sha1.matches("^[0-9A-Fa-f]{40}$")) {
|
||||
throw new IllegalArgumentException("Invalid SHA1 format");
|
||||
}
|
||||
|
||||
List<MavenArtifact> result = null;
|
||||
final URL url = new URL(rootURL + String.format("?q=1:\"%s\"&wt=xml", sha1));
|
||||
|
||||
LOGGER.debug("Searching Central url {}", url.toString());
|
||||
LOGGER.debug("Searching Central url {}", url);
|
||||
|
||||
// Determine if we need to use a proxy. The rules:
|
||||
// 1) If the proxy is set, AND the setting is set to true, use the proxy
|
||||
@@ -108,15 +110,16 @@ public class CentralSearch {
|
||||
if (conn.getResponseCode() == 200) {
|
||||
boolean missing = false;
|
||||
try {
|
||||
final DocumentBuilder builder = DocumentBuilderFactory
|
||||
.newInstance().newDocumentBuilder();
|
||||
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
final DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
final Document doc = builder.parse(conn.getInputStream());
|
||||
final XPath xpath = XPathFactory.newInstance().newXPath();
|
||||
final String numFound = xpath.evaluate("/response/result/@numFound", doc);
|
||||
if ("0".equals(numFound)) {
|
||||
missing = true;
|
||||
} else {
|
||||
final ArrayList<MavenArtifact> result = new ArrayList<MavenArtifact>();
|
||||
result = new ArrayList<MavenArtifact>();
|
||||
final NodeList docs = (NodeList) xpath.evaluate("/response/result/doc", doc, XPathConstants.NODESET);
|
||||
for (int i = 0; i < docs.getLength(); i++) {
|
||||
final String g = xpath.evaluate("./str[@name='g']", docs.item(i));
|
||||
@@ -144,16 +147,12 @@ public class CentralSearch {
|
||||
useHTTPS = true;
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.trace("Version: {}", v);
|
||||
result.add(new MavenArtifact(g, a, v, jarAvailable, pomAvailable, useHTTPS));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
// Anything else is jacked up XML stuff that we really can't recover
|
||||
// from well
|
||||
// Anything else is jacked up XML stuff that we really can't recover from well
|
||||
throw new IOException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
@@ -162,10 +161,9 @@ public class CentralSearch {
|
||||
}
|
||||
} else {
|
||||
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");
|
||||
}
|
||||
|
||||
return null;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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) 2015 The OWASP Foundation. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.composer;
|
||||
|
||||
/**
|
||||
* Reperesents a dependency (GAV, right now) from a Composer dependency.
|
||||
*
|
||||
* @author colezlaw
|
||||
*/
|
||||
public final class ComposerDependency {
|
||||
|
||||
/**
|
||||
* The group
|
||||
*/
|
||||
private final String group;
|
||||
|
||||
/**
|
||||
* The project
|
||||
*/
|
||||
private final String project;
|
||||
|
||||
/**
|
||||
* The version
|
||||
*/
|
||||
private final String version;
|
||||
|
||||
/**
|
||||
* Create a ComposerDependency from group, project, and version.
|
||||
*
|
||||
* @param group the group
|
||||
* @param project the project
|
||||
* @param version the version
|
||||
*/
|
||||
public ComposerDependency(String group, String project, String version) {
|
||||
this.group = group;
|
||||
this.project = project;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the group.
|
||||
*
|
||||
* @return the group
|
||||
*/
|
||||
public String getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the project.
|
||||
*
|
||||
* @return the project
|
||||
*/
|
||||
public String getProject() {
|
||||
return project;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the version.
|
||||
*
|
||||
* @return the version
|
||||
*/
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof ComposerDependency)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final ComposerDependency that = (ComposerDependency) o;
|
||||
|
||||
if (group != null ? !group.equals(that.group) : that.group != null) {
|
||||
return false;
|
||||
}
|
||||
if (project != null ? !project.equals(that.project) : that.project != null) {
|
||||
return false;
|
||||
}
|
||||
return !(version != null ? !version.equals(that.version) : that.version != null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = group != null ? group.hashCode() : 0;
|
||||
result = 31 * result + (project != null ? project.hashCode() : 0);
|
||||
result = 31 * result + (version != null ? version.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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) 2015 The OWASP Foundation. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.composer;
|
||||
|
||||
/**
|
||||
* Represents an exception when handling a composer.json or composer.lock file. Generally used to wrap a downstream exception.
|
||||
*
|
||||
* @author colezlaw
|
||||
*/
|
||||
public class ComposerException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* The serial version UID for serialization.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates a ComposerException with default message.
|
||||
*/
|
||||
public ComposerException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ComposerException with the specified message.
|
||||
*
|
||||
* @param message the exception message
|
||||
*/
|
||||
public ComposerException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Composer exception with the specified message and cause.
|
||||
*
|
||||
* @param message the message
|
||||
* @param cause the underlying cause
|
||||
*/
|
||||
public ComposerException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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) 2015 The OWASP Foundation. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.composer;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.json.Json;
|
||||
import javax.json.JsonArray;
|
||||
import javax.json.JsonException;
|
||||
import javax.json.JsonObject;
|
||||
import javax.json.JsonReader;
|
||||
import javax.json.stream.JsonParsingException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Parses a Composer.lock file from an input stream. In a separate class so it can hopefully be injected.
|
||||
*
|
||||
* @author colezlaw
|
||||
*/
|
||||
public class ComposerLockParser {
|
||||
|
||||
/**
|
||||
* The JsonReader for parsing JSON
|
||||
*/
|
||||
private final JsonReader jsonReader;
|
||||
|
||||
/**
|
||||
* The input stream we'll read
|
||||
*/
|
||||
private final InputStream inputStream; // NOPMD - it gets set in the constructor, read later
|
||||
|
||||
/**
|
||||
* The List of ComposerDependencies found
|
||||
*/
|
||||
private final List<ComposerDependency> composerDependencies;
|
||||
|
||||
/**
|
||||
* The LOGGER
|
||||
*/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ComposerLockParser.class);
|
||||
|
||||
/**
|
||||
* Createas a ComposerLockParser from a JsonReader and an InputStream.
|
||||
*
|
||||
* @param inputStream the InputStream to parse
|
||||
*/
|
||||
public ComposerLockParser(InputStream inputStream) {
|
||||
LOGGER.info("Creating a ComposerLockParser");
|
||||
this.inputStream = inputStream;
|
||||
this.jsonReader = Json.createReader(inputStream);
|
||||
this.composerDependencies = new ArrayList<ComposerDependency>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the input stream to create the list of dependencies.
|
||||
*/
|
||||
public void process() {
|
||||
LOGGER.info("Beginning Composer lock processing");
|
||||
try {
|
||||
final JsonObject composer = jsonReader.readObject();
|
||||
if (composer.containsKey("packages")) {
|
||||
LOGGER.debug("Found packages");
|
||||
final JsonArray packages = composer.getJsonArray("packages");
|
||||
for (JsonObject pkg : packages.getValuesAs(JsonObject.class)) {
|
||||
if (pkg.containsKey("name")) {
|
||||
final String groupName = pkg.getString("name");
|
||||
if (groupName.indexOf('/') >= 0 && groupName.indexOf('/') <= groupName.length() - 1) {
|
||||
if (pkg.containsKey("version")) {
|
||||
final String group = groupName.substring(0, groupName.indexOf('/'));
|
||||
final String project = groupName.substring(groupName.indexOf('/') + 1);
|
||||
String version = pkg.getString("version");
|
||||
// Some version nubmers begin with v - which doesn't end up matching CPE's
|
||||
if (version.startsWith("v")) {
|
||||
version = version.substring(1);
|
||||
}
|
||||
LOGGER.debug("Got package {}/{}/{}", group, project, version);
|
||||
composerDependencies.add(new ComposerDependency(group, project, version));
|
||||
} else {
|
||||
LOGGER.debug("Group/package {} does not have a version", groupName);
|
||||
}
|
||||
} else {
|
||||
LOGGER.debug("Got a dependency with no name");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (JsonParsingException jsonpe) {
|
||||
throw new ComposerException("Error parsing stream", jsonpe);
|
||||
} catch (JsonException jsone) {
|
||||
throw new ComposerException("Error reading stream", jsone);
|
||||
} catch (IllegalStateException ise) {
|
||||
throw new ComposerException("Illegal state in composer stream", ise);
|
||||
} catch (ClassCastException cce) {
|
||||
throw new ComposerException("Not exactly composer lock", cce);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of dependencies.
|
||||
*
|
||||
* @return the list of dependencies
|
||||
*/
|
||||
public List<ComposerDependency> getDependencies() {
|
||||
return composerDependencies;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Model elements for PHP Composer files
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.composer;
|
||||
@@ -38,7 +38,6 @@ import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.TopDocs;
|
||||
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.SearchFieldAnalyzer;
|
||||
import org.owasp.dependencycheck.data.nvdcve.CveDB;
|
||||
@@ -48,8 +47,8 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* An in memory lucene index that contains the vendor/product combinations from the CPE (application) identifiers within the NVD
|
||||
* CVE data.
|
||||
* An in memory lucene index that contains the vendor/product combinations from
|
||||
* the CPE (application) identifiers within the NVD CVE data.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
@@ -101,11 +100,11 @@ public final class CpeMemoryIndex {
|
||||
/**
|
||||
* The search field analyzer for the product field.
|
||||
*/
|
||||
private SearchFieldAnalyzer productSearchFieldAnalyzer;
|
||||
private SearchFieldAnalyzer productFieldAnalyzer;
|
||||
/**
|
||||
* The search field analyzer for the vendor field.
|
||||
*/
|
||||
private SearchFieldAnalyzer vendorSearchFieldAnalyzer;
|
||||
private SearchFieldAnalyzer vendorFieldAnalyzer;
|
||||
|
||||
/**
|
||||
* Creates and loads data into an in memory index.
|
||||
@@ -144,51 +143,20 @@ public final class CpeMemoryIndex {
|
||||
return openState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the indexing analyzer for the CPE Index.
|
||||
*
|
||||
* @return the CPE Analyzer.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Analyzer createIndexingAnalyzer() {
|
||||
final Map fieldAnalyzers = new HashMap();
|
||||
fieldAnalyzers.put(Fields.DOCUMENT_KEY, new KeywordAnalyzer());
|
||||
return new PerFieldAnalyzerWrapper(new FieldAnalyzer(LuceneUtils.CURRENT_VERSION), fieldAnalyzers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Analyzer for searching the CPE Index.
|
||||
*
|
||||
* @return the CPE Analyzer.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Analyzer createSearchingAnalyzer() {
|
||||
final Map<String, Analyzer> fieldAnalyzers = new HashMap<String, Analyzer>();
|
||||
fieldAnalyzers.put(Fields.DOCUMENT_KEY, new KeywordAnalyzer());
|
||||
productSearchFieldAnalyzer = new SearchFieldAnalyzer(LuceneUtils.CURRENT_VERSION);
|
||||
vendorSearchFieldAnalyzer = new SearchFieldAnalyzer(LuceneUtils.CURRENT_VERSION);
|
||||
fieldAnalyzers.put(Fields.PRODUCT, productSearchFieldAnalyzer);
|
||||
fieldAnalyzers.put(Fields.VENDOR, vendorSearchFieldAnalyzer);
|
||||
productFieldAnalyzer = new SearchFieldAnalyzer(LuceneUtils.CURRENT_VERSION);
|
||||
vendorFieldAnalyzer = new SearchFieldAnalyzer(LuceneUtils.CURRENT_VERSION);
|
||||
fieldAnalyzers.put(Fields.PRODUCT, productFieldAnalyzer);
|
||||
fieldAnalyzers.put(Fields.VENDOR, vendorFieldAnalyzer);
|
||||
|
||||
return new PerFieldAnalyzerWrapper(new FieldAnalyzer(LuceneUtils.CURRENT_VERSION), fieldAnalyzers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a CPE IndexEntry into the Lucene index.
|
||||
*
|
||||
* @param vendor the vendor to index
|
||||
* @param product the product to index
|
||||
* @param indexWriter the index writer to write the entry into
|
||||
* @throws CorruptIndexException is thrown if the index is corrupt
|
||||
* @throws IOException is thrown if an IOException occurs
|
||||
*/
|
||||
public void saveEntry(String vendor, String product, IndexWriter indexWriter) throws CorruptIndexException, IOException {
|
||||
final Document doc = new Document();
|
||||
final Field v = new TextField(Fields.VENDOR, vendor, Field.Store.YES);
|
||||
final Field p = new TextField(Fields.PRODUCT, product, Field.Store.YES);
|
||||
doc.add(v);
|
||||
doc.add(p);
|
||||
indexWriter.addDocument(doc);
|
||||
return new PerFieldAnalyzerWrapper(new KeywordAnalyzer(), fieldAnalyzers);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,13 +194,28 @@ public final class CpeMemoryIndex {
|
||||
Analyzer analyzer = null;
|
||||
IndexWriter indexWriter = null;
|
||||
try {
|
||||
analyzer = createIndexingAnalyzer();
|
||||
analyzer = createSearchingAnalyzer();
|
||||
final IndexWriterConfig conf = new IndexWriterConfig(LuceneUtils.CURRENT_VERSION, analyzer);
|
||||
indexWriter = new IndexWriter(index, conf);
|
||||
try {
|
||||
// Tip: reuse the Document and Fields for performance...
|
||||
// See "Re-use Document and Field instances" from
|
||||
// http://wiki.apache.org/lucene-java/ImproveIndexingSpeed
|
||||
final Document doc = new Document();
|
||||
final Field v = new TextField(Fields.VENDOR, Fields.VENDOR, Field.Store.YES);
|
||||
final Field p = new TextField(Fields.PRODUCT, Fields.PRODUCT, Field.Store.YES);
|
||||
doc.add(v);
|
||||
doc.add(p);
|
||||
|
||||
final Set<Pair<String, String>> data = cve.getVendorProductList();
|
||||
for (Pair<String, String> pair : data) {
|
||||
saveEntry(pair.getLeft(), pair.getRight(), indexWriter);
|
||||
//todo figure out why there are null products
|
||||
if (pair.getLeft() != null && pair.getRight() != null) {
|
||||
v.setStringValue(pair.getLeft());
|
||||
p.setStringValue(pair.getRight());
|
||||
indexWriter.addDocument(doc);
|
||||
resetFieldAnalyzer();
|
||||
}
|
||||
}
|
||||
} catch (DatabaseException ex) {
|
||||
LOGGER.debug("", ex);
|
||||
@@ -263,14 +246,14 @@ public final class CpeMemoryIndex {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the searching analyzers
|
||||
* Resets the product and vendor field analyzers.
|
||||
*/
|
||||
private void resetSearchingAnalyzer() {
|
||||
if (productSearchFieldAnalyzer != null) {
|
||||
productSearchFieldAnalyzer.clear();
|
||||
private void resetFieldAnalyzer() {
|
||||
if (productFieldAnalyzer != null) {
|
||||
productFieldAnalyzer.clear();
|
||||
}
|
||||
if (vendorSearchFieldAnalyzer != null) {
|
||||
vendorSearchFieldAnalyzer.clear();
|
||||
if (vendorFieldAnalyzer != null) {
|
||||
vendorFieldAnalyzer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,14 +264,16 @@ public final class CpeMemoryIndex {
|
||||
* @param maxQueryResults the maximum number of documents to return
|
||||
* @return the TopDocs found by the search
|
||||
* @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 {
|
||||
if (searchString == null || searchString.trim().isEmpty()) {
|
||||
throw new ParseException("Query is null or empty");
|
||||
}
|
||||
LOGGER.debug(searchString);
|
||||
final Query query = queryParser.parse(searchString);
|
||||
return indexSearcher.search(query, maxQueryResults);
|
||||
return search(query, maxQueryResults);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -301,7 +286,7 @@ public final class CpeMemoryIndex {
|
||||
* @throws IOException thrown if there is an IOException
|
||||
*/
|
||||
public TopDocs search(Query query, int maxQueryResults) throws CorruptIndexException, IOException {
|
||||
resetSearchingAnalyzer();
|
||||
resetFieldAnalyzer();
|
||||
return indexSearcher.search(query, maxQueryResults);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ public class IndexEntry implements Serializable {
|
||||
*/
|
||||
public String getDocumentId() {
|
||||
if (documentId == null && vendor != null && product != null) {
|
||||
documentId = vendor + ":" + product;
|
||||
documentId = vendor + ':' + product;
|
||||
}
|
||||
return documentId;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -45,21 +46,21 @@ public final class CweDB {
|
||||
/**
|
||||
* A HashMap of the CWE data.
|
||||
*/
|
||||
private static final HashMap<String, String> CWE = loadData();
|
||||
private static final Map<String, String> CWE = loadData();
|
||||
|
||||
/**
|
||||
* Loads a HashMap containing the CWE data from a resource found in the jar.
|
||||
*
|
||||
* @return a HashMap of CWE data
|
||||
*/
|
||||
private static HashMap<String, String> loadData() {
|
||||
private static Map<String, String> loadData() {
|
||||
ObjectInputStream oin = null;
|
||||
try {
|
||||
final String filePath = "data/cwe.hashmap.serialized";
|
||||
final InputStream input = CweDB.class.getClassLoader().getResourceAsStream(filePath);
|
||||
oin = new ObjectInputStream(input);
|
||||
@SuppressWarnings("unchecked")
|
||||
final HashMap<String, String> ret = (HashMap<String, String>) oin.readObject();
|
||||
final Map<String, String> ret = (HashMap<String, String>) oin.readObject();
|
||||
return ret;
|
||||
} catch (ClassNotFoundException ex) {
|
||||
LOGGER.warn("Unable to load CWE data. This should not be an issue.");
|
||||
|
||||
@@ -37,7 +37,7 @@ public class CweHandler extends DefaultHandler {
|
||||
/**
|
||||
* 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 <String, String>
|
||||
*/
|
||||
public HashMap<String, String> getCwe() {
|
||||
return cwe;
|
||||
|
||||
@@ -29,11 +29,15 @@ import org.apache.lucene.util.Version;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A Lucene Analyzer that utilizes the WhitespaceTokenizer, WordDelimiterFilter, LowerCaseFilter, and StopFilter. The intended
|
||||
* purpose of this Analyzer is to index the CPE fields vendor and product.</p>
|
||||
* A Lucene Analyzer that utilizes the WhitespaceTokenizer, WordDelimiterFilter,
|
||||
* LowerCaseFilter, and StopFilter. The intended purpose of this Analyzer is to
|
||||
* index the CPE fields vendor and product.</p>
|
||||
*
|
||||
* @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 {
|
||||
|
||||
/**
|
||||
|
||||
@@ -77,6 +77,7 @@ public final class LuceneUtils {
|
||||
case '*':
|
||||
case '?':
|
||||
case ':':
|
||||
case '/':
|
||||
case '\\': //it is supposed to fall through here
|
||||
buf.append('\\');
|
||||
default:
|
||||
@@ -93,17 +94,12 @@ public final class LuceneUtils {
|
||||
* @return the escaped text.
|
||||
*/
|
||||
public static String escapeLuceneQuery(final CharSequence text) {
|
||||
|
||||
if (text == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int size = text.length();
|
||||
size = size >> 1;
|
||||
final int size = text.length() << 1;
|
||||
final StringBuilder buf = new StringBuilder(size);
|
||||
|
||||
appendEscapedLuceneQuery(buf, text);
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
|
||||
* <p>
|
||||
* Takes a TokenStream and adds additional tokens by concatenating pairs of words.</p>
|
||||
* <p>
|
||||
* <b>Example:</b> "Spring Framework Core" -> "Spring SpringFramework Framework FrameworkCore Core".</p>
|
||||
* <b>Example:</b> "Spring Framework Core" -> "Spring SpringFramework Framework FrameworkCore Core".</p>
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
@@ -75,8 +75,8 @@ public final class TokenPairConcatenatingFilter extends TokenFilter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the underlying TokenStream and sets CharTermAttributes to construct an expanded set of tokens by
|
||||
* concatenating tokens with the previous token.
|
||||
* Increments the underlying TokenStream and sets CharTermAttributes to construct an expanded set of tokens by concatenating
|
||||
* tokens with the previous token.
|
||||
*
|
||||
* @return whether or not we have hit the end of the TokenStream
|
||||
* @throws IOException is thrown when an IOException occurs
|
||||
@@ -112,8 +112,7 @@ public final class TokenPairConcatenatingFilter extends TokenFilter {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Resets the Filter and clears any internal state data that may have been left-over from previous uses of the
|
||||
* Filter.</p>
|
||||
* Resets the Filter and clears any internal state data that may have been left-over from previous uses of the Filter.</p>
|
||||
* <p>
|
||||
* <b>If this Filter is re-used this method must be called between uses.</b></p>
|
||||
*/
|
||||
@@ -121,4 +120,46 @@ public final class TokenPairConcatenatingFilter extends TokenFilter {
|
||||
previousWord = null;
|
||||
words.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard hash code implementation.
|
||||
*
|
||||
* @return the hash code
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 3;
|
||||
hash = 31 * hash + (this.termAtt != null ? this.termAtt.hashCode() : 0);
|
||||
hash = 31 * hash + (this.previousWord != null ? this.previousWord.hashCode() : 0);
|
||||
hash = 31 * hash + (this.words != null ? this.words.hashCode() : 0);
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard equals implementation.
|
||||
*
|
||||
* @param obj the object to compare
|
||||
* @return true if the objects are equal; otherwise false.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final TokenPairConcatenatingFilter other = (TokenPairConcatenatingFilter) obj;
|
||||
if (this.termAtt != other.termAtt && (this.termAtt == null || !this.termAtt.equals(other.termAtt))) {
|
||||
return false;
|
||||
}
|
||||
if ((this.previousWord == null) ? (other.previousWord != null) : !this.previousWord.equals(other.previousWord)) {
|
||||
return false;
|
||||
}
|
||||
if (this.words != other.words && (this.words == null || !this.words.equals(other.words))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,15 +31,17 @@ import org.slf4j.LoggerFactory;
|
||||
* <p>
|
||||
* Takes a TokenStream and splits or adds tokens to correctly index version numbers.</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" -> "3 3.0 3.0.0 RELEASE 3.0.0.RELEASE".</p>
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
public final class UrlTokenizingFilter extends AbstractTokenizingFilter {
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(UrlTokenizingFilter.class);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* concatenating tokens with the previous token.
|
||||
* Increments the underlying TokenStream and sets CharTermAttributes to construct an expanded set of tokens by concatenating
|
||||
* tokens with the previous token.
|
||||
*
|
||||
* @return whether or not we have hit the end of the TokenStream
|
||||
* @throws IOException is thrown when an IOException occurs
|
||||
|
||||
@@ -94,13 +94,13 @@ public class MavenArtifact {
|
||||
}
|
||||
if (jarAvailable) {
|
||||
//org/springframework/spring-core/3.2.0.RELEASE/spring-core-3.2.0.RELEASE.pom
|
||||
this.artifactUrl = base + groupId.replace('.', '/') + "/" + artifactId + "/"
|
||||
+ version + "/" + artifactId + "-" + version + ".jar";
|
||||
this.artifactUrl = base + groupId.replace('.', '/') + '/' + artifactId + '/'
|
||||
+ version + '/' + artifactId + '-' + version + ".jar";
|
||||
}
|
||||
if (pomAvailable) {
|
||||
//org/springframework/spring-core/3.2.0.RELEASE/spring-core-3.2.0.RELEASE.pom
|
||||
this.pomUrl = base + groupId.replace('.', '/') + "/" + artifactId + "/"
|
||||
+ version + "/" + artifactId + "-" + version + ".pom";
|
||||
this.pomUrl = base + groupId.replace('.', '/') + '/' + artifactId + '/'
|
||||
+ version + '/' + artifactId + '-' + version + ".pom";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
|
||||
import org.owasp.dependencycheck.utils.InvalidSettingException;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.owasp.dependencycheck.utils.URLConnectionFactory;
|
||||
@@ -47,7 +48,7 @@ public class NexusSearch {
|
||||
/**
|
||||
* Whether to use the Proxy when making requests.
|
||||
*/
|
||||
private boolean useProxy;
|
||||
private final boolean useProxy;
|
||||
/**
|
||||
* Used for logging.
|
||||
*/
|
||||
@@ -56,32 +57,26 @@ public class NexusSearch {
|
||||
/**
|
||||
* Creates a NexusSearch for the given repository URL.
|
||||
*
|
||||
* @param rootURL the root URL of the repository on which searches should execute. full URL's are calculated relative to this
|
||||
* URL, so it should end with a /
|
||||
* @param rootURL the root URL of the repository on which searches should
|
||||
* execute. full URL's are calculated relative to this URL, so it should end
|
||||
* with a /
|
||||
* @param useProxy flag indicating if the proxy settings should be used
|
||||
*/
|
||||
public NexusSearch(URL rootURL) {
|
||||
public NexusSearch(URL rootURL, boolean useProxy) {
|
||||
this.rootURL = rootURL;
|
||||
try {
|
||||
if (null != Settings.getString(Settings.KEYS.PROXY_SERVER)
|
||||
&& Settings.getBoolean(Settings.KEYS.ANALYZER_NEXUS_PROXY)) {
|
||||
useProxy = true;
|
||||
LOGGER.debug("Using proxy");
|
||||
} else {
|
||||
useProxy = false;
|
||||
LOGGER.debug("Not using proxy");
|
||||
}
|
||||
} catch (InvalidSettingException ise) {
|
||||
useProxy = false;
|
||||
}
|
||||
this.useProxy = useProxy;
|
||||
LOGGER.debug("Using proxy: {}", useProxy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the configured Nexus repository for the given sha1 hash. If the artifact is found, a <code>MavenArtifact</code> is
|
||||
* populated with the coordinate information.
|
||||
* Searches the configured Nexus repository for the given sha1 hash. If the
|
||||
* artifact is found, a <code>MavenArtifact</code> is populated with the
|
||||
* coordinate information.
|
||||
*
|
||||
* @param sha1 The SHA-1 hash string for which to search
|
||||
* @return the populated Maven coordinates
|
||||
* @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 MavenArtifact searchSha1(String sha1) throws IOException {
|
||||
if (null == sha1 || !sha1.matches("^[0-9A-Fa-f]{40}$")) {
|
||||
@@ -106,57 +101,60 @@ public class NexusSearch {
|
||||
conn.addRequestProperty("Accept", "application/xml");
|
||||
conn.connect();
|
||||
|
||||
if (conn.getResponseCode() == 200) {
|
||||
try {
|
||||
final DocumentBuilder builder = DocumentBuilderFactory
|
||||
.newInstance().newDocumentBuilder();
|
||||
final Document doc = builder.parse(conn.getInputStream());
|
||||
final XPath xpath = XPathFactory.newInstance().newXPath();
|
||||
final String groupId = xpath
|
||||
.evaluate(
|
||||
"/org.sonatype.nexus.rest.model.NexusArtifact/groupId",
|
||||
doc);
|
||||
final String artifactId = xpath.evaluate(
|
||||
"/org.sonatype.nexus.rest.model.NexusArtifact/artifactId",
|
||||
doc);
|
||||
final String version = xpath
|
||||
.evaluate(
|
||||
"/org.sonatype.nexus.rest.model.NexusArtifact/version",
|
||||
doc);
|
||||
final String link = xpath
|
||||
.evaluate(
|
||||
"/org.sonatype.nexus.rest.model.NexusArtifact/artifactLink",
|
||||
doc);
|
||||
final String pomLink = xpath
|
||||
.evaluate(
|
||||
"/org.sonatype.nexus.rest.model.NexusArtifact/pomLink",
|
||||
doc);
|
||||
final MavenArtifact ma = new MavenArtifact(groupId, artifactId, version);
|
||||
if (link != null && !"".equals(link)) {
|
||||
ma.setArtifactUrl(link);
|
||||
switch (conn.getResponseCode()) {
|
||||
case 200:
|
||||
try {
|
||||
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
final DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
final Document doc = builder.parse(conn.getInputStream());
|
||||
final XPath xpath = XPathFactory.newInstance().newXPath();
|
||||
final String groupId = xpath
|
||||
.evaluate(
|
||||
"/org.sonatype.nexus.rest.model.NexusArtifact/groupId",
|
||||
doc);
|
||||
final String artifactId = xpath.evaluate(
|
||||
"/org.sonatype.nexus.rest.model.NexusArtifact/artifactId",
|
||||
doc);
|
||||
final String version = xpath
|
||||
.evaluate(
|
||||
"/org.sonatype.nexus.rest.model.NexusArtifact/version",
|
||||
doc);
|
||||
final String link = xpath
|
||||
.evaluate(
|
||||
"/org.sonatype.nexus.rest.model.NexusArtifact/artifactLink",
|
||||
doc);
|
||||
final String pomLink = xpath
|
||||
.evaluate(
|
||||
"/org.sonatype.nexus.rest.model.NexusArtifact/pomLink",
|
||||
doc);
|
||||
final MavenArtifact ma = new MavenArtifact(groupId, artifactId, version);
|
||||
if (link != null && !link.isEmpty()) {
|
||||
ma.setArtifactUrl(link);
|
||||
}
|
||||
if (pomLink != null && !pomLink.isEmpty()) {
|
||||
ma.setPomUrl(pomLink);
|
||||
}
|
||||
return ma;
|
||||
} catch (Throwable e) {
|
||||
// Anything else is jacked-up XML stuff that we really can't recover
|
||||
// from well
|
||||
throw new IOException(e.getMessage(), e);
|
||||
}
|
||||
if (pomLink != null && !"".equals(pomLink)) {
|
||||
ma.setPomUrl(pomLink);
|
||||
}
|
||||
return ma;
|
||||
} catch (Throwable e) {
|
||||
// Anything else is jacked-up XML stuff that we really can't recover
|
||||
// from well
|
||||
throw new IOException(e.getMessage(), e);
|
||||
}
|
||||
} else if (conn.getResponseCode() == 404) {
|
||||
throw new FileNotFoundException("Artifact not found in Nexus");
|
||||
} else {
|
||||
LOGGER.debug("Could not connect to Nexus received response code: {} {}",
|
||||
conn.getResponseCode(), conn.getResponseMessage());
|
||||
throw new IOException("Could not connect to Nexus");
|
||||
case 404:
|
||||
throw new FileNotFoundException("Artifact not found in Nexus");
|
||||
default:
|
||||
LOGGER.debug("Could not connect to Nexus received response code: {} {}",
|
||||
conn.getResponseCode(), conn.getResponseMessage());
|
||||
throw new IOException("Could not connect to Nexus");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do a preflight request to see if the repository is actually working.
|
||||
*
|
||||
* @return whether the repository is listening and returns the /status URL correctly
|
||||
* @return whether the repository is listening and returns the /status URL
|
||||
* correctly
|
||||
*/
|
||||
public boolean preflightRequest() {
|
||||
HttpURLConnection conn;
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -36,7 +36,8 @@ public class XPathNuspecParser implements NuspecParser {
|
||||
* Gets the string value of a node or null if it's not present
|
||||
*
|
||||
* @param n the node to test
|
||||
* @return the string content of the node, or null if the node itself is null
|
||||
* @return the string content of the node, or null if the node itself is
|
||||
* null
|
||||
*/
|
||||
private String getOrNull(Node n) {
|
||||
if (n != null) {
|
||||
@@ -56,7 +57,10 @@ public class XPathNuspecParser implements NuspecParser {
|
||||
@Override
|
||||
public NugetPackage parse(InputStream stream) throws NuspecParseException {
|
||||
try {
|
||||
final Document d = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(stream);
|
||||
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
final Document d = factory.newDocumentBuilder().parse(stream);
|
||||
|
||||
final XPath xpath = XPathFactory.newInstance().newXPath();
|
||||
final NugetPackage nuspec = new NugetPackage();
|
||||
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.nuget;
|
||||
|
||||
@@ -17,26 +17,29 @@
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.nvdcve;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.Connection;
|
||||
import java.sql.Driver;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.owasp.dependencycheck.utils.DBUtils;
|
||||
import org.owasp.dependencycheck.utils.DependencyVersion;
|
||||
import org.owasp.dependencycheck.utils.DependencyVersionUtil;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Loads the configured database driver and returns the database connection. If the embedded H2 database is used obtaining a
|
||||
* connection will ensure the database file exists and that the appropriate table structure has been created.
|
||||
* Loads the configured database driver and returns the database connection. If
|
||||
* 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
|
||||
*/
|
||||
@@ -58,6 +61,10 @@ public final class ConnectionFactory {
|
||||
* Resource location for SQL file used to create the database schema.
|
||||
*/
|
||||
public static final String DB_STRUCTURE_UPDATE_RESOURCE = "data/upgrade_%s.sql";
|
||||
/**
|
||||
* The URL that discusses upgrading non-H2 databases.
|
||||
*/
|
||||
public static final String UPGRADE_HELP_URL = "http://jeremylong.github.io/DependencyCheck/data/upgrade.html";
|
||||
/**
|
||||
* The database driver used to connect to the database.
|
||||
*/
|
||||
@@ -82,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
|
||||
* successfully.
|
||||
* Initializes the connection factory. Ensuring that the appropriate drivers
|
||||
* 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.
|
||||
if (connectionString != null) {
|
||||
return;
|
||||
@@ -183,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
|
||||
* unregistered prior to the 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.
|
||||
* Cleans up resources and unloads any registered database drivers. This
|
||||
* needs to be called to ensure the driver is unregistered prior to the
|
||||
* 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) {
|
||||
try {
|
||||
DriverManager.deregisterDriver(driver);
|
||||
@@ -205,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
|
||||
* @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 {
|
||||
initialize();
|
||||
@@ -223,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
|
||||
* @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 {
|
||||
final File dir = Settings.getDataDirectory();
|
||||
@@ -236,29 +249,23 @@ 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
|
||||
* @throws DatabaseException thrown if there is a Database Exception
|
||||
*/
|
||||
private static void createTables(Connection conn) throws DatabaseException {
|
||||
LOGGER.debug("Creating database structure");
|
||||
InputStream is;
|
||||
InputStreamReader reader;
|
||||
BufferedReader in = null;
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = ConnectionFactory.class.getClassLoader().getResourceAsStream(DB_STRUCTURE_RESOURCE);
|
||||
reader = new InputStreamReader(is, "UTF-8");
|
||||
in = new BufferedReader(reader);
|
||||
final StringBuilder sb = new StringBuilder(2110);
|
||||
String tmp;
|
||||
while ((tmp = in.readLine()) != null) {
|
||||
sb.append(tmp);
|
||||
}
|
||||
final String dbStructure = IOUtils.toString(is, "UTF-8");
|
||||
|
||||
Statement statement = null;
|
||||
try {
|
||||
statement = conn.createStatement();
|
||||
statement.execute(sb.toString());
|
||||
statement.execute(dbStructure);
|
||||
} catch (SQLException ex) {
|
||||
LOGGER.debug("", ex);
|
||||
throw new DatabaseException("Unable to create database statement", ex);
|
||||
@@ -268,86 +275,119 @@ public final class ConnectionFactory {
|
||||
} catch (IOException ex) {
|
||||
throw new DatabaseException("Unable to create database schema", ex);
|
||||
} finally {
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException ex) {
|
||||
LOGGER.trace("", ex);
|
||||
}
|
||||
}
|
||||
IOUtils.closeQuietly(is);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the database schema by loading the upgrade script for the version specified. The intended use is that if the
|
||||
* current schema version is 2.9 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.
|
||||
* Updates the database schema by loading the upgrade script for the version
|
||||
* specified. The intended use is that if the current schema version is 2.9
|
||||
* 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 schema the current schema version that is being upgraded
|
||||
* @throws DatabaseException thrown if there is an exception upgrading the database schema
|
||||
* @param appExpectedVersion the schema version that the application expects
|
||||
* @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 {
|
||||
LOGGER.debug("Updating database structure");
|
||||
InputStream is;
|
||||
InputStreamReader reader;
|
||||
BufferedReader in = null;
|
||||
String updateFile = null;
|
||||
private static void updateSchema(Connection conn, DependencyVersion appExpectedVersion, DependencyVersion currentDbVersion)
|
||||
throws DatabaseException {
|
||||
|
||||
final String databaseProductName;
|
||||
try {
|
||||
updateFile = String.format(DB_STRUCTURE_UPDATE_RESOURCE, schema);
|
||||
is = ConnectionFactory.class.getClassLoader().getResourceAsStream(updateFile);
|
||||
if (is == null) {
|
||||
throw new DatabaseException(String.format("Unable to load update file '%s'", updateFile));
|
||||
}
|
||||
reader = new InputStreamReader(is, "UTF-8");
|
||||
in = new BufferedReader(reader);
|
||||
final StringBuilder sb = new StringBuilder(2110);
|
||||
String tmp;
|
||||
while ((tmp = in.readLine()) != null) {
|
||||
sb.append(tmp);
|
||||
}
|
||||
Statement statement = null;
|
||||
databaseProductName = conn.getMetaData().getDatabaseProductName();
|
||||
} catch (SQLException ex) {
|
||||
throw new DatabaseException("Unable to get the database product name");
|
||||
}
|
||||
if ("h2".equalsIgnoreCase(databaseProductName)) {
|
||||
LOGGER.debug("Updating database structure");
|
||||
InputStream is = null;
|
||||
String updateFile = null;
|
||||
try {
|
||||
statement = conn.createStatement();
|
||||
statement.execute(sb.toString());
|
||||
} catch (SQLException ex) {
|
||||
LOGGER.debug("", ex);
|
||||
throw new DatabaseException("Unable to update database schema", ex);
|
||||
} finally {
|
||||
DBUtils.closeStatement(statement);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
final String msg = String.format("Upgrade SQL file does not exist: %s", updateFile);
|
||||
throw new DatabaseException(msg, ex);
|
||||
} finally {
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException ex) {
|
||||
LOGGER.trace("", ex);
|
||||
updateFile = String.format(DB_STRUCTURE_UPDATE_RESOURCE, currentDbVersion.toString());
|
||||
is = ConnectionFactory.class.getClassLoader().getResourceAsStream(updateFile);
|
||||
if (is == null) {
|
||||
throw new DatabaseException(String.format("Unable to load update file '%s'", updateFile));
|
||||
}
|
||||
final String dbStructureUpdate = IOUtils.toString(is, "UTF-8");
|
||||
|
||||
Statement statement = null;
|
||||
try {
|
||||
statement = conn.createStatement();
|
||||
final boolean success = statement.execute(dbStructureUpdate);
|
||||
if (!success && statement.getUpdateCount() <= 0) {
|
||||
throw new DatabaseException(String.format("Unable to upgrade the database schema to %s",
|
||||
currentDbVersion.toString()));
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
LOGGER.debug("", ex);
|
||||
throw new DatabaseException("Unable to update database schema", ex);
|
||||
} finally {
|
||||
DBUtils.closeStatement(statement);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
final String msg = String.format("Upgrade SQL file does not exist: %s", updateFile);
|
||||
throw new DatabaseException(msg, ex);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(is);
|
||||
}
|
||||
} else {
|
||||
final int e0 = Integer.parseInt(appExpectedVersion.getVersionParts().get(0));
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the provided connection to check the specified schema version within the database.
|
||||
* Counter to ensure that calls to ensureSchemaVersion does not end up in an
|
||||
* endless loop.
|
||||
*/
|
||||
private static int callDepth = 0;
|
||||
|
||||
/**
|
||||
* Uses the provided connection to check the specified schema version within
|
||||
* the database.
|
||||
*
|
||||
* @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 {
|
||||
ResultSet rs = null;
|
||||
CallableStatement cs = null;
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
//TODO convert this to use DatabaseProperties
|
||||
cs = conn.prepareCall("SELECT value FROM properties WHERE id = 'version'");
|
||||
rs = cs.executeQuery();
|
||||
ps = conn.prepareStatement("SELECT value FROM properties WHERE id = 'version'");
|
||||
rs = ps.executeQuery();
|
||||
if (rs.next()) {
|
||||
if (!DB_SCHEMA_VERSION.equals(rs.getString(1))) {
|
||||
LOGGER.debug("Current Schema: " + DB_SCHEMA_VERSION);
|
||||
LOGGER.debug("DB Schema: " + rs.getString(1));
|
||||
updateSchema(conn, rs.getString(1));
|
||||
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));
|
||||
if (db == null) {
|
||||
throw new DatabaseException("Invalid database schema");
|
||||
}
|
||||
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) {
|
||||
ensureSchemaVersion(conn);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new DatabaseException("Database schema is missing");
|
||||
@@ -357,7 +397,7 @@ public final class ConnectionFactory {
|
||||
throw new DatabaseException("Unable to check the database schema version");
|
||||
} finally {
|
||||
DBUtils.closeResultSet(rs);
|
||||
DBUtils.closeStatement(cs);
|
||||
DBUtils.closeStatement(ps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,11 @@
|
||||
package org.owasp.dependencycheck.data.nvdcve;
|
||||
|
||||
/**
|
||||
* An exception used to indicate the db4o database is corrupt. This could be due to invalid data or a complete failure
|
||||
* of the db.
|
||||
* An exception used to indicate the db4o database is corrupt. This could be due to invalid data or a complete failure of the db.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
class CorruptDatabaseException extends DatabaseException {
|
||||
public class CorruptDatabaseException extends DatabaseException {
|
||||
|
||||
/**
|
||||
* the serial version uid.
|
||||
@@ -31,7 +30,7 @@ class CorruptDatabaseException extends DatabaseException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates an CorruptDatabaseException
|
||||
* Creates an CorruptDatabaseException.
|
||||
*
|
||||
* @param msg the exception message
|
||||
*/
|
||||
@@ -40,7 +39,7 @@ class CorruptDatabaseException extends DatabaseException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an CorruptDatabaseException
|
||||
* Creates an CorruptDatabaseException.
|
||||
*
|
||||
* @param msg the exception message
|
||||
* @param ex the cause of the exception
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.owasp.dependencycheck.data.nvdcve;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
@@ -29,8 +28,10 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.Properties;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
@@ -67,16 +68,35 @@ public class CveDB {
|
||||
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
|
||||
* the close method.
|
||||
* Creates a new CveDB object and opens the database connection. Note, the
|
||||
* 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 {
|
||||
super();
|
||||
statementBundle = ResourceBundle.getBundle("data/dbStatements");
|
||||
try {
|
||||
open();
|
||||
try {
|
||||
final String databaseProductName = conn.getMetaData().getDatabaseProductName();
|
||||
LOGGER.debug("Database dialect: {}", databaseProductName);
|
||||
final Locale dbDialect = new Locale(databaseProductName);
|
||||
statementBundle = ResourceBundle.getBundle("data/dbStatements", dbDialect);
|
||||
if ("mysql".equalsIgnoreCase(databaseProductName)) {
|
||||
batchSupported = false;
|
||||
}
|
||||
} catch (SQLException se) {
|
||||
LOGGER.warn("Problem loading database specific dialect!", se);
|
||||
statementBundle = ResourceBundle.getBundle("data/dbStatements");
|
||||
}
|
||||
databaseProperties = new DatabaseProperties(this);
|
||||
} catch (DatabaseException ex) {
|
||||
throw ex;
|
||||
@@ -93,20 +113,23 @@ 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 synchronized void open() throws DatabaseException {
|
||||
if (!isOpen()) {
|
||||
conn = ConnectionFactory.getConnection();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 synchronized void close() {
|
||||
if (conn != null) {
|
||||
try {
|
||||
conn.close();
|
||||
@@ -126,7 +149,7 @@ public class CveDB {
|
||||
*
|
||||
* @return whether the database connection is open or closed
|
||||
*/
|
||||
public boolean isOpen() {
|
||||
public synchronized boolean isOpen() {
|
||||
return conn != null;
|
||||
}
|
||||
|
||||
@@ -135,7 +158,7 @@ public class CveDB {
|
||||
*
|
||||
* @throws SQLException thrown if a SQL Exception occurs
|
||||
*/
|
||||
public void commit() throws SQLException {
|
||||
public synchronized void commit() throws SQLException {
|
||||
//temporary remove this as autocommit is on.
|
||||
//if (conn != null) {
|
||||
// conn.commit();
|
||||
@@ -155,7 +178,8 @@ public class CveDB {
|
||||
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;
|
||||
|
||||
@@ -169,14 +193,16 @@ public class CveDB {
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the CPE entries in the database and retrieves all entries for a given vendor and product combination. The returned
|
||||
* list will include all versions of the product that are registered in the NVD CVE data.
|
||||
* Searches the CPE entries in the database and retrieves all entries for a
|
||||
* 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 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
|
||||
*/
|
||||
public Set<VulnerableSoftware> getCPEs(String vendor, String product) {
|
||||
public synchronized Set<VulnerableSoftware> getCPEs(String vendor, String product) {
|
||||
final Set<VulnerableSoftware> cpe = new HashSet<VulnerableSoftware>();
|
||||
ResultSet rs = null;
|
||||
PreparedStatement ps = null;
|
||||
@@ -205,9 +231,10 @@ public class CveDB {
|
||||
* Returns 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 synchronized Set<Pair<String, String>> getVendorProductList() throws DatabaseException {
|
||||
final Set<Pair<String, String>> data = new HashSet<Pair<String, String>>();
|
||||
ResultSet rs = null;
|
||||
PreparedStatement ps = null;
|
||||
@@ -232,7 +259,7 @@ public class CveDB {
|
||||
*
|
||||
* @return the properties from the database
|
||||
*/
|
||||
Properties getProperties() {
|
||||
synchronized Properties getProperties() {
|
||||
final Properties prop = new Properties();
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
@@ -252,83 +279,45 @@ public class CveDB {
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a set of properties to the database.
|
||||
*
|
||||
* @param props a collection of properties
|
||||
*/
|
||||
void saveProperties(Properties props) {
|
||||
PreparedStatement updateProperty = null;
|
||||
PreparedStatement insertProperty = null;
|
||||
try {
|
||||
try {
|
||||
updateProperty = getConnection().prepareStatement(statementBundle.getString("UPDATE_PROPERTY"));
|
||||
insertProperty = getConnection().prepareStatement(statementBundle.getString("INSERT_PROPERTY"));
|
||||
} catch (SQLException ex) {
|
||||
LOGGER.warn("Unable to save properties to the database");
|
||||
LOGGER.debug("Unable to save properties to the database", ex);
|
||||
return;
|
||||
}
|
||||
for (Entry<Object, Object> entry : props.entrySet()) {
|
||||
final String key = entry.getKey().toString();
|
||||
final String value = entry.getValue().toString();
|
||||
try {
|
||||
updateProperty.setString(1, value);
|
||||
updateProperty.setString(2, key);
|
||||
if (updateProperty.executeUpdate() == 0) {
|
||||
insertProperty.setString(1, key);
|
||||
insertProperty.setString(2, value);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
LOGGER.warn("Unable to save property '{}' with a value of '{}' to the database", key, value);
|
||||
LOGGER.debug("", ex);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
DBUtils.closeStatement(updateProperty);
|
||||
DBUtils.closeStatement(insertProperty);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a property to the database.
|
||||
*
|
||||
* @param key the property key
|
||||
* @param value the property value
|
||||
*/
|
||||
void saveProperty(String key, String value) {
|
||||
PreparedStatement updateProperty = null;
|
||||
PreparedStatement insertProperty = null;
|
||||
synchronized void saveProperty(String key, String value) {
|
||||
try {
|
||||
try {
|
||||
updateProperty = getConnection().prepareStatement(statementBundle.getString("UPDATE_PROPERTY"));
|
||||
} catch (SQLException ex) {
|
||||
LOGGER.warn("Unable to save properties to the database");
|
||||
LOGGER.debug("Unable to save properties to the database", ex);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
updateProperty.setString(1, value);
|
||||
updateProperty.setString(2, key);
|
||||
if (updateProperty.executeUpdate() == 0) {
|
||||
try {
|
||||
insertProperty = getConnection().prepareStatement(statementBundle.getString("INSERT_PROPERTY"));
|
||||
} catch (SQLException ex) {
|
||||
LOGGER.warn("Unable to save properties to the database");
|
||||
LOGGER.debug("Unable to save properties to the database", ex);
|
||||
return;
|
||||
}
|
||||
insertProperty.setString(1, key);
|
||||
insertProperty.setString(2, value);
|
||||
insertProperty.execute();
|
||||
final PreparedStatement mergeProperty = getConnection().prepareStatement(statementBundle.getString("MERGE_PROPERTY"));
|
||||
try {
|
||||
mergeProperty.setString(1, key);
|
||||
mergeProperty.setString(2, value);
|
||||
mergeProperty.executeUpdate();
|
||||
} finally {
|
||||
DBUtils.closeStatement(mergeProperty);
|
||||
}
|
||||
} catch (MissingResourceException mre) {
|
||||
// No Merge statement, so doing an Update/Insert...
|
||||
PreparedStatement updateProperty = null;
|
||||
PreparedStatement insertProperty = null;
|
||||
try {
|
||||
updateProperty = getConnection().prepareStatement(statementBundle.getString("UPDATE_PROPERTY"));
|
||||
updateProperty.setString(1, value);
|
||||
updateProperty.setString(2, key);
|
||||
if (updateProperty.executeUpdate() == 0) {
|
||||
insertProperty = getConnection().prepareStatement(statementBundle.getString("INSERT_PROPERTY"));
|
||||
insertProperty.setString(1, key);
|
||||
insertProperty.setString(2, value);
|
||||
insertProperty.executeUpdate();
|
||||
}
|
||||
} finally {
|
||||
DBUtils.closeStatement(updateProperty);
|
||||
DBUtils.closeStatement(insertProperty);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
LOGGER.warn("Unable to save property '{}' with a value of '{}' to the database", key, value);
|
||||
LOGGER.debug("", ex);
|
||||
}
|
||||
} finally {
|
||||
DBUtils.closeStatement(updateProperty);
|
||||
DBUtils.closeStatement(insertProperty);
|
||||
} catch (SQLException ex) {
|
||||
LOGGER.warn("Unable to save property '{}' with a value of '{}' to the database", key, value);
|
||||
LOGGER.debug("", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,8 +328,7 @@ public class CveDB {
|
||||
* @return a list of Vulnerabilities
|
||||
* @throws DatabaseException thrown if there is an exception retrieving data
|
||||
*/
|
||||
public List<Vulnerability> getVulnerabilities(String cpeStr) throws DatabaseException {
|
||||
ResultSet rs = null;
|
||||
public synchronized List<Vulnerability> getVulnerabilities(String cpeStr) throws DatabaseException {
|
||||
final VulnerableSoftware cpe = new VulnerableSoftware();
|
||||
try {
|
||||
cpe.parseName(cpeStr);
|
||||
@@ -350,7 +338,8 @@ public class CveDB {
|
||||
final DependencyVersion detectedVersion = parseDependencyVersion(cpe);
|
||||
final List<Vulnerability> vulnerabilities = new ArrayList<Vulnerability>();
|
||||
|
||||
PreparedStatement ps;
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
ps = getConnection().prepareStatement(statementBundle.getString("SELECT_CVE_FROM_SOFTWARE"));
|
||||
ps.setString(1, cpe.getVendor());
|
||||
@@ -384,12 +373,11 @@ public class CveDB {
|
||||
v.setMatchedCPE(matchedCPE.getKey(), matchedCPE.getValue() ? "Y" : null);
|
||||
vulnerabilities.add(v);
|
||||
}
|
||||
DBUtils.closeResultSet(rs);
|
||||
DBUtils.closeStatement(ps);
|
||||
} catch (SQLException ex) {
|
||||
throw new DatabaseException("Exception retrieving vulnerability for " + cpeStr, ex);
|
||||
} finally {
|
||||
DBUtils.closeResultSet(rs);
|
||||
DBUtils.closeStatement(ps);
|
||||
}
|
||||
return vulnerabilities;
|
||||
}
|
||||
@@ -401,7 +389,7 @@ public class CveDB {
|
||||
* @return a vulnerability object
|
||||
* @throws DatabaseException if an exception occurs
|
||||
*/
|
||||
private Vulnerability getVulnerability(String cve) throws DatabaseException {
|
||||
public synchronized Vulnerability getVulnerability(String cve) throws DatabaseException {
|
||||
PreparedStatement psV = null;
|
||||
PreparedStatement psR = null;
|
||||
PreparedStatement psS = null;
|
||||
@@ -409,6 +397,7 @@ public class CveDB {
|
||||
ResultSet rsR = null;
|
||||
ResultSet rsS = null;
|
||||
Vulnerability vuln = null;
|
||||
|
||||
try {
|
||||
psV = getConnection().prepareStatement(statementBundle.getString("SELECT_VULNERABILITY"));
|
||||
psV.setString(1, cve);
|
||||
@@ -421,7 +410,7 @@ public class CveDB {
|
||||
if (cwe != null) {
|
||||
final String name = CweDB.getCweName(cwe);
|
||||
if (name != null) {
|
||||
cwe += " " + name;
|
||||
cwe += ' ' + name;
|
||||
}
|
||||
}
|
||||
final int cveId = rsV.getInt(1);
|
||||
@@ -467,12 +456,13 @@ 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
|
||||
* @throws DatabaseException is thrown if the database
|
||||
*/
|
||||
public void updateVulnerability(Vulnerability vuln) throws DatabaseException {
|
||||
public synchronized void updateVulnerability(Vulnerability vuln) throws DatabaseException {
|
||||
PreparedStatement selectVulnerabilityId = null;
|
||||
PreparedStatement deleteVulnerability = null;
|
||||
PreparedStatement deleteReferences = null;
|
||||
@@ -490,7 +480,7 @@ public class CveDB {
|
||||
deleteReferences = getConnection().prepareStatement(statementBundle.getString("DELETE_REFERENCE"));
|
||||
deleteSoftware = getConnection().prepareStatement(statementBundle.getString("DELETE_SOFTWARE"));
|
||||
updateVulnerability = getConnection().prepareStatement(statementBundle.getString("UPDATE_VULNERABILITY"));
|
||||
String ids[] = {"id"};
|
||||
final String[] ids = {"id"};
|
||||
insertVulnerability = getConnection().prepareStatement(statementBundle.getString("INSERT_VULNERABILITY"),
|
||||
//Statement.RETURN_GENERATED_KEYS);
|
||||
ids);
|
||||
@@ -513,6 +503,7 @@ public class CveDB {
|
||||
}
|
||||
DBUtils.closeResultSet(rs);
|
||||
rs = null;
|
||||
|
||||
if (vulnerabilityId != 0) {
|
||||
if (vuln.getDescription().contains("** REJECT **")) {
|
||||
deleteVulnerability.setInt(1, vulnerabilityId);
|
||||
@@ -554,13 +545,24 @@ public class CveDB {
|
||||
rs = null;
|
||||
}
|
||||
}
|
||||
insertReference.setInt(1, vulnerabilityId);
|
||||
|
||||
for (Reference r : vuln.getReferences()) {
|
||||
insertReference.setInt(1, vulnerabilityId);
|
||||
insertReference.setString(2, r.getName());
|
||||
insertReference.setString(3, r.getUrl());
|
||||
insertReference.setString(4, r.getSource());
|
||||
insertReference.execute();
|
||||
|
||||
if (batchSupported) {
|
||||
insertReference.addBatch();
|
||||
} else {
|
||||
insertReference.execute();
|
||||
}
|
||||
}
|
||||
|
||||
if (batchSupported) {
|
||||
insertReference.executeBatch();
|
||||
}
|
||||
|
||||
for (VulnerableSoftware s : vuln.getVulnerableSoftware()) {
|
||||
int cpeProductId = 0;
|
||||
selectCpeId.setString(1, s.getName());
|
||||
@@ -589,17 +591,33 @@ public class CveDB {
|
||||
|
||||
insertSoftware.setInt(1, vulnerabilityId);
|
||||
insertSoftware.setInt(2, cpeProductId);
|
||||
|
||||
if (s.getPreviousVersion() == null) {
|
||||
insertSoftware.setNull(3, java.sql.Types.VARCHAR);
|
||||
} else {
|
||||
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) {
|
||||
final String msg = String.format("Error updating '%s'", vuln.getName());
|
||||
LOGGER.debug("", ex);
|
||||
LOGGER.debug(msg, ex);
|
||||
throw new DatabaseException(msg, ex);
|
||||
} finally {
|
||||
DBUtils.closeStatement(selectVulnerabilityId);
|
||||
@@ -620,7 +638,7 @@ public class CveDB {
|
||||
*
|
||||
* @return <code>true</code> if data exists; otherwise <code>false</code>
|
||||
*/
|
||||
public boolean dataExists() {
|
||||
public synchronized boolean dataExists() {
|
||||
Statement cs = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
@@ -642,7 +660,7 @@ public class CveDB {
|
||||
+ "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 "
|
||||
+ "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);
|
||||
} finally {
|
||||
DBUtils.closeResultSet(rs);
|
||||
@@ -652,10 +670,11 @@ public class CveDB {
|
||||
}
|
||||
|
||||
/**
|
||||
* It is possible that orphaned rows may be generated during database updates. This should be called after all updates have
|
||||
* been completed to ensure orphan entries are removed.
|
||||
* It is possible that orphaned rows may be generated during database
|
||||
* updates. This should be called after all updates have been completed to
|
||||
* ensure orphan entries are removed.
|
||||
*/
|
||||
public void cleanupDatabase() {
|
||||
public synchronized void cleanupDatabase() {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
ps = getConnection().prepareStatement(statementBundle.getString("CLEANUP_ORPHANS"));
|
||||
@@ -671,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
|
||||
* string passed to the previous version argument indicates that all previous versions are affected.
|
||||
* Determines if the given identifiedVersion is affected by the given cpeId
|
||||
* 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 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 identifiedVersion the identified version of the dependency being analyzed
|
||||
* @param vulnerableSoftware a map of the vulnerable software with a boolean
|
||||
* 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
|
||||
*/
|
||||
Entry<String, Boolean> getMatchingSoftware(Map<String, Boolean> vulnerableSoftware, String vendor, String product,
|
||||
@@ -744,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
|
||||
* @return a dependency version
|
||||
@@ -761,15 +785,16 @@ 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
|
||||
* @return a dependency version
|
||||
*/
|
||||
private DependencyVersion parseDependencyVersion(VulnerableSoftware cpe) {
|
||||
DependencyVersion cpeVersion;
|
||||
final DependencyVersion cpeVersion;
|
||||
if (cpe.getVersion() != null && !cpe.getVersion().isEmpty()) {
|
||||
String versionText;
|
||||
final String versionText;
|
||||
if (cpe.getUpdate() != null && !cpe.getUpdate().isEmpty()) {
|
||||
versionText = String.format("%s.%s", cpe.getVersion(), cpe.getUpdate());
|
||||
} else {
|
||||
@@ -783,31 +808,36 @@ public class CveDB {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is only referenced in unused code.
|
||||
*
|
||||
* Deletes unused dictionary entries from the database.
|
||||
*/
|
||||
public void deleteUnusedCpe() {
|
||||
CallableStatement cs = null;
|
||||
public synchronized void deleteUnusedCpe() {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
cs = getConnection().prepareCall(statementBundle.getString("DELETE_UNUSED_DICT_CPE"));
|
||||
cs.executeUpdate();
|
||||
ps = getConnection().prepareStatement(statementBundle.getString("DELETE_UNUSED_DICT_CPE"));
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException ex) {
|
||||
LOGGER.error("Unable to delete CPE dictionary entries", ex);
|
||||
} 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.
|
||||
*
|
||||
* Merges CPE entries into the database.
|
||||
*
|
||||
* @param cpe the CPE identifier
|
||||
* @param vendor the CPE vendor
|
||||
* @param product the CPE product
|
||||
*/
|
||||
public void addCpe(String cpe, String vendor, String product) {
|
||||
public synchronized void addCpe(String cpe, String vendor, String product) {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
ps = getConnection().prepareCall(statementBundle.getString("ADD_DICT_CPE"));
|
||||
ps = getConnection().prepareStatement(statementBundle.getString("ADD_DICT_CPE"));
|
||||
ps.setString(1, cpe);
|
||||
ps.setString(2, vendor);
|
||||
ps.setString(3, product);
|
||||
|
||||
@@ -45,6 +45,10 @@ public class DatabaseProperties {
|
||||
* updates)..
|
||||
*/
|
||||
public static final String MODIFIED = "Modified";
|
||||
/**
|
||||
* The properties file key for the last checked field - used to store the last check time of the Modified NVD CVE xml file.
|
||||
*/
|
||||
public static final String LAST_CHECKED = "NVD CVE Checked";
|
||||
/**
|
||||
* The properties file key for the last updated field - used to store the last updated time of the Modified NVD CVE xml file.
|
||||
*/
|
||||
@@ -66,11 +70,11 @@ public class DatabaseProperties {
|
||||
/**
|
||||
* A collection of properties about the data.
|
||||
*/
|
||||
private Properties properties;
|
||||
private final Properties properties;
|
||||
/**
|
||||
* A reference to the database.
|
||||
*/
|
||||
private CveDB cveDB;
|
||||
private final CveDB cveDB;
|
||||
|
||||
/**
|
||||
* Constructs a new data properties object.
|
||||
@@ -79,13 +83,6 @@ public class DatabaseProperties {
|
||||
*/
|
||||
DatabaseProperties(CveDB cveDB) {
|
||||
this.cveDB = cveDB;
|
||||
loadProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the properties from the database.
|
||||
*/
|
||||
private void loadProperties() {
|
||||
this.properties = cveDB.getProperties();
|
||||
}
|
||||
|
||||
|
||||
@@ -63,15 +63,13 @@ public final class DriverLoader {
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the specified class by registering the supplied paths to the class loader and then registers the driver
|
||||
* with the driver manager. The pathToDriver argument is added to the class loader so that an external driver can be
|
||||
* loaded. Note, the pathToDriver can contain a semi-colon separated list of paths so any dependencies can be added
|
||||
* as needed. If a path in the pathToDriver argument is a directory all files in the directory are added to the
|
||||
* class path.
|
||||
* Loads the specified class by registering the supplied paths to the class loader and then registers the driver with the
|
||||
* driver manager. The pathToDriver argument is added to the class loader so that an external driver can be loaded. Note, the
|
||||
* pathToDriver can contain a semi-colon separated list of paths so any dependencies can be added as needed. If a path in the
|
||||
* pathToDriver argument is a directory all files in the directory are added to the class path.
|
||||
*
|
||||
* @param className the fully qualified name of the desired class
|
||||
* @param pathToDriver the path to the JAR file containing the driver; note, this can be a semi-colon separated list
|
||||
* of paths
|
||||
* @param pathToDriver the path to the JAR file containing the driver; note, this can be a semi-colon separated list of paths
|
||||
* @return the loaded Driver
|
||||
* @throws DriverLoadException thrown if the driver cannot be loaded
|
||||
*/
|
||||
@@ -83,14 +81,15 @@ public final class DriverLoader {
|
||||
final File file = new File(path);
|
||||
if (file.isDirectory()) {
|
||||
final File[] files = file.listFiles();
|
||||
|
||||
for (File f : files) {
|
||||
try {
|
||||
urls.add(f.toURI().toURL());
|
||||
} catch (MalformedURLException ex) {
|
||||
LOGGER.debug("Unable to load database driver '{}'; invalid path provided '{}'",
|
||||
className, f.getAbsoluteFile(), ex);
|
||||
throw new DriverLoadException("Unable to load database driver. Invalid path provided", ex);
|
||||
if (files != null) {
|
||||
for (File f : files) {
|
||||
try {
|
||||
urls.add(f.toURI().toURL());
|
||||
} catch (MalformedURLException ex) {
|
||||
LOGGER.debug("Unable to load database driver '{}'; invalid path provided '{}'",
|
||||
className, f.getAbsoluteFile(), ex);
|
||||
throw new DriverLoadException("Unable to load database driver. Invalid path provided", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (file.exists()) {
|
||||
@@ -98,7 +97,7 @@ public final class DriverLoader {
|
||||
urls.add(file.toURI().toURL());
|
||||
} catch (MalformedURLException ex) {
|
||||
LOGGER.debug("Unable to load database driver '{}'; invalid path provided '{}'",
|
||||
className, file.getAbsoluteFile(), ex);
|
||||
className, file.getAbsoluteFile(), ex);
|
||||
throw new DriverLoadException("Unable to load database driver. Invalid path provided", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ class DriverShim implements Driver {
|
||||
* @throws SQLFeatureNotSupportedException thrown if the feature is not supported
|
||||
* @see java.sql.Driver#getParentLogger()
|
||||
*/
|
||||
//@Override
|
||||
@Override
|
||||
public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException {
|
||||
//return driver.getParentLogger();
|
||||
Method m = null;
|
||||
|
||||
@@ -24,7 +24,6 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
@@ -44,9 +43,15 @@ import org.slf4j.LoggerFactory;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* The CpeUpdater is designed to download the CPE data file from NIST and import the data into the database. However, as this
|
||||
* currently adds no beneficial data, compared to what is in the CPE data contained in the CVE data files, this class is not
|
||||
* currently used. The code is being kept as a future update may utilize more data from the CPE xml files.
|
||||
*
|
||||
* This class is currently unused and if enabled will likely not work on MySQL
|
||||
* as the MERGE statement is used.
|
||||
*
|
||||
* The CpeUpdater is designed to download the CPE data file from NIST and import
|
||||
* the data into the database. However, as this currently adds no beneficial
|
||||
* data, compared to what is in the CPE data contained in the CVE data files,
|
||||
* this class is not currently used. The code is being kept as a future update
|
||||
* may utilize more data from the CPE XML files.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
@@ -69,8 +74,8 @@ public class CpeUpdater extends BaseUpdater implements CachedWebDataSource {
|
||||
for (Cpe cpe : cpes) {
|
||||
getCveDB().addCpe(cpe.getValue(), cpe.getVendor(), cpe.getProduct());
|
||||
}
|
||||
final Date now = new Date();
|
||||
getProperties().save(LAST_CPE_UPDATE, Long.toString(now.getTime()));
|
||||
final long now = System.currentTimeMillis();
|
||||
getProperties().save(LAST_CPE_UPDATE, Long.toString(now));
|
||||
LOGGER.info("CPE update complete");
|
||||
}
|
||||
} finally {
|
||||
@@ -82,7 +87,8 @@ public class CpeUpdater extends BaseUpdater implements CachedWebDataSource {
|
||||
* Downloads the CPE XML file.
|
||||
*
|
||||
* @return the file reference to the CPE.xml file
|
||||
* @throws UpdateException thrown if there is an issue downloading the XML file
|
||||
* @throws UpdateException thrown if there is an issue downloading the XML
|
||||
* file
|
||||
*/
|
||||
private File downloadCpe() throws UpdateException {
|
||||
File xml;
|
||||
@@ -110,11 +116,13 @@ public class CpeUpdater extends BaseUpdater implements CachedWebDataSource {
|
||||
*
|
||||
* @param xml the CPE data file
|
||||
* @return the list of CPE entries
|
||||
* @throws UpdateException thrown if there is an issue with parsing the XML file
|
||||
* @throws UpdateException thrown if there is an issue with parsing the XML
|
||||
* file
|
||||
*/
|
||||
private List<Cpe> processXML(final File xml) throws UpdateException {
|
||||
try {
|
||||
final SAXParserFactory factory = SAXParserFactory.newInstance();
|
||||
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
final SAXParser saxParser = factory.newSAXParser();
|
||||
final CPEHandler handler = new CPEHandler();
|
||||
saxParser.parse(xml, handler);
|
||||
@@ -129,23 +137,25 @@ public class CpeUpdater extends BaseUpdater implements CachedWebDataSource {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to find the last time the CPE data was refreshed and if it needs to be updated.
|
||||
* Checks to find the last time the CPE data was refreshed and if it needs
|
||||
* to be updated.
|
||||
*
|
||||
* @return true if the CPE data should be refreshed
|
||||
*/
|
||||
private boolean updateNeeded() {
|
||||
final Date now = new Date();
|
||||
final int days = Settings.getInt(Settings.KEYS.CVE_MODIFIED_VALID_FOR_DAYS, 30);
|
||||
final long now = System.currentTimeMillis();
|
||||
final int days = Settings.getInt(Settings.KEYS.CPE_MODIFIED_VALID_FOR_DAYS, 30);
|
||||
long timestamp = 0;
|
||||
final String ts = getProperties().getProperty(LAST_CPE_UPDATE);
|
||||
if (ts != null && ts.matches("^[0-9]+$")) {
|
||||
timestamp = Long.parseLong(ts);
|
||||
}
|
||||
return !DateUtil.withinDateRange(timestamp, now.getTime(), days);
|
||||
return !DateUtil.withinDateRange(timestamp, now, days);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws FileNotFoundException thrown if the file does not exist
|
||||
@@ -156,6 +166,7 @@ public class CpeUpdater extends BaseUpdater implements CachedWebDataSource {
|
||||
final String originalPath = file.getPath();
|
||||
final File gzip = new File(originalPath + ".gz");
|
||||
if (gzip.isFile() && !gzip.delete()) {
|
||||
LOGGER.debug("Failed to delete intial temporary file {}", gzip.toString());
|
||||
gzip.deleteOnExit();
|
||||
}
|
||||
if (!file.renameTo(gzip)) {
|
||||
@@ -190,8 +201,9 @@ public class CpeUpdater extends BaseUpdater implements CachedWebDataSource {
|
||||
LOGGER.trace("ignore", ex);
|
||||
}
|
||||
}
|
||||
if (gzip.isFile()) {
|
||||
FileUtils.deleteQuietly(gzip);
|
||||
if (gzip.isFile() && !FileUtils.deleteQuietly(gzip)) {
|
||||
LOGGER.debug("Failed to delete temporary file {}", gzip.toString());
|
||||
gzip.deleteOnExit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Date;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.owasp.dependencycheck.data.nvdcve.CveDB;
|
||||
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
|
||||
@@ -29,6 +28,7 @@ import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
|
||||
import org.owasp.dependencycheck.data.update.exception.UpdateException;
|
||||
import org.owasp.dependencycheck.utils.DateUtil;
|
||||
import org.owasp.dependencycheck.utils.DependencyVersion;
|
||||
import org.owasp.dependencycheck.utils.InvalidSettingException;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.owasp.dependencycheck.utils.URLConnectionFactory;
|
||||
import org.owasp.dependencycheck.utils.URLConnectionFailureException;
|
||||
@@ -36,6 +36,10 @@ import org.slf4j.Logger;
|
||||
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
|
||||
*/
|
||||
@@ -59,12 +63,14 @@ public class EngineVersionCheck implements CachedWebDataSource {
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@@ -73,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
|
||||
*/
|
||||
@@ -81,50 +88,61 @@ public class EngineVersionCheck implements CachedWebDataSource {
|
||||
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
|
||||
public void update() throws UpdateException {
|
||||
try {
|
||||
openDatabase();
|
||||
LOGGER.debug("Begin Engine Version Check");
|
||||
final DatabaseProperties properties = cveDB.getDatabaseProperties();
|
||||
final long lastChecked = Long.parseLong(properties.getProperty(ENGINE_VERSION_CHECKED_ON, "0"));
|
||||
final long now = (new Date()).getTime();
|
||||
updateToVersion = properties.getProperty(CURRENT_ENGINE_RELEASE, "");
|
||||
final String currentVersion = Settings.getString(Settings.KEYS.APPLICATION_VERSION, "0.0.0");
|
||||
LOGGER.debug("Last checked: {}", lastChecked);
|
||||
LOGGER.debug("Now: {}", now);
|
||||
LOGGER.debug("Current version: {}", currentVersion);
|
||||
final boolean updateNeeded = shouldUpdate(lastChecked, now, properties, currentVersion);
|
||||
if (updateNeeded) {
|
||||
LOGGER.warn("A new version of dependency-check is available. Consider updating to version {}.",
|
||||
updateToVersion);
|
||||
if (Settings.getBoolean(Settings.KEYS.AUTO_UPDATE)) {
|
||||
openDatabase();
|
||||
LOGGER.debug("Begin Engine Version Check");
|
||||
final DatabaseProperties properties = cveDB.getDatabaseProperties();
|
||||
final long lastChecked = Long.parseLong(properties.getProperty(ENGINE_VERSION_CHECKED_ON, "0"));
|
||||
final long now = System.currentTimeMillis();
|
||||
updateToVersion = properties.getProperty(CURRENT_ENGINE_RELEASE, "");
|
||||
final String currentVersion = Settings.getString(Settings.KEYS.APPLICATION_VERSION, "0.0.0");
|
||||
LOGGER.debug("Last checked: {}", lastChecked);
|
||||
LOGGER.debug("Now: {}", now);
|
||||
LOGGER.debug("Current version: {}", currentVersion);
|
||||
final boolean updateNeeded = shouldUpdate(lastChecked, now, properties, currentVersion);
|
||||
if (updateNeeded) {
|
||||
LOGGER.warn("A new version of dependency-check is available. Consider updating to version {}.",
|
||||
updateToVersion);
|
||||
}
|
||||
}
|
||||
} catch (DatabaseException ex) {
|
||||
LOGGER.debug("Database Exception opening databases to retrieve properties", ex);
|
||||
throw new UpdateException("Error occured updating database properties.");
|
||||
throw new UpdateException("Error occurred updating database properties.");
|
||||
} catch (InvalidSettingException ex) {
|
||||
LOGGER.debug("Unable to determine if autoupdate is enabled", ex);
|
||||
} finally {
|
||||
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 now the current epoch time
|
||||
* @param properties the database properties object
|
||||
* @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>
|
||||
* @throws UpdateException thrown if there is an error connecting to the github documentation site or accessing the local
|
||||
* database.
|
||||
* @return <code>true</code> if a newer version of the database has been
|
||||
* released; otherwise <code>false</code>
|
||||
* @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,
|
||||
String currentVersion) throws UpdateException {
|
||||
//check every 30 days if we know there is an update, otherwise check every 7 days
|
||||
int checkRange = 30;
|
||||
if (updateToVersion.isEmpty()) {
|
||||
checkRange = 7;
|
||||
}
|
||||
final int checkRange = 30;
|
||||
if (!DateUtil.withinDateRange(lastChecked, now, checkRange)) {
|
||||
LOGGER.debug("Checking web for new version.");
|
||||
final String currentRelease = getCurrentReleaseVersion();
|
||||
@@ -134,14 +152,16 @@ public class EngineVersionCheck implements CachedWebDataSource {
|
||||
updateToVersion = v.toString();
|
||||
if (!currentRelease.equals(updateToVersion)) {
|
||||
properties.save(CURRENT_ENGINE_RELEASE, updateToVersion);
|
||||
} else {
|
||||
properties.save(CURRENT_ENGINE_RELEASE, "");
|
||||
}
|
||||
properties.save(ENGINE_VERSION_CHECKED_ON, Long.toString(now));
|
||||
}
|
||||
}
|
||||
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 released = new DependencyVersion(updateToVersion);
|
||||
if (running.compareTo(released) < 0) {
|
||||
@@ -180,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
|
||||
*/
|
||||
@@ -199,11 +220,11 @@ public class EngineVersionCheck implements CachedWebDataSource {
|
||||
return releaseVersion.trim();
|
||||
}
|
||||
} 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) {
|
||||
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) {
|
||||
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 {
|
||||
if (conn != null) {
|
||||
conn.disconnect();
|
||||
|
||||
@@ -19,13 +19,14 @@ package org.owasp.dependencycheck.data.update;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
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 static org.owasp.dependencycheck.data.nvdcve.DatabaseProperties.MODIFIED;
|
||||
import org.owasp.dependencycheck.data.update.exception.InvalidDataException;
|
||||
@@ -49,7 +50,7 @@ import org.slf4j.LoggerFactory;
|
||||
public class NvdCveUpdater extends BaseUpdater implements CachedWebDataSource {
|
||||
|
||||
/**
|
||||
* The logger
|
||||
* The logger.
|
||||
*/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(NvdCveUpdater.class);
|
||||
/**
|
||||
@@ -58,23 +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);
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Downloads the latest NVD CVE XML file from the web and imports it into the current CVE Database.</p>
|
||||
* Downloads the latest NVD CVE XML file from the web and imports it into
|
||||
* 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
|
||||
public void update() throws UpdateException {
|
||||
try {
|
||||
openDataStores();
|
||||
final UpdateableNvdCve updateable = getUpdatesNeeded();
|
||||
if (updateable.isUpdateNeeded()) {
|
||||
performUpdate(updateable);
|
||||
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();
|
||||
if (updateable.isUpdateNeeded()) {
|
||||
performUpdate(updateable);
|
||||
}
|
||||
getProperties().save(DatabaseProperties.LAST_CHECKED, Long.toString(System.currentTimeMillis()));
|
||||
}
|
||||
} catch (MalformedURLException ex) {
|
||||
LOGGER.warn(
|
||||
"NVD CVE properties files contain an invalid URL, unable to update the data to use the most current data.");
|
||||
LOGGER.debug("", ex);
|
||||
throw new UpdateException("NVD CVE properties files contain an invalid URL, unable to update the data to use the most current data.", ex);
|
||||
} catch (DownloadFailedException ex) {
|
||||
LOGGER.warn(
|
||||
"Unable to download the NVD CVE data; the results may not include the most recent CPE/CVEs from the NVD.");
|
||||
@@ -82,118 +91,167 @@ public class NvdCveUpdater extends BaseUpdater implements CachedWebDataSource {
|
||||
LOGGER.info(
|
||||
"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 {
|
||||
closeDataStores();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the latest NVD CVE XML file from the web and imports it into the current CVE Database.
|
||||
* Checks if the NVD CVE XML files were last checked recently. As an
|
||||
* optimization, we can avoid repetitive checks against the NVD. Setting
|
||||
* CVE_CHECK_VALID_FOR_HOURS determines the duration since last check before
|
||||
* checking again. A database property stores the timestamp of the last
|
||||
* check.
|
||||
*
|
||||
* @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
|
||||
* @return true to proceed with the check, or false to skip
|
||||
* @throws UpdateException thrown when there is an issue checking for
|
||||
* updates
|
||||
*/
|
||||
public void performUpdate(UpdateableNvdCve updateable) throws UpdateException {
|
||||
int maxUpdates = 0;
|
||||
private boolean checkUpdate() throws UpdateException {
|
||||
boolean proceed = true;
|
||||
// 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);
|
||||
if (dataExists() && 0 < validForHours) {
|
||||
// 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 lastChecked = Long.parseLong(getProperties().getProperty(DatabaseProperties.LAST_CHECKED, "0"));
|
||||
final long now = System.currentTimeMillis();
|
||||
proceed = (now - lastChecked) > msValid;
|
||||
if (!proceed) {
|
||||
LOGGER.info("Skipping NVD check since last check was within {} hours.", validForHours);
|
||||
LOGGER.debug("Last NVD was at {}, and now {} is within {} ms.",
|
||||
lastChecked, now, msValid);
|
||||
}
|
||||
}
|
||||
return proceed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the CVE Index to ensure data exists and analysis can continue.
|
||||
*
|
||||
* @return true if the database contains data
|
||||
*/
|
||||
private boolean dataExists() {
|
||||
CveDB cve = null;
|
||||
try {
|
||||
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.");
|
||||
}
|
||||
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.");
|
||||
}
|
||||
cve = new CveDB();
|
||||
cve.open();
|
||||
return cve.dataExists();
|
||||
} catch (DatabaseException ex) {
|
||||
return false;
|
||||
} 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
|
||||
* date. If the data needs to be refreshed this method will return the NvdCveUrl for the files that need to be updated.
|
||||
* Downloads the latest NVD CVE XML file from the web and imports it into
|
||||
* 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
|
||||
* @throws MalformedURLException is thrown if the URL for the NVD CVE Meta data is incorrect
|
||||
* @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
|
||||
* @throws MalformedURLException is thrown if the URL for the NVD CVE Meta
|
||||
* data is incorrect
|
||||
* @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 {
|
||||
UpdateableNvdCve updates = null;
|
||||
@@ -214,11 +272,11 @@ public class NvdCveUpdater extends BaseUpdater implements CachedWebDataSource {
|
||||
if (!getProperties().isEmpty()) {
|
||||
try {
|
||||
final long lastUpdated = Long.parseLong(getProperties().getProperty(DatabaseProperties.LAST_UPDATED, "0"));
|
||||
final Date now = new Date();
|
||||
final long now = System.currentTimeMillis();
|
||||
final int days = Settings.getInt(Settings.KEYS.CVE_MODIFIED_VALID_FOR_DAYS, 7);
|
||||
if (lastUpdated == updates.getTimeStamp(MODIFIED)) {
|
||||
updates.clear(); //we don't need to update anything.
|
||||
} else if (DateUtil.withinDateRange(lastUpdated, now.getTime(), days)) {
|
||||
} else if (DateUtil.withinDateRange(lastUpdated, now, days)) {
|
||||
for (NvdCveInfo entry : updates) {
|
||||
if (MODIFIED.equals(entry.getId())) {
|
||||
entry.setNeedsUpdate(true);
|
||||
@@ -257,9 +315,12 @@ public class NvdCveUpdater extends BaseUpdater implements CachedWebDataSource {
|
||||
* Retrieves the timestamps from the NVD CVE meta data file.
|
||||
*
|
||||
* @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 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 MalformedURLException thrown if the URL for the NVD CCE Meta data
|
||||
* is incorrect.
|
||||
* @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
|
||||
*/
|
||||
private UpdateableNvdCve retrieveCurrentTimestampsFromWeb()
|
||||
@@ -281,5 +342,4 @@ public class NvdCveUpdater extends BaseUpdater implements CachedWebDataSource {
|
||||
}
|
||||
return updates;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.owasp.dependencycheck.data.update.NvdCveUpdater;
|
||||
import org.owasp.dependencycheck.data.update.exception.InvalidDataException;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.xml.sax.Attributes;
|
||||
@@ -40,13 +41,18 @@ public class CPEHandler extends DefaultHandler {
|
||||
*/
|
||||
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;
|
||||
/**
|
||||
* A reference to the current element.
|
||||
*/
|
||||
private Element current = new Element();
|
||||
private final Element current = new Element();
|
||||
/**
|
||||
* The logger.
|
||||
*/
|
||||
@@ -54,7 +60,7 @@ public class CPEHandler extends DefaultHandler {
|
||||
/**
|
||||
* 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.
|
||||
@@ -72,7 +78,8 @@ public class CPEHandler extends DefaultHandler {
|
||||
* @param localName the local name
|
||||
* @param qName the qualified name
|
||||
* @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
|
||||
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 value = attributes.getValue("name");
|
||||
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 {
|
||||
final Cpe cpe = new Cpe(value);
|
||||
data.add(cpe);
|
||||
@@ -123,7 +130,8 @@ public class CPEHandler extends DefaultHandler {
|
||||
* @param ch the char array
|
||||
* @param start the start position 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
|
||||
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 localName the local 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
|
||||
public void endElement(String uri, String localName, String qName) throws SAXException {
|
||||
@@ -177,9 +187,10 @@ public class CPEHandler extends DefaultHandler {
|
||||
|
||||
// <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 class Element {
|
||||
protected static final class Element {
|
||||
|
||||
/**
|
||||
* A node type in the CPE Schema 2.2
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user