Compare commits
926 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f1578b2d6 | ||
|
|
7969b343b0 | ||
|
|
58cf1f4c8e | ||
|
|
a5b87af862 | ||
|
|
a0e592b934 | ||
|
|
7eccc538bb | ||
|
|
59daa8570a | ||
|
|
3f52d318bc | ||
|
|
11a7a0c934 | ||
|
|
89f49b0e29 | ||
|
|
72457cbf8e | ||
|
|
c11ba27509 | ||
|
|
8a611f9ba6 | ||
|
|
4a73875e4d | ||
|
|
d9d5e612ff | ||
|
|
4d8599e4fc | ||
|
|
59c7061d29 | ||
|
|
996557c667 | ||
|
|
519fb19a77 | ||
|
|
36456cb151 | ||
|
|
4ae87cc36c | ||
|
|
b37df89fb1 | ||
|
|
d18e7a751d | ||
|
|
8d5ea98e50 | ||
|
|
835dc08049 | ||
|
|
62c9409fe9 | ||
|
|
2374f578ed | ||
|
|
34e2f033d8 | ||
|
|
420825cacc | ||
|
|
466ec93d8e | ||
|
|
3f5bb6ab29 | ||
|
|
ebe5f858c8 | ||
|
|
9dd025437b | ||
|
|
c0ebac305a | ||
|
|
1f23ab7ba4 | ||
|
|
ea3b63998d | ||
|
|
3093426458 | ||
|
|
37716feac7 | ||
|
|
56b12c38d2 | ||
|
|
749ead5d4a | ||
|
|
3be50ab8da | ||
|
|
649f4a6991 | ||
|
|
0ff7641471 | ||
|
|
1679bfae20 | ||
|
|
45aa364436 | ||
|
|
778516c4d9 | ||
|
|
464d523c42 | ||
|
|
0f6a1987d4 | ||
|
|
20c6247ce5 | ||
|
|
a10dd67e0f | ||
|
|
5729ad6026 | ||
|
|
9aa0d87a21 | ||
|
|
fe3f1b9924 | ||
|
|
00e52a88fa | ||
|
|
5811dffe7a | ||
|
|
7278982af4 | ||
|
|
c17b4154ec | ||
|
|
d6e74cce08 | ||
|
|
3f80749241 | ||
|
|
7f72b6ac69 | ||
|
|
03e7b90b9f | ||
|
|
7936b3533b | ||
|
|
bd7e61d7cc | ||
|
|
69214e0c22 | ||
|
|
45bff26558 | ||
|
|
b2e429ccc6 | ||
|
|
76363c227b | ||
|
|
d5a3e5c2c5 | ||
|
|
2b02807ef0 | ||
|
|
be659ae094 | ||
|
|
b2c105adbc | ||
|
|
c61f462948 | ||
|
|
3ffed18e02 | ||
|
|
f54e7257d1 | ||
|
|
cc13b6a27c | ||
|
|
8877db1979 | ||
|
|
af58122c91 | ||
|
|
b7ca5e5590 | ||
|
|
69b6d875e6 | ||
|
|
1fbd516b83 | ||
|
|
dec5d3b165 | ||
|
|
d5e2040cef | ||
|
|
4326befdec | ||
|
|
3d4a5d9917 | ||
|
|
d770034788 | ||
|
|
a977533c78 | ||
|
|
c5e13dd5e4 | ||
|
|
a8040fe4d2 | ||
|
|
9e066008c3 | ||
|
|
22c6601526 | ||
|
|
425464fd76 | ||
|
|
ccb0751ffd | ||
|
|
f832de81b7 | ||
|
|
8a37de0686 | ||
|
|
836e4704f8 | ||
|
|
3e5390309c | ||
|
|
f8c0b38716 | ||
|
|
65e6070e5f | ||
|
|
7b78ebbc42 | ||
|
|
03c3189c02 | ||
|
|
4a34dfe0e9 | ||
|
|
4cf9a723fe | ||
|
|
bd1b135db3 | ||
|
|
8c3b305149 | ||
|
|
a3719038b8 | ||
|
|
c68a261c0b | ||
|
|
75fea79ac1 | ||
|
|
eb9f9680ec | ||
|
|
3634afdb81 | ||
|
|
77b5df896a | ||
|
|
b81f64058c | ||
|
|
a8a90d7c63 | ||
|
|
17bb575002 | ||
|
|
abcea1a14d | ||
|
|
10942f7c08 | ||
|
|
87ee829e80 | ||
|
|
fcc2c1e4c7 | ||
|
|
269095d034 | ||
|
|
40492ee00a | ||
|
|
64cdd5aedc | ||
|
|
3bb650cb77 | ||
|
|
774544c975 | ||
|
|
299805a726 | ||
|
|
276363e793 | ||
|
|
e750bd53fc | ||
|
|
98fee7b5d2 | ||
|
|
53aaea9fe2 | ||
|
|
824fbb6368 | ||
|
|
80566b91ab | ||
|
|
533d05a1b5 | ||
|
|
6a1fc4fade | ||
|
|
9008d0ddf0 | ||
|
|
583f4577bc | ||
|
|
e5716d5092 | ||
|
|
7192ae1287 | ||
|
|
99c65eff48 | ||
|
|
91df593566 | ||
|
|
07aeaeb989 | ||
|
|
cfeecdacd0 | ||
|
|
564dfa8b62 | ||
|
|
75dd6f2010 | ||
|
|
e26fd0b759 | ||
|
|
d630680a51 | ||
|
|
1723c3f6a0 | ||
|
|
53dd90302e | ||
|
|
5c6e06b05e | ||
|
|
cf6fb0c8a5 | ||
|
|
e0e71b2eae | ||
|
|
53f4a29fb1 | ||
|
|
89d58d1abc | ||
|
|
d6b6969cb3 | ||
|
|
e7bf6fa69d | ||
|
|
6e51970b91 | ||
|
|
56d7d43768 | ||
|
|
256c99ffa2 | ||
|
|
9c0bc3b13b | ||
|
|
9b8a323d85 | ||
|
|
3178c1e326 | ||
|
|
321d68e03a | ||
|
|
3d8753c621 | ||
|
|
967c56909d | ||
|
|
7c4831d2d1 | ||
|
|
4b49e11a33 | ||
|
|
d84a6a8627 | ||
|
|
63b7f4a8db | ||
|
|
ca2160264a | ||
|
|
7842594f53 | ||
|
|
7db056102c | ||
|
|
a5a800fa0a | ||
|
|
9147ec148d | ||
|
|
b3260588c6 | ||
|
|
7d31328271 | ||
|
|
6e82981ee3 | ||
|
|
9d7b115bb5 | ||
|
|
8eae5002a3 | ||
|
|
31bd6c0371 | ||
|
|
7585f9d537 | ||
|
|
76037cdf72 | ||
|
|
98c5421edc | ||
|
|
e63fc7e3f5 | ||
|
|
6ed9cf3fb4 | ||
|
|
9865eab2c0 | ||
|
|
678e72a8b6 | ||
|
|
ec41899089 | ||
|
|
b2d913cc21 | ||
|
|
bc86c24e6a | ||
|
|
87a77dd95c | ||
|
|
e8188f3432 | ||
|
|
50506be546 | ||
|
|
4ded028258 | ||
|
|
6da8b3c4a1 | ||
|
|
d5c92cbcb3 | ||
|
|
ed5f98d6f0 | ||
|
|
f854b8f908 | ||
|
|
de7a6159d4 | ||
|
|
6090a34037 | ||
|
|
f566745479 | ||
|
|
153234b623 | ||
|
|
ac510d21ff | ||
|
|
44fa2c5800 | ||
|
|
d785fc2a54 | ||
|
|
ea800e04bc | ||
|
|
fe582ac635 | ||
|
|
330edb3bce | ||
|
|
212fec7115 | ||
|
|
24d7021c47 | ||
|
|
e3a01ff6a8 | ||
|
|
81f2ba8a46 | ||
|
|
9e9370b178 | ||
|
|
ced6114a95 | ||
|
|
3144faae5d | ||
|
|
8960c67a82 | ||
|
|
f8ca924434 | ||
|
|
399a08775e | ||
|
|
92f36ca558 | ||
|
|
3dcc58205a | ||
|
|
09779962cf | ||
|
|
9cc78770a3 | ||
|
|
f653ca9131 | ||
|
|
6f9fd91849 | ||
|
|
cb1aec4fc0 | ||
|
|
7cebaf8a76 | ||
|
|
241c943424 | ||
|
|
d5d88d8cf0 | ||
|
|
cf9d26068c | ||
|
|
308a93dc72 | ||
|
|
d6a7e65e4c | ||
|
|
e0a5c5d3b8 | ||
|
|
314f775243 | ||
|
|
7a1644135a | ||
|
|
5076326589 | ||
|
|
ce56261b52 | ||
|
|
baa0e897b2 | ||
|
|
1d49c0e1ce | ||
|
|
08755e446e | ||
|
|
bb12d9dadb | ||
|
|
fd1429fef0 | ||
|
|
d3c421a4a8 | ||
|
|
0c919da4b1 | ||
|
|
9afbf1255f | ||
|
|
50b105c4af | ||
|
|
028508c1f7 | ||
|
|
f0137a3695 | ||
|
|
e6d3a1718c | ||
|
|
86ba551e07 | ||
|
|
26418be937 | ||
|
|
092a19bdc1 | ||
|
|
6d3398574c | ||
|
|
b08969ad89 | ||
|
|
0653656526 | ||
|
|
7a5793c562 | ||
|
|
562ff7807d | ||
|
|
7971bdf7f7 | ||
|
|
d926b7fd6d | ||
|
|
c00404793a | ||
|
|
a0e0ee6c1e | ||
|
|
4ccbee705b | ||
|
|
db43d55b2c | ||
|
|
5a3a333eec | ||
|
|
039edf1616 | ||
|
|
47498bbf23 | ||
|
|
cc28bf4ae2 | ||
|
|
0e8736045e | ||
|
|
19b581edef | ||
|
|
295f6656d9 | ||
|
|
1214d8c14d | ||
|
|
b4cd96fc9a | ||
|
|
3238a9b898 | ||
|
|
c0f66320f6 | ||
|
|
383220f384 | ||
|
|
76a9c37e6b | ||
|
|
e788e6a5ad | ||
|
|
d00e912934 | ||
|
|
8ebb663368 | ||
|
|
445ffc4123 | ||
|
|
6af49f4d55 | ||
|
|
1de9e8c086 | ||
|
|
59b0812adf | ||
|
|
719785c1ed | ||
|
|
8e5f627e59 | ||
|
|
5ced3c72b8 | ||
|
|
c002f0168c | ||
|
|
00c690f516 | ||
|
|
ab68ad5cc5 | ||
|
|
5c84ebefab | ||
|
|
eb2acaff22 | ||
|
|
84d0655c52 | ||
|
|
e137ebb9c2 | ||
|
|
10d690d929 | ||
|
|
14611d2fd9 | ||
|
|
0665bfe15f | ||
|
|
473096d35d | ||
|
|
0eae26e261 | ||
|
|
a32845f652 | ||
|
|
15a0f7eadb | ||
|
|
5a0a6abf11 | ||
|
|
032b8d9572 | ||
|
|
5798e3af83 | ||
|
|
8e15b9ce1c | ||
|
|
7a1f132c1f | ||
|
|
a8483b2195 | ||
|
|
83bbbd0cb0 | ||
|
|
132432dce6 | ||
|
|
e5eb8e42f5 | ||
|
|
1095ebea24 | ||
|
|
1541a602b2 | ||
|
|
03a141c252 | ||
|
|
5f2183fc8e | ||
|
|
820831fa5d | ||
|
|
6d2d767c52 | ||
|
|
e0c3a728ae | ||
|
|
ec92f7797f | ||
|
|
0ba490c6df | ||
|
|
cfd668e11d | ||
|
|
a8bc25321e | ||
|
|
fec13bcb86 | ||
|
|
cb1c07f998 | ||
|
|
6312b97faa | ||
|
|
21f13b55eb | ||
|
|
187598382b | ||
|
|
551fdd5022 | ||
|
|
58b0d03e28 | ||
|
|
3790197699 | ||
|
|
579fff122c | ||
|
|
feb3f79a13 | ||
|
|
b5cb08ac43 | ||
|
|
4ac5d9e0da | ||
|
|
93f741da35 | ||
|
|
648a999514 | ||
|
|
71490aebd9 | ||
|
|
9e90c0f912 | ||
|
|
de65073f61 | ||
|
|
6129ac7bd4 | ||
|
|
b5d4d27312 | ||
|
|
823fcd91f4 | ||
|
|
477e12d5cf | ||
|
|
a36a226ae2 | ||
|
|
886a21c633 | ||
|
|
fd19fa2082 | ||
|
|
843f1a462f | ||
|
|
5c5b8a361d | ||
|
|
417df0582d | ||
|
|
999d8f5866 | ||
|
|
47a444e795 | ||
|
|
dbceca8780 | ||
|
|
c66898e608 | ||
|
|
ee20cb59a5 | ||
|
|
5c51d83573 | ||
|
|
47b3b3848b | ||
|
|
95eb980f58 | ||
|
|
f738622c28 | ||
|
|
577509bbf9 | ||
|
|
774c78add0 | ||
|
|
b14406e329 | ||
|
|
29cf4bb517 | ||
|
|
a233e08929 | ||
|
|
cbd1c12773 | ||
|
|
0a3f0f9ffc | ||
|
|
d3014025b0 | ||
|
|
2887dc0d36 | ||
|
|
5f49e7da8e | ||
|
|
9e0032b258 | ||
|
|
008da49b83 | ||
|
|
9899cba816 | ||
|
|
27724a2faf | ||
|
|
8b6a283114 | ||
|
|
4379b8bacf | ||
|
|
56603dcfae | ||
|
|
1752736714 | ||
|
|
b1428b6758 | ||
|
|
9b6d84def6 | ||
|
|
ed162d7d6e | ||
|
|
1aae425945 | ||
|
|
26e447f11a | ||
|
|
ffbaa0a508 | ||
|
|
a9ebac3818 | ||
|
|
738e9fb119 | ||
|
|
7778783dd8 | ||
|
|
c442a433b0 | ||
|
|
f7aa85746d | ||
|
|
1883da3b2a | ||
|
|
997dd6022f | ||
|
|
63394a2400 | ||
|
|
a662b038dc | ||
|
|
e9df2bfa01 | ||
|
|
a7951b727c | ||
|
|
c6ad9ea57a | ||
|
|
a14810bbd4 | ||
|
|
bc5a95ebb3 | ||
|
|
306182e2ae | ||
|
|
ad096196ee | ||
|
|
af66e44427 | ||
|
|
0a012273ec | ||
|
|
73b011eba7 | ||
|
|
a31974a3c0 | ||
|
|
eb02bdd95a | ||
|
|
74805c6be8 | ||
|
|
d9bc4499a4 | ||
|
|
9128e2748b | ||
|
|
7f8c975bd7 | ||
|
|
8b6c841b1e | ||
|
|
4fcdea3ccb | ||
|
|
3be11cf52f | ||
|
|
b285cb0e57 | ||
|
|
dd5a7920e5 | ||
|
|
cfb848918f | ||
|
|
b977558f38 | ||
|
|
210e3dc990 | ||
|
|
f36671784e | ||
|
|
d626cc8a8b | ||
|
|
f26b61d773 | ||
|
|
12c2d3cbc6 | ||
|
|
209ca704de | ||
|
|
2e37d3adc1 | ||
|
|
509fb045b6 | ||
|
|
a2c364f9eb | ||
|
|
17a4e532c1 | ||
|
|
c103b79ec2 | ||
|
|
b545b5d0a3 | ||
|
|
342a1c6cff | ||
|
|
aafbdcd34d | ||
|
|
ec092501c3 | ||
|
|
bb708db89f | ||
|
|
085a9dcb79 | ||
|
|
037e12b0bd | ||
|
|
c9ab956f8f | ||
|
|
587c87b3a0 | ||
|
|
1a319859eb | ||
|
|
c989c31aeb | ||
|
|
e5d32c8764 | ||
|
|
23c177ed4a | ||
|
|
10a27042b5 | ||
|
|
2cec20c7ee | ||
|
|
7ecd09f497 | ||
|
|
8bf7f6cac5 | ||
|
|
067a2315df | ||
|
|
fecd1ad464 | ||
|
|
a3f2555bc1 | ||
|
|
5bf4cd46ff | ||
|
|
f878e225cc | ||
|
|
eb2598f3b3 | ||
|
|
e20a59b991 | ||
|
|
703c142659 | ||
|
|
8335b40368 | ||
|
|
05884c2d29 | ||
|
|
33b2aa2d52 | ||
|
|
9ab0622886 | ||
|
|
b33cd54916 | ||
|
|
d4bec0dd9a | ||
|
|
bdf6efeaac | ||
|
|
74431ca63f | ||
|
|
c90be385ef | ||
|
|
b0d9c0b550 | ||
|
|
9255132f9b | ||
|
|
d5c0092fa3 | ||
|
|
c7019debb9 | ||
|
|
7131270cad | ||
|
|
af5a1204bc | ||
|
|
58afcfc49a | ||
|
|
986762ca85 | ||
|
|
6342cf79f5 | ||
|
|
5fbf67f971 | ||
|
|
e441e5a696 | ||
|
|
d201efb029 | ||
|
|
25960126c7 | ||
|
|
63d5a6f584 | ||
|
|
2030951a8f | ||
|
|
cd841462cd | ||
|
|
735aa835a6 | ||
|
|
92e213ca32 | ||
|
|
d077c29716 | ||
|
|
d6eba48a50 | ||
|
|
2a1608d1d2 | ||
|
|
cc7d3dc2aa | ||
|
|
a5c4c682f5 | ||
|
|
688cfd6872 | ||
|
|
7e268dbae1 | ||
|
|
ce6a4231ef | ||
|
|
e1de8ab626 | ||
|
|
0058eaf357 | ||
|
|
732d95098a | ||
|
|
52f0943207 | ||
|
|
41f99f2b65 | ||
|
|
1f9e5c6263 | ||
|
|
2f3eddd2ab | ||
|
|
619a0ee700 | ||
|
|
b1b5c2c9a0 | ||
|
|
a86035c0bf | ||
|
|
c66b0f4db4 | ||
|
|
a4cf4bd314 | ||
|
|
f1cd9383c1 | ||
|
|
6fa57abe10 | ||
|
|
6e77c714b5 | ||
|
|
fbab020e6e | ||
|
|
5581a5cce7 | ||
|
|
b4be11775e | ||
|
|
b079f5e52e | ||
|
|
f9bf470a37 | ||
|
|
9d783dd2ab | ||
|
|
1b9aafbbaf | ||
|
|
1d3ee6a241 | ||
|
|
2f9c3071a6 | ||
|
|
4b0be4f115 | ||
|
|
1419c7c8c6 | ||
|
|
851cecdd73 | ||
|
|
753da3aad7 | ||
|
|
65c10d6d8e | ||
|
|
1b8b423131 | ||
|
|
55b1264c7d | ||
|
|
902a1888d4 | ||
|
|
98151f7d0e | ||
|
|
a6f0c559f8 | ||
|
|
e7ec5b841d | ||
|
|
d6f72ac0f3 | ||
|
|
7e3a10025a | ||
|
|
e16ec15226 | ||
|
|
6935b56c9d | ||
|
|
0e3a0b64e7 | ||
|
|
74e6aee236 | ||
|
|
db0602b7b8 | ||
|
|
c9b7c3f179 | ||
|
|
5bd9f4afb4 | ||
|
|
9d2ba5912e | ||
|
|
9986c4a6f3 | ||
|
|
df2c9697ef | ||
|
|
ab0388e882 | ||
|
|
c05d8a36eb | ||
|
|
492753d905 | ||
|
|
6e08bd23f4 | ||
|
|
a687c97808 | ||
|
|
c6864289cb | ||
|
|
97d85258c5 | ||
|
|
bee25f5aa2 | ||
|
|
386b97d2be | ||
|
|
00660485b7 | ||
|
|
1e8f24dedb | ||
|
|
2be190f863 | ||
|
|
ec7c6e6c85 | ||
|
|
c52bc53fd8 | ||
|
|
981631503a | ||
|
|
48de3a6a4f | ||
|
|
d1983a6978 | ||
|
|
f821a26aec | ||
|
|
3380e905de | ||
|
|
b5c2718756 | ||
|
|
a03a803b89 | ||
|
|
e743177ae6 | ||
|
|
6e12c69953 | ||
|
|
019ab77466 | ||
|
|
1730caf124 | ||
|
|
59d1533795 | ||
|
|
a6278ab7ea | ||
|
|
42a6004c7d | ||
|
|
6084c1b1d3 | ||
|
|
c96fbc1dba | ||
|
|
5546a8b093 | ||
|
|
6b76b38dcd | ||
|
|
941e50b460 | ||
|
|
5a10e5c9ff | ||
|
|
883fe13756 | ||
|
|
2e7c34cf9f | ||
|
|
9216efbd2f | ||
|
|
6c8100e5b6 | ||
|
|
e7ef50bedf | ||
|
|
386ca3565a | ||
|
|
2d854cd64d | ||
|
|
49b4b8be22 | ||
|
|
db975ebfee | ||
|
|
d60a41139b | ||
|
|
f62d869d27 | ||
|
|
6cbe3cdb93 | ||
|
|
b13e7b9da4 | ||
|
|
8fe34c8474 | ||
|
|
bef29be50f | ||
|
|
20275a1063 | ||
|
|
910385b084 | ||
|
|
8e779374a7 | ||
|
|
44fc6f728e | ||
|
|
1f62dcf22a | ||
|
|
0416c3b561 | ||
|
|
a6912cae76 | ||
|
|
63dfe8a952 | ||
|
|
62d1b761bd | ||
|
|
082b10a15b | ||
|
|
1a6bcd82b0 | ||
|
|
6ecd70220b | ||
|
|
e9f55f5772 | ||
|
|
155cadf901 | ||
|
|
cb29289167 | ||
|
|
e4db9d1d91 | ||
|
|
7b2e2cb817 | ||
|
|
c717f8d15d | ||
|
|
8db147acab | ||
|
|
e6de7aa9ca | ||
|
|
46f96740a2 | ||
|
|
8f9fb5c262 | ||
|
|
171d6d6684 | ||
|
|
f648b5ad0a | ||
|
|
ef21376f0a | ||
|
|
58958d68d8 | ||
|
|
a06b565ee9 | ||
|
|
a7db27ce5a | ||
|
|
cda69dc7f0 | ||
|
|
39f9594548 | ||
|
|
6d82ad32a9 | ||
|
|
cfcd8bf223 | ||
|
|
8149ad00b5 | ||
|
|
2310522806 | ||
|
|
e40ef656d6 | ||
|
|
e060d40a32 | ||
|
|
a522218c4e | ||
|
|
820455399c | ||
|
|
959d612534 | ||
|
|
cd81e6eab2 | ||
|
|
e6ec6920ad | ||
|
|
18a92fa1ca | ||
|
|
f95af9897b | ||
|
|
b61adcb1fd | ||
|
|
1bbf320755 | ||
|
|
159f26171c | ||
|
|
8ac00f6c0d | ||
|
|
ce2daf2493 | ||
|
|
f014f8fd59 | ||
|
|
f50a39a9e2 | ||
|
|
e0d8147104 | ||
|
|
c5cfac62da | ||
|
|
83469ce5cc | ||
|
|
7cd7b4a9a2 | ||
|
|
7681b277cf | ||
|
|
406efa96c0 | ||
|
|
9a7a30c0bc | ||
|
|
64bdfa0e80 | ||
|
|
067089973c | ||
|
|
85e6d753c7 | ||
|
|
4094984642 | ||
|
|
85c0009a43 | ||
|
|
234e312ee2 | ||
|
|
ce3ca64678 | ||
|
|
b042a600c3 | ||
|
|
686e9f07a9 | ||
|
|
bb6725372b | ||
|
|
6f012fc9c5 | ||
|
|
4c82458481 | ||
|
|
a0ac863998 | ||
|
|
d23ef838f8 | ||
|
|
f81ac197f5 | ||
|
|
652b37e630 | ||
|
|
c57e430393 | ||
|
|
fff6047df9 | ||
|
|
1e2b93d55b | ||
|
|
66b27a7795 | ||
|
|
63f0a272c4 | ||
|
|
8d2180cf5a | ||
|
|
1986f7e4dd | ||
|
|
21beb396b4 | ||
|
|
cb5a6f38d6 | ||
|
|
67e4aaede0 | ||
|
|
b42805d00c | ||
|
|
95d6888c87 | ||
|
|
549b315a65 | ||
|
|
5b80b16684 | ||
|
|
0cd0a4bf2b | ||
|
|
b5cf06cad8 | ||
|
|
b964d19d82 | ||
|
|
cf7990d444 | ||
|
|
738ccf7dbb | ||
|
|
fc2ea48c1d | ||
|
|
3af93b93d7 | ||
|
|
f386c3be92 | ||
|
|
239d910dbe | ||
|
|
48929deabd | ||
|
|
79523de1db | ||
|
|
fbfc14dfeb | ||
|
|
a8dc886f89 | ||
|
|
cfc9e064b9 | ||
|
|
e72fa3362a | ||
|
|
26364421e8 | ||
|
|
4a07974b54 | ||
|
|
eaddc7f2ba | ||
|
|
85056aaa00 | ||
|
|
c077c740fa | ||
|
|
c2eab87a3f | ||
|
|
ea582d2d2e | ||
|
|
2f89a24100 | ||
|
|
73ebb94f67 | ||
|
|
95bf387ecc | ||
|
|
f17a8452f9 | ||
|
|
920ffe1f33 | ||
|
|
093bcb7477 | ||
|
|
c06b3ec9eb | ||
|
|
ac6fe6f9fc | ||
|
|
2dffdaac42 | ||
|
|
cb445c9504 | ||
|
|
e3fc3aa9d1 | ||
|
|
97c3f5d642 | ||
|
|
0a52fc9a56 | ||
|
|
c831339b0d | ||
|
|
058ccf575f | ||
|
|
92be12bc2f | ||
|
|
1aa2f4b5b1 | ||
|
|
bba9431985 | ||
|
|
3c39f1e737 | ||
|
|
e6f4d07a87 | ||
|
|
e43358a0d2 | ||
|
|
f0644e8a9d | ||
|
|
11b010b281 | ||
|
|
c751029127 | ||
|
|
fb70d1b2f0 | ||
|
|
3d68783b7f | ||
|
|
0d77853912 | ||
|
|
ea1b5dd8f7 | ||
|
|
2dcb7d5ce1 | ||
|
|
99cab34527 | ||
|
|
f5eeed0bc2 | ||
|
|
1b85e56961 | ||
|
|
8a8ac5fd22 | ||
|
|
00c0354a8e | ||
|
|
a2a6973ba1 | ||
|
|
dd1d3a05fa | ||
|
|
2afe2d2640 | ||
|
|
29678f9b59 | ||
|
|
77edb251bb | ||
|
|
29151fa267 | ||
|
|
b3f13790bd | ||
|
|
38857c3356 | ||
|
|
d75990d9fd | ||
|
|
ed063f6534 | ||
|
|
c8a9bdc517 | ||
|
|
595729cdf8 | ||
|
|
6119f79748 | ||
|
|
d4fb46c9ba | ||
|
|
c41301afca | ||
|
|
50fd80830e | ||
|
|
1c203b4272 | ||
|
|
c545e9045d | ||
|
|
2721dc0647 | ||
|
|
51d13f4234 | ||
|
|
a60a5d6eab | ||
|
|
5959235425 | ||
|
|
d8e6d4e5fc | ||
|
|
7dfc9815b3 | ||
|
|
0c53b187a4 | ||
|
|
42dadfed8f | ||
|
|
a46c603c77 | ||
|
|
ad0020d9a6 | ||
|
|
a224f0bfd4 | ||
|
|
d8dc3650d3 | ||
|
|
30f7527f10 | ||
|
|
b1f5bdd8b2 | ||
|
|
c8e7c8b9fa | ||
|
|
30bf3223f8 | ||
|
|
886710ec30 | ||
|
|
510dc8d828 | ||
|
|
5ff7b2aab4 | ||
|
|
1e33536205 | ||
|
|
8b264a564a | ||
|
|
227da93c13 | ||
|
|
f939041606 | ||
|
|
e5b1a0bef8 | ||
|
|
b9404d0880 | ||
|
|
d6f12868be | ||
|
|
b79e96f6cf | ||
|
|
b066cc819e | ||
|
|
4b669a0d49 | ||
|
|
5e9de5d91a | ||
|
|
da68b061e3 | ||
|
|
6c3802071f | ||
|
|
ad84f09bce | ||
|
|
04166632d3 | ||
|
|
376238b1ad | ||
|
|
4f0dbff059 | ||
|
|
f506e2b50a | ||
|
|
88d2fbf5e2 | ||
|
|
7fd8cc5449 | ||
|
|
d033463b34 | ||
|
|
740208cf74 | ||
|
|
0036c0b10e | ||
|
|
834c832390 | ||
|
|
5bc99dfd25 | ||
|
|
c92d2d064a | ||
|
|
a60c21323c | ||
|
|
34d6d6e709 | ||
|
|
f2ddafc718 | ||
|
|
267afdd15d | ||
|
|
48b7b82e33 | ||
|
|
84e5e5432e | ||
|
|
201e18eac2 | ||
|
|
3f3f0b1fec | ||
|
|
ca697c5038 | ||
|
|
5aeeb4e8b4 | ||
|
|
c285f9f587 | ||
|
|
d046608426 | ||
|
|
b91ed9cff5 | ||
|
|
185d85bfdd | ||
|
|
44b2c1464a | ||
|
|
a0762a0a6c | ||
|
|
2ad7660c09 | ||
|
|
d8b8c38182 | ||
|
|
1d50e5126a | ||
|
|
aa55e30358 | ||
|
|
f662de50db | ||
|
|
24c798ad3a | ||
|
|
0e304ae546 | ||
|
|
cd604cbfe7 | ||
|
|
b8e66d9df0 | ||
|
|
a2c738e57b | ||
|
|
ae16cd708c | ||
|
|
2ed0443f88 | ||
|
|
38f1c5075d | ||
|
|
55043a6348 | ||
|
|
1f6eb55b86 | ||
|
|
d9d8500484 | ||
|
|
0fca75c2db | ||
|
|
a7dcccbdf9 | ||
|
|
396eb5aec2 | ||
|
|
79d2076e09 | ||
|
|
693dca4ca2 | ||
|
|
4047076033 | ||
|
|
acb0b71f1b | ||
|
|
32d9352048 | ||
|
|
0246556f7c | ||
|
|
a17284681f | ||
|
|
adb66e3298 | ||
|
|
ad062d777d | ||
|
|
ffe1ff73a5 | ||
|
|
54f9202d74 | ||
|
|
ef3e173fb2 | ||
|
|
1aeec2ae51 | ||
|
|
1f50bfd801 | ||
|
|
d3466eabe5 | ||
|
|
8aff1af939 | ||
|
|
af35303432 | ||
|
|
0ef1a5a3ce | ||
|
|
e958bc8212 | ||
|
|
e0ca6e89d1 | ||
|
|
55d8ae124a | ||
|
|
5e28ec22e1 | ||
|
|
c3deb93489 | ||
|
|
a9aca94848 | ||
|
|
f3c06890dd | ||
|
|
d9d0e629fd | ||
|
|
17181405e3 | ||
|
|
c209564945 | ||
|
|
2da01db438 | ||
|
|
8c4913d411 | ||
|
|
e7ffc24844 | ||
|
|
259f23f6ee | ||
|
|
0de38b99c2 | ||
|
|
1044fb8574 | ||
|
|
e5bfa1bd6f | ||
|
|
a29b2a2ad9 | ||
|
|
b6899ce461 | ||
|
|
32c11af07c | ||
|
|
6ff55d24d0 | ||
|
|
055aacd7f6 | ||
|
|
5ecf58fd56 | ||
|
|
8a9106052f | ||
|
|
91264547c9 | ||
|
|
3190b877ae | ||
|
|
f8a8cc4676 | ||
|
|
93ee329315 | ||
|
|
b45163388d | ||
|
|
6029784f76 | ||
|
|
058ab55a6f | ||
|
|
1005d241b8 | ||
|
|
33b1ccba67 | ||
|
|
a5549fb500 | ||
|
|
b057ed3e77 | ||
|
|
1e88cc10e7 | ||
|
|
2f8634383e | ||
|
|
86f9e5ce96 | ||
|
|
9ae42d647c | ||
|
|
54d6217b93 | ||
|
|
150b1c2406 | ||
|
|
51b6f1b5f3 | ||
|
|
3eae14cef6 | ||
|
|
cc6dc1ca69 | ||
|
|
7f2361f58c | ||
|
|
7cb02d77ae | ||
|
|
52cc9b0cc0 | ||
|
|
d91bf61038 | ||
|
|
d5f81674f8 | ||
|
|
9381883835 | ||
|
|
f82e5a281d | ||
|
|
904e6241e4 | ||
|
|
ce39a3daf9 | ||
|
|
f2c7f74beb | ||
|
|
20db997fc2 | ||
|
|
7188e97444 | ||
|
|
6d528e741d | ||
|
|
d356e8370d | ||
|
|
5e336b5928 | ||
|
|
787ad0629b | ||
|
|
53e4adf24e | ||
|
|
6af811d63e | ||
|
|
359dab3380 | ||
|
|
97a8e6e965 | ||
|
|
8ea699aa08 | ||
|
|
7d924d2b0c | ||
|
|
3c85613ada | ||
|
|
c536d26db3 | ||
|
|
4350ff2692 | ||
|
|
0b9a1e7bb4 | ||
|
|
714ad18fa0 | ||
|
|
f81f785813 | ||
|
|
76c32af46f | ||
|
|
cd108263e1 | ||
|
|
093c47b59c | ||
|
|
56a40ec51a | ||
|
|
1337be2b84 | ||
|
|
eecd2c60f5 | ||
|
|
da071cb120 | ||
|
|
012cfa3cbe | ||
|
|
21180847dc | ||
|
|
9e9e538846 | ||
|
|
66025b1ae2 | ||
|
|
5999361358 | ||
|
|
e8699d1cb7 | ||
|
|
9292448e73 | ||
|
|
d7e156613d | ||
|
|
c3604aa66d | ||
|
|
5e037b1743 | ||
|
|
ebc79805ed | ||
|
|
c37e56e51d | ||
|
|
28a93c02e6 | ||
|
|
da16f9673e |
7
.dockerignore
Normal file
@@ -0,0 +1,7 @@
|
||||
.dockerignore
|
||||
Dockerfile
|
||||
vendor/
|
||||
cve.sqlite3*
|
||||
oval.sqlite3*
|
||||
setup/
|
||||
img/
|
||||
3
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: kotakanbe
|
||||
43
.github/ISSUE_TEMPLATE/BUG_REPORT.md
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
name: Bug Report
|
||||
labels: bug
|
||||
about: If something isn't working as expected.
|
||||
---
|
||||
|
||||
# What did you do? (required. The issue will be **closed** when not provided.)
|
||||
|
||||
|
||||
# What did you expect to happen?
|
||||
|
||||
|
||||
# What happened instead?
|
||||
|
||||
* Current Output
|
||||
|
||||
Please re-run the command using ```-debug``` and provide the output below.
|
||||
|
||||
# Steps to reproduce the behaviour
|
||||
|
||||
|
||||
# Configuration (**MUST** fill this out):
|
||||
|
||||
* Go version (`go version`):
|
||||
|
||||
* Go environment (`go env`):
|
||||
|
||||
* Vuls environment:
|
||||
|
||||
Hash : ____
|
||||
|
||||
To check the commit hash of HEAD
|
||||
$ vuls -v
|
||||
|
||||
or
|
||||
|
||||
$ cd $GOPATH/src/github.com/future-architect/vuls
|
||||
$ git rev-parse --short HEAD
|
||||
|
||||
* config.toml:
|
||||
|
||||
* command:
|
||||
|
||||
9
.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
name: Feature Request
|
||||
labels: enhancement
|
||||
about: I have a suggestion (and might want to implement myself)!
|
||||
---
|
||||
|
||||
<!--
|
||||
If this is a FEATURE REQUEST, request format does not matter!
|
||||
-->
|
||||
10
.github/ISSUE_TEMPLATE/SUPPORT_QUESTION.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Support Question
|
||||
labels: question
|
||||
about: If you have a question about Vuls.
|
||||
---
|
||||
|
||||
<!--
|
||||
If you have a trouble, feel free to ask.
|
||||
Make sure you're not asking duplicate question by searching on the issues lists.
|
||||
-->
|
||||
7
.github/ISSUE_TEMPLATE/VULSREPO.md
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
name: Vuls Repo
|
||||
labels: vulsrepo
|
||||
about: If something isn't working as expected.
|
||||
---
|
||||
|
||||
|
||||
40
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
|
||||
If this Pull Request is work in progress, Add a prefix of “[WIP]” in the title.
|
||||
|
||||
# What did you implement:
|
||||
|
||||
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context.
|
||||
|
||||
Fixes # (issue)
|
||||
|
||||
## Type of change
|
||||
|
||||
Please delete options that are not relevant.
|
||||
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] This change requires a documentation update
|
||||
|
||||
# How Has This Been Tested?
|
||||
|
||||
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce.
|
||||
|
||||
# Checklist:
|
||||
You don't have to satisfy all of the following.
|
||||
|
||||
- [ ] Write tests
|
||||
- [ ] Write documentation
|
||||
- [ ] Check that there aren't other open pull requests for the same issue/feature
|
||||
- [ ] Format your source code by `make fmt`
|
||||
- [ ] Pass the test by `make test`
|
||||
- [ ] Provide verification config / commands
|
||||
- [ ] Enable "Allow edits from maintainers" for this PR
|
||||
- [ ] Update the messages below
|
||||
|
||||
***Is this ready for review?:*** NO
|
||||
|
||||
# Reference
|
||||
|
||||
* https://blog.github.com/2015-01-21-how-to-write-the-perfect-pull-request/
|
||||
|
||||
28
.github/workflows/golangci.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
name: golangci-lint
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
jobs:
|
||||
golangci:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v1
|
||||
with:
|
||||
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
||||
version: v1.26
|
||||
|
||||
# Optional: working directory, useful for monorepos
|
||||
# working-directory: somedir
|
||||
|
||||
# Optional: golangci-lint command line arguments.
|
||||
# args: --issues-exit-code=0
|
||||
|
||||
# Optional: show only new issues if it's a pull request. The default value is `false`.
|
||||
# only-new-issues: true
|
||||
31
.github/workflows/goreleaser.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: goreleaser
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
goreleaser:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
-
|
||||
name: Unshallow
|
||||
run: git fetch --prune --unshallow
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.14
|
||||
-
|
||||
name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v2
|
||||
with:
|
||||
version: latest
|
||||
args: release --rm-dist
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
21
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
name: Test
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.14.x
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Test
|
||||
run: make test
|
||||
22
.github/workflows/tidy.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
name: go-mod-tidy-pr
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * 1" # Weekly build
|
||||
|
||||
jobs:
|
||||
go-mod-tidy-pr:
|
||||
name: go-mod-tidy-pr
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Run go-mod-tidy-pr
|
||||
uses: sue445/go-mod-tidy-pr@master
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
git_user_name: kotakanbe
|
||||
git_user_email: kotakanbe@gmail.com
|
||||
go_version: 1.14.x
|
||||
17
.gitignore
vendored
@@ -1,9 +1,18 @@
|
||||
vuls
|
||||
.vscode
|
||||
*.txt
|
||||
*.json
|
||||
*.sqlite3*
|
||||
*.db
|
||||
tags
|
||||
.gitmodules
|
||||
coverage.out
|
||||
issues/
|
||||
*.txt
|
||||
vendor/
|
||||
log/
|
||||
.gitmodules
|
||||
vuls
|
||||
*.sqlite3
|
||||
results/
|
||||
*config.toml
|
||||
!setup/docker/*
|
||||
.DS_Store
|
||||
dist/
|
||||
.idea
|
||||
|
||||
17
.golangci.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
name: golang-ci
|
||||
|
||||
linters-settings:
|
||||
errcheck:
|
||||
#exclude: /path/to/file.txt
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- goimports
|
||||
- golint
|
||||
- govet
|
||||
- misspell
|
||||
- errcheck
|
||||
- staticcheck
|
||||
- prealloc
|
||||
- ineffassign
|
||||
70
.goreleaser.yml
Normal file
@@ -0,0 +1,70 @@
|
||||
project_name: vuls
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
release:
|
||||
github:
|
||||
owner: future-architect
|
||||
name: vuls
|
||||
builds:
|
||||
- id: vuls
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
main: .
|
||||
flags:
|
||||
- -a
|
||||
ldflags: -s -w -X main.version={{.Version}} -X main.revision={{.Commit}}
|
||||
binary: vuls
|
||||
|
||||
- id: trivy-to-vuls
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
main: ./contrib/trivy/cmd/main.go
|
||||
binary: trivy-to-vuls
|
||||
|
||||
- id: future-vuls
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
main: ./contrib/future-vuls/cmd/main.go
|
||||
binary: future-vuls
|
||||
archives:
|
||||
|
||||
- id: vuls
|
||||
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
|
||||
builds:
|
||||
- vuls
|
||||
format: tar.gz
|
||||
files:
|
||||
- LICENSE
|
||||
- NOTICE
|
||||
- README*
|
||||
- CHANGELOG.md
|
||||
|
||||
- id: trivy-to-vuls
|
||||
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
|
||||
builds:
|
||||
- trivy-to-vuls
|
||||
format: tar.gz
|
||||
files:
|
||||
- LICENSE
|
||||
- NOTICE
|
||||
- README*
|
||||
- CHANGELOG.md
|
||||
- id: future-vuls
|
||||
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
|
||||
builds:
|
||||
|
||||
- future-vuls
|
||||
format: tar.gz
|
||||
files:
|
||||
- LICENSE
|
||||
- NOTICE
|
||||
- README*
|
||||
- CHANGELOG.md
|
||||
snapshot:
|
||||
name_template: SNAPSHOT-{{ .Commit }}
|
||||
482
CHANGELOG.md
@@ -1,5 +1,483 @@
|
||||
# Change Log
|
||||
|
||||
## v0.4.1 and later, see [GitHub release](https://github.com/future-architect/vuls/releases)
|
||||
|
||||
## [v0.4.0](https://github.com/future-architect/vuls/tree/v0.4.0) (2017-08-25)
|
||||
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.3.0...v0.4.0)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Output changelog in report, TUI and JSON for RHEL [\#367](https://github.com/future-architect/vuls/issues/367)
|
||||
- Output changelog in report, TUI and JSON for Amazon Linux [\#366](https://github.com/future-architect/vuls/issues/366)
|
||||
- Improve scanning accuracy by checking package versions [\#256](https://github.com/future-architect/vuls/issues/256)
|
||||
- Improve SSH [\#415](https://github.com/future-architect/vuls/issues/415)
|
||||
- Enable to scan even if target server can not connect to the Internet [\#258](https://github.com/future-architect/vuls/issues/258)
|
||||
- SSH Hostkey check [\#417](https://github.com/future-architect/vuls/pull/417) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- v0.4.0 [\#449](https://github.com/future-architect/vuls/pull/449) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Change default ssh method from go library to external command [\#416](https://github.com/future-architect/vuls/pull/416) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add containers-only option to configtest [\#411](https://github.com/future-architect/vuls/pull/411) ([knqyf263](https://github.com/knqyf263))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Running Vuls tui before vuls report does not show vulnerabilities checked by CPE [\#396](https://github.com/future-architect/vuls/issues/396)
|
||||
- With a long package name, Local shell mode \(stty dont' work\) [\#444](https://github.com/future-architect/vuls/issues/444)
|
||||
- Improve SSH [\#415](https://github.com/future-architect/vuls/issues/415)
|
||||
- Report that a vulnerability exists in the wrong package [\#408](https://github.com/future-architect/vuls/issues/408)
|
||||
- With a long package name, a parse error occurs. [\#391](https://github.com/future-architect/vuls/issues/391)
|
||||
- Ubuntu failed to scan vulnerable packages [\#205](https://github.com/future-architect/vuls/issues/205)
|
||||
- CVE-ID in changelog can't be picked up. [\#154](https://github.com/future-architect/vuls/issues/154)
|
||||
- v0.4.0 [\#449](https://github.com/future-architect/vuls/pull/449) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix SSH dial error [\#413](https://github.com/future-architect/vuls/pull/413) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Update deps, Change deps tool from glide to dep [\#412](https://github.com/future-architect/vuls/pull/412) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- fix report option Loaded error-info [\#406](https://github.com/future-architect/vuls/pull/406) ([hogehogehugahuga](https://github.com/hogehogehugahuga))
|
||||
- Add --user root to docker exec command [\#389](https://github.com/future-architect/vuls/pull/389) ([PaulFurtado](https://github.com/PaulFurtado))
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- README.md.ja not include "Oracle Linux, FreeBSD" [\#465](https://github.com/future-architect/vuls/issues/465)
|
||||
- Can't scan remote server - \(centos 7 - updated\) [\#451](https://github.com/future-architect/vuls/issues/451)
|
||||
- An abnormality in the result of vuls tui [\#439](https://github.com/future-architect/vuls/issues/439)
|
||||
- compile faild [\#436](https://github.com/future-architect/vuls/issues/436)
|
||||
- Can't install vuls on CentOS 7 [\#432](https://github.com/future-architect/vuls/issues/432)
|
||||
- Vuls scan doesn't show severity score in any of the vulnerable packages [\#430](https://github.com/future-architect/vuls/issues/430)
|
||||
- Load config failedtoml: cannot load TOML value of type string into a Go slice [\#429](https://github.com/future-architect/vuls/issues/429)
|
||||
- vuls scan not running check-update with sudo for Centos 7 [\#428](https://github.com/future-architect/vuls/issues/428)
|
||||
- options for configtest not being activated [\#422](https://github.com/future-architect/vuls/issues/422)
|
||||
- "could not find project Gopkg.toml, use dep init to initiate a manifest" when installing vuls [\#420](https://github.com/future-architect/vuls/issues/420)
|
||||
- go get not get [\#407](https://github.com/future-architect/vuls/issues/407)
|
||||
- Failed to scan via docker. err: Unknown format [\#404](https://github.com/future-architect/vuls/issues/404)
|
||||
- Failed to scan - kernel-xxx is an installed security update [\#403](https://github.com/future-architect/vuls/issues/403)
|
||||
- 169.254.169.254 port 80: Connection refused [\#402](https://github.com/future-architect/vuls/issues/402)
|
||||
- vuls scan --debug cause `invalid memory address` error [\#397](https://github.com/future-architect/vuls/issues/397)
|
||||
- Provide a command line flag that will automatically install aptitude on debian? [\#390](https://github.com/future-architect/vuls/issues/390)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- export fill cve info [\#467](https://github.com/future-architect/vuls/pull/467) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- add oval docker [\#466](https://github.com/future-architect/vuls/pull/466) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- fix typos in commands. [\#464](https://github.com/future-architect/vuls/pull/464) ([ymomoi](https://github.com/ymomoi))
|
||||
- Update README [\#463](https://github.com/future-architect/vuls/pull/463) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- export FillWithOval [\#462](https://github.com/future-architect/vuls/pull/462) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- add serveruuid field [\#458](https://github.com/future-architect/vuls/pull/458) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- add s3 dirctory option [\#457](https://github.com/future-architect/vuls/pull/457) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- Extract Advisory.Description on RHEL, Amazon, Oracle [\#450](https://github.com/future-architect/vuls/pull/450) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- nosudo on CentOS and Fetch Changelogs on Amazon, RHEL [\#448](https://github.com/future-architect/vuls/pull/448) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- change logrus package to lowercase and update other packages [\#446](https://github.com/future-architect/vuls/pull/446) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- add db backend redis [\#445](https://github.com/future-architect/vuls/pull/445) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- fast test [\#435](https://github.com/future-architect/vuls/pull/435) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- fix typo [\#433](https://github.com/future-architect/vuls/pull/433) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- Add support for PostgreSQL as a DB storage back-end [\#431](https://github.com/future-architect/vuls/pull/431) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- typo README.js.md [\#426](https://github.com/future-architect/vuls/pull/426) ([ryurock](https://github.com/ryurock))
|
||||
- Add TOC to README [\#425](https://github.com/future-architect/vuls/pull/425) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fixing \#420 where lock and manifest have moved to TOML [\#421](https://github.com/future-architect/vuls/pull/421) ([elfgoh](https://github.com/elfgoh))
|
||||
- Define timeout for vulnerabilities scan and platform detection [\#414](https://github.com/future-architect/vuls/pull/414) ([s7anley](https://github.com/s7anley))
|
||||
- Enable -timeout option when detecting OS [\#410](https://github.com/future-architect/vuls/pull/410) ([knqyf263](https://github.com/knqyf263))
|
||||
- Remove duplicate command in README [\#401](https://github.com/future-architect/vuls/pull/401) ([knqyf263](https://github.com/knqyf263))
|
||||
- Fix to read config.toml at tui [\#441](https://github.com/future-architect/vuls/pull/441) ([usiusi360](https://github.com/usiusi360))
|
||||
- Change NVD URL to new one [\#419](https://github.com/future-architect/vuls/pull/419) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add some testcases [\#418](https://github.com/future-architect/vuls/pull/418) ([kotakanbe](https://github.com/kotakanbe))
|
||||
|
||||
## [v0.3.0](https://github.com/future-architect/vuls/tree/v0.3.0) (2017-03-24)
|
||||
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.2.0...v0.3.0)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Changelog parsing fails when package maintainers aren't consistent regarding versions [\#327](https://github.com/future-architect/vuls/issues/327)
|
||||
- Docker scan doesn't report image name [\#325](https://github.com/future-architect/vuls/issues/325)
|
||||
- vuls report -to-email only one E-Mail [\#295](https://github.com/future-architect/vuls/issues/295)
|
||||
- Support RHEL5 [\#286](https://github.com/future-architect/vuls/issues/286)
|
||||
- Continue scanning even when some hosts have tech issues? [\#264](https://github.com/future-architect/vuls/issues/264)
|
||||
- Normalization of JSON output [\#259](https://github.com/future-architect/vuls/issues/259)
|
||||
- Add report subcommand, change scan subcommand options [\#239](https://github.com/future-architect/vuls/issues/239)
|
||||
- scan localhost? [\#210](https://github.com/future-architect/vuls/issues/210)
|
||||
- Can Vuls show details about updateable packages [\#341](https://github.com/future-architect/vuls/issues/341)
|
||||
- Scan all containers except [\#285](https://github.com/future-architect/vuls/issues/285)
|
||||
- Notify the difference from the previous scan result [\#255](https://github.com/future-architect/vuls/issues/255)
|
||||
- EC2RoleCreds support? [\#250](https://github.com/future-architect/vuls/issues/250)
|
||||
- Output confidence score of detection accuracy and detection method to JSON or Reporting [\#350](https://github.com/future-architect/vuls/pull/350) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Avoid null slice being null in JSON [\#345](https://github.com/future-architect/vuls/pull/345) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add -format-one-email option [\#331](https://github.com/future-architect/vuls/pull/331) ([knqyf263](https://github.com/knqyf263))
|
||||
- Support Raspbian [\#330](https://github.com/future-architect/vuls/pull/330) ([knqyf263](https://github.com/knqyf263))
|
||||
- Add leniancy to the version matching for debian to account for versio… [\#328](https://github.com/future-architect/vuls/pull/328) ([jsulinski](https://github.com/jsulinski))
|
||||
- Add image information for docker containers [\#326](https://github.com/future-architect/vuls/pull/326) ([jsulinski](https://github.com/jsulinski))
|
||||
- Continue scanning even when some hosts have tech issues [\#309](https://github.com/future-architect/vuls/pull/309) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add -log-dir option [\#301](https://github.com/future-architect/vuls/pull/301) ([knqyf263](https://github.com/knqyf263))
|
||||
- Use --assumeno option [\#300](https://github.com/future-architect/vuls/pull/300) ([knqyf263](https://github.com/knqyf263))
|
||||
- Add local scan mode\(Scan without SSH when target server is localhost\) [\#291](https://github.com/future-architect/vuls/pull/291) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Support RHEL5 [\#289](https://github.com/future-architect/vuls/pull/289) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add LXD support [\#288](https://github.com/future-architect/vuls/pull/288) ([jiazio](https://github.com/jiazio))
|
||||
- Add timeout option to configtest [\#400](https://github.com/future-architect/vuls/pull/400) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Notify the difference from the previous scan result [\#392](https://github.com/future-architect/vuls/pull/392) ([knqyf263](https://github.com/knqyf263))
|
||||
- Add Oracle Linux support [\#386](https://github.com/future-architect/vuls/pull/386) ([Djelibeybi](https://github.com/Djelibeybi))
|
||||
- Change container scan format in config.toml [\#381](https://github.com/future-architect/vuls/pull/381) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Obsolete CentOS5 support [\#378](https://github.com/future-architect/vuls/pull/378) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Deprecate prepare subcommand to minimize the root authority defined by /etc/sudoers [\#375](https://github.com/future-architect/vuls/pull/375) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Support IAM role for report to S3. [\#370](https://github.com/future-architect/vuls/pull/370) ([ohsawa0515](https://github.com/ohsawa0515))
|
||||
- Add .travis.yml [\#363](https://github.com/future-architect/vuls/pull/363) ([knqyf263](https://github.com/knqyf263))
|
||||
- Output changelog in report, TUI and JSON for Ubuntu/Debian/CentOS [\#356](https://github.com/future-architect/vuls/pull/356) ([kotakanbe](https://github.com/kotakanbe))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Debian scans failing in docker [\#323](https://github.com/future-architect/vuls/issues/323)
|
||||
- Local CVE DB is still checked, even if a CVE Dictionary URL is defined [\#316](https://github.com/future-architect/vuls/issues/316)
|
||||
- vuls needs gmake. [\#313](https://github.com/future-architect/vuls/issues/313)
|
||||
- patch request for FreeBSD [\#312](https://github.com/future-architect/vuls/issues/312)
|
||||
- Report: failed to read from json \(Docker\) [\#294](https://github.com/future-architect/vuls/issues/294)
|
||||
- -report-mail option does not output required mail header [\#282](https://github.com/future-architect/vuls/issues/282)
|
||||
- PackInfo not found error when vuls scan. [\#281](https://github.com/future-architect/vuls/issues/281)
|
||||
- Normalize character set [\#279](https://github.com/future-architect/vuls/issues/279)
|
||||
- The number of Updatable Packages is different from the number of yum check-update [\#373](https://github.com/future-architect/vuls/issues/373)
|
||||
- sudo is needed when exec yum check-update on RHEL7 [\#371](https://github.com/future-architect/vuls/issues/371)
|
||||
- `123-3ubuntu4` should be marked as ChangelogLenientMatch [\#362](https://github.com/future-architect/vuls/issues/362)
|
||||
- CentOS multi package invalid result [\#360](https://github.com/future-architect/vuls/issues/360)
|
||||
- Parse error after check-update. \(Unknown format\) [\#359](https://github.com/future-architect/vuls/issues/359)
|
||||
- Fix candidate to confidence. [\#354](https://github.com/future-architect/vuls/pull/354) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Bug fix: not send e-mail to cc address [\#346](https://github.com/future-architect/vuls/pull/346) ([knqyf263](https://github.com/knqyf263))
|
||||
- Change the command used for os detection from uname to freebsd-version [\#340](https://github.com/future-architect/vuls/pull/340) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix error handling of detectOS [\#337](https://github.com/future-architect/vuls/pull/337) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix infinite retry at size overrun error in Slack report [\#329](https://github.com/future-architect/vuls/pull/329) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- aptitude changelog defaults to using more, which is not interactive a… [\#324](https://github.com/future-architect/vuls/pull/324) ([jsulinski](https://github.com/jsulinski))
|
||||
- Do not use sudo when echo [\#322](https://github.com/future-architect/vuls/pull/322) ([knqyf263](https://github.com/knqyf263))
|
||||
- Reduce privilege requirements for commands that don't need sudo on Ubuntu/Debian [\#319](https://github.com/future-architect/vuls/pull/319) ([jsulinski](https://github.com/jsulinski))
|
||||
- Don't check for a CVE DB when CVE Dictionary URL is defined [\#317](https://github.com/future-architect/vuls/pull/317) ([jsulinski](https://github.com/jsulinski))
|
||||
- Fix typo contianer -\> container [\#314](https://github.com/future-architect/vuls/pull/314) ([justyns](https://github.com/justyns))
|
||||
- Fix the changelog cache logic for ubuntu/debian [\#305](https://github.com/future-architect/vuls/pull/305) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix yum updateinfo options [\#304](https://github.com/future-architect/vuls/pull/304) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Update glide.lock to fix create-log-dir error. [\#303](https://github.com/future-architect/vuls/pull/303) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix a bug in logging \(file output\) at scan command [\#302](https://github.com/future-architect/vuls/pull/302) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add -pipe flag \#294 [\#299](https://github.com/future-architect/vuls/pull/299) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix RHEL5 scan stopped halfway [\#293](https://github.com/future-architect/vuls/pull/293) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix amazon linux scan stopped halfway [\#292](https://github.com/future-architect/vuls/pull/292) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix nil-ponter in TUI [\#388](https://github.com/future-architect/vuls/pull/388) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix Bug of Mysql Backend [\#384](https://github.com/future-architect/vuls/pull/384) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix scan confidence on Ubuntu/Debian/Raspbian \#362 [\#379](https://github.com/future-architect/vuls/pull/379) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix updatalbe packages count \#373 [\#374](https://github.com/future-architect/vuls/pull/374) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- sudo yum check-update on RHEL [\#372](https://github.com/future-architect/vuls/pull/372) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Change ssh option from -t to -tt [\#369](https://github.com/future-architect/vuls/pull/369) ([knqyf263](https://github.com/knqyf263))
|
||||
- Increase the width of RequestPty [\#364](https://github.com/future-architect/vuls/pull/364) ([knqyf263](https://github.com/knqyf263))
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- vuls configtest --debugがsudoのチェックで止まってしまう [\#395](https://github.com/future-architect/vuls/issues/395)
|
||||
- Add support for Oracle Linux [\#385](https://github.com/future-architect/vuls/issues/385)
|
||||
- error on install - Ubuntu 16.04 [\#376](https://github.com/future-architect/vuls/issues/376)
|
||||
- Unknown OS Type [\#335](https://github.com/future-architect/vuls/issues/335)
|
||||
- mac os 10.12.3 make install error [\#334](https://github.com/future-architect/vuls/issues/334)
|
||||
- assumeYes doesn't work because there is no else condition [\#320](https://github.com/future-architect/vuls/issues/320)
|
||||
- Debian scan uses sudo where unnecessary [\#318](https://github.com/future-architect/vuls/issues/318)
|
||||
- Add FreeBSD 11 to supported OS on documents. [\#311](https://github.com/future-architect/vuls/issues/311)
|
||||
- docker fetchnvd failing [\#274](https://github.com/future-architect/vuls/issues/274)
|
||||
- Latest version of labstack echo breaks installation [\#268](https://github.com/future-architect/vuls/issues/268)
|
||||
- fetchnvd Fails using example loop [\#267](https://github.com/future-architect/vuls/issues/267)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- fix typo in README.ja.md [\#394](https://github.com/future-architect/vuls/pull/394) ([lv7777](https://github.com/lv7777))
|
||||
- Update Tutorial in README [\#387](https://github.com/future-architect/vuls/pull/387) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix README [\#383](https://github.com/future-architect/vuls/pull/383) ([usiusi360](https://github.com/usiusi360))
|
||||
- s/dictinary/dictionary typo [\#382](https://github.com/future-architect/vuls/pull/382) ([beuno](https://github.com/beuno))
|
||||
- Fix Japanese typo [\#377](https://github.com/future-architect/vuls/pull/377) ([IMAI-Yuji](https://github.com/IMAI-Yuji))
|
||||
- Improve kanji character [\#351](https://github.com/future-architect/vuls/pull/351) ([hasegawa-tomoki](https://github.com/hasegawa-tomoki))
|
||||
- Add PULL\_REQUEST\_TEMPLATE.md [\#348](https://github.com/future-architect/vuls/pull/348) ([knqyf263](https://github.com/knqyf263))
|
||||
- Update README [\#347](https://github.com/future-architect/vuls/pull/347) ([knqyf263](https://github.com/knqyf263))
|
||||
- Fix test case [\#344](https://github.com/future-architect/vuls/pull/344) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix typo [\#343](https://github.com/future-architect/vuls/pull/343) ([knqyf263](https://github.com/knqyf263))
|
||||
- Rename Makefile to GNUmakefile \#313 [\#339](https://github.com/future-architect/vuls/pull/339) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Update README [\#338](https://github.com/future-architect/vuls/pull/338) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- add error handling [\#332](https://github.com/future-architect/vuls/pull/332) ([kazuminn](https://github.com/kazuminn))
|
||||
- Update readme [\#308](https://github.com/future-architect/vuls/pull/308) ([lapthorn](https://github.com/lapthorn))
|
||||
- Update glide.lock to fix import error [\#306](https://github.com/future-architect/vuls/pull/306) ([knqyf263](https://github.com/knqyf263))
|
||||
- Check whether echo is executable with nopasswd [\#298](https://github.com/future-architect/vuls/pull/298) ([knqyf263](https://github.com/knqyf263))
|
||||
- Update docker README [\#297](https://github.com/future-architect/vuls/pull/297) ([knqyf263](https://github.com/knqyf263))
|
||||
- update readme [\#296](https://github.com/future-architect/vuls/pull/296) ([galigalikun](https://github.com/galigalikun))
|
||||
- remove unused import line. [\#358](https://github.com/future-architect/vuls/pull/358) ([ymomoi](https://github.com/ymomoi))
|
||||
|
||||
## [v0.2.0](https://github.com/future-architect/vuls/tree/v0.2.0) (2017-01-10)
|
||||
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.7...v0.2.0)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Add report subcommand, change scan options. \#239 [\#270](https://github.com/future-architect/vuls/pull/270) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add --assume-yes to prepare \#260 [\#266](https://github.com/future-architect/vuls/pull/266) ([Code0x58](https://github.com/Code0x58))
|
||||
- Use RFC3339 timestamps in the results [\#265](https://github.com/future-architect/vuls/pull/265) ([Code0x58](https://github.com/Code0x58))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- vuls prepare failed to centos7 [\#275](https://github.com/future-architect/vuls/issues/275)
|
||||
- Failed to scan on RHEL5 [\#94](https://github.com/future-architect/vuls/issues/94)
|
||||
- Fix container os detection [\#287](https://github.com/future-architect/vuls/pull/287) ([jiazio](https://github.com/jiazio))
|
||||
- Add date header to report mail. [\#283](https://github.com/future-architect/vuls/pull/283) ([ymomoi](https://github.com/ymomoi))
|
||||
- Add Content-Type header to report/mail.go . [\#280](https://github.com/future-architect/vuls/pull/280) ([hogehogehugahuga](https://github.com/hogehogehugahuga))
|
||||
- Keep output of "vuls scan -report-\*" to be same every times [\#272](https://github.com/future-architect/vuls/pull/272) ([yoheimuta](https://github.com/yoheimuta))
|
||||
- Fix JSON-dir regex pattern \#265 [\#271](https://github.com/future-architect/vuls/pull/271) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Stop quietly ignoring `--ssh-external` on Windows [\#263](https://github.com/future-architect/vuls/pull/263) ([Code0x58](https://github.com/Code0x58))
|
||||
- Fix non-interactive `apt-get install` \#251 [\#253](https://github.com/future-architect/vuls/pull/253) ([Code0x58](https://github.com/Code0x58))
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- gocui.NewGui now takes a parameter [\#261](https://github.com/future-architect/vuls/issues/261)
|
||||
- Add a `--yes` flag to bypass interactive prompt for `vuls prepare` [\#260](https://github.com/future-architect/vuls/issues/260)
|
||||
- `vuls prepare` doesn't work on Debian host due to apt-get confirmation prompt [\#251](https://github.com/future-architect/vuls/issues/251)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Fix gocui.NewGui after signature change \#261 [\#262](https://github.com/future-architect/vuls/pull/262) ([Code0x58](https://github.com/Code0x58))
|
||||
- Replace inconsistent tabs with spaces [\#254](https://github.com/future-architect/vuls/pull/254) ([Code0x58](https://github.com/Code0x58))
|
||||
- Fix README [\#249](https://github.com/future-architect/vuls/pull/249) ([usiusi360](https://github.com/usiusi360))
|
||||
|
||||
## [v0.1.7](https://github.com/future-architect/vuls/tree/v0.1.7) (2016-11-08)
|
||||
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.6...v0.1.7)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Enable to scan only docker container, without docker host [\#122](https://github.com/future-architect/vuls/issues/122)
|
||||
- Add -skip-broken option \[CentOS only\] \#245 [\#248](https://github.com/future-architect/vuls/pull/248) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Display unknown CVEs to TUI [\#244](https://github.com/future-architect/vuls/pull/244) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add the XML output [\#240](https://github.com/future-architect/vuls/pull/240) ([gleentea](https://github.com/gleentea))
|
||||
- add '-ssh-external' option to prepare subcommand [\#234](https://github.com/future-architect/vuls/pull/234) ([mykstmhr](https://github.com/mykstmhr))
|
||||
- Integrate OWASP Dependency Check [\#232](https://github.com/future-architect/vuls/pull/232) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add support for reading CVE data from MySQL. [\#225](https://github.com/future-architect/vuls/pull/225) ([oswell](https://github.com/oswell))
|
||||
- Remove base docker image, -v shows commit hash [\#223](https://github.com/future-architect/vuls/pull/223) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- Support ignore CveIDs in config [\#222](https://github.com/future-architect/vuls/pull/222) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Confirm before installing dependencies on prepare [\#219](https://github.com/future-architect/vuls/pull/219) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Remove all.json [\#218](https://github.com/future-architect/vuls/pull/218) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add GitHub issue template [\#217](https://github.com/future-architect/vuls/pull/217) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Improve makefile, -version shows git hash, fix README [\#216](https://github.com/future-architect/vuls/pull/216) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- change e-mail package from gomail to net/smtp [\#211](https://github.com/future-architect/vuls/pull/211) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- Add only-containers option to scan subcommand \#122 [\#190](https://github.com/future-architect/vuls/pull/190) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix -results-dir option of scan subcommand [\#185](https://github.com/future-architect/vuls/pull/185) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Show error when no scannable servers are detected. [\#177](https://github.com/future-architect/vuls/pull/177) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add sudo check to prepare subcommand [\#176](https://github.com/future-architect/vuls/pull/176) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Supports yum --enablerepo option \(supports only base,updates for now\) [\#147](https://github.com/future-architect/vuls/pull/147) ([kotakanbe](https://github.com/kotakanbe))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Debian 8.6 \(jessie\) scan does not show vulnerable packages [\#235](https://github.com/future-architect/vuls/issues/235)
|
||||
- panic: runtime error: index out of range - ubuntu 16.04 + vuls history [\#180](https://github.com/future-architect/vuls/issues/180)
|
||||
- Moved golang.org/x/net/context to context [\#243](https://github.com/future-architect/vuls/pull/243) ([yoheimuta](https://github.com/yoheimuta))
|
||||
- Fix changelog cache bug on Ubuntu and Debian \#235 [\#238](https://github.com/future-architect/vuls/pull/238) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- add '-ssh-external' option to prepare subcommand [\#234](https://github.com/future-architect/vuls/pull/234) ([mykstmhr](https://github.com/mykstmhr))
|
||||
- Fixed error for the latest version of gocui [\#231](https://github.com/future-architect/vuls/pull/231) ([ymd38](https://github.com/ymd38))
|
||||
- Handle the refactored gocui SetCurrentView method. [\#229](https://github.com/future-architect/vuls/pull/229) ([oswell](https://github.com/oswell))
|
||||
- Fix locale env var LANG to LANGUAGE [\#215](https://github.com/future-architect/vuls/pull/215) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fixed bug with parsing update line on CentOS/RHEL [\#206](https://github.com/future-architect/vuls/pull/206) ([andyone](https://github.com/andyone))
|
||||
- Fix defer cache.DB.close [\#201](https://github.com/future-architect/vuls/pull/201) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix a help message of -report-azure-blob option [\#195](https://github.com/future-architect/vuls/pull/195) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix error handling in tui [\#193](https://github.com/future-architect/vuls/pull/193) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix not working changelog cache on Container [\#189](https://github.com/future-architect/vuls/pull/189) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix release version detection on FreeBSD [\#184](https://github.com/future-architect/vuls/pull/184) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix defer cahce.DB.close\(\) [\#183](https://github.com/future-architect/vuls/pull/183) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix a mode of files/dir \(report, log\) [\#182](https://github.com/future-architect/vuls/pull/182) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix a error when no json dirs are found under results \#180 [\#181](https://github.com/future-architect/vuls/pull/181) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- ssh-external option of configtest is not working \#178 [\#179](https://github.com/future-architect/vuls/pull/179) ([kotakanbe](https://github.com/kotakanbe))
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- --enable-repos of yum option [\#246](https://github.com/future-architect/vuls/issues/246)
|
||||
- --skip-broken at yum option [\#245](https://github.com/future-architect/vuls/issues/245)
|
||||
- Recent changes to gobui cause build failures [\#228](https://github.com/future-architect/vuls/issues/228)
|
||||
- https://hub.docker.com/r/vuls/go-cve-dictionary/ is empty [\#208](https://github.com/future-architect/vuls/issues/208)
|
||||
- Not able to install gomail fails [\#202](https://github.com/future-architect/vuls/issues/202)
|
||||
- No results file created - vuls tui failed [\#199](https://github.com/future-architect/vuls/issues/199)
|
||||
- Wrong file permissions for results/\*.json in official Docker container [\#197](https://github.com/future-architect/vuls/issues/197)
|
||||
- Failed: Unknown OS Type [\#196](https://github.com/future-architect/vuls/issues/196)
|
||||
- Segmentation fault with configtest [\#192](https://github.com/future-architect/vuls/issues/192)
|
||||
- Failed to scan. err: No server defined. Check the configuration [\#187](https://github.com/future-architect/vuls/issues/187)
|
||||
- vuls configtest -ssh-external doesnt work [\#178](https://github.com/future-architect/vuls/issues/178)
|
||||
- apt-get update: time out [\#175](https://github.com/future-architect/vuls/issues/175)
|
||||
- scanning on Centos6, but vuls recognizes debian. [\#174](https://github.com/future-architect/vuls/issues/174)
|
||||
- Fix READMEja \#164 [\#173](https://github.com/future-architect/vuls/issues/173)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Update README \#225 [\#242](https://github.com/future-architect/vuls/pull/242) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- fix readme [\#241](https://github.com/future-architect/vuls/pull/241) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- Fix README \#234 [\#237](https://github.com/future-architect/vuls/pull/237) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Update glide files [\#236](https://github.com/future-architect/vuls/pull/236) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- fix README [\#226](https://github.com/future-architect/vuls/pull/226) ([usiusi360](https://github.com/usiusi360))
|
||||
- fix some misspelling. [\#221](https://github.com/future-architect/vuls/pull/221) ([ymomoi](https://github.com/ymomoi))
|
||||
- fix docker readme [\#214](https://github.com/future-architect/vuls/pull/214) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- Fix ja document about typo [\#213](https://github.com/future-architect/vuls/pull/213) ([shokohara](https://github.com/shokohara))
|
||||
- fix readme [\#212](https://github.com/future-architect/vuls/pull/212) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- fix README [\#207](https://github.com/future-architect/vuls/pull/207) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- fix typo [\#204](https://github.com/future-architect/vuls/pull/204) ([usiusi360](https://github.com/usiusi360))
|
||||
- fix gitignore [\#191](https://github.com/future-architect/vuls/pull/191) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- Update glide.lock [\#188](https://github.com/future-architect/vuls/pull/188) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix path in setup/docker/README [\#186](https://github.com/future-architect/vuls/pull/186) ([dladuke](https://github.com/dladuke))
|
||||
- Vuls and vulsrepo are now separated [\#163](https://github.com/future-architect/vuls/pull/163) ([hikachan](https://github.com/hikachan))
|
||||
|
||||
## [v0.1.6](https://github.com/future-architect/vuls/tree/v0.1.6) (2016-09-12)
|
||||
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.5...v0.1.6)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- High speed scan on Ubuntu/Debian [\#172](https://github.com/future-architect/vuls/pull/172) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Support CWE\(Common Weakness Enumeration\) [\#169](https://github.com/future-architect/vuls/pull/169) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Enable to scan without sudo on amazon linux [\#167](https://github.com/future-architect/vuls/pull/167) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Remove deprecated options -use-unattended-upgrades,-use-yum-plugin-security [\#161](https://github.com/future-architect/vuls/pull/161) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- delete sqlite3 [\#152](https://github.com/future-architect/vuls/pull/152) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Failed to setup vuls docker [\#170](https://github.com/future-architect/vuls/issues/170)
|
||||
- yum check-update error occurred when no reboot after kernel updating [\#165](https://github.com/future-architect/vuls/issues/165)
|
||||
- error thrown from 'docker build .' [\#157](https://github.com/future-architect/vuls/issues/157)
|
||||
- CVE-ID is truncated to 4 digits [\#153](https://github.com/future-architect/vuls/issues/153)
|
||||
- 'yum update --changelog' stalled in 'vuls scan'. if ssh user is not 'root'. [\#150](https://github.com/future-architect/vuls/issues/150)
|
||||
- Panic on packet scan [\#131](https://github.com/future-architect/vuls/issues/131)
|
||||
- Update glide.lock \#170 [\#171](https://github.com/future-architect/vuls/pull/171) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix detecting a platform on Azure [\#168](https://github.com/future-architect/vuls/pull/168) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix parse error for yum check-update \#165 [\#166](https://github.com/future-architect/vuls/pull/166) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix bug: Vuls on Docker [\#159](https://github.com/future-architect/vuls/pull/159) ([tjinjin](https://github.com/tjinjin))
|
||||
- Fix CVE-ID is truncated to 4 digits [\#155](https://github.com/future-architect/vuls/pull/155) ([usiusi360](https://github.com/usiusi360))
|
||||
- Fix yum update --changelog stalled when non-root ssh user on CentOS \#150 [\#151](https://github.com/future-architect/vuls/pull/151) ([kotakanbe](https://github.com/kotakanbe))
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Support su for root privilege escalation [\#44](https://github.com/future-architect/vuls/issues/44)
|
||||
- Support FreeBSD [\#34](https://github.com/future-architect/vuls/issues/34)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Change scripts for data fetching from jvn [\#164](https://github.com/future-architect/vuls/pull/164) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix: setup vulsrepo [\#162](https://github.com/future-architect/vuls/pull/162) ([tjinjin](https://github.com/tjinjin))
|
||||
- Fix-docker-vulsrepo-install [\#160](https://github.com/future-architect/vuls/pull/160) ([usiusi360](https://github.com/usiusi360))
|
||||
- Reduce regular expression compilation [\#158](https://github.com/future-architect/vuls/pull/158) ([itchyny](https://github.com/itchyny))
|
||||
- Add testcases for \#153 [\#156](https://github.com/future-architect/vuls/pull/156) ([kotakanbe](https://github.com/kotakanbe))
|
||||
|
||||
## [v0.1.5](https://github.com/future-architect/vuls/tree/v0.1.5) (2016-08-16)
|
||||
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.4...v0.1.5)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Enable to scan without running go-cve-dictionary as server mode [\#84](https://github.com/future-architect/vuls/issues/84)
|
||||
- Support high-speed scanning for CentOS [\#138](https://github.com/future-architect/vuls/pull/138) ([tai-ga](https://github.com/tai-ga))
|
||||
- Add configtest subcommand. skip un-ssh-able servers. [\#134](https://github.com/future-architect/vuls/pull/134) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Support -report-azure-blob option [\#130](https://github.com/future-architect/vuls/pull/130) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add optional key-values that will be outputted to JSON in config [\#117](https://github.com/future-architect/vuls/pull/117) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Change dir structure [\#115](https://github.com/future-architect/vuls/pull/115) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add some validation of loading config. user, host and port [\#113](https://github.com/future-architect/vuls/pull/113) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Support scanning with external ssh command [\#101](https://github.com/future-architect/vuls/pull/101) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Detect Platform and get instance-id of amazon ec2 [\#95](https://github.com/future-architect/vuls/pull/95) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add -report-s3 option [\#92](https://github.com/future-architect/vuls/pull/92) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Added FreeBSD support. [\#90](https://github.com/future-architect/vuls/pull/90) ([justyntemme](https://github.com/justyntemme))
|
||||
- Add glide files for vendoring [\#89](https://github.com/future-architect/vuls/pull/89) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix README, change -cvedbpath to -cve-dictionary-dbpath \#84 [\#85](https://github.com/future-architect/vuls/pull/85) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add option for it get cve detail from cve.sqlite3. [\#81](https://github.com/future-architect/vuls/pull/81) ([ymd38](https://github.com/ymd38))
|
||||
- Add -report-text option, Fix small bug of report in japanese [\#78](https://github.com/future-architect/vuls/pull/78) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add JSONWriter, Fix CVE sort order of report [\#77](https://github.com/future-architect/vuls/pull/77) ([kotakanbe](https://github.com/kotakanbe))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Docker: Panic [\#76](https://github.com/future-architect/vuls/issues/76)
|
||||
- Fix apt command to scan correctly when system locale is not english [\#149](https://github.com/future-architect/vuls/pull/149) ([kit494way](https://github.com/kit494way))
|
||||
- Disable -ask-sudo-password for security reasons [\#148](https://github.com/future-architect/vuls/pull/148) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix no tty error while executing with -external-ssh option [\#143](https://github.com/future-architect/vuls/pull/143) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- wrong log packages [\#141](https://github.com/future-architect/vuls/pull/141) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- Fix platform detection. [\#137](https://github.com/future-architect/vuls/pull/137) ([Rompei](https://github.com/Rompei))
|
||||
- Fix nil pointer when scan with -cve-dictionary-dbpath and cpeNames [\#111](https://github.com/future-architect/vuls/pull/111) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Remove vulndb file before pkg audit [\#110](https://github.com/future-architect/vuls/pull/110) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add error handling when unable to connect via ssh. status code: 255 [\#108](https://github.com/future-architect/vuls/pull/108) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Enable to detect vulnerabilities on FreeBSD [\#98](https://github.com/future-architect/vuls/pull/98) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix unknown format err while check-update on RHEL6.5 [\#93](https://github.com/future-architect/vuls/pull/93) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- Fix type of SMTP Port of discovery command's output [\#88](https://github.com/future-architect/vuls/pull/88) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix error msg when go-cve-dictionary is unavailable \#84 [\#86](https://github.com/future-architect/vuls/pull/86) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix error handling to avoid nil pointer err on debian [\#83](https://github.com/future-architect/vuls/pull/83) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix nil pointer while doing apt-cache policy on ubuntu \#76 [\#82](https://github.com/future-architect/vuls/pull/82) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- fix log import url [\#79](https://github.com/future-architect/vuls/pull/79) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- Fix error handling of gorequest [\#75](https://github.com/future-architect/vuls/pull/75) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix freezing forever when no args specified in TUI mode [\#73](https://github.com/future-architect/vuls/pull/73) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- mv version.go version/version.go to run main.go without compile [\#71](https://github.com/future-architect/vuls/pull/71) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- SSh password authentication failed on FreeBSD [\#99](https://github.com/future-architect/vuls/issues/99)
|
||||
- BUG: -o pipefail is not work on FreeBSD's /bin/sh. because it isn't bash [\#91](https://github.com/future-architect/vuls/issues/91)
|
||||
- Use ~/.ssh/config [\#62](https://github.com/future-architect/vuls/issues/62)
|
||||
- SSH ciphers [\#37](https://github.com/future-architect/vuls/issues/37)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Update README \#138 [\#144](https://github.com/future-architect/vuls/pull/144) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix a typo [\#142](https://github.com/future-architect/vuls/pull/142) ([dtan4](https://github.com/dtan4))
|
||||
- Remove unnecessary step in readme of docker setup [\#140](https://github.com/future-architect/vuls/pull/140) ([mikkame](https://github.com/mikkame))
|
||||
- Update logo [\#139](https://github.com/future-architect/vuls/pull/139) ([chanomaru](https://github.com/chanomaru))
|
||||
- Update README.ja.md to fix wrong tips. [\#135](https://github.com/future-architect/vuls/pull/135) ([a2atsu](https://github.com/a2atsu))
|
||||
- add tips about NVD JVN issue [\#133](https://github.com/future-architect/vuls/pull/133) ([a2atsu](https://github.com/a2atsu))
|
||||
- Fix README wrong links [\#129](https://github.com/future-architect/vuls/pull/129) ([aomoriringo](https://github.com/aomoriringo))
|
||||
- Add logo [\#126](https://github.com/future-architect/vuls/pull/126) ([chanomaru](https://github.com/chanomaru))
|
||||
- Improve setup/docker [\#125](https://github.com/future-architect/vuls/pull/125) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix scan command help [\#124](https://github.com/future-architect/vuls/pull/124) ([aomoriringo](https://github.com/aomoriringo))
|
||||
- added dockernized-vuls with vulsrepo [\#121](https://github.com/future-architect/vuls/pull/121) ([hikachan](https://github.com/hikachan))
|
||||
- Fix detect platform on azure and degital ocean [\#119](https://github.com/future-architect/vuls/pull/119) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Remove json marshall-indent [\#118](https://github.com/future-architect/vuls/pull/118) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Improve Readme.ja [\#116](https://github.com/future-architect/vuls/pull/116) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add architecture diag to README.md [\#114](https://github.com/future-architect/vuls/pull/114) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Rename linux.go to base.go [\#100](https://github.com/future-architect/vuls/pull/100) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Update README.md [\#74](https://github.com/future-architect/vuls/pull/74) ([yoshi-taka](https://github.com/yoshi-taka))
|
||||
- Refactoring debian.go [\#72](https://github.com/future-architect/vuls/pull/72) ([kotakanbe](https://github.com/kotakanbe))
|
||||
|
||||
## [v0.1.4](https://github.com/future-architect/vuls/tree/v0.1.4) (2016-05-24)
|
||||
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.3...v0.1.4)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Initial fetch from NVD is too heavy \(2.3 GB of memory consumed\) [\#27](https://github.com/future-architect/vuls/issues/27)
|
||||
- Enable to show previous scan result [\#69](https://github.com/future-architect/vuls/pull/69) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add ignore-unscored-cves option [\#68](https://github.com/future-architect/vuls/pull/68) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Support dynamic scanning docker container [\#67](https://github.com/future-architect/vuls/pull/67) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add version flag [\#65](https://github.com/future-architect/vuls/pull/65) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Update Dockerfile [\#57](https://github.com/future-architect/vuls/pull/57) ([theonlydoo](https://github.com/theonlydoo))
|
||||
- Update run.sh [\#56](https://github.com/future-architect/vuls/pull/56) ([theonlydoo](https://github.com/theonlydoo))
|
||||
- Support Windows [\#33](https://github.com/future-architect/vuls/pull/33) ([mattn](https://github.com/mattn))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- vuls scan -cvss-over does not work. [\#59](https://github.com/future-architect/vuls/issues/59)
|
||||
- `panic: runtime error: invalid memory address or nil pointer dereference` when scan CentOS5.5 [\#58](https://github.com/future-architect/vuls/issues/58)
|
||||
- It rans out of memory. [\#47](https://github.com/future-architect/vuls/issues/47)
|
||||
- BUG: vuls scan on CentOS with Japanese environment. [\#43](https://github.com/future-architect/vuls/issues/43)
|
||||
- yum --color=never [\#36](https://github.com/future-architect/vuls/issues/36)
|
||||
- Failed to parse yum check-update [\#32](https://github.com/future-architect/vuls/issues/32)
|
||||
- Pointless sudo [\#29](https://github.com/future-architect/vuls/issues/29)
|
||||
- Can't init database in a path having blanks [\#26](https://github.com/future-architect/vuls/issues/26)
|
||||
- Fix pointless sudo in debian.go \#29 [\#66](https://github.com/future-architect/vuls/pull/66) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix error handling of httpGet in cve-client \#58 [\#64](https://github.com/future-architect/vuls/pull/64) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix nil pointer at error handling of cve\_client \#58 [\#63](https://github.com/future-architect/vuls/pull/63) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Set language en\_US. [\#61](https://github.com/future-architect/vuls/pull/61) ([pabroff](https://github.com/pabroff))
|
||||
- Fix -cvss-over flag \#59 [\#60](https://github.com/future-architect/vuls/pull/60) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix scan on Japanese environment. [\#55](https://github.com/future-architect/vuls/pull/55) ([pabroff](https://github.com/pabroff))
|
||||
- Fix a typo: replace Depricated by Deprecated. [\#54](https://github.com/future-architect/vuls/pull/54) ([jody-frankowski](https://github.com/jody-frankowski))
|
||||
- Fix yes no infinite loop while doing yum update --changelog on root@CentOS \#47 [\#50](https://github.com/future-architect/vuls/pull/50) ([pabroff](https://github.com/pabroff))
|
||||
- Fix $servername in output of discover command [\#45](https://github.com/future-architect/vuls/pull/45) ([kotakanbe](https://github.com/kotakanbe))
|
||||
|
||||
## [v0.1.3](https://github.com/future-architect/vuls/tree/v0.1.3) (2016-04-21)
|
||||
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.2...v0.1.3)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Add sudo support for prepare [\#11](https://github.com/future-architect/vuls/issues/11)
|
||||
- Dockerfile? [\#10](https://github.com/future-architect/vuls/issues/10)
|
||||
- Update README [\#41](https://github.com/future-architect/vuls/pull/41) ([theonlydoo](https://github.com/theonlydoo))
|
||||
- Sparse dockerization [\#38](https://github.com/future-architect/vuls/pull/38) ([theonlydoo](https://github.com/theonlydoo))
|
||||
- No password in config [\#35](https://github.com/future-architect/vuls/pull/35) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fr readme translation [\#23](https://github.com/future-architect/vuls/pull/23) ([novakin](https://github.com/novakin))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Issues updating CVE database behind https proxy [\#39](https://github.com/future-architect/vuls/issues/39)
|
||||
- Vuls failed to parse yum check-update [\#24](https://github.com/future-architect/vuls/issues/24)
|
||||
- Fix yum to yum --color=never \#36 [\#42](https://github.com/future-architect/vuls/pull/42) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix parse yum check update [\#40](https://github.com/future-architect/vuls/pull/40) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- fix typo [\#31](https://github.com/future-architect/vuls/pull/31) ([blue119](https://github.com/blue119))
|
||||
- Fix error while parsing yum check-update \#24 [\#30](https://github.com/future-architect/vuls/pull/30) ([kotakanbe](https://github.com/kotakanbe))
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Unable to scan on ubuntu because changelog.ubuntu.com is down... [\#21](https://github.com/future-architect/vuls/issues/21)
|
||||
- err: Not initialize\(d\) yet.. [\#16](https://github.com/future-architect/vuls/issues/16)
|
||||
- Errors when using fish shell [\#8](https://github.com/future-architect/vuls/issues/8)
|
||||
|
||||
## [v0.1.2](https://github.com/future-architect/vuls/tree/v0.1.2) (2016-04-12)
|
||||
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.1...v0.1.2)
|
||||
|
||||
@@ -8,7 +486,7 @@
|
||||
- Maximum 6 nodes available to scan [\#12](https://github.com/future-architect/vuls/issues/12)
|
||||
- panic: runtime error: index out of range [\#5](https://github.com/future-architect/vuls/issues/5)
|
||||
- Fix sudo option on RedHat like Linux and change some messages. [\#20](https://github.com/future-architect/vuls/pull/20) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Typo fix and updated readme [\#19](https://github.com/future-architect/vuls/pull/19) ([Euan-Kerr](https://github.com/Euan-Kerr))
|
||||
- Typo fix and updated readme [\#19](https://github.com/future-architect/vuls/pull/19) ([EuanKerr](https://github.com/EuanKerr))
|
||||
- remove a period at the end of error messages. [\#18](https://github.com/future-architect/vuls/pull/18) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- fix error while yum updateinfo --security update on rhel@aws [\#17](https://github.com/future-architect/vuls/pull/17) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fixed typos [\#15](https://github.com/future-architect/vuls/pull/15) ([radarhere](https://github.com/radarhere))
|
||||
@@ -33,4 +511,4 @@
|
||||
|
||||
|
||||
|
||||
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
|
||||
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
|
||||
|
||||
34
Dockerfile
Normal file
@@ -0,0 +1,34 @@
|
||||
FROM golang:alpine as builder
|
||||
|
||||
RUN apk add --no-cache \
|
||||
git \
|
||||
make \
|
||||
gcc \
|
||||
musl-dev
|
||||
|
||||
ENV REPOSITORY github.com/future-architect/vuls
|
||||
COPY . $GOPATH/src/$REPOSITORY
|
||||
RUN cd $GOPATH/src/$REPOSITORY && make install
|
||||
|
||||
|
||||
FROM alpine:3.11
|
||||
|
||||
MAINTAINER hikachan sadayuki-matsuno
|
||||
|
||||
ENV LOGDIR /var/log/vuls
|
||||
ENV WORKDIR /vuls
|
||||
|
||||
RUN apk add --no-cache \
|
||||
openssh-client \
|
||||
ca-certificates \
|
||||
git \
|
||||
&& mkdir -p $WORKDIR $LOGDIR
|
||||
|
||||
COPY --from=builder /go/bin/vuls /usr/local/bin/
|
||||
|
||||
VOLUME ["$WORKDIR", "$LOGDIR"]
|
||||
WORKDIR $WORKDIR
|
||||
ENV PWD $WORKDIR
|
||||
|
||||
ENTRYPOINT ["vuls"]
|
||||
CMD ["--help"]
|
||||
75
GNUmakefile
Normal file
@@ -0,0 +1,75 @@
|
||||
.PHONY: \
|
||||
build \
|
||||
install \
|
||||
all \
|
||||
vendor \
|
||||
lint \
|
||||
vet \
|
||||
fmt \
|
||||
fmtcheck \
|
||||
pretest \
|
||||
test \
|
||||
cov \
|
||||
clean
|
||||
|
||||
SRCS = $(shell git ls-files '*.go')
|
||||
PKGS = $(shell go list ./...)
|
||||
VERSION := $(shell git describe --tags --abbrev=0)
|
||||
REVISION := $(shell git rev-parse --short HEAD)
|
||||
BUILDTIME := $(shell date "+%Y%m%d_%H%M%S")
|
||||
LDFLAGS := -X 'github.com/future-architect/vuls/config.Version=$(VERSION)' \
|
||||
-X 'github.com/future-architect/vuls/config.Revision=build-$(BUILDTIME)_$(REVISION)'
|
||||
GO := GO111MODULE=on go
|
||||
GO_OFF := GO111MODULE=off go
|
||||
|
||||
|
||||
all: build
|
||||
|
||||
build: main.go pretest fmt
|
||||
$(GO) build -a -ldflags "$(LDFLAGS)" -o vuls $<
|
||||
|
||||
b: main.go pretest fmt
|
||||
$(GO) build -ldflags "$(LDFLAGS)" -o vuls $<
|
||||
|
||||
install: main.go pretest
|
||||
$(GO) install -ldflags "$(LDFLAGS)"
|
||||
|
||||
lint:
|
||||
$(GO_OFF) get -u golang.org/x/lint/golint
|
||||
golint $(PKGS)
|
||||
|
||||
vet:
|
||||
echo $(PKGS) | xargs env $(GO) vet || exit;
|
||||
|
||||
fmt:
|
||||
gofmt -s -w $(SRCS)
|
||||
|
||||
mlint:
|
||||
$(foreach file,$(SRCS),gometalinter $(file) || exit;)
|
||||
|
||||
fmtcheck:
|
||||
$(foreach file,$(SRCS),gofmt -s -d $(file);)
|
||||
|
||||
pretest: lint vet fmtcheck
|
||||
|
||||
test:
|
||||
$(GO) test -cover -v ./... || exit;
|
||||
|
||||
unused:
|
||||
$(foreach pkg,$(PKGS),unused $(pkg);)
|
||||
|
||||
cov:
|
||||
@ go get -v github.com/axw/gocov/gocov
|
||||
@ go get golang.org/x/tools/cmd/cover
|
||||
gocov test | gocov report
|
||||
|
||||
clean:
|
||||
echo $(PKGS) | xargs go clean || exit;
|
||||
|
||||
# trivy-to-vuls
|
||||
build-trivy-to-vuls: pretest fmt
|
||||
$(GO) build -o trivy-to-vuls contrib/trivy/cmd/*.go
|
||||
|
||||
# future-vuls
|
||||
build-future-vuls: pretest fmt
|
||||
$(GO) build -o future-vuls contrib/future-vuls/cmd/*.go
|
||||
153
LICENSE
@@ -1,23 +1,21 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
@@ -26,44 +24,34 @@ them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
@@ -72,7 +60,7 @@ modification follow.
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
@@ -549,35 +537,45 @@ to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
@@ -631,44 +629,33 @@ to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
Vuls Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
52
Makefile
@@ -1,52 +0,0 @@
|
||||
.PHONY: \
|
||||
all \
|
||||
vendor \
|
||||
lint \
|
||||
vet \
|
||||
fmt \
|
||||
fmtcheck \
|
||||
pretest \
|
||||
test \
|
||||
integration \
|
||||
cov \
|
||||
clean
|
||||
|
||||
SRCS = $(shell git ls-files '*.go')
|
||||
PKGS = ./. ./db ./config ./models ./report ./cveapi ./scan ./util ./commands
|
||||
|
||||
all: test
|
||||
|
||||
vendor:
|
||||
@ go get -v github.com/mjibson/party
|
||||
party -d external -c -u
|
||||
|
||||
lint:
|
||||
@ go get -v github.com/golang/lint/golint
|
||||
$(foreach file,$(SRCS),golint $(file) || exit;)
|
||||
|
||||
vet:
|
||||
@-go get -v golang.org/x/tools/cmd/vet
|
||||
$(foreach pkg,$(PKGS),go vet $(pkg);)
|
||||
|
||||
fmt:
|
||||
gofmt -w $(SRCS)
|
||||
|
||||
fmtcheck:
|
||||
$(foreach file,$(SRCS),gofmt -d $(file);)
|
||||
|
||||
pretest: lint vet fmtcheck
|
||||
|
||||
test: pretest
|
||||
$(foreach pkg,$(PKGS),go test -v $(pkg) || exit;)
|
||||
|
||||
unused :
|
||||
$(foreach pkg,$(PKGS),unused $(pkg);)
|
||||
|
||||
cov:
|
||||
@ go get -v github.com/axw/gocov/gocov
|
||||
@ go get golang.org/x/tools/cmd/cover
|
||||
gocov test | gocov report
|
||||
|
||||
clean:
|
||||
$(foreach pkg,$(PKGS),go clean $(pkg) || exit;)
|
||||
|
||||
2
NOTICE
@@ -1,2 +1,2 @@
|
||||
Vuls Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
Vuls Copyright (C) 2016 Future Corporation , Japan.
|
||||
|
||||
|
||||
652
README.fr.md
@@ -1,652 +0,0 @@
|
||||
|
||||
# Vuls: VULnerability Scanner
|
||||
|
||||
[](http://goo.gl/forms/xm5KFo35tu)
|
||||
|
||||
Scanneur de vulnérabilité Linux, sans agent, écrit en golang
|
||||
|
||||
Nous avons une équipe Slack. [Rejoignez notre Slack Team](http://goo.gl/forms/xm5KFo35tu)
|
||||
|
||||
[README en Japonais](https://github.com/future-architect/vuls/blob/master/README.ja.md)
|
||||
|
||||
[](https://asciinema.org/a/3y9zrf950agiko7klg8abvyck)
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
----
|
||||
|
||||
# Résumé
|
||||
|
||||
Effectuer des recherches de vulnérabilités et des mises à jour quotidiennes peut etre un fardeau pour un administrateur système.
|
||||
Afin d'éviter des interruptions systèmes dans un environnement de production, il est fréquent pour un administrateur système de choisir de ne pas utiliser la fonction de mise à jour automatique proposée par le gestionnaire de paquets et d'effecter ces mises à jour manuellement.
|
||||
Ce qui implique les problèmes suivants :
|
||||
- L'administrateur système devra surveiller constamment toutes les nouvelles vulnérabilités dans NVD (National Vulnerability Database) etc.
|
||||
- Il pourrait être impossible pour un administrateur système de surveiller tous les logiciels installés sur un serveur.
|
||||
- Il est coûteux d'effectuer une analyse pour déterminer quels sont les serveurs affectés par de nouvelles vulnérabilités. La possibilité de négliger un serveur ou deux est bien présente.
|
||||
|
||||
Vuls est un outil crée pour palier aux problèmes listés ci-dessus. Voici ses caractéristiques.
|
||||
- Informer les utilisateurs des vulnérabilités système.
|
||||
- Informer les utilisateurs des systèmes concernés.
|
||||
- La détection de vulnérabilités est effectuée automatiquement pour éviter toute négligence.
|
||||
- Les rapports sont générés régulièrement via CRON pour mieux gérer ces vulnérabilités.
|
||||
|
||||

|
||||
|
||||
----
|
||||
|
||||
# Caractéristiques principales
|
||||
|
||||
- Recherche de vulnérabilités sur des serveurs Linux
|
||||
- Supporte Ubuntu, Debian, CentOS, Amazon Linux, RHEL
|
||||
- Cloud, auto-hébergement, Docker
|
||||
- Scan d'intergiciels non inclus dans le gestionnaire de paquets de l'OS
|
||||
- Scan d'intergiciels, de libraries de language de programmation et framework pour des vulnérabilités
|
||||
- Supporte les logiciels inscrits au CPE
|
||||
- Architecture sans agent
|
||||
- L'utilisateur doit seulement mettre en place VULS sur une seule machine qui se connectera aux autres via SSH
|
||||
- Génération automatique des fichiers de configuration
|
||||
- Auto detection de serveurs via CIDR et génération de configuration
|
||||
- Email et notification Slack possibles (supporte le Japonais)
|
||||
- Les résultats d'un scan sont accessibles dans un shell via TUI Viewer terminal.
|
||||
|
||||
----
|
||||
|
||||
# Ce que Vuls ne fait pas
|
||||
|
||||
- Vuls ne met pas à jour les programmes affectés par les vulnérabilités découvertes.
|
||||
|
||||
----
|
||||
|
||||
# Hello Vuls
|
||||
|
||||
Ce tutoriel décrit la recherche de vulnérabilités sur une machine locale avec Vuls.
|
||||
Voici les étapes à suivre.
|
||||
|
||||
1. Démrarrage d'Amazon Linux
|
||||
1. Autoriser les connexions SSH depuis localhost
|
||||
1. Installation des prérequis
|
||||
1. Déploiement de go-cve-dictionary
|
||||
1. Deploiement de Vuls
|
||||
1. Configuration
|
||||
1. Préparation
|
||||
1. Scan
|
||||
1. TUI(Terminal-Based User Interface)
|
||||
|
||||
## Step1. Démrarrage d'Amazon Linux
|
||||
|
||||
- Nous utilisons dans cette exemple une vieille AMI (amzn-ami-hvm-2015.09.1.x86_64-gp2 - ami-383c1956)
|
||||
- Taille de l'instance : t2.medium
|
||||
- La première fois, t2.medium et plus sont requis pour la récupération des CVE depuis NVD (2.3GB de mémoire utilisé)
|
||||
- Une fois la récupération initiale des données NVD terminée vous pouvez passer sur une instance t2.nano.
|
||||
- Ajoutez la configuration suivante au cloud-init, afin d'éviter une mise à jour automatique lors du premier démarrage.
|
||||
|
||||
- [Q: How do I disable the automatic installation of critical and important security updates on initial launch?](https://aws.amazon.com/amazon-linux-ami/faqs/?nc1=h_ls)
|
||||
```
|
||||
#cloud-config
|
||||
repo_upgrade: none
|
||||
```
|
||||
|
||||
## Step2. Paramètres SSH
|
||||
|
||||
Il est obligatoire que le serveur puisse se connecter à son propre serveur SSH
|
||||
|
||||
Générez une paire de clés SSH et ajoutez la clé publique dans le fichier authorized_keys
|
||||
```bash
|
||||
$ ssh-keygen -t rsa
|
||||
$ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
|
||||
$ chmod 600 ~/.ssh/authorized_keys
|
||||
```
|
||||
|
||||
## Step3. Installation des prérequis
|
||||
|
||||
Vuls requiert l'installation des paquets suivants :
|
||||
|
||||
- sqlite
|
||||
- git
|
||||
- gcc
|
||||
- go v1.6
|
||||
- https://golang.org/doc/install
|
||||
|
||||
```bash
|
||||
$ ssh ec2-user@52.100.100.100 -i ~/.ssh/private.pem
|
||||
$ sudo yum -y install sqlite git gcc
|
||||
$ wget https://storage.googleapis.com/golang/go1.6.linux-amd64.tar.gz
|
||||
$ sudo tar -C /usr/local -xzf go1.6.linux-amd64.tar.gz
|
||||
$ mkdir $HOME/go
|
||||
```
|
||||
Ajoutez les lignes suivantes dans /etc/profile.d/goenv.sh
|
||||
|
||||
```bash
|
||||
export GOROOT=/usr/local/go
|
||||
export GOPATH=$HOME/go
|
||||
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
|
||||
```
|
||||
|
||||
Ajoutons ces nouvelles variables d’environnement au shell
|
||||
```bash
|
||||
$ source /etc/profile.d/goenv.sh
|
||||
```
|
||||
|
||||
## Step4. Déploiement de [go-cve-dictionary](https://github.com/kotakanbe/go-cve-dictionary)
|
||||
|
||||
go get
|
||||
|
||||
```bash
|
||||
$ sudo mkdir /var/log/vuls
|
||||
$ sudo chown ec2-user /var/log/vuls
|
||||
$ sudo chmod 700 /var/log/vuls
|
||||
$ go get github.com/kotakanbe/go-cve-dictionary
|
||||
```
|
||||
|
||||
Démarrez go-cve-dictionary en mode serveur.
|
||||
Lors de son premier démarrage go-cve-dictionary récupère la liste des vulnérabilités depuis NVD
|
||||
Cette opération prend environ 10 minutes (sur AWS).
|
||||
|
||||
```bash
|
||||
$ go-cve-dictionary server
|
||||
... Fetching ...
|
||||
$ ls -alh cve.sqlite3
|
||||
-rw-r--r-- 1 ec2-user ec2-user 7.0M Mar 24 13:20 cve.sqlite3
|
||||
```
|
||||
|
||||
Une fois les informations de vulnérabilités collectées redémarrez le mode serveur.
|
||||
```bash
|
||||
$ go-cve-dictionary server
|
||||
[Mar 24 15:21:55] INFO Opening DB. datafile: /home/ec2-user/cve.sqlite3
|
||||
[Mar 24 15:21:55] INFO Migrating DB
|
||||
[Mar 24 15:21:56] INFO Starting HTTP Sever...
|
||||
[Mar 24 15:21:56] INFO Listening on 127.0.0.1:1323
|
||||
```
|
||||
|
||||
## Step5. Déploiement de Vuls
|
||||
|
||||
Ouvrez un second terminal, connectez vous à l'instance ec2 via SSH
|
||||
|
||||
go get
|
||||
```
|
||||
$ go get github.com/future-architect/vuls
|
||||
```
|
||||
|
||||
## Step6. Configuration
|
||||
|
||||
Créez un fichier de configuration (TOML format).
|
||||
|
||||
```
|
||||
$ cat config.toml
|
||||
[servers]
|
||||
|
||||
[servers.172-31-4-82]
|
||||
host = "172.31.4.82"
|
||||
port = "22"
|
||||
user = "ec2-user"
|
||||
keyPath = "/home/ec2-user/.ssh/id_rsa"
|
||||
```
|
||||
|
||||
## Step7. Configuration des serveurs cibles vuls
|
||||
|
||||
```
|
||||
$ vuls prepare
|
||||
```
|
||||
|
||||
## Step8. Scan
|
||||
|
||||
```
|
||||
$ vuls scan
|
||||
INFO[0000] Begin scanning (config: /home/ec2-user/config.toml)
|
||||
|
||||
... snip ...
|
||||
|
||||
172-31-4-82 (amazon 2015.09)
|
||||
============================
|
||||
CVE-2016-0494 10.0 Unspecified vulnerability in the Java SE and Java SE Embedded components in Oracle
|
||||
Java SE 6u105, 7u91, and 8u66 and Java SE Embedded 8u65 allows remote attackers to
|
||||
affect confidentiality, integrity, and availability via unknown vectors related to
|
||||
2D.
|
||||
... snip ...
|
||||
|
||||
CVE-2016-0494
|
||||
-------------
|
||||
Score 10.0 (High)
|
||||
Vector (AV:N/AC:L/Au:N/C:C/I:C/A:C)
|
||||
Summary Unspecified vulnerability in the Java SE and Java SE Embedded components in Oracle Java SE 6u105,
|
||||
7u91, and 8u66 and Java SE Embedded 8u65 allows remote attackers to affect confidentiality,
|
||||
integrity, and availability via unknown vectors related to 2D.
|
||||
NVD https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-0494
|
||||
MITRE https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-0494
|
||||
CVE Details http://www.cvedetails.com/cve/CVE-2016-0494
|
||||
CVSS Claculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-0494&vector=(AV:N/AC:L/Au:N/C:C/I:C/A:C)
|
||||
RHEL-CVE https://access.redhat.com/security/cve/CVE-2016-0494
|
||||
ALAS-2016-643 https://alas.aws.amazon.com/ALAS-2016-643.html
|
||||
Package/CPE java-1.7.0-openjdk-1.7.0.91-2.6.2.2.63.amzn1 -> java-1.7.0-openjdk-1:1.7.0.95-2.6.4.0.65.amzn1
|
||||
|
||||
```
|
||||
|
||||
## Step9. TUI
|
||||
|
||||
Les résultats de Vuls peuvent etre affichés dans un Shell via TUI (Terminal-Based User Interface).
|
||||
|
||||
```
|
||||
$ vuls tui
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
----
|
||||
|
||||
# Architecture
|
||||
|
||||

|
||||
|
||||
## go-cve-dictinary
|
||||
- Collecte les informations de vulnérabilités depuis NVD, JVN(Japonais), et les envoie dans SQLite.
|
||||
|
||||
## Vuls
|
||||
- Scan de vulnérabilités sur serveurs et création d'une liste contenant les CVE ID
|
||||
- Pour des informations plus détaillés sur une CVE, envoie une requete HTTP à go-cve-dictinary
|
||||
- Rapport à Slack et par Email
|
||||
- L'administrateur système peut voir les résultats du dernier rapport dans le terminal
|
||||
|
||||
----
|
||||
|
||||
# Exemples d'utilisation
|
||||
|
||||
## Scan de tous les serverus
|
||||
|
||||

|
||||
|
||||
## Scan d'un seul serveur
|
||||
|
||||
web/app server in the same configuration under the load balancer
|
||||
|
||||

|
||||
|
||||
----
|
||||
|
||||
# OS supportés
|
||||
|
||||
| Distribution| Release |
|
||||
|:------------|-------------------:|
|
||||
| Ubuntu | 12, 14, 16|
|
||||
| Debian | 7, 8|
|
||||
| RHEL | 4, 5, 6, 7|
|
||||
| CentOS | 5, 6, 7|
|
||||
| Amazon Linux| All |
|
||||
|
||||
----
|
||||
|
||||
|
||||
# Usage: Détection Automatique de Serveurs
|
||||
|
||||
La sous-commande Discovery permet de détecter les serveurs actifs dans un range d'IP CIDR, les résultas sont directement affichés dans le terminal en respectant le format du fichier de configuration (TOML format).
|
||||
|
||||
```
|
||||
$ vuls discover -help
|
||||
discover:
|
||||
discover 192.168.0.0/24
|
||||
```
|
||||
|
||||
## Exemple
|
||||
|
||||
```
|
||||
$ vuls discover 172.31.4.0/24
|
||||
# Create config.toml using below and then ./vuls --config=/path/to/config.toml
|
||||
|
||||
[slack]
|
||||
hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
|
||||
channel = "#channel-name"
|
||||
#channel = "#{servername}"
|
||||
iconEmoji = ":ghost:"
|
||||
authUser = "username"
|
||||
notifyUsers = ["@username"]
|
||||
|
||||
[mail]
|
||||
smtpAddr = "smtp.gmail.com"
|
||||
smtpPort = 465
|
||||
user = "username"
|
||||
password = "password"
|
||||
from = "from@address.com"
|
||||
to = ["to@address.com"]
|
||||
cc = ["cc@address.com"]
|
||||
subjectPrefix = "[vuls]"
|
||||
|
||||
[default]
|
||||
#port = "22"
|
||||
#user = "username"
|
||||
#password = "password"
|
||||
#keyPath = "/home/username/.ssh/id_rsa"
|
||||
#keyPassword = "password"
|
||||
|
||||
[servers]
|
||||
|
||||
[servers.172-31-4-82]
|
||||
host = "172.31.4.82"
|
||||
#port = "22"
|
||||
#user = "root"
|
||||
#password = "password"
|
||||
#keyPath = "/home/username/.ssh/id_rsa"
|
||||
#keyPassword = "password"
|
||||
#cpeNames = [
|
||||
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
#]
|
||||
```
|
||||
|
||||
Vous pouvez customiser votre configuration en utilisant ce modèle.
|
||||
|
||||
----
|
||||
|
||||
# Configuration
|
||||
|
||||
- Slack section
|
||||
```
|
||||
[slack]
|
||||
hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
|
||||
channel = "#channel-name"
|
||||
#channel = "#{servername}"
|
||||
iconEmoji = ":ghost:"
|
||||
authUser = "username"
|
||||
notifyUsers = ["@username"]
|
||||
```
|
||||
|
||||
- hookURL : Incomming webhook's URL
|
||||
- channel : channel name.
|
||||
If you set #{servername} to channel, the report will be sent to #servername channel.
|
||||
In the following example, the report will be sent to the #server1 and #server2.
|
||||
Be sure to create these channels before scanning.
|
||||
```
|
||||
[slack]
|
||||
channel = "#{servername}"
|
||||
...snip...
|
||||
|
||||
[servers]
|
||||
|
||||
[servers.server1]
|
||||
host = "172.31.4.82"
|
||||
...snip...
|
||||
|
||||
[servers.server2]
|
||||
host = "172.31.4.83"
|
||||
...snip...
|
||||
```
|
||||
|
||||
- iconEmoji: emoji
|
||||
- authUser: username of the slack team
|
||||
- notifyUsers: a list of Slack usernames to send Slack notifications.
|
||||
If you set ["@foo", "@bar"] to notifyUsers, @foo @bar will be included in text.
|
||||
So @foo, @bar can receive mobile push notifications on their smartphone.
|
||||
|
||||
- Mail section
|
||||
```
|
||||
[mail]
|
||||
smtpAddr = "smtp.gmail.com"
|
||||
smtpPort = 465
|
||||
user = "username"
|
||||
password = "password"
|
||||
from = "from@address.com"
|
||||
to = ["to@address.com"]
|
||||
cc = ["cc@address.com"]
|
||||
subjectPrefix = "[vuls]"
|
||||
```
|
||||
|
||||
- Default section
|
||||
```
|
||||
[default]
|
||||
#port = "22"
|
||||
#user = "username"
|
||||
#password = "password"
|
||||
#keyPath = "/home/username/.ssh/id_rsa"
|
||||
#keyPassword = "password"
|
||||
```
|
||||
Items of the default section will be used if not specified.
|
||||
|
||||
- servers section
|
||||
```
|
||||
[servers]
|
||||
|
||||
[servers.172-31-4-82]
|
||||
host = "172.31.4.82"
|
||||
#port = "22"
|
||||
#user = "root"
|
||||
#password = "password"
|
||||
#keyPath = "/home/username/.ssh/id_rsa"
|
||||
#keyPassword = "password"
|
||||
#cpeNames = [
|
||||
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
#]
|
||||
```
|
||||
Vous pouvez remplacer les valeurs par défaut indiquées en modifiant la section default
|
||||
Vuls supporte plusieurs méthodes d'authentification SSH :
|
||||
- SSH agent
|
||||
- SSH authentication par clés (avec mot de passe ou sans mot de passe)
|
||||
- Authentification par mot de passe
|
||||
|
||||
----
|
||||
|
||||
# Utilisation : Prepare
|
||||
|
||||
La sous-commande prepare installe tous les paquets nécessaires sur chaque serveur.
|
||||
|
||||
| Distribution| Release | Requirements |
|
||||
|:------------|-------------------:|:-------------|
|
||||
| Ubuntu | 12, 14, 16| - |
|
||||
| Debian | 7, 8| apptitude |
|
||||
| CentOS | 5| yum-plugin-security, yum-changelog |
|
||||
| CentOS | 6, 7| yum-plugin-security, yum-plugin-changelog |
|
||||
| Amazon | All | - |
|
||||
| RHEL | 4, 5, 6, 7 | - |
|
||||
|
||||
|
||||
```
|
||||
$ vuls prepare -help
|
||||
prepare:
|
||||
prepare [-config=/path/to/config.toml] [-debug]
|
||||
|
||||
-config string
|
||||
/path/to/toml (default "$PWD/config.toml")
|
||||
-debug
|
||||
debug mode
|
||||
-use-unattended-upgrades
|
||||
[Deprecated] For Ubuntu, install unattended-upgrades
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
# Utilisation : Scan
|
||||
|
||||
```
|
||||
$ vuls scan -help
|
||||
scan:
|
||||
scan
|
||||
[-lang=en|ja]
|
||||
[-config=/path/to/config.toml]
|
||||
[-dbpath=/path/to/vuls.sqlite3]
|
||||
[-cve-dictionary-url=http://127.0.0.1:1323]
|
||||
[-cvss-over=7]
|
||||
[-report-slack]
|
||||
[-report-mail]
|
||||
[-http-proxy=http://192.168.0.1:8080]
|
||||
[-debug]
|
||||
[-debug-sql]
|
||||
-config string
|
||||
/path/to/toml (default "$PWD/config.toml")
|
||||
-cve-dictionary-url string
|
||||
http://CVE.Dictionary (default "http://127.0.0.1:1323")
|
||||
-cvss-over float
|
||||
-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))
|
||||
-dbpath string
|
||||
/path/to/sqlite3 (default "$PWD/vuls.sqlite3")
|
||||
-debug
|
||||
debug mode
|
||||
-debug-sql
|
||||
SQL debug mode
|
||||
-http-proxy string
|
||||
http://proxy-url:port (default: empty)
|
||||
-lang string
|
||||
[en|ja] (default "en")
|
||||
-report-mail
|
||||
Email report
|
||||
-report-slack
|
||||
Slack report
|
||||
-use-unattended-upgrades
|
||||
[Deprecated] For Ubuntu. Scan by unattended-upgrades or not (use apt-get upgrade --dry-run by default)
|
||||
-use-yum-plugin-security
|
||||
[Deprecated] For CentOS 5. Scan by yum-plugin-security or not (use yum check-update by default)
|
||||
|
||||
```
|
||||
|
||||
## exemple
|
||||
|
||||
Lancez go-cve-dictionary en mode serveur avant de lancer un scan
|
||||
```
|
||||
$ go-cve-dictionary server
|
||||
```
|
||||
|
||||
### Scan tous les serveurs identifiés dans le fichier de configuration
|
||||
```
|
||||
$ vuls scan --report-slack --report-mail --cvss-over=7
|
||||
```
|
||||
Via cette simple commande Vuls va : ..
|
||||
- Scanner tous les serveurs identifiés dans le fichier de configuration
|
||||
- Envoyer les résultas du scan à slack et par email
|
||||
- Ne rapporter que les CVE dont la note CVSS est au dessus de 7
|
||||
- Afficher les résultats du scan dans le terminal
|
||||
|
||||
### Scan de serveurs spécifiques
|
||||
```
|
||||
$ vuls scan server1 server2
|
||||
```
|
||||
Via cette simple commande Vuls va : ..
|
||||
- Scanner seulement 2 serveurs. (server1, server2)
|
||||
- Afficher les résultats du scan dans le terminal
|
||||
|
||||
----
|
||||
|
||||
# Utilisation : Recherche de vulnérabilités sur des paquets non compris dans l'OS
|
||||
|
||||
Il est possible de détecter des vulnérabilités sur des programmes que vous avez compilés, des lors que les libraries et frameworks ont été enregistré dans [CPE](https://nvd.nist.gov/cpe.cfm).
|
||||
|
||||
- Comment rechercher dans CPE via le nom du programme
|
||||
- [NVD: Search Common Platform Enumerations (CPE)](https://web.nvd.nist.gov/view/cpe/search)
|
||||
**Check CPE Naming Format: 2.2**
|
||||
|
||||
- Configuration
|
||||
Pour détecter des vulnérabilités sur Ruby on Rails v4.2.1, cpeNames doit etre déclaré dans la section servers.
|
||||
```
|
||||
[servers]
|
||||
|
||||
[servers.172-31-4-82]
|
||||
host = "172.31.4.82"
|
||||
user = "ec2-user"
|
||||
keyPath = "/home/username/.ssh/id_rsa"
|
||||
cpeNames = [
|
||||
"cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
]
|
||||
```
|
||||
|
||||
# Utilisation : Mise à jour des données NVD.
|
||||
|
||||
```
|
||||
$ go-cve-dictionary fetchnvd -h
|
||||
fetchnvd:
|
||||
fetchnvd
|
||||
[-last2y]
|
||||
[-dbpath=/path/to/cve.sqlite3]
|
||||
[-debug]
|
||||
[-debug-sql]
|
||||
|
||||
-dbpath string
|
||||
/path/to/sqlite3 (default "$PWD/cve.sqlite3")
|
||||
-debug
|
||||
debug mode
|
||||
-debug-sql
|
||||
SQL debug mode
|
||||
-last2y
|
||||
Refresh NVD data in the last two years.
|
||||
```
|
||||
|
||||
- Récupérer toutes les données jusqu'à aujourd'hui
|
||||
|
||||
```
|
||||
$ go-cve-dictionary fetchnvd -entire
|
||||
```
|
||||
|
||||
- Reçupérer les données des 2 denières années
|
||||
|
||||
```
|
||||
$ go-cve-dictionary fetchnvd -last2y
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
# Misc
|
||||
|
||||
- HTTP Proxy Support
|
||||
If your system is behind HTTP proxy, you have to specify --http-proxy option.
|
||||
|
||||
- How to Daemonize go-cve-dictionary
|
||||
Use Systemd, Upstart or supervisord, daemontools...
|
||||
|
||||
- How to Enable Automatic-Update of Vunerability Data.
|
||||
Use job scheduler like Cron (with -last2y option).
|
||||
|
||||
- How to cross compile
|
||||
```bash
|
||||
$ cd /path/to/your/local-git-reporsitory/vuls
|
||||
$ GOOS=linux GOARCH=amd64 go build -o vuls.amd64
|
||||
```
|
||||
|
||||
- Logging
|
||||
Log wrote to under /var/log/vuls/
|
||||
|
||||
- Debug
|
||||
Run with --debug, --sql-debug option.
|
||||
|
||||
- Ajusting Open File Limit
|
||||
[Riak docs](http://docs.basho.com/riak/latest/ops/tuning/open-files-limit/) is awesome.
|
||||
|
||||
- Does Vuls accept ssh connections with fish-shell or old zsh as the login shell?
|
||||
No, Vuls needs a user on the server for bash login. see also [#8](/../../issues/8)
|
||||
|
||||
- Windows
|
||||
Use Microsoft Baseline Security Analyzer. [MBSA](https://technet.microsoft.com/en-us/security/cc184924.aspx)
|
||||
|
||||
----
|
||||
|
||||
# Data Source
|
||||
|
||||
- [NVD](https://nvd.nist.gov/)
|
||||
- [JVN(Japanese)](http://jvndb.jvn.jp/apis/myjvn/)
|
||||
|
||||
|
||||
# Authors
|
||||
|
||||
kotakanbe ([@kotakanbe](https://twitter.com/kotakanbe)) created vuls and [these fine people](https://github.com/future-architect/vuls/graphs/contributors) have contributed.
|
||||
|
||||
----
|
||||
|
||||
# Contribute
|
||||
|
||||
1. Fork it
|
||||
2. Create your feature branch (`git checkout -b my-new-feature`)
|
||||
3. Commit your changes (`git commit -am 'Add some feature'`)
|
||||
4. Push to the branch (`git push origin my-new-feature`)
|
||||
5. Create new Pull Request
|
||||
|
||||
----
|
||||
|
||||
# Change Log
|
||||
|
||||
Please see [CHANGELOG](https://github.com/future-architect/vuls/blob/master/CHANGELOG.md).
|
||||
|
||||
----
|
||||
|
||||
# Licence
|
||||
|
||||
Please see [LICENSE](https://github.com/future-architect/vuls/blob/master/LICENSE).
|
||||
|
||||
|
||||
[](https://bitdeli.com/free "Bitdeli Badge")
|
||||
|
||||
|
||||
109
README.ja.md
@@ -1,109 +0,0 @@
|
||||
|
||||
# Vuls: VULnerability Scanner
|
||||
|
||||
[](http://goo.gl/forms/xm5KFo35tu)
|
||||
|
||||
Vulnerability scanner for Linux, agentless, written in golang.
|
||||
|
||||
[README in English](https://github.com/future-architect/vuls/blob/master/README.md)
|
||||
Slackチームは[こちらから](http://goo.gl/forms/xm5KFo35tu)参加できます。(日本語でオッケーです)
|
||||
|
||||
[](https://asciinema.org/a/bazozlxrw1wtxfu9yojyihick)
|
||||
|
||||

|
||||
|
||||
|
||||
----
|
||||
|
||||
# Abstract
|
||||
|
||||
毎日のように発見される脆弱性の調査やソフトウェアアップデート作業は、システム管理者にとって負荷の高いタスクである。
|
||||
プロダクション環境ではサービス停止リスクを避けるために、パッケージマネージャの自動更新機能を使わずに手動更新で運用するケースも多い。
|
||||
だが、手動更新での運用には以下の問題がある。
|
||||
- システム管理者がNVDなどで新着の脆弱性をウォッチし続けなければならない
|
||||
- サーバにインストールされているソフトウェアは膨大であり、システム管理者が全てを把握するのは困難
|
||||
- 新着の脆弱性がどのサーバに該当するのかといった調査コストが大きく、漏れる可能性がある
|
||||
|
||||
|
||||
Vulsは上に挙げた手動運用での課題を解決するツールであり、以下の特徴がある。
|
||||
- システムに関係ある脆弱性のみ教えてくれる
|
||||
- その脆弱性に該当するサーバを教えてくれる
|
||||
- 自動スキャンのため脆弱性検知の漏れを防ぐことができる
|
||||
- CRONなどで定期実行、レポートすることで脆弱性の放置を防ぐことできる
|
||||
|
||||

|
||||
|
||||
----
|
||||
|
||||
# Main Features
|
||||
|
||||
- Linuxサーバに存在する脆弱性をスキャン
|
||||
- Ubuntu, Debian, CentOS, Amazon Linux, RHELに対応
|
||||
- クラウド、オンプレミス、Docker
|
||||
- OSパッケージ管理対象外のミドルウェアをスキャン
|
||||
- プログラミング言語のライブラリやフレームワーク、ミドルウェアの脆弱性スキャン
|
||||
- CPEに登録されているソフトウェアが対象
|
||||
- エージェントレスアーキテクチャ
|
||||
- スキャン対象サーバにSSH接続可能なマシン1台にセットアップするだけで動作
|
||||
- 設定ファイルのテンプレート自動生成
|
||||
- CIDRを指定してサーバを自動検出、設定ファイルのテンプレートを生成
|
||||
- EmailやSlackで通知可能(日本語でのレポートも可能)
|
||||
- 付属するTerminal-Based User Interfaceビューアでは、Vim風キーバインドでスキャン結果を参照可能
|
||||
|
||||
----
|
||||
|
||||
詳細は[README in English](https://github.com/future-architect/vuls/blob/master/README.md) を参照
|
||||
|
||||
# レポートの日本語化
|
||||
|
||||
- JVNから日本語の脆弱性情報を取得
|
||||
```
|
||||
$ go-cve-dictionary fetchjvn -help
|
||||
fetchjvn:
|
||||
fetchjvn [-dump-path=$PWD/cve] [-dpath=$PWD/vuls.sqlite3] [-week] [-month] [-entire]
|
||||
|
||||
-dbpath string
|
||||
/path/to/sqlite3/DBfile (default "$PWD/cve.sqlite3")
|
||||
-debug
|
||||
debug mode
|
||||
-debug-sql
|
||||
SQL debug mode
|
||||
-dump-path string
|
||||
/path/to/dump.json (default "$PWD/cve.json")
|
||||
-entire
|
||||
Fetch data for entire period.(This operation is time-consuming) (default: false)
|
||||
-month
|
||||
Fetch data in the last month (default: false)
|
||||
-week
|
||||
Fetch data in the last week. (default: false)
|
||||
|
||||
```
|
||||
|
||||
- すべての期間の脆弱性情報を取得(1時間以上かかる)
|
||||
```
|
||||
$ go-cve-dictionary fetchjvn -entire
|
||||
```
|
||||
|
||||
- 直近1ヶ月間に更新された脆弱性情報を取得(1分未満)
|
||||
```
|
||||
$ go-cve-dictionary fetchjvn -month
|
||||
```
|
||||
|
||||
- 直近1週間に更新された脆弱性情報を取得(1分未満)
|
||||
```
|
||||
$ go-cve-dictionary fetchjvn -week
|
||||
```
|
||||
|
||||
- 脆弱性情報の自動アップデート
|
||||
Cronなどのジョブスケジューラを用いて実現可能。
|
||||
-week オプションを指定して夜間の日次実行を推奨。
|
||||
|
||||
|
||||
## スキャン実行
|
||||
|
||||
```
|
||||
$ vuls scan -lang=ja
|
||||
```
|
||||
Scan時にlang=jaを指定すると脆弱性レポートが日本語になる
|
||||
slack, emailは日本語対応済み TUIは日本語表示未対応
|
||||
|
||||
790
README.md
@@ -2,687 +2,211 @@
|
||||
# Vuls: VULnerability Scanner
|
||||
|
||||
[](http://goo.gl/forms/xm5KFo35tu)
|
||||
[](https://github.com/future-architect/vuls/blob/master/LICENSE)
|
||||
[](https://travis-ci.org/future-architect/vuls)
|
||||
[](https://goreportcard.com/report/github.com/future-architect/vuls)
|
||||
[](https://github.com/future-architect/vuls/graphs/contributors)
|
||||
|
||||
Vulnerability scanner for Linux, agentless, written in golang.
|
||||

|
||||
|
||||
We have a slack team. [Join slack team](http://goo.gl/forms/xm5KFo35tu)
|
||||
Vulnerability scanner for Linux/FreeBSD, agentless, written in golang.
|
||||
We have a slack team. [Join slack team](http://goo.gl/forms/xm5KFo35tu)
|
||||
Twitter: [@vuls_en](https://twitter.com/vuls_en)
|
||||
|
||||
[README in Japanese](https://github.com/future-architect/vuls/blob/master/README.ja.md)
|
||||
[README in French](https://github.com/future-architect/vuls/blob/master/README.fr.md)
|
||||

|
||||
|
||||

|
||||
|
||||
[](https://asciinema.org/a/3y9zrf950agiko7klg8abvyck)
|
||||
|
||||

|
||||
|
||||
----
|
||||
|
||||
## NEWS
|
||||
|
||||
| Version | Main Feature | Date |
|
||||
|:------------|:---------------------------------|:--------------------|
|
||||
| [v0.8.0](https://github.com/future-architect/vuls/releases/tag/v0.8.0) | secret | Coming soon |
|
||||
| [v0.7.0](https://github.com/future-architect/vuls/releases/tag/v0.7.0) | WordPress Vulnerability Scan | 2019/Apr/8 |
|
||||
| [v0.6.3](https://github.com/future-architect/vuls/releases/tag/v0.6.3) | GitHub Integration | 2019/Feb/20 |
|
||||
| [v0.6.2](https://github.com/future-architect/vuls/releases/tag/v0.6.2) | Add US-CERT/JPCERT Alerts as VulnSrc | 2019/Jan/23 |
|
||||
| [v0.6.1](https://github.com/future-architect/vuls/releases/tag/v0.6.1) | BugFix | 2018/Nov/16 |
|
||||
| [v0.6.0](https://github.com/future-architect/vuls/releases/tag/v0.6.0) | Add ExploitDB as VulnSrc | 2018/Nov/3 |
|
||||
| [v0.5.0](https://github.com/future-architect/vuls/releases/tag/v0.5.0) | Scan accuracy improvement | 2018/Aug/27 |
|
||||
|
||||
----
|
||||
|
||||
# Abstract
|
||||
## Abstract
|
||||
|
||||
For a system administrator, having to perform security vulnerability analysis and software update on a daily basis can be a burden.
|
||||
To avoid downtime in production environment, it is common for system administrator to choose not to use the automatic update option provided by package manager and to perform update manually.
|
||||
To avoid downtime in a production environment, it is common for a system administrator to choose not to use the automatic update option provided by the package manager and to perform update manually.
|
||||
This leads to the following problems.
|
||||
- System administrator will have to constantly watch out for any new vulnerabilities in NVD(National Vulnerability Database) and etc.
|
||||
- It might be impossible for the system administrator to monitor all the software if there are a large number of software installed in server.
|
||||
|
||||
- The system administrator will have to constantly watch out for any new vulnerabilities in NVD (National Vulnerability Database) or similar databases.
|
||||
- It might be impossible for the system administrator to monitor all the software if there are a large number of software packages installed in the server.
|
||||
- It is expensive to perform analysis to determine the servers affected by new vulnerabilities. The possibility of overlooking a server or two during analysis is there.
|
||||
|
||||
|
||||
Vuls is a tool created to solve the problems listed above. It has the following characteristics.
|
||||
|
||||
- Informs users of the vulnerabilities that are related to the system.
|
||||
- Informs users of the servers that are affected.
|
||||
- Vulnerability detection is done automatically to prevent any oversight.
|
||||
- Report is generated on regular basis using CRON etc. to manage vulnerability.
|
||||
- A report is generated on a regular basis using CRON or other methods. to manage vulnerability.
|
||||
|
||||

|
||||
|
||||
----
|
||||
|
||||
# Main Features
|
||||
## Main Features
|
||||
|
||||
- Scan for any vulnerabilities in Linux Server
|
||||
- Supports Ubuntu, Debian, CentOS, Amazon Linux, RHEL
|
||||
- Cloud, on-premise, Docker
|
||||
- Scan middleware that are not included in OS package management
|
||||
- Scan middleware, programming language libraries and framework for vulnerability
|
||||
- Support software registered in CPE
|
||||
- Agentless architecture
|
||||
- User is required to only setup one machine that is connected to other target servers via SSH
|
||||
- Auto generation of configuration file template
|
||||
- Auto detection of servers set using CIDR, generate configuration file template
|
||||
- Email and Slack notification is possible (supports Japanese language)
|
||||
- Scan result is viewable on accessory software, TUI Viewer terminal.
|
||||
### Scan for any vulnerabilities in Linux/FreeBSD Server
|
||||
|
||||
[Supports major Linux/FreeBSD](https://vuls.io/docs/en/supported-os.html)
|
||||
|
||||
- Alpine, Amazon Linux, CentOS, Debian, Oracle Linux, Raspbian, RHEL, SUSE Enterprise Linux, and Ubuntu
|
||||
- FreeBSD
|
||||
- Cloud, on-premise, Docker Container and Docker Image
|
||||
|
||||
### High-quality scan
|
||||
|
||||
Vuls uses multiple vulnerability databases
|
||||
|
||||
- [NVD](https://nvd.nist.gov/)
|
||||
- [JVN(Japanese)](http://jvndb.jvn.jp/apis/myjvn/)
|
||||
- OVAL
|
||||
- [Debian](https://www.debian.org/security/oval/)
|
||||
- [Oracle Linux](https://linux.oracle.com/security/oval/)
|
||||
- [RedHat](https://www.redhat.com/security/data/oval/)
|
||||
- [SUSE](http://ftp.suse.com/pub/projects/security/oval/)
|
||||
- [Ubuntu](https://people.canonical.com/~ubuntu-security/oval/)
|
||||
|
||||
- [Alpine-secdb](https://git.alpinelinux.org/cgit/alpine-secdb/)
|
||||
- [Debian Security Bug Tracker](https://security-tracker.debian.org/tracker/)
|
||||
- [Red Hat Security Advisories](https://access.redhat.com/security/security-updates/)
|
||||
- Commands (yum, zypper, and pkg-audit)
|
||||
- RHSA/ALAS/ELSA/FreeBSD-SA
|
||||
- [Exploit Database](https://www.exploit-db.com/)
|
||||
- [US-CERT](https://www.us-cert.gov/ncas/alerts)
|
||||
- [JPCERT](http://www.jpcert.or.jp/at/2019.html)
|
||||
- [WPVulnDB](https://wpvulndb.com/api)
|
||||
- [Node.js Security Working Group](https://github.com/nodejs/security-wg)
|
||||
- [Ruby Advisory Database](https://github.com/rubysec/ruby-advisory-db)
|
||||
- [Safety DB(Python)](https://github.com/pyupio/safety-db)
|
||||
- [PHP Security Advisories Database](https://github.com/FriendsOfPHP/security-advisories)
|
||||
- [RustSec Advisory Database](https://github.com/RustSec/advisory-db)
|
||||
- Changelog
|
||||
|
||||
### Scan mode
|
||||
|
||||
[Fast Scan](https://vuls.io/docs/en/architecture-fast-scan.html)
|
||||
|
||||
- Scan without root privilege, no dependencies
|
||||
- Almost no load on the scan target server
|
||||
- Offline mode scan with no internet access. (CentOS, Debian, Oracle Linux, Red Hat, and Ubuntu)
|
||||
|
||||
[Fast Root Scan](https://vuls.io/docs/en/architecture-fast-root-scan.html)
|
||||
|
||||
- Scan with root privilege
|
||||
- Almost no load on the scan target server
|
||||
- Detect processes affected by update using yum-ps (Amazon Linux, CentOS, Oracle Linux, and RedHat)
|
||||
- Detect processes which updated before but not restarting yet using checkrestart of debian-goodies (Debian and Ubuntu)
|
||||
- Offline mode scan with no internet access. (CentOS, Debian, Oracle Linux, Red Hat, and Ubuntu)
|
||||
|
||||
### [Remote, Local scan mode, Server mode](https://vuls.io/docs/en/architecture-remote-local.html)
|
||||
|
||||
[Remote scan mode](https://vuls.io/docs/en/architecture-remote-scan.html)
|
||||
|
||||
- User is required to only set up one machine that is connected to other target servers via SSH
|
||||
|
||||
[Local scan mode](https://vuls.io/docs/en/architecture-local-scan.html)
|
||||
|
||||
- If you don't want the central Vuls server to connect to each server by SSH, you can use Vuls in the Local Scan mode.
|
||||
|
||||
[Server mode](https://vuls.io/docs/en/usage-server.html)
|
||||
|
||||
- First, start Vuls in server mode and listen as an HTTP server.
|
||||
- Next, issue a command on the scan target server to collect software information. Then send the result to Vuls Server via HTTP. You receive the scan results as JSON format.
|
||||
- No SSH needed, No Scanner needed. Only issuing Linux commands directory on the scan target server.
|
||||
|
||||
### **Dynamic** Analysis
|
||||
|
||||
- It is possible to acquire the state of the server by connecting via SSH and executing the command.
|
||||
- Vuls warns when the scan target server was updated the kernel etc. but not restarting it.
|
||||
|
||||
### **Static** Analysis
|
||||
|
||||
**Image scan function is no longer supported from Vuls v0.9.5. Use Trivy directry**
|
||||
|
||||
~~Vuls v0.8.0 can scan Docker images using [knqyf263/trivy](https://github.com/knqyf263/trivy).
|
||||
Following Registry supported.~~
|
||||
|
||||
- ~~ECR~~
|
||||
- ~~GCR~~
|
||||
- ~~Local Image~~
|
||||
|
||||
~~For details, see [Scan docker image](https://vuls.io/docs/en/tutorial-scan-docker-image.html)~~
|
||||
|
||||
### Scan vulnerabilities of non-OS-packages
|
||||
|
||||
- Libraries of programming language
|
||||
- Self-compiled software
|
||||
- Network Devices
|
||||
|
||||
Vuls has some options to detect the vulnerabilities
|
||||
|
||||
- [Lockfile based Scan](https://vuls.io/docs/en/usage-scan-non-os-packages.html#library-vulns-scan)
|
||||
- [GitHub Integration](https://vuls.io/docs/en/usage-scan-non-os-packages.html#usage-integrate-with-github-security-alerts)
|
||||
- [Common Platform Enumeration (CPE) based Scan](https://vuls.io/docs/en/usage-scan-non-os-packages.html#cpe-scan)
|
||||
- [OWASP Dependency Check Integration](https://vuls.io/docs/en/usage-scan-non-os-packages.html#usage-integrate-with-owasp-dependency-check-to-automatic-update-when-the-libraries-are-updated-experimental)
|
||||
|
||||
## Scan WordPress core, themes, plugins
|
||||
|
||||
- [Scan WordPress](https://vuls.io/docs/en/usage-scan-wordpress.html)
|
||||
|
||||
## MISC
|
||||
|
||||
- Nondestructive testing
|
||||
- Pre-authorization is *NOT* necessary before scanning on AWS
|
||||
- Vuls works well with Continuous Integration since tests can be run every day. This allows you to find vulnerabilities very quickly.
|
||||
- Auto-generation of configuration file template
|
||||
- Auto-detection of servers set using CIDR, generate configuration file template
|
||||
- Email and Slack notification is possible (supports Japanese language)
|
||||
- Scan result is viewable on accessory software, TUI Viewer in a terminal or Web UI ([VulsRepo](https://github.com/ishiDACo/vulsrepo)).
|
||||
|
||||
----
|
||||
|
||||
# What Vuls Doesn't Do
|
||||
## What Vuls Doesn't Do
|
||||
|
||||
- Vuls doesn't update the vulnerable packages.
|
||||
|
||||
----
|
||||
|
||||
# Hello Vuls
|
||||
|
||||
This tutorial will let you scan the vulnerabilities on the localhost with vuls.
|
||||
This can be done in the following steps.
|
||||
|
||||
1. Launch Amazon Linux
|
||||
1. Enable to ssh from localhost
|
||||
1. Install requirements
|
||||
1. Deploy go-cve-dictionary
|
||||
1. Deploy Vuls
|
||||
1. Configuration
|
||||
1. Prepare
|
||||
1. Scan
|
||||
1. TUI(Terminal-Based User Interface)
|
||||
|
||||
## Step1. Launch Amazon Linux
|
||||
|
||||
- We are using the old AMI (amzn-ami-hvm-2015.09.1.x86_64-gp2 - ami-383c1956) for this example
|
||||
- Add the following to the cloud-init, to avoid auto-update at the first launch.
|
||||
|
||||
```
|
||||
#cloud-config
|
||||
repo_upgrade: none
|
||||
```
|
||||
|
||||
- [Q: How do I disable the automatic installation of critical and important security updates on initial launch?](https://aws.amazon.com/amazon-linux-ami/faqs/?nc1=h_ls)
|
||||
|
||||
## Step2. SSH setting
|
||||
|
||||
This is required to ssh to itself.
|
||||
|
||||
Create a keypair then append public key to authorized_keys
|
||||
```bash
|
||||
$ ssh-keygen -t rsa
|
||||
$ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
|
||||
$ chmod 600 ~/.ssh/authorized_keys
|
||||
```
|
||||
|
||||
## Step3. Install requirements
|
||||
|
||||
Vuls requires the following packages.
|
||||
|
||||
- SQLite3
|
||||
- git
|
||||
- gcc
|
||||
- go v1.6
|
||||
- https://golang.org/doc/install
|
||||
|
||||
```bash
|
||||
$ ssh ec2-user@52.100.100.100 -i ~/.ssh/private.pem
|
||||
$ sudo yum -y install sqlite git gcc
|
||||
$ wget https://storage.googleapis.com/golang/go1.6.linux-amd64.tar.gz
|
||||
$ sudo tar -C /usr/local -xzf go1.6.linux-amd64.tar.gz
|
||||
$ mkdir $HOME/go
|
||||
```
|
||||
Add these lines into /etc/profile.d/goenv.sh
|
||||
|
||||
```bash
|
||||
export GOROOT=/usr/local/go
|
||||
export GOPATH=$HOME/go
|
||||
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
|
||||
```
|
||||
|
||||
Set the OS environment variable to current shell
|
||||
```bash
|
||||
$ source /etc/profile.d/goenv.sh
|
||||
```
|
||||
|
||||
## Step4. Deploy [go-cve-dictionary](https://github.com/kotakanbe/go-cve-dictionary)
|
||||
|
||||
go get
|
||||
|
||||
```bash
|
||||
$ sudo mkdir /var/log/vuls
|
||||
$ sudo chown ec2-user /var/log/vuls
|
||||
$ sudo chmod 700 /var/log/vuls
|
||||
$ go get github.com/kotakanbe/go-cve-dictionary
|
||||
```
|
||||
|
||||
Fetch vulnerability data from NVD.
|
||||
It takes about 10 minutes (on AWS).
|
||||
|
||||
```bash
|
||||
$ for i in {2002..2016}; do go-cve-dictionary fetchnvd -years $i; done
|
||||
... snip ...
|
||||
$ ls -alh cve.sqlite3
|
||||
-rw-r--r-- 1 ec2-user ec2-user 7.0M Mar 24 13:20 cve.sqlite3
|
||||
```
|
||||
|
||||
Now we successfully collected vulnerbility data, then start as server.
|
||||
```bash
|
||||
$ go-cve-dictionary server
|
||||
[Mar 24 15:21:55] INFO Opening DB. datafile: /home/ec2-user/cve.sqlite3
|
||||
[Mar 24 15:21:55] INFO Migrating DB
|
||||
[Mar 24 15:21:56] INFO Starting HTTP Sever...
|
||||
[Mar 24 15:21:56] INFO Listening on 127.0.0.1:1323
|
||||
```
|
||||
|
||||
## Step5. Deploy vuls
|
||||
|
||||
Launch a new terminal, SSH to the ec2 instance.
|
||||
|
||||
go get
|
||||
```
|
||||
$ go get github.com/future-architect/vuls
|
||||
```
|
||||
|
||||
## Step6. Config
|
||||
|
||||
Create a config file(TOML format).
|
||||
|
||||
```
|
||||
$ cat config.toml
|
||||
[servers]
|
||||
|
||||
[servers.172-31-4-82]
|
||||
host = "172.31.4.82"
|
||||
port = "22"
|
||||
user = "ec2-user"
|
||||
keyPath = "/home/ec2-user/.ssh/id_rsa"
|
||||
```
|
||||
|
||||
## Step7. Setting up target servers for vuls
|
||||
|
||||
```
|
||||
$ vuls prepare
|
||||
```
|
||||
see [Usage: Prepare](https://github.com/future-architect/vuls#usage-prepare)
|
||||
|
||||
## Step8. Start Scanning
|
||||
|
||||
```
|
||||
$ vuls scan
|
||||
INFO[0000] Begin scanning (config: /home/ec2-user/config.toml)
|
||||
|
||||
... snip ...
|
||||
|
||||
172-31-4-82 (amazon 2015.09)
|
||||
============================
|
||||
CVE-2016-0494 10.0 Unspecified vulnerability in the Java SE and Java SE Embedded components in Oracle
|
||||
Java SE 6u105, 7u91, and 8u66 and Java SE Embedded 8u65 allows remote attackers to
|
||||
affect confidentiality, integrity, and availability via unknown vectors related to
|
||||
2D.
|
||||
... snip ...
|
||||
|
||||
CVE-2016-0494
|
||||
-------------
|
||||
Score 10.0 (High)
|
||||
Vector (AV:N/AC:L/Au:N/C:C/I:C/A:C)
|
||||
Summary Unspecified vulnerability in the Java SE and Java SE Embedded components in Oracle Java SE 6u105,
|
||||
7u91, and 8u66 and Java SE Embedded 8u65 allows remote attackers to affect confidentiality,
|
||||
integrity, and availability via unknown vectors related to 2D.
|
||||
NVD https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-0494
|
||||
MITRE https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-0494
|
||||
CVE Details http://www.cvedetails.com/cve/CVE-2016-0494
|
||||
CVSS Claculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-0494&vector=(AV:N/AC:L/Au:N/C:C/I:C/A:C)
|
||||
RHEL-CVE https://access.redhat.com/security/cve/CVE-2016-0494
|
||||
ALAS-2016-643 https://alas.aws.amazon.com/ALAS-2016-643.html
|
||||
Package/CPE java-1.7.0-openjdk-1.7.0.91-2.6.2.2.63.amzn1 -> java-1.7.0-openjdk-1:1.7.0.95-2.6.4.0.65.amzn1
|
||||
|
||||
```
|
||||
|
||||
## Step9. TUI
|
||||
|
||||
Vuls has Terminal-Based User Interface to display the scan result.
|
||||
|
||||
```
|
||||
$ vuls tui
|
||||
```
|
||||
|
||||

|
||||
## Document
|
||||
|
||||
For more information such as Installation, Tutorial, Usage, visit [vuls.io](https://vuls.io/)
|
||||
[日本語翻訳ドキュメント](https://vuls.io/ja/)
|
||||
|
||||
----
|
||||
|
||||
# Hello Vuls in a docker container
|
||||
|
||||
see https://github.com/future-architect/vuls/tree/master/docker
|
||||
|
||||
----
|
||||
|
||||
# Architecture
|
||||
|
||||

|
||||
|
||||
## [go-cve-dictinary](https://github.com/kotakanbe/go-cve-dictionary)
|
||||
- Fetch vulnerability information from NVD, JVN(Japanese), then insert into SQLite3.
|
||||
|
||||
## Vuls
|
||||
- Scan vulnerabilities on the servers and create a list of the CVE ID
|
||||
- For more detailed information of the detected CVE, send HTTP request to go-cve-dictinary
|
||||
- Send a report by Slack, Email
|
||||
- System operator can view the latest report by terminal
|
||||
|
||||
----
|
||||
|
||||
# Use Cases
|
||||
|
||||
## Scan all servers
|
||||
|
||||

|
||||
|
||||
## Scan a single server
|
||||
|
||||
web/app server in the same configuration under the load balancer
|
||||
|
||||

|
||||
|
||||
----
|
||||
|
||||
# Support OS
|
||||
|
||||
| Distribution| Release |
|
||||
|:------------|-------------------:|
|
||||
| Ubuntu | 12, 14, 16|
|
||||
| Debian | 7, 8|
|
||||
| RHEL | 4, 5, 6, 7|
|
||||
| CentOS | 5, 6, 7|
|
||||
| Amazon Linux| All |
|
||||
|
||||
----
|
||||
|
||||
|
||||
# Usage: Automatic Server Discovery
|
||||
|
||||
Discovery subcommand discovers active servers specified in CIDR range, then print the template of config file(TOML format) to terminal.
|
||||
|
||||
```
|
||||
$ vuls discover -help
|
||||
discover:
|
||||
discover 192.168.0.0/24
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```
|
||||
$ vuls discover 172.31.4.0/24
|
||||
# Create config.toml using below and then ./vuls --config=/path/to/config.toml
|
||||
|
||||
[slack]
|
||||
hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
|
||||
channel = "#channel-name"
|
||||
#channel = "#{servername}"
|
||||
iconEmoji = ":ghost:"
|
||||
authUser = "username"
|
||||
notifyUsers = ["@username"]
|
||||
|
||||
[mail]
|
||||
smtpAddr = "smtp.gmail.com"
|
||||
smtpPort = 465
|
||||
user = "username"
|
||||
password = "password"
|
||||
from = "from@address.com"
|
||||
to = ["to@address.com"]
|
||||
cc = ["cc@address.com"]
|
||||
subjectPrefix = "[vuls]"
|
||||
|
||||
[default]
|
||||
#port = "22"
|
||||
#user = "username"
|
||||
#keyPath = "/home/username/.ssh/id_rsa"
|
||||
|
||||
[servers]
|
||||
|
||||
[servers.172-31-4-82]
|
||||
host = "172.31.4.82"
|
||||
#port = "22"
|
||||
#user = "root"
|
||||
#keyPath = "/home/username/.ssh/id_rsa"
|
||||
#cpeNames = [
|
||||
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
#]
|
||||
```
|
||||
|
||||
You can customize your configuration using this template.
|
||||
|
||||
----
|
||||
|
||||
# Configuration
|
||||
|
||||
- Slack section
|
||||
```
|
||||
[slack]
|
||||
hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
|
||||
channel = "#channel-name"
|
||||
#channel = "#{servername}"
|
||||
iconEmoji = ":ghost:"
|
||||
authUser = "username"
|
||||
notifyUsers = ["@username"]
|
||||
```
|
||||
|
||||
- hookURL : Incomming webhook's URL
|
||||
- channel : channel name.
|
||||
If you set #{servername} to channel, the report will be sent to #servername channel.
|
||||
In the following example, the report will be sent to the #server1 and #server2.
|
||||
Be sure to create these channels before scanning.
|
||||
```
|
||||
[slack]
|
||||
channel = "#{servername}"
|
||||
...snip...
|
||||
|
||||
[servers]
|
||||
|
||||
[servers.server1]
|
||||
host = "172.31.4.82"
|
||||
...snip...
|
||||
|
||||
[servers.server2]
|
||||
host = "172.31.4.83"
|
||||
...snip...
|
||||
```
|
||||
|
||||
- iconEmoji: emoji
|
||||
- authUser: username of the slack team
|
||||
- notifyUsers: a list of Slack usernames to send Slack notifications.
|
||||
If you set ["@foo", "@bar"] to notifyUsers, @foo @bar will be included in text.
|
||||
So @foo, @bar can receive mobile push notifications on their smartphone.
|
||||
|
||||
- Mail section
|
||||
```
|
||||
[mail]
|
||||
smtpAddr = "smtp.gmail.com"
|
||||
smtpPort = 465
|
||||
user = "username"
|
||||
password = "password"
|
||||
from = "from@address.com"
|
||||
to = ["to@address.com"]
|
||||
cc = ["cc@address.com"]
|
||||
subjectPrefix = "[vuls]"
|
||||
```
|
||||
|
||||
- Default section
|
||||
```
|
||||
[default]
|
||||
#port = "22"
|
||||
#user = "username"
|
||||
#keyPath = "/home/username/.ssh/id_rsa"
|
||||
```
|
||||
Items of the default section will be used if not specified.
|
||||
|
||||
- servers section
|
||||
```
|
||||
[servers]
|
||||
|
||||
[servers.172-31-4-82]
|
||||
host = "172.31.4.82"
|
||||
#port = "22"
|
||||
#user = "root"
|
||||
#keyPath = "/home/username/.ssh/id_rsa"
|
||||
#cpeNames = [
|
||||
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
#]
|
||||
```
|
||||
You can overwrite the default value specified in default section.
|
||||
Vuls supports multiple SSH authentication methods.
|
||||
- SSH agent
|
||||
- SSH public key authentication (with password, empty password)
|
||||
- Password authentication
|
||||
|
||||
----
|
||||
|
||||
# Usage: Prepare
|
||||
|
||||
Prepare subcommand installs required packages on each server.
|
||||
|
||||
| Distribution| Release | Requirements |
|
||||
|:------------|-------------------:|:-------------|
|
||||
| Ubuntu | 12, 14, 16| - |
|
||||
| Debian | 7, 8| aptitude |
|
||||
| CentOS | 5| yum-plugin-security, yum-changelog |
|
||||
| CentOS | 6, 7| yum-plugin-security, yum-plugin-changelog |
|
||||
| Amazon | All | - |
|
||||
| RHEL | 4, 5, 6, 7 | - |
|
||||
|
||||
|
||||
```
|
||||
$ vuls prepare -help
|
||||
prepare
|
||||
[-config=/path/to/config.toml] [-debug]
|
||||
[-ask-sudo-password]
|
||||
[-ask-key-password]
|
||||
|
||||
-ask-key-password
|
||||
Ask ssh privatekey password before scanning
|
||||
-ask-sudo-password
|
||||
Ask sudo password of target servers before scanning
|
||||
-config string
|
||||
/path/to/toml (default "$PWD/config.toml")
|
||||
-debug
|
||||
debug mode
|
||||
-use-unattended-upgrades
|
||||
[Deprecated] For Ubuntu, install unattended-upgrades
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
# Usage: Scan
|
||||
|
||||
```
|
||||
|
||||
$ vuls scan -help
|
||||
scan:
|
||||
scan
|
||||
[-lang=en|ja]
|
||||
[-config=/path/to/config.toml]
|
||||
[-dbpath=/path/to/vuls.sqlite3]
|
||||
[-cve-dictionary-url=http://127.0.0.1:1323]
|
||||
[-cvss-over=7]
|
||||
[-report-slack]
|
||||
[-report-mail]
|
||||
[-http-proxy=http://192.168.0.1:8080]
|
||||
[-ask-sudo-password]
|
||||
[-ask-key-password]
|
||||
[-debug]
|
||||
[-debug-sql]
|
||||
-ask-key-password
|
||||
Ask ssh privatekey password before scanning
|
||||
-ask-sudo-password
|
||||
Ask sudo password of target servers before scanning
|
||||
-config string
|
||||
/path/to/toml (default "$PWD/config.toml")
|
||||
-cve-dictionary-url string
|
||||
http://CVE.Dictionary (default "http://127.0.0.1:1323")
|
||||
-cvss-over float
|
||||
-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))
|
||||
-dbpath string
|
||||
/path/to/sqlite3 (default "$PWD/vuls.sqlite3")
|
||||
-debug
|
||||
debug mode
|
||||
-debug-sql
|
||||
SQL debug mode
|
||||
-http-proxy string
|
||||
http://proxy-url:port (default: empty)
|
||||
-lang string
|
||||
[en|ja] (default "en")
|
||||
-report-mail
|
||||
Email report
|
||||
-report-slack
|
||||
Slack report
|
||||
-use-unattended-upgrades
|
||||
[Deprecated] For Ubuntu. Scan by unattended-upgrades or not (use apt-get upgrade --dry-run by default)
|
||||
-use-yum-plugin-security
|
||||
[Deprecated] For CentOS 5. Scan by yum-plugin-security or not (use yum check-update by default)
|
||||
|
||||
```
|
||||
|
||||
## ask-key-password option
|
||||
|
||||
| SSH key password | -ask-key-password | |
|
||||
|:-----------------|:-------------------|:----|
|
||||
| empty password | - | |
|
||||
| with password | required | or use ssh-agent |
|
||||
|
||||
## ask-sudo-password option
|
||||
|
||||
| sudo password on target servers | -ask-sudo-password | |
|
||||
|:-----------------|:-------|:------|
|
||||
| NOPASSWORD | - | defined as NOPASSWORD in /etc/sudoers on target servers |
|
||||
| with password | required | . |
|
||||
|
||||
|
||||
## example
|
||||
|
||||
Run go-cve-dictionary as server mode before scanning.
|
||||
```
|
||||
$ go-cve-dictionary server
|
||||
```
|
||||
|
||||
### Scan all servers defined in config file
|
||||
```
|
||||
$ vuls scan --report-slack --report-mail --cvss-over=7 -ask-sudo-password -ask-key-password
|
||||
```
|
||||
With this sample command, it will ..
|
||||
- Ask sudo password and ssh key passsword before scanning
|
||||
- Scan all servers defined in config file
|
||||
- Send scan results to slack and email
|
||||
- Only Report CVEs that CVSS score is over 7
|
||||
- Print scan result to terminal
|
||||
|
||||
### Scan specific servers
|
||||
```
|
||||
$ vuls scan server1 server2
|
||||
```
|
||||
With this sample command, it will ..
|
||||
- Use SSH Key-Based authentication with empty password (without -ask-key-password option)
|
||||
- Sudo with no password (without -ask-sudo-password option)
|
||||
- Scan only 2 servers (server1, server2)
|
||||
- Print scan result to terminal
|
||||
|
||||
----
|
||||
|
||||
# Usage: Scan vulnerability of non-OS package
|
||||
|
||||
It is possible to detect vulnerabilities something you compiled by yourself, the language libraries and the frameworks that have been registered in the [CPE](https://nvd.nist.gov/cpe.cfm).
|
||||
|
||||
- How to search CPE name by software name
|
||||
- [NVD: Search Common Platform Enumerations (CPE)](https://web.nvd.nist.gov/view/cpe/search)
|
||||
**Check CPE Naming Format: 2.2**
|
||||
|
||||
- [go-cpe-dictionary](https://github.com/kotakanbe/go-cpe-dictionary) is a good choice for geeks.
|
||||
You can search a CPE name by the application name incremenally.
|
||||
|
||||
- Configuration
|
||||
To detect the vulnerbility of Ruby on Rails v4.2.1, cpeNames needs to be set in the servers section.
|
||||
```
|
||||
[servers]
|
||||
|
||||
[servers.172-31-4-82]
|
||||
host = "172.31.4.82"
|
||||
user = "ec2-user"
|
||||
keyPath = "/home/username/.ssh/id_rsa"
|
||||
cpeNames = [
|
||||
"cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
]
|
||||
```
|
||||
|
||||
# Usage: Update NVD Data.
|
||||
|
||||
```
|
||||
$ go-cve-dictionary fetchnvd -h
|
||||
fetchnvd:
|
||||
fetchnvd
|
||||
[-last2y]
|
||||
[-dbpath=/path/to/cve.sqlite3]
|
||||
[-debug]
|
||||
[-debug-sql]
|
||||
|
||||
-dbpath string
|
||||
/path/to/sqlite3 (default "$PWD/cve.sqlite3")
|
||||
-debug
|
||||
debug mode
|
||||
-debug-sql
|
||||
SQL debug mode
|
||||
-last2y
|
||||
Refresh NVD data in the last two years.
|
||||
```
|
||||
|
||||
- Fetch data of the entire period
|
||||
|
||||
```
|
||||
$ go-cve-dictionary fetchnvd -entire
|
||||
```
|
||||
|
||||
- Fetch data in the last 2 years
|
||||
|
||||
```
|
||||
$ go-cve-dictionary fetchnvd -last2y
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
# Misc
|
||||
|
||||
- HTTP Proxy Support
|
||||
If your system is behind HTTP proxy, you have to specify --http-proxy option.
|
||||
|
||||
- How to Daemonize go-cve-dictionary
|
||||
Use Systemd, Upstart or supervisord, daemontools...
|
||||
|
||||
- How to Enable Automatic-Update of Vunerability Data.
|
||||
Use job scheduler like Cron (with -last2y option).
|
||||
|
||||
- How to Enable Automatic-Scan.
|
||||
Use job scheduler like Cron.
|
||||
Set NOPASSWORD option in /etc/sudoers on target servers.
|
||||
Use SSH Key-Based Authentication with empty password or ssh-agent.
|
||||
|
||||
- How to cross compile
|
||||
```bash
|
||||
$ cd /path/to/your/local-git-reporsitory/vuls
|
||||
$ GOOS=linux GOARCH=amd64 go build -o vuls.amd64
|
||||
```
|
||||
|
||||
- Logging
|
||||
Log wrote to under /var/log/vuls/
|
||||
|
||||
- Debug
|
||||
Run with --debug, --sql-debug option.
|
||||
|
||||
- Ajusting Open File Limit
|
||||
[Riak docs](http://docs.basho.com/riak/latest/ops/tuning/open-files-limit/) is awesome.
|
||||
|
||||
- Does Vuls accept ssh connections with fish-shell or old zsh as the login shell?
|
||||
No, Vuls needs a user on the server for bash login. see also [#8](/../../issues/8)
|
||||
|
||||
- Windows
|
||||
Use Microsoft Baseline Security Analyzer. [MBSA](https://technet.microsoft.com/en-us/security/cc184924.aspx)
|
||||
|
||||
----
|
||||
|
||||
# Data Source
|
||||
|
||||
- [NVD](https://nvd.nist.gov/)
|
||||
- [JVN(Japanese)](http://jvndb.jvn.jp/apis/myjvn/)
|
||||
|
||||
|
||||
# Authors
|
||||
## Authors
|
||||
|
||||
kotakanbe ([@kotakanbe](https://twitter.com/kotakanbe)) created vuls and [these fine people](https://github.com/future-architect/vuls/graphs/contributors) have contributed.
|
||||
|
||||
----
|
||||
|
||||
# Contribute
|
||||
|
||||
1. Fork it
|
||||
2. Create your feature branch (`git checkout -b my-new-feature`)
|
||||
3. Commit your changes (`git commit -am 'Add some feature'`)
|
||||
4. Push to the branch (`git push origin my-new-feature`)
|
||||
5. Create new Pull Request
|
||||
|
||||
----
|
||||
|
||||
# Change Log
|
||||
## Change Log
|
||||
|
||||
Please see [CHANGELOG](https://github.com/future-architect/vuls/blob/master/CHANGELOG.md).
|
||||
|
||||
----
|
||||
|
||||
# Licence
|
||||
## Stargazers over time
|
||||
|
||||
[](https://starcharts.herokuapp.com/future-architect/vuls)
|
||||
|
||||
-----;
|
||||
|
||||
## License
|
||||
|
||||
Please see [LICENSE](https://github.com/future-architect/vuls/blob/master/LICENSE).
|
||||
|
||||
|
||||
[](https://bitdeli.com/free "Bitdeli Badge")
|
||||
|
||||
|
||||
171
cache/bolt.go
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// Bolt holds a pointer of bolt.DB
|
||||
// boltdb is used to store a cache of Changelogs of Ubuntu/Debian
|
||||
type Bolt struct {
|
||||
Path string
|
||||
Log *logrus.Entry
|
||||
db *bolt.DB
|
||||
}
|
||||
|
||||
// SetupBolt opens a boltdb and creates a meta bucket if not exists.
|
||||
func SetupBolt(path string, l *logrus.Entry) error {
|
||||
l.Infof("Open boltDB: %s", path)
|
||||
db, err := bolt.Open(path, 0600, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b := Bolt{
|
||||
Path: path,
|
||||
Log: l,
|
||||
db: db,
|
||||
}
|
||||
if err = b.createBucketIfNotExists(metabucket); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
DB = b
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close a db.
|
||||
func (b Bolt) Close() error {
|
||||
if b.db == nil {
|
||||
return nil
|
||||
}
|
||||
return b.db.Close()
|
||||
}
|
||||
|
||||
// CreateBucketIfNotExists creates a buket that is specified by arg.
|
||||
func (b *Bolt) createBucketIfNotExists(name string) error {
|
||||
return b.db.Update(func(tx *bolt.Tx) error {
|
||||
_, err := tx.CreateBucketIfNotExists([]byte(name))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to create bucket: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// GetMeta gets a Meta Information os the servername to boltdb.
|
||||
func (b Bolt) GetMeta(serverName string) (meta Meta, found bool, err error) {
|
||||
err = b.db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket([]byte(metabucket))
|
||||
v := bkt.Get([]byte(serverName))
|
||||
if len(v) == 0 {
|
||||
found = false
|
||||
return nil
|
||||
}
|
||||
if e := json.Unmarshal(v, &meta); e != nil {
|
||||
return e
|
||||
}
|
||||
found = true
|
||||
return nil
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// RefreshMeta gets a Meta Information os the servername to boltdb.
|
||||
func (b Bolt) RefreshMeta(meta Meta) error {
|
||||
meta.CreatedAt = time.Now()
|
||||
jsonBytes, err := json.Marshal(meta)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to marshal to JSON: %w", err)
|
||||
}
|
||||
return b.db.Update(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket([]byte(metabucket))
|
||||
if err := bkt.Put([]byte(meta.Name), jsonBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
b.Log.Debugf("Refreshed Meta: %s", meta.Name)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// EnsureBuckets puts a Meta information and create a buket that holds changelogs.
|
||||
func (b Bolt) EnsureBuckets(meta Meta) error {
|
||||
jsonBytes, err := json.Marshal(meta)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to marshal to JSON: %w", err)
|
||||
}
|
||||
return b.db.Update(func(tx *bolt.Tx) error {
|
||||
b.Log.Debugf("Put to meta: %s", meta.Name)
|
||||
bkt := tx.Bucket([]byte(metabucket))
|
||||
if err := bkt.Put([]byte(meta.Name), jsonBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// re-create a bucket (bucket name: servername)
|
||||
bkt = tx.Bucket([]byte(meta.Name))
|
||||
if bkt != nil {
|
||||
b.Log.Debugf("Delete bucket: %s", meta.Name)
|
||||
if err := tx.DeleteBucket([]byte(meta.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
b.Log.Debugf("Bucket deleted: %s", meta.Name)
|
||||
}
|
||||
b.Log.Debugf("Create bucket: %s", meta.Name)
|
||||
if _, err := tx.CreateBucket([]byte(meta.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
b.Log.Debugf("Bucket created: %s", meta.Name)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// PrettyPrint is for debug
|
||||
func (b Bolt) PrettyPrint(meta Meta) error {
|
||||
return b.db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket([]byte(metabucket))
|
||||
v := bkt.Get([]byte(meta.Name))
|
||||
b.Log.Debugf("Meta: key:%s, value:%s", meta.Name, v)
|
||||
|
||||
bkt = tx.Bucket([]byte(meta.Name))
|
||||
c := bkt.Cursor()
|
||||
for k, v := c.First(); k != nil; k, v = c.Next() {
|
||||
b.Log.Debugf("key:%s, len: %d, %s...",
|
||||
k, len(v), util.Truncate(string(v), 30))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// GetChangelog get the changelog of specified packName from the Bucket
|
||||
func (b Bolt) GetChangelog(servername, packName string) (changelog string, err error) {
|
||||
err = b.db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket([]byte(servername))
|
||||
if bkt == nil {
|
||||
return xerrors.Errorf("Failed to get Bucket: %s", servername)
|
||||
}
|
||||
v := bkt.Get([]byte(packName))
|
||||
if v == nil {
|
||||
changelog = ""
|
||||
return nil
|
||||
}
|
||||
changelog = string(v)
|
||||
return nil
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// PutChangelog put the changelgo of specified packName into the Bucket
|
||||
func (b Bolt) PutChangelog(servername, packName, changelog string) error {
|
||||
return b.db.Update(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket([]byte(servername))
|
||||
if bkt == nil {
|
||||
return xerrors.Errorf("Failed to get Bucket: %s", servername)
|
||||
}
|
||||
return bkt.Put([]byte(packName), []byte(changelog))
|
||||
})
|
||||
}
|
||||
120
cache/bolt_test.go
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const path = "/tmp/vuls-test-cache-11111111.db"
|
||||
const servername = "server1"
|
||||
|
||||
var meta = Meta{
|
||||
Name: servername,
|
||||
Distro: config.Distro{
|
||||
Family: "ubuntu",
|
||||
Release: "16.04",
|
||||
},
|
||||
Packs: models.Packages{
|
||||
"apt": {
|
||||
Name: "apt",
|
||||
Version: "1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestSetupBolt(t *testing.T) {
|
||||
log := logrus.NewEntry(&logrus.Logger{})
|
||||
err := SetupBolt(path, log)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to setup bolt: %s", err)
|
||||
}
|
||||
defer os.Remove(path)
|
||||
|
||||
if err := DB.Close(); err != nil {
|
||||
t.Errorf("Failed to close bolt: %s", err)
|
||||
}
|
||||
|
||||
// check if meta bucket exists
|
||||
db, err := bolt.Open(path, 0600, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to open bolt: %s", err)
|
||||
}
|
||||
|
||||
_ = db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket([]byte(metabucket))
|
||||
if bkt == nil {
|
||||
t.Errorf("Meta bucket nof found")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestEnsureBuckets(t *testing.T) {
|
||||
log := logrus.NewEntry(&logrus.Logger{})
|
||||
if err := SetupBolt(path, log); err != nil {
|
||||
t.Errorf("Failed to setup bolt: %s", err)
|
||||
}
|
||||
if err := DB.EnsureBuckets(meta); err != nil {
|
||||
t.Errorf("Failed to ensure buckets: %s", err)
|
||||
}
|
||||
defer os.Remove(path)
|
||||
|
||||
m, found, err := DB.GetMeta(servername)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get meta: %s", err)
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Not Found in meta")
|
||||
}
|
||||
if meta.Name != m.Name || meta.Distro != m.Distro {
|
||||
t.Errorf("expected %v, actual %v", meta, m)
|
||||
}
|
||||
if !reflect.DeepEqual(meta.Packs, m.Packs) {
|
||||
t.Errorf("expected %v, actual %v", meta.Packs, m.Packs)
|
||||
}
|
||||
if err := DB.Close(); err != nil {
|
||||
t.Errorf("Failed to close bolt: %s", err)
|
||||
}
|
||||
|
||||
db, err := bolt.Open(path, 0600, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to open bolt: %s", err)
|
||||
}
|
||||
_ = db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket([]byte(servername))
|
||||
if bkt == nil {
|
||||
t.Errorf("Meta bucket nof found")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func TestPutGetChangelog(t *testing.T) {
|
||||
clog := "changelog-text"
|
||||
log := logrus.NewEntry(&logrus.Logger{})
|
||||
if err := SetupBolt(path, log); err != nil {
|
||||
t.Errorf("Failed to setup bolt: %s", err)
|
||||
}
|
||||
defer os.Remove(path)
|
||||
|
||||
if err := DB.EnsureBuckets(meta); err != nil {
|
||||
t.Errorf("Failed to ensure buckets: %s", err)
|
||||
}
|
||||
if err := DB.PutChangelog(servername, "apt", clog); err != nil {
|
||||
t.Errorf("Failed to put changelog: %s", err)
|
||||
}
|
||||
if actual, err := DB.GetChangelog(servername, "apt"); err != nil {
|
||||
t.Errorf("Failed to get changelog: %s", err)
|
||||
} else {
|
||||
if actual != clog {
|
||||
t.Errorf("changelog is not same. e: %s, a: %s", clog, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
33
cache/db.go
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
)
|
||||
|
||||
// DB has a cache instance
|
||||
var DB Cache
|
||||
|
||||
const metabucket = "changelog-meta"
|
||||
|
||||
// Cache is a interface of cache
|
||||
type Cache interface {
|
||||
Close() error
|
||||
GetMeta(string) (Meta, bool, error)
|
||||
RefreshMeta(Meta) error
|
||||
EnsureBuckets(Meta) error
|
||||
PrettyPrint(Meta) error
|
||||
GetChangelog(string, string) (string, error)
|
||||
PutChangelog(string, string, string) error
|
||||
}
|
||||
|
||||
// Meta holds a server name, distro information of the scanned server and
|
||||
// package information that was collected at the last scan.
|
||||
type Meta struct {
|
||||
Name string
|
||||
Distro config.Distro
|
||||
Packs models.Packages
|
||||
CreatedAt time.Time
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/howeyc/gopass"
|
||||
)
|
||||
|
||||
func getPasswd(prompt string) (string, error) {
|
||||
for {
|
||||
fmt.Print(prompt)
|
||||
pass, err := gopass.GetPasswdMasked()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to read password")
|
||||
}
|
||||
if 0 < len(pass) {
|
||||
return string(pass[:]), nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
173
commands/configtest.go
Normal file
@@ -0,0 +1,173 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/google/subcommands"
|
||||
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/scan"
|
||||
"github.com/future-architect/vuls/util"
|
||||
)
|
||||
|
||||
// ConfigtestCmd is Subcommand
|
||||
type ConfigtestCmd struct {
|
||||
configPath string
|
||||
askKeyPassword bool
|
||||
timeoutSec int
|
||||
}
|
||||
|
||||
// Name return subcommand name
|
||||
func (*ConfigtestCmd) Name() string { return "configtest" }
|
||||
|
||||
// Synopsis return synopsis
|
||||
func (*ConfigtestCmd) Synopsis() string { return "Test configuration" }
|
||||
|
||||
// Usage return usage
|
||||
func (*ConfigtestCmd) Usage() string {
|
||||
return `configtest:
|
||||
configtest
|
||||
[-config=/path/to/config.toml]
|
||||
[-log-dir=/path/to/log]
|
||||
[-ask-key-password]
|
||||
[-timeout=300]
|
||||
[-ssh-config]
|
||||
[-containers-only]
|
||||
[-http-proxy=http://192.168.0.1:8080]
|
||||
[-debug]
|
||||
[-vvv]
|
||||
|
||||
[SERVER]...
|
||||
`
|
||||
}
|
||||
|
||||
// SetFlags set flag
|
||||
func (p *ConfigtestCmd) SetFlags(f *flag.FlagSet) {
|
||||
wd, _ := os.Getwd()
|
||||
defaultConfPath := filepath.Join(wd, "config.toml")
|
||||
f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
|
||||
|
||||
defaultLogDir := util.GetDefaultLogDir()
|
||||
f.StringVar(&c.Conf.LogDir, "log-dir", defaultLogDir, "/path/to/log")
|
||||
f.BoolVar(&c.Conf.Debug, "debug", false, "debug mode")
|
||||
|
||||
f.IntVar(&p.timeoutSec, "timeout", 5*60, "Timeout(Sec)")
|
||||
|
||||
f.BoolVar(&p.askKeyPassword, "ask-key-password", false,
|
||||
"Ask ssh privatekey password before scanning",
|
||||
)
|
||||
|
||||
f.StringVar(&c.Conf.HTTPProxy, "http-proxy", "",
|
||||
"http://proxy-url:port (default: empty)")
|
||||
|
||||
f.BoolVar(&c.Conf.SSHNative, "ssh-native-insecure", false,
|
||||
"Use Native Go implementation of SSH. Default: Use the external command")
|
||||
|
||||
f.BoolVar(&c.Conf.SSHConfig, "ssh-config", false,
|
||||
"[Deprecated] Use SSH options specified in ssh_config preferentially")
|
||||
|
||||
f.BoolVar(&c.Conf.ContainersOnly, "containers-only", false,
|
||||
"Test containers only. Default: Test both of hosts and containers")
|
||||
|
||||
f.BoolVar(&c.Conf.Vvv, "vvv", false, "ssh -vvv")
|
||||
}
|
||||
|
||||
// Execute execute
|
||||
func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
util.Log = util.NewCustomLogger(c.ServerInfo{})
|
||||
|
||||
if err := mkdirDotVuls(); err != nil {
|
||||
util.Log.Errorf("Failed to create .vuls. err: %+v", err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
var keyPass string
|
||||
var err error
|
||||
if p.askKeyPassword {
|
||||
prompt := "SSH key password: "
|
||||
if keyPass, err = getPasswd(prompt); err != nil {
|
||||
util.Log.Error(err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
err = c.Load(p.configPath, keyPass)
|
||||
if err != nil {
|
||||
msg := []string{
|
||||
fmt.Sprintf("Error loading %s", p.configPath),
|
||||
"If you update Vuls and get this error, there may be incompatible changes in config.toml",
|
||||
"Please check config.toml template : https://vuls.io/docs/en/usage-settings.html",
|
||||
}
|
||||
util.Log.Errorf("%s\n%+v", strings.Join(msg, "\n"), err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
if c.Conf.SSHConfig {
|
||||
msg := []string{
|
||||
"-ssh-config is deprecated",
|
||||
"If you update Vuls and get this error, there may be incompatible changes in config.toml",
|
||||
"Please check config.toml template : https://vuls.io/docs/en/usage-settings.html",
|
||||
}
|
||||
util.Log.Errorf("%s", strings.Join(msg, "\n"))
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
var servernames []string
|
||||
if 0 < len(f.Args()) {
|
||||
servernames = f.Args()
|
||||
}
|
||||
|
||||
target := make(map[string]c.ServerInfo)
|
||||
for _, arg := range servernames {
|
||||
found := false
|
||||
for servername, info := range c.Conf.Servers {
|
||||
if servername == arg {
|
||||
target[servername] = info
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
util.Log.Errorf("%s is not in config", arg)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
}
|
||||
if 0 < len(servernames) {
|
||||
c.Conf.Servers = target
|
||||
}
|
||||
|
||||
util.Log.Info("Validating config...")
|
||||
if !c.Conf.ValidateOnConfigtest() {
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
util.Log.Info("Detecting Server/Container OS... ")
|
||||
if err := scan.InitServers(p.timeoutSec); err != nil {
|
||||
util.Log.Errorf("Failed to init servers. err: %+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
util.Log.Info("Checking Scan Modes...")
|
||||
if err := scan.CheckScanModes(); err != nil {
|
||||
util.Log.Errorf("Fix config.toml. err: %+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
util.Log.Info("Checking dependencies...")
|
||||
scan.CheckDependencies(p.timeoutSec)
|
||||
|
||||
util.Log.Info("Checking sudo settings...")
|
||||
scan.CheckIfSudoNoPasswd(p.timeoutSec)
|
||||
|
||||
util.Log.Info("It can be scanned with fast scan mode even if warn or err messages are displayed due to lack of dependent packages or sudo settings in fast-root or deep scan mode")
|
||||
|
||||
if scan.PrintSSHableServerNames() {
|
||||
return subcommands.ExitSuccess
|
||||
}
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
@@ -1,23 +1,7 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
@@ -25,10 +9,9 @@ import (
|
||||
"text/template"
|
||||
|
||||
"github.com/google/subcommands"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
ps "github.com/kotakanbe/go-pingscanner"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// DiscoverCmd is Subcommand of host discovery mode
|
||||
@@ -57,6 +40,7 @@ func (p *DiscoverCmd) SetFlags(f *flag.FlagSet) {
|
||||
func (p *DiscoverCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
// validate
|
||||
if len(f.Args()) == 0 {
|
||||
logrus.Errorf("Usage: " + p.Usage())
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
@@ -65,7 +49,6 @@ func (p *DiscoverCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface
|
||||
CIDR: cidr,
|
||||
PingOptions: []string{
|
||||
"-c1",
|
||||
"-t1",
|
||||
},
|
||||
NumOfConcurrency: 100,
|
||||
}
|
||||
@@ -87,48 +70,163 @@ func (p *DiscoverCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface
|
||||
return subcommands.ExitSuccess
|
||||
}
|
||||
|
||||
// Output the tmeplate of config.toml
|
||||
// Output the template of config.toml
|
||||
func printConfigToml(ips []string) (err error) {
|
||||
const tomlTempale = `
|
||||
[slack]
|
||||
hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
|
||||
channel = "#channel-name"
|
||||
#channel = "#{servername}"
|
||||
iconEmoji = ":ghost:"
|
||||
authUser = "username"
|
||||
notifyUsers = ["@username"]
|
||||
const tomlTemplate = `
|
||||
|
||||
[mail]
|
||||
smtpAddr = "smtp.gmail.com"
|
||||
smtpPort = 465
|
||||
user = "username"
|
||||
password = "password"
|
||||
from = "from@address.com"
|
||||
to = ["to@address.com"]
|
||||
cc = ["cc@address.com"]
|
||||
subjectPrefix = "[vuls]"
|
||||
# https://vuls.io/docs/en/usage-settings.html
|
||||
[cveDict]
|
||||
type = "sqlite3"
|
||||
sqlite3Path = "/path/to/cve.sqlite3"
|
||||
#url = ""
|
||||
|
||||
[ovalDict]
|
||||
type = "sqlite3"
|
||||
sqlite3Path = "/path/to/oval.sqlite3"
|
||||
#url = ""
|
||||
|
||||
[gost]
|
||||
type = "sqlite3"
|
||||
sqlite3Path = "/path/to/gost.sqlite3"
|
||||
#url = ""
|
||||
|
||||
[exploit]
|
||||
type = "sqlite3"
|
||||
sqlite3Path = "/path/to/go-exploitdb.sqlite3"
|
||||
#url = ""
|
||||
|
||||
[metasploit]
|
||||
type = "sqlite3"
|
||||
sqlite3Path = "/path/to/go-msfdb.sqlite3"
|
||||
#url = ""
|
||||
|
||||
# https://vuls.io/docs/en/usage-settings.html#slack-section
|
||||
#[slack]
|
||||
#hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
|
||||
##legacyToken = "xoxp-11111111111-222222222222-3333333333"
|
||||
#channel = "#channel-name"
|
||||
##channel = "${servername}"
|
||||
#iconEmoji = ":ghost:"
|
||||
#authUser = "username"
|
||||
#notifyUsers = ["@username"]
|
||||
|
||||
# https://vuls.io/docs/en/usage-settings.html#email-section
|
||||
#[email]
|
||||
#smtpAddr = "smtp.example.com"
|
||||
#smtpPort = "587"
|
||||
#user = "username"
|
||||
#password = "password"
|
||||
#from = "from@example.com"
|
||||
#to = ["to@example.com"]
|
||||
#cc = ["cc@example.com"]
|
||||
#subjectPrefix = "[vuls]"
|
||||
|
||||
# https://vuls.io/docs/en/usage-settings.html#http-section
|
||||
#[http]
|
||||
#url = "http://localhost:11234"
|
||||
|
||||
# https://vuls.io/docs/en/usage-settings.html#syslog-section
|
||||
#[syslog]
|
||||
#protocol = "tcp"
|
||||
#host = "localhost"
|
||||
#port = "514"
|
||||
#tag = "vuls"
|
||||
#facility = "local0"
|
||||
#severity = "alert"
|
||||
#verbose = false
|
||||
|
||||
# https://vuls.io/docs/en/usage-report.html#example-put-results-in-s3-bucket
|
||||
#[aws]
|
||||
#profile = "default"
|
||||
#region = "ap-northeast-1"
|
||||
#s3Bucket = "vuls"
|
||||
#s3ResultsDir = "/path/to/result"
|
||||
#s3ServerSideEncryption = "AES256"
|
||||
|
||||
# https://vuls.io/docs/en/usage-report.html#example-put-results-in-azure-blob-storage<Paste>
|
||||
#[azure]
|
||||
#accountName = "default"
|
||||
#accountKey = "xxxxxxxxxxxxxx"
|
||||
#containerName = "vuls"
|
||||
|
||||
# https://vuls.io/docs/en/usage-settings.html#stride-section
|
||||
#[stride]
|
||||
#hookURL = "xxxxxxxxxxxxxxx"
|
||||
#authToken = "xxxxxxxxxxxxxx"
|
||||
|
||||
# https://vuls.io/docs/en/usage-settings.html#hipchat-section
|
||||
#[hipchat]
|
||||
#room = "vuls"
|
||||
#authToken = "xxxxxxxxxxxxxx"
|
||||
|
||||
# https://vuls.io/docs/en/usage-settings.html#chatwork-section
|
||||
#[chatwork]
|
||||
#room = "xxxxxxxxxxx"
|
||||
#apiToken = "xxxxxxxxxxxxxxxxxx"
|
||||
|
||||
# https://vuls.io/docs/en/usage-settings.html#telegram-section
|
||||
#[telegram]
|
||||
#chatID = "xxxxxxxxxxx"
|
||||
#token = "xxxxxxxxxxxxxxxxxx"
|
||||
|
||||
# https://vuls.io/docs/en/usage-settings.html#default-section
|
||||
[default]
|
||||
#port = "22"
|
||||
#user = "username"
|
||||
#keyPath = "/home/username/.ssh/id_rsa"
|
||||
#port = "22"
|
||||
#user = "username"
|
||||
#keyPath = "/home/username/.ssh/id_rsa"
|
||||
#scanMode = ["fast", "fast-root", "deep", "offline"]
|
||||
#cpeNames = [
|
||||
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
#]
|
||||
#owaspDCXMLPath = "/tmp/dependency-check-report.xml"
|
||||
#ignoreCves = ["CVE-2014-6271"]
|
||||
#containerType = "docker" #or "lxd" or "lxc" default: docker
|
||||
#containersIncluded = ["${running}"]
|
||||
#containersExcluded = ["container_name_a"]
|
||||
|
||||
# https://vuls.io/docs/en/usage-settings.html#servers-section
|
||||
[servers]
|
||||
{{- $names:= .Names}}
|
||||
{{range $i, $ip := .IPs}}
|
||||
[servers.{{index $names $i}}]
|
||||
host = "{{$ip}}"
|
||||
#port = "22"
|
||||
#user = "root"
|
||||
#keyPath = "/home/username/.ssh/id_rsa"
|
||||
#cpeNames = [
|
||||
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
#]
|
||||
host = "{{$ip}}"
|
||||
#port = "22"
|
||||
#user = "root"
|
||||
#sshConfigPath = "/home/username/.ssh/config"
|
||||
#keyPath = "/home/username/.ssh/id_rsa"
|
||||
#scanMode = ["fast", "fast-root", "deep", "offline"]
|
||||
#type = "pseudo"
|
||||
#memo = "DB Server"
|
||||
#cpeNames = [ "cpe:/a:rubyonrails:ruby_on_rails:4.2.1" ]
|
||||
#owaspDCXMLPath = "/path/to/dependency-check-report.xml"
|
||||
#ignoreCves = ["CVE-2014-0160"]
|
||||
#containerType = "docker" #or "lxd" or "lxc" default: docker
|
||||
#containersIncluded = ["${running}"]
|
||||
#containersExcluded = ["container_name_a"]
|
||||
|
||||
#[servers.{{index $names $i}}.containers.container_name_a]
|
||||
#cpeNames = [ "cpe:/a:rubyonrails:ruby_on_rails:4.2.1" ]
|
||||
#owaspDCXMLPath = "/path/to/dependency-check-report.xml"
|
||||
#ignoreCves = ["CVE-2014-0160"]
|
||||
|
||||
#[servers.{{index $names $i}}.githubs."owner/repo"]
|
||||
#token = "yourToken"
|
||||
|
||||
#[servers.{{index $names $i}}.wordpress]
|
||||
#cmdPath = "/usr/local/bin/wp"
|
||||
#osUser = "wordpress"
|
||||
#docRoot = "/path/to/DocumentRoot/"
|
||||
#wpVulnDBToken = "xxxxTokenxxxx"
|
||||
#ignoreInactive = true
|
||||
|
||||
#[servers.{{index $names $i}}.optional]
|
||||
#key = "value1"
|
||||
|
||||
{{end}}
|
||||
|
||||
`
|
||||
var tpl *template.Template
|
||||
if tpl, err = template.New("tempalte").Parse(tomlTempale); err != nil {
|
||||
if tpl, err = template.New("template").Parse(tomlTemplate); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -146,7 +244,7 @@ host = "{{$ip}}"
|
||||
}
|
||||
a.Names = names
|
||||
|
||||
fmt.Println("# Create config.toml using below and then ./vuls --config=/path/to/config.toml")
|
||||
fmt.Println("# Create config.toml using below and then ./vuls -config=/path/to/config.toml")
|
||||
if err = tpl.Execute(os.Stdout, a); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
74
commands/history.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/report"
|
||||
"github.com/google/subcommands"
|
||||
)
|
||||
|
||||
// HistoryCmd is Subcommand of list scanned results
|
||||
type HistoryCmd struct{}
|
||||
|
||||
// Name return subcommand name
|
||||
func (*HistoryCmd) Name() string { return "history" }
|
||||
|
||||
// Synopsis return synopsis
|
||||
func (*HistoryCmd) Synopsis() string {
|
||||
return `List history of scanning.`
|
||||
}
|
||||
|
||||
// Usage return usage
|
||||
func (*HistoryCmd) Usage() string {
|
||||
return `history:
|
||||
history
|
||||
[-results-dir=/path/to/results]
|
||||
`
|
||||
}
|
||||
|
||||
// SetFlags set flag
|
||||
func (p *HistoryCmd) SetFlags(f *flag.FlagSet) {
|
||||
f.BoolVar(&c.Conf.DebugSQL, "debug-sql", false, "SQL debug mode")
|
||||
|
||||
wd, _ := os.Getwd()
|
||||
defaultResultsDir := filepath.Join(wd, "results")
|
||||
f.StringVar(&c.Conf.ResultsDir, "results-dir", defaultResultsDir, "/path/to/results")
|
||||
}
|
||||
|
||||
// Execute execute
|
||||
func (p *HistoryCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
|
||||
dirs, err := report.ListValidJSONDirs()
|
||||
if err != nil {
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
for _, d := range dirs {
|
||||
var files []os.FileInfo
|
||||
if files, err = ioutil.ReadDir(d); err != nil {
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
var hosts []string
|
||||
for _, f := range files {
|
||||
if filepath.Ext(f.Name()) != ".json" {
|
||||
continue
|
||||
}
|
||||
fileBase := strings.TrimSuffix(f.Name(), filepath.Ext(f.Name()))
|
||||
hosts = append(hosts, fileBase)
|
||||
}
|
||||
splitPath := strings.Split(d, string(os.PathSeparator))
|
||||
timeStr := splitPath[len(splitPath)-1]
|
||||
fmt.Printf("%s %d servers: %s\n",
|
||||
timeStr,
|
||||
len(hosts),
|
||||
strings.Join(hosts, ", "),
|
||||
)
|
||||
}
|
||||
return subcommands.ExitSuccess
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/scan"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/google/subcommands"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// PrepareCmd is Subcommand of host discovery mode
|
||||
type PrepareCmd struct {
|
||||
debug bool
|
||||
configPath string
|
||||
|
||||
askSudoPassword bool
|
||||
askKeyPassword bool
|
||||
|
||||
useUnattendedUpgrades bool
|
||||
}
|
||||
|
||||
// Name return subcommand name
|
||||
func (*PrepareCmd) Name() string { return "prepare" }
|
||||
|
||||
// Synopsis return synopsis
|
||||
func (*PrepareCmd) Synopsis() string {
|
||||
// return "Install packages Ubuntu: unattended-upgrade, CentOS: yum-plugin-security)"
|
||||
return `Install required packages to scan.
|
||||
CentOS: yum-plugin-security, yum-plugin-changelog
|
||||
Amazon: None
|
||||
RHEL: TODO
|
||||
Ubuntu: None
|
||||
|
||||
`
|
||||
}
|
||||
|
||||
// Usage return usage
|
||||
func (*PrepareCmd) Usage() string {
|
||||
return `prepare:
|
||||
prepare
|
||||
[-config=/path/to/config.toml]
|
||||
[-ask-sudo-password]
|
||||
[-ask-key-password]
|
||||
[-debug]
|
||||
|
||||
`
|
||||
}
|
||||
|
||||
// SetFlags set flag
|
||||
func (p *PrepareCmd) SetFlags(f *flag.FlagSet) {
|
||||
|
||||
f.BoolVar(&p.debug, "debug", false, "debug mode")
|
||||
|
||||
defaultConfPath := os.Getenv("PWD") + "/config.toml"
|
||||
f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
|
||||
|
||||
f.BoolVar(
|
||||
&p.askKeyPassword,
|
||||
"ask-key-password",
|
||||
false,
|
||||
"Ask ssh privatekey password before scanning",
|
||||
)
|
||||
|
||||
f.BoolVar(
|
||||
&p.askSudoPassword,
|
||||
"ask-sudo-password",
|
||||
false,
|
||||
"Ask sudo password of target servers before scanning",
|
||||
)
|
||||
|
||||
f.BoolVar(
|
||||
&p.useUnattendedUpgrades,
|
||||
"use-unattended-upgrades",
|
||||
false,
|
||||
"[Depricated] For Ubuntu, install unattended-upgrades",
|
||||
)
|
||||
}
|
||||
|
||||
// Execute execute
|
||||
func (p *PrepareCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
var keyPass, sudoPass string
|
||||
var err error
|
||||
if p.askKeyPassword {
|
||||
prompt := "SSH key password: "
|
||||
if keyPass, err = getPasswd(prompt); err != nil {
|
||||
logrus.Error(err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
if p.askSudoPassword {
|
||||
prompt := "sudo password: "
|
||||
if sudoPass, err = getPasswd(prompt); err != nil {
|
||||
logrus.Error(err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
err = c.Load(p.configPath, keyPass, sudoPass)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error loading %s, %s", p.configPath, err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
logrus.Infof("Start Preparing (config: %s)", p.configPath)
|
||||
target := make(map[string]c.ServerInfo)
|
||||
for _, arg := range f.Args() {
|
||||
found := false
|
||||
for servername, info := range c.Conf.Servers {
|
||||
if servername == arg {
|
||||
target[servername] = info
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
logrus.Errorf("%s is not in config", arg)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
}
|
||||
if 0 < len(f.Args()) {
|
||||
c.Conf.Servers = target
|
||||
}
|
||||
|
||||
c.Conf.Debug = p.debug
|
||||
c.Conf.UseUnattendedUpgrades = p.useUnattendedUpgrades
|
||||
|
||||
// Set up custom logger
|
||||
logger := util.NewCustomLogger(c.ServerInfo{})
|
||||
|
||||
logger.Info("Detecting OS... ")
|
||||
err = scan.InitServers(logger)
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to init servers. err: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
logger.Info("Installing...")
|
||||
if errs := scan.Prepare(); 0 < len(errs) {
|
||||
for _, e := range errs {
|
||||
logger.Errorf("Failed: %s", e)
|
||||
}
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
logger.Info("Success")
|
||||
return subcommands.ExitSuccess
|
||||
}
|
||||
452
commands/report.go
Normal file
@@ -0,0 +1,452 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/utils"
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/exploit"
|
||||
"github.com/future-architect/vuls/gost"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/msf"
|
||||
"github.com/future-architect/vuls/oval"
|
||||
"github.com/future-architect/vuls/report"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/google/subcommands"
|
||||
"github.com/k0kubun/pp"
|
||||
)
|
||||
|
||||
// ReportCmd is subcommand for reporting
|
||||
type ReportCmd struct {
|
||||
configPath string
|
||||
cveDict c.GoCveDictConf
|
||||
ovalDict c.GovalDictConf
|
||||
gostConf c.GostConf
|
||||
exploitConf c.ExploitConf
|
||||
metasploitConf c.MetasploitConf
|
||||
httpConf c.HTTPConf
|
||||
}
|
||||
|
||||
// Name return subcommand name
|
||||
func (*ReportCmd) Name() string { return "report" }
|
||||
|
||||
// Synopsis return synopsis
|
||||
func (*ReportCmd) Synopsis() string { return "Reporting" }
|
||||
|
||||
// Usage return usage
|
||||
func (*ReportCmd) Usage() string {
|
||||
return `report:
|
||||
report
|
||||
[-lang=en|ja]
|
||||
[-config=/path/to/config.toml]
|
||||
[-results-dir=/path/to/results]
|
||||
[-log-dir=/path/to/log]
|
||||
[-refresh-cve]
|
||||
[-cvss-over=7]
|
||||
[-diff]
|
||||
[-wp-ignore-inactive]
|
||||
[-ignore-unscored-cves]
|
||||
[-ignore-unfixed]
|
||||
[-ignore-github-dismissed]
|
||||
[-to-email]
|
||||
[-to-http]
|
||||
[-to-slack]
|
||||
[-to-stride]
|
||||
[-to-hipchat]
|
||||
[-to-chatwork]
|
||||
[-to-telegram]
|
||||
[-to-localfile]
|
||||
[-to-s3]
|
||||
[-to-azure-blob]
|
||||
[-to-saas]
|
||||
[-format-json]
|
||||
[-format-xml]
|
||||
[-format-one-email]
|
||||
[-format-one-line-text]
|
||||
[-format-list]
|
||||
[-format-full-text]
|
||||
[-gzip]
|
||||
[-uuid]
|
||||
[-http-proxy=http://192.168.0.1:8080]
|
||||
[-debug]
|
||||
[-debug-sql]
|
||||
[-quiet]
|
||||
[-no-progress]
|
||||
[-pipe]
|
||||
[-cvedb-type=sqlite3|mysql|postgres|redis|http]
|
||||
[-cvedb-sqlite3-path=/path/to/cve.sqlite3]
|
||||
[-cvedb-url=http://127.0.0.1:1323 or DB connection string]
|
||||
[-ovaldb-type=sqlite3|mysql|redis|http]
|
||||
[-ovaldb-sqlite3-path=/path/to/oval.sqlite3]
|
||||
[-ovaldb-url=http://127.0.0.1:1324 or DB connection string]
|
||||
[-gostdb-type=sqlite3|mysql|redis|http]
|
||||
[-gostdb-sqlite3-path=/path/to/gost.sqlite3]
|
||||
[-gostdb-url=http://127.0.0.1:1325 or DB connection string]
|
||||
[-exploitdb-type=sqlite3|mysql|redis|http]
|
||||
[-exploitdb-sqlite3-path=/path/to/exploitdb.sqlite3]
|
||||
[-exploitdb-url=http://127.0.0.1:1326 or DB connection string]
|
||||
[-msfdb-type=sqlite3|mysql|redis|http]
|
||||
[-msfdb-sqlite3-path=/path/to/msfdb.sqlite3]
|
||||
[-msfdb-url=http://127.0.0.1:1327 or DB connection string]
|
||||
[-http="http://vuls-report-server"]
|
||||
[-trivy-cachedb-dir=/path/to/dir]
|
||||
|
||||
[RFC3339 datetime format under results dir]
|
||||
`
|
||||
}
|
||||
|
||||
// SetFlags set flag
|
||||
func (p *ReportCmd) SetFlags(f *flag.FlagSet) {
|
||||
f.StringVar(&c.Conf.Lang, "lang", "en", "[en|ja]")
|
||||
f.BoolVar(&c.Conf.Debug, "debug", false, "debug mode")
|
||||
f.BoolVar(&c.Conf.DebugSQL, "debug-sql", false, "SQL debug mode")
|
||||
f.BoolVar(&c.Conf.Quiet, "quiet", false, "Quiet mode. No output on stdout")
|
||||
f.BoolVar(&c.Conf.NoProgress, "no-progress", false, "Suppress progress bar")
|
||||
|
||||
wd, _ := os.Getwd()
|
||||
defaultConfPath := filepath.Join(wd, "config.toml")
|
||||
f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
|
||||
|
||||
defaultResultsDir := filepath.Join(wd, "results")
|
||||
f.StringVar(&c.Conf.ResultsDir, "results-dir", defaultResultsDir, "/path/to/results")
|
||||
|
||||
defaultLogDir := util.GetDefaultLogDir()
|
||||
f.StringVar(&c.Conf.LogDir, "log-dir", defaultLogDir, "/path/to/log")
|
||||
|
||||
f.BoolVar(&c.Conf.RefreshCve, "refresh-cve", false,
|
||||
"Refresh CVE information in JSON file under results dir")
|
||||
|
||||
f.Float64Var(&c.Conf.CvssScoreOver, "cvss-over", 0,
|
||||
"-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))")
|
||||
|
||||
f.BoolVar(&c.Conf.Diff, "diff", false,
|
||||
"Difference between previous result and current result ")
|
||||
|
||||
f.BoolVar(&c.Conf.WpIgnoreInactive, "wp-ignore-inactive", false,
|
||||
"ignore inactive on wordpress's plugin and theme")
|
||||
|
||||
f.BoolVar(&c.Conf.IgnoreUnscoredCves, "ignore-unscored-cves", false,
|
||||
"Don't report the unscored CVEs")
|
||||
|
||||
f.BoolVar(&c.Conf.IgnoreUnfixed, "ignore-unfixed", false,
|
||||
"Don't report the unfixed CVEs")
|
||||
|
||||
f.BoolVar(&c.Conf.IgnoreGitHubDismissed, "ignore-github-dismissed", false,
|
||||
"Don't report the dismissed CVEs on GitHub Security Alerts")
|
||||
|
||||
f.StringVar(
|
||||
&c.Conf.HTTPProxy, "http-proxy", "",
|
||||
"http://proxy-url:port (default: empty)")
|
||||
|
||||
f.BoolVar(&c.Conf.FormatJSON, "format-json", false, "JSON format")
|
||||
f.BoolVar(&c.Conf.FormatXML, "format-xml", false, "XML format")
|
||||
f.BoolVar(&c.Conf.FormatOneEMail, "format-one-email", false,
|
||||
"Send all the host report via only one EMail (Specify with -to-email)")
|
||||
f.BoolVar(&c.Conf.FormatOneLineText, "format-one-line-text", false,
|
||||
"One line summary in plain text")
|
||||
f.BoolVar(&c.Conf.FormatList, "format-list", false, "Display as list format")
|
||||
f.BoolVar(&c.Conf.FormatFullText, "format-full-text", false,
|
||||
"Detail report in plain text")
|
||||
|
||||
f.BoolVar(&c.Conf.ToSlack, "to-slack", false, "Send report via Slack")
|
||||
f.BoolVar(&c.Conf.ToStride, "to-stride", false, "Send report via Stride")
|
||||
f.BoolVar(&c.Conf.ToHipChat, "to-hipchat", false, "Send report via hipchat")
|
||||
f.BoolVar(&c.Conf.ToChatWork, "to-chatwork", false, "Send report via chatwork")
|
||||
f.BoolVar(&c.Conf.ToTelegram, "to-telegram", false, "Send report via Telegram")
|
||||
f.BoolVar(&c.Conf.ToEmail, "to-email", false, "Send report via Email")
|
||||
f.BoolVar(&c.Conf.ToSyslog, "to-syslog", false, "Send report via Syslog")
|
||||
f.BoolVar(&c.Conf.ToLocalFile, "to-localfile", false, "Write report to localfile")
|
||||
f.BoolVar(&c.Conf.ToS3, "to-s3", false,
|
||||
"Write report to S3 (bucket/yyyyMMdd_HHmm/servername.json/xml/txt)")
|
||||
f.BoolVar(&c.Conf.ToHTTP, "to-http", false, "Send report via HTTP POST")
|
||||
f.BoolVar(&c.Conf.ToAzureBlob, "to-azure-blob", false,
|
||||
"Write report to Azure Storage blob (container/yyyyMMdd_HHmm/servername.json/xml/txt)")
|
||||
f.BoolVar(&c.Conf.ToSaas, "to-saas", false,
|
||||
"Upload report to Future Vuls(https://vuls.biz/) before report")
|
||||
|
||||
f.BoolVar(&c.Conf.GZIP, "gzip", false, "gzip compression")
|
||||
f.BoolVar(&c.Conf.UUID, "uuid", false,
|
||||
"Auto generate of scan target servers and then write to config.toml and scan result")
|
||||
f.BoolVar(&c.Conf.Pipe, "pipe", false, "Use args passed via PIPE")
|
||||
|
||||
f.StringVar(&p.cveDict.Type, "cvedb-type", "",
|
||||
"DB type of go-cve-dictionary (sqlite3, mysql, postgres, redis or http)")
|
||||
f.StringVar(&p.cveDict.SQLite3Path, "cvedb-sqlite3-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.cveDict.URL, "cvedb-url", "",
|
||||
"http://go-cve-dictionary.com:1323 or DB connection string")
|
||||
|
||||
f.StringVar(&p.ovalDict.Type, "ovaldb-type", "",
|
||||
"DB type of goval-dictionary (sqlite3, mysql, postgres, redis or http)")
|
||||
f.StringVar(&p.ovalDict.SQLite3Path, "ovaldb-sqlite3-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.ovalDict.URL, "ovaldb-url", "",
|
||||
"http://goval-dictionary.com:1324 or DB connection string")
|
||||
|
||||
f.StringVar(&p.gostConf.Type, "gostdb-type", "",
|
||||
"DB type of gost (sqlite3, mysql, postgres, redis or http)")
|
||||
f.StringVar(&p.gostConf.SQLite3Path, "gostdb-sqlite3-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.gostConf.URL, "gostdb-url", "",
|
||||
"http://gost.com:1325 or DB connection string")
|
||||
|
||||
f.StringVar(&p.exploitConf.Type, "exploitdb-type", "",
|
||||
"DB type of exploit (sqlite3, mysql, postgres, redis or http)")
|
||||
f.StringVar(&p.exploitConf.SQLite3Path, "exploitdb-sqlite3-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.exploitConf.URL, "exploitdb-url", "",
|
||||
"http://exploit.com:1326 or DB connection string")
|
||||
|
||||
f.StringVar(&p.metasploitConf.Type, "msfdb-type", "",
|
||||
"DB type of msf (sqlite3, mysql, postgres, redis or http)")
|
||||
f.StringVar(&p.metasploitConf.SQLite3Path, "msfdb-sqlite3-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.metasploitConf.URL, "msfdb-url", "",
|
||||
"http://metasploit.com:1327 or DB connection string")
|
||||
|
||||
f.StringVar(&p.httpConf.URL, "http", "", "-to-http http://vuls-report")
|
||||
|
||||
f.StringVar(&c.Conf.TrivyCacheDBDir, "trivy-cachedb-dir",
|
||||
utils.DefaultCacheDir(), "/path/to/dir")
|
||||
}
|
||||
|
||||
// Execute execute
|
||||
func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
util.Log = util.NewCustomLogger(c.ServerInfo{})
|
||||
if err := c.Load(p.configPath, ""); err != nil {
|
||||
util.Log.Errorf("Error loading %s, %+v", p.configPath, err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
c.Conf.CveDict.Overwrite(p.cveDict)
|
||||
c.Conf.OvalDict.Overwrite(p.ovalDict)
|
||||
c.Conf.Gost.Overwrite(p.gostConf)
|
||||
c.Conf.Exploit.Overwrite(p.exploitConf)
|
||||
c.Conf.Metasploit.Overwrite(p.metasploitConf)
|
||||
c.Conf.HTTP.Overwrite(p.httpConf)
|
||||
|
||||
var dir string
|
||||
var err error
|
||||
if c.Conf.Diff {
|
||||
dir, err = report.JSONDir([]string{})
|
||||
} else {
|
||||
dir, err = report.JSONDir(f.Args())
|
||||
}
|
||||
if err != nil {
|
||||
util.Log.Errorf("Failed to read from JSON: %+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
// report
|
||||
reports := []report.ResultWriter{
|
||||
report.StdoutWriter{},
|
||||
}
|
||||
|
||||
if c.Conf.ToSlack {
|
||||
reports = append(reports, report.SlackWriter{})
|
||||
}
|
||||
|
||||
if c.Conf.ToStride {
|
||||
reports = append(reports, report.StrideWriter{})
|
||||
}
|
||||
|
||||
if c.Conf.ToHipChat {
|
||||
reports = append(reports, report.HipChatWriter{})
|
||||
}
|
||||
|
||||
if c.Conf.ToChatWork {
|
||||
reports = append(reports, report.ChatWorkWriter{})
|
||||
}
|
||||
|
||||
if c.Conf.ToTelegram {
|
||||
reports = append(reports, report.TelegramWriter{})
|
||||
}
|
||||
|
||||
if c.Conf.ToEmail {
|
||||
reports = append(reports, report.EMailWriter{})
|
||||
}
|
||||
|
||||
if c.Conf.ToSyslog {
|
||||
reports = append(reports, report.SyslogWriter{})
|
||||
}
|
||||
|
||||
if c.Conf.ToHTTP {
|
||||
reports = append(reports, report.HTTPRequestWriter{})
|
||||
}
|
||||
|
||||
if c.Conf.ToLocalFile {
|
||||
reports = append(reports, report.LocalFileWriter{
|
||||
CurrentDir: dir,
|
||||
})
|
||||
}
|
||||
|
||||
if c.Conf.ToS3 {
|
||||
if err := report.CheckIfBucketExists(); err != nil {
|
||||
util.Log.Errorf("Check if there is a bucket beforehand: %s, err: %+v",
|
||||
c.Conf.AWS.S3Bucket, err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
reports = append(reports, report.S3Writer{})
|
||||
}
|
||||
|
||||
if c.Conf.ToAzureBlob {
|
||||
if len(c.Conf.Azure.AccountName) == 0 {
|
||||
c.Conf.Azure.AccountName = os.Getenv("AZURE_STORAGE_ACCOUNT")
|
||||
}
|
||||
|
||||
if len(c.Conf.Azure.AccountKey) == 0 {
|
||||
c.Conf.Azure.AccountKey = os.Getenv("AZURE_STORAGE_ACCESS_KEY")
|
||||
}
|
||||
|
||||
if len(c.Conf.Azure.ContainerName) == 0 {
|
||||
util.Log.Error("Azure storage container name is required with -azure-container option")
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
if err := report.CheckIfAzureContainerExists(); err != nil {
|
||||
util.Log.Errorf("Check if there is a container beforehand: %s, err: %+v",
|
||||
c.Conf.Azure.ContainerName, err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
reports = append(reports, report.AzureBlobWriter{})
|
||||
}
|
||||
|
||||
if c.Conf.ToSaas {
|
||||
if !c.Conf.UUID {
|
||||
util.Log.Errorf("If you use the -to-saas option, you need to enable the uuid option")
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
reports = append(reports, report.SaasWriter{})
|
||||
}
|
||||
|
||||
if !(c.Conf.FormatJSON || c.Conf.FormatOneLineText ||
|
||||
c.Conf.FormatList || c.Conf.FormatFullText || c.Conf.FormatXML) {
|
||||
c.Conf.FormatList = true
|
||||
}
|
||||
|
||||
util.Log.Info("Validating config...")
|
||||
if !c.Conf.ValidateOnReport() {
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
var loaded models.ScanResults
|
||||
if loaded, err = report.LoadScanResults(dir); err != nil {
|
||||
util.Log.Error(err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
util.Log.Infof("Loaded: %s", dir)
|
||||
|
||||
var res models.ScanResults
|
||||
hasError := false
|
||||
for _, r := range loaded {
|
||||
if len(r.Errors) == 0 {
|
||||
res = append(res, r)
|
||||
} else {
|
||||
util.Log.Errorf("Ignored since errors occurred during scanning: %s, err: %v",
|
||||
r.ServerName, r.Errors)
|
||||
hasError = true
|
||||
}
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
for _, r := range res {
|
||||
util.Log.Debugf("%s: %s",
|
||||
r.ServerInfo(),
|
||||
pp.Sprintf("%s", c.Conf.Servers[r.ServerName]))
|
||||
}
|
||||
|
||||
if c.Conf.UUID {
|
||||
// Ensure UUIDs of scan target servers in config.toml
|
||||
if err := report.EnsureUUIDs(p.configPath, res); err != nil {
|
||||
util.Log.Errorf("Failed to ensure UUIDs. err: %+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
if !c.Conf.ToSaas {
|
||||
util.Log.Info("Validating db config...")
|
||||
if !c.Conf.ValidateOnReportDB() {
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
if c.Conf.CveDict.URL != "" {
|
||||
if err := report.CveClient.CheckHealth(); err != nil {
|
||||
util.Log.Errorf("CVE HTTP server is not running. err: %+v", err)
|
||||
util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with `-cvedb-type=sqlite3 -cvedb-sqlite3-path` option instead of -cvedb-url")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
if c.Conf.OvalDict.URL != "" {
|
||||
err := oval.Base{}.CheckHTTPHealth()
|
||||
if err != nil {
|
||||
util.Log.Errorf("OVAL HTTP server is not running. err: %+v", err)
|
||||
util.Log.Errorf("Run goval-dictionary as server mode before reporting or run with `-ovaldb-type=sqlite3 -ovaldb-sqlite3-path` option instead of -ovaldb-url")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
if c.Conf.Gost.URL != "" {
|
||||
util.Log.Infof("gost: %s", c.Conf.Gost.URL)
|
||||
err := gost.Base{}.CheckHTTPHealth()
|
||||
if err != nil {
|
||||
util.Log.Errorf("gost HTTP server is not running. err: %+v", err)
|
||||
util.Log.Errorf("Run gost as server mode before reporting or run with `-gostdb-type=sqlite3 -gostdb-sqlite3-path` option instead of -gostdb-url")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
if c.Conf.Exploit.URL != "" {
|
||||
err := exploit.CheckHTTPHealth()
|
||||
if err != nil {
|
||||
util.Log.Errorf("exploit HTTP server is not running. err: %+v", err)
|
||||
util.Log.Errorf("Run go-exploitdb as server mode before reporting")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
if c.Conf.Metasploit.URL != "" {
|
||||
err := msf.CheckHTTPHealth()
|
||||
if err != nil {
|
||||
util.Log.Errorf("metasploit HTTP server is not running. err: %+v", err)
|
||||
util.Log.Errorf("Run go-msfdb as server mode before reporting")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
dbclient, locked, err := report.NewDBClient(report.DBClientConf{
|
||||
CveDictCnf: c.Conf.CveDict,
|
||||
OvalDictCnf: c.Conf.OvalDict,
|
||||
GostCnf: c.Conf.Gost,
|
||||
ExploitCnf: c.Conf.Exploit,
|
||||
MetasploitCnf: c.Conf.Metasploit,
|
||||
DebugSQL: c.Conf.DebugSQL,
|
||||
})
|
||||
if locked {
|
||||
util.Log.Errorf("SQLite3 is locked. Close other DB connections and try again. err: %+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
if err != nil {
|
||||
util.Log.Errorf("Failed to init DB Clients. err: %+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
defer dbclient.CloseDB()
|
||||
|
||||
if res, err = report.FillCveInfos(*dbclient, res, dir); err != nil {
|
||||
util.Log.Errorf("%+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
for _, w := range reports {
|
||||
if err := w.Write(res...); err != nil {
|
||||
util.Log.Errorf("Failed to report. err: %+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
if hasError {
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
return subcommands.ExitSuccess
|
||||
}
|
||||
315
commands/scan.go
@@ -1,59 +1,27 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/cveapi"
|
||||
"github.com/future-architect/vuls/db"
|
||||
"github.com/future-architect/vuls/report"
|
||||
"github.com/future-architect/vuls/scan"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/google/subcommands"
|
||||
"golang.org/x/net/context"
|
||||
"github.com/k0kubun/pp"
|
||||
)
|
||||
|
||||
// ScanCmd is Subcommand of host discovery mode
|
||||
type ScanCmd struct {
|
||||
lang string
|
||||
debug bool
|
||||
debugSQL bool
|
||||
|
||||
configPath string
|
||||
|
||||
dbpath string
|
||||
cveDictionaryURL string
|
||||
cvssScoreOver float64
|
||||
httpProxy string
|
||||
|
||||
// reporting
|
||||
reportSlack bool
|
||||
reportMail bool
|
||||
|
||||
askSudoPassword bool
|
||||
askKeyPassword bool
|
||||
|
||||
useYumPluginSecurity bool
|
||||
useUnattendedUpgrades bool
|
||||
configPath string
|
||||
askKeyPassword bool
|
||||
timeoutSec int
|
||||
scanTimeoutSec int
|
||||
}
|
||||
|
||||
// Name return subcommand name
|
||||
@@ -66,114 +34,150 @@ func (*ScanCmd) Synopsis() string { return "Scan vulnerabilities" }
|
||||
func (*ScanCmd) Usage() string {
|
||||
return `scan:
|
||||
scan
|
||||
[-lang=en|ja]
|
||||
[-config=/path/to/config.toml]
|
||||
[-dbpath=/path/to/vuls.sqlite3]
|
||||
[-cve-dictionary-url=http://127.0.0.1:1323]
|
||||
[-cvss-over=7]
|
||||
[-report-slack]
|
||||
[-report-mail]
|
||||
[-results-dir=/path/to/results]
|
||||
[-log-dir=/path/to/log]
|
||||
[-cachedb-path=/path/to/cache.db]
|
||||
[-ssh-native-insecure]
|
||||
[-ssh-config]
|
||||
[-containers-only]
|
||||
[-libs-only]
|
||||
[-wordpress-only]
|
||||
[-skip-broken]
|
||||
[-http-proxy=http://192.168.0.1:8080]
|
||||
[-ask-sudo-password]
|
||||
[-ask-key-password]
|
||||
[-timeout=300]
|
||||
[-timeout-scan=7200]
|
||||
[-debug]
|
||||
[-debug-sql]
|
||||
[-quiet]
|
||||
[-pipe]
|
||||
[-vvv]
|
||||
[-ips]
|
||||
|
||||
|
||||
[SERVER]...
|
||||
`
|
||||
}
|
||||
|
||||
// SetFlags set flag
|
||||
func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
|
||||
f.StringVar(&p.lang, "lang", "en", "[en|ja]")
|
||||
f.BoolVar(&p.debug, "debug", false, "debug mode")
|
||||
f.BoolVar(&p.debugSQL, "debug-sql", false, "SQL debug mode")
|
||||
f.BoolVar(&c.Conf.Debug, "debug", false, "debug mode")
|
||||
f.BoolVar(&c.Conf.Quiet, "quiet", false, "Quiet mode. No output on stdout")
|
||||
|
||||
defaultConfPath := os.Getenv("PWD") + "/config.toml"
|
||||
wd, _ := os.Getwd()
|
||||
defaultConfPath := filepath.Join(wd, "config.toml")
|
||||
f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
|
||||
|
||||
defaultDBPath := os.Getenv("PWD") + "/vuls.sqlite3"
|
||||
f.StringVar(&p.dbpath, "dbpath", defaultDBPath, "/path/to/sqlite3")
|
||||
defaultResultsDir := filepath.Join(wd, "results")
|
||||
f.StringVar(&c.Conf.ResultsDir, "results-dir", defaultResultsDir, "/path/to/results")
|
||||
|
||||
defaultURL := "http://127.0.0.1:1323"
|
||||
f.StringVar(
|
||||
&p.cveDictionaryURL,
|
||||
"cve-dictionary-url",
|
||||
defaultURL,
|
||||
"http://CVE.Dictionary")
|
||||
defaultLogDir := util.GetDefaultLogDir()
|
||||
f.StringVar(&c.Conf.LogDir, "log-dir", defaultLogDir, "/path/to/log")
|
||||
|
||||
f.Float64Var(
|
||||
&p.cvssScoreOver,
|
||||
"cvss-over",
|
||||
0,
|
||||
"-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))")
|
||||
defaultCacheDBPath := filepath.Join(wd, "cache.db")
|
||||
f.StringVar(&c.Conf.CacheDBPath, "cachedb-path", defaultCacheDBPath,
|
||||
"/path/to/cache.db (local cache of changelog for Ubuntu/Debian)")
|
||||
|
||||
f.StringVar(
|
||||
&p.httpProxy,
|
||||
"http-proxy",
|
||||
"",
|
||||
"http://proxy-url:port (default: empty)",
|
||||
)
|
||||
f.BoolVar(&c.Conf.SSHNative, "ssh-native-insecure", false,
|
||||
"Use Native Go implementation of SSH. Default: Use the external command")
|
||||
|
||||
f.BoolVar(&p.reportSlack, "report-slack", false, "Slack report")
|
||||
f.BoolVar(&p.reportMail, "report-mail", false, "Email report")
|
||||
f.BoolVar(&c.Conf.SSHConfig, "ssh-config", false,
|
||||
"[Deprecated] Use SSH options specified in ssh_config preferentially")
|
||||
|
||||
f.BoolVar(
|
||||
&p.askKeyPassword,
|
||||
"ask-key-password",
|
||||
false,
|
||||
f.BoolVar(&c.Conf.ContainersOnly, "containers-only", false,
|
||||
"Scan running containers only. Default: Scan both of hosts and running containers")
|
||||
|
||||
f.BoolVar(&c.Conf.LibsOnly, "libs-only", false,
|
||||
"Scan libraries (lock files) specified in config.toml only.")
|
||||
|
||||
f.BoolVar(&c.Conf.WordPressOnly, "wordpress-only", false,
|
||||
"Scan WordPress only.")
|
||||
|
||||
f.BoolVar(&c.Conf.SkipBroken, "skip-broken", false,
|
||||
"[For CentOS] yum update changelog with --skip-broken option")
|
||||
|
||||
f.StringVar(&c.Conf.HTTPProxy, "http-proxy", "",
|
||||
"http://proxy-url:port (default: empty)")
|
||||
|
||||
f.BoolVar(&p.askKeyPassword, "ask-key-password", false,
|
||||
"Ask ssh privatekey password before scanning",
|
||||
)
|
||||
|
||||
f.BoolVar(
|
||||
&p.askSudoPassword,
|
||||
"ask-sudo-password",
|
||||
false,
|
||||
"Ask sudo password of target servers before scanning",
|
||||
f.BoolVar(&c.Conf.Pipe, "pipe", false, "Use stdin via PIPE")
|
||||
|
||||
f.BoolVar(&c.Conf.DetectIPS, "ips", false, "retrieve IPS information")
|
||||
f.BoolVar(&c.Conf.Vvv, "vvv", false, "ssh -vvv")
|
||||
|
||||
f.IntVar(&p.timeoutSec, "timeout", 5*60,
|
||||
"Number of seconds for processing other than scan",
|
||||
)
|
||||
|
||||
f.BoolVar(
|
||||
&p.useYumPluginSecurity,
|
||||
"use-yum-plugin-security",
|
||||
false,
|
||||
"[Depricated] For CentOS 5. Scan by yum-plugin-security or not (use yum check-update by default)",
|
||||
f.IntVar(&p.scanTimeoutSec, "timeout-scan", 120*60,
|
||||
"Number of seconds for scanning vulnerabilities for all servers",
|
||||
)
|
||||
|
||||
f.BoolVar(
|
||||
&p.useUnattendedUpgrades,
|
||||
"use-unattended-upgrades",
|
||||
false,
|
||||
"[Depricated] For Ubuntu. Scan by unattended-upgrades or not (use apt-get upgrade --dry-run by default)",
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
// Execute execute
|
||||
func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
var keyPass, sudoPass string
|
||||
// Setup Logger
|
||||
util.Log = util.NewCustomLogger(c.ServerInfo{})
|
||||
|
||||
if err := mkdirDotVuls(); err != nil {
|
||||
util.Log.Errorf("Failed to create .vuls. err: %+v", err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
var keyPass string
|
||||
var err error
|
||||
if p.askKeyPassword {
|
||||
prompt := "SSH key password: "
|
||||
if keyPass, err = getPasswd(prompt); err != nil {
|
||||
logrus.Error(err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
if p.askSudoPassword {
|
||||
prompt := "sudo password: "
|
||||
if sudoPass, err = getPasswd(prompt); err != nil {
|
||||
logrus.Error(err)
|
||||
util.Log.Error(err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
err = c.Load(p.configPath, keyPass, sudoPass)
|
||||
err = c.Load(p.configPath, keyPass)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error loading %s, %s", p.configPath, err)
|
||||
msg := []string{
|
||||
fmt.Sprintf("Error loading %s", p.configPath),
|
||||
"If you update Vuls and get this error, there may be incompatible changes in config.toml",
|
||||
"Please check config.toml template : https://vuls.io/docs/en/usage-settings.html",
|
||||
}
|
||||
util.Log.Errorf("%s\n%+v", strings.Join(msg, "\n"), err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
logrus.Infof("Start scanning (config: %s)", p.configPath)
|
||||
if c.Conf.SSHConfig {
|
||||
msg := []string{
|
||||
"-ssh-config is deprecated",
|
||||
"If you update Vuls and get this error, there may be incompatible changes in config.toml",
|
||||
"Please check config.toml template : https://vuls.io/docs/en/usage-settings.html",
|
||||
}
|
||||
util.Log.Errorf("%s", strings.Join(msg, "\n"))
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
util.Log.Info("Start scanning")
|
||||
util.Log.Infof("config: %s", p.configPath)
|
||||
|
||||
var servernames []string
|
||||
if 0 < len(f.Args()) {
|
||||
servernames = f.Args()
|
||||
} else if c.Conf.Pipe {
|
||||
bytes, err := ioutil.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
util.Log.Errorf("Failed to read stdin. err: %+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
fields := strings.Fields(string(bytes))
|
||||
if 0 < len(fields) {
|
||||
servernames = fields
|
||||
}
|
||||
}
|
||||
|
||||
target := make(map[string]c.ServerInfo)
|
||||
for _, arg := range f.Args() {
|
||||
for _, arg := range servernames {
|
||||
found := false
|
||||
for servername, info := range c.Conf.Servers {
|
||||
if servername == arg {
|
||||
@@ -183,94 +187,45 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
logrus.Errorf("%s is not in config", arg)
|
||||
util.Log.Errorf("%s is not in config", arg)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
}
|
||||
if 0 < len(f.Args()) {
|
||||
if 0 < len(servernames) {
|
||||
c.Conf.Servers = target
|
||||
}
|
||||
util.Log.Debugf("%s", pp.Sprintf("%v", target))
|
||||
|
||||
c.Conf.Lang = p.lang
|
||||
c.Conf.Debug = p.debug
|
||||
c.Conf.DebugSQL = p.debugSQL
|
||||
|
||||
// logger
|
||||
Log := util.NewCustomLogger(c.ServerInfo{})
|
||||
|
||||
// report
|
||||
reports := []report.ResultWriter{
|
||||
report.TextWriter{},
|
||||
report.LogrusWriter{},
|
||||
}
|
||||
if p.reportSlack {
|
||||
reports = append(reports, report.SlackWriter{})
|
||||
}
|
||||
if p.reportMail {
|
||||
reports = append(reports, report.MailWriter{})
|
||||
}
|
||||
|
||||
c.Conf.DBPath = p.dbpath
|
||||
c.Conf.CveDictionaryURL = p.cveDictionaryURL
|
||||
c.Conf.HTTPProxy = p.httpProxy
|
||||
c.Conf.UseYumPluginSecurity = p.useYumPluginSecurity
|
||||
c.Conf.UseUnattendedUpgrades = p.useUnattendedUpgrades
|
||||
|
||||
Log.Info("Validating Config...")
|
||||
if !c.Conf.Validate() {
|
||||
util.Log.Info("Validating config...")
|
||||
if !c.Conf.ValidateOnScan() {
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
if ok, err := cveapi.CveClient.CheckHealth(); !ok {
|
||||
Log.Errorf("CVE HTTP server is not running. %#v", cveapi.CveClient)
|
||||
Log.Fatal(err)
|
||||
util.Log.Info("Detecting Server/Container OS... ")
|
||||
if err := scan.InitServers(p.timeoutSec); err != nil {
|
||||
util.Log.Errorf("Failed to init servers: %+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
Log.Info("Detecting the type of OS... ")
|
||||
err = scan.InitServers(Log)
|
||||
if err != nil {
|
||||
Log.Errorf("Failed to init servers. Check the configuration. err: %s", err)
|
||||
util.Log.Info("Checking Scan Modes... ")
|
||||
if err := scan.CheckScanModes(); err != nil {
|
||||
util.Log.Errorf("Fix config.toml. err: %+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
Log.Info("Scanning vulnerabilities... ")
|
||||
if errs := scan.Scan(); 0 < len(errs) {
|
||||
for _, e := range errs {
|
||||
Log.Errorf("Failed to scan. err: %s", e)
|
||||
}
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
util.Log.Info("Detecting Platforms... ")
|
||||
scan.DetectPlatforms(p.timeoutSec)
|
||||
util.Log.Info("Detecting IPS identifiers... ")
|
||||
scan.DetectIPSs(p.timeoutSec)
|
||||
|
||||
scanResults, err := scan.GetScanResults()
|
||||
if err != nil {
|
||||
Log.Fatal(err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
Log.Info("Reporting...")
|
||||
filtered := scanResults.FilterByCvssOver()
|
||||
for _, w := range reports {
|
||||
if err := w.Write(filtered); err != nil {
|
||||
Log.Fatalf("Failed to output report, err: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
Log.Info("Insert to DB...")
|
||||
if err := db.OpenDB(); err != nil {
|
||||
Log.Errorf("Failed to open DB. datafile: %s, err: %s", c.Conf.DBPath, err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
if err := db.MigrateDB(); err != nil {
|
||||
Log.Errorf("Failed to migrate. err: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
if err := db.Insert(scanResults); err != nil {
|
||||
Log.Fatalf("Failed to insert. dbpath: %s, err: %s", c.Conf.DBPath, err)
|
||||
util.Log.Info("Scanning vulnerabilities... ")
|
||||
if err := scan.Scan(p.scanTimeoutSec); err != nil {
|
||||
util.Log.Errorf("Failed to scan. err: %+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
fmt.Printf("\n\n\n")
|
||||
fmt.Println("To view the detail, vuls tui is useful.")
|
||||
fmt.Println("To send a report, run vuls report -h.")
|
||||
|
||||
return subcommands.ExitSuccess
|
||||
}
|
||||
|
||||
241
commands/server.go
Normal file
@@ -0,0 +1,241 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
// "github.com/future-architect/vuls/Server"
|
||||
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/exploit"
|
||||
"github.com/future-architect/vuls/gost"
|
||||
"github.com/future-architect/vuls/msf"
|
||||
"github.com/future-architect/vuls/oval"
|
||||
"github.com/future-architect/vuls/report"
|
||||
"github.com/future-architect/vuls/server"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/google/subcommands"
|
||||
)
|
||||
|
||||
// ServerCmd is subcommand for server
|
||||
type ServerCmd struct {
|
||||
configPath string
|
||||
listen string
|
||||
cveDict c.GoCveDictConf
|
||||
ovalDict c.GovalDictConf
|
||||
gostConf c.GostConf
|
||||
exploitConf c.ExploitConf
|
||||
metasploitConf c.MetasploitConf
|
||||
}
|
||||
|
||||
// Name return subcommand name
|
||||
func (*ServerCmd) Name() string { return "server" }
|
||||
|
||||
// Synopsis return synopsis
|
||||
func (*ServerCmd) Synopsis() string { return "Server" }
|
||||
|
||||
// Usage return usage
|
||||
func (*ServerCmd) Usage() string {
|
||||
return `Server:
|
||||
Server
|
||||
[-lang=en|ja]
|
||||
[-config=/path/to/config.toml]
|
||||
[-log-dir=/path/to/log]
|
||||
[-cvss-over=7]
|
||||
[-ignore-unscored-cves]
|
||||
[-ignore-unfixed]
|
||||
[-to-localfile]
|
||||
[-format-json]
|
||||
[-http-proxy=http://192.168.0.1:8080]
|
||||
[-debug]
|
||||
[-debug-sql]
|
||||
[-listen=localhost:5515]
|
||||
[-cvedb-type=sqlite3|mysql|postgres|redis|http]
|
||||
[-cvedb-sqlite3-path=/path/to/cve.sqlite3]
|
||||
[-cvedb-url=http://127.0.0.1:1323 or DB connection string]
|
||||
[-ovaldb-type=sqlite3|mysql|redis|http]
|
||||
[-ovaldb-sqlite3-path=/path/to/oval.sqlite3]
|
||||
[-ovaldb-url=http://127.0.0.1:1324 or DB connection string]
|
||||
[-gostdb-type=sqlite3|mysql|redis|http]
|
||||
[-gostdb-sqlite3-path=/path/to/gost.sqlite3]
|
||||
[-gostdb-url=http://127.0.0.1:1325 or DB connection string]
|
||||
[-exploitdb-type=sqlite3|mysql|redis|http]
|
||||
[-exploitdb-sqlite3-path=/path/to/exploitdb.sqlite3]
|
||||
[-exploitdb-url=http://127.0.0.1:1326 or DB connection string]
|
||||
[-msfdb-type=sqlite3|mysql|redis|http]
|
||||
[-msfdb-sqlite3-path=/path/to/msfdb.sqlite3]
|
||||
[-msfdb-url=http://127.0.0.1:1327 or DB connection string]
|
||||
|
||||
[RFC3339 datetime format under results dir]
|
||||
`
|
||||
}
|
||||
|
||||
// SetFlags set flag
|
||||
func (p *ServerCmd) SetFlags(f *flag.FlagSet) {
|
||||
f.StringVar(&c.Conf.Lang, "lang", "en", "[en|ja]")
|
||||
f.BoolVar(&c.Conf.Debug, "debug", false, "debug mode")
|
||||
f.BoolVar(&c.Conf.DebugSQL, "debug-sql", false, "SQL debug mode")
|
||||
|
||||
wd, _ := os.Getwd()
|
||||
f.StringVar(&p.configPath, "config", "", "/path/to/toml")
|
||||
|
||||
defaultResultsDir := filepath.Join(wd, "results")
|
||||
f.StringVar(&c.Conf.ResultsDir, "results-dir", defaultResultsDir, "/path/to/results")
|
||||
|
||||
defaultLogDir := util.GetDefaultLogDir()
|
||||
f.StringVar(&c.Conf.LogDir, "log-dir", defaultLogDir, "/path/to/log")
|
||||
|
||||
f.Float64Var(&c.Conf.CvssScoreOver, "cvss-over", 0,
|
||||
"-cvss-over=6.5 means Servering CVSS Score 6.5 and over (default: 0 (means Server all))")
|
||||
|
||||
f.BoolVar(&c.Conf.IgnoreUnscoredCves, "ignore-unscored-cves", false,
|
||||
"Don't Server the unscored CVEs")
|
||||
|
||||
f.BoolVar(&c.Conf.IgnoreUnfixed, "ignore-unfixed", false,
|
||||
"Don't Server the unfixed CVEs")
|
||||
|
||||
f.StringVar(&c.Conf.HTTPProxy, "http-proxy", "",
|
||||
"http://proxy-url:port (default: empty)")
|
||||
|
||||
f.BoolVar(&c.Conf.FormatJSON, "format-json", false, "JSON format")
|
||||
|
||||
f.BoolVar(&c.Conf.ToLocalFile, "to-localfile", false, "Write report to localfile")
|
||||
f.StringVar(&p.listen, "listen", "localhost:5515",
|
||||
"host:port (default: localhost:5515)")
|
||||
|
||||
f.StringVar(&p.cveDict.Type, "cvedb-type", "",
|
||||
"DB type of go-cve-dictionary (sqlite3, mysql, postgres, redis or http)")
|
||||
f.StringVar(&p.cveDict.SQLite3Path, "cvedb-sqlite3-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.cveDict.URL, "cvedb-url", "",
|
||||
"http://go-cve-dictionary.com:1323 or DB connection string")
|
||||
|
||||
f.StringVar(&p.ovalDict.Type, "ovaldb-type", "",
|
||||
"DB type of goval-dictionary (sqlite3, mysql, postgres, redis or http)")
|
||||
f.StringVar(&p.ovalDict.SQLite3Path, "ovaldb-sqlite3-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.ovalDict.URL, "ovaldb-url", "",
|
||||
"http://goval-dictionary.com:1324 or DB connection string")
|
||||
|
||||
f.StringVar(&p.gostConf.Type, "gostdb-type", "",
|
||||
"DB type of gost (sqlite3, mysql, postgres, redis or http)")
|
||||
f.StringVar(&p.gostConf.SQLite3Path, "gostdb-sqlite3-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.gostConf.URL, "gostdb-url", "",
|
||||
"http://gost.com:1325 or DB connection string")
|
||||
|
||||
f.StringVar(&p.exploitConf.Type, "exploitdb-type", "",
|
||||
"DB type of exploit (sqlite3, mysql, postgres, redis or http)")
|
||||
f.StringVar(&p.exploitConf.SQLite3Path, "exploitdb-sqlite3-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.exploitConf.URL, "exploitdb-url", "",
|
||||
"http://exploit.com:1326 or DB connection string")
|
||||
|
||||
f.StringVar(&p.metasploitConf.Type, "msfdb-type", "",
|
||||
"DB type of msf (sqlite3, mysql, postgres, redis or http)")
|
||||
f.StringVar(&p.metasploitConf.SQLite3Path, "msfdb-sqlite3-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.metasploitConf.URL, "msfdb-url", "",
|
||||
"http://metasploit.com:1327 or DB connection string")
|
||||
}
|
||||
|
||||
// Execute execute
|
||||
func (p *ServerCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
util.Log = util.NewCustomLogger(c.ServerInfo{})
|
||||
if p.configPath != "" {
|
||||
if err := c.Load(p.configPath, ""); err != nil {
|
||||
util.Log.Errorf("Error loading %s. err: %+v", p.configPath, err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
}
|
||||
|
||||
c.Conf.CveDict.Overwrite(p.cveDict)
|
||||
c.Conf.OvalDict.Overwrite(p.ovalDict)
|
||||
c.Conf.Gost.Overwrite(p.gostConf)
|
||||
c.Conf.Exploit.Overwrite(p.exploitConf)
|
||||
c.Conf.Metasploit.Overwrite(p.metasploitConf)
|
||||
|
||||
util.Log.Info("Validating config...")
|
||||
if !c.Conf.ValidateOnReport() {
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
util.Log.Info("Validating db config...")
|
||||
if !c.Conf.ValidateOnReportDB() {
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
if c.Conf.CveDict.URL != "" {
|
||||
if err := report.CveClient.CheckHealth(); err != nil {
|
||||
util.Log.Errorf("CVE HTTP server is not running. err: %+v", err)
|
||||
util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with `-cvedb-type=sqlite3 -cvedb-sqlite3-path` option instead of -cvedb-url")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
if c.Conf.OvalDict.URL != "" {
|
||||
err := oval.Base{}.CheckHTTPHealth()
|
||||
if err != nil {
|
||||
util.Log.Errorf("OVAL HTTP server is not running. err: %s", err)
|
||||
util.Log.Errorf("Run goval-dictionary as server mode before reporting or run with `-ovaldb-type=sqlite3 -ovaldb-sqlite3-path` option instead of -ovaldb-url")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
if c.Conf.Gost.URL != "" {
|
||||
util.Log.Infof("gost: %s", c.Conf.Gost.URL)
|
||||
err := gost.Base{}.CheckHTTPHealth()
|
||||
if err != nil {
|
||||
util.Log.Errorf("gost HTTP server is not running. err: %+v", err)
|
||||
util.Log.Errorf("Run gost as server mode before reporting or run with `-gostdb-type=sqlite3 -gostdb-sqlite3-path` option instead of -gostdb-url")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
if c.Conf.Exploit.URL != "" {
|
||||
err := exploit.CheckHTTPHealth()
|
||||
if err != nil {
|
||||
util.Log.Errorf("exploit HTTP server is not running. err: %+v", err)
|
||||
util.Log.Errorf("Run go-exploitdb as server mode before reporting")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
if c.Conf.Metasploit.URL != "" {
|
||||
err := msf.CheckHTTPHealth()
|
||||
if err != nil {
|
||||
util.Log.Errorf("metasploit HTTP server is not running. err: %+v", err)
|
||||
util.Log.Errorf("Run go-msfdb as server mode before reporting")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
dbclient, locked, err := report.NewDBClient(report.DBClientConf{
|
||||
CveDictCnf: c.Conf.CveDict,
|
||||
OvalDictCnf: c.Conf.OvalDict,
|
||||
GostCnf: c.Conf.Gost,
|
||||
ExploitCnf: c.Conf.Exploit,
|
||||
MetasploitCnf: c.Conf.Metasploit,
|
||||
DebugSQL: c.Conf.DebugSQL,
|
||||
})
|
||||
if locked {
|
||||
util.Log.Errorf("SQLite3 is locked. Close other DB connections and try again: %+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
util.Log.Errorf("Failed to init DB Clients. err: %+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
defer dbclient.CloseDB()
|
||||
|
||||
http.Handle("/vuls", server.VulsHandler{DBclient: *dbclient})
|
||||
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "ok")
|
||||
})
|
||||
util.Log.Infof("Listening on %s", p.listen)
|
||||
if err := http.ListenAndServe(p.listen, nil); err != nil {
|
||||
util.Log.Errorf("Failed to start server. err: %+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
return subcommands.ExitSuccess
|
||||
}
|
||||
259
commands/tui.go
@@ -1,50 +1,72 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/utils"
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/exploit"
|
||||
"github.com/future-architect/vuls/gost"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/msf"
|
||||
"github.com/future-architect/vuls/oval"
|
||||
"github.com/future-architect/vuls/report"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/google/subcommands"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// TuiCmd is Subcommand of host discovery mode
|
||||
type TuiCmd struct {
|
||||
lang string
|
||||
debugSQL bool
|
||||
dbpath string
|
||||
configPath string
|
||||
cveDict c.GoCveDictConf
|
||||
ovalDict c.GovalDictConf
|
||||
gostConf c.GostConf
|
||||
exploitConf c.ExploitConf
|
||||
metasploitConf c.MetasploitConf
|
||||
}
|
||||
|
||||
// Name return subcommand name
|
||||
func (*TuiCmd) Name() string { return "tui" }
|
||||
|
||||
// Synopsis return synopsis
|
||||
func (*TuiCmd) Synopsis() string { return "Run Tui view to anayze vulnerabilites" }
|
||||
func (*TuiCmd) Synopsis() string { return "Run Tui view to analyze vulnerabilities" }
|
||||
|
||||
// Usage return usage
|
||||
func (*TuiCmd) Usage() string {
|
||||
return `tui:
|
||||
tui [-dbpath=/path/to/vuls.sqlite3]
|
||||
tui
|
||||
[-refresh-cve]
|
||||
[-config=/path/to/config.toml]
|
||||
[-cvss-over=7]
|
||||
[-diff]
|
||||
[-ignore-unscored-cves]
|
||||
[-ignore-unfixed]
|
||||
[-results-dir=/path/to/results]
|
||||
[-log-dir=/path/to/log]
|
||||
[-debug]
|
||||
[-debug-sql]
|
||||
[-quiet]
|
||||
[-no-progress]
|
||||
[-pipe]
|
||||
[-cvedb-type=sqlite3|mysql|postgres|redis|http]
|
||||
[-cvedb-sqlite3-path=/path/to/cve.sqlite3]
|
||||
[-cvedb-url=http://127.0.0.1:1323 or DB connection string]
|
||||
[-ovaldb-type=sqlite3|mysql|redis|http]
|
||||
[-ovaldb-sqlite3-path=/path/to/oval.sqlite3]
|
||||
[-ovaldb-url=http://127.0.0.1:1324 or DB connection string]
|
||||
[-gostdb-type=sqlite3|mysql|redis|http]
|
||||
[-gostdb-sqlite3-path=/path/to/gost.sqlite3]
|
||||
[-gostdb-url=http://127.0.0.1:1325 or DB connection string]
|
||||
[-exploitdb-type=sqlite3|mysql|redis|http]
|
||||
[-exploitdb-sqlite3-path=/path/to/exploitdb.sqlite3]
|
||||
[-exploitdb-url=http://127.0.0.1:1326 or DB connection string]
|
||||
[-msfdb-type=sqlite3|mysql|redis|http]
|
||||
[-msfdb-sqlite3-path=/path/to/msfdb.sqlite3]
|
||||
[-msfdb-url=http://127.0.0.1:1327 or DB connection string]
|
||||
[-trivy-cachedb-dir=/path/to/dir]
|
||||
|
||||
`
|
||||
}
|
||||
@@ -52,17 +74,192 @@ func (*TuiCmd) Usage() string {
|
||||
// SetFlags set flag
|
||||
func (p *TuiCmd) SetFlags(f *flag.FlagSet) {
|
||||
// f.StringVar(&p.lang, "lang", "en", "[en|ja]")
|
||||
f.BoolVar(&p.debugSQL, "debug-sql", false, "debug SQL")
|
||||
f.BoolVar(&c.Conf.DebugSQL, "debug-sql", false, "debug SQL")
|
||||
f.BoolVar(&c.Conf.Debug, "debug", false, "debug mode")
|
||||
f.BoolVar(&c.Conf.Quiet, "quiet", false, "Quiet mode. No output on stdout")
|
||||
f.BoolVar(&c.Conf.NoProgress, "no-progress", false, "Suppress progress bar")
|
||||
|
||||
defaultDBPath := os.Getenv("PWD") + "/vuls.sqlite3"
|
||||
f.StringVar(&p.dbpath, "dbpath", defaultDBPath,
|
||||
fmt.Sprintf("/path/to/sqlite3 (default: %s)", defaultDBPath))
|
||||
defaultLogDir := util.GetDefaultLogDir()
|
||||
f.StringVar(&c.Conf.LogDir, "log-dir", defaultLogDir, "/path/to/log")
|
||||
|
||||
wd, _ := os.Getwd()
|
||||
defaultResultsDir := filepath.Join(wd, "results")
|
||||
f.StringVar(&c.Conf.ResultsDir, "results-dir", defaultResultsDir, "/path/to/results")
|
||||
|
||||
defaultConfPath := filepath.Join(wd, "config.toml")
|
||||
f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
|
||||
|
||||
f.BoolVar(&c.Conf.RefreshCve, "refresh-cve", false,
|
||||
"Refresh CVE information in JSON file under results dir")
|
||||
|
||||
f.Float64Var(&c.Conf.CvssScoreOver, "cvss-over", 0,
|
||||
"-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))")
|
||||
|
||||
f.BoolVar(&c.Conf.Diff, "diff", false,
|
||||
"Difference between previous result and current result ")
|
||||
|
||||
f.BoolVar(
|
||||
&c.Conf.IgnoreUnscoredCves, "ignore-unscored-cves", false,
|
||||
"Don't report the unscored CVEs")
|
||||
|
||||
f.BoolVar(&c.Conf.IgnoreUnfixed, "ignore-unfixed", false,
|
||||
"Don't report the unfixed CVEs")
|
||||
|
||||
f.BoolVar(&c.Conf.Pipe, "pipe", false, "Use stdin via PIPE")
|
||||
|
||||
f.StringVar(&p.cveDict.Type, "cvedb-type", "",
|
||||
"DB type of go-cve-dictionary (sqlite3, mysql, postgres or redis)")
|
||||
f.StringVar(&p.cveDict.SQLite3Path, "cvedb-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.cveDict.URL, "cvedb-url", "",
|
||||
"http://go-cve-dictionary.com:1323 or DB connection string")
|
||||
|
||||
f.StringVar(&p.ovalDict.Type, "ovaldb-type", "",
|
||||
"DB type of goval-dictionary (sqlite3, mysql, postgres or redis)")
|
||||
f.StringVar(&p.ovalDict.SQLite3Path, "ovaldb-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.ovalDict.URL, "ovaldb-url", "",
|
||||
"http://goval-dictionary.com:1324 or DB connection string")
|
||||
|
||||
f.StringVar(&p.gostConf.Type, "gostdb-type", "",
|
||||
"DB type of gost (sqlite3, mysql, postgres or redis)")
|
||||
f.StringVar(&p.gostConf.SQLite3Path, "gostdb-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.gostConf.URL, "gostdb-url", "",
|
||||
"http://gost.com:1325 or DB connection string")
|
||||
|
||||
f.StringVar(&p.exploitConf.Type, "exploitdb-type", "",
|
||||
"DB type of exploit (sqlite3, mysql, postgres, redis or http)")
|
||||
f.StringVar(&p.exploitConf.SQLite3Path, "exploitdb-sqlite3-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.exploitConf.URL, "exploitdb-url", "",
|
||||
"http://exploit.com:1326 or DB connection string")
|
||||
|
||||
f.StringVar(&p.metasploitConf.Type, "msfdb-type", "",
|
||||
"DB type of msf (sqlite3, mysql, postgres, redis or http)")
|
||||
f.StringVar(&p.metasploitConf.SQLite3Path, "msfdb-sqlite3-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.metasploitConf.URL, "msfdb-url", "",
|
||||
"http://metasploit.com:1327 or DB connection string")
|
||||
|
||||
f.StringVar(&c.Conf.TrivyCacheDBDir, "trivy-cachedb-dir",
|
||||
utils.DefaultCacheDir(), "/path/to/dir")
|
||||
}
|
||||
|
||||
// Execute execute
|
||||
func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
util.Log = util.NewCustomLogger(c.ServerInfo{})
|
||||
if err := c.Load(p.configPath, ""); err != nil {
|
||||
util.Log.Errorf("Error loading %s, err: %+v", p.configPath, err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
c.Conf.Lang = "en"
|
||||
c.Conf.DebugSQL = p.debugSQL
|
||||
c.Conf.DBPath = p.dbpath
|
||||
return report.RunTui()
|
||||
c.Conf.CveDict.Overwrite(p.cveDict)
|
||||
c.Conf.OvalDict.Overwrite(p.ovalDict)
|
||||
c.Conf.Gost.Overwrite(p.gostConf)
|
||||
c.Conf.Exploit.Overwrite(p.exploitConf)
|
||||
c.Conf.Metasploit.Overwrite(p.metasploitConf)
|
||||
|
||||
var dir string
|
||||
var err error
|
||||
if c.Conf.Diff {
|
||||
dir, err = report.JSONDir([]string{})
|
||||
} else {
|
||||
dir, err = report.JSONDir(f.Args())
|
||||
}
|
||||
if err != nil {
|
||||
util.Log.Errorf("Failed to read from JSON. err: %+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
util.Log.Info("Validating config...")
|
||||
if !c.Conf.ValidateOnTui() {
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
var res models.ScanResults
|
||||
if res, err = report.LoadScanResults(dir); err != nil {
|
||||
util.Log.Error(err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
util.Log.Infof("Loaded: %s", dir)
|
||||
|
||||
util.Log.Info("Validating db config...")
|
||||
if !c.Conf.ValidateOnReportDB() {
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
if c.Conf.CveDict.URL != "" {
|
||||
if err := report.CveClient.CheckHealth(); err != nil {
|
||||
util.Log.Errorf("CVE HTTP server is not running. err: %+v", err)
|
||||
util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with `-cvedb-type=sqlite3 -cvedb-sqlite3-path` option instead of -cvedb-url")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
if c.Conf.OvalDict.URL != "" {
|
||||
err := oval.Base{}.CheckHTTPHealth()
|
||||
if err != nil {
|
||||
util.Log.Errorf("OVAL HTTP server is not running. err: %+v", err)
|
||||
util.Log.Errorf("Run goval-dictionary as server mode before reporting or run with `-ovaldb-type=sqlite3 -ovaldb-sqlite3-path` option instead of -ovaldb-url")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
if c.Conf.Gost.URL != "" {
|
||||
util.Log.Infof("gost: %s", c.Conf.Gost.URL)
|
||||
err := gost.Base{}.CheckHTTPHealth()
|
||||
if err != nil {
|
||||
util.Log.Errorf("gost HTTP server is not running. err: %+v", err)
|
||||
util.Log.Errorf("Run gost as server mode before reporting or run with `-gostdb-type=sqlite3 -gostdb-sqlite3-path` option instead of -gostdb-url")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
if c.Conf.Exploit.URL != "" {
|
||||
err := exploit.CheckHTTPHealth()
|
||||
if err != nil {
|
||||
util.Log.Errorf("exploit HTTP server is not running. err: %+v", err)
|
||||
util.Log.Errorf("Run go-exploitdb as server mode before reporting")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
if c.Conf.Metasploit.URL != "" {
|
||||
err := msf.CheckHTTPHealth()
|
||||
if err != nil {
|
||||
util.Log.Errorf("metasploit HTTP server is not running. err: %+v", err)
|
||||
util.Log.Errorf("Run go-msfdb as server mode before reporting")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
dbclient, locked, err := report.NewDBClient(report.DBClientConf{
|
||||
CveDictCnf: c.Conf.CveDict,
|
||||
OvalDictCnf: c.Conf.OvalDict,
|
||||
GostCnf: c.Conf.Gost,
|
||||
ExploitCnf: c.Conf.Exploit,
|
||||
MetasploitCnf: c.Conf.Metasploit,
|
||||
DebugSQL: c.Conf.DebugSQL,
|
||||
})
|
||||
if locked {
|
||||
util.Log.Errorf("SQLite3 is locked. Close other DB connections and try again: %+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
util.Log.Errorf("Failed to init DB Clients. err: %+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
defer dbclient.CloseDB()
|
||||
|
||||
if res, err = report.FillCveInfos(*dbclient, res, dir); err != nil {
|
||||
util.Log.Error(err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
for _, r := range res {
|
||||
if len(r.Warnings) != 0 {
|
||||
util.Log.Warnf("Warning: Some warnings occurred while scanning on %s: %s",
|
||||
r.FormatServerName(), r.Warnings)
|
||||
}
|
||||
}
|
||||
|
||||
return report.RunTui(res)
|
||||
}
|
||||
|
||||
39
commands/util.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/howeyc/gopass"
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
func getPasswd(prompt string) (string, error) {
|
||||
for {
|
||||
fmt.Print(prompt)
|
||||
pass, err := gopass.GetPasswdMasked()
|
||||
if err != nil {
|
||||
return "", xerrors.New("Failed to read a password")
|
||||
}
|
||||
if 0 < len(pass) {
|
||||
return string(pass), nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func mkdirDotVuls() error {
|
||||
home, err := homedir.Dir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dotVuls := filepath.Join(home, ".vuls")
|
||||
if _, err := os.Stat(dotVuls); os.IsNotExist(err) {
|
||||
if err := os.Mkdir(dotVuls, 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,20 +1,3 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
var (
|
||||
|
||||
1273
config/config.go
103
config/config_test.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSyslogConfValidate(t *testing.T) {
|
||||
var tests = []struct {
|
||||
conf SyslogConf
|
||||
expectedErrLength int
|
||||
}{
|
||||
{
|
||||
conf: SyslogConf{},
|
||||
expectedErrLength: 0,
|
||||
},
|
||||
{
|
||||
conf: SyslogConf{
|
||||
Protocol: "tcp",
|
||||
Port: "5140",
|
||||
},
|
||||
expectedErrLength: 0,
|
||||
},
|
||||
{
|
||||
conf: SyslogConf{
|
||||
Protocol: "udp",
|
||||
Port: "12345",
|
||||
Severity: "emerg",
|
||||
Facility: "user",
|
||||
},
|
||||
expectedErrLength: 0,
|
||||
},
|
||||
{
|
||||
conf: SyslogConf{
|
||||
Protocol: "foo",
|
||||
Port: "514",
|
||||
},
|
||||
expectedErrLength: 1,
|
||||
},
|
||||
{
|
||||
conf: SyslogConf{
|
||||
Protocol: "invalid",
|
||||
Port: "-1",
|
||||
},
|
||||
expectedErrLength: 2,
|
||||
},
|
||||
{
|
||||
conf: SyslogConf{
|
||||
Protocol: "invalid",
|
||||
Port: "invalid",
|
||||
Severity: "invalid",
|
||||
Facility: "invalid",
|
||||
},
|
||||
expectedErrLength: 4,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
Conf.ToSyslog = true
|
||||
errs := tt.conf.Validate()
|
||||
if len(errs) != tt.expectedErrLength {
|
||||
t.Errorf("test: %d, expected %d, actual %d", i, tt.expectedErrLength, len(errs))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_MajorVersion(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in Distro
|
||||
out int
|
||||
}{
|
||||
{
|
||||
in: Distro{
|
||||
Family: Amazon,
|
||||
Release: "2 (2017.12)",
|
||||
},
|
||||
out: 2,
|
||||
},
|
||||
{
|
||||
in: Distro{
|
||||
Family: Amazon,
|
||||
Release: "2017.12",
|
||||
},
|
||||
out: 1,
|
||||
},
|
||||
{
|
||||
in: Distro{
|
||||
Family: CentOS,
|
||||
Release: "7.10",
|
||||
},
|
||||
out: 7,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
ver, err := tt.in.MajorVersion()
|
||||
if err != nil {
|
||||
t.Errorf("[%d] err occurred: %s", i, err)
|
||||
}
|
||||
if tt.out != ver {
|
||||
t.Errorf("[%d] expected %d, actual %d", i, tt.out, ver)
|
||||
}
|
||||
}
|
||||
}
|
||||
9
config/ips.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package config
|
||||
|
||||
// IPS is
|
||||
type IPS string
|
||||
|
||||
const (
|
||||
// DeepSecurity is
|
||||
DeepSecurity IPS = "deepsecurity"
|
||||
)
|
||||
@@ -1,29 +1,12 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import "fmt"
|
||||
import "golang.org/x/xerrors"
|
||||
|
||||
// JSONLoader loads configuration
|
||||
type JSONLoader struct {
|
||||
}
|
||||
|
||||
// Load load the configuraiton JSON file specified by path arg.
|
||||
// Load load the configuration JSON file specified by path arg.
|
||||
func (c JSONLoader) Load(path, sudoPass, keyPass string) (err error) {
|
||||
return fmt.Errorf("Not implement yet")
|
||||
return xerrors.New("Not implement yet")
|
||||
}
|
||||
|
||||
@@ -1,31 +1,13 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
// Load loads configuration
|
||||
func Load(path, keyPass, sudoPass string) error {
|
||||
func Load(path, keyPass string) error {
|
||||
var loader Loader
|
||||
loader = TOMLLoader{}
|
||||
|
||||
return loader.Load(path, keyPass, sudoPass)
|
||||
return loader.Load(path, keyPass)
|
||||
}
|
||||
|
||||
// Loader is interface of concrete loader
|
||||
type Loader interface {
|
||||
Load(string, string, string) error
|
||||
Load(string, string) error
|
||||
}
|
||||
|
||||
@@ -1,45 +1,41 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/k0kubun/pp"
|
||||
"github.com/knqyf263/go-cpe/naming"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// TOMLLoader loads config
|
||||
type TOMLLoader struct {
|
||||
}
|
||||
|
||||
// Load load the configuraiton TOML file specified by path arg.
|
||||
func (c TOMLLoader) Load(pathToToml, keyPass, sudoPass string) (err error) {
|
||||
// Load load the configuration TOML file specified by path arg.
|
||||
func (c TOMLLoader) Load(pathToToml, keyPass string) error {
|
||||
var conf Config
|
||||
if _, err := toml.DecodeFile(pathToToml, &conf); err != nil {
|
||||
log.Error("Load config failed", err)
|
||||
return err
|
||||
}
|
||||
|
||||
Conf.Mail = conf.Mail
|
||||
Conf.EMail = conf.EMail
|
||||
Conf.Slack = conf.Slack
|
||||
Conf.Stride = conf.Stride
|
||||
Conf.HipChat = conf.HipChat
|
||||
Conf.ChatWork = conf.ChatWork
|
||||
Conf.Telegram = conf.Telegram
|
||||
Conf.Saas = conf.Saas
|
||||
Conf.Syslog = conf.Syslog
|
||||
Conf.HTTP = conf.HTTP
|
||||
Conf.AWS = conf.AWS
|
||||
Conf.Azure = conf.Azure
|
||||
|
||||
Conf.CveDict = conf.CveDict
|
||||
Conf.OvalDict = conf.OvalDict
|
||||
Conf.Gost = conf.Gost
|
||||
Conf.Exploit = conf.Exploit
|
||||
Conf.Metasploit = conf.Metasploit
|
||||
|
||||
d := conf.Default
|
||||
Conf.Default = d
|
||||
@@ -49,51 +45,82 @@ func (c TOMLLoader) Load(pathToToml, keyPass, sudoPass string) (err error) {
|
||||
d.KeyPassword = keyPass
|
||||
}
|
||||
|
||||
if sudoPass != "" {
|
||||
d.Password = sudoPass
|
||||
}
|
||||
|
||||
i := 0
|
||||
for name, v := range conf.Servers {
|
||||
|
||||
if 0 < len(v.KeyPassword) || 0 < len(v.Password) {
|
||||
log.Warn("[Depricated] password and keypassword in config file are unsecure. Remove them immediately for a security reason. They will be removed in a future release.")
|
||||
for serverName, v := range conf.Servers {
|
||||
if 0 < len(v.KeyPassword) {
|
||||
return xerrors.Errorf("[Deprecated] KEYPASSWORD IN CONFIG FILE ARE UNSECURE. REMOVE THEM IMMEDIATELY FOR A SECURITY REASONS. THEY WILL BE REMOVED IN A FUTURE RELEASE: %s", serverName)
|
||||
}
|
||||
|
||||
s := ServerInfo{ServerName: name}
|
||||
s.User = v.User
|
||||
if s.User == "" {
|
||||
s.User = d.User
|
||||
}
|
||||
s := ServerInfo{ServerName: serverName}
|
||||
if v.Type != ServerTypePseudo {
|
||||
s.Host = v.Host
|
||||
if len(s.Host) == 0 {
|
||||
return xerrors.Errorf("%s is invalid. host is empty", serverName)
|
||||
}
|
||||
|
||||
// s.Password = sudoPass
|
||||
s.Password = v.Password
|
||||
if s.Password == "" {
|
||||
s.Password = d.Password
|
||||
}
|
||||
s.JumpServer = v.JumpServer
|
||||
if len(s.JumpServer) == 0 {
|
||||
s.JumpServer = d.JumpServer
|
||||
}
|
||||
|
||||
s.Host = v.Host
|
||||
switch {
|
||||
case v.Port != "":
|
||||
s.Port = v.Port
|
||||
case d.Port != "":
|
||||
s.Port = d.Port
|
||||
default:
|
||||
s.Port = "22"
|
||||
}
|
||||
|
||||
s.Port = v.Port
|
||||
if s.Port == "" {
|
||||
s.Port = d.Port
|
||||
}
|
||||
switch {
|
||||
case v.User != "":
|
||||
s.User = v.User
|
||||
case d.User != "":
|
||||
s.User = d.User
|
||||
default:
|
||||
if s.Port != "local" {
|
||||
return xerrors.Errorf("%s is invalid. User is empty", serverName)
|
||||
}
|
||||
}
|
||||
|
||||
s.KeyPath = v.KeyPath
|
||||
if s.KeyPath == "" {
|
||||
s.KeyPath = d.KeyPath
|
||||
}
|
||||
if s.KeyPath != "" {
|
||||
if _, err := os.Stat(s.KeyPath); err != nil {
|
||||
return fmt.Errorf(
|
||||
"config.toml is invalid. keypath: %s not exists", s.KeyPath)
|
||||
s.SSHConfigPath = v.SSHConfigPath
|
||||
if len(s.SSHConfigPath) == 0 {
|
||||
s.SSHConfigPath = d.SSHConfigPath
|
||||
}
|
||||
|
||||
s.KeyPath = v.KeyPath
|
||||
if len(s.KeyPath) == 0 {
|
||||
s.KeyPath = d.KeyPath
|
||||
}
|
||||
s.KeyPassword = v.KeyPassword
|
||||
if len(s.KeyPassword) == 0 {
|
||||
s.KeyPassword = d.KeyPassword
|
||||
}
|
||||
}
|
||||
|
||||
// s.KeyPassword = keyPass
|
||||
s.KeyPassword = v.KeyPassword
|
||||
if s.KeyPassword == "" {
|
||||
s.KeyPassword = d.KeyPassword
|
||||
s.ScanMode = v.ScanMode
|
||||
if len(s.ScanMode) == 0 {
|
||||
s.ScanMode = d.ScanMode
|
||||
if len(s.ScanMode) == 0 {
|
||||
s.ScanMode = []string{"fast"}
|
||||
}
|
||||
}
|
||||
for _, m := range s.ScanMode {
|
||||
switch m {
|
||||
case "fast":
|
||||
s.Mode.Set(Fast)
|
||||
case "fast-root":
|
||||
s.Mode.Set(FastRoot)
|
||||
case "deep":
|
||||
s.Mode.Set(Deep)
|
||||
case "offline":
|
||||
s.Mode.Set(Offline)
|
||||
default:
|
||||
return xerrors.Errorf("scanMode: %s of %s is invalid. Specify -fast, -fast-root, -deep or offline", m, serverName)
|
||||
}
|
||||
}
|
||||
if err := s.Mode.validate(); err != nil {
|
||||
return xerrors.Errorf("%s in %s", err, serverName)
|
||||
}
|
||||
|
||||
s.CpeNames = v.CpeNames
|
||||
@@ -101,13 +128,168 @@ func (c TOMLLoader) Load(pathToToml, keyPass, sudoPass string) (err error) {
|
||||
s.CpeNames = d.CpeNames
|
||||
}
|
||||
|
||||
s.Lockfiles = v.Lockfiles
|
||||
if len(s.Lockfiles) == 0 {
|
||||
s.Lockfiles = d.Lockfiles
|
||||
}
|
||||
|
||||
s.FindLock = v.FindLock
|
||||
|
||||
for i, n := range s.CpeNames {
|
||||
uri, err := toCpeURI(n)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to parse CPENames %s in %s, err: %w", n, serverName, err)
|
||||
}
|
||||
s.CpeNames[i] = uri
|
||||
}
|
||||
|
||||
s.ContainersIncluded = v.ContainersIncluded
|
||||
if len(s.ContainersIncluded) == 0 {
|
||||
s.ContainersIncluded = d.ContainersIncluded
|
||||
}
|
||||
|
||||
s.ContainersExcluded = v.ContainersExcluded
|
||||
if len(s.ContainersExcluded) == 0 {
|
||||
s.ContainersExcluded = d.ContainersExcluded
|
||||
}
|
||||
|
||||
s.ContainerType = v.ContainerType
|
||||
if len(s.ContainerType) == 0 {
|
||||
s.ContainerType = d.ContainerType
|
||||
}
|
||||
|
||||
s.Containers = v.Containers
|
||||
for contName, cont := range s.Containers {
|
||||
cont.IgnoreCves = append(cont.IgnoreCves, d.IgnoreCves...)
|
||||
s.Containers[contName] = cont
|
||||
}
|
||||
|
||||
if len(v.DependencyCheckXMLPath) != 0 || len(d.DependencyCheckXMLPath) != 0 {
|
||||
return xerrors.Errorf("[DEPRECATED] dependencyCheckXMLPath IS DEPRECATED. USE owaspDCXMLPath INSTEAD: %s", serverName)
|
||||
}
|
||||
|
||||
s.OwaspDCXMLPath = v.OwaspDCXMLPath
|
||||
if len(s.OwaspDCXMLPath) == 0 {
|
||||
s.OwaspDCXMLPath = d.OwaspDCXMLPath
|
||||
}
|
||||
|
||||
s.Memo = v.Memo
|
||||
if s.Memo == "" {
|
||||
s.Memo = d.Memo
|
||||
}
|
||||
|
||||
s.IgnoreCves = v.IgnoreCves
|
||||
for _, cve := range d.IgnoreCves {
|
||||
found := false
|
||||
for _, c := range s.IgnoreCves {
|
||||
if cve == c {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
s.IgnoreCves = append(s.IgnoreCves, cve)
|
||||
}
|
||||
}
|
||||
|
||||
s.IgnorePkgsRegexp = v.IgnorePkgsRegexp
|
||||
for _, pkg := range d.IgnorePkgsRegexp {
|
||||
found := false
|
||||
for _, p := range s.IgnorePkgsRegexp {
|
||||
if pkg == p {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
s.IgnorePkgsRegexp = append(s.IgnorePkgsRegexp, pkg)
|
||||
}
|
||||
}
|
||||
for _, reg := range s.IgnorePkgsRegexp {
|
||||
_, err := regexp.Compile(reg)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to parse %s in %s. err: %w", reg, serverName, err)
|
||||
}
|
||||
}
|
||||
for contName, cont := range s.Containers {
|
||||
for _, reg := range cont.IgnorePkgsRegexp {
|
||||
_, err := regexp.Compile(reg)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to parse %s in %s@%s. err: %w",
|
||||
reg, contName, serverName, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
opt := map[string]interface{}{}
|
||||
for k, v := range d.Optional {
|
||||
opt[k] = v
|
||||
}
|
||||
for k, v := range v.Optional {
|
||||
opt[k] = v
|
||||
}
|
||||
s.Optional = opt
|
||||
|
||||
s.Enablerepo = v.Enablerepo
|
||||
if len(s.Enablerepo) == 0 {
|
||||
s.Enablerepo = d.Enablerepo
|
||||
}
|
||||
if len(s.Enablerepo) != 0 {
|
||||
for _, repo := range s.Enablerepo {
|
||||
switch repo {
|
||||
case "base", "updates":
|
||||
// nop
|
||||
default:
|
||||
return xerrors.Errorf(
|
||||
"For now, enablerepo have to be base or updates: %s, servername: %s",
|
||||
s.Enablerepo, serverName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s.GitHubRepos = v.GitHubRepos
|
||||
for ownerRepo, githubSetting := range s.GitHubRepos {
|
||||
if ss := strings.Split(ownerRepo, "/"); len(ss) != 2 {
|
||||
return xerrors.Errorf("Failed to parse GitHub owner/repo: %s in %s",
|
||||
ownerRepo, serverName)
|
||||
}
|
||||
if githubSetting.Token == "" {
|
||||
return xerrors.Errorf("GitHub owner/repo: %s in %s token is empty",
|
||||
ownerRepo, serverName)
|
||||
}
|
||||
}
|
||||
|
||||
s.UUIDs = v.UUIDs
|
||||
s.Type = v.Type
|
||||
|
||||
s.WordPress.WPVulnDBToken = v.WordPress.WPVulnDBToken
|
||||
s.WordPress.CmdPath = v.WordPress.CmdPath
|
||||
s.WordPress.DocRoot = v.WordPress.DocRoot
|
||||
s.WordPress.OSUser = v.WordPress.OSUser
|
||||
s.WordPress.IgnoreInactive = v.WordPress.IgnoreInactive
|
||||
|
||||
s.LogMsgAnsiColor = Colors[i%len(Colors)]
|
||||
i++
|
||||
|
||||
servers[name] = s
|
||||
servers[serverName] = s
|
||||
}
|
||||
log.Debug("Config loaded")
|
||||
log.Debugf("%s", pp.Sprintf("%v", servers))
|
||||
Conf.Servers = servers
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
func toCpeURI(cpename string) (string, error) {
|
||||
if strings.HasPrefix(cpename, "cpe:2.3:") {
|
||||
wfn, err := naming.UnbindFS(cpename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return naming.BindToURI(wfn), nil
|
||||
} else if strings.HasPrefix(cpename, "cpe:/") {
|
||||
wfn, err := naming.UnbindURI(cpename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return naming.BindToURI(wfn), nil
|
||||
}
|
||||
return "", xerrors.Errorf("Unknown CPE format: %s", cpename)
|
||||
}
|
||||
|
||||
44
config/tomlloader_test.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestToCpeURI(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in string
|
||||
expected string
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
in: "",
|
||||
expected: "",
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
in: "cpe:/a:microsoft:internet_explorer:10",
|
||||
expected: "cpe:/a:microsoft:internet_explorer:10",
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
in: "cpe:2.3:a:microsoft:internet_explorer:10:*:*:*:*:*:*:*",
|
||||
expected: "cpe:/a:microsoft:internet_explorer:10",
|
||||
err: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
actual, err := toCpeURI(tt.in)
|
||||
if err != nil && !tt.err {
|
||||
t.Errorf("[%d] unexpected error occurred, in: %s act: %s, exp: %s",
|
||||
i, tt.in, actual, tt.expected)
|
||||
} else if err == nil && tt.err {
|
||||
t.Errorf("[%d] expected error is not occurred, in: %s act: %s, exp: %s",
|
||||
i, tt.in, actual, tt.expected)
|
||||
}
|
||||
if actual != tt.expected {
|
||||
t.Errorf("[%d] in: %s, actual: %s, expected: %s",
|
||||
i, tt.in, actual, tt.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
38
contrib/future-vuls/README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# future-vuls
|
||||
|
||||
## Main Features
|
||||
|
||||
- upload vuls results json to future-vuls
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
git clone https://github.com/future-architect/vuls.git
|
||||
make build-future-vuls
|
||||
```
|
||||
|
||||
## Command Reference
|
||||
|
||||
```
|
||||
Upload to FutureVuls
|
||||
|
||||
Usage:
|
||||
future-vuls upload [flags]
|
||||
|
||||
Flags:
|
||||
--config string config file (default is $HOME/.cobra.yaml)
|
||||
-g, --group-id int future vuls group id, ENV: VULS_GROUP_ID
|
||||
-h, --help help for upload
|
||||
-s, --stdin input from stdin. ENV: VULS_STDIN
|
||||
-t, --token string future vuls token
|
||||
--url string future vuls upload url
|
||||
--uuid string server uuid. ENV: VULS_SERVER_UUID
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
- update results json
|
||||
|
||||
```
|
||||
cat results.json | future-vuls upload --stdin --token xxxx --url https://xxxx --group-id 1 --uuid xxxx
|
||||
```
|
||||
98
contrib/future-vuls/cmd/main.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/report"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
configFile string
|
||||
stdIn bool
|
||||
jsonDir string
|
||||
serverUUID string
|
||||
groupID int64
|
||||
token string
|
||||
url string
|
||||
)
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
var cmdFvulsUploader = &cobra.Command{
|
||||
Use: "upload",
|
||||
Short: "Upload to FutureVuls",
|
||||
Long: `Upload to FutureVuls`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(serverUUID) == 0 {
|
||||
serverUUID = os.Getenv("VULS_SERVER_UUID")
|
||||
}
|
||||
if groupID == 0 {
|
||||
envGroupID := os.Getenv("VULS_GROUP_ID")
|
||||
if groupID, err = strconv.ParseInt(envGroupID, 10, 64); err != nil {
|
||||
fmt.Printf("Invalid GroupID: %s\n", envGroupID)
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(url) == 0 {
|
||||
url = os.Getenv("VULS_URL")
|
||||
}
|
||||
if len(token) == 0 {
|
||||
token = os.Getenv("VULS_TOKEN")
|
||||
}
|
||||
|
||||
var scanResultJSON []byte
|
||||
if stdIn {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
buf := new(bytes.Buffer)
|
||||
if _, err = buf.ReadFrom(reader); err != nil {
|
||||
return
|
||||
}
|
||||
scanResultJSON = buf.Bytes()
|
||||
} else {
|
||||
fmt.Println("use --stdin option")
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
|
||||
var scanResult models.ScanResult
|
||||
if err = json.Unmarshal(scanResultJSON, &scanResult); err != nil {
|
||||
fmt.Println("Failed to parse json", err)
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
scanResult.ServerUUID = serverUUID
|
||||
|
||||
config.Conf.Saas.GroupID = groupID
|
||||
config.Conf.Saas.Token = token
|
||||
config.Conf.Saas.URL = url
|
||||
if err = (report.SaasWriter{}).Write(scanResult); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
return
|
||||
},
|
||||
}
|
||||
cmdFvulsUploader.PersistentFlags().StringVar(&serverUUID, "uuid", "", "server uuid. ENV: VULS_SERVER_UUID")
|
||||
cmdFvulsUploader.PersistentFlags().StringVar(&configFile, "config", "", "config file (default is $HOME/.cobra.yaml)")
|
||||
cmdFvulsUploader.PersistentFlags().BoolVarP(&stdIn, "stdin", "s", false, "input from stdin. ENV: VULS_STDIN")
|
||||
// TODO Read JSON file from directory
|
||||
// cmdFvulsUploader.Flags().StringVarP(&jsonDir, "results-dir", "d", "./", "vuls scan results json dir")
|
||||
cmdFvulsUploader.PersistentFlags().Int64VarP(&groupID, "group-id", "g", 0, "future vuls group id, ENV: VULS_GROUP_ID")
|
||||
cmdFvulsUploader.PersistentFlags().StringVarP(&token, "token", "t", "", "future vuls token")
|
||||
cmdFvulsUploader.PersistentFlags().StringVar(&url, "url", "", "future vuls upload url")
|
||||
|
||||
var rootCmd = &cobra.Command{Use: "future-vuls"}
|
||||
rootCmd.AddCommand(cmdFvulsUploader)
|
||||
if err = rootCmd.Execute(); err != nil {
|
||||
fmt.Println("Failed to execute command", err)
|
||||
}
|
||||
}
|
||||
71
contrib/owasp-dependency-check/parser/parser.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/knqyf263/go-cpe/naming"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type analysis struct {
|
||||
Dependencies []dependency `xml:"dependencies>dependency"`
|
||||
}
|
||||
|
||||
type dependency struct {
|
||||
Identifiers []vulnerabilityID `xml:"identifiers>vulnerabilityIds"`
|
||||
}
|
||||
|
||||
type vulnerabilityID struct {
|
||||
ID string `xml:"id"`
|
||||
}
|
||||
|
||||
func appendIfMissing(slice []string, str string) []string {
|
||||
for _, s := range slice {
|
||||
if s == str {
|
||||
return slice
|
||||
}
|
||||
}
|
||||
return append(slice, str)
|
||||
}
|
||||
|
||||
// Parse parses OWASP dependency check XML and collect list of cpe
|
||||
func Parse(path string) ([]string, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
log.Warnf("OWASP Dependency Check XML is not found: %s", path)
|
||||
return []string{}, nil
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
b, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to read OWASP Dependency Check XML: %s", path)
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
var anal analysis
|
||||
if err := xml.Unmarshal(b, &anal); err != nil {
|
||||
return nil, xerrors.Errorf("Failed to unmarshal: %s", err)
|
||||
}
|
||||
|
||||
cpes := []string{}
|
||||
for _, d := range anal.Dependencies {
|
||||
for _, ident := range d.Identifiers {
|
||||
id := ident.ID // Start with cpe:2.3:
|
||||
// Convert from CPE 2.3 to CPE 2.2
|
||||
if strings.HasPrefix(id, "cpe:2.3:") {
|
||||
wfn, err := naming.UnbindFS(id)
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
id = naming.BindToURI(wfn)
|
||||
}
|
||||
cpes = appendIfMissing(cpes, id)
|
||||
}
|
||||
}
|
||||
return cpes, nil
|
||||
}
|
||||
35
contrib/trivy/README.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# trivy-to-vuls
|
||||
|
||||
## Main Features
|
||||
|
||||
- convert trivy's results json to vuls's report json
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
git clone https://github.com/future-architect/vuls.git
|
||||
make build-trivy-to-vuls
|
||||
```
|
||||
|
||||
## Command Reference
|
||||
|
||||
```
|
||||
Parse trivy json to vuls results
|
||||
|
||||
Usage:
|
||||
trivy-to-vuls parse [flags]
|
||||
|
||||
Flags:
|
||||
-h, --help help for parse
|
||||
-s, --stdin input from stdin
|
||||
-d, --trivy-json-dir string trivy json dir (default "./")
|
||||
-f, --trivy-json-file-name string trivy json file name (default "results.json")
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
- use trivy output
|
||||
|
||||
```
|
||||
trivy -q image -f=json python:3.4-alpine | trivy-to-vuls parse --stdin
|
||||
```
|
||||
78
contrib/trivy/cmd/main.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/future-architect/vuls/contrib/trivy/parser"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
serverUUID string
|
||||
stdIn bool
|
||||
jsonDir string
|
||||
jsonFileName string
|
||||
)
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
var cmdTrivyToVuls = &cobra.Command{
|
||||
Use: "parse",
|
||||
Short: "Parse trivy json to vuls results",
|
||||
Long: `Parse trivy json to vuls results`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
jsonFilePath := filepath.Join(jsonDir, jsonFileName)
|
||||
var trivyJSON []byte
|
||||
if stdIn {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
buf := new(bytes.Buffer)
|
||||
if _, err = buf.ReadFrom(reader); err != nil {
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
trivyJSON = buf.Bytes()
|
||||
} else {
|
||||
if trivyJSON, err = ioutil.ReadFile(jsonFilePath); err != nil {
|
||||
fmt.Println("Failed to read file", err)
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
scanResult := &models.ScanResult{
|
||||
JSONVersion: models.JSONVersion,
|
||||
ScannedCves: models.VulnInfos{},
|
||||
}
|
||||
if scanResult, err = parser.Parse(trivyJSON, scanResult); err != nil {
|
||||
fmt.Println("Failed to execute command", err)
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
var resultJSON []byte
|
||||
if resultJSON, err = json.MarshalIndent(scanResult, "", " "); err != nil {
|
||||
fmt.Println("Failed to create json", err)
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
fmt.Println(string(resultJSON))
|
||||
return
|
||||
},
|
||||
}
|
||||
cmdTrivyToVuls.Flags().BoolVarP(&stdIn, "stdin", "s", false, "input from stdin")
|
||||
cmdTrivyToVuls.Flags().StringVarP(&jsonDir, "trivy-json-dir", "d", "./", "trivy json dir")
|
||||
cmdTrivyToVuls.Flags().StringVarP(&jsonFileName, "trivy-json-file-name", "f", "results.json", "trivy json file name")
|
||||
|
||||
var rootCmd = &cobra.Command{Use: "trivy-to-vuls"}
|
||||
rootCmd.AddCommand(cmdTrivyToVuls)
|
||||
if err = rootCmd.Execute(); err != nil {
|
||||
fmt.Println("Failed to execute command", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
164
contrib/trivy/parser/parser.go
Normal file
@@ -0,0 +1,164 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/aquasecurity/fanal/analyzer/os"
|
||||
"github.com/aquasecurity/trivy/pkg/report"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
"github.com/future-architect/vuls/models"
|
||||
)
|
||||
|
||||
// Parse :
|
||||
func Parse(vulnJSON []byte, scanResult *models.ScanResult) (result *models.ScanResult, err error) {
|
||||
var trivyResults report.Results
|
||||
if err = json.Unmarshal(vulnJSON, &trivyResults); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pkgs := models.Packages{}
|
||||
vulnInfos := models.VulnInfos{}
|
||||
uniqueLibraryScannerPaths := map[string]models.LibraryScanner{}
|
||||
for _, trivyResult := range trivyResults {
|
||||
for _, vuln := range trivyResult.Vulnerabilities {
|
||||
if _, ok := vulnInfos[vuln.VulnerabilityID]; !ok {
|
||||
vulnInfos[vuln.VulnerabilityID] = models.VulnInfo{
|
||||
CveID: vuln.VulnerabilityID,
|
||||
Confidences: models.Confidences{
|
||||
{
|
||||
Score: 100,
|
||||
DetectionMethod: models.TrivyMatchStr,
|
||||
},
|
||||
},
|
||||
AffectedPackages: models.PackageFixStatuses{},
|
||||
CveContents: models.CveContents{},
|
||||
LibraryFixedIns: models.LibraryFixedIns{},
|
||||
// VulnType : "",
|
||||
}
|
||||
}
|
||||
vulnInfo := vulnInfos[vuln.VulnerabilityID]
|
||||
var notFixedYet bool
|
||||
fixState := ""
|
||||
if len(vuln.FixedVersion) == 0 {
|
||||
notFixedYet = true
|
||||
fixState = "Affected"
|
||||
}
|
||||
var references models.References
|
||||
for _, reference := range vuln.References {
|
||||
references = append(references, models.Reference{
|
||||
Source: "trivy",
|
||||
Link: reference,
|
||||
})
|
||||
}
|
||||
|
||||
sort.Slice(references, func(i, j int) bool {
|
||||
return references[i].Link < references[j].Link
|
||||
})
|
||||
|
||||
vulnInfo.CveContents = models.CveContents{
|
||||
models.Trivy: models.CveContent{
|
||||
Cvss3Severity: vuln.Severity,
|
||||
References: references,
|
||||
Title: vuln.Title,
|
||||
Summary: vuln.Description,
|
||||
},
|
||||
}
|
||||
// do only if image type is Vuln
|
||||
if IsTrivySupportedOS(trivyResult.Type) {
|
||||
pkgs[vuln.PkgName] = models.Package{
|
||||
Name: vuln.PkgName,
|
||||
Version: vuln.InstalledVersion,
|
||||
}
|
||||
vulnInfo.AffectedPackages = append(vulnInfo.AffectedPackages, models.PackageFixStatus{
|
||||
Name: vuln.PkgName,
|
||||
NotFixedYet: notFixedYet,
|
||||
FixState: fixState,
|
||||
FixedIn: vuln.FixedVersion,
|
||||
})
|
||||
|
||||
// overwrite every time if os package
|
||||
scanResult.Family = trivyResult.Type
|
||||
scanResult.ServerName = trivyResult.Target
|
||||
scanResult.Optional = map[string]interface{}{
|
||||
"trivy-target": trivyResult.Target,
|
||||
}
|
||||
scanResult.ScannedAt = time.Now()
|
||||
scanResult.ScannedBy = "trivy"
|
||||
scanResult.ScannedVia = "trivy"
|
||||
} else {
|
||||
// LibraryScanの結果
|
||||
vulnInfo.LibraryFixedIns = append(vulnInfo.LibraryFixedIns, models.LibraryFixedIn{
|
||||
Key: trivyResult.Type,
|
||||
Name: vuln.PkgName,
|
||||
Path: trivyResult.Target,
|
||||
FixedIn: vuln.FixedVersion,
|
||||
})
|
||||
libScanner := uniqueLibraryScannerPaths[trivyResult.Target]
|
||||
libScanner.Libs = append(libScanner.Libs, types.Library{
|
||||
Name: vuln.PkgName,
|
||||
Version: vuln.InstalledVersion,
|
||||
})
|
||||
uniqueLibraryScannerPaths[trivyResult.Target] = libScanner
|
||||
}
|
||||
vulnInfos[vuln.VulnerabilityID] = vulnInfo
|
||||
}
|
||||
}
|
||||
// flatten and unique libraries
|
||||
libraryScanners := make([]models.LibraryScanner, 0, len(uniqueLibraryScannerPaths))
|
||||
for path, v := range uniqueLibraryScannerPaths {
|
||||
uniqueLibrary := map[string]types.Library{}
|
||||
for _, lib := range v.Libs {
|
||||
uniqueLibrary[lib.Name+lib.Version] = lib
|
||||
}
|
||||
|
||||
var libraries []types.Library
|
||||
for _, library := range uniqueLibrary {
|
||||
libraries = append(libraries, library)
|
||||
}
|
||||
|
||||
sort.Slice(libraries, func(i, j int) bool {
|
||||
return libraries[i].Name < libraries[j].Name
|
||||
})
|
||||
|
||||
libscanner := models.LibraryScanner{
|
||||
Path: path,
|
||||
Libs: libraries,
|
||||
}
|
||||
libraryScanners = append(libraryScanners, libscanner)
|
||||
}
|
||||
sort.Slice(libraryScanners, func(i, j int) bool {
|
||||
return libraryScanners[i].Path < libraryScanners[j].Path
|
||||
})
|
||||
scanResult.ScannedCves = vulnInfos
|
||||
scanResult.Packages = pkgs
|
||||
scanResult.LibraryScanners = libraryScanners
|
||||
return scanResult, nil
|
||||
}
|
||||
|
||||
// IsTrivySupportedOS :
|
||||
func IsTrivySupportedOS(family string) bool {
|
||||
supportedFamilies := []string{
|
||||
os.RedHat,
|
||||
os.Debian,
|
||||
os.Ubuntu,
|
||||
os.CentOS,
|
||||
os.Fedora,
|
||||
os.Amazon,
|
||||
os.Oracle,
|
||||
os.Windows,
|
||||
os.OpenSUSE,
|
||||
os.OpenSUSELeap,
|
||||
os.OpenSUSETumbleweed,
|
||||
os.SLES,
|
||||
os.Photon,
|
||||
os.Alpine,
|
||||
}
|
||||
for _, supportedFamily := range supportedFamilies {
|
||||
if family == supportedFamily {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
5482
contrib/trivy/parser/parser_test.go
Normal file
@@ -1,240 +0,0 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package cveapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff"
|
||||
"github.com/parnurzeal/gorequest"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/util"
|
||||
cve "github.com/kotakanbe/go-cve-dictionary/models"
|
||||
)
|
||||
|
||||
// CveClient is api client of CVE disctionary service.
|
||||
var CveClient cvedictClient
|
||||
|
||||
type cvedictClient struct {
|
||||
// httpProxy string
|
||||
baseURL string
|
||||
}
|
||||
|
||||
func (api *cvedictClient) initialize() {
|
||||
api.baseURL = config.Conf.CveDictionaryURL
|
||||
}
|
||||
|
||||
func (api cvedictClient) CheckHealth() (ok bool, err error) {
|
||||
api.initialize()
|
||||
url := fmt.Sprintf("%s/health", api.baseURL)
|
||||
var errs []error
|
||||
var resp *http.Response
|
||||
resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
|
||||
// resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
|
||||
if len(errs) > 0 || resp.StatusCode != 200 {
|
||||
return false, fmt.Errorf("Failed to request to CVE server. url: %s, errs: %v",
|
||||
url,
|
||||
errs,
|
||||
)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
type response struct {
|
||||
Key string
|
||||
CveDetail cve.CveDetail
|
||||
}
|
||||
|
||||
func (api cvedictClient) FetchCveDetails(cveIDs []string) (cveDetails cve.CveDetails, err error) {
|
||||
api.baseURL = config.Conf.CveDictionaryURL
|
||||
reqChan := make(chan string, len(cveIDs))
|
||||
resChan := make(chan response, len(cveIDs))
|
||||
errChan := make(chan error, len(cveIDs))
|
||||
defer close(reqChan)
|
||||
defer close(resChan)
|
||||
defer close(errChan)
|
||||
|
||||
go func() {
|
||||
for _, cveID := range cveIDs {
|
||||
reqChan <- cveID
|
||||
}
|
||||
}()
|
||||
|
||||
concurrency := 10
|
||||
tasks := util.GenWorkers(concurrency)
|
||||
for range cveIDs {
|
||||
tasks <- func() {
|
||||
select {
|
||||
case cveID := <-reqChan:
|
||||
url, err := util.URLPathJoin(api.baseURL, "cves", cveID)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
} else {
|
||||
log.Debugf("HTTP Request to %s", url)
|
||||
api.httpGet(cveID, url, resChan, errChan)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
timeout := time.After(2 * 60 * time.Second)
|
||||
var errs []error
|
||||
for range cveIDs {
|
||||
select {
|
||||
case res := <-resChan:
|
||||
if len(res.CveDetail.CveID) == 0 {
|
||||
cveDetails = append(cveDetails, cve.CveDetail{
|
||||
CveID: res.Key,
|
||||
})
|
||||
} else {
|
||||
cveDetails = append(cveDetails, res.CveDetail)
|
||||
}
|
||||
case err := <-errChan:
|
||||
errs = append(errs, err)
|
||||
case <-timeout:
|
||||
return []cve.CveDetail{}, fmt.Errorf("Timeout Fetching CVE")
|
||||
}
|
||||
}
|
||||
if len(errs) != 0 {
|
||||
return []cve.CveDetail{},
|
||||
fmt.Errorf("Failed to fetch CVE. err: %v", errs)
|
||||
}
|
||||
|
||||
// order by CVE ID desc
|
||||
sort.Sort(cveDetails)
|
||||
return
|
||||
}
|
||||
|
||||
func (api cvedictClient) httpGet(key, url string, resChan chan<- response, errChan chan<- error) {
|
||||
var body string
|
||||
var errs []error
|
||||
var resp *http.Response
|
||||
f := func() (err error) {
|
||||
// resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
|
||||
resp, body, errs = gorequest.New().Get(url).End()
|
||||
if len(errs) > 0 || resp.StatusCode != 200 {
|
||||
return fmt.Errorf("HTTP GET error: %v, code: %d, url: %s", errs, resp.StatusCode, url)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
notify := func(err error, t time.Duration) {
|
||||
log.Warnf("Failed to HTTP GET. retrying in %s seconds. err: %s", t, err)
|
||||
}
|
||||
err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
|
||||
if err != nil {
|
||||
errChan <- fmt.Errorf("HTTP Error %s", err)
|
||||
}
|
||||
cveDetail := cve.CveDetail{}
|
||||
if err := json.Unmarshal([]byte(body), &cveDetail); err != nil {
|
||||
errChan <- fmt.Errorf("Failed to Unmarshall. body: %s, err: %s", body, err)
|
||||
}
|
||||
resChan <- response{
|
||||
key,
|
||||
cveDetail,
|
||||
}
|
||||
}
|
||||
|
||||
// func (api cvedictClient) httpGet(key, url string, query map[string]string, resChan chan<- response, errChan chan<- error) {
|
||||
|
||||
// var body string
|
||||
// var errs []error
|
||||
// var resp *http.Response
|
||||
// f := func() (err error) {
|
||||
// req := gorequest.New().SetDebug(true).Proxy(api.httpProxy).Get(url)
|
||||
// for key := range query {
|
||||
// req = req.Query(fmt.Sprintf("%s=%s", key, query[key])).Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
// }
|
||||
// pp.Println(req)
|
||||
// resp, body, errs = req.End()
|
||||
// if len(errs) > 0 || resp.StatusCode != 200 {
|
||||
// errChan <- fmt.Errorf("HTTP error. errs: %v, url: %s", errs, url)
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
// notify := func(err error, t time.Duration) {
|
||||
// log.Warnf("Failed to get. retrying in %s seconds. err: %s", t, err)
|
||||
// }
|
||||
// err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
|
||||
// if err != nil {
|
||||
// errChan <- fmt.Errorf("HTTP Error %s", err)
|
||||
// }
|
||||
// // resChan <- body
|
||||
// cveDetail := cve.CveDetail{}
|
||||
// if err := json.Unmarshal([]byte(body), &cveDetail); err != nil {
|
||||
// errChan <- fmt.Errorf("Failed to Unmarshall. body: %s, err: %s", body, err)
|
||||
// }
|
||||
// resChan <- response{
|
||||
// key,
|
||||
// cveDetail,
|
||||
// }
|
||||
// }
|
||||
|
||||
type responseGetCveDetailByCpeName struct {
|
||||
CpeName string
|
||||
CveDetails []cve.CveDetail
|
||||
}
|
||||
|
||||
func (api cvedictClient) FetchCveDetailsByCpeName(cpeName string) ([]cve.CveDetail, error) {
|
||||
api.baseURL = config.Conf.CveDictionaryURL
|
||||
|
||||
url, err := util.URLPathJoin(api.baseURL, "cpes")
|
||||
if err != nil {
|
||||
return []cve.CveDetail{}, err
|
||||
}
|
||||
|
||||
query := map[string]string{"name": cpeName}
|
||||
log.Debugf("HTTP Request to %s, query: %#v", url, query)
|
||||
return api.httpPost(cpeName, url, query)
|
||||
}
|
||||
|
||||
func (api cvedictClient) httpPost(key, url string, query map[string]string) ([]cve.CveDetail, error) {
|
||||
var body string
|
||||
var errs []error
|
||||
var resp *http.Response
|
||||
f := func() (err error) {
|
||||
req := gorequest.New().SetDebug(config.Conf.Debug).Post(url)
|
||||
for key := range query {
|
||||
req = req.Send(fmt.Sprintf("%s=%s", key, query[key])).Type("json")
|
||||
}
|
||||
resp, body, errs = req.End()
|
||||
if len(errs) > 0 || resp.StatusCode != 200 {
|
||||
return fmt.Errorf("HTTP POST errors: %v, code: %d, url: %s", errs, resp.StatusCode, url)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
notify := func(err error, t time.Duration) {
|
||||
log.Warnf("Failed to HTTP POST. retrying in %s seconds. err: %s", t, err)
|
||||
}
|
||||
err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
|
||||
if err != nil {
|
||||
return []cve.CveDetail{}, fmt.Errorf("HTTP Error %s", err)
|
||||
}
|
||||
|
||||
cveDetails := []cve.CveDetail{}
|
||||
if err := json.Unmarshal([]byte(body), &cveDetails); err != nil {
|
||||
return []cve.CveDetail{},
|
||||
fmt.Errorf("Failed to Unmarshall. body: %s, err: %s", body, err)
|
||||
}
|
||||
return cveDetails, nil
|
||||
}
|
||||
33
cwe/cwe.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package cwe
|
||||
|
||||
// CweTopTwentyfive2019 has CWE-ID in CWE Top 25
|
||||
var CweTopTwentyfive2019 = map[string]string{
|
||||
"119": "1",
|
||||
"79": "2",
|
||||
"20": "3",
|
||||
"200": "4",
|
||||
"125": "5",
|
||||
"89": "6",
|
||||
"416": "7",
|
||||
"190": "8",
|
||||
"352": "9",
|
||||
"22": "10",
|
||||
"78": "11",
|
||||
"787": "12",
|
||||
"287": "13",
|
||||
"476": "14",
|
||||
"732": "16",
|
||||
"434": "16",
|
||||
"611": "17",
|
||||
"94": "18",
|
||||
"798": "19",
|
||||
"400": "20",
|
||||
"772": "21",
|
||||
"426": "22",
|
||||
"502": "23",
|
||||
"269": "24",
|
||||
"295": "25",
|
||||
}
|
||||
|
||||
// CweTopTwentyfive2019URL has CWE Top25 links
|
||||
var CweTopTwentyfive2019URL = "https://cwe.mitre.org/top25/archive/2019/2019_cwe_top25.html"
|
||||
65
cwe/owasp.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package cwe
|
||||
|
||||
// OwaspTopTen2017 has CWE-ID in OWSP Top 10
|
||||
var OwaspTopTen2017 = map[string]string{
|
||||
"77": "1",
|
||||
"89": "1",
|
||||
"564": "1",
|
||||
"917": "1",
|
||||
|
||||
"287": "2",
|
||||
"384": "2",
|
||||
|
||||
"220": "3",
|
||||
"310": "3",
|
||||
"312": "3",
|
||||
"319": "3",
|
||||
"326": "3",
|
||||
"359": "3",
|
||||
|
||||
"611": "4",
|
||||
|
||||
"22": "5",
|
||||
"284": "5",
|
||||
"285": "5",
|
||||
"639": "5",
|
||||
|
||||
"2": "6",
|
||||
"16": "6",
|
||||
"388": "6",
|
||||
|
||||
"79": "7",
|
||||
|
||||
"502": "8",
|
||||
|
||||
"223": "10",
|
||||
"778": "10",
|
||||
}
|
||||
|
||||
// OwaspTopTen2017GitHubURLEn has GitHub links
|
||||
var OwaspTopTen2017GitHubURLEn = map[string]string{
|
||||
"1": "https://github.com/OWASP/Top10/blob/master/2017/en/0xa1-injection.md",
|
||||
"2": "https://github.com/OWASP/Top10/blob/master/2017/en/0xa2-broken-authentication.md",
|
||||
"3": "https://github.com/OWASP/Top10/blob/master/2017/en/0xa3-sensitive-data-disclosure.md",
|
||||
"4": "https://github.com/OWASP/Top10/blob/master/2017/en/0xa4-xxe.md",
|
||||
"5": "https://github.com/OWASP/Top10/blob/master/2017/en/0xa5-broken-access-control.md",
|
||||
"6": "https://github.com/OWASP/Top10/blob/master/2017/en/0xa6-security-misconfiguration.md",
|
||||
"7": "https://github.com/OWASP/Top10/blob/master/2017/en/0xa7-xss.md",
|
||||
"8": "https://github.com/OWASP/Top10/blob/master/2017/en/0xa8-insecure-deserialization.md",
|
||||
"9": "https://github.com/OWASP/Top10/blob/master/2017/en/0xa9-known-vulns.md<Paste>",
|
||||
"10": "https://github.com/OWASP/Top10/blob/master/2017/en/0xaa-logging-detection-response.md",
|
||||
}
|
||||
|
||||
// OwaspTopTen2017GitHubURLJa has GitHub links
|
||||
var OwaspTopTen2017GitHubURLJa = map[string]string{
|
||||
"1": "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa1-injection.md",
|
||||
"2": "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa2-broken-authentication.md",
|
||||
"3": "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa3-sensitive-data-disclosure.md",
|
||||
"4": "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa4-xxe.md",
|
||||
"5": "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa5-broken-access-control.md",
|
||||
"6": "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa6-security-misconfiguration.md",
|
||||
"7": "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa7-xss.md",
|
||||
"8": "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa8-insecure-deserialization.md",
|
||||
"9": "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa9-known-vulns.md<Paste>",
|
||||
"10": "https://github.com/OWASP/Top10/blob/master/2017/ja/0xaa-logging-detection-response.md",
|
||||
}
|
||||
33
cwe/sans.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package cwe
|
||||
|
||||
// SansTopTwentyfive has CWE-ID in CWE/SANS Top 25
|
||||
var SansTopTwentyfive = map[string]string{
|
||||
"89": "1",
|
||||
"78": "2",
|
||||
"120": "3",
|
||||
"79": "4",
|
||||
"306": "5",
|
||||
"862": "6",
|
||||
"798": "7",
|
||||
"311": "8",
|
||||
"434": "9",
|
||||
"807": "10",
|
||||
"250": "11",
|
||||
"352": "12",
|
||||
"22": "13",
|
||||
"494": "14",
|
||||
"863": "15",
|
||||
"829": "16",
|
||||
"732": "17",
|
||||
"676": "18",
|
||||
"327": "19",
|
||||
"131": "20",
|
||||
"307": "21",
|
||||
"601": "22",
|
||||
"134": "23",
|
||||
"190": "24",
|
||||
"759": "25",
|
||||
}
|
||||
|
||||
// SansTopTwentyfiveURL is a URL of sans 25
|
||||
var SansTopTwentyfiveURL = "https://www.sans.org/top25-software-errors/"
|
||||
272
db/db.go
@@ -1,272 +0,0 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
m "github.com/future-architect/vuls/models"
|
||||
"github.com/jinzhu/gorm"
|
||||
cvedb "github.com/kotakanbe/go-cve-dictionary/db"
|
||||
cve "github.com/kotakanbe/go-cve-dictionary/models"
|
||||
)
|
||||
|
||||
var db *gorm.DB
|
||||
|
||||
// OpenDB opens Database
|
||||
func OpenDB() (err error) {
|
||||
db, err = gorm.Open("sqlite3", config.Conf.DBPath)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Failed to open DB. datafile: %s, err: %s", config.Conf.DBPath, err)
|
||||
return
|
||||
|
||||
}
|
||||
db.LogMode(config.Conf.DebugSQL)
|
||||
return
|
||||
}
|
||||
|
||||
// MigrateDB migrates Database
|
||||
func MigrateDB() error {
|
||||
if err := db.AutoMigrate(
|
||||
&m.ScanHistory{},
|
||||
&m.ScanResult{},
|
||||
// &m.NWLink{},
|
||||
&m.CveInfo{},
|
||||
&m.CpeName{},
|
||||
&m.PackageInfo{},
|
||||
&m.DistroAdvisory{},
|
||||
&cve.CveDetail{},
|
||||
&cve.Jvn{},
|
||||
&cve.Nvd{},
|
||||
&cve.Reference{},
|
||||
&cve.Cpe{},
|
||||
).Error; err != nil {
|
||||
return fmt.Errorf("Failed to migrate. err: %s", err)
|
||||
}
|
||||
|
||||
errMsg := "Failed to create index. err: %s"
|
||||
// if err := db.Model(&m.NWLink{}).
|
||||
// AddIndex("idx_n_w_links_scan_result_id", "scan_result_id").Error; err != nil {
|
||||
// return fmt.Errorf(errMsg, err)
|
||||
// }
|
||||
if err := db.Model(&m.CveInfo{}).
|
||||
AddIndex("idx_cve_infos_scan_result_id", "scan_result_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&m.CpeName{}).
|
||||
AddIndex("idx_cpe_names_cve_info_id", "cve_info_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&m.PackageInfo{}).
|
||||
AddIndex("idx_package_infos_cve_info_id", "cve_info_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&m.DistroAdvisory{}).
|
||||
//TODO check table name
|
||||
AddIndex("idx_distro_advisories_cve_info_id", "cve_info_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&cve.CveDetail{}).
|
||||
AddIndex("idx_cve_detail_cve_info_id", "cve_info_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&cve.CveDetail{}).
|
||||
AddIndex("idx_cve_detail_cveid", "cve_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&cve.Nvd{}).
|
||||
AddIndex("idx_nvds_cve_detail_id", "cve_detail_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&cve.Jvn{}).
|
||||
AddIndex("idx_jvns_cve_detail_id", "cve_detail_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&cve.Cpe{}).
|
||||
AddIndex("idx_cpes_jvn_id", "jvn_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&cve.Reference{}).
|
||||
AddIndex("idx_references_jvn_id", "jvn_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&cve.Cpe{}).
|
||||
AddIndex("idx_cpes_nvd_id", "nvd_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&cve.Reference{}).
|
||||
AddIndex("idx_references_nvd_id", "nvd_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Insert inserts scan results into DB
|
||||
func Insert(results []m.ScanResult) error {
|
||||
for _, r := range results {
|
||||
r.KnownCves = resetGormIDs(r.KnownCves)
|
||||
r.UnknownCves = resetGormIDs(r.UnknownCves)
|
||||
}
|
||||
|
||||
history := m.ScanHistory{
|
||||
ScanResults: results,
|
||||
ScannedAt: time.Now(),
|
||||
}
|
||||
|
||||
db = db.Set("gorm:save_associations", false)
|
||||
if err := db.Create(&history).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
for _, scanResult := range history.ScanResults {
|
||||
scanResult.ScanHistoryID = history.ID
|
||||
if err := db.Create(&scanResult).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := insertCveInfos(scanResult.ID, scanResult.KnownCves); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := insertCveInfos(scanResult.ID, scanResult.UnknownCves); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func insertCveInfos(scanResultID uint, infos []m.CveInfo) error {
|
||||
for _, cveInfo := range infos {
|
||||
cveInfo.ScanResultID = scanResultID
|
||||
if err := db.Create(&cveInfo).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, pack := range cveInfo.Packages {
|
||||
pack.CveInfoID = cveInfo.ID
|
||||
if err := db.Create(&pack).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, distroAdvisory := range cveInfo.DistroAdvisories {
|
||||
distroAdvisory.CveInfoID = cveInfo.ID
|
||||
if err := db.Create(&distroAdvisory).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, cpeName := range cveInfo.CpeNames {
|
||||
cpeName.CveInfoID = cveInfo.ID
|
||||
if err := db.Create(&cpeName).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
db = db.Set("gorm:save_associations", true)
|
||||
cveDetail := cveInfo.CveDetail
|
||||
cveDetail.CveInfoID = cveInfo.ID
|
||||
if err := db.Create(&cveDetail).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
db = db.Set("gorm:save_associations", false)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func resetGormIDs(infos []m.CveInfo) []m.CveInfo {
|
||||
for i := range infos {
|
||||
infos[i].CveDetail.ID = 0
|
||||
// NVD
|
||||
infos[i].CveDetail.Nvd.ID = 0
|
||||
for j := range infos[i].CveDetail.Nvd.Cpes {
|
||||
infos[i].CveDetail.Nvd.Cpes[j].ID = 0
|
||||
}
|
||||
for j := range infos[i].CveDetail.Nvd.References {
|
||||
infos[i].CveDetail.Nvd.References[j].ID = 0
|
||||
}
|
||||
|
||||
// JVN
|
||||
infos[i].CveDetail.Jvn.ID = 0
|
||||
for j := range infos[i].CveDetail.Jvn.Cpes {
|
||||
infos[i].CveDetail.Jvn.Cpes[j].ID = 0
|
||||
}
|
||||
for j := range infos[i].CveDetail.Jvn.References {
|
||||
infos[i].CveDetail.Jvn.References[j].ID = 0
|
||||
}
|
||||
|
||||
//Packages
|
||||
for j := range infos[i].Packages {
|
||||
infos[i].Packages[j].ID = 0
|
||||
infos[i].Packages[j].CveInfoID = 0
|
||||
}
|
||||
}
|
||||
return infos
|
||||
}
|
||||
|
||||
// SelectLatestScanHistory select latest scan history from DB
|
||||
func SelectLatestScanHistory() (m.ScanHistory, error) {
|
||||
scanHistory := m.ScanHistory{}
|
||||
db.Order("scanned_at desc").First(&scanHistory)
|
||||
|
||||
if scanHistory.ID == 0 {
|
||||
return m.ScanHistory{}, fmt.Errorf("No scanHistory records")
|
||||
}
|
||||
|
||||
results := []m.ScanResult{}
|
||||
db.Model(&scanHistory).Related(&results, "ScanResults")
|
||||
scanHistory.ScanResults = results
|
||||
|
||||
for i, r := range results {
|
||||
// nw := []m.NWLink{}
|
||||
// db.Model(&r).Related(&nw, "NWLinks")
|
||||
// scanHistory.ScanResults[i].NWLinks = nw
|
||||
|
||||
knownCves := selectCveInfos(&r, "KnownCves")
|
||||
sort.Sort(m.CveInfos(knownCves))
|
||||
scanHistory.ScanResults[i].KnownCves = knownCves
|
||||
}
|
||||
return scanHistory, nil
|
||||
}
|
||||
|
||||
func selectCveInfos(result *m.ScanResult, fieldName string) []m.CveInfo {
|
||||
cveInfos := []m.CveInfo{}
|
||||
db.Model(&result).Related(&cveInfos, fieldName)
|
||||
|
||||
for i, cveInfo := range cveInfos {
|
||||
cveDetail := cve.CveDetail{}
|
||||
db.Model(&cveInfo).Related(&cveDetail, "CveDetail")
|
||||
id := cveDetail.CveID
|
||||
filledCveDetail := cvedb.Get(id, db)
|
||||
cveInfos[i].CveDetail = filledCveDetail
|
||||
|
||||
packs := []m.PackageInfo{}
|
||||
db.Model(&cveInfo).Related(&packs, "Packages")
|
||||
cveInfos[i].Packages = packs
|
||||
|
||||
advisories := []m.DistroAdvisory{}
|
||||
db.Model(&cveInfo).Related(&advisories, "DistroAdvisories")
|
||||
cveInfos[i].DistroAdvisories = advisories
|
||||
|
||||
names := []m.CpeName{}
|
||||
db.Model(&cveInfo).Related(&names, "CpeNames")
|
||||
cveInfos[i].CpeNames = names
|
||||
}
|
||||
return cveInfos
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
FROM golang:1.6
|
||||
RUN apt-get update \
|
||||
&& apt-get upgrade -y \
|
||||
&& apt-get install -y git openssh-client gcc
|
||||
WORKDIR /app
|
||||
RUN go get github.com/kotakanbe/go-cve-dictionary
|
||||
RUN go get github.com/future-architect/vuls
|
||||
COPY fetch.sh .
|
||||
RUN /bin/bash /app/fetch.sh
|
||||
COPY config.toml .
|
||||
COPY run.sh .
|
||||
ENTRYPOINT ["/bin/bash", "/app/run.sh"]
|
||||
COPY id_rsa .
|
||||
COPY id_rsa.pub .
|
||||
@@ -1,2 +0,0 @@
|
||||
# Must do
|
||||
* Edit your config.toml to match your infrastructure
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/bin/bash
|
||||
for i in {2002..2016}; do go-cve-dictionary fetchnvd -years $i ; done
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/bin/bash
|
||||
go-cve-dictionary server &
|
||||
sleep 2
|
||||
vuls scan -config /app/config.toml -report-slack
|
||||
27
errof/errof.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package errof
|
||||
|
||||
// ErrorCode is vuls error code
|
||||
type ErrorCode string
|
||||
|
||||
// Error is vuls error
|
||||
type Error struct {
|
||||
Code ErrorCode
|
||||
Message string
|
||||
}
|
||||
|
||||
func (e Error) Error() string {
|
||||
return e.Message
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrFailedToAccessGithubAPI is error of github alert's api access
|
||||
ErrFailedToAccessGithubAPI ErrorCode = "ErrFailedToAccessGithubAPI"
|
||||
)
|
||||
|
||||
// New :
|
||||
func New(code ErrorCode, msg string) Error {
|
||||
return Error{
|
||||
Code: code,
|
||||
Message: msg,
|
||||
}
|
||||
}
|
||||
117
exploit/exploit.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package exploit
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
cnf "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/mozqnet/go-exploitdb/db"
|
||||
exploitmodels "github.com/mozqnet/go-exploitdb/models"
|
||||
"github.com/parnurzeal/gorequest"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// FillWithExploit fills exploit information that has in Exploit
|
||||
func FillWithExploit(driver db.DB, r *models.ScanResult) (nExploitCve int, err error) {
|
||||
if cnf.Conf.Exploit.IsFetchViaHTTP() {
|
||||
var cveIDs []string
|
||||
for cveID := range r.ScannedCves {
|
||||
cveIDs = append(cveIDs, cveID)
|
||||
}
|
||||
prefix, _ := util.URLPathJoin(cnf.Conf.Exploit.URL, "cves")
|
||||
responses, err := getCvesViaHTTP(cveIDs, prefix)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for _, res := range responses {
|
||||
exps := []*exploitmodels.Exploit{}
|
||||
if err := json.Unmarshal([]byte(res.json), &exps); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
exploits := ConvertToModels(exps)
|
||||
v, ok := r.ScannedCves[res.request.cveID]
|
||||
if ok {
|
||||
v.Exploits = exploits
|
||||
}
|
||||
r.ScannedCves[res.request.cveID] = v
|
||||
nExploitCve++
|
||||
}
|
||||
} else {
|
||||
if driver == nil {
|
||||
return 0, nil
|
||||
}
|
||||
for cveID, vuln := range r.ScannedCves {
|
||||
if cveID == "" {
|
||||
continue
|
||||
}
|
||||
es := driver.GetExploitByCveID(cveID)
|
||||
if len(es) == 0 {
|
||||
continue
|
||||
}
|
||||
exploits := ConvertToModels(es)
|
||||
vuln.Exploits = exploits
|
||||
r.ScannedCves[cveID] = vuln
|
||||
nExploitCve++
|
||||
}
|
||||
}
|
||||
return nExploitCve, nil
|
||||
}
|
||||
|
||||
// ConvertToModels converts gost model to vuls model
|
||||
func ConvertToModels(es []*exploitmodels.Exploit) (exploits []models.Exploit) {
|
||||
for _, e := range es {
|
||||
var documentURL, shellURL *string
|
||||
if e.OffensiveSecurity != nil {
|
||||
os := e.OffensiveSecurity
|
||||
if os.Document != nil {
|
||||
documentURL = &os.Document.DocumentURL
|
||||
}
|
||||
if os.ShellCode != nil {
|
||||
shellURL = &os.ShellCode.ShellCodeURL
|
||||
}
|
||||
}
|
||||
exploit := models.Exploit{
|
||||
ExploitType: e.ExploitType,
|
||||
ID: e.ExploitUniqueID,
|
||||
URL: e.URL,
|
||||
Description: e.Description,
|
||||
DocumentURL: documentURL,
|
||||
ShellCodeURL: shellURL,
|
||||
}
|
||||
exploits = append(exploits, exploit)
|
||||
}
|
||||
return exploits
|
||||
}
|
||||
|
||||
// CheckHTTPHealth do health check
|
||||
func CheckHTTPHealth() error {
|
||||
if !cnf.Conf.Exploit.IsFetchViaHTTP() {
|
||||
return nil
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/health", cnf.Conf.Exploit.URL)
|
||||
var errs []error
|
||||
var resp *http.Response
|
||||
resp, _, errs = gorequest.New().Get(url).End()
|
||||
// resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
|
||||
// resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
|
||||
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
|
||||
return xerrors.Errorf("Failed to connect to exploit server. url: %s, errs: %w", url, errs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckIfExploitFetched checks if oval entries are in DB by family, release.
|
||||
func CheckIfExploitFetched(driver db.DB, osFamily string) (fetched bool, err error) {
|
||||
//TODO
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// CheckIfExploitFresh checks if oval entries are fresh enough
|
||||
func CheckIfExploitFresh(driver db.DB, osFamily string) (ok bool, err error) {
|
||||
//TODO
|
||||
return true, nil
|
||||
}
|
||||
115
exploit/util.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package exploit
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/parnurzeal/gorequest"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type response struct {
|
||||
request request
|
||||
json string
|
||||
}
|
||||
|
||||
func getCvesViaHTTP(cveIDs []string, urlPrefix string) (
|
||||
responses []response, err error) {
|
||||
nReq := len(cveIDs)
|
||||
reqChan := make(chan request, nReq)
|
||||
resChan := make(chan response, nReq)
|
||||
errChan := make(chan error, nReq)
|
||||
defer close(reqChan)
|
||||
defer close(resChan)
|
||||
defer close(errChan)
|
||||
|
||||
go func() {
|
||||
for _, cveID := range cveIDs {
|
||||
reqChan <- request{
|
||||
cveID: cveID,
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
concurrency := 10
|
||||
tasks := util.GenWorkers(concurrency)
|
||||
for i := 0; i < nReq; i++ {
|
||||
tasks <- func() {
|
||||
select {
|
||||
case req := <-reqChan:
|
||||
url, err := util.URLPathJoin(
|
||||
urlPrefix,
|
||||
req.cveID,
|
||||
)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
} else {
|
||||
util.Log.Debugf("HTTP Request to %s", url)
|
||||
httpGet(url, req, resChan, errChan)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
timeout := time.After(2 * 60 * time.Second)
|
||||
var errs []error
|
||||
for i := 0; i < nReq; i++ {
|
||||
select {
|
||||
case res := <-resChan:
|
||||
responses = append(responses, res)
|
||||
case err := <-errChan:
|
||||
errs = append(errs, err)
|
||||
case <-timeout:
|
||||
return nil, xerrors.New("Timeout Fetching OVAL")
|
||||
}
|
||||
}
|
||||
if len(errs) != 0 {
|
||||
return nil, xerrors.Errorf("Failed to fetch OVAL. err: %w", errs)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type request struct {
|
||||
osMajorVersion string
|
||||
packName string
|
||||
isSrcPack bool
|
||||
cveID string
|
||||
}
|
||||
|
||||
func httpGet(url string, req request, resChan chan<- response, errChan chan<- error) {
|
||||
var body string
|
||||
var errs []error
|
||||
var resp *http.Response
|
||||
count, retryMax := 0, 3
|
||||
f := func() (err error) {
|
||||
// resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
|
||||
resp, body, errs = gorequest.New().Get(url).End()
|
||||
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
|
||||
count++
|
||||
if count == retryMax {
|
||||
return nil
|
||||
}
|
||||
return xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %w", url, resp, errs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
notify := func(err error, t time.Duration) {
|
||||
util.Log.Warnf("Failed to HTTP GET. retrying in %s seconds. err: %s", t, err)
|
||||
}
|
||||
err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
|
||||
if err != nil {
|
||||
errChan <- xerrors.Errorf("HTTP Error %w", err)
|
||||
return
|
||||
}
|
||||
if count == retryMax {
|
||||
errChan <- xerrors.New("Retry count exceeded")
|
||||
return
|
||||
}
|
||||
|
||||
resChan <- response{
|
||||
request: req,
|
||||
json: body,
|
||||
}
|
||||
}
|
||||
179
github/github.go
Normal file
@@ -0,0 +1,179 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/errof"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
// FillGitHubSecurityAlerts access to owner/repo on GitHub and fetch security alerts of the repository via GitHub API v4 GraphQL and then set to the given ScanResult.
|
||||
// https://help.github.com/articles/about-security-alerts-for-vulnerable-dependencies/
|
||||
func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (nCVEs int, err error) {
|
||||
src := oauth2.StaticTokenSource(
|
||||
&oauth2.Token{AccessToken: token},
|
||||
)
|
||||
httpClient := oauth2.NewClient(context.Background(), src)
|
||||
|
||||
// TODO Use `https://github.com/shurcooL/githubv4` if the tool supports vulnerabilityAlerts Endpoint
|
||||
// Memo : https://developer.github.com/v4/explorer/
|
||||
const jsonfmt = `{"query":
|
||||
"query { repository(owner:\"%s\", name:\"%s\") { url vulnerabilityAlerts(first: %d, %s) { pageInfo { endCursor hasNextPage startCursor } edges { node { id dismissReason dismissedAt securityVulnerability{ package { name ecosystem } severity vulnerableVersionRange firstPatchedVersion { identifier } } securityAdvisory { description ghsaId permalink publishedAt summary updatedAt withdrawnAt origin severity references { url } identifiers { type value } } } } } } } "}`
|
||||
after := ""
|
||||
|
||||
for {
|
||||
jsonStr := fmt.Sprintf(jsonfmt, owner, repo, 100, after)
|
||||
req, err := http.NewRequest("POST",
|
||||
"https://api.github.com/graphql",
|
||||
bytes.NewBuffer([]byte(jsonStr)),
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// https://developer.github.com/v4/previews/#repository-vulnerability-alerts
|
||||
// To toggle this preview and access data, need to provide a custom media type in the Accept header:
|
||||
// MEMO: I tried to get the affected version via GitHub API. Bit it seems difficult to determin the affected version if there are multiple dependency files such as package.json.
|
||||
// TODO remove this header if it is no longer preview status in the future.
|
||||
req.Header.Set("Accept", "application/vnd.github.package-deletes-preview+json")
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
alerts := SecurityAlerts{}
|
||||
if err := json.Unmarshal(body, &alerts); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// util.Log.Debugf("%s", pp.Sprint(alerts))
|
||||
// util.Log.Debugf("%s", string(body))
|
||||
if alerts.Data.Repository.URL == "" {
|
||||
return 0, errof.New(
|
||||
errof.ErrFailedToAccessGithubAPI,
|
||||
fmt.Sprintf("Failed to access to GitHub API. Response: %s", string(body)),
|
||||
)
|
||||
}
|
||||
|
||||
for _, v := range alerts.Data.Repository.VulnerabilityAlerts.Edges {
|
||||
if config.Conf.IgnoreGitHubDismissed && v.Node.DismissReason != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
pkgName := fmt.Sprintf("%s %s",
|
||||
alerts.Data.Repository.URL, v.Node.SecurityVulnerability.Package.Name)
|
||||
|
||||
m := models.GitHubSecurityAlert{
|
||||
PackageName: pkgName,
|
||||
FixedIn: v.Node.SecurityVulnerability.FirstPatchedVersion.Identifier,
|
||||
AffectedRange: v.Node.SecurityVulnerability.VulnerableVersionRange,
|
||||
Dismissed: len(v.Node.DismissReason) != 0,
|
||||
DismissedAt: v.Node.DismissedAt,
|
||||
DismissReason: v.Node.DismissReason,
|
||||
}
|
||||
|
||||
cveIDs, other := []string{}, []string{}
|
||||
for _, identifier := range v.Node.SecurityAdvisory.Identifiers {
|
||||
if identifier.Type == "CVE" {
|
||||
cveIDs = append(cveIDs, identifier.Value)
|
||||
} else {
|
||||
other = append(other, identifier.Value)
|
||||
}
|
||||
}
|
||||
|
||||
// If CVE-ID has not been assigned, use the GHSA ID etc as a ID.
|
||||
if len(cveIDs) == 0 {
|
||||
cveIDs = other
|
||||
}
|
||||
|
||||
for _, cveID := range cveIDs {
|
||||
if val, ok := r.ScannedCves[cveID]; ok {
|
||||
val.GitHubSecurityAlerts = val.GitHubSecurityAlerts.Add(m)
|
||||
r.ScannedCves[cveID] = val
|
||||
nCVEs++
|
||||
} else {
|
||||
v := models.VulnInfo{
|
||||
CveID: cveID,
|
||||
Confidences: models.Confidences{models.GitHubMatch},
|
||||
GitHubSecurityAlerts: models.GitHubSecurityAlerts{m},
|
||||
}
|
||||
r.ScannedCves[cveID] = v
|
||||
nCVEs++
|
||||
}
|
||||
}
|
||||
}
|
||||
if !alerts.Data.Repository.VulnerabilityAlerts.PageInfo.HasNextPage {
|
||||
break
|
||||
}
|
||||
after = fmt.Sprintf(`after: \"%s\"`, alerts.Data.Repository.VulnerabilityAlerts.PageInfo.EndCursor)
|
||||
}
|
||||
return nCVEs, err
|
||||
}
|
||||
|
||||
//SecurityAlerts has detected CVE-IDs, PackageNames, Refs
|
||||
type SecurityAlerts struct {
|
||||
Data struct {
|
||||
Repository struct {
|
||||
URL string `json:"url"`
|
||||
VulnerabilityAlerts struct {
|
||||
PageInfo struct {
|
||||
EndCursor string `json:"endCursor"`
|
||||
HasNextPage bool `json:"hasNextPage"`
|
||||
StartCursor string `json:"startCursor"`
|
||||
} `json:"pageInfo"`
|
||||
Edges []struct {
|
||||
Node struct {
|
||||
ID string `json:"id"`
|
||||
DismissReason string `json:"dismissReason"`
|
||||
DismissedAt time.Time `json:"dismissedAt"`
|
||||
SecurityVulnerability struct {
|
||||
Package struct {
|
||||
Name string `json:"name"`
|
||||
Ecosystem string `json:"ecosystem"`
|
||||
} `json:"package"`
|
||||
Severity string `json:"severity"`
|
||||
VulnerableVersionRange string `json:"vulnerableVersionRange"`
|
||||
FirstPatchedVersion struct {
|
||||
Identifier string `json:"identifier"`
|
||||
} `json:"firstPatchedVersion"`
|
||||
} `json:"securityVulnerability"`
|
||||
SecurityAdvisory struct {
|
||||
Description string `json:"description"`
|
||||
GhsaID string `json:"ghsaId"`
|
||||
Permalink string `json:"permalink"`
|
||||
PublishedAt time.Time `json:"publishedAt"`
|
||||
Summary string `json:"summary"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
WithdrawnAt time.Time `json:"withdrawnAt"`
|
||||
Origin string `json:"origin"`
|
||||
Severity string `json:"severity"`
|
||||
References []struct {
|
||||
URL string `json:"url"`
|
||||
} `json:"references"`
|
||||
Identifiers []struct {
|
||||
Type string `json:"type"`
|
||||
Value string `json:"value"`
|
||||
} `json:"identifiers"`
|
||||
} `json:"securityAdvisory"`
|
||||
} `json:"node"`
|
||||
} `json:"edges"`
|
||||
} `json:"vulnerabilityAlerts"`
|
||||
} `json:"repository"`
|
||||
} `json:"data"`
|
||||
}
|
||||
56
go.mod
Normal file
@@ -0,0 +1,56 @@
|
||||
module github.com/future-architect/vuls
|
||||
|
||||
go 1.14
|
||||
|
||||
replace (
|
||||
gopkg.in/mattn/go-colorable.v0 => github.com/mattn/go-colorable v0.1.0
|
||||
gopkg.in/mattn/go-isatty.v0 => github.com/mattn/go-isatty v0.0.6
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go v43.3.0+incompatible
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91
|
||||
github.com/aquasecurity/fanal v0.0.0-20200615091807-df25cfa5f9af
|
||||
github.com/aquasecurity/trivy v0.9.1
|
||||
github.com/aquasecurity/trivy-db v0.0.0-20200616161554-cd5b3da29bc8
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a
|
||||
github.com/aws/aws-sdk-go v1.33.21
|
||||
github.com/boltdb/bolt v1.3.1
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible
|
||||
github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b
|
||||
github.com/google/subcommands v1.2.0
|
||||
github.com/gosuri/uitable v0.0.4
|
||||
github.com/hashicorp/go-uuid v1.0.2
|
||||
github.com/hashicorp/go-version v1.2.1
|
||||
github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c
|
||||
github.com/jesseduffield/gocui v0.3.0
|
||||
github.com/k0kubun/pp v3.0.1+incompatible
|
||||
github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f
|
||||
github.com/knqyf263/go-cpe v0.0.0-20180327054844-659663f6eca2
|
||||
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d
|
||||
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936
|
||||
github.com/knqyf263/go-version v1.1.1
|
||||
github.com/knqyf263/gost v0.1.4
|
||||
github.com/kotakanbe/go-cve-dictionary v0.5.0
|
||||
github.com/kotakanbe/go-pingscanner v0.1.0
|
||||
github.com/kotakanbe/goval-dictionary v0.2.10
|
||||
github.com/kotakanbe/logrus-prefixed-formatter v0.0.0-20180123152602-928f7356cb96
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/mozqnet/go-exploitdb v0.1.0
|
||||
github.com/nlopes/slack v0.6.0
|
||||
github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.4
|
||||
github.com/parnurzeal/gorequest v0.2.16
|
||||
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
|
||||
github.com/sirupsen/logrus v1.6.0
|
||||
github.com/spf13/afero v1.3.0
|
||||
github.com/spf13/cobra v1.0.0
|
||||
github.com/takuzoo3868/go-msfdb v0.1.1
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
|
||||
k8s.io/utils v0.0.0-20200619165400-6e3d28b6ed19
|
||||
)
|
||||
51
gost/base.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
cnf "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/knqyf263/gost/db"
|
||||
"github.com/parnurzeal/gorequest"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// Base is a base struct
|
||||
type Base struct {
|
||||
}
|
||||
|
||||
// FillCVEsWithRedHat fills cve information that has in Gost
|
||||
func (b Base) FillCVEsWithRedHat(driver db.DB, r *models.ScanResult) error {
|
||||
return RedHat{}.fillFixed(driver, r)
|
||||
}
|
||||
|
||||
// CheckHTTPHealth do health check
|
||||
func (b Base) CheckHTTPHealth() error {
|
||||
if !cnf.Conf.Gost.IsFetchViaHTTP() {
|
||||
return nil
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/health", cnf.Conf.Gost.URL)
|
||||
var errs []error
|
||||
var resp *http.Response
|
||||
resp, _, errs = gorequest.New().Get(url).End()
|
||||
// resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
|
||||
// resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
|
||||
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
|
||||
return xerrors.Errorf("Failed to connect to gost server. url: %s, errs: %w", url, errs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckIfGostFetched checks if oval entries are in DB by family, release.
|
||||
func (b Base) CheckIfGostFetched(driver db.DB, osFamily string) (fetched bool, err error) {
|
||||
//TODO
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// CheckIfGostFresh checks if oval entries are fresh enough
|
||||
func (b Base) CheckIfGostFresh(driver db.DB, osFamily string) (ok bool, err error) {
|
||||
//TODO
|
||||
return true, nil
|
||||
}
|
||||
188
gost/debian.go
Normal file
@@ -0,0 +1,188 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/knqyf263/gost/db"
|
||||
gostmodels "github.com/knqyf263/gost/models"
|
||||
)
|
||||
|
||||
// Debian is Gost client for Debian GNU/Linux
|
||||
type Debian struct {
|
||||
Base
|
||||
}
|
||||
|
||||
type packCves struct {
|
||||
packName string
|
||||
isSrcPack bool
|
||||
cves []models.CveContent
|
||||
}
|
||||
|
||||
func (deb Debian) Supported(major string) bool {
|
||||
_, ok := map[string]string{
|
||||
"8": "jessie",
|
||||
"9": "stretch",
|
||||
"10": "buster",
|
||||
}[major]
|
||||
return ok
|
||||
}
|
||||
|
||||
// DetectUnfixed fills cve information that has in Gost
|
||||
func (deb Debian) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (nCVEs int, err error) {
|
||||
if !deb.Supported(major(r.Release)) {
|
||||
// only logging
|
||||
util.Log.Warnf("Debian %s is not supported yet", r.Release)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
linuxImage := "linux-image-" + r.RunningKernel.Release
|
||||
// Add linux and set the version of running kernel to search OVAL.
|
||||
if r.Container.ContainerID == "" {
|
||||
newVer := ""
|
||||
if p, ok := r.Packages[linuxImage]; ok {
|
||||
newVer = p.NewVersion
|
||||
}
|
||||
r.Packages["linux"] = models.Package{
|
||||
Name: "linux",
|
||||
Version: r.RunningKernel.Version,
|
||||
NewVersion: newVer,
|
||||
}
|
||||
}
|
||||
|
||||
// Debian Security Tracker does not support Package for Raspbian, so skip it.
|
||||
var scanResult models.ScanResult
|
||||
if r.Family != config.Raspbian {
|
||||
scanResult = *r
|
||||
} else {
|
||||
scanResult = r.RemoveRaspbianPackFromResult()
|
||||
}
|
||||
|
||||
packCvesList := []packCves{}
|
||||
if config.Conf.Gost.IsFetchViaHTTP() {
|
||||
url, _ := util.URLPathJoin(config.Conf.Gost.URL, "debian", major(scanResult.Release), "pkgs")
|
||||
responses, err := getAllUnfixedCvesViaHTTP(r, url)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
for _, res := range responses {
|
||||
debCves := map[string]gostmodels.DebianCVE{}
|
||||
if err := json.Unmarshal([]byte(res.json), &debCves); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cves := []models.CveContent{}
|
||||
for _, debcve := range debCves {
|
||||
cves = append(cves, *deb.ConvertToModel(&debcve))
|
||||
}
|
||||
packCvesList = append(packCvesList, packCves{
|
||||
packName: res.request.packName,
|
||||
isSrcPack: res.request.isSrcPack,
|
||||
cves: cves,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if driver == nil {
|
||||
return 0, nil
|
||||
}
|
||||
for _, pack := range scanResult.Packages {
|
||||
cveDebs := driver.GetUnfixedCvesDebian(major(scanResult.Release), pack.Name)
|
||||
cves := []models.CveContent{}
|
||||
for _, cveDeb := range cveDebs {
|
||||
cves = append(cves, *deb.ConvertToModel(&cveDeb))
|
||||
}
|
||||
packCvesList = append(packCvesList, packCves{
|
||||
packName: pack.Name,
|
||||
isSrcPack: false,
|
||||
cves: cves,
|
||||
})
|
||||
}
|
||||
|
||||
// SrcPack
|
||||
for _, pack := range scanResult.SrcPackages {
|
||||
cveDebs := driver.GetUnfixedCvesDebian(major(scanResult.Release), pack.Name)
|
||||
cves := []models.CveContent{}
|
||||
for _, cveDeb := range cveDebs {
|
||||
cves = append(cves, *deb.ConvertToModel(&cveDeb))
|
||||
}
|
||||
packCvesList = append(packCvesList, packCves{
|
||||
packName: pack.Name,
|
||||
isSrcPack: true,
|
||||
cves: cves,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
delete(r.Packages, "linux")
|
||||
|
||||
for _, p := range packCvesList {
|
||||
for _, cve := range p.cves {
|
||||
v, ok := r.ScannedCves[cve.CveID]
|
||||
if ok {
|
||||
if v.CveContents == nil {
|
||||
v.CveContents = models.NewCveContents(cve)
|
||||
} else {
|
||||
v.CveContents[models.DebianSecurityTracker] = cve
|
||||
}
|
||||
} else {
|
||||
v = models.VulnInfo{
|
||||
CveID: cve.CveID,
|
||||
CveContents: models.NewCveContents(cve),
|
||||
Confidences: models.Confidences{models.DebianSecurityTrackerMatch},
|
||||
}
|
||||
nCVEs++
|
||||
}
|
||||
|
||||
names := []string{}
|
||||
if p.isSrcPack {
|
||||
if srcPack, ok := r.SrcPackages[p.packName]; ok {
|
||||
for _, binName := range srcPack.BinaryNames {
|
||||
if _, ok := r.Packages[binName]; ok {
|
||||
names = append(names, binName)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if p.packName == "linux" {
|
||||
names = append(names, linuxImage)
|
||||
} else {
|
||||
names = append(names, p.packName)
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range names {
|
||||
v.AffectedPackages = v.AffectedPackages.Store(models.PackageFixStatus{
|
||||
Name: name,
|
||||
FixState: "open",
|
||||
NotFixedYet: true,
|
||||
})
|
||||
}
|
||||
r.ScannedCves[cve.CveID] = v
|
||||
}
|
||||
}
|
||||
return nCVEs, nil
|
||||
}
|
||||
|
||||
// ConvertToModel converts gost model to vuls model
|
||||
func (deb Debian) ConvertToModel(cve *gostmodels.DebianCVE) *models.CveContent {
|
||||
severity := ""
|
||||
for _, p := range cve.Package {
|
||||
for _, r := range p.Release {
|
||||
severity = r.Urgency
|
||||
break
|
||||
}
|
||||
}
|
||||
return &models.CveContent{
|
||||
Type: models.DebianSecurityTracker,
|
||||
CveID: cve.CveID,
|
||||
Summary: cve.Description,
|
||||
Cvss2Severity: severity,
|
||||
Cvss3Severity: severity,
|
||||
SourceLink: "https://security-tracker.debian.org/tracker/" + cve.CveID,
|
||||
Optional: map[string]string{
|
||||
"attack range": cve.Scope,
|
||||
},
|
||||
}
|
||||
}
|
||||
61
gost/debian_test.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package gost
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestDebian_Supported(t *testing.T) {
|
||||
type fields struct {
|
||||
Base Base
|
||||
}
|
||||
type args struct {
|
||||
major string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "8 is supported",
|
||||
args: args{
|
||||
major: "8",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "9 is supported",
|
||||
args: args{
|
||||
major: "9",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "10 is supported",
|
||||
args: args{
|
||||
major: "10",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "11 is not supported yet",
|
||||
args: args{
|
||||
major: "11",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "empty string is not supported yet",
|
||||
args: args{
|
||||
major: "",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
deb := Debian{}
|
||||
if got := deb.Supported(tt.args.major); got != tt.want {
|
||||
t.Errorf("Debian.Supported() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
33
gost/gost.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
cnf "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/knqyf263/gost/db"
|
||||
)
|
||||
|
||||
// Client is the interface of OVAL client.
|
||||
type Client interface {
|
||||
DetectUnfixed(db.DB, *models.ScanResult, bool) (int, error)
|
||||
FillCVEsWithRedHat(db.DB, *models.ScanResult) error
|
||||
|
||||
//TODO implement
|
||||
// CheckHTTPHealth() error
|
||||
// CheckIfGostFetched checks if Gost entries are fetched
|
||||
// CheckIfGostFetched(db.DB, string, string) (bool, error)
|
||||
// CheckIfGostFresh(db.DB, string, string) (bool, error)
|
||||
}
|
||||
|
||||
// NewClient make Client by family
|
||||
func NewClient(family string) Client {
|
||||
switch family {
|
||||
case cnf.RedHat, cnf.CentOS:
|
||||
return RedHat{}
|
||||
case cnf.Debian, cnf.Raspbian:
|
||||
return Debian{}
|
||||
case cnf.Windows:
|
||||
return Microsoft{}
|
||||
default:
|
||||
return Pseudo{}
|
||||
}
|
||||
}
|
||||
129
gost/gost_test.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/future-architect/vuls/models"
|
||||
gostmodels "github.com/knqyf263/gost/models"
|
||||
)
|
||||
|
||||
func TestSetPackageStates(t *testing.T) {
|
||||
var tests = []struct {
|
||||
pkgstats []gostmodels.RedhatPackageState
|
||||
installed models.Packages
|
||||
release string
|
||||
in models.VulnInfo
|
||||
out models.PackageFixStatuses
|
||||
}{
|
||||
|
||||
//0 one
|
||||
{
|
||||
pkgstats: []gostmodels.RedhatPackageState{
|
||||
{
|
||||
FixState: "Will not fix",
|
||||
PackageName: "bouncycastle",
|
||||
Cpe: "cpe:/o:redhat:enterprise_linux:7",
|
||||
},
|
||||
},
|
||||
installed: models.Packages{
|
||||
"bouncycastle": models.Package{},
|
||||
},
|
||||
release: "7",
|
||||
in: models.VulnInfo{},
|
||||
out: []models.PackageFixStatus{
|
||||
{
|
||||
Name: "bouncycastle",
|
||||
FixState: "Will not fix",
|
||||
NotFixedYet: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
//1 two
|
||||
{
|
||||
pkgstats: []gostmodels.RedhatPackageState{
|
||||
{
|
||||
FixState: "Will not fix",
|
||||
PackageName: "bouncycastle",
|
||||
Cpe: "cpe:/o:redhat:enterprise_linux:7",
|
||||
},
|
||||
{
|
||||
FixState: "Fix deferred",
|
||||
PackageName: "pack_a",
|
||||
Cpe: "cpe:/o:redhat:enterprise_linux:7",
|
||||
},
|
||||
// ignore not-installed-package
|
||||
{
|
||||
FixState: "Fix deferred",
|
||||
PackageName: "pack_b",
|
||||
Cpe: "cpe:/o:redhat:enterprise_linux:7",
|
||||
},
|
||||
},
|
||||
installed: models.Packages{
|
||||
"bouncycastle": models.Package{},
|
||||
"pack_a": models.Package{},
|
||||
},
|
||||
release: "7",
|
||||
in: models.VulnInfo{},
|
||||
out: []models.PackageFixStatus{
|
||||
{
|
||||
Name: "bouncycastle",
|
||||
FixState: "Will not fix",
|
||||
NotFixedYet: true,
|
||||
},
|
||||
{
|
||||
Name: "pack_a",
|
||||
FixState: "Fix deferred",
|
||||
NotFixedYet: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
//2 ignore affected
|
||||
{
|
||||
pkgstats: []gostmodels.RedhatPackageState{
|
||||
{
|
||||
FixState: "affected",
|
||||
PackageName: "bouncycastle",
|
||||
Cpe: "cpe:/o:redhat:enterprise_linux:7",
|
||||
},
|
||||
},
|
||||
installed: models.Packages{
|
||||
"bouncycastle": models.Package{},
|
||||
},
|
||||
release: "7",
|
||||
in: models.VulnInfo{
|
||||
AffectedPackages: models.PackageFixStatuses{},
|
||||
},
|
||||
out: models.PackageFixStatuses{},
|
||||
},
|
||||
|
||||
//3 look only the same os release.
|
||||
{
|
||||
pkgstats: []gostmodels.RedhatPackageState{
|
||||
{
|
||||
FixState: "Will not fix",
|
||||
PackageName: "bouncycastle",
|
||||
Cpe: "cpe:/o:redhat:enterprise_linux:6",
|
||||
},
|
||||
},
|
||||
installed: models.Packages{
|
||||
"bouncycastle": models.Package{},
|
||||
},
|
||||
release: "7",
|
||||
in: models.VulnInfo{
|
||||
AffectedPackages: models.PackageFixStatuses{},
|
||||
},
|
||||
out: models.PackageFixStatuses{},
|
||||
},
|
||||
}
|
||||
|
||||
r := RedHat{}
|
||||
for i, tt := range tests {
|
||||
out := r.mergePackageStates(tt.in, tt.pkgstats, tt.installed, tt.release)
|
||||
if ok := reflect.DeepEqual(tt.out, out); !ok {
|
||||
t.Errorf("[%d]\nexpected: %v:%T\n actual: %v:%T\n", i, tt.out, tt.out, out, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
99
gost/microsoft.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/knqyf263/gost/db"
|
||||
gostmodels "github.com/knqyf263/gost/models"
|
||||
)
|
||||
|
||||
// Microsoft is Gost client for windows
|
||||
type Microsoft struct {
|
||||
Base
|
||||
}
|
||||
|
||||
// DetectUnfixed fills cve information that has in Gost
|
||||
func (ms Microsoft) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (nCVEs int, err error) {
|
||||
if driver == nil {
|
||||
return 0, nil
|
||||
}
|
||||
cveIDs := []string{}
|
||||
for cveID := range r.ScannedCves {
|
||||
cveIDs = append(cveIDs, cveID)
|
||||
}
|
||||
for cveID, msCve := range driver.GetMicrosoftMulti(cveIDs) {
|
||||
if _, ok := r.ScannedCves[cveID]; !ok {
|
||||
continue
|
||||
}
|
||||
cveCont := ms.ConvertToModel(&msCve)
|
||||
v, _ := r.ScannedCves[cveID]
|
||||
if v.CveContents == nil {
|
||||
v.CveContents = models.CveContents{}
|
||||
}
|
||||
v.CveContents[models.Microsoft] = *cveCont
|
||||
r.ScannedCves[cveID] = v
|
||||
}
|
||||
return len(cveIDs), nil
|
||||
}
|
||||
|
||||
// ConvertToModel converts gost model to vuls model
|
||||
func (ms Microsoft) ConvertToModel(cve *gostmodels.MicrosoftCVE) *models.CveContent {
|
||||
v3score := 0.0
|
||||
var v3Vector string
|
||||
for _, scoreSet := range cve.ScoreSets {
|
||||
if v3score < scoreSet.BaseScore {
|
||||
v3score = scoreSet.BaseScore
|
||||
v3Vector = scoreSet.Vector
|
||||
}
|
||||
}
|
||||
|
||||
var v3Severity string
|
||||
for _, s := range cve.Severity {
|
||||
v3Severity = s.Description
|
||||
}
|
||||
|
||||
var refs []models.Reference
|
||||
for _, r := range cve.References {
|
||||
if r.AttrType == "External" {
|
||||
refs = append(refs, models.Reference{Link: r.URL})
|
||||
}
|
||||
}
|
||||
|
||||
var cwe []string
|
||||
if 0 < len(cve.CWE) {
|
||||
cwe = []string{cve.CWE}
|
||||
}
|
||||
|
||||
option := map[string]string{}
|
||||
if 0 < len(cve.ExploitStatus) {
|
||||
option["exploit"] = cve.ExploitStatus
|
||||
}
|
||||
if 0 < len(cve.Workaround) {
|
||||
option["workaround"] = cve.Workaround
|
||||
}
|
||||
kbids := []string{}
|
||||
for _, kbid := range cve.KBIDs {
|
||||
kbids = append(kbids, kbid.KBID)
|
||||
}
|
||||
if 0 < len(kbids) {
|
||||
option["kbids"] = strings.Join(kbids, ",")
|
||||
}
|
||||
|
||||
return &models.CveContent{
|
||||
Type: models.Microsoft,
|
||||
CveID: cve.CveID,
|
||||
Title: cve.Title,
|
||||
Summary: cve.Description,
|
||||
Cvss3Score: v3score,
|
||||
Cvss3Vector: v3Vector,
|
||||
Cvss3Severity: v3Severity,
|
||||
References: refs,
|
||||
CweIDs: cwe,
|
||||
Mitigation: cve.Mitigation,
|
||||
Published: cve.PublishDate,
|
||||
LastModified: cve.LastUpdateDate,
|
||||
SourceLink: "https://portal.msrc.microsoft.com/ja-jp/security-guidance/advisory/" + cve.CveID,
|
||||
Optional: option,
|
||||
}
|
||||
}
|
||||
22
gost/pseudo.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/knqyf263/gost/db"
|
||||
)
|
||||
|
||||
// Pseudo is Gost client except for RedHat family and Debian
|
||||
type Pseudo struct {
|
||||
Base
|
||||
}
|
||||
|
||||
// DetectUnfixed fills cve information that has in Gost
|
||||
func (pse Pseudo) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func major(osVer string) (majorVersion string) {
|
||||
return strings.Split(osVer, ".")[0]
|
||||
}
|
||||
269
gost/redhat.go
Normal file
@@ -0,0 +1,269 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/knqyf263/gost/db"
|
||||
gostmodels "github.com/knqyf263/gost/models"
|
||||
)
|
||||
|
||||
// RedHat is Gost client for RedHat family linux
|
||||
type RedHat struct {
|
||||
Base
|
||||
}
|
||||
|
||||
// DetectUnfixed fills cve information that has in Gost
|
||||
func (red RedHat) DetectUnfixed(driver db.DB, r *models.ScanResult, ignoreWillNotFix bool) (nCVEs int, err error) {
|
||||
return red.fillUnfixed(driver, r, ignoreWillNotFix)
|
||||
}
|
||||
|
||||
func (red RedHat) fillFixed(driver db.DB, r *models.ScanResult) error {
|
||||
cveIDs := []string{}
|
||||
for cveID, vuln := range r.ScannedCves {
|
||||
if _, ok := vuln.CveContents[models.RedHatAPI]; ok {
|
||||
continue
|
||||
}
|
||||
cveIDs = append(cveIDs, cveID)
|
||||
}
|
||||
|
||||
if config.Conf.Gost.IsFetchViaHTTP() {
|
||||
prefix, _ := util.URLPathJoin(config.Conf.Gost.URL,
|
||||
"redhat", "cves")
|
||||
responses, err := getCvesViaHTTP(cveIDs, prefix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, res := range responses {
|
||||
redCve := gostmodels.RedhatCVE{}
|
||||
if err := json.Unmarshal([]byte(res.json), &redCve); err != nil {
|
||||
return err
|
||||
}
|
||||
if redCve.ID == 0 {
|
||||
continue
|
||||
}
|
||||
cveCont := red.ConvertToModel(&redCve)
|
||||
v, ok := r.ScannedCves[res.request.cveID]
|
||||
if ok {
|
||||
if v.CveContents == nil {
|
||||
v.CveContents = models.NewCveContents(*cveCont)
|
||||
} else {
|
||||
v.CveContents[models.RedHatAPI] = *cveCont
|
||||
}
|
||||
} else {
|
||||
v = models.VulnInfo{
|
||||
CveID: cveCont.CveID,
|
||||
CveContents: models.NewCveContents(*cveCont),
|
||||
Confidences: models.Confidences{models.RedHatAPIMatch},
|
||||
}
|
||||
}
|
||||
r.ScannedCves[res.request.cveID] = v
|
||||
}
|
||||
} else {
|
||||
if driver == nil {
|
||||
return nil
|
||||
}
|
||||
for cveID, redCve := range driver.GetRedhatMulti(cveIDs) {
|
||||
if len(redCve.Name) == 0 {
|
||||
continue
|
||||
}
|
||||
cveCont := red.ConvertToModel(&redCve)
|
||||
v, ok := r.ScannedCves[cveID]
|
||||
if ok {
|
||||
if v.CveContents == nil {
|
||||
v.CveContents = models.NewCveContents(*cveCont)
|
||||
} else {
|
||||
v.CveContents[models.RedHatAPI] = *cveCont
|
||||
}
|
||||
} else {
|
||||
v = models.VulnInfo{
|
||||
CveID: cveCont.CveID,
|
||||
CveContents: models.NewCveContents(*cveCont),
|
||||
Confidences: models.Confidences{models.RedHatAPIMatch},
|
||||
}
|
||||
}
|
||||
r.ScannedCves[cveID] = v
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (red RedHat) fillUnfixed(driver db.DB, r *models.ScanResult, ignoreWillNotFix bool) (nCVEs int, err error) {
|
||||
if config.Conf.Gost.IsFetchViaHTTP() {
|
||||
prefix, _ := util.URLPathJoin(config.Conf.Gost.URL,
|
||||
"redhat", major(r.Release), "pkgs")
|
||||
responses, err := getAllUnfixedCvesViaHTTP(r, prefix)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for _, res := range responses {
|
||||
// CVE-ID: RedhatCVE
|
||||
cves := map[string]gostmodels.RedhatCVE{}
|
||||
if err := json.Unmarshal([]byte(res.json), &cves); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
for _, cve := range cves {
|
||||
cveCont := red.ConvertToModel(&cve)
|
||||
v, ok := r.ScannedCves[cve.Name]
|
||||
if ok {
|
||||
if v.CveContents == nil {
|
||||
v.CveContents = models.NewCveContents(*cveCont)
|
||||
} else {
|
||||
v.CveContents[models.RedHatAPI] = *cveCont
|
||||
}
|
||||
} else {
|
||||
v = models.VulnInfo{
|
||||
CveID: cveCont.CveID,
|
||||
CveContents: models.NewCveContents(*cveCont),
|
||||
Confidences: models.Confidences{models.RedHatAPIMatch},
|
||||
}
|
||||
nCVEs++
|
||||
}
|
||||
pkgStats := red.mergePackageStates(v,
|
||||
cve.PackageState, r.Packages, r.Release)
|
||||
if 0 < len(pkgStats) {
|
||||
v.AffectedPackages = pkgStats
|
||||
r.ScannedCves[cve.Name] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if driver == nil {
|
||||
return 0, nil
|
||||
}
|
||||
for _, pack := range r.Packages {
|
||||
// CVE-ID: RedhatCVE
|
||||
cves := driver.GetUnfixedCvesRedhat(major(r.Release), pack.Name, ignoreWillNotFix)
|
||||
for _, cve := range cves {
|
||||
cveCont := red.ConvertToModel(&cve)
|
||||
v, ok := r.ScannedCves[cve.Name]
|
||||
if ok {
|
||||
if v.CveContents == nil {
|
||||
v.CveContents = models.NewCveContents(*cveCont)
|
||||
} else {
|
||||
v.CveContents[models.RedHatAPI] = *cveCont
|
||||
}
|
||||
} else {
|
||||
v = models.VulnInfo{
|
||||
CveID: cveCont.CveID,
|
||||
CveContents: models.NewCveContents(*cveCont),
|
||||
Confidences: models.Confidences{models.RedHatAPIMatch},
|
||||
}
|
||||
nCVEs++
|
||||
}
|
||||
|
||||
pkgStats := red.mergePackageStates(v,
|
||||
cve.PackageState, r.Packages, r.Release)
|
||||
if 0 < len(pkgStats) {
|
||||
v.AffectedPackages = pkgStats
|
||||
r.ScannedCves[cve.Name] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nCVEs, nil
|
||||
}
|
||||
|
||||
func (red RedHat) mergePackageStates(v models.VulnInfo, ps []gostmodels.RedhatPackageState, installed models.Packages, release string) (pkgStats models.PackageFixStatuses) {
|
||||
pkgStats = v.AffectedPackages
|
||||
for _, pstate := range ps {
|
||||
if pstate.Cpe !=
|
||||
"cpe:/o:redhat:enterprise_linux:"+major(release) {
|
||||
return
|
||||
}
|
||||
|
||||
if !(pstate.FixState == "Will not fix" ||
|
||||
pstate.FixState == "Fix deferred" ||
|
||||
pstate.FixState == "Affected") {
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := installed[pstate.PackageName]; !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notFixedYet := false
|
||||
switch pstate.FixState {
|
||||
case "Will not fix", "Fix deferred", "Affected":
|
||||
notFixedYet = true
|
||||
}
|
||||
|
||||
pkgStats = pkgStats.Store(models.PackageFixStatus{
|
||||
Name: pstate.PackageName,
|
||||
FixState: pstate.FixState,
|
||||
NotFixedYet: notFixedYet,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (red RedHat) parseCwe(str string) (cwes []string) {
|
||||
if str != "" {
|
||||
s := strings.Replace(str, "(", "|", -1)
|
||||
s = strings.Replace(s, ")", "|", -1)
|
||||
s = strings.Replace(s, "->", "|", -1)
|
||||
for _, s := range strings.Split(s, "|") {
|
||||
if s != "" {
|
||||
cwes = append(cwes, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ConvertToModel converts gost model to vuls model
|
||||
func (red RedHat) ConvertToModel(cve *gostmodels.RedhatCVE) *models.CveContent {
|
||||
cwes := red.parseCwe(cve.Cwe)
|
||||
|
||||
details := []string{}
|
||||
for _, detail := range cve.Details {
|
||||
details = append(details, detail.Detail)
|
||||
}
|
||||
|
||||
v2score := 0.0
|
||||
if cve.Cvss.CvssBaseScore != "" {
|
||||
v2score, _ = strconv.ParseFloat(cve.Cvss.CvssBaseScore, 64)
|
||||
}
|
||||
v2severity := ""
|
||||
if v2score != 0 {
|
||||
v2severity = cve.ThreatSeverity
|
||||
}
|
||||
|
||||
v3score := 0.0
|
||||
if cve.Cvss3.Cvss3BaseScore != "" {
|
||||
v3score, _ = strconv.ParseFloat(cve.Cvss3.Cvss3BaseScore, 64)
|
||||
}
|
||||
v3severity := ""
|
||||
if v3score != 0 {
|
||||
v3severity = cve.ThreatSeverity
|
||||
}
|
||||
|
||||
refs := []models.Reference{}
|
||||
for _, r := range cve.References {
|
||||
refs = append(refs, models.Reference{Link: r.Reference})
|
||||
}
|
||||
|
||||
return &models.CveContent{
|
||||
Type: models.RedHatAPI,
|
||||
CveID: cve.Name,
|
||||
Title: cve.Bugzilla.Description,
|
||||
Summary: strings.Join(details, "\n"),
|
||||
Cvss2Score: v2score,
|
||||
Cvss2Vector: cve.Cvss.CvssScoringVector,
|
||||
Cvss2Severity: v2severity,
|
||||
Cvss3Score: v3score,
|
||||
Cvss3Vector: cve.Cvss3.Cvss3ScoringVector,
|
||||
Cvss3Severity: v3severity,
|
||||
References: refs,
|
||||
CweIDs: cwes,
|
||||
Mitigation: cve.Mitigation,
|
||||
Published: cve.PublicDate,
|
||||
SourceLink: "https://access.redhat.com/security/cve/" + cve.Name,
|
||||
}
|
||||
}
|
||||
37
gost/redhat_test.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseCwe(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in string
|
||||
out []string
|
||||
}{
|
||||
{
|
||||
in: "CWE-665->(CWE-200|CWE-89)",
|
||||
out: []string{"CWE-665", "CWE-200", "CWE-89"},
|
||||
},
|
||||
{
|
||||
in: "CWE-841->CWE-770->CWE-454",
|
||||
out: []string{"CWE-841", "CWE-770", "CWE-454"},
|
||||
},
|
||||
{
|
||||
in: "(CWE-122|CWE-125)",
|
||||
out: []string{"CWE-122", "CWE-125"},
|
||||
},
|
||||
}
|
||||
|
||||
r := RedHat{}
|
||||
for i, tt := range tests {
|
||||
out := r.parseCwe(tt.in)
|
||||
sort.Strings(out)
|
||||
sort.Strings(tt.out)
|
||||
if !reflect.DeepEqual(tt.out, out) {
|
||||
t.Errorf("[%d]expected: %s, actual: %s", i, tt.out, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
183
gost/util.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/parnurzeal/gorequest"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type response struct {
|
||||
request request
|
||||
json string
|
||||
}
|
||||
|
||||
func getCvesViaHTTP(cveIDs []string, urlPrefix string) (
|
||||
responses []response, err error) {
|
||||
nReq := len(cveIDs)
|
||||
reqChan := make(chan request, nReq)
|
||||
resChan := make(chan response, nReq)
|
||||
errChan := make(chan error, nReq)
|
||||
defer close(reqChan)
|
||||
defer close(resChan)
|
||||
defer close(errChan)
|
||||
|
||||
go func() {
|
||||
for _, cveID := range cveIDs {
|
||||
reqChan <- request{
|
||||
cveID: cveID,
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
concurrency := 10
|
||||
tasks := util.GenWorkers(concurrency)
|
||||
for i := 0; i < nReq; i++ {
|
||||
tasks <- func() {
|
||||
select {
|
||||
case req := <-reqChan:
|
||||
url, err := util.URLPathJoin(
|
||||
urlPrefix,
|
||||
req.cveID,
|
||||
)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
} else {
|
||||
util.Log.Debugf("HTTP Request to %s", url)
|
||||
httpGet(url, req, resChan, errChan)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
timeout := time.After(2 * 60 * time.Second)
|
||||
var errs []error
|
||||
for i := 0; i < nReq; i++ {
|
||||
select {
|
||||
case res := <-resChan:
|
||||
responses = append(responses, res)
|
||||
case err := <-errChan:
|
||||
errs = append(errs, err)
|
||||
case <-timeout:
|
||||
return nil, xerrors.New("Timeout Fetching OVAL")
|
||||
}
|
||||
}
|
||||
if len(errs) != 0 {
|
||||
return nil, xerrors.Errorf("Failed to fetch OVAL. err: %w", errs)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type request struct {
|
||||
osMajorVersion string
|
||||
packName string
|
||||
isSrcPack bool
|
||||
cveID string
|
||||
}
|
||||
|
||||
func getAllUnfixedCvesViaHTTP(r *models.ScanResult, urlPrefix string) (
|
||||
responses []response, err error) {
|
||||
|
||||
nReq := len(r.Packages) + len(r.SrcPackages)
|
||||
reqChan := make(chan request, nReq)
|
||||
resChan := make(chan response, nReq)
|
||||
errChan := make(chan error, nReq)
|
||||
defer close(reqChan)
|
||||
defer close(resChan)
|
||||
defer close(errChan)
|
||||
|
||||
go func() {
|
||||
for _, pack := range r.Packages {
|
||||
reqChan <- request{
|
||||
osMajorVersion: major(r.Release),
|
||||
packName: pack.Name,
|
||||
isSrcPack: false,
|
||||
}
|
||||
}
|
||||
for _, pack := range r.SrcPackages {
|
||||
reqChan <- request{
|
||||
osMajorVersion: major(r.Release),
|
||||
packName: pack.Name,
|
||||
isSrcPack: true,
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
concurrency := 10
|
||||
tasks := util.GenWorkers(concurrency)
|
||||
for i := 0; i < nReq; i++ {
|
||||
tasks <- func() {
|
||||
select {
|
||||
case req := <-reqChan:
|
||||
url, err := util.URLPathJoin(
|
||||
urlPrefix,
|
||||
req.packName,
|
||||
"unfixed-cves",
|
||||
)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
} else {
|
||||
util.Log.Debugf("HTTP Request to %s", url)
|
||||
httpGet(url, req, resChan, errChan)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
timeout := time.After(2 * 60 * time.Second)
|
||||
var errs []error
|
||||
for i := 0; i < nReq; i++ {
|
||||
select {
|
||||
case res := <-resChan:
|
||||
responses = append(responses, res)
|
||||
case err := <-errChan:
|
||||
errs = append(errs, err)
|
||||
case <-timeout:
|
||||
return nil, xerrors.New("Timeout Fetching OVAL")
|
||||
}
|
||||
}
|
||||
if len(errs) != 0 {
|
||||
return nil, xerrors.Errorf("Failed to fetch OVAL. err: %w", errs)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func httpGet(url string, req request, resChan chan<- response, errChan chan<- error) {
|
||||
var body string
|
||||
var errs []error
|
||||
var resp *http.Response
|
||||
count, retryMax := 0, 3
|
||||
f := func() (err error) {
|
||||
// resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
|
||||
resp, body, errs = gorequest.New().Get(url).End()
|
||||
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
|
||||
count++
|
||||
if count == retryMax {
|
||||
return nil
|
||||
}
|
||||
return xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %w", url, resp, errs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
notify := func(err error, t time.Duration) {
|
||||
util.Log.Warnf("Failed to HTTP GET. retrying in %s seconds. err: %s", t, err)
|
||||
}
|
||||
err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
|
||||
if err != nil {
|
||||
errChan <- xerrors.Errorf("HTTP Error %w", err)
|
||||
return
|
||||
}
|
||||
if count == retryMax {
|
||||
errChan <- xerrors.New("Retry count exceeded")
|
||||
return
|
||||
}
|
||||
|
||||
resChan <- response{
|
||||
request: req,
|
||||
json: body,
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 247 KiB |
BIN
img/vuls-abstract.png
Normal file
|
After Width: | Height: | Size: 297 KiB |
@@ -1,979 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
|
||||
<!--Created by yEd 3.14.2-->
|
||||
<key attr.name="Description" attr.type="string" for="graph" id="d0"/>
|
||||
<key for="port" id="d1" yfiles.type="portgraphics"/>
|
||||
<key for="port" id="d2" yfiles.type="portgeometry"/>
|
||||
<key for="port" id="d3" yfiles.type="portuserdata"/>
|
||||
<key attr.name="url" attr.type="string" for="node" id="d4"/>
|
||||
<key attr.name="description" attr.type="string" for="node" id="d5"/>
|
||||
<key for="node" id="d6" yfiles.type="nodegraphics"/>
|
||||
<key for="graphml" id="d7" yfiles.type="resources"/>
|
||||
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
|
||||
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
|
||||
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
|
||||
<graph edgedefault="directed" id="G">
|
||||
<data key="d0"/>
|
||||
<node id="n0">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.cloud">
|
||||
<y:Geometry height="50.0" width="80.0" x="269.4041252136233" y="446.4841308593749"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="38.0" y="23.0">
|
||||
<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n1" yfiles.foldertype="group">
|
||||
<data key="d4"/>
|
||||
<data key="d6">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="285.54366048177087" width="173.0" x="66.40412521362327" y="347.9090576171874"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="173.0" x="0.0" y="0.0">Vulnerbility Database</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="54" bottomF="53.63557942708337" left="30" leftF="29.90412521362373" right="28" rightF="28.09587478637627" top="27" topF="27.242065429687557"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 1</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n1:">
|
||||
<node id="n1::n0">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="70.0" width="85.0" x="111.308250427247" y="494.8171386718749"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="63.279296875" x="10.8603515625" y="18.8671875">JVN
|
||||
(Japanese)<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n1::n1">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="70.0" width="85.0" x="111.308250427247" y="411.81713867187494"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="29.69921875" x="27.650390625" y="25.93359375">NVD<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<node id="n2" yfiles.foldertype="group">
|
||||
<data key="d4"/>
|
||||
<data key="d6">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="285.54366048177087" width="137.0" x="1209.345874786376" y="347.9090576171874"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="137.0" x="0.0" y="0.0">Linux Support</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="4" bottomF="4.059529622395871" left="5" leftF="4.85411262512207" right="8" rightF="8.14588737487793" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 2</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n2:">
|
||||
<node id="n2::n0">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="71.484130859375" width="94.0" x="1229.1999874114981" y="463.7420654296874"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="64.158203125" x="14.9208984375" y="19.6092529296875">apptitude
|
||||
changelog<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n2::n1">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="71.484130859375" width="94.0" x="1229.1999874114981" y="384.5750732421874"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="64.158203125" x="14.9208984375" y="19.6092529296875">yum
|
||||
changelog<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n2::n2">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="71.484130859375" width="94.0" x="1229.1999874114981" y="542.9090576171874"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="92.828125" x="0.5859375" y="19.6092529296875">RHSA (RedHat)
|
||||
ALAS (Amazon)<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<node id="n3">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.cloud">
|
||||
<y:Geometry height="50.0" width="80.0" x="1109.272931098937" y="399.1136678059895"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="38.0" y="23.0">
|
||||
<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n4">
|
||||
<data key="d6">
|
||||
<y:SVGNode>
|
||||
<y:Geometry height="121.666015625" width="137.0" x="942.2729310989371" y="363.2806599934895"/>
|
||||
<y:Fill color="#CCCCFF" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="45.63671875" x="45.681640625" y="-30.475463867187386">servers<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.5" labelRatioY="0.5" nodeRatioX="0.16655736770072993" nodeRatioY="-0.5" offsetX="0.0" offsetY="-12.342651367187386" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="66.5" y="58.8330078125">
|
||||
<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:SVGNodeProperties usingVisualBounds="false"/>
|
||||
<y:SVGModel svgBoundsPolicy="0">
|
||||
<y:SVGContent refid="1"/>
|
||||
</y:SVGModel>
|
||||
</y:SVGNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n5" yfiles.foldertype="group">
|
||||
<data key="d4"/>
|
||||
<data key="d6">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="285.54366048177087" width="220.24999999999977" x="662.2499999999998" y="347.9090576171874"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="220.24999999999977" x="0.0" y="0.0">Vuls</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="12" bottomF="11.710652669270871" left="10" leftF="9.999999999999773" right="0" rightF="0.0" top="31" topF="30.57621256510413"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 3</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n5:">
|
||||
<node id="n5::n0">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="80.0" x="787.4999999999995" y="495.1512858072915"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="42.595703125" x="18.7021484375" y="15.93359375">Report<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n5::n1">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="80.0" x="787.4999999999995" y="556.7420654296874"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="54.40234375" x="12.798828125" y="15.93359375">TUI View<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n5::n2">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="180.25" x="687.2499999999995" y="415.1512858072915"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="30.68359375" x="74.783203125" y="15.93359375">Scan<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<node id="n6">
|
||||
<data key="d6">
|
||||
<y:SVGNode>
|
||||
<y:Geometry height="64.96826171875" width="56.554100036621094" x="1083.5729436874383" y="568.4844563802083"/>
|
||||
<y:Fill color="#CCCCFF" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="100.890625" x="-22.168262481689453" y="68.96826171875">System Operator<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="-0.5" nodeRatioX="0.0" nodeRatioY="0.5" offsetX="0.0" offsetY="4.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:SVGNodeProperties usingVisualBounds="true"/>
|
||||
<y:SVGModel svgBoundsPolicy="0">
|
||||
<y:SVGContent refid="2"/>
|
||||
</y:SVGModel>
|
||||
</y:SVGNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n7">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
|
||||
<y:Geometry height="70.0" width="60.5" x="696.9999999999995" y="546.7420654296874"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="28.25" y="33.0">
|
||||
<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="48.4140625" x="6.04296875" y="25.93359375">SQLite3<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n8">
|
||||
<data key="d6">
|
||||
<y:SVGNode>
|
||||
<y:Geometry height="37.0" width="109.57881927490234" x="991.1335277557366" y="532.4466756184895"/>
|
||||
<y:Fill color="#CCCCFF" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="sandwich" modelPosition="s" textColor="#000000" visible="true" width="4.0" x="52.78940963745117" y="41.0"/>
|
||||
<y:SVGNodeProperties usingVisualBounds="true"/>
|
||||
<y:SVGModel svgBoundsPolicy="0">
|
||||
<y:SVGContent refid="3"/>
|
||||
</y:SVGModel>
|
||||
</y:SVGNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n9">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.bpmn.Artifact.withShadow">
|
||||
<y:Geometry height="24.0" width="35.0" x="943.5205974578851" y="538.9466756184895"/>
|
||||
<y:Fill color="#FFFFFFE6" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="15.5" y="28.0">
|
||||
<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="-0.5" nodeRatioX="0.0" nodeRatioY="0.5" offsetX="0.0" offsetY="4.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:StyleProperties>
|
||||
<y:Property class="java.awt.Color" name="com.yworks.bpmn.icon.line.color" value="#000000"/>
|
||||
<y:Property class="java.awt.Color" name="com.yworks.bpmn.icon.fill2" value="#d4d4d4cc"/>
|
||||
<y:Property class="java.awt.Color" name="com.yworks.bpmn.icon.fill" value="#ffffffe6"/>
|
||||
<y:Property class="com.yworks.yfiles.bpmn.view.BPMNTypeEnum" name="com.yworks.bpmn.type" value="ARTIFACT_TYPE_REQUEST_MESSAGE"/>
|
||||
</y:StyleProperties>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n10" yfiles.foldertype="group">
|
||||
<data key="d4"/>
|
||||
<data key="d6">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="285.54366048177087" width="233.0" x="379.4041252136233" y="347.9090576171874"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="233.0" x="0.0" y="0.0">go-cve-dictionary</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="2" leftF="1.5" right="5" rightF="5.0" top="62" topF="61.9090576171875"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 4</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n10:">
|
||||
<node id="n10::n0">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
|
||||
<y:Geometry height="70.0" width="60.5" x="447.15412521362305" y="548.4527180989583"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="28.25" y="33.0">
|
||||
<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="48.4140625" x="6.04296875" y="25.93359375">SQLite3<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n10::n1">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="101.0" x="491.4041252136233" y="446.4841308593749"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="73.943359375" x="13.5283203125" y="15.93359375">HTTP server<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n10::n2">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="80.0" x="395.9041252136233" y="446.4841308593749"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="46.796875" x="16.6015625" y="15.93359375">Fetcher<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<edge id="n10::e0" source="n10::n1" target="n10::n0">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="none"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e0" source="n10::n2" target="n0">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="none"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="106.240234375" x="-141.12006313536395" y="46.068162980709076">Fetch
|
||||
Vulnerability data<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="62.2009754807092" distanceToCenter="true" position="left" ratio="41.499957391955974" segment="-1"/>
|
||||
</y:ModelParameter>
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
||||
</y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e1" source="n3" target="n2">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e2" source="n5::n2" target="n10::n1">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="34.626953125" x="-64.74898719787609" y="-33.01884181664411">HTTP<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.5" segment="0"/>
|
||||
</y:ModelParameter>
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
||||
</y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e3" source="n5::n1" target="n7">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="none"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e4" source="n0" target="n1">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e5" source="n5::n0" target="n6">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e6" source="n4" target="n3">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="n5::e0" source="n5::n2" target="n5::n0">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e7" source="n5::n2" target="n4">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="25.744140625" x="44.03690814971833" y="17.093924778052497">SSH<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="1.0" segment="-1"/>
|
||||
</y:ModelParameter>
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
||||
</y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e8" source="n6" target="n5::n1">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="n10::e1" source="n10::n2" target="n10::n0">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="none"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e9" source="n5::n2" target="n7">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="none"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
</graph>
|
||||
<data key="d7">
|
||||
<y:Resources>
|
||||
<y:Resource id="1"><?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px" y="0px" width="68px" height="60px" viewBox="-0.435 -0.869 68 60" enable-background="new -0.435 -0.869 68 60"
|
||||
xml:space="preserve">
|
||||
<defs>
|
||||
</defs>
|
||||
<path fill="#666666" d="M52.462,30.881c-0.021,0-0.037,0.01-0.059,0.012c-0.021-0.002-0.037-0.012-0.059-0.012h-18.5v-7.555
|
||||
c0-0.414-0.335-0.75-0.75-0.75c-0.414,0-0.75,0.336-0.75,0.75v7.555h-18.5c-0.02,0-0.037,0.01-0.057,0.012
|
||||
c-0.02-0.002-0.037-0.012-0.057-0.012c-0.414,0-0.75,0.336-0.75,0.75v3.834c0,0.414,0.336,0.75,0.75,0.75s0.75-0.336,0.75-0.75
|
||||
v-3.084H51.71v3.084c0,0.414,0.336,0.75,0.75,0.75s0.75-0.336,0.75-0.75v-3.834C53.212,31.217,52.876,30.881,52.462,30.881z"/>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="130.7236" y1="-184.1631" x2="130.7236" y2="-191.9565" gradientTransform="matrix(1 0 0 -1 -97.6001 -158.6377)">
|
||||
<stop offset="0" style="stop-color:#9CD7FF"/>
|
||||
<stop offset="1" style="stop-color:#3C89C9"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_1_)" d="M36.296,29.976c-0.832,0-1.513-0.681-1.513-1.513v-1.424c0-0.832-0.681-1.513-1.513-1.513h-0.214
|
||||
c-0.832,0-1.513,0.681-1.513,1.513v1.424c0,0.832-0.681,1.513-1.513,1.513h-2.499c-0.832,0-1.513,0.681-1.513,1.513v0.317
|
||||
c0,0.832,0.681,1.513,1.513,1.513h11.187c0.832,0,1.513-0.681,1.513-1.513v-0.317c0-0.833-0.681-1.513-1.513-1.513H36.296z"/>
|
||||
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="605.8877" y1="2040.6665" x2="593.1709" y2="2040.6665" gradientTransform="matrix(1 0 0 1 -585.5996 -1982.4023)">
|
||||
<stop offset="0" style="stop-color:#4D4D4D"/>
|
||||
<stop offset="1" style="stop-color:#999999"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_2_)" d="M20.205,57.452c0,0.519-3.619,0.752-6.627,0.752c-2.083,0-5.846-0.186-6.089-0.678
|
||||
c0,0.238,0,0.806,0,0.89c0,0.389,2.573,0.661,6.084,0.661c3.511,0,6.632-0.344,6.632-0.729C20.205,58.264,20.205,57.7,20.205,57.452
|
||||
z"/>
|
||||
<path fill="#808080" d="M13.846,56.806c3.512,0,6.358,0.313,6.358,0.699s-2.846,0.763-6.358,0.763c-3.59,0-6.358-0.375-6.358-0.763
|
||||
S10.335,56.806,13.846,56.806z"/>
|
||||
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="600.833" y1="2037.4702" x2="598.1563" y2="2037.4702" gradientTransform="matrix(1 0 0 1 -585.5996 -1982.4023)">
|
||||
<stop offset="0" style="stop-color:#999999"/>
|
||||
<stop offset="0.0417" style="stop-color:#8D8D8D"/>
|
||||
<stop offset="0.1617" style="stop-color:#717171"/>
|
||||
<stop offset="0.2821" style="stop-color:#5D5D5D"/>
|
||||
<stop offset="0.4021" style="stop-color:#515151"/>
|
||||
<stop offset="0.5212" style="stop-color:#4D4D4D"/>
|
||||
<stop offset="0.6202" style="stop-color:#565656"/>
|
||||
<stop offset="0.7817" style="stop-color:#6E6E6E"/>
|
||||
<stop offset="0.9844" style="stop-color:#969696"/>
|
||||
<stop offset="1" style="stop-color:#999999"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_3_)" d="M15.215,57.657c0,0-0.792,0.053-1.339,0.053s-1.338-0.053-1.338-0.053v-5.231h2.677V57.657z"/>
|
||||
<radialGradient id="SVGID_4_" cx="465.1113" cy="2023.4497" r="12.8975" gradientTransform="matrix(1.15 0 0 1 -526.6041 -1982.4023)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" style="stop-color:#F2F2F2"/>
|
||||
<stop offset="1" style="stop-color:#666666"/>
|
||||
</radialGradient>
|
||||
<path fill="url(#SVGID_4_)" d="M0.065,36.888c0-0.59,0.482-1.071,1.072-1.071H26.98c0.589,0,1.071,0.481,1.071,1.071v16.108
|
||||
c0,0.589-0.482,1.07-1.071,1.07H1.137c-0.59,0.002-1.072-0.481-1.072-1.07V36.888z"/>
|
||||
<path fill="none" stroke="#666666" stroke-width="0.1305" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="
|
||||
M0.065,36.888c0-0.59,0.482-1.071,1.072-1.071H26.98c0.589,0,1.071,0.481,1.071,1.071v16.108c0,0.589-0.482,1.07-1.071,1.07H1.137
|
||||
c-0.59,0.002-1.072-0.481-1.072-1.07V36.888z"/>
|
||||
<radialGradient id="SVGID_5_" cx="439.1309" cy="2019.0845" r="28.5715" fx="461.6079" fy="2015.234" gradientTransform="matrix(1.1935 0 0 1 -509.6013 -1982.4023)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" style="stop-color:#4D4D4D"/>
|
||||
<stop offset="1" style="stop-color:#999999"/>
|
||||
</radialGradient>
|
||||
<path fill="url(#SVGID_5_)" d="M0.613,37.436c0-0.591,0.482-1.072,1.071-1.072h24.871c0.589,0,1.071,0.481,1.071,1.072v14.893
|
||||
c0,0.59-0.482,1.072-1.071,1.072H1.685c-0.589,0-1.071-0.482-1.071-1.072V37.436z"/>
|
||||
<radialGradient id="SVGID_6_" cx="440.0439" cy="2019.1304" r="18.3134" gradientTransform="matrix(1.1923 0 0 1 -510.0601 -1982.4023)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" style="stop-color:#9CD7FF"/>
|
||||
<stop offset="1" style="stop-color:#3C89C9"/>
|
||||
</radialGradient>
|
||||
<path fill="url(#SVGID_6_)" d="M0.917,37.679c0-0.59,0.482-1.071,1.072-1.071h24.262c0.589,0,1.071,0.481,1.071,1.071v14.406
|
||||
c0,0.588-0.482,1.069-1.071,1.069H1.989c-0.59,0-1.072-0.481-1.072-1.069V37.679z"/>
|
||||
<path opacity="0.24" fill="#F2F2F2" d="M0.917,49.11V37.679c0-0.59,0.482-1.071,1.072-1.071h24.262c0.589,0,1.071,0.481,1.071,1.071
|
||||
v7.252l-12.407,2.646c-0.57,0.146-1.52,0.293-2.107,0.326L0.917,49.11z"/>
|
||||
<linearGradient id="SVGID_7_" gradientUnits="userSpaceOnUse" x1="644.3887" y1="2040.6665" x2="631.6719" y2="2040.6665" gradientTransform="matrix(1 0 0 1 -585.5996 -1982.4023)">
|
||||
<stop offset="0" style="stop-color:#4D4D4D"/>
|
||||
<stop offset="1" style="stop-color:#999999"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_7_)" d="M58.706,57.452c0,0.518-3.621,0.752-6.627,0.752c-2.084,0-5.848-0.186-6.09-0.678
|
||||
c0,0.237,0,0.805,0,0.889c0,0.389,2.572,0.662,6.084,0.662s6.633-0.344,6.633-0.729C58.706,58.263,58.706,57.7,58.706,57.452z"/>
|
||||
<path fill="#808080" d="M52.347,56.805c3.512,0,6.357,0.313,6.357,0.699s-2.847,0.762-6.357,0.762c-3.59,0-6.357-0.373-6.357-0.762
|
||||
C45.989,57.118,48.837,56.805,52.347,56.805z"/>
|
||||
<linearGradient id="SVGID_8_" gradientUnits="userSpaceOnUse" x1="639.333" y1="2037.4683" x2="636.6553" y2="2037.4683" gradientTransform="matrix(1 0 0 1 -585.5996 -1982.4023)">
|
||||
<stop offset="0" style="stop-color:#999999"/>
|
||||
<stop offset="0.0417" style="stop-color:#8D8D8D"/>
|
||||
<stop offset="0.1617" style="stop-color:#717171"/>
|
||||
<stop offset="0.2821" style="stop-color:#5D5D5D"/>
|
||||
<stop offset="0.4021" style="stop-color:#515151"/>
|
||||
<stop offset="0.5212" style="stop-color:#4D4D4D"/>
|
||||
<stop offset="0.6202" style="stop-color:#565656"/>
|
||||
<stop offset="0.7817" style="stop-color:#6E6E6E"/>
|
||||
<stop offset="0.9844" style="stop-color:#969696"/>
|
||||
<stop offset="1" style="stop-color:#999999"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_8_)" d="M53.716,57.657c0,0-0.791,0.052-1.34,0.052c-0.547,0-1.338-0.052-1.338-0.052v-5.232h2.678V57.657z"
|
||||
/>
|
||||
<radialGradient id="SVGID_9_" cx="498.5898" cy="2023.4487" r="12.8975" gradientTransform="matrix(1.15 0 0 1 -526.6041 -1982.4023)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" style="stop-color:#F2F2F2"/>
|
||||
<stop offset="1" style="stop-color:#666666"/>
|
||||
</radialGradient>
|
||||
<path fill="url(#SVGID_9_)" d="M38.566,36.887c0-0.59,0.481-1.072,1.071-1.072h25.844c0.589,0,1.07,0.482,1.07,1.072v16.107
|
||||
c0,0.59-0.481,1.072-1.07,1.072H39.638c-0.59,0-1.071-0.482-1.071-1.072V36.887z"/>
|
||||
<path fill="none" stroke="#666666" stroke-width="0.1305" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="
|
||||
M38.566,36.887c0-0.59,0.481-1.072,1.071-1.072h25.844c0.589,0,1.07,0.482,1.07,1.072v16.107c0,0.59-0.481,1.072-1.07,1.072H39.638
|
||||
c-0.59,0-1.071-0.482-1.071-1.072V36.887z"/>
|
||||
<radialGradient id="SVGID_10_" cx="471.3896" cy="2019.0845" r="28.5697" fx="493.8652" fy="2015.2343" gradientTransform="matrix(1.1935 0 0 1 -509.6013 -1982.4023)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" style="stop-color:#4D4D4D"/>
|
||||
<stop offset="1" style="stop-color:#999999"/>
|
||||
</radialGradient>
|
||||
<path fill="url(#SVGID_10_)" d="M39.114,37.434c0-0.59,0.482-1.072,1.071-1.072h24.87c0.589,0,1.07,0.482,1.07,1.072v14.895
|
||||
c0,0.589-0.481,1.07-1.07,1.07h-24.87c-0.589,0-1.071-0.481-1.071-1.07V37.434z"/>
|
||||
<radialGradient id="SVGID_11_" cx="472.334" cy="2019.1294" r="18.3139" gradientTransform="matrix(1.1923 0 0 1 -510.0601 -1982.4023)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" style="stop-color:#9CD7FF"/>
|
||||
<stop offset="1" style="stop-color:#3C89C9"/>
|
||||
</radialGradient>
|
||||
<path fill="url(#SVGID_11_)" d="M39.419,37.678c0-0.59,0.481-1.072,1.07-1.072h24.264c0.588,0,1.07,0.482,1.07,1.072v14.406
|
||||
c0,0.588-0.482,1.07-1.07,1.07H40.489c-0.589,0-1.07-0.482-1.07-1.07V37.678z"/>
|
||||
<path opacity="0.24" fill="#F2F2F2" d="M39.419,49.108v-11.43c0-0.59,0.481-1.072,1.07-1.072h24.264c0.588,0,1.07,0.482,1.07,1.072
|
||||
v7.252l-12.408,2.645c-0.57,0.146-1.52,0.295-2.106,0.326L39.419,49.108z"/>
|
||||
<linearGradient id="SVGID_12_" gradientUnits="userSpaceOnUse" x1="624.8936" y1="2004.9155" x2="612.1787" y2="2004.9155" gradientTransform="matrix(1 0 0 1 -585.5996 -1982.4023)">
|
||||
<stop offset="0" style="stop-color:#4D4D4D"/>
|
||||
<stop offset="1" style="stop-color:#999999"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_12_)" d="M39.212,21.701c0,0.518-3.621,0.752-6.626,0.752c-2.083,0-5.847-0.186-6.089-0.678
|
||||
c0,0.238,0,0.805,0,0.889c0,0.389,2.573,0.662,6.084,0.662c3.51,0,6.631-0.344,6.631-0.729
|
||||
C39.212,22.513,39.212,21.949,39.212,21.701z"/>
|
||||
<path fill="#808080" d="M32.854,21.055c3.511,0,6.358,0.313,6.358,0.699c0,0.386-2.848,0.762-6.358,0.762
|
||||
c-3.589,0-6.358-0.374-6.358-0.762C26.496,21.367,29.342,21.055,32.854,21.055z"/>
|
||||
<linearGradient id="SVGID_13_" gradientUnits="userSpaceOnUse" x1="619.8379" y1="2001.7183" x2="617.1611" y2="2001.7183" gradientTransform="matrix(1 0 0 1 -585.5996 -1982.4023)">
|
||||
<stop offset="0" style="stop-color:#999999"/>
|
||||
<stop offset="0.0417" style="stop-color:#8D8D8D"/>
|
||||
<stop offset="0.1617" style="stop-color:#717171"/>
|
||||
<stop offset="0.2821" style="stop-color:#5D5D5D"/>
|
||||
<stop offset="0.4021" style="stop-color:#515151"/>
|
||||
<stop offset="0.5212" style="stop-color:#4D4D4D"/>
|
||||
<stop offset="0.6202" style="stop-color:#565656"/>
|
||||
<stop offset="0.7817" style="stop-color:#6E6E6E"/>
|
||||
<stop offset="0.9844" style="stop-color:#969696"/>
|
||||
<stop offset="1" style="stop-color:#999999"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_13_)" d="M34.222,21.906c0,0-0.791,0.052-1.338,0.052c-0.547,0-1.338-0.052-1.338-0.052v-5.232h2.677
|
||||
L34.222,21.906L34.222,21.906z"/>
|
||||
<radialGradient id="SVGID_14_" cx="481.6387" cy="1987.6978" r="12.8975" gradientTransform="matrix(1.15 0 0 1 -526.6041 -1982.4023)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" style="stop-color:#F2F2F2"/>
|
||||
<stop offset="1" style="stop-color:#666666"/>
|
||||
</radialGradient>
|
||||
<path fill="url(#SVGID_14_)" d="M19.072,1.137c0-0.59,0.482-1.072,1.071-1.072h25.843c0.589,0,1.071,0.482,1.071,1.072v16.108
|
||||
c0,0.589-0.482,1.071-1.071,1.071H20.145c-0.589,0-1.071-0.482-1.071-1.071L19.072,1.137L19.072,1.137z"/>
|
||||
<path fill="none" stroke="#666666" stroke-width="0.1305" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="
|
||||
M19.072,1.137c0-0.59,0.482-1.072,1.071-1.072h25.843c0.589,0,1.071,0.482,1.071,1.072v16.108c0,0.589-0.482,1.071-1.071,1.071
|
||||
H20.145c-0.589,0-1.071-0.482-1.071-1.071L19.072,1.137L19.072,1.137z"/>
|
||||
<radialGradient id="SVGID_15_" cx="455.0566" cy="1983.3345" r="28.5689" fx="477.5316" fy="1979.4844" gradientTransform="matrix(1.1935 0 0 1 -509.6013 -1982.4023)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" style="stop-color:#4D4D4D"/>
|
||||
<stop offset="1" style="stop-color:#999999"/>
|
||||
</radialGradient>
|
||||
<path fill="url(#SVGID_15_)" d="M19.621,1.685c0-0.59,0.482-1.072,1.072-1.072h24.87c0.589,0,1.071,0.482,1.071,1.072v14.894
|
||||
c0,0.589-0.482,1.071-1.071,1.071h-24.87c-0.589,0-1.072-0.482-1.072-1.071V1.685z"/>
|
||||
<radialGradient id="SVGID_16_" cx="455.9854" cy="1983.3784" r="18.3134" gradientTransform="matrix(1.1923 0 0 1 -510.0601 -1982.4023)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" style="stop-color:#9CD7FF"/>
|
||||
<stop offset="1" style="stop-color:#3C89C9"/>
|
||||
</radialGradient>
|
||||
<path fill="url(#SVGID_16_)" d="M19.924,1.928c0-0.59,0.482-1.072,1.072-1.072h24.262c0.589,0,1.07,0.482,1.07,1.072v14.406
|
||||
c0,0.588-0.481,1.07-1.07,1.07H20.997c-0.589,0-1.072-0.482-1.072-1.07V1.928z"/>
|
||||
<path opacity="0.24" fill="#F2F2F2" d="M19.924,13.358V1.928c0-0.59,0.482-1.072,1.072-1.072h24.262c0.589,0,1.07,0.482,1.07,1.072
|
||||
V9.18l-12.408,2.646c-0.569,0.146-1.519,0.294-2.106,0.326L19.924,13.358z"/>
|
||||
</svg>
|
||||
</y:Resource>
|
||||
<y:Resource id="2"><?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="57px" height="65px" viewBox="0 0 57 65" enable-background="new 0 0 57 65" xml:space="preserve">
|
||||
<g>
|
||||
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="26.3398" y1="3115.7266" x2="27.5807" y2="3145.5239" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)">
|
||||
<stop offset="0.2711" style="stop-color:#FFAB4F"/>
|
||||
<stop offset="1" style="stop-color:#FFD28F"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109
|
||||
V37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77
|
||||
c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z"/>
|
||||
|
||||
<radialGradient id="face_x5F_white_1_" cx="27.5835" cy="3117.4922" r="23.425" fx="23.0139" fy="3115.0024" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" style="stop-color:#FFD28F"/>
|
||||
<stop offset="1" style="stop-color:#FFAB4F"/>
|
||||
</radialGradient>
|
||||
<path id="face_x5F_white_3_" fill="url(#face_x5F_white_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M43.676,23.357
|
||||
c0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012
|
||||
C36.627,4.945,43.59,13.158,43.676,23.357z"/>
|
||||
|
||||
<linearGradient id="face_highlight_1_" gradientUnits="userSpaceOnUse" x1="6468.501" y1="-12291.5195" x2="6492.1304" y2="-12384.9688" gradientTransform="matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)">
|
||||
<stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.24"/>
|
||||
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.16"/>
|
||||
</linearGradient>
|
||||
<path id="face_highlight_3_" fill="url(#face_highlight_1_)" d="M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386
|
||||
c-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247
|
||||
c2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z"/>
|
||||
<path fill="#CC9869" stroke="#99724F" stroke-width="0.9271" stroke-linecap="round" stroke-linejoin="round" d="M28.02,31.921
|
||||
c-6.78,0-6.717,3.708-6.717,3.708c0,8.133,2.985,8.788,6.955,8.788c4.243,0,6.792-0.926,6.792-8.595
|
||||
C35.051,35.822,35.881,31.921,28.02,31.921z M23.989,35.678c0-0.556,1.838-1.005,4.107-1.005c2.27,0,4.107,0.449,4.107,1.005
|
||||
C32.204,36.232,23.989,36.232,23.989,35.678z"/>
|
||||
<path id="hair_x5F_gray_2_" fill="#CC9869" stroke="#99724F" stroke-linecap="round" stroke-linejoin="round" d="M20.278,13.25
|
||||
c0,0,5.321,7.25,15,3.75c2.729-0.563,9.058,1.035,9.058,1.035S40.68,1.865,27.289,2.744C9.403,4.125,12.058,25.678,12.058,25.678
|
||||
s2.768-0.684,5.036-4.802C18.068,19.106,20.278,13.25,20.278,13.25z"/>
|
||||
|
||||
<radialGradient id="collar_x5F_body_1_" cx="14.9609" cy="3148.9336" r="32.4004" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" style="stop-color:#B0E8FF"/>
|
||||
<stop offset="1" style="stop-color:#74AEEE"/>
|
||||
</radialGradient>
|
||||
<path id="collar_x5F_body_3_" fill="url(#collar_x5F_body_1_)" stroke="#5491CF" d="M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494
|
||||
h48.51c1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146
|
||||
c-1.771,1.655-5.61,2.802-10.063,2.802c-4.453,0-8.292-1.146-10.063-2.802c0,0-5.755,0.586-11.189,6.021
|
||||
C1.378,56.689,0.5,62.768,0.5,62.768z"/>
|
||||
|
||||
<radialGradient id="collar_x5F_r_1_" cx="31.2998" cy="3139.0605" r="9.2823" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" style="stop-color:#80CCFF"/>
|
||||
<stop offset="1" style="stop-color:#74AEEE"/>
|
||||
</radialGradient>
|
||||
<path id="collar_x5F_r_3_" fill="url(#collar_x5F_r_1_)" stroke="#5491CF" d="M38.159,41.381c0,0-0.574,2.369-3.013,4.441
|
||||
c-2.108,1.795-5.783,2.072-5.783,2.072l3.974,6.217c0,0,2.957-1.637,5.009-3.848c1.922-2.072,1.37-5.479,1.37-5.479L38.159,41.381z
|
||||
"/>
|
||||
|
||||
<radialGradient id="collar_x5F_l_1_" cx="18.9375" cy="3139.1016" r="9.2843" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" style="stop-color:#80CCFF"/>
|
||||
<stop offset="1" style="stop-color:#74AEEE"/>
|
||||
</radialGradient>
|
||||
<path id="collar_x5F_l_3_" fill="url(#collar_x5F_l_1_)" stroke="#5491CF" d="M18.63,41.422c0,0,0.576,2.369,3.012,4.441
|
||||
c2.109,1.793,5.785,2.072,5.785,2.072l-3.974,6.217c0,0-2.957-1.637-5.007-3.85c-1.922-2.072-1.37-5.48-1.37-5.48L18.63,41.422z"/>
|
||||
|
||||
<radialGradient id="Knob2_1_" cx="27.6895" cy="2375.2871" r="0.9669" gradientTransform="matrix(1 0 0 1 0.2402 -2319.0742)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" style="stop-color:#80CCFF"/>
|
||||
<stop offset="1" style="stop-color:#74AEEE"/>
|
||||
</radialGradient>
|
||||
<circle id="Knob2_3_" fill="url(#Knob2_1_)" stroke="#5491CF" cx="28.258" cy="56.254" r="0.584"/>
|
||||
|
||||
<radialGradient id="Knob1_1_" cx="27.7275" cy="2381.5283" r="0.9669" gradientTransform="matrix(1 0 0 1 0.2402 -2319.0742)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" style="stop-color:#80CCFF"/>
|
||||
<stop offset="1" style="stop-color:#74AEEE"/>
|
||||
</radialGradient>
|
||||
<circle id="Knob1_3_" fill="url(#Knob1_1_)" stroke="#5491CF" cx="28.297" cy="62.499" r="0.584"/>
|
||||
<path id="path5135_5_" fill="#D54A30" stroke="#B51A19" d="M27.442,55.23c0,0-1.852,2.057-2.082,6.543c-0.23,4.488,0,4.488,0,4.488
|
||||
h6.546c0,0,0.23,0.063-0.154-4.367c-0.4-4.604-2.389-6.668-2.389-6.668L27.442,55.23L27.442,55.23z"/>
|
||||
<path id="path5131_5_" fill="#D54A30" stroke="#B51A19" d="M28.325,48.688h0.125L31,52.691c0.516,0.953-1.207,1.797-1.457,2.547
|
||||
l-2.277-0.018c-0.242-0.761-2.26-1.369-1.477-2.584L28.325,48.688z"/>
|
||||
</g>
|
||||
</svg>
|
||||
</y:Resource>
|
||||
<y:Resource id="3"><?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
viewBox="0 0 585.16241 167.58249"
|
||||
height="167.58249"
|
||||
width="585.16241"
|
||||
xml:space="preserve"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="Slack CMYK.svg"><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:window-height="705"
|
||||
id="namedview3358"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:zoom="1.4633713"
|
||||
inkscape:cx="271.33569"
|
||||
inkscape:cy="125.32114"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2" /><metadata
|
||||
id="metadata8"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs6"><clipPath
|
||||
id="clipPath20"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path18"
|
||||
d="M 0,1256.87 0,0 l 4388.72,0 0,1256.87 z"
|
||||
inkscape:connector-curvature="0" /></clipPath></defs><g
|
||||
id="g3411"
|
||||
transform="translate(12.322913,-242.28632)"><path
|
||||
d="m 93.133967,257.26632 c -2.4724,-7.60934 -10.644271,-11.77334 -18.253204,-9.30001 -7.608267,2.472 -11.7724,10.64401 -9.300533,18.252 l 37.45734,115.24281 c 2.57133,7.10786 10.24946,11.10266 17.62079,8.98133 7.68814,-2.2136 12.3748,-10.37867 9.92147,-17.93027 -0.0933,-0.2864 -37.445863,-115.24586 -37.445863,-115.24586"
|
||||
style="fill:#e7a213;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path22-7"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 35.095431,276.12365 c -2.4724,-7.608 -10.644267,-11.772 -18.2532,-9.3 -7.6082657,2.472 -11.7723997,10.64426 -9.3005327,18.2532 L 44.99903,400.3195 c 2.571334,7.10834 10.249467,11.1026 17.620267,8.98021 7.688133,-2.21198 12.375599,-10.37761 9.921866,-17.92913 -0.0932,-0.28706 -37.445732,-115.24693 -37.445732,-115.24693"
|
||||
style="fill:#4dc088;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path24-8"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 140.27983,352.71018 c 7.60933,-2.4724 11.772,-10.64427 9.3,-18.2532 -2.472,-7.60827 -10.644,-11.7724 -18.252,-9.30053 L 16.085031,362.61378 c -7.1083997,2.57134 -11.1026657,10.24947 -8.9813327,17.62027 2.213067,7.688 10.3781327,12.37507 17.9302657,9.92133 0.2864,-0.0932 115.245866,-37.4452 115.245866,-37.4452"
|
||||
style="fill:#e10d63;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path26-3"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 40.103697,385.25965 c 7.5016,-2.4376 17.169866,-5.57867 27.543733,-8.94947 -2.436934,-7.50106 -5.579067,-17.17093 -8.950533,-27.5464 l -27.5452,8.95254 8.952,27.54333"
|
||||
style="fill:#3f2543;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path28-4"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 98.142767,366.40125 c 10.413603,-3.38333 20.091733,-6.52813 27.543733,-8.94947 -2.43734,-7.50213 -5.58014,-17.174 -8.95254,-27.5516 l -27.545333,8.95267 8.95414,27.5484"
|
||||
style="fill:#d01e25;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path30-9"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 121.4225,294.67165 c 7.608,-2.4724 11.772,-10.64427 9.3,-18.25334 -2.472,-7.60933 -10.64427,-11.772 -18.2532,-9.3 L -2.7733549,304.57525 c -7.1078131,2.57133 -11.1026001,10.25 -8.9807301,17.62026 2.2130236,7.688 10.3781303,12.3756 17.9296503,9.92187 C 6.4626313,332.02418 121.4225,294.67165 121.4225,294.67165"
|
||||
style="fill:#7cd3dc;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path32-2"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 21.240764,327.22258 c 7.501467,-2.438 17.1724,-5.5796 27.548933,-8.95147 -3.383867,-10.41413 -6.528667,-20.0928 -8.950533,-27.5464 l -27.550533,8.95467 8.952133,27.5432"
|
||||
style="fill:#36987b;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path34-0"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 79.279296,308.36431 c 10.415601,-3.38386 20.095731,-6.5292 27.548934,-8.95106 -3.3844,-10.41667 -6.53026,-20.09787 -8.952133,-27.55227 l -27.551067,8.95533 8.954266,27.548"
|
||||
style="fill:#5a872d;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path36-4"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 257.17716,287.60071 c 5.09733,2.224 5.468,3.89227 1.48267,11.49067 -4.076,7.78387 -5.096,8.24733 -10.1,6.20827 -6.30133,-2.68694 -14.364,-4.726 -19.55333,-4.726 -8.524,0 -14.17734,3.0584 -14.17734,7.69173 0,15.29 48.836,7.04213 48.836,39.66094 0,16.40253 -14.08667,27.33746 -35.21333,27.33746 -11.12,0 -24.836,-3.7068 -34.288,-8.526 -4.72533,-2.40893 -5.00267,-3.79906 -0.92667,-11.5828 3.52267,-6.85786 4.63334,-7.59906 9.73067,-5.46773 8.06133,3.52133 18.256,6.2088 25.11333,6.2088 7.784,0 12.97334,-3.15107 12.97334,-7.78387 0,-14.82707 -49.66934,-7.7844 -49.66934,-39.38387 0,-16.7724 13.992,-27.98533 34.93467,-27.98533 9.82267,0 22.24,2.96507 30.85733,6.85773"
|
||||
style="fill:#373d47;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path38-9"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 296.56516,248.86498 0,121.57947 c 0,1.85373 -1.94667,3.79947 -4.72533,3.79947 l -12.78934,0 c -2.78,0 -4.72666,-1.94574 -4.72666,-3.79947 l 0,-121.57947 c 0,-6.02266 1.66933,-6.57866 11.12133,-6.57866 10.74933,0 11.12,0.74133 11.12,6.57866"
|
||||
style="fill:#373d47;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path40-2"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 328.53449,347.55592 c 0,6.20893 5.28267,10.37866 13.252,10.37866 9.63734,0 18.34667,-4.63333 23.444,-12.50986 l 0,-8.15467 c -5.09733,-1.9464 -11.30533,-3.0584 -16.86533,-3.0584 -11.86133,0 -19.83067,5.6532 -19.83067,13.34427 z m 58.936,-30.30201 0,52.35667 c 0,2.77974 -1.94533,4.72614 -4.72533,4.72614 l -12.604,0 c -2.872,0 -4.91067,-2.13134 -4.72533,-5.00374 l 0.18533,-5.65253 c -6.85733,7.59787 -16.68,11.58333 -26.50267,11.58333 -19.08933,0 -31.87733,-11.02813 -31.87733,-27.5224 0,-17.514 14.456,-29.2828 36.14,-29.37547 8.248,0 15.75333,1.4828 21.86933,3.98427 l 0,-6.02333 c 0,-9.63707 -7.59866,-15.3824 -20.47866,-15.3824 -6.024,0 -13.43734,2.41 -19.368,5.83813 -4.54134,2.59467 -5.65334,2.40933 -10.564,-4.91093 -4.81867,-7.32147 -4.63334,-8.71094 0,-11.67667 8.896,-5.74533 20.94266,-9.452 32.06266,-9.452 24.92667,0 40.588,13.5296 40.588,36.51093"
|
||||
style="fill:#373d47;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path42-7"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 474.21182,287.78618 c 4.632,2.68693 4.81867,4.16973 -0.27866,12.0464 -4.81734,7.41347 -5.652,7.87707 -10.748,5.28227 -3.89334,-2.03907 -10.10134,-3.79947 -15.19867,-3.79947 -16.03067,0 -26.688,10.56347 -26.688,26.50253 0,16.58747 10.65733,27.70734 26.688,27.70734 5.56133,0 12.51067,-2.13173 17.05067,-4.63333 4.63333,-2.68747 5.65333,-2.50214 10.564,4.63333 4.448,6.6724 4.356,8.2468 0.37066,11.0276 -7.13466,4.91147 -18.44,8.71093 -28.35466,8.71093 -29.65334,0 -49.48534,-18.99693 -49.48534,-47.44587 0,-28.26293 19.832,-47.07493 49.66934,-47.07493 9.08133,0 19.73866,3.05827 26.41066,7.0432"
|
||||
style="fill:#373d47;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path44-1"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 570.95849,362.75338 c 3.70666,4.72547 2.224,6.39427 -7.04267,9.73014 -9.452,3.42813 -10.74933,3.2428 -14.084,-1.11187 l -26.504,-35.39907 -11.86133,11.49014 0,22.98173 c 0,1.85373 -1.94533,3.79947 -4.72533,3.79947 l -12.788,0 c -2.78,0 -4.72667,-1.94574 -4.72667,-3.79947 l 0,-121.57947 c 0,-6.02266 1.668,-6.57866 11.12,-6.57866 10.74933,0 11.12,0.74133 11.12,6.57866 l 0,69.13 36.32533,-34.84213 c 3.98533,-3.8 6.20933,-3.52134 13.344,1.20466 7.87733,5.0964 8.43333,6.4864 4.63333,10.19267 l -27.05866,26.31773 32.248,41.88547"
|
||||
style="fill:#373d47;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path46-3"
|
||||
inkscape:connector-curvature="0" /></g></svg></y:Resource>
|
||||
</y:Resources>
|
||||
</data>
|
||||
</graphml>
|
||||
|
Before Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 179 KiB |
@@ -1,265 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
|
||||
<!--Created by yEd 3.14.2-->
|
||||
<key attr.name="Description" attr.type="string" for="graph" id="d0"/>
|
||||
<key for="port" id="d1" yfiles.type="portgraphics"/>
|
||||
<key for="port" id="d2" yfiles.type="portgeometry"/>
|
||||
<key for="port" id="d3" yfiles.type="portuserdata"/>
|
||||
<key attr.name="url" attr.type="string" for="node" id="d4"/>
|
||||
<key attr.name="description" attr.type="string" for="node" id="d5"/>
|
||||
<key for="node" id="d6" yfiles.type="nodegraphics"/>
|
||||
<key for="graphml" id="d7" yfiles.type="resources"/>
|
||||
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
|
||||
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
|
||||
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
|
||||
<graph edgedefault="directed" id="G">
|
||||
<data key="d0"/>
|
||||
<node id="n0">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="478.6165008544913" y="1358.206868489578"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="28.87890625" x="22.185546875" y="15.93359375">Vuls<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n1">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="711.9623756408686" y="1043.7241210937468"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="38.623046875" x="17.3134765625" y="15.93359375">Nginx<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n2">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="711.9623756408686" y="1287.206868489578"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="42.7890625" x="15.23046875" y="15.93359375">MySQL<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n3" yfiles.foldertype="group">
|
||||
<data key="d4"/>
|
||||
<data key="d6">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="101.666015625" width="291.7208747863772" x="602.72693824768" y="1146.2994791666624"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="291.7208747863772" x="0.0" y="0.0">Web/App</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="23" leftF="23.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 5</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n3:">
|
||||
<node id="n3::n0">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="640.72693824768" y="1182.9654947916624"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n3::n1">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="723.4623756408686" y="1182.9654947916624"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n3::n2">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="806.1978130340572" y="1182.9654947916624"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<node id="n4">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="821.1978130340572" y="1287.206868489578"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="35.412109375" x="18.9189453125" y="15.93359375">Redis<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<edge id="e0" source="n3" target="n1">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="none"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e1" source="n3" target="n2">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="none"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e2" source="n0" target="n3::n0">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e3" source="n0" target="n3::n1">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e4" source="n0" target="n3::n2">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e5" source="n3" target="n4">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="none"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e6" source="n0" target="n4">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e7" source="n0" target="n1">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e8" source="n0" target="n2">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
</graph>
|
||||
<data key="d7">
|
||||
<y:Resources/>
|
||||
</data>
|
||||
</graphml>
|
||||
|
Before Width: | Height: | Size: 14 KiB |
@@ -1,194 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
|
||||
<!--Created by yEd 3.14.2-->
|
||||
<key attr.name="Description" attr.type="string" for="graph" id="d0"/>
|
||||
<key for="port" id="d1" yfiles.type="portgraphics"/>
|
||||
<key for="port" id="d2" yfiles.type="portgeometry"/>
|
||||
<key for="port" id="d3" yfiles.type="portuserdata"/>
|
||||
<key attr.name="url" attr.type="string" for="node" id="d4"/>
|
||||
<key attr.name="description" attr.type="string" for="node" id="d5"/>
|
||||
<key for="node" id="d6" yfiles.type="nodegraphics"/>
|
||||
<key for="graphml" id="d7" yfiles.type="resources"/>
|
||||
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
|
||||
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
|
||||
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
|
||||
<graph edgedefault="directed" id="G">
|
||||
<data key="d0"/>
|
||||
<node id="n0">
|
||||
<data key="d5"/>
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="508.30825042724564" y="1132.4827473958312"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="28.87890625" x="22.185546875" y="15.93359375">Vuls<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n1">
|
||||
<data key="d5"/>
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="749.6541252136229" y="993.2413736979156"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="23.8046875" x="24.72265625" y="15.93359375">ELB<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n2">
|
||||
<data key="d5"/>
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="749.6541252136229" y="1236.7241210937468"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="27.0390625" x="23.10546875" y="15.93359375">RDS<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n3" yfiles.foldertype="group">
|
||||
<data key="d4"/>
|
||||
<data key="d5"/>
|
||||
<data key="d6">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="101.666015625" width="291.7208747863772" x="640.4186878204343" y="1095.8167317708312"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="291.7208747863772" x="0.0" y="0.0">Web/App</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="23" leftF="23.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 5</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n3:">
|
||||
<node id="n3::n0">
|
||||
<data key="d5"/>
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="678.4186878204343" y="1132.4827473958312"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n3::n1">
|
||||
<data key="d5"/>
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="761.1541252136229" y="1132.4827473958312"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n3::n2">
|
||||
<data key="d5"/>
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="843.8895626068115" y="1132.4827473958312"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<edge id="e0" source="n3" target="n1">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="none"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e1" source="n3" target="n2">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="none"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e2" source="n0" target="n3::n0">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
</graph>
|
||||
<data key="d7">
|
||||
<y:Resources/>
|
||||
</data>
|
||||
</graphml>
|
||||
|
Before Width: | Height: | Size: 5.6 KiB |
BIN
img/vuls_icon.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
img/vuls_logo.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
BIN
img/vuls_logo_large.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
108
libmanager/libManager.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package libmanager
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
db2 "github.com/aquasecurity/trivy-db/pkg/db"
|
||||
"github.com/aquasecurity/trivy/pkg/db"
|
||||
"github.com/aquasecurity/trivy/pkg/github"
|
||||
"github.com/aquasecurity/trivy/pkg/indicator"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/spf13/afero"
|
||||
"golang.org/x/xerrors"
|
||||
"k8s.io/utils/clock"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
)
|
||||
|
||||
// FillLibrary fills LibraryScanner information
|
||||
func FillLibrary(r *models.ScanResult) (totalCnt int, err error) {
|
||||
if len(r.LibraryScanners) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// initialize trivy's logger and db
|
||||
err = log.InitLogger(false, false)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
util.Log.Info("Updating library db...")
|
||||
if err := downloadDB(config.Version, config.Conf.TrivyCacheDBDir, config.Conf.NoProgress, false, false); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if err := db2.Init(config.Conf.TrivyCacheDBDir); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer db2.Close()
|
||||
|
||||
for _, lib := range r.LibraryScanners {
|
||||
vinfos, err := lib.Scan()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for _, vinfo := range vinfos {
|
||||
vinfo.Confidences.AppendIfMissing(models.TrivyMatch)
|
||||
if v, ok := r.ScannedCves[vinfo.CveID]; !ok {
|
||||
r.ScannedCves[vinfo.CveID] = vinfo
|
||||
} else {
|
||||
v.LibraryFixedIns = append(v.LibraryFixedIns, vinfo.LibraryFixedIns...)
|
||||
r.ScannedCves[vinfo.CveID] = v
|
||||
}
|
||||
}
|
||||
totalCnt += len(vinfos)
|
||||
}
|
||||
|
||||
return totalCnt, nil
|
||||
}
|
||||
|
||||
func downloadDB(appVersion, cacheDir string, quiet, light, skipUpdate bool) error {
|
||||
client := initializeDBClient(cacheDir, quiet)
|
||||
ctx := context.Background()
|
||||
needsUpdate, err := client.NeedsUpdate(appVersion, light, skipUpdate)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("database error: %w", err)
|
||||
}
|
||||
|
||||
if needsUpdate {
|
||||
util.Log.Info("Need to update DB")
|
||||
util.Log.Info("Downloading DB...")
|
||||
if err := client.Download(ctx, cacheDir, light); err != nil {
|
||||
return xerrors.Errorf("failed to download vulnerability DB: %w", err)
|
||||
}
|
||||
if err = client.UpdateMetadata(cacheDir); err != nil {
|
||||
return xerrors.Errorf("unable to update database metadata: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// for debug
|
||||
if err := showDBInfo(cacheDir); err != nil {
|
||||
return xerrors.Errorf("failed to show database info: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func initializeDBClient(cacheDir string, quiet bool) db.Client {
|
||||
config := db2.Config{}
|
||||
client := github.NewClient()
|
||||
progressBar := indicator.NewProgressBar(quiet)
|
||||
realClock := clock.RealClock{}
|
||||
fs := afero.NewOsFs()
|
||||
metadata := db.NewMetadata(fs, cacheDir)
|
||||
dbClient := db.NewClient(config, client, progressBar, realClock, metadata)
|
||||
return dbClient
|
||||
}
|
||||
|
||||
func showDBInfo(cacheDir string) error {
|
||||
m := db.NewMetadata(afero.NewOsFs(), cacheDir)
|
||||
metadata, err := m.Get()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("something wrong with DB: %w", err)
|
||||
}
|
||||
util.Log.Debugf("DB Schema: %d, Type: %d, UpdatedAt: %s, NextUpdate: %s",
|
||||
metadata.Version, metadata.Type, metadata.UpdatedAt, metadata.NextUpdate)
|
||||
return nil
|
||||
}
|
||||
36
main.go
@@ -1,32 +1,15 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"context"
|
||||
|
||||
"github.com/future-architect/vuls/commands"
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/google/subcommands"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -36,9 +19,20 @@ func main() {
|
||||
subcommands.Register(&commands.DiscoverCmd{}, "discover")
|
||||
subcommands.Register(&commands.TuiCmd{}, "tui")
|
||||
subcommands.Register(&commands.ScanCmd{}, "scan")
|
||||
subcommands.Register(&commands.PrepareCmd{}, "prepare")
|
||||
subcommands.Register(&commands.HistoryCmd{}, "history")
|
||||
subcommands.Register(&commands.ReportCmd{}, "report")
|
||||
subcommands.Register(&commands.ConfigtestCmd{}, "configtest")
|
||||
subcommands.Register(&commands.ServerCmd{}, "server")
|
||||
|
||||
var v = flag.Bool("v", false, "Show version")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if *v {
|
||||
fmt.Printf("vuls %s %s\n", config.Version, config.Revision)
|
||||
os.Exit(int(subcommands.ExitSuccess))
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
os.Exit(int(subcommands.Execute(ctx)))
|
||||
}
|
||||
|
||||
360
models/cvecontents.go
Normal file
@@ -0,0 +1,360 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
|
||||
)
|
||||
|
||||
// CveContents has CveContent
|
||||
type CveContents map[CveContentType]CveContent
|
||||
|
||||
// NewCveContents create CveContents
|
||||
func NewCveContents(conts ...CveContent) CveContents {
|
||||
m := CveContents{}
|
||||
for _, cont := range conts {
|
||||
m[cont.Type] = cont
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// CveContentStr has CveContentType and Value
|
||||
type CveContentStr struct {
|
||||
Type CveContentType
|
||||
Value string
|
||||
}
|
||||
|
||||
// Except returns CveContents except given keys for enumeration
|
||||
func (v CveContents) Except(exceptCtypes ...CveContentType) (values CveContents) {
|
||||
values = CveContents{}
|
||||
for ctype, content := range v {
|
||||
found := false
|
||||
for _, exceptCtype := range exceptCtypes {
|
||||
if ctype == exceptCtype {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
values[ctype] = content
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SourceLinks returns link of source
|
||||
func (v CveContents) SourceLinks(lang, myFamily, cveID string) (values []CveContentStr) {
|
||||
if lang == "ja" {
|
||||
if cont, found := v[Jvn]; found && 0 < len(cont.SourceLink) {
|
||||
values = append(values, CveContentStr{Jvn, cont.SourceLink})
|
||||
}
|
||||
}
|
||||
|
||||
order := CveContentTypes{Nvd, NvdXML, NewCveContentType(myFamily)}
|
||||
for _, ctype := range order {
|
||||
if cont, found := v[ctype]; found {
|
||||
if cont.SourceLink == "" {
|
||||
continue
|
||||
}
|
||||
values = append(values, CveContentStr{ctype, cont.SourceLink})
|
||||
}
|
||||
}
|
||||
|
||||
if len(values) == 0 {
|
||||
return []CveContentStr{{
|
||||
Type: Nvd,
|
||||
Value: "https://nvd.nist.gov/vuln/detail/" + cveID,
|
||||
}}
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
/*
|
||||
// Severities returns Severities
|
||||
func (v CveContents) Severities(myFamily string) (values []CveContentStr) {
|
||||
order := CveContentTypes{NVD, NewCveContentType(myFamily)}
|
||||
order = append(order, AllCveContetTypes.Except(append(order)...)...)
|
||||
|
||||
for _, ctype := range order {
|
||||
if cont, found := v[ctype]; found && 0 < len(cont.Severity) {
|
||||
values = append(values, CveContentStr{
|
||||
Type: ctype,
|
||||
Value: cont.Severity,
|
||||
})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
// CveContentCpes has CveContentType and Value
|
||||
type CveContentCpes struct {
|
||||
Type CveContentType
|
||||
Value []Cpe
|
||||
}
|
||||
|
||||
// Cpes returns affected CPEs of this Vulnerability
|
||||
func (v CveContents) Cpes(myFamily string) (values []CveContentCpes) {
|
||||
order := CveContentTypes{NewCveContentType(myFamily)}
|
||||
order = append(order, AllCveContetTypes.Except(order...)...)
|
||||
|
||||
for _, ctype := range order {
|
||||
if cont, found := v[ctype]; found && 0 < len(cont.Cpes) {
|
||||
values = append(values, CveContentCpes{
|
||||
Type: ctype,
|
||||
Value: cont.Cpes,
|
||||
})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CveContentRefs has CveContentType and Cpes
|
||||
type CveContentRefs struct {
|
||||
Type CveContentType
|
||||
Value []Reference
|
||||
}
|
||||
|
||||
// References returns References
|
||||
func (v CveContents) References(myFamily string) (values []CveContentRefs) {
|
||||
order := CveContentTypes{NewCveContentType(myFamily)}
|
||||
order = append(order, AllCveContetTypes.Except(order...)...)
|
||||
|
||||
for _, ctype := range order {
|
||||
if cont, found := v[ctype]; found && 0 < len(cont.References) {
|
||||
values = append(values, CveContentRefs{
|
||||
Type: ctype,
|
||||
Value: cont.References,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CweIDs returns related CweIDs of the vulnerability
|
||||
func (v CveContents) CweIDs(myFamily string) (values []CveContentStr) {
|
||||
order := CveContentTypes{NewCveContentType(myFamily)}
|
||||
order = append(order, AllCveContetTypes.Except(order...)...)
|
||||
for _, ctype := range order {
|
||||
if cont, found := v[ctype]; found && 0 < len(cont.CweIDs) {
|
||||
for _, cweID := range cont.CweIDs {
|
||||
for _, val := range values {
|
||||
if val.Value == cweID {
|
||||
continue
|
||||
}
|
||||
}
|
||||
values = append(values, CveContentStr{
|
||||
Type: ctype,
|
||||
Value: cweID,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UniqCweIDs returns Uniq CweIDs
|
||||
func (v CveContents) UniqCweIDs(myFamily string) (values []CveContentStr) {
|
||||
uniq := map[string]CveContentStr{}
|
||||
for _, cwes := range v.CweIDs(myFamily) {
|
||||
uniq[cwes.Value] = cwes
|
||||
}
|
||||
for _, cwe := range uniq {
|
||||
values = append(values, cwe)
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
// CveContent has abstraction of various vulnerability information
|
||||
type CveContent struct {
|
||||
Type CveContentType `json:"type"`
|
||||
CveID string `json:"cveID"`
|
||||
Title string `json:"title"`
|
||||
Summary string `json:"summary"`
|
||||
Cvss2Score float64 `json:"cvss2Score"`
|
||||
Cvss2Vector string `json:"cvss2Vector"`
|
||||
Cvss2Severity string `json:"cvss2Severity"`
|
||||
Cvss3Score float64 `json:"cvss3Score"`
|
||||
Cvss3Vector string `json:"cvss3Vector"`
|
||||
Cvss3Severity string `json:"cvss3Severity"`
|
||||
SourceLink string `json:"sourceLink"`
|
||||
Cpes []Cpe `json:"cpes,omitempty"`
|
||||
References References `json:"references,omitempty"`
|
||||
CweIDs []string `json:"cweIDs,omitempty"`
|
||||
Published time.Time `json:"published"`
|
||||
LastModified time.Time `json:"lastModified"`
|
||||
Mitigation string `json:"mitigation"` // RedHat API
|
||||
Optional map[string]string `json:"optional,omitempty"`
|
||||
}
|
||||
|
||||
// Empty checks the content is empty
|
||||
func (c CveContent) Empty() bool {
|
||||
return c.Summary == ""
|
||||
}
|
||||
|
||||
// CveContentType is a source of CVE information
|
||||
type CveContentType string
|
||||
|
||||
// NewCveContentType create CveContentType
|
||||
func NewCveContentType(name string) CveContentType {
|
||||
switch name {
|
||||
case "nvdxml":
|
||||
return NvdXML
|
||||
case "nvd":
|
||||
return Nvd
|
||||
case "jvn":
|
||||
return Jvn
|
||||
case "redhat", "centos":
|
||||
return RedHat
|
||||
case "oracle":
|
||||
return Oracle
|
||||
case "ubuntu":
|
||||
return Ubuntu
|
||||
case "debian", vulnerability.DebianOVAL:
|
||||
return Debian
|
||||
case "redhat_api":
|
||||
return RedHatAPI
|
||||
case "debian_security_tracker":
|
||||
return DebianSecurityTracker
|
||||
case "microsoft":
|
||||
return Microsoft
|
||||
case "wordpress":
|
||||
return WPVulnDB
|
||||
case "amazon":
|
||||
return Amazon
|
||||
case "trivy":
|
||||
return Trivy
|
||||
// case vulnerability.NodejsSecurityWg:
|
||||
// return NodeSec
|
||||
// case vulnerability.PythonSafetyDB:
|
||||
// return PythonSec
|
||||
// case vulnerability.RustSec:
|
||||
// return RustSec
|
||||
// case vulnerability.PhpSecurityAdvisories:
|
||||
// return PhpSec
|
||||
// case vulnerability.RubySec:
|
||||
// return RubySec
|
||||
default:
|
||||
return Unknown
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// NvdXML is NvdXML
|
||||
NvdXML CveContentType = "nvdxml"
|
||||
|
||||
// Nvd is Nvd
|
||||
Nvd CveContentType = "nvd"
|
||||
|
||||
// Jvn is Jvn
|
||||
Jvn CveContentType = "jvn"
|
||||
|
||||
// RedHat is RedHat
|
||||
RedHat CveContentType = "redhat"
|
||||
|
||||
// RedHatAPI is RedHat
|
||||
RedHatAPI CveContentType = "redhat_api"
|
||||
|
||||
// DebianSecurityTracker is Debian Security tracker
|
||||
DebianSecurityTracker CveContentType = "debian_security_tracker"
|
||||
|
||||
// Debian is Debian
|
||||
Debian CveContentType = "debian"
|
||||
|
||||
// Ubuntu is Ubuntu
|
||||
Ubuntu CveContentType = "ubuntu"
|
||||
|
||||
// Oracle is Oracle Linux
|
||||
Oracle CveContentType = "oracle"
|
||||
|
||||
// Amazon is Amazon Linux
|
||||
Amazon CveContentType = "amazon"
|
||||
|
||||
// SUSE is SUSE Linux
|
||||
SUSE CveContentType = "suse"
|
||||
|
||||
// Microsoft is Microsoft
|
||||
Microsoft CveContentType = "microsoft"
|
||||
|
||||
// WPVulnDB is WordPress
|
||||
WPVulnDB CveContentType = "wpvulndb"
|
||||
|
||||
// Trivy is Trivy
|
||||
Trivy CveContentType = "trivy"
|
||||
|
||||
// NodeSec : for JS
|
||||
// NodeSec CveContentType = "node"
|
||||
|
||||
// // PythonSec : for PHP
|
||||
// PythonSec CveContentType = "python"
|
||||
|
||||
// // PhpSec : for PHP
|
||||
// PhpSec CveContentType = "php"
|
||||
|
||||
// // RubySec : for Ruby
|
||||
// RubySec CveContentType = "ruby"
|
||||
|
||||
// // RustSec : for Rust
|
||||
// RustSec CveContentType = "rust"
|
||||
|
||||
// Unknown is Unknown
|
||||
Unknown CveContentType = "unknown"
|
||||
)
|
||||
|
||||
// CveContentTypes has slide of CveContentType
|
||||
type CveContentTypes []CveContentType
|
||||
|
||||
// AllCveContetTypes has all of CveContentTypes
|
||||
var AllCveContetTypes = CveContentTypes{
|
||||
Nvd,
|
||||
NvdXML,
|
||||
Jvn,
|
||||
RedHat,
|
||||
RedHatAPI,
|
||||
Debian,
|
||||
Ubuntu,
|
||||
Amazon,
|
||||
SUSE,
|
||||
DebianSecurityTracker,
|
||||
WPVulnDB,
|
||||
Trivy,
|
||||
// NodeSec,
|
||||
// PythonSec,
|
||||
// PhpSec,
|
||||
// RubySec,
|
||||
// RustSec,
|
||||
}
|
||||
|
||||
// Except returns CveContentTypes except for given args
|
||||
func (c CveContentTypes) Except(excepts ...CveContentType) (excepted CveContentTypes) {
|
||||
for _, ctype := range c {
|
||||
found := false
|
||||
for _, except := range excepts {
|
||||
if ctype == except {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
excepted = append(excepted, ctype)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Cpe is Common Platform Enumeration
|
||||
type Cpe struct {
|
||||
URI string `json:"uri"`
|
||||
FormattedString string `json:"formattedString"`
|
||||
}
|
||||
|
||||
// References is a slice of Reference
|
||||
type References []Reference
|
||||
|
||||
// Reference has a related link of the CVE
|
||||
type Reference struct {
|
||||
Source string `json:"source"`
|
||||
Link string `json:"link"`
|
||||
RefID string `json:"refID"`
|
||||
}
|
||||
190
models/cvecontents_test.go
Normal file
@@ -0,0 +1,190 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExcept(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in CveContents
|
||||
out CveContents
|
||||
}{{
|
||||
in: CveContents{
|
||||
RedHat: {Type: RedHat},
|
||||
Ubuntu: {Type: Ubuntu},
|
||||
Debian: {Type: Debian},
|
||||
},
|
||||
out: CveContents{
|
||||
RedHat: {Type: RedHat},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
actual := tt.in.Except(Ubuntu, Debian)
|
||||
if !reflect.DeepEqual(tt.out, actual) {
|
||||
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSourceLinks(t *testing.T) {
|
||||
type in struct {
|
||||
lang string
|
||||
cveID string
|
||||
cont CveContents
|
||||
}
|
||||
var tests = []struct {
|
||||
in in
|
||||
out []CveContentStr
|
||||
}{
|
||||
// lang: ja
|
||||
{
|
||||
in: in{
|
||||
lang: "ja",
|
||||
cveID: "CVE-2017-6074",
|
||||
cont: CveContents{
|
||||
Jvn: {
|
||||
Type: Jvn,
|
||||
SourceLink: "https://jvn.jp/vu/JVNVU93610402/",
|
||||
},
|
||||
RedHat: {
|
||||
Type: RedHat,
|
||||
SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
|
||||
},
|
||||
NvdXML: {
|
||||
Type: NvdXML,
|
||||
SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
|
||||
},
|
||||
},
|
||||
},
|
||||
out: []CveContentStr{
|
||||
{
|
||||
Type: Jvn,
|
||||
Value: "https://jvn.jp/vu/JVNVU93610402/",
|
||||
},
|
||||
{
|
||||
Type: NvdXML,
|
||||
Value: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
|
||||
},
|
||||
{
|
||||
Type: RedHat,
|
||||
Value: "https://access.redhat.com/security/cve/CVE-2017-6074",
|
||||
},
|
||||
},
|
||||
},
|
||||
// lang: en
|
||||
{
|
||||
in: in{
|
||||
lang: "en",
|
||||
cveID: "CVE-2017-6074",
|
||||
cont: CveContents{
|
||||
Jvn: {
|
||||
Type: Jvn,
|
||||
SourceLink: "https://jvn.jp/vu/JVNVU93610402/",
|
||||
},
|
||||
RedHat: {
|
||||
Type: RedHat,
|
||||
SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
|
||||
},
|
||||
NvdXML: {
|
||||
Type: NvdXML,
|
||||
SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
|
||||
},
|
||||
},
|
||||
},
|
||||
out: []CveContentStr{
|
||||
{
|
||||
Type: NvdXML,
|
||||
Value: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
|
||||
},
|
||||
{
|
||||
Type: RedHat,
|
||||
Value: "https://access.redhat.com/security/cve/CVE-2017-6074",
|
||||
},
|
||||
},
|
||||
},
|
||||
// lang: empty
|
||||
{
|
||||
in: in{
|
||||
lang: "en",
|
||||
cveID: "CVE-2017-6074",
|
||||
cont: CveContents{},
|
||||
},
|
||||
out: []CveContentStr{
|
||||
{
|
||||
Type: Nvd,
|
||||
Value: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
actual := tt.in.cont.SourceLinks(tt.in.lang, "redhat", tt.in.cveID)
|
||||
if !reflect.DeepEqual(tt.out, actual) {
|
||||
t.Errorf("\n[%d] expected: %v\n actual: %v\n", i, tt.out, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVendorLink(t *testing.T) {
|
||||
type in struct {
|
||||
family string
|
||||
vinfo VulnInfo
|
||||
}
|
||||
var tests = []struct {
|
||||
in in
|
||||
out map[string]string
|
||||
}{
|
||||
{
|
||||
in: in{
|
||||
family: "redhat",
|
||||
vinfo: VulnInfo{
|
||||
CveID: "CVE-2017-6074",
|
||||
CveContents: CveContents{
|
||||
Jvn: {
|
||||
Type: Jvn,
|
||||
SourceLink: "https://jvn.jp/vu/JVNVU93610402/",
|
||||
},
|
||||
RedHat: {
|
||||
Type: RedHat,
|
||||
SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
|
||||
},
|
||||
NvdXML: {
|
||||
Type: NvdXML,
|
||||
SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
out: map[string]string{
|
||||
"RHEL-CVE": "https://access.redhat.com/security/cve/CVE-2017-6074",
|
||||
},
|
||||
},
|
||||
{
|
||||
in: in{
|
||||
family: "ubuntu",
|
||||
vinfo: VulnInfo{
|
||||
CveID: "CVE-2017-6074",
|
||||
CveContents: CveContents{
|
||||
RedHat: {
|
||||
Type: Ubuntu,
|
||||
SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
out: map[string]string{
|
||||
"Ubuntu-CVE": "http://people.ubuntu.com/~ubuntu-security/cve/CVE-2017-6074",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
actual := tt.in.vinfo.VendorLinks(tt.in.family)
|
||||
for k := range tt.out {
|
||||
if tt.out[k] != actual[k] {
|
||||
t.Errorf("\nexpected: %s\n actual: %s\n", tt.out[k], actual[k])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
145
models/library.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/aquasecurity/trivy-db/pkg/db"
|
||||
trivyDBTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
"github.com/aquasecurity/trivy/pkg/detector/library"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
// "github.com/aquasecurity/go-dep-parser/pkg/types"
|
||||
"github.com/knqyf263/go-version"
|
||||
)
|
||||
|
||||
// LibraryScanners is an array of LibraryScanner
|
||||
type LibraryScanners []LibraryScanner
|
||||
|
||||
// Find : find by name
|
||||
func (lss LibraryScanners) Find(path, name string) map[string]types.Library {
|
||||
filtered := map[string]types.Library{}
|
||||
for _, ls := range lss {
|
||||
for _, lib := range ls.Libs {
|
||||
if ls.Path == path && lib.Name == name {
|
||||
filtered[ls.Path] = lib
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
// LibraryScanner has libraries information
|
||||
type LibraryScanner struct {
|
||||
Path string
|
||||
Libs []types.Library
|
||||
}
|
||||
|
||||
// Scan : scan target library
|
||||
func (s LibraryScanner) Scan() ([]VulnInfo, error) {
|
||||
scanner, err := library.DriverFactory{}.NewDriver(filepath.Base(string(s.Path)))
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("Failed to new a library driver: %w", err)
|
||||
}
|
||||
var vulnerabilities = []VulnInfo{}
|
||||
for _, pkg := range s.Libs {
|
||||
v, err := version.NewVersion(pkg.Version)
|
||||
if err != nil {
|
||||
util.Log.Debugf("new version cant detected %s@%s", pkg.Name, pkg.Version)
|
||||
continue
|
||||
}
|
||||
|
||||
tvulns, err := scanner.Detect(pkg.Name, v)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to detect %s vulnerabilities: %w", scanner.Type(), err)
|
||||
}
|
||||
if len(tvulns) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
vulns := s.convertFanalToVuln(tvulns)
|
||||
vulnerabilities = append(vulnerabilities, vulns...)
|
||||
}
|
||||
|
||||
return vulnerabilities, nil
|
||||
}
|
||||
|
||||
func (s LibraryScanner) convertFanalToVuln(tvulns []types.DetectedVulnerability) (vulns []VulnInfo) {
|
||||
for _, tvuln := range tvulns {
|
||||
vinfo, err := s.getVulnDetail(tvuln)
|
||||
if err != nil {
|
||||
util.Log.Debugf("failed to getVulnDetail. err: %s, tvuln: %#v", err, tvuln)
|
||||
continue
|
||||
}
|
||||
vulns = append(vulns, vinfo)
|
||||
}
|
||||
return vulns
|
||||
}
|
||||
|
||||
func (s LibraryScanner) getVulnDetail(tvuln types.DetectedVulnerability) (vinfo VulnInfo, err error) {
|
||||
vul, err := db.Config{}.GetVulnerability(tvuln.VulnerabilityID)
|
||||
if err != nil {
|
||||
return vinfo, err
|
||||
}
|
||||
|
||||
vinfo.CveID = tvuln.VulnerabilityID
|
||||
vinfo.CveContents = getCveContents(tvuln.VulnerabilityID, vul)
|
||||
if tvuln.FixedVersion != "" {
|
||||
vinfo.LibraryFixedIns = []LibraryFixedIn{
|
||||
{
|
||||
Key: s.GetLibraryKey(),
|
||||
Name: tvuln.PkgName,
|
||||
FixedIn: tvuln.FixedVersion,
|
||||
Path: s.Path,
|
||||
},
|
||||
}
|
||||
}
|
||||
return vinfo, nil
|
||||
}
|
||||
|
||||
func getCveContents(cveID string, vul trivyDBTypes.Vulnerability) (contents map[CveContentType]CveContent) {
|
||||
contents = map[CveContentType]CveContent{}
|
||||
refs := []Reference{}
|
||||
for _, refURL := range vul.References {
|
||||
refs = append(refs, Reference{Source: "trivy", Link: refURL})
|
||||
}
|
||||
|
||||
content := CveContent{
|
||||
Type: Trivy,
|
||||
CveID: cveID,
|
||||
Title: vul.Title,
|
||||
Summary: vul.Description,
|
||||
Cvss3Severity: string(vul.Severity),
|
||||
References: refs,
|
||||
}
|
||||
contents[Trivy] = content
|
||||
return contents
|
||||
}
|
||||
|
||||
// LibraryMap is filename and library type
|
||||
var LibraryMap = map[string]string{
|
||||
"package-lock.json": "node",
|
||||
"yarn.lock": "node",
|
||||
"Gemfile.lock": "ruby",
|
||||
"Cargo.lock": "rust",
|
||||
"composer.lock": "php",
|
||||
"Pipfile.lock": "python",
|
||||
"poetry.lock": "python",
|
||||
}
|
||||
|
||||
// GetLibraryKey returns target library key
|
||||
func (s LibraryScanner) GetLibraryKey() string {
|
||||
fileName := filepath.Base(s.Path)
|
||||
return LibraryMap[fileName]
|
||||
}
|
||||
|
||||
// LibraryFixedIn has library fixed information
|
||||
type LibraryFixedIn struct {
|
||||
Key string `json:"key,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
FixedIn string `json:"fixedIn,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
}
|
||||
96
models/library_test.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
)
|
||||
|
||||
func TestLibraryScanners_Find(t *testing.T) {
|
||||
type args struct {
|
||||
path string
|
||||
name string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
lss LibraryScanners
|
||||
args args
|
||||
want map[string]types.Library
|
||||
}{
|
||||
{
|
||||
name: "single file",
|
||||
lss: LibraryScanners{
|
||||
{
|
||||
Path: "/pathA",
|
||||
Libs: []types.Library{
|
||||
{
|
||||
Name: "libA",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{"/pathA", "libA"},
|
||||
want: map[string]types.Library{
|
||||
"/pathA": {
|
||||
Name: "libA",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multi file",
|
||||
lss: LibraryScanners{
|
||||
{
|
||||
Path: "/pathA",
|
||||
Libs: []types.Library{
|
||||
{
|
||||
Name: "libA",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Path: "/pathB",
|
||||
Libs: []types.Library{
|
||||
{
|
||||
Name: "libA",
|
||||
Version: "1.0.5",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{"/pathA", "libA"},
|
||||
want: map[string]types.Library{
|
||||
"/pathA": {
|
||||
Name: "libA",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "miss",
|
||||
lss: LibraryScanners{
|
||||
{
|
||||
Path: "/pathA",
|
||||
Libs: []types.Library{
|
||||
{
|
||||
Name: "libA",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{"/pathA", "libB"},
|
||||
want: map[string]types.Library{},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.lss.Find(tt.args.path, tt.args.name); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("LibraryScanners.Find() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||