Compare commits
601 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3605645ff6 | ||
|
|
1d97e91341 | ||
|
|
96333f38c9 | ||
|
|
8b5d1c8e92 | ||
|
|
dea80f860c | ||
|
|
6eb4c5a5fe | ||
|
|
b219a8495e | ||
|
|
eb87d5d4e1 | ||
|
|
6963442a5e | ||
|
|
f7299b9dba | ||
|
|
379fc8a1a1 | ||
|
|
947fbbb29e | ||
|
|
06d2032c9c | ||
|
|
d055c48827 | ||
|
|
2a00339da1 | ||
|
|
2d959b3af8 | ||
|
|
595e26db41 | ||
|
|
1e457320c5 | ||
|
|
a06e689502 | ||
|
|
ca3f6b1dbf | ||
|
|
f1c78e42a2 | ||
|
|
2f3b8bf3cc | ||
|
|
ab54266f9e | ||
|
|
d79d138440 | ||
|
|
139f3a81b6 | ||
|
|
d1a617cfff | ||
|
|
48f7597bcf | ||
|
|
93731311a1 | ||
|
|
999529a05b | ||
|
|
847d820af7 | ||
|
|
5234306ded | ||
|
|
86b60e1478 | ||
|
|
42fdc08933 | ||
|
|
38b1d622f6 | ||
|
|
2477f9a8f8 | ||
|
|
ec6e90acd3 | ||
|
|
2aca2e4352 | ||
|
|
14518d925e | ||
|
|
948f8c0751 | ||
|
|
1c1e40058e | ||
|
|
2158fc6cb1 | ||
|
|
91ed318c5d | ||
|
|
bfc3828ce1 | ||
|
|
c7eac4e7fe | ||
|
|
cc63a0eccf | ||
|
|
fd18df1dd4 | ||
|
|
8775b5efdf | ||
|
|
a9f29a6c5d | ||
|
|
05fdde48f9 | ||
|
|
3dfbd6b616 | ||
|
|
04f246cf8b | ||
|
|
7500f41655 | ||
|
|
a1cc152e81 | ||
|
|
1c77bc1ba3 | ||
|
|
ec31c54caf | ||
|
|
2f05864813 | ||
|
|
2fbc0a001e | ||
|
|
7d8a24ee1a | ||
|
|
7750347010 | ||
|
|
9bcffcd721 | ||
|
|
787604de6a | ||
|
|
5164fb1423 | ||
|
|
07335617d3 | ||
|
|
e5855922c1 | ||
|
|
671be3f2f7 | ||
|
|
fe8d252c51 | ||
|
|
0cdc7a3af5 | ||
|
|
1cfe155a3a | ||
|
|
2923cbc645 | ||
|
|
7c209cc9dc | ||
|
|
84fa4ce432 | ||
|
|
f2e9cd9668 | ||
|
|
77049d6cbb | ||
|
|
b4c23c158b | ||
|
|
964b4aa389 | ||
|
|
dc5aa35db7 | ||
|
|
43c05d06fc | ||
|
|
a3f7d1d7e7 | ||
|
|
bb4a1ca6c2 | ||
|
|
57cce640e1 | ||
|
|
1eb5d36668 | ||
|
|
6bc4850596 | ||
|
|
24005ae7ae | ||
|
|
7aa296bb57 | ||
|
|
3829ed2f8e | ||
|
|
2b7294a504 | ||
|
|
0c6a892893 | ||
|
|
89d94ad85a | ||
|
|
ffdb78962f | ||
|
|
321dae37ce | ||
|
|
a31797af0b | ||
|
|
32999cf432 | ||
|
|
88218f5d92 | ||
|
|
15761933ac | ||
|
|
0b62842f0e | ||
|
|
6bceddeeda | ||
|
|
2dcbff8cd5 | ||
|
|
8659668177 | ||
|
|
e07b6a9160 | ||
|
|
aac5ef1438 | ||
|
|
d780a73297 | ||
|
|
9ef8cee36e | ||
|
|
77808a2c05 | ||
|
|
177e553d12 | ||
|
|
40f8272a28 | ||
|
|
a7eb1141ae | ||
|
|
c73ed7f32f | ||
|
|
f047a6fe0c | ||
|
|
7f15a86d6a | ||
|
|
da1e515253 | ||
|
|
591786fde6 | ||
|
|
47e6ea249d | ||
|
|
4a72295de7 | ||
|
|
9ed5f2cac5 | ||
|
|
3e67f04fe4 | ||
|
|
b9416ae062 | ||
|
|
b4e49e093e | ||
|
|
020f6ac609 | ||
|
|
7e71cbdd46 | ||
|
|
1003f62212 | ||
|
|
9b18e1f9f0 | ||
|
|
24f790f474 | ||
|
|
fb8749fc5e | ||
|
|
96c3592db1 | ||
|
|
d65421cf46 | ||
|
|
c52ba448cd | ||
|
|
21adce463b | ||
|
|
f24240bf90 | ||
|
|
ff83cadd6e | ||
|
|
e8c09282d9 | ||
|
|
5f4d68cde4 | ||
|
|
9077a83ea8 | ||
|
|
543dc99ecd | ||
|
|
f0b3a8b1db | ||
|
|
0b9ec05181 | ||
|
|
0bf12412d6 | ||
|
|
0ea4d58c63 | ||
|
|
5755b00576 | ||
|
|
1c8e074c9d | ||
|
|
0e0e5ce4be | ||
|
|
23dfe53885 | ||
|
|
8e6351a9e4 | ||
|
|
3086e2760f | ||
|
|
b8db2e0b74 | ||
|
|
43b46cb324 | ||
|
|
d0559c7719 | ||
|
|
231c63cf62 | ||
|
|
2a9aebe059 | ||
|
|
4e535d792f | ||
|
|
4b487503d4 | ||
|
|
0095c40e69 | ||
|
|
82c1abfd3a | ||
|
|
40988401bd | ||
|
|
e8e3f4d138 | ||
|
|
7eb77f5b51 | ||
|
|
e115235299 | ||
|
|
151d4b2d30 | ||
|
|
e553f8b4c5 | ||
|
|
47652ef0fb | ||
|
|
ab0e950800 | ||
|
|
a7b0ce1c85 | ||
|
|
dc9c0edece | ||
|
|
17ae386d1e | ||
|
|
2d369d0cfe | ||
|
|
c36e645d9b | ||
|
|
40039c07e2 | ||
|
|
a692cec0ef | ||
|
|
e7ca491a94 | ||
|
|
23f3e2fc11 | ||
|
|
27b3e17b79 | ||
|
|
740781af56 | ||
|
|
36c9c229b8 | ||
|
|
183fdcbdef | ||
|
|
a2a697900a | ||
|
|
6fef4db8a0 | ||
|
|
e879ff1e9e | ||
|
|
9bfe0627ae | ||
|
|
0179f4299a | ||
|
|
56017e57a0 | ||
|
|
cda91e0906 | ||
|
|
5d47adb5c9 | ||
|
|
54e73c2f54 | ||
|
|
2d075079f1 | ||
|
|
2a8ee4b22b | ||
|
|
1ec31d7be9 | ||
|
|
02286b0c59 | ||
|
|
1d0c5dea9f | ||
|
|
1c4a12c4b7 | ||
|
|
3f2ac45d71 | ||
|
|
518f4dc039 | ||
|
|
2cdeef4ffe | ||
|
|
03579126fd | ||
|
|
e3c27e1817 | ||
|
|
aeaf308679 | ||
|
|
f5e47bea40 | ||
|
|
50cf13a7f2 | ||
|
|
abd8041772 | ||
|
|
847c6438e7 | ||
|
|
ef8309df27 | ||
|
|
0dff6cf983 | ||
|
|
4c04acbd9e | ||
|
|
1c4f231572 | ||
|
|
51b8e169d2 | ||
|
|
b4611ae9b7 | ||
|
|
cd6722017b | ||
|
|
290edffccf | ||
|
|
64a6222bf9 | ||
|
|
adb686b7c9 | ||
|
|
d4af341b0f | ||
|
|
fea7e93c8d | ||
|
|
8b6b8d0f2e | ||
|
|
4dcbd865cc | ||
|
|
39b19444fe | ||
|
|
644d5a5462 | ||
|
|
8e18451e3f | ||
|
|
3dbdd01f97 | ||
|
|
a89079c005 | ||
|
|
a8c0926b4f | ||
|
|
dd2959a31b | ||
|
|
51099f42c3 | ||
|
|
63f170cc7a | ||
|
|
3c1489e588 | ||
|
|
e4f1e03f62 | ||
|
|
83d48ec990 | ||
|
|
b20d2b2684 | ||
|
|
2b918c70ae | ||
|
|
1100c133ba | ||
|
|
88899f0e89 | ||
|
|
59dc0059bc | ||
|
|
986fb304c0 | ||
|
|
d6435d2885 | ||
|
|
affb456499 | ||
|
|
705ed0a0ac | ||
|
|
dfffe5b508 | ||
|
|
fca102edba | ||
|
|
554b6345a2 | ||
|
|
aa954dc84c | ||
|
|
b5506a1368 | ||
|
|
0b55f94828 | ||
|
|
a67052f48c | ||
|
|
6eff6a9329 | ||
|
|
69d32d4511 | ||
|
|
d7a613b710 | ||
|
|
669c019287 | ||
|
|
fcc4901a10 | ||
|
|
4359503484 | ||
|
|
b13f93a2d3 | ||
|
|
8405e0fad6 | ||
|
|
aceb3f1826 | ||
|
|
a206675f3e | ||
|
|
f4253d74ae | ||
|
|
aaea15e516 | ||
|
|
83d1f80959 | ||
|
|
a33cff8f13 | ||
|
|
8679759f60 | ||
|
|
53deaee3d7 | ||
|
|
5a14a58fe4 | ||
|
|
fb1fbf8f95 | ||
|
|
cfbf779f9b | ||
|
|
d576b6c6c1 | ||
|
|
514eb71482 | ||
|
|
43ed904db1 | ||
|
|
0a440ca629 | ||
|
|
eff1dbf95b | ||
|
|
9a32a94806 | ||
|
|
2534098509 | ||
|
|
9497365758 | ||
|
|
101c44c9c0 | ||
|
|
ffd745c004 | ||
|
|
5fea4eaef8 | ||
|
|
1f610043cf | ||
|
|
3f8de02683 | ||
|
|
d02535d053 | ||
|
|
75fceff5f7 | ||
|
|
ebd3834a35 | ||
|
|
93059b74c3 | ||
|
|
2fc3462d35 | ||
|
|
f78dab50cb | ||
|
|
edb324c3d9 | ||
|
|
83bcca6e66 | ||
|
|
a124518d78 | ||
|
|
94bf630e29 | ||
|
|
31bb33fd90 | ||
|
|
4b680b9960 | ||
|
|
8a8ab8cb18 | ||
|
|
8146f5fd1b | ||
|
|
425c585e47 | ||
|
|
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 |
36
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,36 +0,0 @@
|
||||
|
||||
# Environment
|
||||
|
||||
## Vuls
|
||||
|
||||
Hash : ____
|
||||
|
||||
To check the commit hash of HEAD
|
||||
$ vuls -v
|
||||
|
||||
or
|
||||
$ cd $GOPATH/src/github.com/future-architect/vuls
|
||||
$ git rev-parse --short HEAD
|
||||
|
||||
## OS
|
||||
- Target Server: Write here
|
||||
- Vuls Server: Write here
|
||||
|
||||
## Go
|
||||
- Go version: here
|
||||
|
||||
# Current Output
|
||||
|
||||
Please re-run the command using ```-debug``` and provide the output below.
|
||||
|
||||
# Addition Details
|
||||
|
||||
Can you also please fill in each of the remaining sections.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
## Actual Behavior
|
||||
|
||||
## Steps to reproduce the behaviour
|
||||
|
||||
|
||||
24
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,24 +0,0 @@
|
||||
## What did you implement:
|
||||
|
||||
Closes #XXXXX
|
||||
|
||||
## How did you implement it:
|
||||
|
||||
|
||||
## How can we verify it:
|
||||
|
||||
|
||||
## Todos:
|
||||
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
|
||||
***Is it a breaking change?:*** NO
|
||||
26
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
version: latest
|
||||
args: --timeout=10m
|
||||
|
||||
- name: testing
|
||||
run: go test -race ./...
|
||||
36
.gitignore
vendored
@@ -1,14 +1,24 @@
|
||||
vuls
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
.vscode
|
||||
*.txt
|
||||
*.sqlite3*
|
||||
*.db
|
||||
tags
|
||||
.gitmodules
|
||||
coverage.out
|
||||
issues/
|
||||
vendor/
|
||||
log/
|
||||
results/
|
||||
*config.toml
|
||||
!setup/docker/*
|
||||
|
||||
# Vuls
|
||||
vuls
|
||||
!cmd/vuls
|
||||
vuls.db
|
||||
config.json
|
||||
results
|
||||
@@ -1,5 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.8
|
||||
|
||||
512
CHANGELOG.md
@@ -1,512 +0,0 @@
|
||||
# Change Log
|
||||
|
||||
## [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)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- 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) ([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))
|
||||
- Typo fix in error messages [\#14](https://github.com/future-architect/vuls/pull/14) ([Bregor](https://github.com/Bregor))
|
||||
- Fix index out of range error when the number of servers is over 6. \#12 [\#13](https://github.com/future-architect/vuls/pull/13) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Revise small grammar mistakes in serverapi.go [\#9](https://github.com/future-architect/vuls/pull/9) ([cpobrien](https://github.com/cpobrien))
|
||||
- Fix error handling in HTTP backoff function [\#7](https://github.com/future-architect/vuls/pull/7) ([kotakanbe](https://github.com/kotakanbe))
|
||||
|
||||
## [v0.1.1](https://github.com/future-architect/vuls/tree/v0.1.1) (2016-04-06)
|
||||
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.0...v0.1.1)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Typo in Exapmle [\#6](https://github.com/future-architect/vuls/pull/6) ([toli](https://github.com/toli))
|
||||
|
||||
## [v0.1.0](https://github.com/future-architect/vuls/tree/v0.1.0) (2016-04-04)
|
||||
**Merged pull requests:**
|
||||
|
||||
- English translation [\#4](https://github.com/future-architect/vuls/pull/4) ([hikachan](https://github.com/hikachan))
|
||||
- English translation [\#3](https://github.com/future-architect/vuls/pull/3) ([chewyinping](https://github.com/chewyinping))
|
||||
- Add a Bitdeli Badge to README [\#2](https://github.com/future-architect/vuls/pull/2) ([bitdeli-chef](https://github.com/bitdeli-chef))
|
||||
|
||||
|
||||
|
||||
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
|
||||
89
GNUmakefile
@@ -1,71 +1,42 @@
|
||||
.PHONY: \
|
||||
dep \
|
||||
depup \
|
||||
build \
|
||||
install \
|
||||
all \
|
||||
vendor \
|
||||
lint \
|
||||
vet \
|
||||
fmt \
|
||||
fmtcheck \
|
||||
pretest \
|
||||
test \
|
||||
cov \
|
||||
clean
|
||||
|
||||
SRCS = $(shell git ls-files '*.go')
|
||||
PKGS = ./. ./cache ./commands ./config ./models ./oval ./report ./scan ./util
|
||||
VERSION := $(shell git describe --tags --abbrev=0)
|
||||
ifeq ($(VERSION), )
|
||||
VERSION := $(shell git rev-parse --abbrev-ref HEAD)
|
||||
endif
|
||||
ifeq ($(shell git rev-parse --abbrev-ref HEAD), nightly)
|
||||
VERSION := nightly
|
||||
endif
|
||||
REVISION := $(shell git rev-parse --short HEAD)
|
||||
LDFLAGS := -X 'main.version=$(VERSION)' \
|
||||
-X 'main.revision=$(REVISION)'
|
||||
LDFLAGS := -ldflags "-s -w -X=github.com/future-architect/vuls/pkg/cmd/version.Version=$(VERSION) -X=github.com/future-architect/vuls/pkg/cmd/version.Revision=$(REVISION)"
|
||||
|
||||
all: dep build test
|
||||
GOPATH := $(shell go env GOPATH)
|
||||
GOBIN := $(GOPATH)/bin
|
||||
|
||||
dep:
|
||||
go get -u github.com/golang/dep/...
|
||||
dep ensure
|
||||
$(GOBIN)/golangci-lint:
|
||||
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
|
||||
|
||||
depup:
|
||||
go get -u github.com/golang/dep/...
|
||||
dep ensure -update
|
||||
.PHONY: build
|
||||
build:
|
||||
go build $(LDFLAGS) ./cmd/vuls
|
||||
|
||||
build: main.go dep
|
||||
go build -ldflags "$(LDFLAGS)" -o vuls $<
|
||||
.PHONY: install
|
||||
install:
|
||||
go install $(LDFLAGS) ./cmd/vuls
|
||||
|
||||
install: main.go dep
|
||||
go install -ldflags "$(LDFLAGS)"
|
||||
|
||||
|
||||
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
|
||||
echo $(PKGS) | xargs go vet || exit;
|
||||
|
||||
fmt:
|
||||
gofmt -w $(SRCS)
|
||||
|
||||
fmtcheck:
|
||||
$(foreach file,$(SRCS),gofmt -d $(file);)
|
||||
.PHONY: test
|
||||
test: pretest
|
||||
go test -race ./...
|
||||
|
||||
.PHONY: pretest
|
||||
pretest: lint vet fmtcheck
|
||||
|
||||
test: pretest
|
||||
go install
|
||||
echo $(PKGS) | xargs go test -cover -v || exit;
|
||||
.PHONY: lint
|
||||
lint: $(GOBIN)/golangci-lint
|
||||
golangci-lint run
|
||||
|
||||
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;
|
||||
.PHONY: vet
|
||||
vet:
|
||||
go vet ./...
|
||||
|
||||
.PHONY: fmtcheck
|
||||
fmtcheck:
|
||||
gofmt -s -d .
|
||||
291
Gopkg.lock
generated
@@ -1,291 +0,0 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/Azure/azure-sdk-for-go"
|
||||
packages = ["storage"]
|
||||
revision = "57db66900881e9fd21fd041a9d013514700ecab3"
|
||||
version = "v10.3.0-beta"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/Azure/go-autorest"
|
||||
packages = ["autorest","autorest/adal","autorest/azure","autorest/date"]
|
||||
revision = "77a52603f06947221c672f10275abc9bf2c7d557"
|
||||
version = "v8.3.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/BurntSushi/toml"
|
||||
packages = ["."]
|
||||
revision = "b26d9c308763d68093482582cea63d69be07a0f0"
|
||||
version = "v0.3.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/asaskevich/govalidator"
|
||||
packages = ["."]
|
||||
revision = "4918b99a7cb949bb295f3c7bbaf24b577d806e35"
|
||||
version = "v6"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/aws/aws-sdk-go"
|
||||
packages = ["aws","aws/awserr","aws/awsutil","aws/client","aws/client/metadata","aws/corehandlers","aws/credentials","aws/credentials/ec2rolecreds","aws/credentials/endpointcreds","aws/credentials/stscreds","aws/defaults","aws/ec2metadata","aws/endpoints","aws/request","aws/session","aws/signer/v4","internal/shareddefaults","private/protocol","private/protocol/query","private/protocol/query/queryutil","private/protocol/rest","private/protocol/restxml","private/protocol/xml/xmlutil","service/s3","service/sts"]
|
||||
revision = "264af29009637e0a9e5d4a276d0969c3ed918ffd"
|
||||
version = "v1.10.29"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/boltdb/bolt"
|
||||
packages = ["."]
|
||||
revision = "2f1ce7a837dcb8da3ec595b1dac9d0632f0f99e8"
|
||||
version = "v1.3.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/cenkalti/backoff"
|
||||
packages = ["."]
|
||||
revision = "61153c768f31ee5f130071d08fc82b85208528de"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/cheggaaa/pb"
|
||||
packages = ["."]
|
||||
revision = "0d6285554e726cc0620cbecc7e6969c945dcc63b"
|
||||
version = "v1.0.17"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/dgrijalva/jwt-go"
|
||||
packages = ["."]
|
||||
revision = "d2709f9f1f31ebcda9651b03077758c1f3a0018c"
|
||||
version = "v3.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/go-ini/ini"
|
||||
packages = ["."]
|
||||
revision = "20b96f641a5ea98f2f8619ff4f3e061cff4833bd"
|
||||
version = "v1.28.2"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/go-redis/redis"
|
||||
packages = [".","internal","internal/consistenthash","internal/hashtag","internal/pool","internal/proto"]
|
||||
revision = "19c1c2272e00c1aaa903cf574c746cd449f9cd3c"
|
||||
version = "v6.5.7"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/go-sql-driver/mysql"
|
||||
packages = ["."]
|
||||
revision = "a0583e0143b1624142adab07e0e97fe106d99561"
|
||||
version = "v1.3"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/google/subcommands"
|
||||
packages = ["."]
|
||||
revision = "ce3d4cfc062faac7115d44e5befec8b5a08c3faa"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/gosuri/uitable"
|
||||
packages = [".","util/strutil","util/wordwrap"]
|
||||
revision = "36ee7e946282a3fb1cfecd476ddc9b35d8847e42"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/howeyc/gopass"
|
||||
packages = ["."]
|
||||
revision = "bf9dde6d0d2c004a008c27aaee91170c786f6db8"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/jinzhu/gorm"
|
||||
packages = [".","dialects/mysql","dialects/postgres","dialects/sqlite"]
|
||||
revision = "5174cc5c242a728b435ea2be8a2f7f998e15429b"
|
||||
version = "v1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/jinzhu/inflection"
|
||||
packages = ["."]
|
||||
revision = "1c35d901db3da928c72a72d8458480cc9ade058f"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/jmespath/go-jmespath"
|
||||
packages = ["."]
|
||||
revision = "3433f3ea46d9f8019119e7dd41274e112a2359a9"
|
||||
version = "0.2.2"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/jroimartin/gocui"
|
||||
packages = ["."]
|
||||
revision = "4e9ce9a8e26f2ef33dfe297dbdfca148733b6b9b"
|
||||
version = "v0.3.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/k0kubun/pp"
|
||||
packages = ["."]
|
||||
revision = "d1532fc5d94ecdf2da29e24d7b99721f3287de4a"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/knqyf263/go-deb-version"
|
||||
packages = ["."]
|
||||
revision = "9865fe14d09b1c729188ac810466dde90f897ee3"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/knqyf263/go-rpm-version"
|
||||
packages = ["."]
|
||||
revision = "74609b86c936dff800c69ec89fcf4bc52d5f13a4"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/kotakanbe/go-cve-dictionary"
|
||||
packages = ["config","db","jvn","log","models","nvd","util"]
|
||||
revision = "c20fa7e1d07f7c700baf12c855f7fcf61525f1b6"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/kotakanbe/go-pingscanner"
|
||||
packages = ["."]
|
||||
revision = "641dc2cc2d3cbf295dad356667b74c69bcbd6f70"
|
||||
version = "v0.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/kotakanbe/goval-dictionary"
|
||||
packages = ["config","db","db/rdb","log","models"]
|
||||
revision = "3523cc174e68f285d0572d07c68ffa3a9290799c"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/kotakanbe/logrus-prefixed-formatter"
|
||||
packages = ["."]
|
||||
revision = "75edb2e85a38873f0318be05a458446681d1022f"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/labstack/gommon"
|
||||
packages = ["color","log"]
|
||||
revision = "779b8a8b9850a97acba6a3fe20feb628c39e17c1"
|
||||
version = "0.2.2"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/lib/pq"
|
||||
packages = [".","hstore","oid"]
|
||||
revision = "e42267488fe361b9dc034be7a6bffef5b195bceb"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-colorable"
|
||||
packages = ["."]
|
||||
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
|
||||
version = "v0.0.9"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-isatty"
|
||||
packages = ["."]
|
||||
revision = "fc9e8d8ef48496124e79ae0df75490096eccf6fe"
|
||||
version = "v0.0.2"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-runewidth"
|
||||
packages = ["."]
|
||||
revision = "9e777a8366cce605130a531d2cd6363d07ad7317"
|
||||
version = "v0.0.2"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-sqlite3"
|
||||
packages = ["."]
|
||||
revision = "ca5e3819723d8eeaf170ad510e7da1d6d2e94a08"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/mgutz/ansi"
|
||||
packages = ["."]
|
||||
revision = "9520e82c474b0a04dd04f8a40959027271bab992"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/moul/http2curl"
|
||||
packages = ["."]
|
||||
revision = "4e24498b31dba4683efb9d35c1c8a91e2eda28c8"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/nsf/termbox-go"
|
||||
packages = ["."]
|
||||
revision = "4ed959e0540971545eddb8c75514973d670cf739"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/parnurzeal/gorequest"
|
||||
packages = ["."]
|
||||
revision = "a578a48e8d6ca8b01a3b18314c43c6716bb5f5a3"
|
||||
version = "v0.2.15"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pkg/errors"
|
||||
packages = ["."]
|
||||
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
||||
version = "v0.8.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/rifflock/lfshook"
|
||||
packages = ["."]
|
||||
revision = "6844c808343cb8fa357d7f141b1b990e05d24e41"
|
||||
version = "1.7"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/satori/uuid"
|
||||
packages = ["."]
|
||||
revision = "879c5887cd475cd7864858769793b2ceb0d44feb"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/sirupsen/logrus"
|
||||
packages = ["."]
|
||||
revision = "84573d5f03ab3740f524c7842c3a9bf617961d32"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/valyala/bytebufferpool"
|
||||
packages = ["."]
|
||||
revision = "e746df99fe4a3986f4d4f79e13c1e0117ce9c2f7"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/valyala/fasttemplate"
|
||||
packages = ["."]
|
||||
revision = "dcecefd839c4193db0d35b88ec65b4c12d360ab0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/ymomoi/goval-parser"
|
||||
packages = ["oval"]
|
||||
revision = "0a0be1dd9d0855b50be0be5a10ad3085382b6d59"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = ["curve25519","ed25519","ed25519/internal/edwards25519","ssh","ssh/agent","ssh/terminal"]
|
||||
revision = "eb71ad9bd329b5ac0fd0148dd99bd62e8be8e035"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
packages = ["context","idna","publicsuffix"]
|
||||
revision = "1c05540f6879653db88113bc4a2b70aec4bd491f"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix","windows"]
|
||||
revision = "07c182904dbd53199946ba614a412c61d3c548f5"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/text"
|
||||
packages = ["internal/gen","internal/triegen","internal/ucd","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"]
|
||||
revision = "e56139fd9c5bc7244c76116c68e500765bb6db6b"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "36d700add80d36c56484ed310b9a7e622b3e308ab22eb42bdfb02fd8f5c90407"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
90
Gopkg.toml
@@ -1,90 +0,0 @@
|
||||
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/BurntSushi/toml"
|
||||
version = "0.3.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/asaskevich/govalidator"
|
||||
version = "6.0.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/boltdb/bolt"
|
||||
version = "1.3.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/cenkalti/backoff"
|
||||
version = "1.0.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/google/subcommands"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/gosuri/uitable"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/howeyc/gopass"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/jroimartin/gocui"
|
||||
version = "0.3.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/k0kubun/pp"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/knqyf263/go-deb-version"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/knqyf263/go-rpm-version"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/kotakanbe/go-pingscanner"
|
||||
version = "0.1.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/kotakanbe/logrus-prefixed-formatter"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/parnurzeal/gorequest"
|
||||
version = "0.2.15"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/rifflock/lfshook"
|
||||
version = "1.7.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/sirupsen/logrus"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/kotakanbe/go-cve-dictionary"
|
||||
674
LICENSE
@@ -1,674 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://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 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
|
||||
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.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
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.
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
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.
|
||||
|
||||
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/>.
|
||||
|
||||
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".
|
||||
|
||||
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>.
|
||||
224
README.fr.md
@@ -1,224 +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 English](https://github.com/future-architect/vuls/blob/master/README.md)
|
||||
[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, Raspbian
|
||||
- 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.7.1 or later
|
||||
- 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.7.1.linux-amd64.tar.gz
|
||||
$ sudo tar -C /usr/local -xzf go1.7.1.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).
|
||||
|
||||
## 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 -cve-dictionary-dbpath=$PWD/cve.sqlite3
|
||||
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 Calculator 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
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
----
|
||||
|
||||
For more information see [README in English](https://github.com/future-architect/vuls/blob/master/README.md)
|
||||
1882
README.ja.md
191
cache/bolt.go
vendored
@@ -1,191 +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 cache
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// 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 fmt.Errorf("Failed to create bucket: %s", 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 fmt.Errorf("Failed to marshal to JSON: %s", 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 fmt.Errorf("Failed to marshal to JSON: %s", 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 changelgo 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 fmt.Errorf("Faild 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 fmt.Errorf("Faild to get Bucket: %s", servername)
|
||||
}
|
||||
if err := bkt.Put([]byte(packName), []byte(changelog)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
137
cache/bolt_test.go
vendored
@@ -1,137 +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 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
50
cache/db.go
vendored
@@ -1,50 +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 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
|
||||
}
|
||||
15
cmd/vuls/main.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/future-architect/vuls/pkg/cmd/root"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := root.NewCmdRoot().Execute(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to exec vuls: %s\n", fmt.Sprintf("%+v", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -1,185 +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 (
|
||||
"context"
|
||||
"flag"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"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
|
||||
logDir string
|
||||
askKeyPassword bool
|
||||
containersOnly bool
|
||||
deep bool
|
||||
sshNative bool
|
||||
httpProxy string
|
||||
timeoutSec int
|
||||
|
||||
debug bool
|
||||
}
|
||||
|
||||
// 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
|
||||
[-deep]
|
||||
[-config=/path/to/config.toml]
|
||||
[-log-dir=/path/to/log]
|
||||
[-ask-key-password]
|
||||
[-timeout=300]
|
||||
[-ssh-external]
|
||||
[-containers-only]
|
||||
[-http-proxy=http://192.168.0.1:8080]
|
||||
[-debug]
|
||||
|
||||
[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(&p.logDir, "log-dir", defaultLogDir, "/path/to/log")
|
||||
|
||||
f.BoolVar(&p.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.BoolVar(&p.deep, "deep", false, "Config test for deep scan mode")
|
||||
|
||||
f.StringVar(
|
||||
&p.httpProxy,
|
||||
"http-proxy",
|
||||
"",
|
||||
"http://proxy-url:port (default: empty)",
|
||||
)
|
||||
|
||||
f.BoolVar(
|
||||
&p.sshNative,
|
||||
"ssh-native-insecure",
|
||||
false,
|
||||
"Use Native Go implementation of SSH. Default: Use the external command")
|
||||
|
||||
f.BoolVar(
|
||||
&p.containersOnly,
|
||||
"containers-only",
|
||||
false,
|
||||
"Test containers only. Default: Test both of hosts and containers")
|
||||
}
|
||||
|
||||
// Execute execute
|
||||
func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
// Setup Logger
|
||||
c.Conf.Debug = p.debug
|
||||
c.Conf.LogDir = p.logDir
|
||||
util.Log = util.NewCustomLogger(c.ServerInfo{})
|
||||
|
||||
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 {
|
||||
util.Log.Errorf("Error loading %s, %s", p.configPath, err)
|
||||
util.Log.Errorf("If you update Vuls and get this error, there may be incompatible changes in config.toml")
|
||||
util.Log.Errorf("Please check README: https://github.com/future-architect/vuls#configuration")
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
c.Conf.SSHNative = p.sshNative
|
||||
c.Conf.HTTPProxy = p.httpProxy
|
||||
c.Conf.ContainersOnly = p.containersOnly
|
||||
c.Conf.Deep = p.deep
|
||||
|
||||
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: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
util.Log.Info("Checking dependencies...")
|
||||
scan.CheckDependencies(p.timeoutSec)
|
||||
|
||||
util.Log.Info("Checking sudo settings...")
|
||||
scan.CheckIfSudoNoPasswd(p.timeoutSec)
|
||||
|
||||
scan.PrintSSHableServerNames()
|
||||
return subcommands.ExitSuccess
|
||||
}
|
||||
@@ -1,175 +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 (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/google/subcommands"
|
||||
|
||||
ps "github.com/kotakanbe/go-pingscanner"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// DiscoverCmd is Subcommand of host discovery mode
|
||||
type DiscoverCmd struct {
|
||||
}
|
||||
|
||||
// Name return subcommand name
|
||||
func (*DiscoverCmd) Name() string { return "discover" }
|
||||
|
||||
// Synopsis return synopsis
|
||||
func (*DiscoverCmd) Synopsis() string { return "Host discovery in the CIDR" }
|
||||
|
||||
// Usage return usage
|
||||
func (*DiscoverCmd) Usage() string {
|
||||
return `discover:
|
||||
discover 192.168.0.0/24
|
||||
|
||||
`
|
||||
}
|
||||
|
||||
// SetFlags set flag
|
||||
func (p *DiscoverCmd) SetFlags(f *flag.FlagSet) {
|
||||
}
|
||||
|
||||
// Execute execute
|
||||
func (p *DiscoverCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
// validate
|
||||
if len(f.Args()) == 0 {
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
for _, cidr := range f.Args() {
|
||||
scanner := ps.PingScanner{
|
||||
CIDR: cidr,
|
||||
PingOptions: []string{
|
||||
"-c1",
|
||||
"-t1",
|
||||
},
|
||||
NumOfConcurrency: 100,
|
||||
}
|
||||
hosts, err := scanner.Scan()
|
||||
|
||||
if err != nil {
|
||||
logrus.Errorf("Host Discovery failed. err: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
if len(hosts) < 1 {
|
||||
logrus.Errorf("Active hosts not found in %s", cidr)
|
||||
return subcommands.ExitSuccess
|
||||
} else if err := printConfigToml(hosts); err != nil {
|
||||
logrus.Errorf("Failed to parse template. err: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
return subcommands.ExitSuccess
|
||||
}
|
||||
|
||||
// Output the template of config.toml
|
||||
func printConfigToml(ips []string) (err error) {
|
||||
const tomlTemplate = `
|
||||
[slack]
|
||||
hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
|
||||
channel = "#channel-name"
|
||||
#channel = "${servername}"
|
||||
iconEmoji = ":ghost:"
|
||||
authUser = "username"
|
||||
notifyUsers = ["@username"]
|
||||
|
||||
[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]"
|
||||
|
||||
[default]
|
||||
#port = "22"
|
||||
#user = "username"
|
||||
#keyPath = "/home/username/.ssh/id_rsa"
|
||||
#cpeNames = [
|
||||
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
#]
|
||||
#dependencyCheckXMLPath = "/tmp/dependency-check-report.xml"
|
||||
#ignoreCves = ["CVE-2014-6271"]
|
||||
#optional = [
|
||||
# ["key", "value"],
|
||||
#]
|
||||
#containers = ["${running}"]
|
||||
|
||||
|
||||
[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",
|
||||
#]
|
||||
#dependencyCheckXMLPath = "/tmp/dependency-check-report.xml"
|
||||
#ignoreCves = ["CVE-2014-0160"]
|
||||
#optional = [
|
||||
# ["key", "value"],
|
||||
#]
|
||||
#[servers.{{index $names $i}}.containers]
|
||||
#type = "docker" #or "lxd" default: docker
|
||||
#includes = ["${running}"]
|
||||
#excludes = ["container_name_a", "4aa37a8b63b9"]
|
||||
|
||||
|
||||
{{end}}
|
||||
|
||||
`
|
||||
var tpl *template.Template
|
||||
if tpl, err = template.New("template").Parse(tomlTemplate); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
type activeHosts struct {
|
||||
IPs []string
|
||||
Names []string
|
||||
}
|
||||
|
||||
a := activeHosts{IPs: ips}
|
||||
names := []string{}
|
||||
for _, ip := range ips {
|
||||
// TOML section header must not contain "."
|
||||
name := strings.Replace(ip, ".", "-", -1)
|
||||
names = append(names, name)
|
||||
}
|
||||
a.Names = names
|
||||
|
||||
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
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,98 +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 (
|
||||
"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 {
|
||||
debug bool
|
||||
debugSQL bool
|
||||
resultsDir string
|
||||
}
|
||||
|
||||
// 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(&p.debugSQL, "debug-sql", false, "SQL debug mode")
|
||||
|
||||
wd, _ := os.Getwd()
|
||||
defaultResultsDir := filepath.Join(wd, "results")
|
||||
f.StringVar(&p.resultsDir, "results-dir", defaultResultsDir, "/path/to/results")
|
||||
}
|
||||
|
||||
// Execute execute
|
||||
func (p *HistoryCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
|
||||
c.Conf.DebugSQL = p.debugSQL
|
||||
c.Conf.ResultsDir = p.resultsDir
|
||||
|
||||
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,444 +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 (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/oval"
|
||||
"github.com/future-architect/vuls/report"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/google/subcommands"
|
||||
)
|
||||
|
||||
// ReportCmd is subcommand for reporting
|
||||
type ReportCmd struct {
|
||||
lang string
|
||||
debug bool
|
||||
debugSQL bool
|
||||
configPath string
|
||||
resultsDir string
|
||||
logDir string
|
||||
refreshCve bool
|
||||
|
||||
cvssScoreOver float64
|
||||
ignoreUnscoredCves bool
|
||||
httpProxy string
|
||||
|
||||
cveDBType string
|
||||
cveDBPath string
|
||||
cveDBURL string
|
||||
|
||||
ovalDBType string
|
||||
ovalDBPath string
|
||||
ovalDBURL string
|
||||
|
||||
toSlack bool
|
||||
toEMail bool
|
||||
toLocalFile bool
|
||||
toS3 bool
|
||||
toAzureBlob bool
|
||||
|
||||
formatJSON bool
|
||||
formatXML bool
|
||||
formatOneEMail bool
|
||||
formatOneLineText bool
|
||||
formatShortText bool
|
||||
formatFullText bool
|
||||
|
||||
gzip bool
|
||||
|
||||
awsProfile string
|
||||
awsS3Bucket string
|
||||
awsS3ResultsDir string
|
||||
awsRegion string
|
||||
|
||||
azureAccount string
|
||||
azureKey string
|
||||
azureContainer string
|
||||
|
||||
pipe bool
|
||||
|
||||
diff bool
|
||||
}
|
||||
|
||||
// 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]
|
||||
[-cvedb-type=sqlite3|mysql|postgres]
|
||||
[-cvedb-path=/path/to/cve.sqlite3]
|
||||
[-cvedb-url=http://127.0.0.1:1323 or DB connection string]
|
||||
[-ovaldb-type=sqlite3|mysql]
|
||||
[-ovaldb-path=/path/to/oval.sqlite3]
|
||||
[-ovaldb-url=http://127.0.0.1:1324 or DB connection string]
|
||||
[-cvss-over=7]
|
||||
[-diff]
|
||||
[-ignore-unscored-cves]
|
||||
[-to-email]
|
||||
[-to-slack]
|
||||
[-to-localfile]
|
||||
[-to-s3]
|
||||
[-to-azure-blob]
|
||||
[-format-json]
|
||||
[-format-xml]
|
||||
[-format-one-email]
|
||||
[-format-one-line-text]
|
||||
[-format-short-text]
|
||||
[-format-full-text]
|
||||
[-gzip]
|
||||
[-aws-profile=default]
|
||||
[-aws-region=us-west-2]
|
||||
[-aws-s3-bucket=bucket_name]
|
||||
[-aws-s3-results-dir=/bucket/path/to/results]
|
||||
[-azure-account=account]
|
||||
[-azure-key=key]
|
||||
[-azure-container=container]
|
||||
[-http-proxy=http://192.168.0.1:8080]
|
||||
[-debug]
|
||||
[-debug-sql]
|
||||
[-pipe]
|
||||
|
||||
[SERVER]...
|
||||
`
|
||||
}
|
||||
|
||||
// SetFlags set flag
|
||||
func (p *ReportCmd) 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")
|
||||
|
||||
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(&p.resultsDir, "results-dir", defaultResultsDir, "/path/to/results")
|
||||
|
||||
defaultLogDir := util.GetDefaultLogDir()
|
||||
f.StringVar(&p.logDir, "log-dir", defaultLogDir, "/path/to/log")
|
||||
|
||||
f.BoolVar(
|
||||
&p.refreshCve,
|
||||
"refresh-cve",
|
||||
false,
|
||||
"Refresh CVE information in JSON file under results dir")
|
||||
|
||||
f.StringVar(
|
||||
&p.cveDBType,
|
||||
"cvedb-type",
|
||||
"sqlite3",
|
||||
"DB type for fetching CVE dictionary (sqlite3, mysql or postgres)")
|
||||
|
||||
defaultCveDBPath := filepath.Join(wd, "cve.sqlite3")
|
||||
f.StringVar(
|
||||
&p.cveDBPath,
|
||||
"cvedb-path",
|
||||
defaultCveDBPath,
|
||||
"/path/to/sqlite3 (For get cve detail from cve.sqlite3)")
|
||||
|
||||
f.StringVar(
|
||||
&p.cveDBURL,
|
||||
"cvedb-url",
|
||||
"",
|
||||
"http://cve-dictionary.com:1323 or mysql connection string")
|
||||
|
||||
f.StringVar(
|
||||
&p.ovalDBType,
|
||||
"ovaldb-type",
|
||||
"sqlite3",
|
||||
"DB type for fetching OVAL dictionary (sqlite3 or mysql)")
|
||||
|
||||
defaultOvalDBPath := filepath.Join(wd, "oval.sqlite3")
|
||||
f.StringVar(
|
||||
&p.ovalDBPath,
|
||||
"ovaldb-path",
|
||||
defaultOvalDBPath,
|
||||
"/path/to/sqlite3 (For get oval detail from oval.sqlite3)")
|
||||
|
||||
f.StringVar(
|
||||
&p.ovalDBURL,
|
||||
"ovaldb-url",
|
||||
"",
|
||||
"http://goval-dictionary.com:1324 or mysql connection string")
|
||||
|
||||
f.Float64Var(
|
||||
&p.cvssScoreOver,
|
||||
"cvss-over",
|
||||
0,
|
||||
"-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))")
|
||||
|
||||
f.BoolVar(&p.diff,
|
||||
"diff",
|
||||
false,
|
||||
fmt.Sprintf("Difference between previous result and current result "))
|
||||
|
||||
f.BoolVar(
|
||||
&p.ignoreUnscoredCves,
|
||||
"ignore-unscored-cves",
|
||||
false,
|
||||
"Don't report the unscored CVEs")
|
||||
|
||||
f.StringVar(
|
||||
&p.httpProxy,
|
||||
"http-proxy",
|
||||
"",
|
||||
"http://proxy-url:port (default: empty)")
|
||||
|
||||
f.BoolVar(&p.formatJSON,
|
||||
"format-json",
|
||||
false,
|
||||
fmt.Sprintf("JSON format"))
|
||||
|
||||
f.BoolVar(&p.formatXML,
|
||||
"format-xml",
|
||||
false,
|
||||
fmt.Sprintf("XML format"))
|
||||
|
||||
f.BoolVar(&p.formatOneEMail,
|
||||
"format-one-email",
|
||||
false,
|
||||
"Send all the host report via only one EMail (Specify with -to-email)")
|
||||
|
||||
f.BoolVar(&p.formatOneLineText,
|
||||
"format-one-line-text",
|
||||
false,
|
||||
fmt.Sprintf("One line summary in plain text"))
|
||||
|
||||
f.BoolVar(&p.formatShortText,
|
||||
"format-short-text",
|
||||
false,
|
||||
fmt.Sprintf("Summary in plain text"))
|
||||
|
||||
f.BoolVar(&p.formatFullText,
|
||||
"format-full-text",
|
||||
false,
|
||||
fmt.Sprintf("Detail report in plain text"))
|
||||
|
||||
f.BoolVar(&p.gzip, "gzip", false, "gzip compression")
|
||||
|
||||
f.BoolVar(&p.toSlack, "to-slack", false, "Send report via Slack")
|
||||
f.BoolVar(&p.toEMail, "to-email", false, "Send report via Email")
|
||||
f.BoolVar(&p.toLocalFile,
|
||||
"to-localfile",
|
||||
false,
|
||||
fmt.Sprintf("Write report to localfile"))
|
||||
|
||||
f.BoolVar(&p.toS3,
|
||||
"to-s3",
|
||||
false,
|
||||
"Write report to S3 (bucket/yyyyMMdd_HHmm/servername.json/xml/txt)")
|
||||
f.StringVar(&p.awsProfile, "aws-profile", "default", "AWS profile to use")
|
||||
f.StringVar(&p.awsRegion, "aws-region", "us-east-1", "AWS region to use")
|
||||
f.StringVar(&p.awsS3Bucket, "aws-s3-bucket", "", "S3 bucket name")
|
||||
f.StringVar(&p.awsS3ResultsDir, "aws-s3-results-dir", "", "/bucket/path/to/results")
|
||||
|
||||
f.BoolVar(&p.toAzureBlob,
|
||||
"to-azure-blob",
|
||||
false,
|
||||
"Write report to Azure Storage blob (container/yyyyMMdd_HHmm/servername.json/xml/txt)")
|
||||
f.StringVar(&p.azureAccount,
|
||||
"azure-account",
|
||||
"",
|
||||
"Azure account name to use. AZURE_STORAGE_ACCOUNT environment variable is used if not specified")
|
||||
f.StringVar(&p.azureKey,
|
||||
"azure-key",
|
||||
"",
|
||||
"Azure account key to use. AZURE_STORAGE_ACCESS_KEY environment variable is used if not specified")
|
||||
f.StringVar(&p.azureContainer, "azure-container", "", "Azure storage container name")
|
||||
|
||||
f.BoolVar(
|
||||
&p.pipe,
|
||||
"pipe",
|
||||
false,
|
||||
"Use args passed via PIPE")
|
||||
}
|
||||
|
||||
// Execute execute
|
||||
func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
c.Conf.Debug = p.debug
|
||||
c.Conf.DebugSQL = p.debugSQL
|
||||
c.Conf.LogDir = p.logDir
|
||||
util.Log = util.NewCustomLogger(c.ServerInfo{})
|
||||
|
||||
if err := c.Load(p.configPath, ""); err != nil {
|
||||
util.Log.Errorf("Error loading %s, %s", p.configPath, err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
c.Conf.Lang = p.lang
|
||||
c.Conf.ResultsDir = p.resultsDir
|
||||
c.Conf.RefreshCve = p.refreshCve
|
||||
c.Conf.Diff = p.diff
|
||||
c.Conf.CveDBType = p.cveDBType
|
||||
c.Conf.CveDBPath = p.cveDBPath
|
||||
c.Conf.CveDBURL = p.cveDBURL
|
||||
c.Conf.OvalDBType = p.ovalDBType
|
||||
c.Conf.OvalDBPath = p.ovalDBPath
|
||||
c.Conf.OvalDBURL = p.ovalDBURL
|
||||
c.Conf.CvssScoreOver = p.cvssScoreOver
|
||||
c.Conf.IgnoreUnscoredCves = p.ignoreUnscoredCves
|
||||
c.Conf.HTTPProxy = p.httpProxy
|
||||
|
||||
c.Conf.FormatXML = p.formatXML
|
||||
c.Conf.FormatJSON = p.formatJSON
|
||||
c.Conf.FormatOneEMail = p.formatOneEMail
|
||||
c.Conf.FormatOneLineText = p.formatOneLineText
|
||||
c.Conf.FormatShortText = p.formatShortText
|
||||
c.Conf.FormatFullText = p.formatFullText
|
||||
|
||||
c.Conf.GZIP = p.gzip
|
||||
c.Conf.Diff = p.diff
|
||||
c.Conf.Pipe = p.pipe
|
||||
|
||||
var dir string
|
||||
var err error
|
||||
if p.diff {
|
||||
dir, err = report.JSONDir([]string{})
|
||||
} else {
|
||||
dir, err = report.JSONDir(f.Args())
|
||||
}
|
||||
if err != nil {
|
||||
util.Log.Errorf("Failed to read from JSON: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
// report
|
||||
reports := []report.ResultWriter{
|
||||
report.StdoutWriter{},
|
||||
}
|
||||
|
||||
if p.toSlack {
|
||||
reports = append(reports, report.SlackWriter{})
|
||||
}
|
||||
|
||||
if p.toEMail {
|
||||
reports = append(reports, report.EMailWriter{})
|
||||
}
|
||||
|
||||
if p.toLocalFile {
|
||||
reports = append(reports, report.LocalFileWriter{
|
||||
CurrentDir: dir,
|
||||
})
|
||||
}
|
||||
|
||||
if p.toS3 {
|
||||
c.Conf.AwsRegion = p.awsRegion
|
||||
c.Conf.AwsProfile = p.awsProfile
|
||||
c.Conf.S3Bucket = p.awsS3Bucket
|
||||
c.Conf.S3ResultsDir = p.awsS3ResultsDir
|
||||
if err := report.CheckIfBucketExists(); err != nil {
|
||||
util.Log.Errorf("Check if there is a bucket beforehand: %s, err: %s", c.Conf.S3Bucket, err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
reports = append(reports, report.S3Writer{})
|
||||
}
|
||||
|
||||
if p.toAzureBlob {
|
||||
c.Conf.AzureAccount = p.azureAccount
|
||||
if len(c.Conf.AzureAccount) == 0 {
|
||||
c.Conf.AzureAccount = os.Getenv("AZURE_STORAGE_ACCOUNT")
|
||||
}
|
||||
|
||||
c.Conf.AzureKey = p.azureKey
|
||||
if len(c.Conf.AzureKey) == 0 {
|
||||
c.Conf.AzureKey = os.Getenv("AZURE_STORAGE_ACCESS_KEY")
|
||||
}
|
||||
|
||||
c.Conf.AzureContainer = p.azureContainer
|
||||
if len(c.Conf.AzureContainer) == 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: %s", c.Conf.AzureContainer, err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
reports = append(reports, report.AzureBlobWriter{})
|
||||
}
|
||||
|
||||
if !(p.formatJSON || p.formatOneLineText ||
|
||||
p.formatShortText || p.formatFullText || p.formatXML) {
|
||||
c.Conf.FormatShortText = true
|
||||
}
|
||||
|
||||
util.Log.Info("Validating config...")
|
||||
if !c.Conf.ValidateOnReport() {
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
if err := report.CveClient.CheckHealth(); err != nil {
|
||||
util.Log.Errorf("CVE HTTP server is not running. err: %s", err)
|
||||
util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with -cvedb-path option")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
if c.Conf.CveDBURL != "" {
|
||||
util.Log.Infof("cve-dictionary: %s", c.Conf.CveDBURL)
|
||||
} else {
|
||||
if c.Conf.CveDBType == "sqlite3" {
|
||||
util.Log.Infof("cve-dictionary: %s", c.Conf.CveDBPath)
|
||||
}
|
||||
}
|
||||
|
||||
if c.Conf.OvalDBURL != "" {
|
||||
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-path option")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
if res, err = report.FillCveInfos(res, dir); err != nil {
|
||||
util.Log.Error(err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
for _, w := range reports {
|
||||
if err := w.Write(res...); err != nil {
|
||||
util.Log.Errorf("Failed to report: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
return subcommands.ExitSuccess
|
||||
}
|
||||
261
commands/scan.go
@@ -1,261 +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 (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/scan"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/google/subcommands"
|
||||
"github.com/k0kubun/pp"
|
||||
)
|
||||
|
||||
// ScanCmd is Subcommand of host discovery mode
|
||||
type ScanCmd struct {
|
||||
debug bool
|
||||
configPath string
|
||||
resultsDir string
|
||||
logDir string
|
||||
cacheDBPath string
|
||||
httpProxy string
|
||||
askKeyPassword bool
|
||||
containersOnly bool
|
||||
deep bool
|
||||
skipBroken bool
|
||||
sshNative bool
|
||||
pipe bool
|
||||
timeoutSec int
|
||||
scanTimeoutSec int
|
||||
}
|
||||
|
||||
// Name return subcommand name
|
||||
func (*ScanCmd) Name() string { return "scan" }
|
||||
|
||||
// Synopsis return synopsis
|
||||
func (*ScanCmd) Synopsis() string { return "Scan vulnerabilities" }
|
||||
|
||||
// Usage return usage
|
||||
func (*ScanCmd) Usage() string {
|
||||
return `scan:
|
||||
scan
|
||||
[-deep]
|
||||
[-config=/path/to/config.toml]
|
||||
[-results-dir=/path/to/results]
|
||||
[-log-dir=/path/to/log]
|
||||
[-cachedb-path=/path/to/cache.db]
|
||||
[-ssh-native-insecure]
|
||||
[-containers-only]
|
||||
[-skip-broken]
|
||||
[-http-proxy=http://192.168.0.1:8080]
|
||||
[-ask-key-password]
|
||||
[-timeout=300]
|
||||
[-timeout-scan=7200]
|
||||
[-debug]
|
||||
[-pipe]
|
||||
|
||||
[SERVER]...
|
||||
`
|
||||
}
|
||||
|
||||
// SetFlags set flag
|
||||
func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
|
||||
f.BoolVar(&p.debug, "debug", false, "debug mode")
|
||||
|
||||
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(&p.resultsDir, "results-dir", defaultResultsDir, "/path/to/results")
|
||||
|
||||
defaultLogDir := util.GetDefaultLogDir()
|
||||
f.StringVar(&p.logDir, "log-dir", defaultLogDir, "/path/to/log")
|
||||
|
||||
defaultCacheDBPath := filepath.Join(wd, "cache.db")
|
||||
f.StringVar(
|
||||
&p.cacheDBPath,
|
||||
"cachedb-path",
|
||||
defaultCacheDBPath,
|
||||
"/path/to/cache.db (local cache of changelog for Ubuntu/Debian)")
|
||||
|
||||
f.BoolVar(
|
||||
&p.sshNative,
|
||||
"ssh-native-insecure",
|
||||
false,
|
||||
"Use Native Go implementation of SSH. Default: Use the external command")
|
||||
|
||||
f.BoolVar(
|
||||
&p.containersOnly,
|
||||
"containers-only",
|
||||
false,
|
||||
"Scan containers only. Default: Scan both of hosts and containers")
|
||||
|
||||
f.BoolVar(
|
||||
&p.skipBroken,
|
||||
"skip-broken",
|
||||
false,
|
||||
"[For CentOS] yum update changelog with --skip-broken option")
|
||||
|
||||
f.StringVar(
|
||||
&p.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.deep,
|
||||
"deep",
|
||||
false,
|
||||
"Deep scan mode. Scan accuracy improves and scanned information becomes richer. Since analysis of changelog, issue commands requiring sudo, but it may be slower and high load on the target server")
|
||||
|
||||
f.BoolVar(
|
||||
&p.pipe,
|
||||
"pipe",
|
||||
false,
|
||||
"Use stdin via PIPE")
|
||||
|
||||
f.IntVar(
|
||||
&p.timeoutSec,
|
||||
"timeout",
|
||||
5*60,
|
||||
"Number of seconds for processing other than scan",
|
||||
)
|
||||
|
||||
f.IntVar(
|
||||
&p.scanTimeoutSec,
|
||||
"timeout-scan",
|
||||
120*60,
|
||||
"Number of seconds for scanning vulnerabilities for all servers",
|
||||
)
|
||||
}
|
||||
|
||||
// Execute execute
|
||||
func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
|
||||
// Setup Logger
|
||||
c.Conf.Debug = p.debug
|
||||
c.Conf.LogDir = p.logDir
|
||||
util.Log = util.NewCustomLogger(c.ServerInfo{})
|
||||
|
||||
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 {
|
||||
util.Log.Errorf("Error loading %s, %s", p.configPath, err)
|
||||
util.Log.Errorf("If you update Vuls and get this error, there may be incompatible changes in config.toml")
|
||||
util.Log.Errorf("Please check README: https://github.com/future-architect/vuls#configuration")
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
util.Log.Info("Start scanning")
|
||||
util.Log.Infof("config: %s", p.configPath)
|
||||
|
||||
c.Conf.Pipe = p.pipe
|
||||
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: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
fields := strings.Fields(string(bytes))
|
||||
if 0 < len(fields) {
|
||||
servernames = fields
|
||||
}
|
||||
}
|
||||
|
||||
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.Debugf("%s", pp.Sprintf("%v", target))
|
||||
|
||||
c.Conf.ResultsDir = p.resultsDir
|
||||
c.Conf.CacheDBPath = p.cacheDBPath
|
||||
c.Conf.SSHNative = p.sshNative
|
||||
c.Conf.HTTPProxy = p.httpProxy
|
||||
c.Conf.ContainersOnly = p.containersOnly
|
||||
c.Conf.Deep = p.deep
|
||||
c.Conf.SkipBroken = p.skipBroken
|
||||
|
||||
util.Log.Info("Validating config...")
|
||||
if !c.Conf.ValidateOnScan() {
|
||||
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: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
util.Log.Info("Detecting Platforms... ")
|
||||
scan.DetectPlatforms(p.timeoutSec)
|
||||
|
||||
util.Log.Info("Scanning vulnerabilities... ")
|
||||
if err := scan.Scan(p.scanTimeoutSec); err != nil {
|
||||
util.Log.Errorf("Failed to scan. err: %s", 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
|
||||
}
|
||||
197
commands/tui.go
@@ -1,197 +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 (
|
||||
"context"
|
||||
"flag"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/report"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/google/subcommands"
|
||||
)
|
||||
|
||||
// TuiCmd is Subcommand of host discovery mode
|
||||
type TuiCmd struct {
|
||||
lang string
|
||||
debugSQL bool
|
||||
debug bool
|
||||
configPath string
|
||||
logDir string
|
||||
|
||||
resultsDir string
|
||||
refreshCve bool
|
||||
|
||||
cvedbtype string
|
||||
cvedbpath string
|
||||
cveDictionaryURL string
|
||||
|
||||
ovalDBType string
|
||||
ovalDBPath string
|
||||
ovalDBURL string
|
||||
|
||||
pipe bool
|
||||
}
|
||||
|
||||
// Name return subcommand name
|
||||
func (*TuiCmd) Name() string { return "tui" }
|
||||
|
||||
// Synopsis return synopsis
|
||||
func (*TuiCmd) Synopsis() string { return "Run Tui view to analyze vulnerabilities" }
|
||||
|
||||
// Usage return usage
|
||||
func (*TuiCmd) Usage() string {
|
||||
return `tui:
|
||||
tui
|
||||
[-config=/path/to/config.toml]
|
||||
[-cvedb-type=sqlite3|mysql|postgres]
|
||||
[-cvedb-path=/path/to/cve.sqlite3]
|
||||
[-cvedb-url=http://127.0.0.1:1323 or DB connection string]
|
||||
[-ovaldb-type=sqlite3|mysql]
|
||||
[-ovaldb-path=/path/to/oval.sqlite3]
|
||||
[-ovaldb-url=http://127.0.0.1:1324 or DB connection string]
|
||||
[-refresh-cve]
|
||||
[-results-dir=/path/to/results]
|
||||
[-log-dir=/path/to/log]
|
||||
[-debug]
|
||||
[-debug-sql]
|
||||
[-pipe]
|
||||
|
||||
`
|
||||
}
|
||||
|
||||
// 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(&p.debug, "debug", false, "debug mode")
|
||||
|
||||
defaultLogDir := util.GetDefaultLogDir()
|
||||
f.StringVar(&p.logDir, "log-dir", defaultLogDir, "/path/to/log")
|
||||
|
||||
wd, _ := os.Getwd()
|
||||
defaultResultsDir := filepath.Join(wd, "results")
|
||||
f.StringVar(&p.resultsDir, "results-dir", defaultResultsDir, "/path/to/results")
|
||||
|
||||
defaultConfPath := filepath.Join(wd, "config.toml")
|
||||
f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
|
||||
|
||||
f.BoolVar(
|
||||
&p.refreshCve,
|
||||
"refresh-cve",
|
||||
false,
|
||||
"Refresh CVE information in JSON file under results dir")
|
||||
|
||||
f.StringVar(
|
||||
&p.cvedbtype,
|
||||
"cvedb-type",
|
||||
"sqlite3",
|
||||
"DB type for fetching CVE dictionary (sqlite3, mysql or postgres)")
|
||||
|
||||
defaultCveDBPath := filepath.Join(wd, "cve.sqlite3")
|
||||
f.StringVar(
|
||||
&p.cvedbpath,
|
||||
"cvedb-path",
|
||||
defaultCveDBPath,
|
||||
"/path/to/sqlite3 (For get cve detail from cve.sqlite3)")
|
||||
|
||||
f.StringVar(
|
||||
&p.cveDictionaryURL,
|
||||
"cvedb-url",
|
||||
"",
|
||||
"http://cve-dictionary.example.com:1323 or mysql connection string")
|
||||
|
||||
f.StringVar(
|
||||
&p.ovalDBType,
|
||||
"ovaldb-type",
|
||||
"sqlite3",
|
||||
"DB type for fetching OVAL dictionary (sqlite3 or mysql)")
|
||||
|
||||
defaultOvalDBPath := filepath.Join(wd, "oval.sqlite3")
|
||||
f.StringVar(
|
||||
&p.ovalDBPath,
|
||||
"ovaldb-path",
|
||||
defaultOvalDBPath,
|
||||
"/path/to/sqlite3 (For get oval detail from oval.sqlite3)")
|
||||
|
||||
f.StringVar(
|
||||
&p.ovalDBURL,
|
||||
"ovaldb-url",
|
||||
"",
|
||||
"http://goval-dictionary.example.com:1324 or mysql connection string")
|
||||
|
||||
f.BoolVar(
|
||||
&p.pipe,
|
||||
"pipe",
|
||||
false,
|
||||
"Use stdin via PIPE")
|
||||
}
|
||||
|
||||
// Execute execute
|
||||
func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
c.Conf.Lang = "en"
|
||||
|
||||
// Setup Logger
|
||||
c.Conf.Debug = p.debug
|
||||
c.Conf.DebugSQL = p.debugSQL
|
||||
c.Conf.LogDir = p.logDir
|
||||
util.Log = util.NewCustomLogger(c.ServerInfo{})
|
||||
log := util.Log
|
||||
|
||||
if err := c.Load(p.configPath, ""); err != nil {
|
||||
util.Log.Errorf("Error loading %s, %s", p.configPath, err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
c.Conf.ResultsDir = p.resultsDir
|
||||
c.Conf.CveDBType = p.cvedbtype
|
||||
c.Conf.CveDBPath = p.cvedbpath
|
||||
c.Conf.CveDBURL = p.cveDictionaryURL
|
||||
c.Conf.OvalDBType = p.ovalDBType
|
||||
c.Conf.OvalDBPath = p.ovalDBPath
|
||||
c.Conf.OvalDBURL = p.ovalDBURL
|
||||
|
||||
log.Info("Validating config...")
|
||||
if !c.Conf.ValidateOnTui() {
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
c.Conf.Pipe = p.pipe
|
||||
|
||||
dir, err := report.JSONDir(f.Args())
|
||||
if err != nil {
|
||||
util.Log.Errorf("Failed to read from JSON: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
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)
|
||||
|
||||
if res, err = report.FillCveInfos(res, dir); err != nil {
|
||||
util.Log.Error(err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
return report.RunTui(res)
|
||||
}
|
||||
@@ -1,38 +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 (
|
||||
"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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,18 +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
|
||||
@@ -1,32 +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 config
|
||||
|
||||
var (
|
||||
// Colors has ansi color list
|
||||
Colors = []string{
|
||||
"\033[32m", // green
|
||||
"\033[33m", // yellow
|
||||
"\033[36m", // cyan
|
||||
"\033[35m", // magenta
|
||||
"\033[31m", // red
|
||||
"\033[34m", // blue
|
||||
}
|
||||
// ResetColor is reset color
|
||||
ResetColor = "\033[0m"
|
||||
)
|
||||
486
config/config.go
@@ -1,486 +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 config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
valid "github.com/asaskevich/govalidator"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Conf has Configuration
|
||||
var Conf Config
|
||||
|
||||
const (
|
||||
// RedHat is
|
||||
RedHat = "redhat"
|
||||
|
||||
// Debian is
|
||||
Debian = "debian"
|
||||
|
||||
// Ubuntu is
|
||||
Ubuntu = "ubuntu"
|
||||
|
||||
// CentOS is
|
||||
CentOS = "centos"
|
||||
|
||||
// Fedora is
|
||||
Fedora = "fedora"
|
||||
|
||||
// Amazon is
|
||||
Amazon = "amazon"
|
||||
|
||||
// Oracle is
|
||||
Oracle = "oracle"
|
||||
|
||||
// FreeBSD is
|
||||
FreeBSD = "freebsd"
|
||||
|
||||
// Raspbian is
|
||||
Raspbian = "raspbian"
|
||||
)
|
||||
|
||||
//Config is struct of Configuration
|
||||
type Config struct {
|
||||
Debug bool
|
||||
DebugSQL bool
|
||||
Lang string
|
||||
|
||||
EMail SMTPConf
|
||||
Slack SlackConf
|
||||
Default ServerInfo
|
||||
Servers map[string]ServerInfo
|
||||
|
||||
CvssScoreOver float64
|
||||
IgnoreUnscoredCves bool
|
||||
|
||||
SSHNative bool
|
||||
ContainersOnly bool
|
||||
Deep bool
|
||||
SkipBroken bool
|
||||
|
||||
HTTPProxy string `valid:"url"`
|
||||
LogDir string
|
||||
ResultsDir string
|
||||
|
||||
CveDBType string
|
||||
CveDBPath string
|
||||
CveDBURL string
|
||||
|
||||
OvalDBType string
|
||||
OvalDBPath string
|
||||
OvalDBURL string
|
||||
|
||||
CacheDBPath string
|
||||
|
||||
RefreshCve bool
|
||||
|
||||
FormatXML bool
|
||||
FormatJSON bool
|
||||
FormatOneEMail bool
|
||||
FormatOneLineText bool
|
||||
FormatShortText bool
|
||||
FormatFullText bool
|
||||
|
||||
GZIP bool
|
||||
|
||||
AwsProfile string
|
||||
AwsRegion string
|
||||
S3Bucket string
|
||||
S3ResultsDir string
|
||||
|
||||
AzureAccount string
|
||||
AzureKey string `json:"-"`
|
||||
AzureContainer string
|
||||
|
||||
Pipe bool
|
||||
Diff bool
|
||||
}
|
||||
|
||||
// ValidateOnConfigtest validates
|
||||
func (c Config) ValidateOnConfigtest() bool {
|
||||
errs := []error{}
|
||||
|
||||
if runtime.GOOS == "windows" && !c.SSHNative {
|
||||
errs = append(errs, fmt.Errorf("-ssh-native-insecure is needed on windows"))
|
||||
}
|
||||
|
||||
_, err := valid.ValidateStruct(c)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
for _, err := range errs {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
return len(errs) == 0
|
||||
}
|
||||
|
||||
// ValidateOnPrepare validates configuration
|
||||
func (c Config) ValidateOnPrepare() bool {
|
||||
return c.ValidateOnConfigtest()
|
||||
}
|
||||
|
||||
// ValidateOnScan validates configuration
|
||||
func (c Config) ValidateOnScan() bool {
|
||||
errs := []error{}
|
||||
|
||||
if len(c.ResultsDir) != 0 {
|
||||
if ok, _ := valid.IsFilePath(c.ResultsDir); !ok {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir))
|
||||
}
|
||||
}
|
||||
|
||||
if runtime.GOOS == "windows" && !c.SSHNative {
|
||||
errs = append(errs, fmt.Errorf("-ssh-native-insecure is needed on windows"))
|
||||
}
|
||||
|
||||
if len(c.ResultsDir) != 0 {
|
||||
if ok, _ := valid.IsFilePath(c.ResultsDir); !ok {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir))
|
||||
}
|
||||
}
|
||||
|
||||
if len(c.CacheDBPath) != 0 {
|
||||
if ok, _ := valid.IsFilePath(c.CacheDBPath); !ok {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"Cache DB path must be a *Absolute* file path. -cache-dbpath: %s", c.CacheDBPath))
|
||||
}
|
||||
}
|
||||
|
||||
_, err := valid.ValidateStruct(c)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
for _, err := range errs {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
return len(errs) == 0
|
||||
}
|
||||
|
||||
// ValidateOnReport validates configuration
|
||||
func (c Config) ValidateOnReport() bool {
|
||||
errs := []error{}
|
||||
|
||||
if len(c.ResultsDir) != 0 {
|
||||
if ok, _ := valid.IsFilePath(c.ResultsDir); !ok {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir))
|
||||
}
|
||||
}
|
||||
|
||||
if err := validateDB("cvedb", c.CveDBType, c.CveDBPath, c.CveDBURL); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if c.CveDBType == "sqlite3" {
|
||||
if _, err := os.Stat(c.CveDBPath); os.IsNotExist(err) {
|
||||
errs = append(errs, fmt.Errorf("SQLite3 DB path (%s) is not exist: %s", "cvedb", c.CveDBPath))
|
||||
}
|
||||
}
|
||||
|
||||
if err := validateDB("ovaldb", c.OvalDBType, c.OvalDBPath, c.OvalDBURL); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
_, err := valid.ValidateStruct(c)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
if mailerrs := c.EMail.Validate(); 0 < len(mailerrs) {
|
||||
errs = append(errs, mailerrs...)
|
||||
}
|
||||
|
||||
if slackerrs := c.Slack.Validate(); 0 < len(slackerrs) {
|
||||
errs = append(errs, slackerrs...)
|
||||
}
|
||||
|
||||
for _, err := range errs {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
return len(errs) == 0
|
||||
}
|
||||
|
||||
// ValidateOnTui validates configuration
|
||||
func (c Config) ValidateOnTui() bool {
|
||||
errs := []error{}
|
||||
|
||||
if len(c.ResultsDir) != 0 {
|
||||
if ok, _ := valid.IsFilePath(c.ResultsDir); !ok {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir))
|
||||
}
|
||||
}
|
||||
|
||||
if err := validateDB("cvedb", c.CveDBType, c.CveDBPath, c.CveDBURL); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if c.CveDBType == "sqlite3" {
|
||||
if _, err := os.Stat(c.CveDBPath); os.IsNotExist(err) {
|
||||
errs = append(errs, fmt.Errorf("SQLite3 DB path (%s) is not exist: %s", "cvedb", c.CveDBPath))
|
||||
}
|
||||
}
|
||||
|
||||
for _, err := range errs {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
return len(errs) == 0
|
||||
}
|
||||
|
||||
// validateDB validates configuration
|
||||
// dictionaryDB name is 'cvedb' or 'ovaldb'
|
||||
func validateDB(dictionaryDBName, dbType, dbPath, dbURL string) error {
|
||||
switch dbType {
|
||||
case "sqlite3":
|
||||
if ok, _ := valid.IsFilePath(dbPath); !ok {
|
||||
return fmt.Errorf(
|
||||
"SQLite3 DB path (%s) must be a *Absolute* file path. -%s-path: %s",
|
||||
dictionaryDBName,
|
||||
dictionaryDBName,
|
||||
dbPath)
|
||||
}
|
||||
case "mysql":
|
||||
if dbURL == "" {
|
||||
return fmt.Errorf(
|
||||
`MySQL connection string is needed. -%s-url="user:pass@tcp(localhost:3306)/dbname"`,
|
||||
dictionaryDBName)
|
||||
}
|
||||
case "postgres":
|
||||
if dbURL == "" {
|
||||
return fmt.Errorf(
|
||||
`PostgreSQL connection string is needed. -%s-url="host=myhost user=user dbname=dbname sslmode=disable password=password"`,
|
||||
dictionaryDBName)
|
||||
}
|
||||
case "redis":
|
||||
if dbURL == "" {
|
||||
return fmt.Errorf(
|
||||
`Redis connection string is needed. -%s-url="redis://localhost/0"`,
|
||||
dictionaryDBName)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"%s type must be either 'sqlite3', 'mysql', 'postgres' or 'redis'. -%s-type: %s",
|
||||
dictionaryDBName,
|
||||
dictionaryDBName,
|
||||
dbType)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SMTPConf is smtp config
|
||||
type SMTPConf struct {
|
||||
SMTPAddr string
|
||||
SMTPPort string `valid:"port"`
|
||||
|
||||
User string
|
||||
Password string `json:"-"`
|
||||
From string
|
||||
To []string
|
||||
Cc []string
|
||||
SubjectPrefix string
|
||||
|
||||
UseThisTime bool
|
||||
}
|
||||
|
||||
func checkEmails(emails []string) (errs []error) {
|
||||
for _, addr := range emails {
|
||||
if len(addr) == 0 {
|
||||
return
|
||||
}
|
||||
if ok := valid.IsEmail(addr); !ok {
|
||||
errs = append(errs, fmt.Errorf("Invalid email address. email: %s", addr))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Validate SMTP configuration
|
||||
func (c *SMTPConf) Validate() (errs []error) {
|
||||
|
||||
if !c.UseThisTime {
|
||||
return
|
||||
}
|
||||
|
||||
// Check Emails fromat
|
||||
emails := []string{}
|
||||
emails = append(emails, c.From)
|
||||
emails = append(emails, c.To...)
|
||||
emails = append(emails, c.Cc...)
|
||||
|
||||
if emailErrs := checkEmails(emails); 0 < len(emailErrs) {
|
||||
errs = append(errs, emailErrs...)
|
||||
}
|
||||
|
||||
if len(c.SMTPAddr) == 0 {
|
||||
errs = append(errs, fmt.Errorf("smtpAddr must not be empty"))
|
||||
}
|
||||
if len(c.SMTPPort) == 0 {
|
||||
errs = append(errs, fmt.Errorf("smtpPort must not be empty"))
|
||||
}
|
||||
if len(c.To) == 0 {
|
||||
errs = append(errs, fmt.Errorf("To required at least one address"))
|
||||
}
|
||||
if len(c.From) == 0 {
|
||||
errs = append(errs, fmt.Errorf("From required at least one address"))
|
||||
}
|
||||
|
||||
_, err := valid.ValidateStruct(c)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SlackConf is slack config
|
||||
type SlackConf struct {
|
||||
HookURL string `valid:"url" json:"-"`
|
||||
Channel string `json:"channel"`
|
||||
IconEmoji string `json:"icon_emoji"`
|
||||
AuthUser string `json:"username"`
|
||||
|
||||
NotifyUsers []string
|
||||
Text string `json:"text"`
|
||||
|
||||
UseThisTime bool
|
||||
}
|
||||
|
||||
// Validate validates configuration
|
||||
func (c *SlackConf) Validate() (errs []error) {
|
||||
if !c.UseThisTime {
|
||||
return
|
||||
}
|
||||
|
||||
if len(c.HookURL) == 0 {
|
||||
errs = append(errs, fmt.Errorf("hookURL must not be empty"))
|
||||
}
|
||||
|
||||
if len(c.Channel) == 0 {
|
||||
errs = append(errs, fmt.Errorf("channel must not be empty"))
|
||||
} else {
|
||||
if !(strings.HasPrefix(c.Channel, "#") ||
|
||||
c.Channel == "${servername}") {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"channel's prefix must be '#', channel: %s", c.Channel))
|
||||
}
|
||||
}
|
||||
|
||||
if len(c.AuthUser) == 0 {
|
||||
errs = append(errs, fmt.Errorf("authUser must not be empty"))
|
||||
}
|
||||
|
||||
_, err := valid.ValidateStruct(c)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ServerInfo has SSH Info, additional CPE packages to scan.
|
||||
type ServerInfo struct {
|
||||
ServerName string
|
||||
User string
|
||||
Host string
|
||||
Port string
|
||||
KeyPath string
|
||||
KeyPassword string `json:"-"`
|
||||
|
||||
CpeNames []string
|
||||
DependencyCheckXMLPath string
|
||||
|
||||
// Container Names or IDs
|
||||
Containers Containers
|
||||
|
||||
IgnoreCves []string
|
||||
|
||||
// Optional key-value set that will be outputted to JSON
|
||||
Optional [][]interface{}
|
||||
|
||||
// For CentOS, RHEL, Amazon
|
||||
Enablerepo []string
|
||||
|
||||
// used internal
|
||||
LogMsgAnsiColor string // DebugLog Color
|
||||
Container Container
|
||||
Distro Distro
|
||||
}
|
||||
|
||||
// GetServerName returns ServerName if this serverInfo is about host.
|
||||
// If this serverInfo is abount a container, returns containerID@ServerName
|
||||
func (s ServerInfo) GetServerName() string {
|
||||
if len(s.Container.ContainerID) == 0 {
|
||||
return s.ServerName
|
||||
}
|
||||
return fmt.Sprintf("%s@%s", s.Container.ContainerID, s.ServerName)
|
||||
}
|
||||
|
||||
// Distro has distribution info
|
||||
type Distro struct {
|
||||
Family string
|
||||
Release string
|
||||
}
|
||||
|
||||
func (l Distro) String() string {
|
||||
return fmt.Sprintf("%s %s", l.Family, l.Release)
|
||||
}
|
||||
|
||||
// MajorVersion returns Major version
|
||||
func (l Distro) MajorVersion() (ver int, err error) {
|
||||
if 0 < len(l.Release) {
|
||||
ver, err = strconv.Atoi(strings.Split(l.Release, ".")[0])
|
||||
} else {
|
||||
err = fmt.Errorf("Release is empty")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// IsContainer returns whether this ServerInfo is about container
|
||||
func (s ServerInfo) IsContainer() bool {
|
||||
return 0 < len(s.Container.ContainerID)
|
||||
}
|
||||
|
||||
// SetContainer set container
|
||||
func (s *ServerInfo) SetContainer(d Container) {
|
||||
s.Container = d
|
||||
}
|
||||
|
||||
// Containers has Containers information.
|
||||
type Containers struct {
|
||||
Type string
|
||||
Includes []string
|
||||
Excludes []string
|
||||
}
|
||||
|
||||
// Container has Container information.
|
||||
type Container struct {
|
||||
ContainerID string
|
||||
Name string
|
||||
Image string
|
||||
}
|
||||
@@ -1,29 +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 config
|
||||
|
||||
import "fmt"
|
||||
|
||||
// JSONLoader loads configuration
|
||||
type JSONLoader struct {
|
||||
}
|
||||
|
||||
// Load load the configuraiton JSON file specified by path arg.
|
||||
func (c JSONLoader) Load(path, sudoPass, keyPass string) (err error) {
|
||||
return fmt.Errorf("Not implement yet")
|
||||
}
|
||||
@@ -1,30 +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 config
|
||||
|
||||
// Load loads configuration
|
||||
func Load(path, keyPass string) error {
|
||||
var loader Loader
|
||||
loader = TOMLLoader{}
|
||||
return loader.Load(path, keyPass)
|
||||
}
|
||||
|
||||
// Loader is interface of concrete loader
|
||||
type Loader interface {
|
||||
Load(string, string) error
|
||||
}
|
||||
@@ -1,185 +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 config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/future-architect/vuls/contrib/owasp-dependency-check/parser"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// TOMLLoader loads config
|
||||
type TOMLLoader struct {
|
||||
}
|
||||
|
||||
// Load load the configuraiton TOML file specified by path arg.
|
||||
func (c TOMLLoader) Load(pathToToml, keyPass string) error {
|
||||
if Conf.Debug {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
}
|
||||
|
||||
var conf Config
|
||||
if _, err := toml.DecodeFile(pathToToml, &conf); err != nil {
|
||||
log.Error("Load config failed", err)
|
||||
return err
|
||||
}
|
||||
|
||||
Conf.EMail = conf.EMail
|
||||
Conf.Slack = conf.Slack
|
||||
|
||||
d := conf.Default
|
||||
Conf.Default = d
|
||||
servers := make(map[string]ServerInfo)
|
||||
|
||||
if keyPass != "" {
|
||||
d.KeyPassword = keyPass
|
||||
}
|
||||
|
||||
i := 0
|
||||
for name, v := range conf.Servers {
|
||||
if 0 < len(v.KeyPassword) {
|
||||
log.Warn("[Deprecated] KEYPASSWORD IN CONFIG FILE ARE UNSECURE. REMOVE THEM IMMEDIATELY FOR A SECURITY REASONS. THEY WILL BE REMOVED IN A FUTURE RELEASE.")
|
||||
}
|
||||
|
||||
s := ServerInfo{ServerName: name}
|
||||
|
||||
s.Host = v.Host
|
||||
if len(s.Host) == 0 {
|
||||
return fmt.Errorf("%s is invalid. host is empty", name)
|
||||
}
|
||||
|
||||
switch {
|
||||
case v.Port != "":
|
||||
s.Port = v.Port
|
||||
case d.Port != "":
|
||||
s.Port = d.Port
|
||||
default:
|
||||
s.Port = "22"
|
||||
}
|
||||
|
||||
switch {
|
||||
case v.User != "":
|
||||
s.User = v.User
|
||||
case d.User != "":
|
||||
s.User = d.User
|
||||
default:
|
||||
if s.Port != "local" {
|
||||
return fmt.Errorf("%s is invalid. User is empty", name)
|
||||
}
|
||||
}
|
||||
|
||||
s.KeyPath = v.KeyPath
|
||||
if len(s.KeyPath) == 0 {
|
||||
s.KeyPath = d.KeyPath
|
||||
}
|
||||
if s.KeyPath != "" {
|
||||
if _, err := os.Stat(s.KeyPath); err != nil {
|
||||
return fmt.Errorf(
|
||||
"%s is invalid. keypath: %s not exists", name, s.KeyPath)
|
||||
}
|
||||
}
|
||||
|
||||
// s.KeyPassword = keyPass
|
||||
s.KeyPassword = v.KeyPassword
|
||||
if len(s.KeyPassword) == 0 {
|
||||
s.KeyPassword = d.KeyPassword
|
||||
}
|
||||
|
||||
s.CpeNames = v.CpeNames
|
||||
if len(s.CpeNames) == 0 {
|
||||
s.CpeNames = d.CpeNames
|
||||
}
|
||||
|
||||
s.DependencyCheckXMLPath = v.DependencyCheckXMLPath
|
||||
if len(s.DependencyCheckXMLPath) == 0 {
|
||||
s.DependencyCheckXMLPath = d.DependencyCheckXMLPath
|
||||
}
|
||||
|
||||
// Load CPEs from OWASP Dependency Check XML
|
||||
if len(s.DependencyCheckXMLPath) != 0 {
|
||||
cpes, err := parser.Parse(s.DependencyCheckXMLPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"Failed to read OWASP Dependency Check XML: %s", err)
|
||||
}
|
||||
log.Debugf("Loaded from OWASP Dependency Check XML: %s",
|
||||
s.ServerName)
|
||||
s.CpeNames = append(s.CpeNames, cpes...)
|
||||
}
|
||||
|
||||
s.Containers = v.Containers
|
||||
if len(s.Containers.Includes) == 0 {
|
||||
s.Containers = d.Containers
|
||||
}
|
||||
|
||||
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.Optional = v.Optional
|
||||
for _, dkv := range d.Optional {
|
||||
found := false
|
||||
for _, kv := range s.Optional {
|
||||
if dkv[0] == kv[0] {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
s.Optional = append(s.Optional, dkv)
|
||||
}
|
||||
}
|
||||
|
||||
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 fmt.Errorf(
|
||||
"For now, enablerepo have to be base or updates: %s, servername: %s",
|
||||
s.Enablerepo, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s.LogMsgAnsiColor = Colors[i%len(Colors)]
|
||||
i++
|
||||
|
||||
servers[name] = s
|
||||
}
|
||||
Conf.Servers = servers
|
||||
return nil
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type analysis struct {
|
||||
Dependencies []dependency `xml:"dependencies>dependency"`
|
||||
}
|
||||
|
||||
type dependency struct {
|
||||
Identifiers []identifier `xml:"identifiers>identifier"`
|
||||
}
|
||||
|
||||
type identifier struct {
|
||||
Name string `xml:"name"`
|
||||
Type string `xml:"type,attr"`
|
||||
}
|
||||
|
||||
func appendIfMissing(slice []string, str string) []string {
|
||||
for _, s := range slice {
|
||||
if s == str {
|
||||
return slice
|
||||
}
|
||||
}
|
||||
return append(slice, str)
|
||||
}
|
||||
|
||||
// Parse parses XML and collect list of cpe
|
||||
func Parse(path string) ([]string, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to open: %s", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
b, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to read: %s", err)
|
||||
}
|
||||
|
||||
var anal analysis
|
||||
if err := xml.Unmarshal(b, &anal); err != nil {
|
||||
return nil, fmt.Errorf("Failed to unmarshal: %s", err)
|
||||
}
|
||||
|
||||
cpes := []string{}
|
||||
for _, d := range anal.Dependencies {
|
||||
for _, ident := range d.Identifiers {
|
||||
if ident.Type == "cpe" {
|
||||
name := strings.TrimPrefix(ident.Name, "(")
|
||||
name = strings.TrimSuffix(name, ")")
|
||||
cpes = appendIfMissing(cpes, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
return cpes, nil
|
||||
}
|
||||
75
go.mod
Normal file
@@ -0,0 +1,75 @@
|
||||
module github.com/future-architect/vuls
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/MakeNowJust/heredoc v1.0.0
|
||||
github.com/go-redis/redis/v9 v9.0.0-rc.1
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/hashicorp/go-version v1.6.0
|
||||
github.com/knqyf263/go-cpe v0.0.0-20201213041631-54f6ab28673f
|
||||
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d
|
||||
github.com/labstack/echo/v4 v4.9.1
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/opencontainers/image-spec v1.1.0-rc2
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/spf13/cobra v1.6.1
|
||||
github.com/ulikunitz/xz v0.5.10
|
||||
go.etcd.io/bbolt v1.3.6
|
||||
go.uber.org/zap v1.13.0
|
||||
golang.org/x/exp v0.0.0-20221106115401-f9659909a136
|
||||
gorm.io/driver/mysql v1.4.3
|
||||
gorm.io/driver/postgres v1.4.5
|
||||
gorm.io/gorm v1.24.1
|
||||
modernc.org/sqlite v1.19.4
|
||||
oras.land/oras-go/v2 v2.0.0-rc.4
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||
github.com/gofrs/uuid v4.2.0+incompatible // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||
github.com/jackc/pgconn v1.13.0 // indirect
|
||||
github.com/jackc/pgio v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgproto3/v2 v2.3.1 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
||||
github.com/jackc/pgtype v1.12.0 // indirect
|
||||
github.com/jackc/pgx/v4 v4.17.2 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/labstack/gommon v0.4.0 // indirect
|
||||
github.com/lib/pq v1.10.6 // indirect
|
||||
github.com/mattn/go-colorable v0.1.11 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.1 // indirect
|
||||
go.uber.org/atomic v1.6.0 // indirect
|
||||
go.uber.org/multierr v1.5.0 // indirect
|
||||
golang.org/x/crypto v0.1.0 // indirect
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de // indirect
|
||||
golang.org/x/mod v0.6.0 // indirect
|
||||
golang.org/x/net v0.1.0 // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
golang.org/x/sys v0.1.0 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
golang.org/x/tools v0.2.0 // indirect
|
||||
lukechampine.com/uint128 v1.2.0 // indirect
|
||||
modernc.org/cc/v3 v3.40.0 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.13 // indirect
|
||||
modernc.org/libc v1.21.4 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.4.0 // indirect
|
||||
modernc.org/opt v0.1.3 // indirect
|
||||
modernc.org/strutil v1.1.3 // indirect
|
||||
modernc.org/token v1.0.1 // indirect
|
||||
)
|
||||
318
go.sum
Normal file
@@ -0,0 +1,318 @@
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
|
||||
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
|
||||
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
|
||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-redis/redis/v9 v9.0.0-rc.1 h1:/+bS+yeUnanqAbuD3QwlejzQZ+4eqgfUtFTG4b+QnXs=
|
||||
github.com/go-redis/redis/v9 v9.0.0-rc.1/go.mod h1:8et+z03j0l8N+DvsVnclzjf3Dl/pFHgRk+2Ct1qw66A=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
|
||||
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
||||
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
|
||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
|
||||
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
|
||||
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
|
||||
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
||||
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
|
||||
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
||||
github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys=
|
||||
github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI=
|
||||
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
||||
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
||||
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
|
||||
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=
|
||||
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y=
|
||||
github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
||||
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
||||
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
|
||||
github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w=
|
||||
github.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
||||
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
|
||||
github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E=
|
||||
github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw=
|
||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/knqyf263/go-cpe v0.0.0-20201213041631-54f6ab28673f h1:vZP1dTKPOR7zSAbgqNbnTnYX77+gj3eu0QK+UmANZqE=
|
||||
github.com/knqyf263/go-cpe v0.0.0-20201213041631-54f6ab28673f/go.mod h1:4cVhzV/TndScEg4xMtSo3TTz3cMFhEAvhAA4igAyXZY=
|
||||
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d h1:X4cedH4Kn3JPupAwwWuo4AzYp16P0OyLO9d7OnMZc/c=
|
||||
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d/go.mod h1:o8sgWoz3JADecfc/cTYD92/Et1yMqMy0utV1z+VaZao=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/labstack/echo/v4 v4.9.1 h1:GliPYSpzGKlyOhqIbG8nmHBo3i1saKWFOgh41AN3b+Y=
|
||||
github.com/labstack/echo/v4 v4.9.1/go.mod h1:Pop5HLc+xoc4qhTZ1ip6C0RtP7Z+4VzRLWZZFKqbbjo=
|
||||
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
|
||||
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs=
|
||||
github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
|
||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/gomega v1.21.1 h1:OB/euWYIExnPBohllTicTHmGTrMaqJ67nIu80j0/uEM=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034=
|
||||
github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
|
||||
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
|
||||
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
|
||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
|
||||
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/exp v0.0.0-20221106115401-f9659909a136 h1:Fq7F/w7MAa1KJ5bt2aJ62ihqp9HDcRuyILskkpIAurw=
|
||||
golang.org/x/exp v0.0.0-20221106115401-f9659909a136/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
|
||||
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
|
||||
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.4.3 h1:/JhWJhO2v17d8hjApTltKNADm7K7YI2ogkR7avJUL3k=
|
||||
gorm.io/driver/mysql v1.4.3/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c=
|
||||
gorm.io/driver/postgres v1.4.5 h1:mTeXTTtHAgnS9PgmhN2YeUbazYpLhUI1doLnw42XUZc=
|
||||
gorm.io/driver/postgres v1.4.5/go.mod h1:GKNQYSJ14qvWkvPwXljMGehpKrhlDNsqYRr5HnYGncg=
|
||||
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
gorm.io/gorm v1.24.1-0.20221019064659-5dd2bb482755/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
gorm.io/gorm v1.24.1 h1:CgvzRniUdG67hBAzsxDGOAuq4Te1osVMYsa1eQbd4fs=
|
||||
gorm.io/gorm v1.24.1/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
|
||||
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
|
||||
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
|
||||
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
|
||||
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
|
||||
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
|
||||
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
|
||||
modernc.org/libc v1.21.4 h1:CzTlumWeIbPV5/HVIMzYHNPCRP8uiU/CWiN2gtd/Qu8=
|
||||
modernc.org/libc v1.21.4/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI=
|
||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk=
|
||||
modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sqlite v1.19.4 h1:nlPIDqumn6/mSvs7T5C8MNYEuN73sISzPdKtMdURpUI=
|
||||
modernc.org/sqlite v1.19.4/go.mod h1:x/yZNb3h5+I3zGQSlwIv4REL5eJhiRkUH5MReogAeIc=
|
||||
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
|
||||
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
|
||||
modernc.org/tcl v1.15.0 h1:oY+JeD11qVVSgVvodMJsu7Edf8tr5E/7tuhF5cNYz34=
|
||||
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
|
||||
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE=
|
||||
oras.land/oras-go/v2 v2.0.0-rc.4 h1:hg/R2znUQ1+qd43gRmL16VeX1GIZ8hQlLalBjYhhKSk=
|
||||
oras.land/oras-go/v2 v2.0.0-rc.4/go.mod h1:YGHvWBGuqRlZgUyXUIoKsR3lcuCOb3DAtG0SEsEw1iY=
|
||||
|
Before Width: | Height: | Size: 247 KiB |
|
Before Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 36 KiB |
@@ -1,415 +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.17-->
|
||||
<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.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="309.6849206349206" y="0.0"/>
|
||||
<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" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="83.482421875" x="92.2587890625" y="18.93359375">Detect the OS<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="0.1619001116071429" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n1">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.decision">
|
||||
<y:Geometry height="40.0" width="80.0" x="403.6849206349206" y="206.44247787610618"/>
|
||||
<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" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="38.0" y="18.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="n2">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="90.44247787610618" width="268.0" x="309.6849206349206" y="86.0"/>
|
||||
<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" height="60.53125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="170.763671875" x="48.61816406250006" y="14.95561393805309">Get installed packages
|
||||
Debian/Ubuntu: dpkg-query
|
||||
Amazon/RHEL/CentOS: rpm
|
||||
FreeBSD: pkg<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="2.220446049250313E-16" 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="n3">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="630.0546766682629"/>
|
||||
<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" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="152.634765625" x="57.6826171875" y="18.93359375">Write results to JSON files<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="0.1619001116071429" 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:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="287.8409153761062"/>
|
||||
<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" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="232.744140625" x="17.6279296875" y="4.80078125">Get CVE IDs by using package manager
|
||||
Amazon: yum plugin security
|
||||
FreeBSD: pkg audit<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="n5">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="750.4705298628534"/>
|
||||
<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" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="42.595703125" x="112.7021484375" y="18.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:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n6" yfiles.foldertype="group">
|
||||
<data key="d4"/>
|
||||
<data key="d6">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="116.89483989807195" width="333.6788874841973" x="234.29467728596296" y="709.1901021013174"/>
|
||||
<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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="333.6788874841973" x="0.0" y="0.0">Vulnerability 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="0" bottomF="0.0" left="0" leftF="0.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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" 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="n6:">
|
||||
<node id="n6::n0">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
|
||||
<y:Geometry height="65.22882427307195" width="136.83944374209864" x="416.1341210280616" y="745.8561177263174"/>
|
||||
<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" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="117.970703125" x="9.434370308549205" y="23.548005886535975">CVE DB (NVD / JVN)<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-8.326672684688674E-16" 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="n6::n1">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
|
||||
<y:Geometry height="65.22882427307195" width="136.83944374209864" x="249.29467728596296" y="745.8561177263174"/>
|
||||
<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" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="55.533203125" x="40.653120308549205" y="23.548005886535975">OVAL DB<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-8.326672684688674E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<node id="n7">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="27.144753476611868" y="287.8409153761062"/>
|
||||
<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" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="260.83984375" x="3.580078125" y="11.8671875">Check upgradable packages
|
||||
Debian/Ubuntu: apt-get upgrade --dry-run<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:GenericNode configuration="com.yworks.flowchart.loopLimit">
|
||||
<y:Geometry height="51.10998735777497" width="137.19216182048035" x="92.54867256637169" y="376.28592169721867"/>
|
||||
<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" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="131.751953125" x="2.7201043477401754" y="9.422181178887513">foreach
|
||||
upgradable packages<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="5.551115123125783E-16" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n9">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="27.144753476611868" y="459.8409153761062"/>
|
||||
<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" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="213.619140625" x="27.1904296875" y="11.8671875">Parse changelog and get CVE IDs
|
||||
Debian/Ubuntu: aptitude 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:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n10">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.loopLimitEnd">
|
||||
<y:Geometry height="50.0" width="137.0" x="92.64475347661187" y="545.8409153761062"/>
|
||||
<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" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="55.24609375" x="40.876953125" y="15.93359375">end loop<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>
|
||||
<edge id="e0" source="n2" target="n1">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="45.22123893805309" tx="0.0" ty="-20.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="n1" target="n4">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="40.0" sy="0.0" tx="0.0" ty="-28.0">
|
||||
<y:Point x="743.3698412698412" y="226.44247787610618"/>
|
||||
</y:Path>
|
||||
<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="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="51.806640625" x="183.35883739927397" y="2.000003510871693">Amazon
|
||||
FreeBSD<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="1.9999999999998863" distanceToCenter="false" position="right" ratio="0.7796030035582084" 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="e2" source="n0" target="n2">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-45.22123893805309"/>
|
||||
<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="e3" source="n5" target="n6">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="10.8330078125"/>
|
||||
<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="n1" target="n3">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="-123.36984126984123" ty="0.0">
|
||||
<y:Point x="443.6849206349206" y="658.0546766682629"/>
|
||||
</y:Path>
|
||||
<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="74.6640625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="77.078125" x="-97.68364242524859" y="5.005267793098369">CentOS
|
||||
RHEL
|
||||
Ubuntu
|
||||
Debian
|
||||
Oracle Linux<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="59.14459455430983" distanceToCenter="true" position="right" ratio="0.0" 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="e5" source="n4" target="n3">
|
||||
<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>
|
||||
<edge id="e6" source="n7" target="n8">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-25.554993678887485"/>
|
||||
<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="n8" target="n9">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="25.554993678887485" tx="0.0" ty="-28.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="e8" source="n9" target="n10">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-25.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="e9" source="n3" target="n5">
|
||||
<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>
|
||||
<edge id="e10" source="n1" target="n7">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
|
||||
<y:Point x="161.14475347661187" y="226.44247787610618"/>
|
||||
</y:Path>
|
||||
<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" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="56.98046875" x="-196.80057112212188" y="20.933597260871807">Raspbian<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="left" ratio="0.6447921222409765" 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="e11" source="n10" target="n3">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="-125.78842258255952" ty="0.0">
|
||||
<y:Point x="161.14475347661187" y="658.0546766682629"/>
|
||||
</y:Path>
|
||||
<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: 74 KiB |
@@ -1,494 +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.17-->
|
||||
<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.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="309.6849206349206" y="0.0"/>
|
||||
<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" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="83.482421875" x="92.2587890625" y="18.93359375">Detect the OS<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="0.1619001116071429" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n1">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.decision">
|
||||
<y:Geometry height="40.0" width="80.0" x="403.6849206349206" y="206.44247787610618"/>
|
||||
<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" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="38.0" y="18.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="n2">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="90.44247787610618" width="268.0" x="309.6849206349206" y="86.0"/>
|
||||
<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" height="60.53125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="170.763671875" x="48.61816406250006" y="14.95561393805309">Get installed packages
|
||||
Debian/Ubuntu: dpkg-query
|
||||
Amazon/RHEL/CentOS: rpm
|
||||
FreeBSD: pkg<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="2.220446049250313E-16" 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="n3">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="10.0" y="287.8409153761062"/>
|
||||
<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" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="260.83984375" x="3.580078125" y="11.8671875">Check upgradable packages
|
||||
Debian/Ubuntu: apt-get upgrade --dry-run<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:GenericNode configuration="com.yworks.flowchart.loopLimit">
|
||||
<y:Geometry height="51.10998735777497" width="137.19216182048035" x="75.40391908975982" y="376.28592169721867"/>
|
||||
<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" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="131.751953125" x="2.7201043477401754" y="9.422181178887513">foreach
|
||||
upgradable packages<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="5.551115123125783E-16" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n5">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="10.0" y="459.8409153761062"/>
|
||||
<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" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="213.619140625" x="27.1904296875" y="11.8671875">Parse changelog and get CVE IDs
|
||||
Debian/Ubuntu: aptitude 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:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n6">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.loopLimitEnd">
|
||||
<y:Geometry height="50.0" width="137.0" x="75.5" y="545.8409153761062"/>
|
||||
<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" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="55.24609375" x="40.876953125" y="15.93359375">end loop<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="n7">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="625.8409153761062"/>
|
||||
<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" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="152.634765625" x="57.6826171875" y="18.93359375">Write results to JSON files<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="0.1619001116071429" 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:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="287.8409153761062"/>
|
||||
<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" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="232.744140625" x="17.6279296875" y="4.80078125">Get CVE IDs by using package manager
|
||||
Amazon/RHEL: yum plugin security
|
||||
FreeBSD: pkg audit<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="n9">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="716.4553275126422"/>
|
||||
<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" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="42.595703125" x="112.7021484375" y="18.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:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n10">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="309.6849206349206" y="371.39590905499364"/>
|
||||
<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" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="293.06640625" x="-12.533203124999943" y="11.8671875">Get all changelogs of updatable packages at once
|
||||
yum changelog<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="2.220446049250313E-16" 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="n11">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="309.68492063492056" y="459.8409153761062"/>
|
||||
<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" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="205.52734375" x="31.236328125000057" y="18.93359375">Parse changelogs and get CVE IDs <y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="2.220446049250313E-16" nodeRatioY="0.1619001116071429" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n12">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="373.8409153761062"/>
|
||||
<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" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="293.06640625" x="-12.533203124999886" y="11.8671875">Get all changelogs of updatable packages at once
|
||||
Amazon / RHEL: yum changelog<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="2.220446049250313E-16" 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="n13" yfiles.foldertype="group">
|
||||
<data key="d4"/>
|
||||
<data key="d5"/>
|
||||
<data key="d6">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="116.89483989807195" width="333.6788874841973" x="229.74083438685204" y="675.1748997511062"/>
|
||||
<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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="333.6788874841973" x="0.0" y="0.0">Vulnerability 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="0" bottomF="0.0" left="0" leftF="0.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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" 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="n13:">
|
||||
<node id="n13::n0">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
|
||||
<y:Geometry height="65.22882427307195" width="136.83944374209864" x="411.5802781289507" y="711.8409153761062"/>
|
||||
<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" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="117.970703125" x="9.434370308549205" y="23.548005886535975">CVE DB (NVD / JVN)<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-8.326672684688674E-16" 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="n13::n1">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
|
||||
<y:Geometry height="65.22882427307195" width="136.83944374209864" x="244.74083438685204" y="711.8409153761062"/>
|
||||
<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" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="55.533203125" x="40.653120308549205" y="23.548005886535975">OVAL DB<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-8.326672684688674E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<edge id="e0" source="n2" target="n1">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="45.22123893805309" tx="0.0" ty="-20.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="n1" target="n3">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="-40.0" sy="0.0" tx="0.0" ty="-28.0">
|
||||
<y:Point x="144.0" y="226.44247787610618"/>
|
||||
</y:Path>
|
||||
<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="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="56.98046875" x="-66.95987036992159" y="-48.39843398912808">Debian
|
||||
Ubuntu
|
||||
Raspbian<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="1.9999999999998863" distanceToCenter="false" position="right" ratio="0.02215389573439544" 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="e2" source="n3" target="n4">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-25.554993678887485"/>
|
||||
<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="e3" source="n4" target="n5">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="25.554993678887485" tx="0.0" ty="-28.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="e4" source="n5" target="n6">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-25.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="n6" target="n7">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="68.5" sy="0.0" tx="0.0" ty="-28.0">
|
||||
<y:Point x="743.3698412698412" y="570.8409153761062"/>
|
||||
</y:Path>
|
||||
<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="n1" target="n8">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="40.0" sy="0.0" tx="0.0" ty="-28.0">
|
||||
<y:Point x="743.3698412698412" y="226.44247787610618"/>
|
||||
</y:Path>
|
||||
<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="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="51.806640625" x="10.125014629061297" y="-48.39843398912805">Amazon
|
||||
RHEL
|
||||
FreeBSD<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="1.9999999999998863" distanceToCenter="false" position="left" ratio="0.022401276994204813" 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="e7" source="n0" target="n2">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-45.22123893805309"/>
|
||||
<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="e8" source="n7" target="n9">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-28.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="e9" source="n1" target="n10">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="20.0" tx="0.0" ty="-28.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" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="46.708984375" x="-53.35447755843876" y="5.000003510871807">CentOS<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.0" 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="e10" source="n10" target="n11">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-28.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="e11" source="n11" target="n7">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="-24.34091537610618">
|
||||
<y:Point x="743.3698412698412" y="487.8409153761062"/>
|
||||
</y:Path>
|
||||
<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="e12" source="n8" target="n12">
|
||||
<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>
|
||||
<edge id="e13" source="n12" target="n7">
|
||||
<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>
|
||||
<edge id="e14" source="n9" target="n13">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="10.8330078125"/>
|
||||
<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/>
|
||||
</data>
|
||||
</graphml>
|
||||
|
Before Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 198 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 |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 56 KiB |
59
main.go
@@ -1,59 +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 main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"context"
|
||||
|
||||
"github.com/future-architect/vuls/commands"
|
||||
"github.com/google/subcommands"
|
||||
)
|
||||
|
||||
// Version of Vuls
|
||||
var version = "0.4.0"
|
||||
|
||||
// Revision of Git
|
||||
var revision string
|
||||
|
||||
func main() {
|
||||
subcommands.Register(subcommands.HelpCommand(), "")
|
||||
subcommands.Register(subcommands.FlagsCommand(), "")
|
||||
subcommands.Register(subcommands.CommandsCommand(), "")
|
||||
subcommands.Register(&commands.DiscoverCmd{}, "discover")
|
||||
subcommands.Register(&commands.TuiCmd{}, "tui")
|
||||
subcommands.Register(&commands.ScanCmd{}, "scan")
|
||||
subcommands.Register(&commands.HistoryCmd{}, "history")
|
||||
subcommands.Register(&commands.ReportCmd{}, "report")
|
||||
subcommands.Register(&commands.ConfigtestCmd{}, "configtest")
|
||||
|
||||
var v = flag.Bool("v", false, "Show version")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if *v {
|
||||
fmt.Printf("vuls %s %s\n", version, revision)
|
||||
os.Exit(int(subcommands.ExitSuccess))
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
os.Exit(int(subcommands.Execute(ctx)))
|
||||
}
|
||||
@@ -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 models
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 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, NewCveContentType(myFamily)}
|
||||
for _, ctype := range order {
|
||||
if cont, found := v[ctype]; found {
|
||||
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(append(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(append(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(append(order)...)...)
|
||||
|
||||
for _, ctype := range order {
|
||||
if cont, found := v[ctype]; found && 0 < len(cont.CweID) {
|
||||
// RedHat's OVAL sometimes contains multiple CWE-IDs separated by spaces
|
||||
for _, cweID := range strings.Fields(cont.CweID) {
|
||||
values = append(values, CveContentStr{
|
||||
Type: ctype,
|
||||
Value: cweID,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CveContent has abstraction of various vulnerability information
|
||||
type CveContent struct {
|
||||
Type CveContentType
|
||||
CveID string
|
||||
Title string
|
||||
Summary string
|
||||
Severity string
|
||||
Cvss2Score float64
|
||||
Cvss2Vector string
|
||||
Cvss3Score float64
|
||||
Cvss3Vector string
|
||||
SourceLink string
|
||||
Cpes []Cpe
|
||||
References References
|
||||
CweID string
|
||||
Published time.Time
|
||||
LastModified time.Time
|
||||
}
|
||||
|
||||
// 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 "nvd":
|
||||
return NVD
|
||||
case "jvn":
|
||||
return JVN
|
||||
case "redhat", "centos":
|
||||
return RedHat
|
||||
case "oracle":
|
||||
return Oracle
|
||||
case "ubuntu":
|
||||
return Ubuntu
|
||||
case "debian":
|
||||
return Debian
|
||||
default:
|
||||
return Unknown
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// NVD is NVD
|
||||
NVD CveContentType = "nvd"
|
||||
|
||||
// JVN is JVN
|
||||
JVN CveContentType = "jvn"
|
||||
|
||||
// RedHat is RedHat
|
||||
RedHat CveContentType = "redhat"
|
||||
|
||||
// Debian is Debian
|
||||
Debian CveContentType = "debian"
|
||||
|
||||
// Ubuntu is Ubuntu
|
||||
Ubuntu CveContentType = "ubuntu"
|
||||
|
||||
// Oracle is Oracle Linux
|
||||
Oracle CveContentType = "oracle"
|
||||
|
||||
// Unknown is Unknown
|
||||
Unknown CveContentType = "unknown"
|
||||
)
|
||||
|
||||
// CveContentTypes has slide of CveContentType
|
||||
type CveContentTypes []CveContentType
|
||||
|
||||
// AllCveContetTypes has all of CveContentTypes
|
||||
var AllCveContetTypes = CveContentTypes{NVD, JVN, RedHat, Debian, Ubuntu}
|
||||
|
||||
// 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 {
|
||||
CpeName string
|
||||
}
|
||||
|
||||
// References is a slice of Reference
|
||||
type References []Reference
|
||||
|
||||
// Reference has a related link of the CVE
|
||||
type Reference struct {
|
||||
Source string
|
||||
Link string
|
||||
RefID string
|
||||
}
|
||||
@@ -1,206 +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 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",
|
||||
},
|
||||
NVD: {
|
||||
Type: NVD,
|
||||
SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
|
||||
},
|
||||
},
|
||||
},
|
||||
out: []CveContentStr{
|
||||
{
|
||||
Type: JVN,
|
||||
Value: "https://jvn.jp/vu/JVNVU93610402/",
|
||||
},
|
||||
{
|
||||
Type: NVD,
|
||||
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",
|
||||
},
|
||||
NVD: {
|
||||
Type: NVD,
|
||||
SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
|
||||
},
|
||||
},
|
||||
},
|
||||
out: []CveContentStr{
|
||||
{
|
||||
Type: NVD,
|
||||
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 _, tt := range tests {
|
||||
actual := tt.in.cont.SourceLinks(tt.in.lang, "redhat", tt.in.cveID)
|
||||
if !reflect.DeepEqual(tt.out, actual) {
|
||||
t.Errorf("\nexpected: %v\n actual: %v\n", 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",
|
||||
},
|
||||
NVD: {
|
||||
Type: NVD,
|
||||
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])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +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 models
|
||||
|
||||
// JSONVersion is JSON Version
|
||||
const JSONVersion = 2
|
||||
@@ -1,18 +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 models
|
||||
@@ -1,153 +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 models
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Packages is Map of Package
|
||||
// { "package-name": Package }
|
||||
type Packages map[string]Package
|
||||
|
||||
// NewPackages create Packages
|
||||
func NewPackages(packs ...Package) Packages {
|
||||
m := Packages{}
|
||||
for _, pack := range packs {
|
||||
m[pack.Name] = pack
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// MergeNewVersion merges candidate version information to the receiver struct
|
||||
func (ps Packages) MergeNewVersion(as Packages) {
|
||||
for _, a := range as {
|
||||
if pack, ok := ps[a.Name]; ok {
|
||||
pack.NewVersion = a.NewVersion
|
||||
pack.NewRelease = a.NewRelease
|
||||
pack.Repository = a.Repository
|
||||
ps[a.Name] = pack
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge returns merged map (immutable)
|
||||
func (ps Packages) Merge(other Packages) Packages {
|
||||
merged := Packages{}
|
||||
for k, v := range ps {
|
||||
merged[k] = v
|
||||
}
|
||||
for k, v := range other {
|
||||
merged[k] = v
|
||||
}
|
||||
return merged
|
||||
}
|
||||
|
||||
// FormatUpdatablePacksSummary returns a summary of updatable packages
|
||||
func (ps Packages) FormatUpdatablePacksSummary() string {
|
||||
nUpdatable := 0
|
||||
for _, p := range ps {
|
||||
if p.NewVersion != "" {
|
||||
nUpdatable++
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%d updatable packages", nUpdatable)
|
||||
}
|
||||
|
||||
// FindOne search a element by name-newver-newrel-arch
|
||||
func (ps Packages) FindOne(f func(Package) bool) (string, Package, bool) {
|
||||
for key, p := range ps {
|
||||
if f(p) {
|
||||
return key, p, true
|
||||
}
|
||||
}
|
||||
return "", Package{}, false
|
||||
}
|
||||
|
||||
// Package has installed packages.
|
||||
type Package struct {
|
||||
Name string
|
||||
Version string
|
||||
Release string
|
||||
NewVersion string
|
||||
NewRelease string
|
||||
Arch string
|
||||
Repository string
|
||||
Changelog Changelog
|
||||
}
|
||||
|
||||
// FormatVer returns package version-release
|
||||
func (p Package) FormatVer() string {
|
||||
ver := p.Version
|
||||
if 0 < len(p.Release) {
|
||||
ver = fmt.Sprintf("%s-%s", ver, p.Release)
|
||||
}
|
||||
return ver
|
||||
}
|
||||
|
||||
// FormatNewVer returns package version-release
|
||||
func (p Package) FormatNewVer() string {
|
||||
ver := p.NewVersion
|
||||
if 0 < len(p.NewRelease) {
|
||||
ver = fmt.Sprintf("%s-%s", ver, p.NewRelease)
|
||||
}
|
||||
return ver
|
||||
}
|
||||
|
||||
// FormatVersionFromTo formats installed and new package version
|
||||
func (p Package) FormatVersionFromTo(notFixedYet bool) string {
|
||||
to := p.FormatNewVer()
|
||||
if notFixedYet {
|
||||
to = "Not Fixed Yet"
|
||||
}
|
||||
return fmt.Sprintf("%s-%s -> %s", p.Name, p.FormatVer(), to)
|
||||
}
|
||||
|
||||
// FormatChangelog formats the changelog
|
||||
func (p Package) FormatChangelog() string {
|
||||
buf := []string{}
|
||||
packVer := fmt.Sprintf("%s-%s -> %s",
|
||||
p.Name, p.FormatVer(), p.FormatNewVer())
|
||||
var delim bytes.Buffer
|
||||
for i := 0; i < len(packVer); i++ {
|
||||
delim.WriteString("-")
|
||||
}
|
||||
|
||||
clog := p.Changelog.Contents
|
||||
if lines := strings.Split(clog, "\n"); len(lines) != 0 {
|
||||
clog = strings.Join(lines[0:len(lines)-1], "\n")
|
||||
}
|
||||
|
||||
switch p.Changelog.Method {
|
||||
case FailedToGetChangelog:
|
||||
clog = "No changelogs"
|
||||
case FailedToFindVersionInChangelog:
|
||||
clog = "Failed to parse changelogs. For detials, check yourself"
|
||||
}
|
||||
buf = append(buf, packVer, delim.String(), clog)
|
||||
return strings.Join(buf, "\n")
|
||||
}
|
||||
|
||||
// Changelog has contents of changelog and how to get it.
|
||||
// Method: models.detectionMethodStr
|
||||
type Changelog struct {
|
||||
Contents string
|
||||
Method DetectionMethod
|
||||
}
|
||||
@@ -1,89 +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 models
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/k0kubun/pp"
|
||||
)
|
||||
|
||||
func TestMergeNewVersion(t *testing.T) {
|
||||
var test = struct {
|
||||
a Packages
|
||||
b Packages
|
||||
expected Packages
|
||||
}{
|
||||
Packages{
|
||||
"hoge": {
|
||||
Name: "hoge",
|
||||
},
|
||||
},
|
||||
Packages{
|
||||
"hoge": {
|
||||
Name: "hoge",
|
||||
NewVersion: "1.0.0",
|
||||
NewRelease: "release1",
|
||||
},
|
||||
},
|
||||
Packages{
|
||||
"hoge": {
|
||||
Name: "hoge",
|
||||
NewVersion: "1.0.0",
|
||||
NewRelease: "release1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
test.a.MergeNewVersion(test.b)
|
||||
if !reflect.DeepEqual(test.a, test.expected) {
|
||||
e := pp.Sprintf("%v", test.a)
|
||||
a := pp.Sprintf("%v", test.expected)
|
||||
t.Errorf("expected %s, actual %s", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMerge(t *testing.T) {
|
||||
var test = struct {
|
||||
a Packages
|
||||
b Packages
|
||||
expected Packages
|
||||
}{
|
||||
Packages{
|
||||
"hoge": {Name: "hoge"},
|
||||
"fuga": {Name: "fuga"},
|
||||
},
|
||||
Packages{
|
||||
"hega": {Name: "hega"},
|
||||
"hage": {Name: "hage"},
|
||||
},
|
||||
Packages{
|
||||
"hoge": {Name: "hoge"},
|
||||
"fuga": {Name: "fuga"},
|
||||
"hega": {Name: "hega"},
|
||||
"hage": {Name: "hage"},
|
||||
},
|
||||
}
|
||||
|
||||
actual := test.a.Merge(test.b)
|
||||
if !reflect.DeepEqual(actual, test.expected) {
|
||||
e := pp.Sprintf("%v", test.expected)
|
||||
a := pp.Sprintf("%v", actual)
|
||||
t.Errorf("expected %s, actual %s", e, a)
|
||||
}
|
||||
}
|
||||
@@ -1,191 +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 models
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
)
|
||||
|
||||
// ScanResults is a slide of ScanResult
|
||||
type ScanResults []ScanResult
|
||||
|
||||
// ScanResult has the result of scanned CVE information.
|
||||
type ScanResult struct {
|
||||
ScannedAt time.Time
|
||||
ReportedAt time.Time
|
||||
JSONVersion int
|
||||
Lang string
|
||||
ServerUUID string
|
||||
ServerName string // TOML Section key
|
||||
Family string
|
||||
Release string
|
||||
Container Container
|
||||
Platform Platform
|
||||
|
||||
// Scanned Vulns by SSH scan + CPE + OVAL
|
||||
ScannedCves VulnInfos
|
||||
|
||||
RunningKernel Kernel
|
||||
Packages Packages
|
||||
Errors []string
|
||||
Optional [][]interface{}
|
||||
|
||||
Config struct {
|
||||
Scan config.Config
|
||||
Report config.Config
|
||||
}
|
||||
}
|
||||
|
||||
// Kernel has the Release, version and whether need restart
|
||||
type Kernel struct {
|
||||
Release string
|
||||
Version string
|
||||
RebootRequired bool
|
||||
}
|
||||
|
||||
// FilterByCvssOver is filter function.
|
||||
func (r ScanResult) FilterByCvssOver(over float64) ScanResult {
|
||||
filtered := r.ScannedCves.Find(func(v VulnInfo) bool {
|
||||
v2Max := v.MaxCvss2Score()
|
||||
v3Max := v.MaxCvss3Score()
|
||||
max := v2Max.Value.Score
|
||||
if max < v3Max.Value.Score {
|
||||
max = v3Max.Value.Score
|
||||
}
|
||||
if over <= max {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
copiedScanResult := r
|
||||
copiedScanResult.ScannedCves = filtered
|
||||
return copiedScanResult
|
||||
}
|
||||
|
||||
// FilterIgnoreCves is filter function.
|
||||
func (r ScanResult) FilterIgnoreCves(cveIDs []string) ScanResult {
|
||||
filtered := r.ScannedCves.Find(func(v VulnInfo) bool {
|
||||
for _, c := range cveIDs {
|
||||
if v.CveID == c {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
copiedScanResult := r
|
||||
copiedScanResult.ScannedCves = filtered
|
||||
return copiedScanResult
|
||||
}
|
||||
|
||||
// ReportFileName returns the filename on localhost without extention
|
||||
func (r ScanResult) ReportFileName() (name string) {
|
||||
if len(r.Container.ContainerID) == 0 {
|
||||
return fmt.Sprintf("%s", r.ServerName)
|
||||
}
|
||||
return fmt.Sprintf("%s@%s", r.Container.Name, r.ServerName)
|
||||
}
|
||||
|
||||
// ReportKeyName returns the name of key on S3, Azure-Blob without extention
|
||||
func (r ScanResult) ReportKeyName() (name string) {
|
||||
timestr := r.ScannedAt.Format(time.RFC3339)
|
||||
if len(r.Container.ContainerID) == 0 {
|
||||
return fmt.Sprintf("%s/%s", timestr, r.ServerName)
|
||||
}
|
||||
return fmt.Sprintf("%s/%s@%s", timestr, r.Container.Name, r.ServerName)
|
||||
}
|
||||
|
||||
// ServerInfo returns server name one line
|
||||
func (r ScanResult) ServerInfo() string {
|
||||
if len(r.Container.ContainerID) == 0 {
|
||||
return fmt.Sprintf("%s (%s%s)",
|
||||
r.FormatServerName(), r.Family, r.Release)
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"%s (%s%s) on %s",
|
||||
r.FormatServerName(),
|
||||
r.Family,
|
||||
r.Release,
|
||||
r.ServerName,
|
||||
)
|
||||
}
|
||||
|
||||
// ServerInfoTui returns server infromation for TUI sidebar
|
||||
func (r ScanResult) ServerInfoTui() string {
|
||||
if len(r.Container.ContainerID) == 0 {
|
||||
line := fmt.Sprintf("%s (%s%s)",
|
||||
r.ServerName, r.Family, r.Release)
|
||||
if r.RunningKernel.RebootRequired {
|
||||
return "[Reboot] " + line
|
||||
}
|
||||
return line
|
||||
}
|
||||
|
||||
fmtstr := "|-- %s (%s%s)"
|
||||
if r.RunningKernel.RebootRequired {
|
||||
fmtstr = "|-- [Reboot] %s (%s%s)"
|
||||
}
|
||||
return fmt.Sprintf(fmtstr, r.Container.Name, r.Family, r.Release)
|
||||
}
|
||||
|
||||
// FormatServerName returns server and container name
|
||||
func (r ScanResult) FormatServerName() (name string) {
|
||||
if len(r.Container.ContainerID) == 0 {
|
||||
name = r.ServerName
|
||||
} else {
|
||||
name = fmt.Sprintf("%s@%s",
|
||||
r.Container.Name, r.ServerName)
|
||||
}
|
||||
if r.RunningKernel.RebootRequired {
|
||||
name = "[Reboot Required] " + name
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FormatTextReportHeadedr returns header of text report
|
||||
func (r ScanResult) FormatTextReportHeadedr() string {
|
||||
serverInfo := r.ServerInfo()
|
||||
var buf bytes.Buffer
|
||||
for i := 0; i < len(serverInfo); i++ {
|
||||
buf.WriteString("=")
|
||||
}
|
||||
return fmt.Sprintf("%s\n%s\n%s\t%s\n",
|
||||
r.ServerInfo(),
|
||||
buf.String(),
|
||||
r.ScannedCves.FormatCveSummary(),
|
||||
r.Packages.FormatUpdatablePacksSummary(),
|
||||
)
|
||||
}
|
||||
|
||||
// Container has Container information
|
||||
type Container struct {
|
||||
ContainerID string
|
||||
Name string
|
||||
Image string
|
||||
Type string
|
||||
}
|
||||
|
||||
// Platform has platform information
|
||||
type Platform struct {
|
||||
Name string // aws or azure or gcp or other...
|
||||
InstanceID string
|
||||
}
|
||||
@@ -1,257 +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 models
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/k0kubun/pp"
|
||||
)
|
||||
|
||||
func TestFilterByCvssOver(t *testing.T) {
|
||||
type in struct {
|
||||
over float64
|
||||
rs ScanResult
|
||||
}
|
||||
var tests = []struct {
|
||||
in in
|
||||
out ScanResult
|
||||
}{
|
||||
{
|
||||
in: in{
|
||||
over: 7.0,
|
||||
rs: ScanResult{
|
||||
ScannedCves: VulnInfos{
|
||||
"CVE-2017-0001": {
|
||||
CveID: "CVE-2017-0001",
|
||||
CveContents: NewCveContents(
|
||||
CveContent{
|
||||
Type: NVD,
|
||||
CveID: "CVE-2017-0001",
|
||||
Cvss2Score: 7.1,
|
||||
LastModified: time.Time{},
|
||||
},
|
||||
),
|
||||
},
|
||||
"CVE-2017-0002": {
|
||||
CveID: "CVE-2017-0002",
|
||||
CveContents: NewCveContents(
|
||||
CveContent{
|
||||
Type: NVD,
|
||||
CveID: "CVE-2017-0002",
|
||||
Cvss2Score: 6.9,
|
||||
LastModified: time.Time{},
|
||||
},
|
||||
),
|
||||
},
|
||||
"CVE-2017-0003": {
|
||||
CveID: "CVE-2017-0003",
|
||||
CveContents: NewCveContents(
|
||||
CveContent{
|
||||
Type: NVD,
|
||||
CveID: "CVE-2017-0003",
|
||||
Cvss2Score: 6.9,
|
||||
LastModified: time.Time{},
|
||||
},
|
||||
CveContent{
|
||||
Type: JVN,
|
||||
CveID: "CVE-2017-0003",
|
||||
Cvss2Score: 7.2,
|
||||
LastModified: time.Time{},
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
out: ScanResult{
|
||||
ScannedCves: VulnInfos{
|
||||
"CVE-2017-0001": {
|
||||
CveID: "CVE-2017-0001",
|
||||
CveContents: NewCveContents(
|
||||
CveContent{
|
||||
Type: NVD,
|
||||
CveID: "CVE-2017-0001",
|
||||
Cvss2Score: 7.1,
|
||||
LastModified: time.Time{},
|
||||
},
|
||||
),
|
||||
},
|
||||
"CVE-2017-0003": {
|
||||
CveID: "CVE-2017-0003",
|
||||
CveContents: NewCveContents(
|
||||
CveContent{
|
||||
Type: NVD,
|
||||
CveID: "CVE-2017-0003",
|
||||
Cvss2Score: 6.9,
|
||||
LastModified: time.Time{},
|
||||
},
|
||||
CveContent{
|
||||
Type: JVN,
|
||||
CveID: "CVE-2017-0003",
|
||||
Cvss2Score: 7.2,
|
||||
LastModified: time.Time{},
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// OVAL Severity
|
||||
{
|
||||
in: in{
|
||||
over: 7.0,
|
||||
rs: ScanResult{
|
||||
ScannedCves: VulnInfos{
|
||||
"CVE-2017-0001": {
|
||||
CveID: "CVE-2017-0001",
|
||||
CveContents: NewCveContents(
|
||||
CveContent{
|
||||
Type: Ubuntu,
|
||||
CveID: "CVE-2017-0001",
|
||||
Severity: "HIGH",
|
||||
LastModified: time.Time{},
|
||||
},
|
||||
),
|
||||
},
|
||||
"CVE-2017-0002": {
|
||||
CveID: "CVE-2017-0002",
|
||||
CveContents: NewCveContents(
|
||||
CveContent{
|
||||
Type: RedHat,
|
||||
CveID: "CVE-2017-0002",
|
||||
Severity: "CRITICAL",
|
||||
LastModified: time.Time{},
|
||||
},
|
||||
),
|
||||
},
|
||||
"CVE-2017-0003": {
|
||||
CveID: "CVE-2017-0003",
|
||||
CveContents: NewCveContents(
|
||||
CveContent{
|
||||
Type: Oracle,
|
||||
CveID: "CVE-2017-0003",
|
||||
Severity: "IMPORTANT",
|
||||
LastModified: time.Time{},
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
out: ScanResult{
|
||||
ScannedCves: VulnInfos{
|
||||
"CVE-2017-0001": {
|
||||
CveID: "CVE-2017-0001",
|
||||
CveContents: NewCveContents(
|
||||
CveContent{
|
||||
Type: Ubuntu,
|
||||
CveID: "CVE-2017-0001",
|
||||
Severity: "HIGH",
|
||||
LastModified: time.Time{},
|
||||
},
|
||||
),
|
||||
},
|
||||
"CVE-2017-0002": {
|
||||
CveID: "CVE-2017-0002",
|
||||
CveContents: NewCveContents(
|
||||
CveContent{
|
||||
Type: RedHat,
|
||||
CveID: "CVE-2017-0002",
|
||||
Severity: "CRITICAL",
|
||||
LastModified: time.Time{},
|
||||
},
|
||||
),
|
||||
},
|
||||
"CVE-2017-0003": {
|
||||
CveID: "CVE-2017-0003",
|
||||
CveContents: NewCveContents(
|
||||
CveContent{
|
||||
Type: Oracle,
|
||||
CveID: "CVE-2017-0003",
|
||||
Severity: "IMPORTANT",
|
||||
LastModified: time.Time{},
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
actual := tt.in.rs.FilterByCvssOver(tt.in.over)
|
||||
for k := range tt.out.ScannedCves {
|
||||
if !reflect.DeepEqual(tt.out.ScannedCves[k], actual.ScannedCves[k]) {
|
||||
o := pp.Sprintf("%v", tt.out.ScannedCves[k])
|
||||
a := pp.Sprintf("%v", actual.ScannedCves[k])
|
||||
t.Errorf("[%s] expected: %v\n actual: %v\n", k, o, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterIgnoreCveIDs(t *testing.T) {
|
||||
type in struct {
|
||||
cves []string
|
||||
rs ScanResult
|
||||
}
|
||||
var tests = []struct {
|
||||
in in
|
||||
out ScanResult
|
||||
}{
|
||||
{
|
||||
in: in{
|
||||
cves: []string{"CVE-2017-0002"},
|
||||
rs: ScanResult{
|
||||
ScannedCves: VulnInfos{
|
||||
"CVE-2017-0001": {
|
||||
CveID: "CVE-2017-0001",
|
||||
},
|
||||
"CVE-2017-0002": {
|
||||
CveID: "CVE-2017-0002",
|
||||
},
|
||||
"CVE-2017-0003": {
|
||||
CveID: "CVE-2017-0003",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
out: ScanResult{
|
||||
ScannedCves: VulnInfos{
|
||||
"CVE-2017-0001": {
|
||||
CveID: "CVE-2017-0001",
|
||||
},
|
||||
"CVE-2017-0003": {
|
||||
CveID: "CVE-2017-0003",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
actual := tt.in.rs.FilterIgnoreCves(tt.in.cves)
|
||||
for k := range tt.out.ScannedCves {
|
||||
if !reflect.DeepEqual(tt.out.ScannedCves[k], actual.ScannedCves[k]) {
|
||||
o := pp.Sprintf("%v", tt.out.ScannedCves[k])
|
||||
a := pp.Sprintf("%v", actual.ScannedCves[k])
|
||||
t.Errorf("[%s] expected: %v\n actual: %v\n", k, o, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
114
models/utils.go
@@ -1,114 +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 models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
cvedict "github.com/kotakanbe/go-cve-dictionary/models"
|
||||
)
|
||||
|
||||
// ConvertNvdToModel convert NVD to CveContent
|
||||
func ConvertNvdToModel(cveID string, nvd cvedict.Nvd) *CveContent {
|
||||
var cpes []Cpe
|
||||
for _, c := range nvd.Cpes {
|
||||
cpes = append(cpes, Cpe{CpeName: c.CpeName})
|
||||
}
|
||||
|
||||
var refs []Reference
|
||||
for _, r := range nvd.References {
|
||||
refs = append(refs, Reference{
|
||||
Link: r.Link,
|
||||
Source: r.Source,
|
||||
})
|
||||
}
|
||||
|
||||
validVec := true
|
||||
for _, v := range []string{
|
||||
nvd.AccessVector,
|
||||
nvd.AccessComplexity,
|
||||
nvd.Authentication,
|
||||
nvd.ConfidentialityImpact,
|
||||
nvd.IntegrityImpact,
|
||||
nvd.AvailabilityImpact,
|
||||
} {
|
||||
if len(v) == 0 {
|
||||
validVec = false
|
||||
}
|
||||
}
|
||||
|
||||
vector := ""
|
||||
if validVec {
|
||||
vector = fmt.Sprintf("AV:%s/AC:%s/Au:%s/C:%s/I:%s/A:%s",
|
||||
string(nvd.AccessVector[0]),
|
||||
string(nvd.AccessComplexity[0]),
|
||||
string(nvd.Authentication[0]),
|
||||
string(nvd.ConfidentialityImpact[0]),
|
||||
string(nvd.IntegrityImpact[0]),
|
||||
string(nvd.AvailabilityImpact[0]))
|
||||
}
|
||||
|
||||
//TODO CVSSv3
|
||||
return &CveContent{
|
||||
Type: NVD,
|
||||
CveID: cveID,
|
||||
Summary: nvd.Summary,
|
||||
Cvss2Score: nvd.Score,
|
||||
Cvss2Vector: vector,
|
||||
Severity: "", // severity is not contained in NVD
|
||||
SourceLink: "https://nvd.nist.gov/vuln/detail/" + cveID,
|
||||
Cpes: cpes,
|
||||
CweID: nvd.CweID,
|
||||
References: refs,
|
||||
Published: nvd.PublishedDate,
|
||||
LastModified: nvd.LastModifiedDate,
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertJvnToModel convert JVN to CveContent
|
||||
func ConvertJvnToModel(cveID string, jvn cvedict.Jvn) *CveContent {
|
||||
var cpes []Cpe
|
||||
for _, c := range jvn.Cpes {
|
||||
cpes = append(cpes, Cpe{CpeName: c.CpeName})
|
||||
}
|
||||
|
||||
refs := []Reference{}
|
||||
for _, r := range jvn.References {
|
||||
refs = append(refs, Reference{
|
||||
Link: r.Link,
|
||||
Source: r.Source,
|
||||
})
|
||||
}
|
||||
|
||||
vector := strings.TrimSuffix(strings.TrimPrefix(jvn.Vector, "("), ")")
|
||||
return &CveContent{
|
||||
Type: JVN,
|
||||
CveID: cveID,
|
||||
Title: jvn.Title,
|
||||
Summary: jvn.Summary,
|
||||
Severity: jvn.Severity,
|
||||
Cvss2Score: jvn.Score,
|
||||
Cvss2Vector: vector,
|
||||
SourceLink: jvn.JvnLink,
|
||||
Cpes: cpes,
|
||||
References: refs,
|
||||
Published: jvn.PublishedDate,
|
||||
LastModified: jvn.LastModifiedDate,
|
||||
}
|
||||
}
|
||||
@@ -1,666 +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 models
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
)
|
||||
|
||||
// VulnInfos has a map of VulnInfo
|
||||
// Key: CveID
|
||||
type VulnInfos map[string]VulnInfo
|
||||
|
||||
// Find elements that matches the function passed in argument
|
||||
func (v VulnInfos) Find(f func(VulnInfo) bool) VulnInfos {
|
||||
filtered := VulnInfos{}
|
||||
for _, vv := range v {
|
||||
if f(vv) {
|
||||
filtered[vv.CveID] = vv
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
// FindScoredVulns return scored vulnerabilities
|
||||
func (v VulnInfos) FindScoredVulns() VulnInfos {
|
||||
return v.Find(func(vv VulnInfo) bool {
|
||||
if 0 < vv.MaxCvss2Score().Value.Score ||
|
||||
0 < vv.MaxCvss3Score().Value.Score {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
// ToSortedSlice returns slice of VulnInfos that is sorted by Score, CVE-ID
|
||||
func (v VulnInfos) ToSortedSlice() (sorted []VulnInfo) {
|
||||
for k := range v {
|
||||
sorted = append(sorted, v[k])
|
||||
}
|
||||
sort.Slice(sorted, func(i, j int) bool {
|
||||
maxI := sorted[i].MaxCvssScore()
|
||||
maxJ := sorted[j].MaxCvssScore()
|
||||
if maxI.Value.Score != maxJ.Value.Score {
|
||||
return maxJ.Value.Score < maxI.Value.Score
|
||||
}
|
||||
return sorted[i].CveID < sorted[j].CveID
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// CountGroupBySeverity summarize the number of CVEs group by CVSSv2 Severity
|
||||
func (v VulnInfos) CountGroupBySeverity() map[string]int {
|
||||
m := map[string]int{}
|
||||
for _, vInfo := range v {
|
||||
score := vInfo.MaxCvss2Score().Value.Score
|
||||
if score < 0.1 {
|
||||
score = vInfo.MaxCvss3Score().Value.Score
|
||||
}
|
||||
switch {
|
||||
case 7.0 <= score:
|
||||
m["High"]++
|
||||
case 4.0 <= score:
|
||||
m["Medium"]++
|
||||
case 0 < score:
|
||||
m["Low"]++
|
||||
default:
|
||||
m["Unknown"]++
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// FormatCveSummary summarize the number of CVEs group by CVSSv2 Severity
|
||||
func (v VulnInfos) FormatCveSummary() string {
|
||||
m := v.CountGroupBySeverity()
|
||||
|
||||
if config.Conf.IgnoreUnscoredCves {
|
||||
return fmt.Sprintf("Total: %d (High:%d Medium:%d Low:%d)",
|
||||
m["High"]+m["Medium"]+m["Low"], m["High"], m["Medium"], m["Low"])
|
||||
}
|
||||
return fmt.Sprintf("Total: %d (High:%d Medium:%d Low:%d ?:%d)",
|
||||
m["High"]+m["Medium"]+m["Low"]+m["Unknown"],
|
||||
m["High"], m["Medium"], m["Low"], m["Unknown"])
|
||||
}
|
||||
|
||||
// PackageStatuses is a list of PackageStatus
|
||||
type PackageStatuses []PackageStatus
|
||||
|
||||
// Sort by Name
|
||||
func (p PackageStatuses) Sort() {
|
||||
sort.Slice(p, func(i, j int) bool {
|
||||
return p[i].Name < p[j].Name
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// PackageStatus has name and other status abount the package
|
||||
type PackageStatus struct {
|
||||
Name string
|
||||
NotFixedYet bool
|
||||
}
|
||||
|
||||
// VulnInfo has a vulnerability information and unsecure packages
|
||||
type VulnInfo struct {
|
||||
CveID string
|
||||
Confidence Confidence
|
||||
AffectedPackages PackageStatuses
|
||||
DistroAdvisories []DistroAdvisory // for Aamazon, RHEL, FreeBSD
|
||||
CpeNames []string
|
||||
CveContents CveContents
|
||||
}
|
||||
|
||||
// Titles returns tilte (TUI)
|
||||
func (v VulnInfo) Titles(lang, myFamily string) (values []CveContentStr) {
|
||||
if lang == "ja" {
|
||||
if cont, found := v.CveContents[JVN]; found && 0 < len(cont.Title) {
|
||||
values = append(values, CveContentStr{JVN, cont.Title})
|
||||
}
|
||||
}
|
||||
|
||||
order := CveContentTypes{NVD, NewCveContentType(myFamily)}
|
||||
order = append(order, AllCveContetTypes.Except(append(order, JVN)...)...)
|
||||
for _, ctype := range order {
|
||||
// Only JVN has meaningful title. so return first 100 char of summary
|
||||
if cont, found := v.CveContents[ctype]; found && 0 < len(cont.Summary) {
|
||||
summary := strings.Replace(cont.Summary, "\n", " ", -1)
|
||||
values = append(values, CveContentStr{
|
||||
Type: ctype,
|
||||
Value: summary,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for _, adv := range v.DistroAdvisories {
|
||||
values = append(values, CveContentStr{
|
||||
Type: "Vendor",
|
||||
Value: strings.Replace(adv.Description, "\n", " ", -1),
|
||||
})
|
||||
}
|
||||
|
||||
if len(values) == 0 {
|
||||
values = []CveContentStr{{
|
||||
Type: Unknown,
|
||||
Value: "-",
|
||||
}}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Summaries returns summaries
|
||||
func (v VulnInfo) Summaries(lang, myFamily string) (values []CveContentStr) {
|
||||
if lang == "ja" {
|
||||
if cont, found := v.CveContents[JVN]; found && 0 < len(cont.Summary) {
|
||||
summary := cont.Title
|
||||
summary += "\n" + strings.Replace(
|
||||
strings.Replace(cont.Summary, "\n", " ", -1), "\r", " ", -1)
|
||||
values = append(values, CveContentStr{JVN, summary})
|
||||
}
|
||||
}
|
||||
|
||||
order := CveContentTypes{NVD, NewCveContentType(myFamily)}
|
||||
order = append(order, AllCveContetTypes.Except(append(order, JVN)...)...)
|
||||
for _, ctype := range order {
|
||||
if cont, found := v.CveContents[ctype]; found && 0 < len(cont.Summary) {
|
||||
summary := strings.Replace(cont.Summary, "\n", " ", -1)
|
||||
values = append(values, CveContentStr{
|
||||
Type: ctype,
|
||||
Value: summary,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for _, adv := range v.DistroAdvisories {
|
||||
values = append(values, CveContentStr{
|
||||
Type: "Vendor",
|
||||
Value: adv.Description,
|
||||
})
|
||||
}
|
||||
|
||||
if len(values) == 0 {
|
||||
return []CveContentStr{{
|
||||
Type: Unknown,
|
||||
Value: "-",
|
||||
}}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Cvss2Scores returns CVSS V2 Scores
|
||||
func (v VulnInfo) Cvss2Scores() (values []CveContentCvss) {
|
||||
order := []CveContentType{NVD, RedHat, JVN}
|
||||
for _, ctype := range order {
|
||||
if cont, found := v.CveContents[ctype]; found && 0 < cont.Cvss2Score {
|
||||
// https://nvd.nist.gov/vuln-metrics/cvss
|
||||
sev := cont.Severity
|
||||
if ctype == NVD {
|
||||
sev = cvss2ScoreToSeverity(cont.Cvss2Score)
|
||||
}
|
||||
values = append(values, CveContentCvss{
|
||||
Type: ctype,
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: cont.Cvss2Score,
|
||||
Vector: cont.Cvss2Vector,
|
||||
Severity: strings.ToUpper(sev),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for _, adv := range v.DistroAdvisories {
|
||||
if adv.Severity != "" {
|
||||
values = append(values, CveContentCvss{
|
||||
Type: "Vendor",
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: severityToV2ScoreRoughly(adv.Severity),
|
||||
CalculatedBySeverity: true,
|
||||
Vector: "-",
|
||||
Severity: strings.ToUpper(adv.Severity),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Cvss3Scores returns CVSS V3 Score
|
||||
func (v VulnInfo) Cvss3Scores() (values []CveContentCvss) {
|
||||
// TODO implement NVD
|
||||
order := []CveContentType{RedHat}
|
||||
for _, ctype := range order {
|
||||
if cont, found := v.CveContents[ctype]; found && 0 < cont.Cvss3Score {
|
||||
// https://nvd.nist.gov/vuln-metrics/cvss
|
||||
sev := cont.Severity
|
||||
values = append(values, CveContentCvss{
|
||||
Type: ctype,
|
||||
Value: Cvss{
|
||||
Type: CVSS3,
|
||||
Score: cont.Cvss3Score,
|
||||
Vector: cont.Cvss3Vector,
|
||||
Severity: strings.ToUpper(sev),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MaxCvss3Score returns Max CVSS V3 Score
|
||||
func (v VulnInfo) MaxCvss3Score() CveContentCvss {
|
||||
// TODO implement NVD
|
||||
order := []CveContentType{RedHat}
|
||||
max := 0.0
|
||||
value := CveContentCvss{
|
||||
Type: Unknown,
|
||||
Value: Cvss{Type: CVSS3},
|
||||
}
|
||||
for _, ctype := range order {
|
||||
if cont, found := v.CveContents[ctype]; found && max < cont.Cvss3Score {
|
||||
// https://nvd.nist.gov/vuln-metrics/cvss
|
||||
sev := cont.Severity
|
||||
value = CveContentCvss{
|
||||
Type: ctype,
|
||||
Value: Cvss{
|
||||
Type: CVSS3,
|
||||
Score: cont.Cvss3Score,
|
||||
Vector: cont.Cvss3Vector,
|
||||
Severity: sev,
|
||||
},
|
||||
}
|
||||
max = cont.Cvss3Score
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// MaxCvssScore returns max CVSS Score
|
||||
// If there is no CVSS Score, return Severity as a numerical value.
|
||||
func (v VulnInfo) MaxCvssScore() CveContentCvss {
|
||||
v3Max := v.MaxCvss3Score()
|
||||
v2Max := v.MaxCvss2Score()
|
||||
max := v3Max
|
||||
if max.Type == Unknown {
|
||||
return v2Max
|
||||
}
|
||||
|
||||
if max.Value.Score < v2Max.Value.Score && !v2Max.Value.CalculatedBySeverity {
|
||||
max = v2Max
|
||||
}
|
||||
return max
|
||||
}
|
||||
|
||||
// MaxCvss2Score returns Max CVSS V2 Score
|
||||
func (v VulnInfo) MaxCvss2Score() CveContentCvss {
|
||||
order := []CveContentType{NVD, RedHat, JVN}
|
||||
max := 0.0
|
||||
value := CveContentCvss{
|
||||
Type: Unknown,
|
||||
Value: Cvss{Type: CVSS2},
|
||||
}
|
||||
for _, ctype := range order {
|
||||
if cont, found := v.CveContents[ctype]; found && max < cont.Cvss2Score {
|
||||
// https://nvd.nist.gov/vuln-metrics/cvss
|
||||
sev := cont.Severity
|
||||
if ctype == NVD {
|
||||
sev = cvss2ScoreToSeverity(cont.Cvss2Score)
|
||||
}
|
||||
value = CveContentCvss{
|
||||
Type: ctype,
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: cont.Cvss2Score,
|
||||
Vector: cont.Cvss2Vector,
|
||||
Severity: sev,
|
||||
},
|
||||
}
|
||||
max = cont.Cvss2Score
|
||||
}
|
||||
}
|
||||
if 0 < max {
|
||||
return value
|
||||
}
|
||||
|
||||
// If CVSS score isn't on NVD, RedHat and JVN, use OVAL and advisory Severity.
|
||||
// Convert severity to cvss srore roughly, then returns max severity.
|
||||
// Only Ubuntu, RedHat and Oracle have severity data in OVAL.
|
||||
order = []CveContentType{Ubuntu, RedHat, Oracle}
|
||||
for _, ctype := range order {
|
||||
if cont, found := v.CveContents[ctype]; found && 0 < len(cont.Severity) {
|
||||
score := severityToV2ScoreRoughly(cont.Severity)
|
||||
if max < score {
|
||||
value = CveContentCvss{
|
||||
Type: ctype,
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: score,
|
||||
CalculatedBySeverity: true,
|
||||
Vector: cont.Cvss2Vector,
|
||||
Severity: cont.Severity,
|
||||
},
|
||||
}
|
||||
}
|
||||
max = score
|
||||
}
|
||||
}
|
||||
|
||||
// Only RedHat, Oracle and Amazon has severity data in advisory.
|
||||
for _, adv := range v.DistroAdvisories {
|
||||
if adv.Severity != "" {
|
||||
score := severityToV2ScoreRoughly(adv.Severity)
|
||||
if max < score {
|
||||
value = CveContentCvss{
|
||||
Type: "Vendor",
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: score,
|
||||
CalculatedBySeverity: true,
|
||||
Vector: "-",
|
||||
Severity: adv.Severity,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// CveContentCvss has CveContentType and Cvss2
|
||||
type CveContentCvss struct {
|
||||
Type CveContentType
|
||||
Value Cvss
|
||||
}
|
||||
|
||||
// CvssType Represent the type of CVSS
|
||||
type CvssType string
|
||||
|
||||
const (
|
||||
// CVSS2 means CVSS vesion2
|
||||
CVSS2 CvssType = "2"
|
||||
|
||||
// CVSS3 means CVSS vesion3
|
||||
CVSS3 CvssType = "3"
|
||||
)
|
||||
|
||||
// Cvss has CVSS Score
|
||||
type Cvss struct {
|
||||
Type CvssType
|
||||
Score float64
|
||||
CalculatedBySeverity bool
|
||||
Vector string
|
||||
Severity string
|
||||
}
|
||||
|
||||
// Format CVSS Score and Vector
|
||||
func (c Cvss) Format() string {
|
||||
switch c.Type {
|
||||
case CVSS2:
|
||||
return fmt.Sprintf("%3.1f/%s", c.Score, c.Vector)
|
||||
case CVSS3:
|
||||
return fmt.Sprintf("%3.1f/CVSS:3.0/%s", c.Score, c.Vector)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func cvss2ScoreToSeverity(score float64) string {
|
||||
if 7.0 <= score {
|
||||
return "HIGH"
|
||||
} else if 4.0 <= score {
|
||||
return "MEDIUM"
|
||||
}
|
||||
return "LOW"
|
||||
}
|
||||
|
||||
// Amazon Linux Security Advisory
|
||||
// Critical, Important, Medium, Low
|
||||
// https://alas.aws.amazon.com/
|
||||
//
|
||||
// RedHat, Oracle OVAL
|
||||
// Critical, Important, Moderate, Low
|
||||
// https://access.redhat.com/security/updates/classification
|
||||
//
|
||||
// Ubuntu OVAL
|
||||
// Critical, High, Medium, Low
|
||||
// https://wiki.ubuntu.com/Bugs/Importance
|
||||
// https://people.canonical.com/~ubuntu-security/cve/priority.html
|
||||
func severityToV2ScoreRoughly(severity string) float64 {
|
||||
switch strings.ToUpper(severity) {
|
||||
case "CRITICAL":
|
||||
return 10.0
|
||||
case "IMPORTANT", "HIGH":
|
||||
return 8.9
|
||||
case "MODERATE", "MEDIUM":
|
||||
return 6.9
|
||||
case "LOW":
|
||||
return 3.9
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// CveContentCvss3 has CveContentType and Cvss3
|
||||
// type CveContentCvss3 struct {
|
||||
// Type CveContentType
|
||||
// Value Cvss3
|
||||
// }
|
||||
|
||||
// Cvss3 has CVSS v3 Score, Vector and Severity
|
||||
// type Cvss3 struct {
|
||||
// Score float64
|
||||
// Vector string
|
||||
// Severity string
|
||||
// }
|
||||
|
||||
// Format CVSS Score and Vector
|
||||
// func (c Cvss3) Format() string {
|
||||
// return fmt.Sprintf("%3.1f/CVSS:3.0/%s", c.Score, c.Vector)
|
||||
// }
|
||||
|
||||
// func cvss3ScoreToSeverity(score float64) string {
|
||||
// if 9.0 <= score {
|
||||
// return "CRITICAL"
|
||||
// } else if 7.0 <= score {
|
||||
// return "HIGH"
|
||||
// } else if 4.0 <= score {
|
||||
// return "MEDIUM"
|
||||
// }
|
||||
// return "LOW"
|
||||
// }
|
||||
|
||||
// FormatMaxCvssScore returns Max CVSS Score
|
||||
func (v VulnInfo) FormatMaxCvssScore() string {
|
||||
v2Max := v.MaxCvss2Score()
|
||||
v3Max := v.MaxCvss3Score()
|
||||
if v2Max.Value.Score <= v3Max.Value.Score {
|
||||
return fmt.Sprintf("%3.1f %s (%s)",
|
||||
v3Max.Value.Score,
|
||||
strings.ToUpper(v3Max.Value.Severity),
|
||||
v3Max.Type)
|
||||
}
|
||||
return fmt.Sprintf("%3.1f %s (%s)",
|
||||
v2Max.Value.Score,
|
||||
strings.ToUpper(v2Max.Value.Severity),
|
||||
v2Max.Type)
|
||||
}
|
||||
|
||||
// Cvss2CalcURL returns CVSS v2 caluclator's URL
|
||||
func (v VulnInfo) Cvss2CalcURL() string {
|
||||
return "https://nvd.nist.gov/vuln-metrics/cvss/v2-calculator?name=" + v.CveID
|
||||
}
|
||||
|
||||
// Cvss3CalcURL returns CVSS v3 caluclator's URL
|
||||
func (v VulnInfo) Cvss3CalcURL() string {
|
||||
return "https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator?name=" + v.CveID
|
||||
}
|
||||
|
||||
// VendorLinks returns links of vendor support's URL
|
||||
func (v VulnInfo) VendorLinks(family string) map[string]string {
|
||||
links := map[string]string{}
|
||||
switch family {
|
||||
case config.RedHat, config.CentOS:
|
||||
links["RHEL-CVE"] = "https://access.redhat.com/security/cve/" + v.CveID
|
||||
for _, advisory := range v.DistroAdvisories {
|
||||
aidURL := strings.Replace(advisory.AdvisoryID, ":", "-", -1)
|
||||
links[advisory.AdvisoryID] = fmt.Sprintf("https://rhn.redhat.com/errata/%s.html", aidURL)
|
||||
}
|
||||
return links
|
||||
case config.Oracle:
|
||||
links["Oracle-CVE"] = fmt.Sprintf("https://linux.oracle.com/cve/%s.html", v.CveID)
|
||||
for _, advisory := range v.DistroAdvisories {
|
||||
links[advisory.AdvisoryID] =
|
||||
fmt.Sprintf("https://linux.oracle.com/errata/%s.html", advisory.AdvisoryID)
|
||||
}
|
||||
return links
|
||||
case config.Amazon:
|
||||
links["RHEL-CVE"] = "https://access.redhat.com/security/cve/" + v.CveID
|
||||
for _, advisory := range v.DistroAdvisories {
|
||||
links[advisory.AdvisoryID] =
|
||||
fmt.Sprintf("https://alas.aws.amazon.com/%s.html", advisory.AdvisoryID)
|
||||
}
|
||||
return links
|
||||
case config.Ubuntu:
|
||||
links["Ubuntu-CVE"] = "http://people.ubuntu.com/~ubuntu-security/cve/" + v.CveID
|
||||
return links
|
||||
case config.Debian:
|
||||
links["Debian-CVE"] = "https://security-tracker.debian.org/tracker/" + v.CveID
|
||||
case config.FreeBSD:
|
||||
for _, advisory := range v.DistroAdvisories {
|
||||
links["FreeBSD-VuXML"] = fmt.Sprintf("https://vuxml.freebsd.org/freebsd/%s.html", advisory.AdvisoryID)
|
||||
|
||||
}
|
||||
return links
|
||||
}
|
||||
return links
|
||||
}
|
||||
|
||||
// NilToEmpty set nil slice or map fields to empty to avoid null in JSON
|
||||
func (v *VulnInfo) NilToEmpty() *VulnInfo {
|
||||
if v.CpeNames == nil {
|
||||
v.CpeNames = []string{}
|
||||
}
|
||||
if v.DistroAdvisories == nil {
|
||||
v.DistroAdvisories = []DistroAdvisory{}
|
||||
}
|
||||
if v.AffectedPackages == nil {
|
||||
v.AffectedPackages = PackageStatuses{}
|
||||
}
|
||||
if v.CveContents == nil {
|
||||
v.CveContents = NewCveContents()
|
||||
}
|
||||
for key := range v.CveContents {
|
||||
if v.CveContents[key].Cpes == nil {
|
||||
cont := v.CveContents[key]
|
||||
cont.Cpes = []Cpe{}
|
||||
v.CveContents[key] = cont
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// DistroAdvisory has Amazon Linux, RHEL, FreeBSD Security Advisory information.
|
||||
type DistroAdvisory struct {
|
||||
AdvisoryID string
|
||||
Severity string
|
||||
Issued time.Time
|
||||
Updated time.Time
|
||||
Description string
|
||||
}
|
||||
|
||||
// Format the distro advisory information
|
||||
func (p DistroAdvisory) Format() string {
|
||||
if p.AdvisoryID == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
var delim bytes.Buffer
|
||||
for i := 0; i < len(p.AdvisoryID); i++ {
|
||||
delim.WriteString("-")
|
||||
}
|
||||
buf := []string{p.AdvisoryID, delim.String(), p.Description}
|
||||
return strings.Join(buf, "\n")
|
||||
}
|
||||
|
||||
// Confidence is a ranking how confident the CVE-ID was deteted correctly
|
||||
// Score: 0 - 100
|
||||
type Confidence struct {
|
||||
Score int
|
||||
DetectionMethod DetectionMethod
|
||||
}
|
||||
|
||||
func (c Confidence) String() string {
|
||||
return fmt.Sprintf("%d / %s", c.Score, c.DetectionMethod)
|
||||
}
|
||||
|
||||
// DetectionMethod indicates
|
||||
// - How to detect the CveID
|
||||
// - How to get the changelog difference between installed and candidate version
|
||||
type DetectionMethod string
|
||||
|
||||
const (
|
||||
// CpeNameMatchStr is a String representation of CpeNameMatch
|
||||
CpeNameMatchStr = "CpeNameMatch"
|
||||
|
||||
// YumUpdateSecurityMatchStr is a String representation of YumUpdateSecurityMatch
|
||||
YumUpdateSecurityMatchStr = "YumUpdateSecurityMatch"
|
||||
|
||||
// PkgAuditMatchStr is a String representation of PkgAuditMatch
|
||||
PkgAuditMatchStr = "PkgAuditMatch"
|
||||
|
||||
// OvalMatchStr is a String representation of OvalMatch
|
||||
OvalMatchStr = "OvalMatch"
|
||||
|
||||
// ChangelogExactMatchStr is a String representation of ChangelogExactMatch
|
||||
ChangelogExactMatchStr = "ChangelogExactMatch"
|
||||
|
||||
// ChangelogLenientMatchStr is a String representation of ChangelogLenientMatch
|
||||
ChangelogLenientMatchStr = "ChangelogLenientMatch"
|
||||
|
||||
// FailedToGetChangelog is a String representation of FailedToGetChangelog
|
||||
FailedToGetChangelog = "FailedToGetChangelog"
|
||||
|
||||
// FailedToFindVersionInChangelog is a String representation of FailedToFindVersionInChangelog
|
||||
FailedToFindVersionInChangelog = "FailedToFindVersionInChangelog"
|
||||
)
|
||||
|
||||
var (
|
||||
// CpeNameMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
CpeNameMatch = Confidence{100, CpeNameMatchStr}
|
||||
|
||||
// YumUpdateSecurityMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
YumUpdateSecurityMatch = Confidence{100, YumUpdateSecurityMatchStr}
|
||||
|
||||
// PkgAuditMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
PkgAuditMatch = Confidence{100, PkgAuditMatchStr}
|
||||
|
||||
// OvalMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
OvalMatch = Confidence{100, OvalMatchStr}
|
||||
|
||||
// ChangelogExactMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
ChangelogExactMatch = Confidence{95, ChangelogExactMatchStr}
|
||||
|
||||
// ChangelogLenientMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
ChangelogLenientMatch = Confidence{50, ChangelogLenientMatchStr}
|
||||
)
|
||||
@@ -1,936 +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 models
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTitles(t *testing.T) {
|
||||
type in struct {
|
||||
lang string
|
||||
cont VulnInfo
|
||||
}
|
||||
var tests = []struct {
|
||||
in in
|
||||
out []CveContentStr
|
||||
}{
|
||||
// lang: ja
|
||||
{
|
||||
in: in{
|
||||
lang: "ja",
|
||||
cont: VulnInfo{
|
||||
CveContents: CveContents{
|
||||
JVN: {
|
||||
Type: JVN,
|
||||
Title: "Title1",
|
||||
},
|
||||
RedHat: {
|
||||
Type: RedHat,
|
||||
Summary: "Summary RedHat",
|
||||
},
|
||||
NVD: {
|
||||
Type: NVD,
|
||||
Summary: "Summary NVD",
|
||||
// Severity is NIOT included in NVD
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
out: []CveContentStr{
|
||||
{
|
||||
Type: JVN,
|
||||
Value: "Title1",
|
||||
},
|
||||
{
|
||||
Type: NVD,
|
||||
Value: "Summary NVD",
|
||||
},
|
||||
{
|
||||
Type: RedHat,
|
||||
Value: "Summary RedHat",
|
||||
},
|
||||
},
|
||||
},
|
||||
// lang: en
|
||||
{
|
||||
in: in{
|
||||
lang: "en",
|
||||
cont: VulnInfo{
|
||||
CveContents: CveContents{
|
||||
JVN: {
|
||||
Type: JVN,
|
||||
Title: "Title1",
|
||||
},
|
||||
RedHat: {
|
||||
Type: RedHat,
|
||||
Summary: "Summary RedHat",
|
||||
},
|
||||
NVD: {
|
||||
Type: NVD,
|
||||
Summary: "Summary NVD",
|
||||
// Severity is NIOT included in NVD
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
out: []CveContentStr{
|
||||
{
|
||||
Type: NVD,
|
||||
Value: "Summary NVD",
|
||||
},
|
||||
{
|
||||
Type: RedHat,
|
||||
Value: "Summary RedHat",
|
||||
},
|
||||
},
|
||||
},
|
||||
// lang: empty
|
||||
{
|
||||
in: in{
|
||||
lang: "en",
|
||||
cont: VulnInfo{},
|
||||
},
|
||||
out: []CveContentStr{
|
||||
{
|
||||
Type: Unknown,
|
||||
Value: "-",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
actual := tt.in.cont.Titles(tt.in.lang, "redhat")
|
||||
if !reflect.DeepEqual(tt.out, actual) {
|
||||
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSummaries(t *testing.T) {
|
||||
type in struct {
|
||||
lang string
|
||||
cont VulnInfo
|
||||
}
|
||||
var tests = []struct {
|
||||
in in
|
||||
out []CveContentStr
|
||||
}{
|
||||
// lang: ja
|
||||
{
|
||||
in: in{
|
||||
lang: "ja",
|
||||
cont: VulnInfo{
|
||||
CveContents: CveContents{
|
||||
JVN: {
|
||||
Type: JVN,
|
||||
Title: "Title JVN",
|
||||
Summary: "Summary JVN",
|
||||
},
|
||||
RedHat: {
|
||||
Type: RedHat,
|
||||
Summary: "Summary RedHat",
|
||||
},
|
||||
NVD: {
|
||||
Type: NVD,
|
||||
Summary: "Summary NVD",
|
||||
// Severity is NIOT included in NVD
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
out: []CveContentStr{
|
||||
{
|
||||
Type: JVN,
|
||||
Value: "Title JVN\nSummary JVN",
|
||||
},
|
||||
{
|
||||
Type: NVD,
|
||||
Value: "Summary NVD",
|
||||
},
|
||||
{
|
||||
Type: RedHat,
|
||||
Value: "Summary RedHat",
|
||||
},
|
||||
},
|
||||
},
|
||||
// lang: en
|
||||
{
|
||||
in: in{
|
||||
lang: "en",
|
||||
cont: VulnInfo{
|
||||
CveContents: CveContents{
|
||||
JVN: {
|
||||
Type: JVN,
|
||||
Title: "Title JVN",
|
||||
Summary: "Summary JVN",
|
||||
},
|
||||
RedHat: {
|
||||
Type: RedHat,
|
||||
Summary: "Summary RedHat",
|
||||
},
|
||||
NVD: {
|
||||
Type: NVD,
|
||||
Summary: "Summary NVD",
|
||||
// Severity is NIOT included in NVD
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
out: []CveContentStr{
|
||||
{
|
||||
Type: NVD,
|
||||
Value: "Summary NVD",
|
||||
},
|
||||
{
|
||||
Type: RedHat,
|
||||
Value: "Summary RedHat",
|
||||
},
|
||||
},
|
||||
},
|
||||
// lang: empty
|
||||
{
|
||||
in: in{
|
||||
lang: "en",
|
||||
cont: VulnInfo{},
|
||||
},
|
||||
out: []CveContentStr{
|
||||
{
|
||||
Type: Unknown,
|
||||
Value: "-",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
actual := tt.in.cont.Summaries(tt.in.lang, "redhat")
|
||||
if !reflect.DeepEqual(tt.out, actual) {
|
||||
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCountGroupBySeverity(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in VulnInfos
|
||||
out map[string]int
|
||||
}{
|
||||
{
|
||||
in: VulnInfos{
|
||||
"CVE-2017-0002": {
|
||||
CveID: "CVE-2017-0002",
|
||||
CveContents: CveContents{
|
||||
NVD: {
|
||||
Type: NVD,
|
||||
Cvss2Score: 6.0,
|
||||
},
|
||||
RedHat: {
|
||||
Type: RedHat,
|
||||
Cvss2Score: 7.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
"CVE-2017-0003": {
|
||||
CveID: "CVE-2017-0003",
|
||||
CveContents: CveContents{
|
||||
NVD: {
|
||||
Type: NVD,
|
||||
Cvss2Score: 2.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
"CVE-2017-0004": {
|
||||
CveID: "CVE-2017-0004",
|
||||
CveContents: CveContents{
|
||||
NVD: {
|
||||
Type: NVD,
|
||||
Cvss2Score: 5.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
"CVE-2017-0005": {
|
||||
CveID: "CVE-2017-0005",
|
||||
},
|
||||
},
|
||||
out: map[string]int{
|
||||
"High": 1,
|
||||
"Medium": 1,
|
||||
"Low": 1,
|
||||
"Unknown": 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
actual := tt.in.CountGroupBySeverity()
|
||||
for k := range tt.out {
|
||||
if tt.out[k] != actual[k] {
|
||||
t.Errorf("\nexpected %s: %d\n actual %d\n",
|
||||
k, tt.out[k], actual[k])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestToSortedSlice(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in VulnInfos
|
||||
out []VulnInfo
|
||||
}{
|
||||
{
|
||||
in: VulnInfos{
|
||||
"CVE-2017-0002": {
|
||||
CveID: "CVE-2017-0002",
|
||||
CveContents: CveContents{
|
||||
NVD: {
|
||||
Type: NVD,
|
||||
Cvss2Score: 6.0,
|
||||
},
|
||||
RedHat: {
|
||||
Type: RedHat,
|
||||
Cvss3Score: 7.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
"CVE-2017-0001": {
|
||||
CveID: "CVE-2017-0001",
|
||||
CveContents: CveContents{
|
||||
NVD: {
|
||||
Type: NVD,
|
||||
Cvss2Score: 7.0,
|
||||
},
|
||||
RedHat: {
|
||||
Type: RedHat,
|
||||
Cvss3Score: 8.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
out: []VulnInfo{
|
||||
{
|
||||
CveID: "CVE-2017-0001",
|
||||
CveContents: CveContents{
|
||||
NVD: {
|
||||
Type: NVD,
|
||||
Cvss2Score: 7.0,
|
||||
},
|
||||
RedHat: {
|
||||
Type: RedHat,
|
||||
Cvss3Score: 8.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
CveID: "CVE-2017-0002",
|
||||
CveContents: CveContents{
|
||||
NVD: {
|
||||
Type: NVD,
|
||||
Cvss2Score: 6.0,
|
||||
},
|
||||
RedHat: {
|
||||
Type: RedHat,
|
||||
Cvss3Score: 7.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// When max scores are the same, sort by CVE-ID
|
||||
{
|
||||
in: VulnInfos{
|
||||
"CVE-2017-0002": {
|
||||
CveID: "CVE-2017-0002",
|
||||
CveContents: CveContents{
|
||||
NVD: {
|
||||
Type: NVD,
|
||||
Cvss2Score: 6.0,
|
||||
},
|
||||
RedHat: {
|
||||
Type: RedHat,
|
||||
Cvss3Score: 7.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
"CVE-2017-0001": {
|
||||
CveID: "CVE-2017-0001",
|
||||
CveContents: CveContents{
|
||||
RedHat: {
|
||||
Type: RedHat,
|
||||
Cvss2Score: 7.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
out: []VulnInfo{
|
||||
{
|
||||
CveID: "CVE-2017-0001",
|
||||
CveContents: CveContents{
|
||||
RedHat: {
|
||||
Type: RedHat,
|
||||
Cvss2Score: 7.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
CveID: "CVE-2017-0002",
|
||||
CveContents: CveContents{
|
||||
NVD: {
|
||||
Type: NVD,
|
||||
Cvss2Score: 6.0,
|
||||
},
|
||||
RedHat: {
|
||||
Type: RedHat,
|
||||
Cvss3Score: 7.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// When there are no cvss scores, sort by severity
|
||||
{
|
||||
in: VulnInfos{
|
||||
"CVE-2017-0002": {
|
||||
CveID: "CVE-2017-0002",
|
||||
CveContents: CveContents{
|
||||
Ubuntu: {
|
||||
Type: Ubuntu,
|
||||
Severity: "High",
|
||||
},
|
||||
},
|
||||
},
|
||||
"CVE-2017-0001": {
|
||||
CveID: "CVE-2017-0001",
|
||||
CveContents: CveContents{
|
||||
Ubuntu: {
|
||||
Type: Ubuntu,
|
||||
Severity: "Low",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
out: []VulnInfo{
|
||||
{
|
||||
CveID: "CVE-2017-0002",
|
||||
CveContents: CveContents{
|
||||
Ubuntu: {
|
||||
Type: Ubuntu,
|
||||
Severity: "High",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
CveID: "CVE-2017-0001",
|
||||
CveContents: CveContents{
|
||||
Ubuntu: {
|
||||
Type: Ubuntu,
|
||||
Severity: "Low",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
actual := tt.in.ToSortedSlice()
|
||||
if !reflect.DeepEqual(tt.out, actual) {
|
||||
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCvss2Scores(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in VulnInfo
|
||||
out []CveContentCvss
|
||||
}{
|
||||
{
|
||||
in: VulnInfo{
|
||||
CveContents: CveContents{
|
||||
JVN: {
|
||||
Type: JVN,
|
||||
Severity: "HIGH",
|
||||
Cvss2Score: 8.2,
|
||||
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
|
||||
},
|
||||
RedHat: {
|
||||
Type: RedHat,
|
||||
Severity: "HIGH",
|
||||
Cvss2Score: 8.0,
|
||||
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
|
||||
},
|
||||
NVD: {
|
||||
Type: NVD,
|
||||
Cvss2Score: 8.1,
|
||||
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
|
||||
// Severity is NIOT included in NVD
|
||||
},
|
||||
},
|
||||
},
|
||||
out: []CveContentCvss{
|
||||
{
|
||||
Type: NVD,
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: 8.1,
|
||||
Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: RedHat,
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: 8.0,
|
||||
Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: JVN,
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: 8.2,
|
||||
Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// Empty
|
||||
{
|
||||
in: VulnInfo{},
|
||||
out: nil,
|
||||
},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
actual := tt.in.Cvss2Scores()
|
||||
if !reflect.DeepEqual(tt.out, actual) {
|
||||
t.Errorf("[%d] expected: %v\n actual: %v\n", i, tt.out, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxCvss2Scores(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in VulnInfo
|
||||
out CveContentCvss
|
||||
}{
|
||||
{
|
||||
in: VulnInfo{
|
||||
CveContents: CveContents{
|
||||
JVN: {
|
||||
Type: JVN,
|
||||
Severity: "HIGH",
|
||||
Cvss2Score: 8.2,
|
||||
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
|
||||
},
|
||||
RedHat: {
|
||||
Type: RedHat,
|
||||
Severity: "HIGH",
|
||||
Cvss2Score: 8.0,
|
||||
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
|
||||
},
|
||||
NVD: {
|
||||
Type: NVD,
|
||||
Cvss2Score: 8.1,
|
||||
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
|
||||
// Severity is NIOT included in NVD
|
||||
},
|
||||
},
|
||||
},
|
||||
out: CveContentCvss{
|
||||
Type: JVN,
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: 8.2,
|
||||
Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
// Severity in OVAL
|
||||
{
|
||||
in: VulnInfo{
|
||||
CveContents: CveContents{
|
||||
Ubuntu: {
|
||||
Type: Ubuntu,
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
out: CveContentCvss{
|
||||
Type: Ubuntu,
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: 8.9,
|
||||
CalculatedBySeverity: true,
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
// Empty
|
||||
{
|
||||
in: VulnInfo{},
|
||||
out: CveContentCvss{
|
||||
Type: Unknown,
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: 0.0,
|
||||
Vector: "",
|
||||
Severity: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
actual := tt.in.MaxCvss2Score()
|
||||
if !reflect.DeepEqual(tt.out, actual) {
|
||||
t.Errorf("[%d] expected: %v\n actual: %v\n", i, tt.out, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCvss3Scores(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in VulnInfo
|
||||
out []CveContentCvss
|
||||
}{
|
||||
{
|
||||
in: VulnInfo{
|
||||
CveContents: CveContents{
|
||||
RedHat: {
|
||||
Type: RedHat,
|
||||
Severity: "HIGH",
|
||||
Cvss3Score: 8.0,
|
||||
Cvss3Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
|
||||
},
|
||||
NVD: {
|
||||
Type: NVD,
|
||||
Cvss3Score: 8.1,
|
||||
Cvss3Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
|
||||
// Severity is NIOT included in NVD
|
||||
},
|
||||
},
|
||||
},
|
||||
out: []CveContentCvss{
|
||||
{
|
||||
Type: RedHat,
|
||||
Value: Cvss{
|
||||
Type: CVSS3,
|
||||
Score: 8.0,
|
||||
Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// Empty
|
||||
{
|
||||
in: VulnInfo{},
|
||||
out: nil,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
actual := tt.in.Cvss3Scores()
|
||||
if !reflect.DeepEqual(tt.out, actual) {
|
||||
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxCvss3Scores(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in VulnInfo
|
||||
out CveContentCvss
|
||||
}{
|
||||
{
|
||||
in: VulnInfo{
|
||||
CveContents: CveContents{
|
||||
RedHat: {
|
||||
Type: RedHat,
|
||||
Severity: "HIGH",
|
||||
Cvss3Score: 8.0,
|
||||
Cvss3Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
|
||||
},
|
||||
},
|
||||
},
|
||||
out: CveContentCvss{
|
||||
Type: RedHat,
|
||||
Value: Cvss{
|
||||
Type: CVSS3,
|
||||
Score: 8.0,
|
||||
Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
// Empty
|
||||
{
|
||||
in: VulnInfo{},
|
||||
out: CveContentCvss{
|
||||
Type: Unknown,
|
||||
Value: Cvss{
|
||||
Type: CVSS3,
|
||||
Score: 0.0,
|
||||
Vector: "",
|
||||
Severity: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
actual := tt.in.MaxCvss3Score()
|
||||
if !reflect.DeepEqual(tt.out, actual) {
|
||||
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxCvssScores(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in VulnInfo
|
||||
out CveContentCvss
|
||||
}{
|
||||
{
|
||||
in: VulnInfo{
|
||||
CveContents: CveContents{
|
||||
NVD: {
|
||||
Type: NVD,
|
||||
Cvss3Score: 7.0,
|
||||
},
|
||||
RedHat: {
|
||||
Type: RedHat,
|
||||
Cvss2Score: 8.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
out: CveContentCvss{
|
||||
Type: RedHat,
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: 8.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
in: VulnInfo{
|
||||
CveContents: CveContents{
|
||||
RedHat: {
|
||||
Type: RedHat,
|
||||
Cvss3Score: 8.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
out: CveContentCvss{
|
||||
Type: RedHat,
|
||||
Value: Cvss{
|
||||
Type: CVSS3,
|
||||
Score: 8.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
//2
|
||||
{
|
||||
in: VulnInfo{
|
||||
CveContents: CveContents{
|
||||
Ubuntu: {
|
||||
Type: Ubuntu,
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
out: CveContentCvss{
|
||||
Type: Ubuntu,
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: 8.9,
|
||||
CalculatedBySeverity: true,
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
//3
|
||||
{
|
||||
in: VulnInfo{
|
||||
CveContents: CveContents{
|
||||
Ubuntu: {
|
||||
Type: Ubuntu,
|
||||
Severity: "MEDIUM",
|
||||
},
|
||||
NVD: {
|
||||
Type: NVD,
|
||||
Cvss2Score: 7.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
out: CveContentCvss{
|
||||
Type: NVD,
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: 7.0,
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
//4
|
||||
{
|
||||
in: VulnInfo{
|
||||
DistroAdvisories: []DistroAdvisory{
|
||||
{
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
out: CveContentCvss{
|
||||
Type: "Vendor",
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: 8.9,
|
||||
CalculatedBySeverity: true,
|
||||
Vector: "-",
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
in: VulnInfo{
|
||||
CveContents: CveContents{
|
||||
Ubuntu: {
|
||||
Type: Ubuntu,
|
||||
Severity: "MEDIUM",
|
||||
},
|
||||
NVD: {
|
||||
Type: NVD,
|
||||
Cvss2Score: 4.0,
|
||||
},
|
||||
},
|
||||
DistroAdvisories: []DistroAdvisory{
|
||||
{
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
out: CveContentCvss{
|
||||
Type: NVD,
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: 4,
|
||||
Severity: "MEDIUM",
|
||||
},
|
||||
},
|
||||
},
|
||||
// Empty
|
||||
{
|
||||
in: VulnInfo{},
|
||||
out: CveContentCvss{
|
||||
Type: Unknown,
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
actual := tt.in.MaxCvssScore()
|
||||
if !reflect.DeepEqual(tt.out, actual) {
|
||||
t.Errorf("\n[%d] expected: %v\n actual: %v\n", i, tt.out, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatMaxCvssScore(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in VulnInfo
|
||||
out string
|
||||
}{
|
||||
{
|
||||
in: VulnInfo{
|
||||
CveContents: CveContents{
|
||||
JVN: {
|
||||
Type: JVN,
|
||||
Severity: "HIGH",
|
||||
Cvss2Score: 8.3,
|
||||
},
|
||||
RedHat: {
|
||||
Type: RedHat,
|
||||
Severity: "HIGH",
|
||||
Cvss3Score: 8.0,
|
||||
},
|
||||
NVD: {
|
||||
Type: NVD,
|
||||
Cvss2Score: 8.1,
|
||||
// Severity is NIOT included in NVD
|
||||
},
|
||||
},
|
||||
},
|
||||
out: "8.3 HIGH (jvn)",
|
||||
},
|
||||
{
|
||||
in: VulnInfo{
|
||||
CveContents: CveContents{
|
||||
JVN: {
|
||||
Type: JVN,
|
||||
Severity: "HIGH",
|
||||
Cvss2Score: 8.3,
|
||||
},
|
||||
RedHat: {
|
||||
Type: RedHat,
|
||||
Severity: "HIGH",
|
||||
Cvss2Score: 8.0,
|
||||
Cvss3Score: 9.9,
|
||||
},
|
||||
NVD: {
|
||||
Type: NVD,
|
||||
Cvss2Score: 8.1,
|
||||
},
|
||||
},
|
||||
},
|
||||
out: "9.9 HIGH (redhat)",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
actual := tt.in.FormatMaxCvssScore()
|
||||
if !reflect.DeepEqual(tt.out, actual) {
|
||||
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSortPackageStatues(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in PackageStatuses
|
||||
out PackageStatuses
|
||||
}{
|
||||
{
|
||||
in: PackageStatuses{
|
||||
{Name: "b"},
|
||||
{Name: "a"},
|
||||
},
|
||||
out: PackageStatuses{
|
||||
{Name: "a"},
|
||||
{Name: "b"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt.in.Sort()
|
||||
if !reflect.DeepEqual(tt.in, tt.out) {
|
||||
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, tt.in)
|
||||
}
|
||||
}
|
||||
}
|
||||
266
oval/debian.go
@@ -1,266 +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 oval
|
||||
|
||||
import (
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
|
||||
)
|
||||
|
||||
// DebianBase is the base struct of Debian and Ubuntu
|
||||
type DebianBase struct {
|
||||
Base
|
||||
}
|
||||
|
||||
func (o DebianBase) update(r *models.ScanResult, defPacks defPacks) {
|
||||
ovalContent := *o.convertToModel(&defPacks.def)
|
||||
ovalContent.Type = models.NewCveContentType(o.family)
|
||||
vinfo, ok := r.ScannedCves[defPacks.def.Debian.CveID]
|
||||
if !ok {
|
||||
util.Log.Debugf("%s is newly detected by OVAL", defPacks.def.Debian.CveID)
|
||||
vinfo = models.VulnInfo{
|
||||
CveID: defPacks.def.Debian.CveID,
|
||||
Confidence: models.OvalMatch,
|
||||
CveContents: models.NewCveContents(ovalContent),
|
||||
}
|
||||
} else {
|
||||
cveContents := vinfo.CveContents
|
||||
ctype := models.NewCveContentType(o.family)
|
||||
if _, ok := vinfo.CveContents[ctype]; ok {
|
||||
util.Log.Debugf("%s OVAL will be overwritten",
|
||||
defPacks.def.Debian.CveID)
|
||||
} else {
|
||||
util.Log.Debugf("%s is also detected by OVAL",
|
||||
defPacks.def.Debian.CveID)
|
||||
cveContents = models.CveContents{}
|
||||
}
|
||||
if vinfo.Confidence.Score < models.OvalMatch.Score {
|
||||
vinfo.Confidence = models.OvalMatch
|
||||
}
|
||||
cveContents[ctype] = ovalContent
|
||||
vinfo.CveContents = cveContents
|
||||
}
|
||||
|
||||
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
|
||||
for _, pack := range vinfo.AffectedPackages {
|
||||
defPacks.actuallyAffectedPackNames[pack.Name] = true
|
||||
}
|
||||
vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family, r.Packages)
|
||||
vinfo.AffectedPackages.Sort()
|
||||
r.ScannedCves[defPacks.def.Debian.CveID] = vinfo
|
||||
}
|
||||
|
||||
func (o DebianBase) convertToModel(def *ovalmodels.Definition) *models.CveContent {
|
||||
var refs []models.Reference
|
||||
for _, r := range def.References {
|
||||
refs = append(refs, models.Reference{
|
||||
Link: r.RefURL,
|
||||
Source: r.Source,
|
||||
RefID: r.RefID,
|
||||
})
|
||||
}
|
||||
|
||||
return &models.CveContent{
|
||||
CveID: def.Debian.CveID,
|
||||
Title: def.Title,
|
||||
Summary: def.Description,
|
||||
Severity: def.Advisory.Severity,
|
||||
References: refs,
|
||||
}
|
||||
}
|
||||
|
||||
// Debian is the interface for Debian OVAL
|
||||
type Debian struct {
|
||||
DebianBase
|
||||
}
|
||||
|
||||
// NewDebian creates OVAL client for Debian
|
||||
func NewDebian() Debian {
|
||||
return Debian{
|
||||
DebianBase{
|
||||
Base{
|
||||
family: config.Debian,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FillWithOval returns scan result after updating CVE info by OVAL
|
||||
func (o Debian) FillWithOval(r *models.ScanResult) (err error) {
|
||||
|
||||
//Debian's uname gives both of kernel release(uname -r), version(kernel-image version)
|
||||
linuxImage := "linux-image-" + r.RunningKernel.Release
|
||||
// Add linux and set the version of running kernel to search OVAL.
|
||||
if r.Container.ContainerID == "" {
|
||||
r.Packages["linux"] = models.Package{
|
||||
Name: "linux",
|
||||
Version: r.RunningKernel.Version,
|
||||
}
|
||||
}
|
||||
|
||||
var relatedDefs ovalResult
|
||||
if o.isFetchViaHTTP() {
|
||||
if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if relatedDefs, err = getDefsByPackNameFromOvalDB(o.family, r.Release, r.Packages); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
delete(r.Packages, "linux")
|
||||
|
||||
for _, defPacks := range relatedDefs.entries {
|
||||
// Remove linux added above to search for oval
|
||||
// linux is not a real package name (key of affected packages in OVAL)
|
||||
if _, ok := defPacks.actuallyAffectedPackNames["linux"]; ok {
|
||||
defPacks.actuallyAffectedPackNames[linuxImage] = true
|
||||
delete(defPacks.actuallyAffectedPackNames, "linux")
|
||||
for i, p := range defPacks.def.AffectedPacks {
|
||||
if p.Name == "linux" {
|
||||
p.Name = linuxImage
|
||||
defPacks.def.AffectedPacks[i] = p
|
||||
}
|
||||
}
|
||||
}
|
||||
o.update(r, defPacks)
|
||||
}
|
||||
|
||||
for _, vuln := range r.ScannedCves {
|
||||
if cont, ok := vuln.CveContents[models.Debian]; ok {
|
||||
cont.SourceLink = "https://security-tracker.debian.org/tracker/" + cont.CveID
|
||||
vuln.CveContents[models.Debian] = cont
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ubuntu is the interface for Debian OVAL
|
||||
type Ubuntu struct {
|
||||
DebianBase
|
||||
}
|
||||
|
||||
// NewUbuntu creates OVAL client for Debian
|
||||
func NewUbuntu() Ubuntu {
|
||||
return Ubuntu{
|
||||
DebianBase{
|
||||
Base{
|
||||
family: config.Ubuntu,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FillWithOval returns scan result after updating CVE info by OVAL
|
||||
func (o Ubuntu) FillWithOval(r *models.ScanResult) (err error) {
|
||||
ovalKernelImageNames := []string{
|
||||
"linux-aws",
|
||||
"linux-azure",
|
||||
"linux-flo",
|
||||
"linux-gcp",
|
||||
"linux-gke",
|
||||
"linux-goldfish",
|
||||
"linux-hwe",
|
||||
"linux-hwe-edge",
|
||||
"linux-kvm",
|
||||
"linux-mako",
|
||||
"linux-raspi2",
|
||||
"linux-snapdragon",
|
||||
}
|
||||
linuxImage := "linux-image-" + r.RunningKernel.Release
|
||||
|
||||
found := false
|
||||
if r.Container.ContainerID == "" {
|
||||
for _, n := range ovalKernelImageNames {
|
||||
if _, ok := r.Packages[n]; ok {
|
||||
v, ok := r.Packages[linuxImage]
|
||||
if ok {
|
||||
// Set running kernel version
|
||||
p := r.Packages[n]
|
||||
p.Version = v.Version
|
||||
p.NewVersion = v.NewVersion
|
||||
r.Packages[n] = p
|
||||
} else {
|
||||
util.Log.Warnf("Running kernel image %s is not found: %s",
|
||||
linuxImage, r.RunningKernel.Version)
|
||||
}
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
// linux-generic is described as "linux" in Ubuntu's oval.
|
||||
// Add "linux" and set the version of running kernel to search OVAL.
|
||||
v, ok := r.Packages[linuxImage]
|
||||
if ok {
|
||||
r.Packages["linux"] = models.Package{
|
||||
Name: "linux",
|
||||
Version: v.Version,
|
||||
NewVersion: v.NewVersion,
|
||||
}
|
||||
} else {
|
||||
util.Log.Warnf("%s is not found. Running: %s",
|
||||
linuxImage, r.RunningKernel.Release)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var relatedDefs ovalResult
|
||||
if o.isFetchViaHTTP() {
|
||||
if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if relatedDefs, err = getDefsByPackNameFromOvalDB(o.family, r.Release, r.Packages); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
delete(r.Packages, "linux")
|
||||
}
|
||||
|
||||
for _, defPacks := range relatedDefs.entries {
|
||||
|
||||
// Remove "linux" added above to search for oval
|
||||
// "linux" is not a real package name (key of affected packages in OVAL)
|
||||
if _, ok := defPacks.actuallyAffectedPackNames["linux"]; !found && ok {
|
||||
defPacks.actuallyAffectedPackNames[linuxImage] = true
|
||||
delete(defPacks.actuallyAffectedPackNames, "linux")
|
||||
for i, p := range defPacks.def.AffectedPacks {
|
||||
if p.Name == "linux" {
|
||||
p.Name = linuxImage
|
||||
defPacks.def.AffectedPacks[i] = p
|
||||
}
|
||||
}
|
||||
}
|
||||
o.update(r, defPacks)
|
||||
}
|
||||
|
||||
for _, vuln := range r.ScannedCves {
|
||||
if cont, ok := vuln.CveContents[models.Ubuntu]; ok {
|
||||
cont.SourceLink = "http://people.ubuntu.com/~ubuntu-security/cve/" + cont.CveID
|
||||
vuln.CveContents[models.Ubuntu] = cont
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,75 +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 oval
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
|
||||
)
|
||||
|
||||
func TestPackNamesOfUpdateDebian(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in models.ScanResult
|
||||
defPacks defPacks
|
||||
out models.ScanResult
|
||||
}{
|
||||
{
|
||||
in: models.ScanResult{
|
||||
ScannedCves: models.VulnInfos{
|
||||
"CVE-2000-1000": models.VulnInfo{
|
||||
AffectedPackages: models.PackageStatuses{{Name: "packA"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
defPacks: defPacks{
|
||||
def: ovalmodels.Definition{
|
||||
Debian: ovalmodels.Debian{
|
||||
CveID: "CVE-2000-1000",
|
||||
},
|
||||
},
|
||||
actuallyAffectedPackNames: map[string]bool{
|
||||
"packB": true,
|
||||
},
|
||||
},
|
||||
out: models.ScanResult{
|
||||
ScannedCves: models.VulnInfos{
|
||||
"CVE-2000-1000": models.VulnInfo{
|
||||
AffectedPackages: models.PackageStatuses{
|
||||
{Name: "packA"},
|
||||
{Name: "packB"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
util.Log = util.NewCustomLogger(config.ServerInfo{})
|
||||
for i, tt := range tests {
|
||||
Debian{}.update(&tt.in, tt.defPacks)
|
||||
e := tt.out.ScannedCves["CVE-2000-1000"].AffectedPackages
|
||||
a := tt.in.ScannedCves["CVE-2000-1000"].AffectedPackages
|
||||
if !reflect.DeepEqual(a, e) {
|
||||
t.Errorf("[%d] expected: %v\n actual: %v\n", i, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
150
oval/oval.go
@@ -1,150 +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 oval
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/kotakanbe/goval-dictionary/db"
|
||||
ovallog "github.com/kotakanbe/goval-dictionary/log"
|
||||
"github.com/parnurzeal/gorequest"
|
||||
)
|
||||
|
||||
// Client is the interface of OVAL client.
|
||||
type Client interface {
|
||||
CheckHTTPHealth() error
|
||||
FillWithOval(r *models.ScanResult) error
|
||||
|
||||
// CheckIfOvalFetched checks if oval entries are in DB by family, release.
|
||||
CheckIfOvalFetched(string, string) (bool, error)
|
||||
CheckIfOvalFresh(string, string) (bool, error)
|
||||
}
|
||||
|
||||
// Base is a base struct
|
||||
type Base struct {
|
||||
family string
|
||||
}
|
||||
|
||||
// CheckHTTPHealth do health check
|
||||
func (b Base) CheckHTTPHealth() error {
|
||||
if !b.isFetchViaHTTP() {
|
||||
return nil
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/health", config.Conf.OvalDBURL)
|
||||
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 fmt.Errorf("Failed to request to OVAL server. url: %s, errs: %v",
|
||||
url, errs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckIfOvalFetched checks if oval entries are in DB by family, release.
|
||||
func (b Base) CheckIfOvalFetched(osFamily, release string) (fetched bool, err error) {
|
||||
ovallog.Initialize(config.Conf.LogDir)
|
||||
if !b.isFetchViaHTTP() {
|
||||
var ovaldb db.DB
|
||||
if ovaldb, err = db.NewDB(
|
||||
osFamily,
|
||||
config.Conf.OvalDBType,
|
||||
config.Conf.OvalDBPath,
|
||||
config.Conf.DebugSQL,
|
||||
); err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer ovaldb.CloseDB()
|
||||
count, err := ovaldb.CountDefs(osFamily, release)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Failed to count OVAL defs: %s, %s, %v",
|
||||
osFamily, release, err)
|
||||
}
|
||||
return 0 < count, nil
|
||||
}
|
||||
|
||||
url, _ := util.URLPathJoin(config.Conf.OvalDBURL, "count", osFamily, release)
|
||||
resp, body, errs := gorequest.New().Get(url).End()
|
||||
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
|
||||
return false, fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v",
|
||||
errs, url, resp)
|
||||
}
|
||||
count := 0
|
||||
if err := json.Unmarshal([]byte(body), &count); err != nil {
|
||||
return false, fmt.Errorf("Failed to Unmarshall. body: %s, err: %s",
|
||||
body, err)
|
||||
}
|
||||
return 0 < count, nil
|
||||
}
|
||||
|
||||
// CheckIfOvalFresh checks if oval entries are fresh enough
|
||||
func (b Base) CheckIfOvalFresh(osFamily, release string) (ok bool, err error) {
|
||||
ovallog.Initialize(config.Conf.LogDir)
|
||||
var lastModified time.Time
|
||||
if !b.isFetchViaHTTP() {
|
||||
var ovaldb db.DB
|
||||
if ovaldb, err = db.NewDB(
|
||||
osFamily,
|
||||
config.Conf.OvalDBType,
|
||||
config.Conf.OvalDBPath,
|
||||
config.Conf.DebugSQL,
|
||||
); err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer ovaldb.CloseDB()
|
||||
lastModified = ovaldb.GetLastModified(osFamily, release)
|
||||
} else {
|
||||
url, _ := util.URLPathJoin(config.Conf.OvalDBURL, "lastmodified", osFamily, release)
|
||||
resp, body, errs := gorequest.New().Get(url).End()
|
||||
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
|
||||
return false, fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v",
|
||||
errs, url, resp)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal([]byte(body), &lastModified); err != nil {
|
||||
return false, fmt.Errorf("Failed to Unmarshall. body: %s, err: %s",
|
||||
body, err)
|
||||
}
|
||||
}
|
||||
|
||||
major := strings.Split(release, ".")[0]
|
||||
since := time.Now()
|
||||
since = since.AddDate(0, 0, -3)
|
||||
if lastModified.Before(since) {
|
||||
util.Log.Warnf("OVAL for %s %s is old, last modified is %s. It's recommended to update OVAL to improve scanning accuracy. How to update OVAL database, see https://github.com/kotakanbe/goval-dictionary#usage",
|
||||
osFamily, major, lastModified)
|
||||
return false, nil
|
||||
}
|
||||
util.Log.Infof("OVAL is fresh: %s %s ", osFamily, major)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (b Base) isFetchViaHTTP() bool {
|
||||
// Default value of OvalDBType is sqlite3
|
||||
return config.Conf.OvalDBURL != "" && config.Conf.OvalDBType == "sqlite3"
|
||||
}
|
||||
224
oval/redhat.go
@@ -1,224 +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 oval
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
|
||||
)
|
||||
|
||||
// RedHatBase is the base struct for RedHat and CentOS
|
||||
type RedHatBase struct {
|
||||
Base
|
||||
}
|
||||
|
||||
// FillWithOval returns scan result after updating CVE info by OVAL
|
||||
func (o RedHatBase) FillWithOval(r *models.ScanResult) (err error) {
|
||||
var relatedDefs ovalResult
|
||||
if o.isFetchViaHTTP() {
|
||||
if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if relatedDefs, err = getDefsByPackNameFromOvalDB(
|
||||
o.family, r.Release, r.Packages); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, defPacks := range relatedDefs.entries {
|
||||
o.update(r, defPacks)
|
||||
}
|
||||
|
||||
for _, vuln := range r.ScannedCves {
|
||||
switch models.NewCveContentType(o.family) {
|
||||
case models.RedHat:
|
||||
if cont, ok := vuln.CveContents[models.RedHat]; ok {
|
||||
cont.SourceLink = "https://access.redhat.com/security/cve/" + cont.CveID
|
||||
vuln.CveContents[models.RedHat] = cont
|
||||
}
|
||||
case models.Oracle:
|
||||
if cont, ok := vuln.CveContents[models.Oracle]; ok {
|
||||
cont.SourceLink = fmt.Sprintf("https://linux.oracle.com/cve/%s.html", cont.CveID)
|
||||
vuln.CveContents[models.Oracle] = cont
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o RedHatBase) update(r *models.ScanResult, defPacks defPacks) {
|
||||
ctype := models.NewCveContentType(o.family)
|
||||
for _, cve := range defPacks.def.Advisory.Cves {
|
||||
ovalContent := *o.convertToModel(cve.CveID, &defPacks.def)
|
||||
vinfo, ok := r.ScannedCves[cve.CveID]
|
||||
if !ok {
|
||||
util.Log.Debugf("%s is newly detected by OVAL", cve.CveID)
|
||||
vinfo = models.VulnInfo{
|
||||
CveID: cve.CveID,
|
||||
Confidence: models.OvalMatch,
|
||||
CveContents: models.NewCveContents(ovalContent),
|
||||
}
|
||||
} else {
|
||||
cveContents := vinfo.CveContents
|
||||
if _, ok := vinfo.CveContents[ctype]; ok {
|
||||
util.Log.Debugf("%s OVAL will be overwritten", cve.CveID)
|
||||
} else {
|
||||
util.Log.Debugf("%s also detected by OVAL", cve.CveID)
|
||||
cveContents = models.CveContents{}
|
||||
}
|
||||
|
||||
if vinfo.Confidence.Score < models.OvalMatch.Score {
|
||||
vinfo.Confidence = models.OvalMatch
|
||||
}
|
||||
cveContents[ctype] = ovalContent
|
||||
vinfo.CveContents = cveContents
|
||||
}
|
||||
|
||||
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
|
||||
for _, pack := range vinfo.AffectedPackages {
|
||||
defPacks.actuallyAffectedPackNames[pack.Name] = true
|
||||
}
|
||||
vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family, r.Packages)
|
||||
vinfo.AffectedPackages.Sort()
|
||||
r.ScannedCves[cve.CveID] = vinfo
|
||||
}
|
||||
}
|
||||
|
||||
func (o RedHatBase) convertToModel(cveID string, def *ovalmodels.Definition) *models.CveContent {
|
||||
for _, cve := range def.Advisory.Cves {
|
||||
if cve.CveID != cveID {
|
||||
continue
|
||||
}
|
||||
var refs []models.Reference
|
||||
for _, r := range def.References {
|
||||
refs = append(refs, models.Reference{
|
||||
Link: r.RefURL,
|
||||
Source: r.Source,
|
||||
RefID: r.RefID,
|
||||
})
|
||||
}
|
||||
|
||||
score2, vec2 := o.parseCvss2(cve.Cvss2)
|
||||
score3, vec3 := o.parseCvss3(cve.Cvss3)
|
||||
|
||||
severity := def.Advisory.Severity
|
||||
if cve.Impact != "" {
|
||||
severity = cve.Impact
|
||||
}
|
||||
|
||||
return &models.CveContent{
|
||||
Type: models.NewCveContentType(o.family),
|
||||
CveID: cve.CveID,
|
||||
Title: def.Title,
|
||||
Summary: def.Description,
|
||||
Severity: severity,
|
||||
Cvss2Score: score2,
|
||||
Cvss2Vector: vec2,
|
||||
Cvss3Score: score3,
|
||||
Cvss3Vector: vec3,
|
||||
References: refs,
|
||||
CweID: cve.Cwe,
|
||||
Published: def.Advisory.Issued,
|
||||
LastModified: def.Advisory.Updated,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseCvss2 divide CVSSv2 string into score and vector
|
||||
// 5/AV:N/AC:L/Au:N/C:N/I:N/A:P
|
||||
func (o RedHatBase) parseCvss2(scoreVector string) (score float64, vector string) {
|
||||
var err error
|
||||
ss := strings.Split(scoreVector, "/")
|
||||
if 1 < len(ss) {
|
||||
if score, err = strconv.ParseFloat(ss[0], 64); err != nil {
|
||||
return 0, ""
|
||||
}
|
||||
return score, strings.Join(ss[1:len(ss)], "/")
|
||||
}
|
||||
return 0, ""
|
||||
}
|
||||
|
||||
// ParseCvss3 divide CVSSv3 string into score and vector
|
||||
// 5.6/CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L
|
||||
func (o RedHatBase) parseCvss3(scoreVector string) (score float64, vector string) {
|
||||
var err error
|
||||
ss := strings.Split(scoreVector, "/CVSS:3.0/")
|
||||
if 1 < len(ss) {
|
||||
if score, err = strconv.ParseFloat(ss[0], 64); err != nil {
|
||||
return 0, ""
|
||||
}
|
||||
return score, strings.Join(ss[1:len(ss)], "/")
|
||||
}
|
||||
return 0, ""
|
||||
}
|
||||
|
||||
// RedHat is the interface for RedhatBase OVAL
|
||||
type RedHat struct {
|
||||
RedHatBase
|
||||
}
|
||||
|
||||
// NewRedhat creates OVAL client for Redhat
|
||||
func NewRedhat() RedHat {
|
||||
return RedHat{
|
||||
RedHatBase{
|
||||
Base{
|
||||
family: config.RedHat,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// CentOS is the interface for CentOS OVAL
|
||||
type CentOS struct {
|
||||
RedHatBase
|
||||
}
|
||||
|
||||
// NewCentOS creates OVAL client for CentOS
|
||||
func NewCentOS() CentOS {
|
||||
return CentOS{
|
||||
RedHatBase{
|
||||
Base{
|
||||
family: config.CentOS,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Oracle is the interface for CentOS OVAL
|
||||
type Oracle struct {
|
||||
RedHatBase
|
||||
}
|
||||
|
||||
// NewOracle creates OVAL client for Oracle
|
||||
func NewOracle() Oracle {
|
||||
return Oracle{
|
||||
RedHatBase{
|
||||
Base{
|
||||
family: config.Oracle,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,145 +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 oval
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
|
||||
)
|
||||
|
||||
func TestParseCvss2(t *testing.T) {
|
||||
type out struct {
|
||||
score float64
|
||||
vector string
|
||||
}
|
||||
var tests = []struct {
|
||||
in string
|
||||
out out
|
||||
}{
|
||||
{
|
||||
in: "5/AV:N/AC:L/Au:N/C:N/I:N/A:P",
|
||||
out: out{
|
||||
score: 5.0,
|
||||
vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
|
||||
},
|
||||
},
|
||||
{
|
||||
in: "",
|
||||
out: out{
|
||||
score: 0,
|
||||
vector: "",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
s, v := RedHatBase{}.parseCvss2(tt.in)
|
||||
if s != tt.out.score || v != tt.out.vector {
|
||||
t.Errorf("\nexpected: %f, %s\n actual: %f, %s",
|
||||
tt.out.score, tt.out.vector, s, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseCvss3(t *testing.T) {
|
||||
type out struct {
|
||||
score float64
|
||||
vector string
|
||||
}
|
||||
var tests = []struct {
|
||||
in string
|
||||
out out
|
||||
}{
|
||||
{
|
||||
in: "5.6/CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
|
||||
out: out{
|
||||
score: 5.6,
|
||||
vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
|
||||
},
|
||||
},
|
||||
{
|
||||
in: "",
|
||||
out: out{
|
||||
score: 0,
|
||||
vector: "",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
s, v := RedHatBase{}.parseCvss3(tt.in)
|
||||
if s != tt.out.score || v != tt.out.vector {
|
||||
t.Errorf("\nexpected: %f, %s\n actual: %f, %s",
|
||||
tt.out.score, tt.out.vector, s, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPackNamesOfUpdate(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in models.ScanResult
|
||||
defPacks defPacks
|
||||
out models.ScanResult
|
||||
}{
|
||||
{
|
||||
in: models.ScanResult{
|
||||
ScannedCves: models.VulnInfos{
|
||||
"CVE-2000-1000": models.VulnInfo{
|
||||
AffectedPackages: models.PackageStatuses{{Name: "packA"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
defPacks: defPacks{
|
||||
def: ovalmodels.Definition{
|
||||
Advisory: ovalmodels.Advisory{
|
||||
Cves: []ovalmodels.Cve{
|
||||
{
|
||||
CveID: "CVE-2000-1000",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
actuallyAffectedPackNames: map[string]bool{
|
||||
"packB": true,
|
||||
},
|
||||
},
|
||||
out: models.ScanResult{
|
||||
ScannedCves: models.VulnInfos{
|
||||
"CVE-2000-1000": models.VulnInfo{
|
||||
AffectedPackages: models.PackageStatuses{
|
||||
{Name: "packA"},
|
||||
{Name: "packB"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
util.Log = util.NewCustomLogger(config.ServerInfo{})
|
||||
for i, tt := range tests {
|
||||
RedHat{}.update(&tt.in, tt.defPacks)
|
||||
e := tt.out.ScannedCves["CVE-2000-1000"].AffectedPackages
|
||||
a := tt.in.ScannedCves["CVE-2000-1000"].AffectedPackages
|
||||
if !reflect.DeepEqual(a, e) {
|
||||
t.Errorf("[%d] expected: %v\n actual: %v\n", i, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
332
oval/util.go
@@ -1,332 +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 oval
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff"
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
debver "github.com/knqyf263/go-deb-version"
|
||||
rpmver "github.com/knqyf263/go-rpm-version"
|
||||
"github.com/kotakanbe/goval-dictionary/db"
|
||||
ovallog "github.com/kotakanbe/goval-dictionary/log"
|
||||
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
|
||||
"github.com/parnurzeal/gorequest"
|
||||
)
|
||||
|
||||
type ovalResult struct {
|
||||
entries []defPacks
|
||||
}
|
||||
|
||||
type defPacks struct {
|
||||
def ovalmodels.Definition
|
||||
actuallyAffectedPackNames map[string]bool
|
||||
}
|
||||
|
||||
func (e defPacks) toPackStatuses(family string, packs models.Packages) (ps models.PackageStatuses) {
|
||||
switch family {
|
||||
case config.Ubuntu:
|
||||
packNotFixedYet := map[string]bool{}
|
||||
for _, p := range e.def.AffectedPacks {
|
||||
packNotFixedYet[p.Name] = p.NotFixedYet
|
||||
}
|
||||
for k := range e.actuallyAffectedPackNames {
|
||||
ps = append(ps, models.PackageStatus{
|
||||
Name: k,
|
||||
NotFixedYet: packNotFixedYet[k],
|
||||
})
|
||||
}
|
||||
|
||||
case config.CentOS, config.Debian:
|
||||
// There are many packages that has been fixed in RedHat, but not been fixed in CentOS
|
||||
for name := range e.actuallyAffectedPackNames {
|
||||
pack, ok := packs[name]
|
||||
if !ok {
|
||||
util.Log.Warnf("Faild to find in Package list: %s", name)
|
||||
return
|
||||
}
|
||||
|
||||
ovalPackVer := ""
|
||||
for _, p := range e.def.AffectedPacks {
|
||||
if p.Name == name {
|
||||
ovalPackVer = p.Version
|
||||
break
|
||||
}
|
||||
}
|
||||
if ovalPackVer == "" {
|
||||
util.Log.Warnf("Faild to find in Oval Package list: %s", name)
|
||||
return
|
||||
}
|
||||
|
||||
if pack.NewVersion == "" {
|
||||
// compare version: installed vs oval
|
||||
vera := rpmver.NewVersion(fmt.Sprintf("%s-%s", pack.Version, pack.Release))
|
||||
verb := rpmver.NewVersion(ovalPackVer)
|
||||
notFixedYet := false
|
||||
if vera.LessThan(verb) {
|
||||
notFixedYet = true
|
||||
}
|
||||
ps = append(ps, models.PackageStatus{
|
||||
Name: name,
|
||||
NotFixedYet: notFixedYet,
|
||||
})
|
||||
} else {
|
||||
// compare version: newVer vs oval
|
||||
packNewVer := fmt.Sprintf("%s-%s", pack.NewVersion, pack.NewRelease)
|
||||
vera := rpmver.NewVersion(packNewVer)
|
||||
verb := rpmver.NewVersion(ovalPackVer)
|
||||
notFixedYet := false
|
||||
if vera.LessThan(verb) {
|
||||
notFixedYet = true
|
||||
}
|
||||
ps = append(ps, models.PackageStatus{
|
||||
Name: name,
|
||||
NotFixedYet: notFixedYet,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
for k := range e.actuallyAffectedPackNames {
|
||||
ps = append(ps, models.PackageStatus{
|
||||
Name: k,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (e *ovalResult) upsert(def ovalmodels.Definition, packName string) (upserted bool) {
|
||||
for i, entry := range e.entries {
|
||||
if entry.def.DefinitionID == def.DefinitionID {
|
||||
e.entries[i].actuallyAffectedPackNames[packName] = true
|
||||
return true
|
||||
}
|
||||
}
|
||||
e.entries = append(e.entries, defPacks{
|
||||
def: def,
|
||||
actuallyAffectedPackNames: map[string]bool{packName: true},
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
type request struct {
|
||||
pack models.Package
|
||||
}
|
||||
|
||||
type response struct {
|
||||
pack *models.Package
|
||||
defs []ovalmodels.Definition
|
||||
}
|
||||
|
||||
// getDefsByPackNameViaHTTP fetches OVAL information via HTTP
|
||||
func getDefsByPackNameViaHTTP(r *models.ScanResult) (
|
||||
relatedDefs ovalResult, err error) {
|
||||
|
||||
reqChan := make(chan request, len(r.Packages))
|
||||
resChan := make(chan response, len(r.Packages))
|
||||
errChan := make(chan error, len(r.Packages))
|
||||
defer close(reqChan)
|
||||
defer close(resChan)
|
||||
defer close(errChan)
|
||||
|
||||
go func() {
|
||||
for _, pack := range r.Packages {
|
||||
reqChan <- request{
|
||||
pack: pack,
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
concurrency := 10
|
||||
tasks := util.GenWorkers(concurrency)
|
||||
for range r.Packages {
|
||||
tasks <- func() {
|
||||
select {
|
||||
case req := <-reqChan:
|
||||
url, err := util.URLPathJoin(
|
||||
config.Conf.OvalDBURL,
|
||||
"packs",
|
||||
r.Family,
|
||||
r.Release,
|
||||
req.pack.Name,
|
||||
)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
} else {
|
||||
util.Log.Debugf("HTTP Request to %s", url)
|
||||
httpGet(url, &req.pack, resChan, errChan)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
timeout := time.After(2 * 60 * time.Second)
|
||||
var errs []error
|
||||
for range r.Packages {
|
||||
select {
|
||||
case res := <-resChan:
|
||||
for _, def := range res.defs {
|
||||
for _, p := range def.AffectedPacks {
|
||||
if res.pack.Name != p.Name {
|
||||
continue
|
||||
}
|
||||
|
||||
if p.NotFixedYet {
|
||||
relatedDefs.upsert(def, p.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
if less, err := lessThan(r.Family, *res.pack, p); err != nil {
|
||||
util.Log.Debugf("Failed to parse versions: %s", err)
|
||||
util.Log.Debugf("%#v\n%#v", *res.pack, p)
|
||||
} else if less {
|
||||
relatedDefs.upsert(def, p.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
case err := <-errChan:
|
||||
errs = append(errs, err)
|
||||
case <-timeout:
|
||||
return relatedDefs, fmt.Errorf("Timeout Fetching OVAL")
|
||||
}
|
||||
}
|
||||
if len(errs) != 0 {
|
||||
return relatedDefs, fmt.Errorf("Failed to fetch OVAL. err: %v", errs)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func httpGet(url string, pack *models.Package, 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 fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v",
|
||||
errs, url, resp)
|
||||
}
|
||||
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 <- fmt.Errorf("HTTP Error %s", err)
|
||||
return
|
||||
}
|
||||
if count == retryMax {
|
||||
errChan <- fmt.Errorf("HRetry count exceeded")
|
||||
return
|
||||
}
|
||||
|
||||
defs := []ovalmodels.Definition{}
|
||||
if err := json.Unmarshal([]byte(body), &defs); err != nil {
|
||||
errChan <- fmt.Errorf("Failed to Unmarshall. body: %s, err: %s",
|
||||
body, err)
|
||||
return
|
||||
}
|
||||
resChan <- response{
|
||||
pack: pack,
|
||||
defs: defs,
|
||||
}
|
||||
}
|
||||
|
||||
func getDefsByPackNameFromOvalDB(family, osRelease string,
|
||||
installedPacks models.Packages) (relatedDefs ovalResult, err error) {
|
||||
|
||||
ovallog.Initialize(config.Conf.LogDir)
|
||||
path := config.Conf.OvalDBURL
|
||||
if config.Conf.OvalDBType == "sqlite3" {
|
||||
path = config.Conf.OvalDBPath
|
||||
}
|
||||
util.Log.Debugf("Open oval-dictionary db (%s): %s", config.Conf.OvalDBType, path)
|
||||
|
||||
var ovaldb db.DB
|
||||
if ovaldb, err = db.NewDB(
|
||||
family,
|
||||
config.Conf.OvalDBType,
|
||||
path,
|
||||
config.Conf.DebugSQL,
|
||||
); err != nil {
|
||||
return
|
||||
}
|
||||
defer ovaldb.CloseDB()
|
||||
for _, installedPack := range installedPacks {
|
||||
definitions, err := ovaldb.GetByPackName(osRelease, installedPack.Name)
|
||||
if err != nil {
|
||||
return relatedDefs, fmt.Errorf("Failed to get %s OVAL info by package name: %v", family, err)
|
||||
}
|
||||
for _, def := range definitions {
|
||||
for _, ovalPack := range def.AffectedPacks {
|
||||
if installedPack.Name != ovalPack.Name {
|
||||
continue
|
||||
}
|
||||
|
||||
if ovalPack.NotFixedYet {
|
||||
relatedDefs.upsert(def, installedPack.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
less, err := lessThan(family, installedPack, ovalPack)
|
||||
if err != nil {
|
||||
util.Log.Debugf("Failed to parse versions: %s", err)
|
||||
util.Log.Debugf("%#v\n%#v", installedPack, ovalPack)
|
||||
} else if less {
|
||||
relatedDefs.upsert(def, installedPack.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func lessThan(family string, packA models.Package, packB ovalmodels.Package) (bool, error) {
|
||||
switch family {
|
||||
case config.Debian, config.Ubuntu:
|
||||
vera, err := debver.NewVersion(packA.Version)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
verb, err := debver.NewVersion(packB.Version)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return vera.LessThan(verb), nil
|
||||
case config.RedHat, config.CentOS, config.Oracle:
|
||||
vera := rpmver.NewVersion(fmt.Sprintf("%s-%s", packA.Version, packA.Release))
|
||||
verb := rpmver.NewVersion(packB.Version)
|
||||
return vera.LessThan(verb), nil
|
||||
}
|
||||
return false, fmt.Errorf("Package version comparison not supported: %s", family)
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
package oval
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/future-architect/vuls/models"
|
||||
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
|
||||
)
|
||||
|
||||
func TestUpsert(t *testing.T) {
|
||||
var tests = []struct {
|
||||
res ovalResult
|
||||
def ovalmodels.Definition
|
||||
packName string
|
||||
upserted bool
|
||||
out ovalResult
|
||||
}{
|
||||
//insert
|
||||
{
|
||||
res: ovalResult{},
|
||||
def: ovalmodels.Definition{
|
||||
DefinitionID: "1111",
|
||||
},
|
||||
packName: "pack1",
|
||||
upserted: false,
|
||||
out: ovalResult{
|
||||
[]defPacks{
|
||||
{
|
||||
def: ovalmodels.Definition{
|
||||
DefinitionID: "1111",
|
||||
},
|
||||
actuallyAffectedPackNames: map[string]bool{
|
||||
"pack1": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
//update
|
||||
{
|
||||
res: ovalResult{
|
||||
[]defPacks{
|
||||
{
|
||||
def: ovalmodels.Definition{
|
||||
DefinitionID: "1111",
|
||||
},
|
||||
actuallyAffectedPackNames: map[string]bool{
|
||||
"pack1": true,
|
||||
},
|
||||
},
|
||||
{
|
||||
def: ovalmodels.Definition{
|
||||
DefinitionID: "2222",
|
||||
},
|
||||
actuallyAffectedPackNames: map[string]bool{
|
||||
"pack3": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
def: ovalmodels.Definition{
|
||||
DefinitionID: "1111",
|
||||
},
|
||||
packName: "pack2",
|
||||
upserted: true,
|
||||
out: ovalResult{
|
||||
[]defPacks{
|
||||
{
|
||||
def: ovalmodels.Definition{
|
||||
DefinitionID: "1111",
|
||||
},
|
||||
actuallyAffectedPackNames: map[string]bool{
|
||||
"pack1": true,
|
||||
"pack2": true,
|
||||
},
|
||||
},
|
||||
{
|
||||
def: ovalmodels.Definition{
|
||||
DefinitionID: "2222",
|
||||
},
|
||||
actuallyAffectedPackNames: map[string]bool{
|
||||
"pack3": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
upserted := tt.res.upsert(tt.def, tt.packName)
|
||||
if tt.upserted != upserted {
|
||||
t.Errorf("[%d]\nexpected: %t\n actual: %t\n", i, tt.upserted, upserted)
|
||||
}
|
||||
if !reflect.DeepEqual(tt.out, tt.res) {
|
||||
t.Errorf("[%d]\nexpected: %v\n actual: %v\n", i, tt.out, tt.res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefpacksToPackStatuses(t *testing.T) {
|
||||
type in struct {
|
||||
dp defPacks
|
||||
family string
|
||||
packs models.Packages
|
||||
}
|
||||
var tests = []struct {
|
||||
in in
|
||||
out models.PackageStatuses
|
||||
}{
|
||||
// Ubuntu
|
||||
{
|
||||
in: in{
|
||||
family: "ubuntu",
|
||||
packs: models.Packages{},
|
||||
dp: defPacks{
|
||||
def: ovalmodels.Definition{
|
||||
AffectedPacks: []ovalmodels.Package{
|
||||
{
|
||||
Name: "a",
|
||||
NotFixedYet: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
NotFixedYet: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
actuallyAffectedPackNames: map[string]bool{
|
||||
"a": true,
|
||||
"b": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
out: models.PackageStatuses{
|
||||
{
|
||||
Name: "a",
|
||||
NotFixedYet: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
NotFixedYet: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// RedHat, Amazon, Debian
|
||||
{
|
||||
in: in{
|
||||
family: "redhat",
|
||||
packs: models.Packages{},
|
||||
dp: defPacks{
|
||||
def: ovalmodels.Definition{
|
||||
AffectedPacks: []ovalmodels.Package{
|
||||
{
|
||||
Name: "a",
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
},
|
||||
},
|
||||
},
|
||||
actuallyAffectedPackNames: map[string]bool{
|
||||
"a": true,
|
||||
"b": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
out: models.PackageStatuses{
|
||||
{
|
||||
Name: "a",
|
||||
NotFixedYet: false,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
NotFixedYet: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// CentOS
|
||||
{
|
||||
in: in{
|
||||
family: "centos",
|
||||
packs: models.Packages{
|
||||
"a": {Version: "1.0.0"},
|
||||
"b": {
|
||||
Version: "1.0.0",
|
||||
NewVersion: "2.0.0",
|
||||
},
|
||||
"c": {
|
||||
Version: "1.0.0",
|
||||
NewVersion: "1.5.0",
|
||||
},
|
||||
},
|
||||
dp: defPacks{
|
||||
def: ovalmodels.Definition{
|
||||
AffectedPacks: []ovalmodels.Package{
|
||||
{
|
||||
Name: "a",
|
||||
Version: "1.0.1",
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
Version: "1.5.0",
|
||||
},
|
||||
{
|
||||
Name: "c",
|
||||
Version: "2.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
actuallyAffectedPackNames: map[string]bool{
|
||||
"a": true,
|
||||
"b": true,
|
||||
"c": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
out: models.PackageStatuses{
|
||||
{
|
||||
Name: "a",
|
||||
NotFixedYet: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
NotFixedYet: false,
|
||||
},
|
||||
{
|
||||
Name: "c",
|
||||
NotFixedYet: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
actual := tt.in.dp.toPackStatuses(tt.in.family, tt.in.packs)
|
||||
sort.Slice(actual, func(i, j int) bool {
|
||||
return actual[i].Name < actual[j].Name
|
||||
})
|
||||
if !reflect.DeepEqual(actual, tt.out) {
|
||||
t.Errorf("[%d]\nexpected: %v\n actual: %v\n", i, tt.out, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
22
pkg/cmd/config/config.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
configInitCmd "github.com/future-architect/vuls/pkg/cmd/config/init"
|
||||
)
|
||||
|
||||
func NewCmdConfig() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "config <subcommand>",
|
||||
Short: "Vuls Config Operation",
|
||||
Example: heredoc.Doc(`
|
||||
$ vuls config init > config.json
|
||||
`),
|
||||
}
|
||||
|
||||
cmd.AddCommand(configInitCmd.NewCmdInit())
|
||||
|
||||
return cmd
|
||||
}
|
||||
101
pkg/cmd/config/init/init.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package init
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func NewCmdInit() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "init",
|
||||
Short: "generate vuls config template",
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return generateConfigTemplate()
|
||||
},
|
||||
Example: heredoc.Doc(`
|
||||
$ vuls config init > config.json
|
||||
`),
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func generateConfigTemplate() error {
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
pwd = os.TempDir()
|
||||
}
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
home = "/home/vuls"
|
||||
}
|
||||
|
||||
create := func(name, t string) *template.Template {
|
||||
return template.Must(template.New(name).Parse(t))
|
||||
}
|
||||
|
||||
t := create("config template",
|
||||
`{
|
||||
"server": {
|
||||
"listen": "127.0.0.1:5515",
|
||||
"path": "{{.dbpath}}"
|
||||
},
|
||||
"hosts": {
|
||||
"local": {
|
||||
"type": "local",
|
||||
"scan": {
|
||||
"ospkg": {
|
||||
"root": false
|
||||
}
|
||||
},
|
||||
"detect": {
|
||||
"path": "{{.dbpath}}",
|
||||
"result_dir": "{{.results}}"
|
||||
}
|
||||
},
|
||||
"remote": {
|
||||
"type": "remote",
|
||||
"host": "127.0.0.1",
|
||||
"port": "22",
|
||||
"user": "vuls",
|
||||
"ssh_config": "{{.sshconfig}}",
|
||||
"ssh_key": "{{.sshkey}}",
|
||||
"scan": {
|
||||
"ospkg": {
|
||||
"root": false
|
||||
}
|
||||
},
|
||||
"detect": {
|
||||
"path": "{{.dbpath}}",
|
||||
"result_dir": "{{.results}}"
|
||||
}
|
||||
},
|
||||
"cpe": {
|
||||
"type": "local",
|
||||
"scan": {
|
||||
"cpe": [
|
||||
{
|
||||
"cpe": "cpe:2.3:a:apache:log4j:2.3:*:*:*:*:*:*:*"
|
||||
}
|
||||
]
|
||||
},
|
||||
"detect": {
|
||||
"path": "{{.dbpath}}",
|
||||
"result_dir": "{{.results}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
if err := t.Execute(os.Stdout, map[string]string{"dbpath": filepath.Join(pwd, "vuls.db"), "results": filepath.Join(pwd, "results"), "sshconfig": filepath.Join(home, ".ssh", "config"), "sshkey": filepath.Join(home, ".ssh", "id_rsa")}); err != nil {
|
||||
return errors.Wrap(err, "output config template")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
413
pkg/cmd/db/create/create.go
Normal file
@@ -0,0 +1,413 @@
|
||||
package create
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/future-architect/vuls/pkg/cmd/db/create/vulnsrc"
|
||||
"github.com/future-architect/vuls/pkg/db"
|
||||
"github.com/future-architect/vuls/pkg/util"
|
||||
)
|
||||
|
||||
type DBCreateOption struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
func NewCmdCreate() *cobra.Command {
|
||||
opts := &DBCreateOption{
|
||||
Path: "vuls.db",
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "create",
|
||||
Short: "Create Vuls DB",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
return create(args[0], opts.Path)
|
||||
},
|
||||
Example: heredoc.Doc(`
|
||||
$ vuls db create https://github.com/vulsio/vuls-data.git
|
||||
$ vuls db create /home/MaineK00n/.cache/vuls
|
||||
`),
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&opts.Path, "path", "p", "vuls.db", "path to create Vuls DB")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func create(src, dbpath string) error {
|
||||
datapath := src
|
||||
if u, err := url.Parse(src); err == nil && u.Scheme != "" {
|
||||
cloneDir := filepath.Join(util.CacheDir(), "clone")
|
||||
if err := exec.Command("git", "clone", "--depth", "1", src, cloneDir).Run(); err != nil {
|
||||
return errors.Wrapf(err, "git clone --depth 1 %s %s", src, cloneDir)
|
||||
}
|
||||
datapath = cloneDir
|
||||
}
|
||||
if _, err := os.Stat(datapath); err != nil {
|
||||
return errors.Wrapf(err, "%s not found", datapath)
|
||||
}
|
||||
|
||||
db, err := db.Open("boltdb", dbpath, false)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "open db")
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
if err := filepath.WalkDir(datapath, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
p := strings.TrimPrefix(strings.TrimPrefix(path, datapath), string(os.PathSeparator))
|
||||
srcType, p, found := strings.Cut(p, string(os.PathSeparator))
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
advType, p, found := strings.Cut(p, string(os.PathSeparator))
|
||||
if !found {
|
||||
return errors.Errorf(`unexpected filepath. expected: "%s/["official", ...]/["vulnerability", "os", "library", "cpe"]/...", actual: "%s"`, datapath, path)
|
||||
}
|
||||
|
||||
bs, err := util.Read(path)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "read %s", path)
|
||||
}
|
||||
if len(bs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch advType {
|
||||
case "vulnerability":
|
||||
var src vulnsrc.Vulnerability
|
||||
if err := json.Unmarshal(bs, &src); err != nil {
|
||||
return errors.Wrapf(err, "unmarshal json. path: %s", path)
|
||||
}
|
||||
if err := db.PutVulnerability(srcType, fmt.Sprintf("vulnerability:%s", src.ID), vulnsrc.ToVulsVulnerability(src)); err != nil {
|
||||
return errors.Wrap(err, "put vulnerability")
|
||||
}
|
||||
case "os":
|
||||
advType, err := toAdvType(p)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "path to adv type")
|
||||
}
|
||||
bucket, err := toAdvBucket(p)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "path to adv bucket")
|
||||
}
|
||||
|
||||
switch advType {
|
||||
case "redhat_oval":
|
||||
if strings.Contains(p, "repository_to_cpe.json") {
|
||||
var src vulnsrc.RepositoryToCPE
|
||||
if err := json.Unmarshal(bs, &src); err != nil {
|
||||
return errors.Wrapf(err, "unmarshal json. path: %s", path)
|
||||
}
|
||||
if err := db.PutRedHatRepoToCPE(srcType, bucket, vulnsrc.ToVulsRepositoryToCPE(src)); err != nil {
|
||||
return errors.Wrap(err, "put repository to cpe")
|
||||
}
|
||||
break
|
||||
}
|
||||
var src vulnsrc.DetectPackage
|
||||
if err := json.Unmarshal(bs, &src); err != nil {
|
||||
return errors.Wrapf(err, "unmarshal json. path: %s", path)
|
||||
}
|
||||
pkgs, err := vulnsrc.ToVulsPackage(src, advType)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "to vuls package")
|
||||
}
|
||||
if err := db.PutPackage(srcType, bucket, pkgs); err != nil {
|
||||
return errors.Wrap(err, "put package")
|
||||
}
|
||||
case "windows":
|
||||
if strings.Contains(p, "supercedence.json") {
|
||||
var supercedences []vulnsrc.Supercedence
|
||||
if err := json.Unmarshal(bs, &supercedences); err != nil {
|
||||
return errors.Wrapf(err, "unnmarshal json. path: %s", path)
|
||||
}
|
||||
if err := db.PutWindowsSupercedence(srcType, bucket, vulnsrc.ToVulsSupercedences(supercedences)); err != nil {
|
||||
return errors.Wrap(err, "put supercedence")
|
||||
}
|
||||
break
|
||||
}
|
||||
var src vulnsrc.DetectPackage
|
||||
if err := json.Unmarshal(bs, &src); err != nil {
|
||||
return errors.Wrapf(err, "unmarshal json. path: %s", path)
|
||||
}
|
||||
pkgs, err := vulnsrc.ToVulsPackage(src, advType)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "to vuls package")
|
||||
}
|
||||
if err := db.PutPackage(srcType, bucket, pkgs); err != nil {
|
||||
return errors.Wrap(err, "put package")
|
||||
}
|
||||
default:
|
||||
var src vulnsrc.DetectPackage
|
||||
if err := json.Unmarshal(bs, &src); err != nil {
|
||||
return errors.Wrapf(err, "unmarshal json. path: %s", path)
|
||||
}
|
||||
pkgs, err := vulnsrc.ToVulsPackage(src, advType)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "to vuls package")
|
||||
}
|
||||
if err := db.PutPackage(srcType, bucket, pkgs); err != nil {
|
||||
return errors.Wrap(err, "put package")
|
||||
}
|
||||
}
|
||||
case "library":
|
||||
case "cpe":
|
||||
var src vulnsrc.DetectCPE
|
||||
if err := json.Unmarshal(bs, &src); err != nil {
|
||||
return errors.Wrapf(err, "unmarshal json. path: %s", path)
|
||||
}
|
||||
|
||||
advType, err := toAdvType(p)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "path to adv type")
|
||||
}
|
||||
cs, err := vulnsrc.ToVulsCPEConfiguration(src, advType)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "to vuls cpe configuration")
|
||||
}
|
||||
bucket, err := toAdvBucket(p)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "path to adv bucket")
|
||||
}
|
||||
|
||||
if err := db.PutCPEConfiguration(srcType, bucket, cs); err != nil {
|
||||
return errors.Wrap(err, "put cpe configuration")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func toAdvType(path string) (string, error) {
|
||||
ss := strings.Split(path, string(os.PathSeparator))
|
||||
if len(ss) < 3 && ss[0] != "windows" {
|
||||
return "", errors.Errorf(`unexpected path. accepts: "[<os name>, <library name>, "nvd", "jvn"]/**/*.json*", received: "%s"`, path)
|
||||
}
|
||||
|
||||
switch ss[0] {
|
||||
case "alma", "alpine", "amazon", "epel", "fedora", "oracle", "rocky":
|
||||
return fmt.Sprintf("%s:%s", ss[0], ss[1]), nil
|
||||
case "arch", "freebsd", "gentoo", "windows", "conan", "erlang", "nvd", "jvn":
|
||||
return ss[0], nil
|
||||
case "debian":
|
||||
switch ss[1] {
|
||||
case "oval":
|
||||
return "debian_oval", nil
|
||||
case "tracker":
|
||||
return "debian_security_tracker", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected debian advisory type. accepts: ["oval", "tracker"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "redhat":
|
||||
switch ss[1] {
|
||||
case "api":
|
||||
return "redhat_security_api", nil
|
||||
case "oval":
|
||||
return "redhat_oval", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected redhat advisory type. accepts: ["api", "oval"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "suse":
|
||||
switch ss[1] {
|
||||
case "cvrf":
|
||||
return "suse_cvrf", nil
|
||||
case "oval":
|
||||
return "suse_oval", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected suse advisory type. accepts: ["cvrf", "oval"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "ubuntu":
|
||||
switch ss[1] {
|
||||
case "oval":
|
||||
return "ubuntu_oval", nil
|
||||
case "tracker":
|
||||
return "ubuntu_security_tracker", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected debian advisory type. accepts: ["oval", "tracker"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "cargo":
|
||||
switch ss[1] {
|
||||
case "db":
|
||||
return "cargo_db", nil
|
||||
case "ghsa":
|
||||
return "cargo_ghsa", nil
|
||||
case "osv":
|
||||
return "cargo_osv", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected cargo advisory type. accepts: ["db", "ghsa", "osv"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "composer":
|
||||
switch ss[1] {
|
||||
case "db":
|
||||
return "composer_db", nil
|
||||
case "ghsa":
|
||||
return "composer_ghsa", nil
|
||||
case "glsa":
|
||||
return "composer_glsa", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected composer advisory type. accepts: ["db", "ghsa", "glsa"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "golang":
|
||||
switch ss[1] {
|
||||
case "db":
|
||||
return "golang_db", nil
|
||||
case "ghsa":
|
||||
return "golang_ghsa", nil
|
||||
case "glsa":
|
||||
return "golang_glsa", nil
|
||||
case "govulndb":
|
||||
return "golang_govulndb", nil
|
||||
case "osv":
|
||||
return "golang_osv", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected golang advisory type. accepts: ["db", "ghsa", "glsa", "govulndb", "osv"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "maven":
|
||||
switch ss[1] {
|
||||
case "ghsa":
|
||||
return "maven_ghsa", nil
|
||||
case "glsa":
|
||||
return "maven_glsa", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected maven advisory type. accepts: ["ghsa", "glsa"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "npm":
|
||||
switch ss[1] {
|
||||
case "db":
|
||||
return "npm_db", nil
|
||||
case "ghsa":
|
||||
return "npm_ghsa", nil
|
||||
case "glsa":
|
||||
return "npm_glsa", nil
|
||||
case "osv":
|
||||
return "npm_osv", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected npm advisory type. accepts: ["db", "ghsa", "glsa", "osv"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "nuget":
|
||||
switch ss[1] {
|
||||
case "ghsa":
|
||||
return "nuget_ghsa", nil
|
||||
case "glsa":
|
||||
return "nuget_glsa", nil
|
||||
case "osv":
|
||||
return "nuget_osv", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected nuget advisory type. accepts: ["ghsa", "glsa", "osv"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "pip":
|
||||
switch ss[1] {
|
||||
case "db":
|
||||
return "pip_db", nil
|
||||
case "ghsa":
|
||||
return "pip_ghsa", nil
|
||||
case "glsa":
|
||||
return "pip_glsa", nil
|
||||
case "osv":
|
||||
return "pip_osv", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected pip advisory type. accepts: ["db", "ghsa", "glsa", "osv"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "rubygems":
|
||||
switch ss[1] {
|
||||
case "db":
|
||||
return "rubygems_db", nil
|
||||
case "ghsa":
|
||||
return "rubygems_ghsa", nil
|
||||
case "glsa":
|
||||
return "rubygems_glsa", nil
|
||||
case "osv":
|
||||
return "rubygems_osv", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected rubygems advisory type. accepts: ["db", "ghsa", "glsa", "osv"], received: "%s"`, ss[1])
|
||||
}
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected os or library or cpe. accepts: ["alma", "alpine", "amazon", "arch", "debian", "epel", "fedora", "freebsd", "gentoo", "oracle", "redhat", "rocky", "suse", "ubuntu", "windows", "cargo", "composer", "conan", "erlang", "golang", "maven", "npm", "nuget", "pip", "rubygems", "nvd", "jvn"], received: "%s"`, ss[0])
|
||||
}
|
||||
}
|
||||
|
||||
func toAdvBucket(path string) (string, error) {
|
||||
ss := strings.Split(path, string(os.PathSeparator))
|
||||
if len(ss) < 3 && ss[0] != "windows" {
|
||||
return "", errors.Errorf(`unexpected path. accepts: "[<os name>, <library name>, "nvd", "jvn"]/**/*.json*", received: "%s"`, path)
|
||||
}
|
||||
|
||||
switch ss[0] {
|
||||
case "alma", "alpine", "amazon", "epel", "fedora", "oracle", "rocky":
|
||||
return fmt.Sprintf("%s:%s", ss[0], ss[1]), nil
|
||||
case "arch", "freebsd", "gentoo", "cargo", "composer", "conan", "erlang", "golang", "maven", "npm", "nuget", "pip", "rubygems":
|
||||
return ss[0], nil
|
||||
case "debian":
|
||||
switch ss[1] {
|
||||
case "oval", "tracker":
|
||||
return fmt.Sprintf("%s:%s", ss[0], ss[2]), nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected debian advisory type. accepts: ["oval", "tracker"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "redhat":
|
||||
switch ss[1] {
|
||||
case "api":
|
||||
return fmt.Sprintf("%s:%s", ss[0], ss[2]), nil
|
||||
case "oval":
|
||||
if len(ss) < 4 {
|
||||
return "", errors.Errorf(`unexpected path. accepts: "redhat/oval/<os version>/<stream>/yyyy/*.json*", received: "%s"`, path)
|
||||
}
|
||||
if strings.Contains(path, "repository_to_cpe.json") {
|
||||
return fmt.Sprintf("%s_cpe:%s", ss[0], ss[3]), nil
|
||||
}
|
||||
return fmt.Sprintf("%s:%s", ss[0], ss[3]), nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected redhat advisory type. accepts: ["api", "oval"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "suse":
|
||||
switch ss[1] {
|
||||
case "cvrf", "oval":
|
||||
if len(ss) < 4 {
|
||||
return "", errors.Errorf(`unexpected path. accepts: "suse/[cvrf, oval]/<os>/<version>/yyyy/*.json*", received: "%s"`, path)
|
||||
}
|
||||
return fmt.Sprintf("%s:%s", ss[2], ss[3]), nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected suse advisory type. accepts: ["cvrf", "oval"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "ubuntu":
|
||||
switch ss[1] {
|
||||
case "oval", "tracker":
|
||||
return fmt.Sprintf("%s:%s", ss[0], ss[2]), nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected debian advisory type. accepts: ["oval", "tracker"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "windows":
|
||||
if strings.Contains(path, "supercedence.json") {
|
||||
return "windows_supercedence", nil
|
||||
}
|
||||
return fmt.Sprintf("%s:%s", ss[0], ss[1]), nil
|
||||
case "nvd", "jvn":
|
||||
return "cpe", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected os or library or cpe. accepts: ["alma", "alpine", "amazon", "arch", "debian", "epel", "fedora", "freebsd", "gentoo", "oracle", "redhat", "rocky", "suse", "ubuntu", "windows", "cargo", "composer", "conan", "erlang", "golang", "maven", "npm", "nuget", "pip", "rubygems", "nvd", "jvn"], received: "%s"`, ss[0])
|
||||
}
|
||||
}
|
||||
269
pkg/cmd/db/create/vulnsrc/types.go
Normal file
@@ -0,0 +1,269 @@
|
||||
package vulnsrc
|
||||
|
||||
// https://github.com/MaineK00n/vuls-data-update/blob/main/pkg/build/types.go
|
||||
|
||||
import "time"
|
||||
|
||||
type Vulnerability struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Advisory *Advisories `json:"advisory,omitempty"`
|
||||
Title *Titles `json:"title,omitempty"`
|
||||
Description *Descriptions `json:"description,omitempty"`
|
||||
CVSS *CVSSes `json:"cvss,omitempty"`
|
||||
EPSS *EPSS `json:"epss,omitempty"`
|
||||
CWE *CWEs `json:"cwe,omitempty"`
|
||||
Metasploit []Metasploit `json:"metasploit,omitempty"`
|
||||
Exploit *Exploit `json:"exploit,omitempty"`
|
||||
KEV *KEV `json:"kev,omitempty"`
|
||||
Mitigation *Mitigation `json:"mitigation,omitempty"`
|
||||
Published *Publisheds `json:"published,omitempty"`
|
||||
Modified *Modifieds `json:"modified,omitempty"`
|
||||
References *References `json:"references,omitempty"`
|
||||
}
|
||||
|
||||
type Advisories struct {
|
||||
MITRE *Advisory `json:"mitre,omitempty"`
|
||||
NVD *Advisory `json:"nvd,omitempty"`
|
||||
JVN []Advisory `json:"jvn,omitempty"`
|
||||
Alma map[string][]Advisory `json:"alma,omitempty"`
|
||||
Alpine map[string]Advisory `json:"alpine,omitempty"`
|
||||
Amazon map[string][]Advisory `json:"amazon,omitempty"`
|
||||
Arch []Advisory `json:"arch,omitempty"`
|
||||
DebianOVAL map[string][]Advisory `json:"debian_oval,omitempty"`
|
||||
DebianSecurityTracker map[string]Advisory `json:"debian_security_tracker,omitempty"`
|
||||
FreeBSD []Advisory `json:"freebsd,omitempty"`
|
||||
Oracle map[string][]Advisory `json:"oracle,omitempty"`
|
||||
RedHatOVAL map[string][]Advisory `json:"redhat_oval,omitempty"`
|
||||
SUSEOVAL map[string][]Advisory `json:"suse_oval,omitempty"`
|
||||
SUSECVRF *Advisory `json:"suse_cvrf,omitempty"`
|
||||
UbuntuOVAL map[string][]Advisory `json:"ubuntu_oval,omitempty"`
|
||||
UbuntuSecurityTracker *Advisory `json:"ubuntu_security_tracker,omitempty"`
|
||||
}
|
||||
type Advisory struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
}
|
||||
|
||||
type Titles struct {
|
||||
MITRE string `json:"mitre,omitempty"`
|
||||
NVD string `json:"nvd,omitempty"`
|
||||
JVN map[string]string `json:"jvn,omitempty"`
|
||||
Alma map[string]map[string]string `json:"alma,omitempty"`
|
||||
Alpine map[string]string `json:"alpine,omitempty"`
|
||||
Amazon map[string]map[string]string `json:"amazon,omitempty"`
|
||||
Arch map[string]string `json:"arch,omitempty"`
|
||||
DebianOVAL map[string]map[string]string `json:"debian_oval,omitempty"`
|
||||
DebianSecurityTracker map[string]string `json:"debian_security_tracker,omitempty"`
|
||||
FreeBSD map[string]string `json:"freebsd,omitempty"`
|
||||
Oracle map[string]map[string]string `json:"oracle,omitempty"`
|
||||
RedHatOVAL map[string]map[string]string `json:"redhat_oval,omitempty"`
|
||||
SUSEOVAL map[string]map[string]string `json:"suse_oval,omitempty"`
|
||||
SUSECVRF string `json:"suse_cvrf,omitempty"`
|
||||
UbuntuOVAL map[string]map[string]string `json:"ubuntu_oval,omitempty"`
|
||||
UbuntuSecurityTracker string `json:"ubuntu_security_tracker,omitempty"`
|
||||
}
|
||||
|
||||
type Descriptions struct {
|
||||
MITRE string `json:"mitre,omitempty"`
|
||||
NVD string `json:"nvd,omitempty"`
|
||||
JVN map[string]string `json:"jvn,omitempty"`
|
||||
Alma map[string]map[string]string `json:"alma,omitempty"`
|
||||
Amazon map[string]map[string]string `json:"amazon,omitempty"`
|
||||
DebianOVAL map[string]map[string]string `json:"debian_oval,omitempty"`
|
||||
DebianSecurityTracker map[string]string `json:"debian_security_tracker,omitempty"`
|
||||
FreeBSD map[string]string `json:"freebsd,omitempty"`
|
||||
Oracle map[string]map[string]string `json:"oracle,omitempty"`
|
||||
RedHatOVAL map[string]map[string]string `json:"redhat_oval,omitempty"`
|
||||
SUSEOVAL map[string]map[string]string `json:"suse_oval,omitempty"`
|
||||
SUSECVRF string `json:"suse_cvrf,omitempty"`
|
||||
UbuntuOVAL map[string]map[string]string `json:"ubuntu_oval,omitempty"`
|
||||
UbuntuSecurityTracker string `json:"ubuntu_security_tracker,omitempty"`
|
||||
}
|
||||
|
||||
type CVSSes struct {
|
||||
NVD []CVSS `json:"nvd,omitempty"`
|
||||
JVN map[string][]CVSS `json:"jvn,omitempty"`
|
||||
Alma map[string]map[string][]CVSS `json:"alma,omitempty"`
|
||||
Amazon map[string]map[string][]CVSS `json:"amazon,omitempty"`
|
||||
Arch map[string][]CVSS `json:"arch,omitempty"`
|
||||
Oracle map[string]map[string][]CVSS `json:"oracle,omitempty"`
|
||||
RedHatOVAL map[string]map[string][]CVSS `json:"redhat_oval,omitempty"`
|
||||
SUSEOVAL map[string]map[string][]CVSS `json:"suse_oval,omitempty"`
|
||||
SUSECVRF []CVSS `json:"suse_cvrf,omitempty"`
|
||||
UbuntuOVAL map[string]map[string][]CVSS `json:"ubuntu_oval,omitempty"`
|
||||
UbuntuSecurityTracker []CVSS `json:"ubuntu_security_tracker,omitempty"`
|
||||
}
|
||||
|
||||
type CVSS struct {
|
||||
Version string `json:"version,omitempty"`
|
||||
Source string `json:"source,omitempty"`
|
||||
Vector string `json:"vector,omitempty"`
|
||||
Score *float64 `json:"score,omitempty"`
|
||||
Severity string `json:"severity,omitempty"`
|
||||
}
|
||||
|
||||
type EPSS struct {
|
||||
EPSS *float64 `json:"epss,omitempty"`
|
||||
Percentile *float64 `json:"percentile,omitempty"`
|
||||
}
|
||||
|
||||
type CWEs struct {
|
||||
NVD []string `json:"nvd,omitempty"`
|
||||
JVN map[string][]string `json:"jvn,omitempty"`
|
||||
RedHatOVAL map[string]map[string][]string `json:"redhat_oval,omitempty"`
|
||||
}
|
||||
|
||||
type Metasploit struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Title string `json:"title,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
URLs []string `json:"urls,omitempty"`
|
||||
}
|
||||
|
||||
type Exploit struct {
|
||||
NVD []string `json:"nvd,omitempty"`
|
||||
ExploitDB []ExploitDB `json:"exploit_db,omitempty"`
|
||||
GitHub []GitHub `json:"github,omitempty"`
|
||||
InTheWild []InTheWild `json:"inthewild,omitempty"`
|
||||
Trickest *Trickest `json:"trickest,omitempty"`
|
||||
}
|
||||
|
||||
type ExploitDB struct {
|
||||
ID string `json:"name,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
FileURL string `json:"file_url,omitempty"`
|
||||
}
|
||||
|
||||
type GitHub struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Stars int `json:"stars"`
|
||||
Forks int `json:"forks"`
|
||||
Watches int `json:"watches"`
|
||||
URL string `json:"url,omitempty"`
|
||||
}
|
||||
|
||||
type InTheWild struct {
|
||||
Source string `json:"source,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
}
|
||||
|
||||
type Trickest struct {
|
||||
Description string `json:"description,omitempty"`
|
||||
PoC *TrickestPoc `json:"poc,omitempty"`
|
||||
}
|
||||
|
||||
type TrickestPoc struct {
|
||||
Reference []string `json:"reference,omitempty"`
|
||||
GitHub []string `json:"github,omitempty"`
|
||||
}
|
||||
|
||||
type KEV struct {
|
||||
Title string `json:"title,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
RequiredAction string `json:"required_action,omitempty"`
|
||||
DueDate *time.Time `json:"due_date,omitempty"`
|
||||
}
|
||||
|
||||
type Mitigation struct {
|
||||
NVD []string `json:"nvd,omitempty"`
|
||||
UbuntuSecurityTracker string `json:"ubuntu_security_tracker,omitempty"`
|
||||
}
|
||||
|
||||
type Publisheds struct {
|
||||
MITRE *time.Time `json:"mitre,omitempty"`
|
||||
NVD *time.Time `json:"nvd,omitempty"`
|
||||
JVN map[string]*time.Time `json:"jvn,omitempty"`
|
||||
Alma map[string]map[string]*time.Time `json:"alma,omitempty"`
|
||||
Amazon map[string]map[string]*time.Time `json:"amazon,omitempty"`
|
||||
FreeBSD map[string]*time.Time `json:"freebsd,omitempty"`
|
||||
Oracle map[string]map[string]*time.Time `json:"oracle,omitempty"`
|
||||
RedHatOVAL map[string]map[string]*time.Time `json:"redhat_oval,omitempty"`
|
||||
SUSEOVAL map[string]map[string]*time.Time `json:"suse_oval,omitempty"`
|
||||
SUSECVRF *time.Time `json:"suse_cvrf,omitempty"`
|
||||
UbuntuOVAL map[string]map[string]*time.Time `json:"ubuntu_oval,omitempty"`
|
||||
UbuntuSecurityTracker *time.Time `json:"ubuntu_security_tracker,omitempty"`
|
||||
}
|
||||
|
||||
type Modifieds struct {
|
||||
MITRE *time.Time `json:"mitre,omitempty"`
|
||||
NVD *time.Time `json:"nvd,omitempty"`
|
||||
JVN map[string]*time.Time `json:"jvn,omitempty"`
|
||||
Alma map[string]map[string]*time.Time `json:"alma,omitempty"`
|
||||
Amazon map[string]map[string]*time.Time `json:"amazon,omitempty"`
|
||||
FreeBSD map[string]*time.Time `json:"freebsd,omitempty"`
|
||||
RedHatOVAL map[string]map[string]*time.Time `json:"redhat_oval,omitempty"`
|
||||
SUSEOVAL map[string]map[string]*time.Time `json:"suse_oval,omitempty"`
|
||||
SUSECVRF *time.Time `json:"suse_cvrf,omitempty"`
|
||||
}
|
||||
|
||||
type References struct {
|
||||
MITRE []Reference `json:"mitre,omitempty"`
|
||||
NVD []Reference `json:"nvd,omitempty"`
|
||||
JVN map[string][]Reference `json:"jvn,omitempty"`
|
||||
Alma map[string]map[string][]Reference `json:"alma,omitempty"`
|
||||
Amazon map[string]map[string][]Reference `json:"amazon,omitempty"`
|
||||
Arch map[string][]Reference `json:"arch,omitempty"`
|
||||
DebianOVAL map[string]map[string][]Reference `json:"debian_oval,omitempty"`
|
||||
DebianSecurityTracker map[string][]Reference `json:"debian_security_tracker,omitempty"`
|
||||
FreeBSD map[string][]Reference `json:"freebsd,omitempty"`
|
||||
Oracle map[string]map[string][]Reference `json:"oracle,omitempty"`
|
||||
RedHatOVAL map[string]map[string][]Reference `json:"redhat_oval,omitempty"`
|
||||
SUSEOVAL map[string]map[string][]Reference `json:"suse_oval,omitempty"`
|
||||
SUSECVRF []Reference `json:"suse_cvrf,omitempty"`
|
||||
UbuntuOVAL map[string]map[string][]Reference `json:"ubuntu_oval,omitempty"`
|
||||
UbuntuSecurityTracker []Reference `json:"ubuntu_security_tracker,omitempty"`
|
||||
}
|
||||
|
||||
type Reference struct {
|
||||
Source string `json:"source,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
}
|
||||
|
||||
type DetectCPE struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Configurations map[string][]CPEConfiguration `json:"configurations,omitempty"`
|
||||
}
|
||||
|
||||
type CPEConfiguration struct {
|
||||
Vulnerable []CPE `json:"vulnerable,omitempty"`
|
||||
RunningOn []CPE `json:"running_on,omitempty"`
|
||||
}
|
||||
|
||||
type CPE struct {
|
||||
CPEVersion string `json:"cpe_version,omitempty"`
|
||||
CPE string `json:"cpe,omitempty"`
|
||||
Version []Version `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
type DetectPackage struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Packages map[string][]Package `json:"packages,omitempty"`
|
||||
}
|
||||
|
||||
type Package struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Version [][]Version `json:"version,omitempty"`
|
||||
ModularityLabel string `json:"modularity_label,omitempty"`
|
||||
Arch []string `json:"arch,omitempty"`
|
||||
Repository string `json:"repository,omitempty"`
|
||||
CPE []string `json:"cpe,omitempty"`
|
||||
}
|
||||
|
||||
type Version struct {
|
||||
Operator string `json:"operator,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
type RepositoryToCPE map[string][]string
|
||||
|
||||
type Supercedence struct {
|
||||
KBID string `json:"KBID,omitempty"`
|
||||
Supersededby struct {
|
||||
KBIDs []string `json:"KBIDs,omitempty"`
|
||||
} `json:"Supersededby,omitempty"`
|
||||
}
|
||||
619
pkg/cmd/db/create/vulnsrc/vulnsrc.go
Normal file
@@ -0,0 +1,619 @@
|
||||
package vulnsrc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
"github.com/knqyf263/go-cpe/common"
|
||||
"github.com/knqyf263/go-cpe/naming"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/future-architect/vuls/pkg/db/types"
|
||||
)
|
||||
|
||||
func ToVulsVulnerability(src Vulnerability) types.Vulnerability {
|
||||
return types.Vulnerability{
|
||||
ID: src.ID,
|
||||
Advisory: toVulsAdvisory(src.Advisory),
|
||||
Title: toVulsTitle(src.Title),
|
||||
Description: toVulsDescription(src.Description),
|
||||
CVSS: toVulsCVSS(src.CVSS),
|
||||
EPSS: toVulsEPSS(src.EPSS),
|
||||
CWE: toVulsCWE(src.CWE),
|
||||
Metasploit: toVulsMetasploit(src.Metasploit),
|
||||
Exploit: toVulsExploit(src.Exploit),
|
||||
KEV: src.KEV != nil,
|
||||
Published: toVulsPublished(src.Published),
|
||||
Modified: toVulsModified(src.Modified),
|
||||
Reference: toVulsReference(src.References),
|
||||
}
|
||||
}
|
||||
|
||||
func toVulsAdvisory(src *Advisories) []string {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var advs []string
|
||||
if src.MITRE != nil {
|
||||
advs = append(advs, "mitre")
|
||||
}
|
||||
if src.NVD != nil {
|
||||
advs = append(advs, "nvd")
|
||||
}
|
||||
for _, a := range src.JVN {
|
||||
advs = append(advs, fmt.Sprintf("jvn:%s", a.ID))
|
||||
}
|
||||
for v, as := range src.Alma {
|
||||
for _, a := range as {
|
||||
advs = append(advs, fmt.Sprintf("alma:%s:%s", v, a.ID))
|
||||
}
|
||||
}
|
||||
for v, a := range src.Alpine {
|
||||
advs = append(advs, fmt.Sprintf("alpine:%s:%s", v, a.ID))
|
||||
}
|
||||
for v, as := range src.Amazon {
|
||||
for _, a := range as {
|
||||
advs = append(advs, fmt.Sprintf("amazon:%s:%s", v, a.ID))
|
||||
}
|
||||
}
|
||||
for _, a := range src.Arch {
|
||||
advs = append(advs, fmt.Sprintf("arch:%s", a.ID))
|
||||
}
|
||||
for v, as := range src.DebianOVAL {
|
||||
for _, a := range as {
|
||||
advs = append(advs, fmt.Sprintf("debian_oval:%s:%s", v, a.ID))
|
||||
}
|
||||
}
|
||||
for v, a := range src.DebianSecurityTracker {
|
||||
advs = append(advs, fmt.Sprintf("debian_security_tracker:%s:%s", v, a.ID))
|
||||
}
|
||||
for _, a := range src.FreeBSD {
|
||||
advs = append(advs, fmt.Sprintf("freebsd:%s", a.ID))
|
||||
}
|
||||
for v, as := range src.Oracle {
|
||||
for _, a := range as {
|
||||
advs = append(advs, fmt.Sprintf("oracle:%s:%s", v, a.ID))
|
||||
}
|
||||
}
|
||||
for v, as := range src.RedHatOVAL {
|
||||
for _, a := range as {
|
||||
advs = append(advs, fmt.Sprintf("redhat_oval:%s:%s", v, a.ID))
|
||||
}
|
||||
}
|
||||
for v, as := range src.SUSEOVAL {
|
||||
for _, a := range as {
|
||||
advs = append(advs, fmt.Sprintf("suse_oval:%s:%s", v, a.ID))
|
||||
}
|
||||
}
|
||||
if src.SUSECVRF != nil {
|
||||
advs = append(advs, "suse_cvrf")
|
||||
}
|
||||
for v, as := range src.UbuntuOVAL {
|
||||
for _, a := range as {
|
||||
advs = append(advs, fmt.Sprintf("ubuntu_oval:%s:%s", v, a.ID))
|
||||
}
|
||||
}
|
||||
if src.UbuntuSecurityTracker != nil {
|
||||
advs = append(advs, "ubuntu_security_tracker")
|
||||
}
|
||||
return advs
|
||||
}
|
||||
|
||||
func toVulsTitle(src *Titles) string {
|
||||
if src == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if src.NVD != "" {
|
||||
return src.NVD
|
||||
}
|
||||
if src.MITRE != "" {
|
||||
return src.MITRE
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func toVulsDescription(src *Descriptions) string {
|
||||
if src == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if src.NVD != "" {
|
||||
return src.NVD
|
||||
}
|
||||
if src.MITRE != "" {
|
||||
return src.MITRE
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func toVulsCVSS(src *CVSSes) []types.CVSS {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var cvsses []types.CVSS
|
||||
for _, c := range src.NVD {
|
||||
cvsses = append(cvsses, types.CVSS{
|
||||
Source: "nvd",
|
||||
Version: c.Version,
|
||||
Vector: c.Vector,
|
||||
Score: c.Score,
|
||||
Severity: c.Severity,
|
||||
})
|
||||
}
|
||||
for id, cs := range src.JVN {
|
||||
for _, c := range cs {
|
||||
cvsses = append(cvsses, types.CVSS{
|
||||
Source: fmt.Sprintf("jvn:%s", id),
|
||||
Version: c.Version,
|
||||
Vector: c.Vector,
|
||||
Score: c.Score,
|
||||
Severity: c.Severity,
|
||||
})
|
||||
}
|
||||
}
|
||||
for v, idcs := range src.Alma {
|
||||
for id, cs := range idcs {
|
||||
for _, c := range cs {
|
||||
cvsses = append(cvsses, types.CVSS{
|
||||
Source: fmt.Sprintf("alma:%s:%s", v, id),
|
||||
Version: c.Version,
|
||||
Vector: c.Vector,
|
||||
Score: c.Score,
|
||||
Severity: c.Severity,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
for v, idcs := range src.Amazon {
|
||||
for id, cs := range idcs {
|
||||
for _, c := range cs {
|
||||
cvsses = append(cvsses, types.CVSS{
|
||||
Source: fmt.Sprintf("amazon:%s:%s", v, id),
|
||||
Version: c.Version,
|
||||
Vector: c.Vector,
|
||||
Score: c.Score,
|
||||
Severity: c.Severity,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
for id, cs := range src.Arch {
|
||||
for _, c := range cs {
|
||||
cvsses = append(cvsses, types.CVSS{
|
||||
Source: fmt.Sprintf("arch:%s", id),
|
||||
Version: c.Version,
|
||||
Vector: c.Vector,
|
||||
Score: c.Score,
|
||||
Severity: c.Severity,
|
||||
})
|
||||
}
|
||||
}
|
||||
for v, idcs := range src.Oracle {
|
||||
for id, cs := range idcs {
|
||||
for _, c := range cs {
|
||||
cvsses = append(cvsses, types.CVSS{
|
||||
Source: fmt.Sprintf("oracle:%s:%s", v, id),
|
||||
Version: c.Version,
|
||||
Vector: c.Vector,
|
||||
Score: c.Score,
|
||||
Severity: c.Severity,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
for v, idcs := range src.RedHatOVAL {
|
||||
for id, cs := range idcs {
|
||||
for _, c := range cs {
|
||||
cvsses = append(cvsses, types.CVSS{
|
||||
Source: fmt.Sprintf("redhat_oval:%s:%s", v, id),
|
||||
Version: c.Version,
|
||||
Vector: c.Vector,
|
||||
Score: c.Score,
|
||||
Severity: c.Severity,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
for v, idcs := range src.SUSEOVAL {
|
||||
for id, cs := range idcs {
|
||||
for _, c := range cs {
|
||||
cvsses = append(cvsses, types.CVSS{
|
||||
Source: fmt.Sprintf("suse_oval:%s:%s", v, id),
|
||||
Version: c.Version,
|
||||
Vector: c.Vector,
|
||||
Score: c.Score,
|
||||
Severity: c.Severity,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, c := range src.SUSECVRF {
|
||||
cvsses = append(cvsses, types.CVSS{
|
||||
Source: "suse_cvrf",
|
||||
Version: c.Version,
|
||||
Vector: c.Vector,
|
||||
Score: c.Score,
|
||||
Severity: c.Severity,
|
||||
})
|
||||
}
|
||||
for v, idcs := range src.UbuntuOVAL {
|
||||
for id, cs := range idcs {
|
||||
for _, c := range cs {
|
||||
cvsses = append(cvsses, types.CVSS{
|
||||
Source: fmt.Sprintf("ubuntu_oval:%s:%s", v, id),
|
||||
Version: c.Version,
|
||||
Vector: c.Vector,
|
||||
Score: c.Score,
|
||||
Severity: c.Severity,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, c := range src.UbuntuSecurityTracker {
|
||||
cvsses = append(cvsses, types.CVSS{
|
||||
Source: "ubuntu_security_tracker",
|
||||
Version: c.Version,
|
||||
Vector: c.Vector,
|
||||
Score: c.Score,
|
||||
Severity: c.Severity,
|
||||
})
|
||||
}
|
||||
|
||||
return cvsses
|
||||
}
|
||||
|
||||
func toVulsEPSS(src *EPSS) *types.EPSS {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
return &types.EPSS{EPSS: src.EPSS, Percentile: src.Percentile}
|
||||
}
|
||||
|
||||
func toVulsCWE(src *CWEs) []types.CWE {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
m := map[string][]string{}
|
||||
for _, c := range src.NVD {
|
||||
m[c] = append(m[c], "nvd")
|
||||
}
|
||||
for id, cs := range src.JVN {
|
||||
for _, c := range cs {
|
||||
m[c] = append(m[c], fmt.Sprintf("jvn:%s", id))
|
||||
}
|
||||
}
|
||||
for v, idcs := range src.RedHatOVAL {
|
||||
for id, cs := range idcs {
|
||||
for _, c := range cs {
|
||||
m[c] = append(m[c], fmt.Sprintf("redhat_oval:%s:%s", v, id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var cwes []types.CWE
|
||||
for id, srcs := range m {
|
||||
cwes = append(cwes, types.CWE{
|
||||
Source: srcs,
|
||||
ID: id,
|
||||
})
|
||||
}
|
||||
return cwes
|
||||
}
|
||||
|
||||
func toVulsMetasploit(src []Metasploit) []types.Metasploit {
|
||||
ms := make([]types.Metasploit, 0, len(src))
|
||||
for _, m := range src {
|
||||
ms = append(ms, types.Metasploit{
|
||||
Title: m.Title,
|
||||
URL: m.URLs[0],
|
||||
})
|
||||
}
|
||||
return ms
|
||||
}
|
||||
|
||||
func toVulsExploit(src *Exploit) []types.Exploit {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
m := map[string][]string{}
|
||||
for _, e := range src.NVD {
|
||||
m[e] = append(m[e], "nvd")
|
||||
}
|
||||
for _, e := range src.ExploitDB {
|
||||
m[e.URL] = append(m[e.URL], "exploit-db")
|
||||
}
|
||||
for _, e := range src.GitHub {
|
||||
m[e.URL] = append(m[e.URL], "github")
|
||||
}
|
||||
for _, e := range src.InTheWild {
|
||||
m[e.URL] = append(m[e.URL], "inthewild")
|
||||
}
|
||||
if src.Trickest != nil {
|
||||
if src.Trickest.PoC != nil {
|
||||
for _, e := range src.Trickest.PoC.Reference {
|
||||
m[e] = append(m[e], "trickest")
|
||||
}
|
||||
for _, e := range src.Trickest.PoC.GitHub {
|
||||
m[e] = append(m[e], "trickest")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var es []types.Exploit
|
||||
for u, srcs := range m {
|
||||
es = append(es, types.Exploit{
|
||||
Source: srcs,
|
||||
URL: u,
|
||||
})
|
||||
}
|
||||
return es
|
||||
}
|
||||
|
||||
func toVulsPublished(src *Publisheds) *time.Time {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if src.NVD != nil {
|
||||
return src.NVD
|
||||
}
|
||||
if src.MITRE != nil {
|
||||
return src.MITRE
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func toVulsModified(src *Modifieds) *time.Time {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if src.NVD != nil {
|
||||
return src.NVD
|
||||
}
|
||||
if src.MITRE != nil {
|
||||
return src.MITRE
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func toVulsReference(src *References) []string {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
m := map[string]struct{}{}
|
||||
for _, r := range src.MITRE {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
for _, r := range src.NVD {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
for _, rs := range src.JVN {
|
||||
for _, r := range rs {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
}
|
||||
for _, idrs := range src.Alma {
|
||||
for _, rs := range idrs {
|
||||
for _, r := range rs {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, idrs := range src.Amazon {
|
||||
for _, rs := range idrs {
|
||||
for _, r := range rs {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, rs := range src.Arch {
|
||||
for _, r := range rs {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
}
|
||||
for _, idrs := range src.DebianOVAL {
|
||||
for _, rs := range idrs {
|
||||
for _, r := range rs {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, rs := range src.DebianSecurityTracker {
|
||||
for _, r := range rs {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
}
|
||||
for _, rs := range src.FreeBSD {
|
||||
for _, r := range rs {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
}
|
||||
for _, idrs := range src.Oracle {
|
||||
for _, rs := range idrs {
|
||||
for _, r := range rs {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, idrs := range src.RedHatOVAL {
|
||||
for _, rs := range idrs {
|
||||
for _, r := range rs {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, idrs := range src.SUSEOVAL {
|
||||
for _, rs := range idrs {
|
||||
for _, r := range rs {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, r := range src.SUSECVRF {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
for _, idrs := range src.UbuntuOVAL {
|
||||
for _, rs := range idrs {
|
||||
for _, r := range rs {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, r := range src.UbuntuSecurityTracker {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
|
||||
return maps.Keys(m)
|
||||
}
|
||||
|
||||
func ToVulsPackage(src DetectPackage, advType string) (map[string]types.Packages, error) {
|
||||
m := map[string]types.Packages{}
|
||||
for id, ps := range src.Packages {
|
||||
id = fmt.Sprintf("%s:%s", advType, id)
|
||||
for _, p := range ps {
|
||||
name, err := toVulsPackageName(p.Name, p.ModularityLabel)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "to vuls package name")
|
||||
}
|
||||
|
||||
base, ok := m[name]
|
||||
if !ok {
|
||||
base = types.Packages{
|
||||
ID: src.ID,
|
||||
Package: map[string]types.Package{},
|
||||
}
|
||||
}
|
||||
|
||||
vers := make([][]types.Version, 0, len(p.Version))
|
||||
for _, vs := range p.Version {
|
||||
vss := make([]types.Version, 0, len(vs))
|
||||
for _, v := range vs {
|
||||
vss = append(vss, types.Version{
|
||||
Operator: v.Operator,
|
||||
Version: v.Version,
|
||||
})
|
||||
}
|
||||
vers = append(vers, vss)
|
||||
}
|
||||
|
||||
base.Package[id] = types.Package{
|
||||
Status: p.Status,
|
||||
Version: vers,
|
||||
Arch: p.Arch,
|
||||
Repository: p.Repository,
|
||||
CPE: p.CPE,
|
||||
}
|
||||
|
||||
m[name] = base
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func toVulsPackageName(name, modularitylabel string) (string, error) {
|
||||
if modularitylabel == "" {
|
||||
return name, nil
|
||||
}
|
||||
|
||||
ss := strings.Split(modularitylabel, ":")
|
||||
if len(ss) < 2 {
|
||||
return name, errors.Errorf(`[WARN] unexpected modularitylabel. accepts: "<module name>:<stream>(:<version>:<context>:<arch>)", received: "%s"`, modularitylabel)
|
||||
}
|
||||
return fmt.Sprintf("%s:%s::%s", ss[0], ss[1], name), nil
|
||||
}
|
||||
|
||||
func ToVulsCPEConfiguration(src DetectCPE, advType string) (map[string]types.CPEConfigurations, error) {
|
||||
m := map[string][]types.CPEConfiguration{}
|
||||
for id, cs := range src.Configurations {
|
||||
id = fmt.Sprintf("%s:%s", advType, id)
|
||||
for _, c := range cs {
|
||||
rs := make([]types.CPE, 0, len(c.RunningOn))
|
||||
for _, r := range c.RunningOn {
|
||||
rs = append(rs, toVulnsrcCPEtoVulsCPE(r))
|
||||
}
|
||||
|
||||
for _, v := range c.Vulnerable {
|
||||
m[id] = append(m[id], types.CPEConfiguration{
|
||||
Vulnerable: toVulnsrcCPEtoVulsCPE(v),
|
||||
RunningOn: rs,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m2 := map[string]types.CPEConfigurations{}
|
||||
for id, cs := range m {
|
||||
for _, c := range cs {
|
||||
pvp, err := toVulsCPEConfigurationName(c.Vulnerable.CPEVersion, c.Vulnerable.CPE)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "to vuls cpe configuration name")
|
||||
}
|
||||
|
||||
base, ok := m2[pvp]
|
||||
if !ok {
|
||||
base = types.CPEConfigurations{
|
||||
ID: src.ID,
|
||||
Configuration: map[string][]types.CPEConfiguration{},
|
||||
}
|
||||
}
|
||||
base.Configuration[id] = append(base.Configuration[id], c)
|
||||
|
||||
m2[pvp] = base
|
||||
}
|
||||
}
|
||||
|
||||
return m2, nil
|
||||
}
|
||||
|
||||
func toVulsCPEConfigurationName(version string, cpe string) (string, error) {
|
||||
var (
|
||||
wfn common.WellFormedName
|
||||
err error
|
||||
)
|
||||
switch version {
|
||||
case "2.3":
|
||||
wfn, err = naming.UnbindFS(cpe)
|
||||
default:
|
||||
wfn, err = naming.UnbindURI(cpe)
|
||||
}
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "unbind %s", cpe)
|
||||
}
|
||||
return fmt.Sprintf("%s:%s:%s", wfn.GetString(common.AttributePart), wfn.GetString(common.AttributeVendor), wfn.GetString(common.AttributeProduct)), nil
|
||||
}
|
||||
|
||||
func toVulnsrcCPEtoVulsCPE(s CPE) types.CPE {
|
||||
d := types.CPE{
|
||||
CPEVersion: s.CPEVersion,
|
||||
CPE: s.CPE,
|
||||
}
|
||||
for _, v := range s.Version {
|
||||
d.Version = append(d.Version, types.Version{
|
||||
Operator: v.Operator,
|
||||
Version: v.Version,
|
||||
})
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func ToVulsRepositoryToCPE(src RepositoryToCPE) types.RepositoryToCPE {
|
||||
return types.RepositoryToCPE(src)
|
||||
}
|
||||
|
||||
func ToVulsSupercedences(src []Supercedence) types.Supercedence {
|
||||
ss := types.Supercedence{}
|
||||
for _, s := range src {
|
||||
ss[s.KBID] = s.Supersededby.KBIDs
|
||||
}
|
||||
return ss
|
||||
}
|
||||
37
pkg/cmd/db/db.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
dbCreateCmd "github.com/future-architect/vuls/pkg/cmd/db/create"
|
||||
dbEditCmd "github.com/future-architect/vuls/pkg/cmd/db/edit"
|
||||
dbFetchCmd "github.com/future-architect/vuls/pkg/cmd/db/fetch"
|
||||
dbSearchCmd "github.com/future-architect/vuls/pkg/cmd/db/search"
|
||||
dbUploadCmd "github.com/future-architect/vuls/pkg/cmd/db/upload"
|
||||
)
|
||||
|
||||
func NewCmdDB() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "db <subcommand>",
|
||||
Short: "Vuls DB Operation",
|
||||
Example: heredoc.Doc(`
|
||||
$ vuls db create https://github.com/vulsio/vuls-data.git
|
||||
$ vuls db create /home/MaineK00n/.cache/vuls
|
||||
$ vuls db edit ubuntu 22.04 openssl
|
||||
$ vuls db edit vulnerability CVE-2022-3602
|
||||
$ vuls db fetch
|
||||
$ vuls db fetch ghcr.io/vuls/db
|
||||
$ vuls db search ubuntu 22.04 openssl
|
||||
$ vuls db search vulnerability CVE-2022-3602
|
||||
`),
|
||||
}
|
||||
|
||||
cmd.AddCommand(dbCreateCmd.NewCmdCreate())
|
||||
cmd.AddCommand(dbEditCmd.NewCmdEdit())
|
||||
cmd.AddCommand(dbFetchCmd.NewCmdFetch())
|
||||
cmd.AddCommand(dbSearchCmd.NewCmdSearch())
|
||||
cmd.AddCommand(dbUploadCmd.NewCmdUpload())
|
||||
|
||||
return cmd
|
||||
}
|
||||
23
pkg/cmd/db/edit/edit.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package edit
|
||||
|
||||
import (
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func NewCmdEdit() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "edit",
|
||||
Short: "Edit Data in Vuls DB",
|
||||
Args: cobra.RangeArgs(2, 3),
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return nil
|
||||
},
|
||||
Example: heredoc.Doc(`
|
||||
$ vuls db edit ubuntu 22.04 openssl
|
||||
$ vuls db edit vulnerability CVE-2022-3602
|
||||
`),
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
152
pkg/cmd/db/fetch/fetch.go
Normal file
@@ -0,0 +1,152 @@
|
||||
package fetch
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"oras.land/oras-go/v2/content"
|
||||
"oras.land/oras-go/v2/registry/remote"
|
||||
)
|
||||
|
||||
type DBFetchOption struct {
|
||||
Path string
|
||||
PlainHTTP bool
|
||||
}
|
||||
|
||||
const (
|
||||
defaultVulsDBRepository = "ghcr.io/mainek00n/vuls-data/vuls-db"
|
||||
defaultTag = "latest"
|
||||
vulsDBConfigMediaType = "application/vnd.vuls.vuls.db"
|
||||
vulsDBLayerMediaType = "application/vnd.vuls.vuls.db.layer.v1.tar+gzip"
|
||||
)
|
||||
|
||||
func NewCmdFetch() *cobra.Command {
|
||||
opts := &DBFetchOption{
|
||||
Path: "vuls.db",
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "fetch",
|
||||
Short: "Fetch Vuls DB",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
p := defaultVulsDBRepository
|
||||
if len(args) > 0 {
|
||||
p = args[0]
|
||||
}
|
||||
return fetch(context.Background(), p, opts.Path, opts.PlainHTTP)
|
||||
},
|
||||
Example: heredoc.Doc(`
|
||||
$ vuls db fetch
|
||||
$ vuls db fetch ghcr.io/vuls/db
|
||||
`),
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&opts.Path, "path", "p", "vuls.db", "path to fetch Vuls DB")
|
||||
cmd.Flags().BoolVarP(&opts.PlainHTTP, "plain-http", "", false, "container registry is provided with plain http")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func fetch(ctx context.Context, ref, dbpath string, plainHTTP bool) error {
|
||||
repo, err := remote.NewRepository(ref)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
if plainHTTP {
|
||||
repo.PlainHTTP = true
|
||||
}
|
||||
|
||||
desc, err := repo.Resolve(ctx, defaultTag)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
pulledBlob, err := content.FetchAll(ctx, repo, desc)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
var manifest ocispec.Manifest
|
||||
if err := json.Unmarshal(pulledBlob, &manifest); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
if manifest.Config.MediaType != vulsDBConfigMediaType {
|
||||
return errors.New("not vuls repository")
|
||||
}
|
||||
|
||||
for _, l := range manifest.Layers {
|
||||
if l.MediaType != vulsDBLayerMediaType {
|
||||
continue
|
||||
}
|
||||
|
||||
desc, err := repo.Blobs().Resolve(ctx, l.Digest.String())
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
rc, err := repo.Fetch(ctx, desc)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
bs, err := content.ReadAll(rc, desc)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
gr, err := gzip.NewReader(bytes.NewReader(bs))
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
defer gr.Close()
|
||||
|
||||
tr := tar.NewReader(gr)
|
||||
for {
|
||||
header, err := tr.Next()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return errors.Wrap(err, "Next()")
|
||||
}
|
||||
|
||||
switch header.Typeflag {
|
||||
case tar.TypeDir:
|
||||
if err := os.MkdirAll(filepath.Join(dbpath, header.Name), header.FileInfo().Mode()); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
case tar.TypeReg:
|
||||
if err := func() error {
|
||||
f, err := os.OpenFile(dbpath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, os.FileMode(header.Mode))
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if _, err := io.Copy(f, tr); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}(); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errors.Errorf("unknown type: %s in %s", header.Typeflag, header.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
23
pkg/cmd/db/search/search.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func NewCmdSearch() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "search",
|
||||
Short: "Search Vulnerabilty/Package in Vuls DB",
|
||||
Args: cobra.RangeArgs(2, 3),
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return nil
|
||||
},
|
||||
Example: heredoc.Doc(`
|
||||
$ vuls db search ubuntu 22.04 openssl
|
||||
$ vuls db search vulnerability CVE-2022-3602
|
||||
`),
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
23
pkg/cmd/db/upload/upload.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package upload
|
||||
|
||||
import (
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func NewCmdUpload() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "upload",
|
||||
Short: "Upload Vuls DB",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return nil
|
||||
},
|
||||
Example: heredoc.Doc(`
|
||||
$ vuls db upload
|
||||
$ vuls db upload ghcr.io/vuls/db
|
||||
`),
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
183
pkg/cmd/detect/detect.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package detect
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/future-architect/vuls/pkg/config"
|
||||
"github.com/future-architect/vuls/pkg/detect"
|
||||
"github.com/future-architect/vuls/pkg/log"
|
||||
"github.com/future-architect/vuls/pkg/types"
|
||||
)
|
||||
|
||||
type DetectOptions struct {
|
||||
Config string
|
||||
}
|
||||
|
||||
func NewCmdDetect() *cobra.Command {
|
||||
opts := &DetectOptions{
|
||||
Config: "config.json",
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "detect ([\"host\"])",
|
||||
Short: "Vuls detect vulnerabilities",
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
if err := exec(context.Background(), opts.Config, args); err != nil {
|
||||
return errors.Wrap(err, "failed to detect")
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
Example: heredoc.Doc(`
|
||||
$ vuls detect
|
||||
$ vuls detect results/**/host.json
|
||||
`),
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&opts.Config, "config", "c", "config.json", "vuls config file path")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func exec(ctx context.Context, path string, args []string) error {
|
||||
logger, err := zap.NewProduction()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "create logger")
|
||||
}
|
||||
|
||||
ctx = log.ContextWithLogger(ctx, logger)
|
||||
|
||||
c, err := config.Open(path)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "open %s as config", path)
|
||||
}
|
||||
|
||||
if len(args) == 0 {
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "get working direcotry")
|
||||
}
|
||||
|
||||
fs, err := os.ReadDir(filepath.Join(pwd, "results"))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "read %s", filepath.Join(pwd, "results"))
|
||||
}
|
||||
|
||||
var ds []time.Time
|
||||
for _, f := range fs {
|
||||
if !f.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
t, err := time.Parse("2006-01-02T150405-0700", f.Name())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ds = append(ds, t)
|
||||
}
|
||||
if len(ds) == 0 {
|
||||
return errors.Wrapf(err, "result dir not found")
|
||||
}
|
||||
|
||||
slices.SortFunc(ds, func(e1, e2 time.Time) bool {
|
||||
return e1.After(e2)
|
||||
})
|
||||
|
||||
args = append(args, filepath.Join(pwd, "results", ds[0].Format("2006-01-02T150405-0700")))
|
||||
}
|
||||
|
||||
type result struct {
|
||||
error string
|
||||
nCVEs int
|
||||
}
|
||||
detectCVEs := map[string]result{}
|
||||
for _, arg := range args {
|
||||
if err := filepath.WalkDir(arg, func(p string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(p, os.O_RDWR, 0644)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "open %s", p)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var host types.Host
|
||||
if err := json.NewDecoder(f).Decode(&host); err != nil {
|
||||
return errors.Wrapf(err, "decode %s", p)
|
||||
}
|
||||
|
||||
hc, ok := c.Hosts[host.Name]
|
||||
if !ok {
|
||||
return errors.Wrapf(err, "not found %s in %s", host.Name, path)
|
||||
}
|
||||
host.Config.Detect = &hc.Detect
|
||||
|
||||
host.ScannedCves = nil
|
||||
host.DetectError = ""
|
||||
|
||||
if err := detect.Detect(ctx, &host); err != nil {
|
||||
host.DetectError = err.Error()
|
||||
}
|
||||
|
||||
name := host.Name
|
||||
if host.Family != "" && host.Release != "" {
|
||||
name = fmt.Sprintf("%s (%s %s)", host.Name, host.Family, host.Release)
|
||||
}
|
||||
errstr := host.DetectError
|
||||
if host.ScanError != "" {
|
||||
errstr = fmt.Sprintf("scan error: %s", host.ScanError)
|
||||
}
|
||||
|
||||
detectCVEs[name] = result{
|
||||
error: errstr,
|
||||
nCVEs: len(host.ScannedCves),
|
||||
}
|
||||
|
||||
if err := f.Truncate(0); err != nil {
|
||||
return errors.Wrap(err, "truncate file")
|
||||
}
|
||||
if _, err := f.Seek(0, 0); err != nil {
|
||||
return errors.Wrap(err, "set offset")
|
||||
}
|
||||
enc := json.NewEncoder(f)
|
||||
enc.SetIndent("", " ")
|
||||
if err := enc.Encode(host); err != nil {
|
||||
return errors.Wrap(err, "encode json")
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return errors.Wrapf(err, "walk %s", arg)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("Detect Summary")
|
||||
fmt.Println("==============")
|
||||
for name, r := range detectCVEs {
|
||||
if r.error != "" {
|
||||
fmt.Printf("%s : error msg: %s\n", name, r.error)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("%s : success %d CVEs detected\n", name, r.nCVEs)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
245
pkg/cmd/report/report.go
Normal file
@@ -0,0 +1,245 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/future-architect/vuls/pkg/log"
|
||||
"github.com/future-architect/vuls/pkg/types"
|
||||
)
|
||||
|
||||
type ReportOptions struct {
|
||||
Format string
|
||||
}
|
||||
|
||||
func NewCmdReport() *cobra.Command {
|
||||
opts := &ReportOptions{
|
||||
Format: "oneline",
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "report (<result path>)",
|
||||
Short: "Vuls report vulnerabilities",
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
if err := exec(context.Background(), opts.Format, args); err != nil {
|
||||
return errors.Wrap(err, "failed to report")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Example: heredoc.Doc(`
|
||||
$ vuls report
|
||||
$ vuls report results
|
||||
$ vuls report resutls/2022-11-05T01:08:44+09:00/local/localhost.json
|
||||
`),
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&opts.Format, "format", "f", "oneline", "stdout format")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
type affectedPackage struct {
|
||||
name string
|
||||
status string
|
||||
source string
|
||||
}
|
||||
type result struct {
|
||||
cveid string
|
||||
cvssVector string
|
||||
cvssScore *float64
|
||||
epss *float64
|
||||
kev bool
|
||||
packages []affectedPackage
|
||||
}
|
||||
|
||||
func exec(ctx context.Context, format string, args []string) error {
|
||||
logger, err := zap.NewProduction()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "create logger")
|
||||
}
|
||||
|
||||
ctx = log.ContextWithLogger(ctx, logger)
|
||||
|
||||
if len(args) == 0 {
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "get working direcotry")
|
||||
}
|
||||
|
||||
fs, err := os.ReadDir(filepath.Join(pwd, "results"))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "read %s", filepath.Join(pwd, "results"))
|
||||
}
|
||||
|
||||
var ds []time.Time
|
||||
for _, f := range fs {
|
||||
if !f.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
t, err := time.Parse("2006-01-02T150405-0700", f.Name())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ds = append(ds, t)
|
||||
}
|
||||
if len(ds) == 0 {
|
||||
return errors.Wrapf(err, "result dir not found")
|
||||
}
|
||||
|
||||
slices.SortFunc(ds, func(e1, e2 time.Time) bool {
|
||||
return e1.After(e2)
|
||||
})
|
||||
|
||||
args = append(args, filepath.Join(pwd, "results", ds[0].Format("2006-01-02T150405-0700")))
|
||||
}
|
||||
|
||||
rs := map[string][]result{}
|
||||
for _, arg := range args {
|
||||
if err := filepath.WalkDir(arg, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "open %s", path)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var host types.Host
|
||||
if err := json.NewDecoder(f).Decode(&host); err != nil {
|
||||
return errors.Wrapf(err, "decode %s", path)
|
||||
}
|
||||
|
||||
name := host.Name
|
||||
if host.Family != "" && host.Release != "" {
|
||||
name = fmt.Sprintf("%s (%s %s)", host.Name, host.Family, host.Release)
|
||||
}
|
||||
|
||||
for id, vinfo := range host.ScannedCves {
|
||||
r := result{
|
||||
cveid: id,
|
||||
}
|
||||
|
||||
if officialCont, ok := vinfo.Content["official"]; ok {
|
||||
for _, c := range officialCont.CVSS {
|
||||
if c.Source != "nvd" || strings.HasPrefix(c.Version, "3") {
|
||||
continue
|
||||
}
|
||||
r.cvssVector = c.Vector
|
||||
r.cvssScore = c.Score
|
||||
}
|
||||
if officialCont.EPSS != nil {
|
||||
r.epss = officialCont.EPSS.EPSS
|
||||
}
|
||||
r.kev = officialCont.KEV
|
||||
}
|
||||
|
||||
for _, p := range vinfo.AffectedPackages {
|
||||
r.packages = append(r.packages, affectedPackage{
|
||||
name: p.Name,
|
||||
status: p.Status,
|
||||
source: p.Source,
|
||||
})
|
||||
}
|
||||
|
||||
rs[name] = append(rs[name], r)
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return errors.Wrapf(err, "walk %s", arg)
|
||||
}
|
||||
}
|
||||
|
||||
switch format {
|
||||
case "oneline":
|
||||
formatOneline(rs)
|
||||
case "list":
|
||||
formatList(rs)
|
||||
default:
|
||||
return errors.Errorf("%s is not implemented format", format)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatOneline(rs map[string][]result) {
|
||||
for name, lines := range rs {
|
||||
fmt.Println(name)
|
||||
fmt.Println(strings.Repeat("=", len(name)))
|
||||
|
||||
status := map[string]int{}
|
||||
for _, l := range lines {
|
||||
for _, p := range l.packages {
|
||||
s := p.status
|
||||
if p.status == "" {
|
||||
s = "(none)"
|
||||
}
|
||||
status[s]++
|
||||
}
|
||||
}
|
||||
|
||||
var ss []string
|
||||
for s, num := range status {
|
||||
ss = append(ss, fmt.Sprintf("%s: %d", s, num))
|
||||
}
|
||||
fmt.Printf("%d CVEs detected. package status: %s\n\n", len(lines), strings.Join(ss, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
func formatList(rs map[string][]result) {
|
||||
for name, lines := range rs {
|
||||
slices.SortFunc(lines, func(l1, l2 result) bool {
|
||||
s1, s2 := 0.0, 0.0
|
||||
if l1.cvssScore != nil {
|
||||
s1 = *l1.cvssScore
|
||||
}
|
||||
if l2.cvssScore != nil {
|
||||
s2 = *l2.cvssScore
|
||||
}
|
||||
return s1 > s2
|
||||
})
|
||||
|
||||
fmt.Println(name)
|
||||
fmt.Println(strings.Repeat("=", len(name)))
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader([]string{"CVEID", "Vector", "CVSS", "EPSS", "KEV", "Package", "Status", "Source"})
|
||||
table.SetAutoMergeCells(true)
|
||||
table.SetRowLine(true)
|
||||
for _, l := range lines {
|
||||
for _, p := range l.packages {
|
||||
var score string
|
||||
if l.cvssScore != nil {
|
||||
score = fmt.Sprintf("%.1f", *l.cvssScore)
|
||||
}
|
||||
var epss string
|
||||
if l.epss != nil {
|
||||
epss = fmt.Sprintf("%f", *l.epss)
|
||||
}
|
||||
source, _, _ := strings.Cut(p.source, ":")
|
||||
table.Append([]string{l.cveid, l.cvssVector, score, epss, fmt.Sprintf("%v", l.kev), p.name, p.status, source})
|
||||
}
|
||||
}
|
||||
table.Render()
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
44
pkg/cmd/root/root.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package root
|
||||
|
||||
import (
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
configCmd "github.com/future-architect/vuls/pkg/cmd/config"
|
||||
dbCmd "github.com/future-architect/vuls/pkg/cmd/db"
|
||||
detectCmd "github.com/future-architect/vuls/pkg/cmd/detect"
|
||||
reportCmd "github.com/future-architect/vuls/pkg/cmd/report"
|
||||
scanCmd "github.com/future-architect/vuls/pkg/cmd/scan"
|
||||
serverCmd "github.com/future-architect/vuls/pkg/cmd/server"
|
||||
tuiCmd "github.com/future-architect/vuls/pkg/cmd/tui"
|
||||
versionCmd "github.com/future-architect/vuls/pkg/cmd/version"
|
||||
)
|
||||
|
||||
func NewCmdRoot() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "vuls <command>",
|
||||
Short: "Vuls",
|
||||
Long: "Vulnerability Scanner: Vuls",
|
||||
SilenceErrors: true,
|
||||
SilenceUsage: true,
|
||||
Example: heredoc.Doc(`
|
||||
$ vuls config init
|
||||
$ vuls db fetch
|
||||
$ vuls scan
|
||||
$ vuls detect
|
||||
$ vuls report
|
||||
$ vuls tui
|
||||
`),
|
||||
}
|
||||
|
||||
cmd.AddCommand(configCmd.NewCmdConfig())
|
||||
cmd.AddCommand(dbCmd.NewCmdDB())
|
||||
cmd.AddCommand(detectCmd.NewCmdDetect())
|
||||
cmd.AddCommand(reportCmd.NewCmdReport())
|
||||
cmd.AddCommand(scanCmd.NewCmdScan())
|
||||
cmd.AddCommand(serverCmd.NewCmdServer())
|
||||
cmd.AddCommand(tuiCmd.NewCmdTUI())
|
||||
cmd.AddCommand(versionCmd.NewCmdVersion())
|
||||
|
||||
return cmd
|
||||
}
|
||||
136
pkg/cmd/scan/scan.go
Normal file
@@ -0,0 +1,136 @@
|
||||
package scan
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
"github.com/future-architect/vuls/pkg/config"
|
||||
"github.com/future-architect/vuls/pkg/log"
|
||||
"github.com/future-architect/vuls/pkg/scan"
|
||||
"github.com/future-architect/vuls/pkg/types"
|
||||
)
|
||||
|
||||
type ScanOptions struct {
|
||||
Config string
|
||||
}
|
||||
|
||||
func NewCmdScan() *cobra.Command {
|
||||
opts := &ScanOptions{
|
||||
Config: "config.json",
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "scan ([\"host\"])",
|
||||
Short: "Vuls scan your machine information",
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
if err := exec(context.Background(), opts.Config, args); err != nil {
|
||||
return errors.Wrap(err, "failed to scan")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Example: heredoc.Doc(`
|
||||
$ vuls scan
|
||||
$ vuls scan host
|
||||
`),
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&opts.Config, "config", "c", "config.json", "vuls config file path")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func exec(ctx context.Context, path string, args []string) error {
|
||||
logger, err := zap.NewProduction()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "create logger")
|
||||
}
|
||||
|
||||
ctx = log.ContextWithLogger(ctx, logger)
|
||||
|
||||
c, err := config.Open(path)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "open %s as config", path)
|
||||
}
|
||||
|
||||
hosts := []types.Host{}
|
||||
targets := args
|
||||
if len(args) == 0 {
|
||||
targets = maps.Keys(c.Hosts)
|
||||
}
|
||||
for _, t := range targets {
|
||||
h, ok := c.Hosts[t]
|
||||
if !ok {
|
||||
return errors.Errorf("host %s is not defined in config %s", t, path)
|
||||
}
|
||||
|
||||
hosts = append(hosts, types.Host{
|
||||
Name: t,
|
||||
Config: types.Config{
|
||||
Type: h.Type,
|
||||
Host: h.Host,
|
||||
Port: h.Port,
|
||||
User: h.User,
|
||||
SSHConfig: h.SSHConfig,
|
||||
SSHKey: h.SSHKey,
|
||||
Scan: &h.Scan,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
for i := range hosts {
|
||||
if err := scan.Scan(ctx, &hosts[i]); err != nil {
|
||||
hosts[i].ScanError = err.Error()
|
||||
}
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
for _, h := range hosts {
|
||||
if err := func() error {
|
||||
resultDir := filepath.Join(h.Config.Scan.ResultDir, now.Format("2006-01-02T150405-0700"))
|
||||
if err := os.MkdirAll(resultDir, os.ModePerm); err != nil {
|
||||
return errors.Wrapf(err, "mkdir %s", resultDir)
|
||||
}
|
||||
f, err := os.Create(filepath.Join(resultDir, fmt.Sprintf("%s.json", h.Name)))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "create %s", filepath.Join(resultDir, fmt.Sprintf("%s.json", h.Name)))
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
enc := json.NewEncoder(f)
|
||||
enc.SetIndent("", " ")
|
||||
if err := enc.Encode(h); err != nil {
|
||||
return errors.Wrapf(err, "encode %s result", h.Name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}(); err != nil {
|
||||
return errors.Wrapf(err, "write %s result", h.Name)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("Scan Summary")
|
||||
fmt.Println("============")
|
||||
for _, h := range hosts {
|
||||
name := h.Name
|
||||
if h.Family != "" && h.Release != "" {
|
||||
name = fmt.Sprintf("%s (%s %s)", h.Name, h.Family, h.Release)
|
||||
}
|
||||
if h.ScanError != "" {
|
||||
fmt.Printf("%s : error msg: %s\n", name, h.ScanError)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("%s: success ospkg: %d, cpe: %d, KB %d installed\n", name, len(h.Packages.OSPkg), len(h.Packages.CPE), len(h.Packages.KB))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
78
pkg/cmd/server/server.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/future-architect/vuls/pkg/config"
|
||||
"github.com/future-architect/vuls/pkg/log"
|
||||
"github.com/future-architect/vuls/pkg/server"
|
||||
)
|
||||
|
||||
type Serveroptions struct {
|
||||
Config string
|
||||
}
|
||||
|
||||
func NewCmdServer() *cobra.Command {
|
||||
opts := &Serveroptions{
|
||||
Config: "config.json",
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "server",
|
||||
Short: "Vuls start server mode",
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
if err := exec(context.Background(), opts.Config); err != nil {
|
||||
return errors.Wrap(err, "failed to server")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Example: heredoc.Doc(`
|
||||
$ vuls server
|
||||
`),
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&opts.Config, "config", "c", "config.json", "vuls config file path")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func exec(ctx context.Context, path string) error {
|
||||
logger, err := zap.NewProduction()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "create logger")
|
||||
}
|
||||
|
||||
ctx = log.ContextWithLogger(ctx, logger)
|
||||
|
||||
c, err := config.Open(path)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "open %s as config", path)
|
||||
}
|
||||
|
||||
if c.Server == nil {
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "get working directory")
|
||||
}
|
||||
|
||||
c.Server = &config.Server{
|
||||
Listen: "127.0.0.1:5515",
|
||||
Path: filepath.Join(pwd, "vuls.db"),
|
||||
}
|
||||
}
|
||||
|
||||
e := echo.New()
|
||||
e.POST("/scan", server.Scan())
|
||||
e.POST("/detect", server.Detect(c.Server.Path))
|
||||
|
||||
return e.Start(c.Server.Listen)
|
||||
}
|
||||
33
pkg/cmd/tui/tui.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package tui
|
||||
|
||||
import (
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type TUIOptions struct {
|
||||
Config string
|
||||
}
|
||||
|
||||
func NewCmdTUI() *cobra.Command {
|
||||
opts := &TUIOptions{
|
||||
Config: "config.json",
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "tui (<result path>)",
|
||||
Short: "View vulnerabilities detected by TUI",
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return nil
|
||||
},
|
||||
Example: heredoc.Doc(`
|
||||
$ vuls tui
|
||||
$ vuls tui results
|
||||
$ vuls tui resutls/2022-11-05T01:08:44+09:00/local/localhost.json
|
||||
`),
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&opts.Config, "config", "c", "config.json", "vuls config file path")
|
||||
|
||||
return cmd
|
||||
}
|
||||
25
pkg/cmd/version/version.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package version
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
Version string
|
||||
Revision string
|
||||
)
|
||||
|
||||
func NewCmdVersion() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Print the version",
|
||||
Args: cobra.NoArgs,
|
||||
Run: func(_ *cobra.Command, _ []string) {
|
||||
fmt.Fprintf(os.Stdout, "vuls %s %s\n", Version, Revision)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
59
pkg/config/config.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func Open(path string) (Config, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return Config{}, errors.Wrapf(err, "open %s", path)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var src Config
|
||||
if err := json.NewDecoder(f).Decode(&src); err != nil {
|
||||
return Config{}, errors.Wrap(err, "decode json")
|
||||
}
|
||||
|
||||
u, err := user.Current()
|
||||
if err != nil {
|
||||
return Config{}, errors.Wrap(err, "get current user")
|
||||
}
|
||||
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return Config{}, errors.Wrap(err, "get working directory")
|
||||
}
|
||||
|
||||
config := Config{Server: src.Server, Hosts: map[string]Host{}}
|
||||
for n, h := range src.Hosts {
|
||||
c := Host{
|
||||
Type: h.Type,
|
||||
Host: h.Host,
|
||||
Port: h.Port,
|
||||
User: h.User,
|
||||
SSHConfig: h.SSHConfig,
|
||||
SSHKey: h.SSHKey,
|
||||
Scan: h.Scan,
|
||||
Detect: h.Detect,
|
||||
}
|
||||
if c.User == nil {
|
||||
c.User = &u.Name
|
||||
}
|
||||
if c.Scan.ResultDir == "" {
|
||||
c.Scan.ResultDir = filepath.Join(pwd, "results")
|
||||
}
|
||||
if c.Detect.ResultDir == "" {
|
||||
c.Detect.ResultDir = filepath.Join(pwd, "results")
|
||||
}
|
||||
config.Hosts[n] = c
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
41
pkg/config/types.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package config
|
||||
|
||||
type Config struct {
|
||||
Server *Server `json:"server"`
|
||||
Hosts map[string]Host `json:"hosts"`
|
||||
}
|
||||
|
||||
type Scan struct {
|
||||
OSPkg *scanOSPkg `json:"ospkg,omitempty"`
|
||||
CPE []scanCPE `json:"cpe,omitempty"`
|
||||
ResultDir string `json:"result_dir,omitempty"`
|
||||
}
|
||||
|
||||
type scanOSPkg struct {
|
||||
Root bool `json:"root"`
|
||||
}
|
||||
|
||||
type scanCPE struct {
|
||||
CPE string `json:"cpe,omitempty"`
|
||||
RunningOn string `json:"running_on,omitempty"`
|
||||
}
|
||||
|
||||
type Detect struct {
|
||||
Path string `json:"path"`
|
||||
ResultDir string `json:"result_dir"`
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
Listen string `json:"listen"`
|
||||
Path string `json:"path"`
|
||||
}
|
||||
type Host struct {
|
||||
Type string `json:"type"`
|
||||
Host *string `json:"host"`
|
||||
Port *string `json:"port"`
|
||||
User *string `json:"user"`
|
||||
SSHConfig *string `json:"ssh_config"`
|
||||
SSHKey *string `json:"ssh_key"`
|
||||
Scan Scan `json:"scan"`
|
||||
Detect Detect `json:"detect"`
|
||||
}
|
||||
490
pkg/db/boltdb/boltdb.go
Normal file
@@ -0,0 +1,490 @@
|
||||
package boltdb
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
"github.com/future-architect/vuls/pkg/db/types"
|
||||
)
|
||||
|
||||
type options struct {
|
||||
}
|
||||
|
||||
type Option interface {
|
||||
apply(*options)
|
||||
}
|
||||
|
||||
type DB struct {
|
||||
conn *bolt.DB
|
||||
}
|
||||
|
||||
func Open(dbPath string, debug bool, opts ...Option) (*DB, error) {
|
||||
db, err := bolt.Open(dbPath, 0666, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "open boltdb")
|
||||
}
|
||||
return &DB{conn: db}, nil
|
||||
}
|
||||
|
||||
func (db *DB) Close() error {
|
||||
if db.conn == nil {
|
||||
return nil
|
||||
}
|
||||
if err := db.conn.Close(); err != nil {
|
||||
return errors.Wrap(err, "close boltdb")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) PutVulnerability(src, key string, value types.Vulnerability) error {
|
||||
bucket, id, found := strings.Cut(key, ":")
|
||||
if !found {
|
||||
return errors.Errorf(`unexpected key. accepts: "vulnerability:<Vulnerability ID>, received: "%s"`, key)
|
||||
}
|
||||
if err := db.conn.Update(func(tx *bolt.Tx) error {
|
||||
b, err := tx.CreateBucketIfNotExists([]byte(bucket))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "create %s bucket", bucket)
|
||||
}
|
||||
|
||||
vb, err := b.CreateBucketIfNotExists([]byte(id))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "create %s/%s bucket", bucket, id)
|
||||
}
|
||||
|
||||
bs, err := json.MarshalIndent(value, "", " ")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "marshal json")
|
||||
}
|
||||
|
||||
if err := vb.Put([]byte(src), bs); err != nil {
|
||||
return errors.Wrapf(err, "put %%s/%s/%s", bucket, id, src)
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "update db")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) PutPackage(src, key string, value map[string]types.Packages) error {
|
||||
if err := db.conn.Update(func(tx *bolt.Tx) error {
|
||||
name, version, found := strings.Cut(key, ":")
|
||||
if !found && name == "" {
|
||||
return errors.Errorf(`unexpected key. accepts: "<osname>(:<version>)", received: "%s"`, key)
|
||||
}
|
||||
|
||||
bucket := name
|
||||
b, err := tx.CreateBucketIfNotExists([]byte(name))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "create %s bucket", name)
|
||||
}
|
||||
switch name {
|
||||
case "arch", "freebsd", "gentoo":
|
||||
case "redhat":
|
||||
if version == "" {
|
||||
return errors.Errorf(`unexpected key. accepts: "<osname>:<version>", received: "%s"`, key)
|
||||
}
|
||||
b, err = b.CreateBucketIfNotExists([]byte(version[:1]))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "create %s/%s bucket", name, version[:1])
|
||||
}
|
||||
b, err = b.CreateBucketIfNotExists([]byte(version))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "create %s/%s/%s bucket", name, version[:1], version)
|
||||
}
|
||||
bucket = fmt.Sprintf("%s/%s/%s", name, version[:1], version)
|
||||
default:
|
||||
if version == "" {
|
||||
return errors.Errorf(`unexpected key. accepts: "<osname>:<version>", received: "%s"`, key)
|
||||
}
|
||||
b, err = b.CreateBucketIfNotExists([]byte(version))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "crate %s/%s bucket", name, version)
|
||||
}
|
||||
bucket = fmt.Sprintf("%s/%s", name, version)
|
||||
}
|
||||
|
||||
for n, v := range value {
|
||||
pb, err := b.CreateBucketIfNotExists([]byte(n))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "create %s/%s bucket", bucket, n)
|
||||
}
|
||||
|
||||
vb, err := pb.CreateBucketIfNotExists([]byte(v.ID))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "create %s/%s/%s bucket", bucket, n, v.ID)
|
||||
}
|
||||
|
||||
var p map[string]types.Package
|
||||
bs := vb.Get([]byte(src))
|
||||
if len(bs) > 0 {
|
||||
if err := json.Unmarshal(bs, &p); err != nil {
|
||||
return errors.Wrap(err, "unmarshal json")
|
||||
}
|
||||
} else {
|
||||
p = map[string]types.Package{}
|
||||
}
|
||||
maps.Copy(p, v.Package)
|
||||
bs, err = json.MarshalIndent(p, "", " ")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "marshal json")
|
||||
}
|
||||
|
||||
if err := vb.Put([]byte(src), bs); err != nil {
|
||||
return errors.Wrapf(err, "put %s", key)
|
||||
}
|
||||
}
|
||||
|
||||
if name == "windows" {
|
||||
kbToProduct := map[string][]string{}
|
||||
for n, ps := range value {
|
||||
for _, p := range ps.Package {
|
||||
for _, v := range p.Version {
|
||||
if _, err := strconv.Atoi(v[0].Version); err != nil {
|
||||
continue
|
||||
}
|
||||
kbToProduct[v[0].Version] = append(kbToProduct[v[0].Version], n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b, err := tx.CreateBucketIfNotExists([]byte("windows_kb_to_product"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "create windows_kb_to_product bucket")
|
||||
}
|
||||
|
||||
if version == "" {
|
||||
return errors.Errorf(`unexpected key. accepts: "<osname>:<version>", received: "%s"`, key)
|
||||
}
|
||||
b, err = b.CreateBucketIfNotExists([]byte(version))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "create %s/%s bucket", name, version)
|
||||
}
|
||||
|
||||
for kb, ps := range kbToProduct {
|
||||
bs, err := json.Marshal(ps)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "marshal json")
|
||||
}
|
||||
b.Put([]byte(kb), bs)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "update db")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) PutCPEConfiguration(src, key string, value map[string]types.CPEConfigurations) error {
|
||||
if err := db.conn.Update(func(tx *bolt.Tx) error {
|
||||
b, err := tx.CreateBucketIfNotExists([]byte(key))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "create %s bucket", key)
|
||||
}
|
||||
|
||||
for pvp, c := range value {
|
||||
pvpb, err := b.CreateBucketIfNotExists([]byte(pvp))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "create %s/%s bucket", key, pvp)
|
||||
}
|
||||
|
||||
vb, err := pvpb.CreateBucketIfNotExists([]byte(c.ID))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "create %s/%s/%s bucket", key, pvp, c.ID)
|
||||
}
|
||||
|
||||
var v map[string][]types.CPEConfiguration
|
||||
bs := vb.Get([]byte(src))
|
||||
if len(bs) > 0 {
|
||||
if err := json.Unmarshal(bs, &v); err != nil {
|
||||
return errors.Wrap(err, "unmarshal json")
|
||||
}
|
||||
} else {
|
||||
v = map[string][]types.CPEConfiguration{}
|
||||
}
|
||||
maps.Copy(v, c.Configuration)
|
||||
bs, err = json.MarshalIndent(v, "", " ")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "marshal json")
|
||||
}
|
||||
|
||||
if err := vb.Put([]byte(src), bs); err != nil {
|
||||
return errors.Wrapf(err, "put %s", key)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "update db")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) PutRedHatRepoToCPE(src, key string, value types.RepositoryToCPE) error {
|
||||
if err := db.conn.Update(func(tx *bolt.Tx) error {
|
||||
name, version, found := strings.Cut(key, ":")
|
||||
if !found && name == "" {
|
||||
return errors.Errorf(`unexpected key. accepts: "redhat_cpe:<version>", received: "%s"`, key)
|
||||
}
|
||||
|
||||
b, err := tx.CreateBucketIfNotExists([]byte(name))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "create %s bucket", name)
|
||||
}
|
||||
|
||||
b, err = b.CreateBucketIfNotExists([]byte(version[:1]))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "create %s/%s bucket", name, version[:1])
|
||||
}
|
||||
|
||||
b, err = b.CreateBucketIfNotExists([]byte(version))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "create %s/%s/%s bucket", name, version[:1], version)
|
||||
}
|
||||
|
||||
for repo, cpes := range value {
|
||||
rb, err := b.CreateBucketIfNotExists([]byte(repo))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "create %s/%s/%s/%s bucket", name, version[:1], version, repo)
|
||||
}
|
||||
|
||||
bs, err := json.MarshalIndent(cpes, "", " ")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "marshal json")
|
||||
}
|
||||
|
||||
if err := rb.Put([]byte(src), bs); err != nil {
|
||||
return errors.Wrapf(err, "put %s", key)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "update db")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) PutWindowsSupercedence(src, key string, value types.Supercedence) error {
|
||||
if err := db.conn.Update(func(tx *bolt.Tx) error {
|
||||
b, err := tx.CreateBucketIfNotExists([]byte(key))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "create %s bucket", key)
|
||||
}
|
||||
for kb, supercedences := range value {
|
||||
kbb, err := b.CreateBucketIfNotExists([]byte(kb))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "create %s/%s bucket", key, kb)
|
||||
}
|
||||
bs, err := json.Marshal(supercedences)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "marshal json")
|
||||
}
|
||||
|
||||
if err := kbb.Put([]byte(src), bs); err != nil {
|
||||
return errors.Wrapf(err, "put %s", key)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "update db")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) GetVulnerability(ids []string) (map[string]map[string]types.Vulnerability, error) {
|
||||
r := map[string]map[string]types.Vulnerability{}
|
||||
if err := db.conn.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte("vulnerability"))
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
for _, id := range ids {
|
||||
vb := b.Bucket([]byte(id))
|
||||
if vb == nil {
|
||||
return nil
|
||||
}
|
||||
r[string(id)] = map[string]types.Vulnerability{}
|
||||
if err := vb.ForEach(func(src, bs []byte) error {
|
||||
var v types.Vulnerability
|
||||
if err := json.Unmarshal(bs, &v); err != nil {
|
||||
return errors.Wrapf(err, "decode %s/%s", string(id), string(src))
|
||||
}
|
||||
r[string(id)][string(src)] = v
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (db *DB) GetPackage(family, release string, name string) (map[string]map[string]map[string]types.Package, error) {
|
||||
r := map[string]map[string]map[string]types.Package{}
|
||||
if err := db.conn.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte(family))
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
switch family {
|
||||
case "debian", "ubuntu", "windows":
|
||||
b = b.Bucket([]byte(release))
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
b = b.Bucket([]byte(name))
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := b.ForEach(func(cveid, _ []byte) error {
|
||||
vb := b.Bucket(cveid)
|
||||
r[string(cveid)] = map[string]map[string]types.Package{}
|
||||
if err := vb.ForEach(func(src, bs []byte) error {
|
||||
var v map[string]types.Package
|
||||
if err := json.Unmarshal(bs, &v); err != nil {
|
||||
return errors.Wrapf(err, "decode %s/%s", string(cveid), string(src))
|
||||
}
|
||||
r[string(cveid)][string(src)] = v
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (db *DB) GetCPEConfiguration(partvendorproduct string) (map[string]map[string]map[string][]types.CPEConfiguration, error) {
|
||||
r := map[string]map[string]map[string][]types.CPEConfiguration{}
|
||||
if err := db.conn.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte("cpe"))
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
b = b.Bucket([]byte(partvendorproduct))
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := b.ForEach(func(cveid, _ []byte) error {
|
||||
vb := b.Bucket(cveid)
|
||||
r[string(cveid)] = map[string]map[string][]types.CPEConfiguration{}
|
||||
if err := vb.ForEach(func(src, bs []byte) error {
|
||||
var v map[string][]types.CPEConfiguration
|
||||
if err := json.Unmarshal(bs, &v); err != nil {
|
||||
return errors.Wrapf(err, "decode cpe/%s/%s/%s", partvendorproduct, string(cveid), string(src))
|
||||
}
|
||||
r[string(cveid)][string(src)] = v
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (db *DB) GetSupercedence(kbs []string) (map[string][]string, error) {
|
||||
r := map[string][]string{}
|
||||
if err := db.conn.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte("windows_supercedence"))
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
for _, kb := range kbs {
|
||||
kbb := b.Bucket([]byte(kb))
|
||||
if kbb == nil {
|
||||
continue
|
||||
}
|
||||
if err := kbb.ForEach(func(_, v []byte) error {
|
||||
var ss []string
|
||||
if err := json.Unmarshal(v, &ss); err != nil {
|
||||
return errors.Wrapf(err, "decode windows_supercedence/%s", kb)
|
||||
}
|
||||
r[kb] = append(r[kb], ss...)
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (db *DB) GetKBtoProduct(release string, kbs []string) ([]string, error) {
|
||||
var r []string
|
||||
if err := db.conn.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte("windows_kb_to_product"))
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
b = b.Bucket([]byte(release))
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
for _, kb := range kbs {
|
||||
if bs := b.Get([]byte(kb)); len(bs) > 0 {
|
||||
var ps []string
|
||||
if err := json.Unmarshal(bs, &ps); err != nil {
|
||||
return errors.Wrapf(err, "decode windows_kb_to_product/%s/%s", release, kb)
|
||||
}
|
||||
r = append(r, ps...)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
149
pkg/db/db.go
Normal file
@@ -0,0 +1,149 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/future-architect/vuls/pkg/db/boltdb"
|
||||
"github.com/future-architect/vuls/pkg/db/rdb"
|
||||
"github.com/future-architect/vuls/pkg/db/redis"
|
||||
"github.com/future-architect/vuls/pkg/db/types"
|
||||
)
|
||||
|
||||
type options struct {
|
||||
}
|
||||
|
||||
type Option interface {
|
||||
apply(*options)
|
||||
}
|
||||
|
||||
type DB struct {
|
||||
name string
|
||||
driver Driver
|
||||
}
|
||||
|
||||
type Driver interface {
|
||||
Close() error
|
||||
|
||||
PutVulnerability(string, string, types.Vulnerability) error
|
||||
PutPackage(string, string, map[string]types.Packages) error
|
||||
PutCPEConfiguration(string, string, map[string]types.CPEConfigurations) error
|
||||
PutRedHatRepoToCPE(string, string, types.RepositoryToCPE) error
|
||||
PutWindowsSupercedence(string, string, types.Supercedence) error
|
||||
|
||||
GetVulnerability([]string) (map[string]map[string]types.Vulnerability, error)
|
||||
GetPackage(string, string, string) (map[string]map[string]map[string]types.Package, error)
|
||||
GetCPEConfiguration(string) (map[string]map[string]map[string][]types.CPEConfiguration, error)
|
||||
GetSupercedence([]string) (map[string][]string, error)
|
||||
GetKBtoProduct(string, []string) ([]string, error)
|
||||
}
|
||||
|
||||
func (db *DB) Name() string {
|
||||
return db.name
|
||||
}
|
||||
|
||||
func Open(dbType, dbPath string, debug bool, opts ...Option) (*DB, error) {
|
||||
switch dbType {
|
||||
case "boltdb":
|
||||
d, err := boltdb.Open(dbPath, debug)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "open boltdb")
|
||||
}
|
||||
return &DB{name: dbType, driver: d}, nil
|
||||
case "sqlite3", "mysql", "postgres":
|
||||
d, err := rdb.Open(dbType, dbPath, debug)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "open rdb")
|
||||
}
|
||||
return &DB{name: dbType, driver: d}, nil
|
||||
case "redis":
|
||||
d, err := redis.Open(dbPath, debug)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "open rdb")
|
||||
}
|
||||
return &DB{name: dbType, driver: d}, nil
|
||||
default:
|
||||
return nil, errors.Errorf(`unexpected dbType. accepts: ["boltdb", "sqlite3", "mysql", "postgres", "redis"], received: "%s"`, dbType)
|
||||
}
|
||||
}
|
||||
|
||||
func (db *DB) Close() error {
|
||||
if err := db.driver.Close(); err != nil {
|
||||
return errors.Wrapf(err, "close %s", db.name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) PutVulnerability(src, key string, value types.Vulnerability) error {
|
||||
if err := db.driver.PutVulnerability(src, key, value); err != nil {
|
||||
return errors.Wrapf(err, "put vulnerability")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) PutPackage(src, key string, value map[string]types.Packages) error {
|
||||
if err := db.driver.PutPackage(src, key, value); err != nil {
|
||||
return errors.Wrapf(err, "put package")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) PutCPEConfiguration(src, key string, value map[string]types.CPEConfigurations) error {
|
||||
if err := db.driver.PutCPEConfiguration(src, key, value); err != nil {
|
||||
return errors.Wrapf(err, "put cpe configuration")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) PutRedHatRepoToCPE(src, key string, value types.RepositoryToCPE) error {
|
||||
if err := db.driver.PutRedHatRepoToCPE(src, key, value); err != nil {
|
||||
return errors.Wrap(err, "put repository to cpe")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) PutWindowsSupercedence(src, key string, value types.Supercedence) error {
|
||||
if err := db.driver.PutWindowsSupercedence(src, key, value); err != nil {
|
||||
return errors.Wrap(err, "put supercedence")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) GetVulnerability(ids []string) (map[string]map[string]types.Vulnerability, error) {
|
||||
rs, err := db.driver.GetVulnerability(ids)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "get vulnerability")
|
||||
}
|
||||
return rs, nil
|
||||
}
|
||||
|
||||
func (db *DB) GetPackage(family, release string, name string) (map[string]map[string]map[string]types.Package, error) {
|
||||
rs, err := db.driver.GetPackage(family, release, name)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "get package")
|
||||
}
|
||||
return rs, nil
|
||||
}
|
||||
|
||||
func (db *DB) GetCPEConfiguration(partvendorproduct string) (map[string]map[string]map[string][]types.CPEConfiguration, error) {
|
||||
rs, err := db.driver.GetCPEConfiguration(partvendorproduct)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "get cpe configuration")
|
||||
}
|
||||
return rs, nil
|
||||
}
|
||||
|
||||
func (db *DB) GetSupercedence(kb []string) (map[string][]string, error) {
|
||||
rs, err := db.driver.GetSupercedence(kb)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get supercedence")
|
||||
}
|
||||
return rs, nil
|
||||
}
|
||||
|
||||
func (db *DB) GetKBtoProduct(release string, kb []string) ([]string, error) {
|
||||
rs, err := db.driver.GetKBtoProduct(release, kb)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get product from kb")
|
||||
}
|
||||
return rs, nil
|
||||
}
|
||||
110
pkg/db/rdb/rdb.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package rdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
_ "modernc.org/sqlite"
|
||||
|
||||
"github.com/future-architect/vuls/pkg/db/types"
|
||||
)
|
||||
|
||||
type options struct {
|
||||
}
|
||||
|
||||
type Option interface {
|
||||
apply(*options)
|
||||
}
|
||||
|
||||
type DB struct {
|
||||
conn *gorm.DB
|
||||
}
|
||||
|
||||
func Open(dbType, dbPath string, debug bool, opts ...Option) (*DB, error) {
|
||||
switch dbType {
|
||||
case "sqlite3":
|
||||
// db, err := gorm.Open(sqlite.Open(dbPath))
|
||||
db := &gorm.DB{}
|
||||
conn, err := sql.Open("sqlite", dbPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "open sqlite3")
|
||||
}
|
||||
db.ConnPool = conn
|
||||
return &DB{conn: db}, nil
|
||||
case "mysql":
|
||||
db, err := gorm.Open(mysql.Open(dbPath))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "open mysql")
|
||||
}
|
||||
return &DB{conn: db}, nil
|
||||
case "postgres":
|
||||
db, err := gorm.Open(postgres.Open(dbPath))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "open postgres")
|
||||
}
|
||||
return &DB{conn: db}, nil
|
||||
default:
|
||||
return nil, errors.Errorf(`unexpected dbType. accepts: ["sqlite3", "mysql", "postgres"], received: "%s"`, dbType)
|
||||
}
|
||||
}
|
||||
|
||||
func (db *DB) Close() error {
|
||||
if db.conn == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
sqlDB *sql.DB
|
||||
err error
|
||||
)
|
||||
if sqlDB, err = db.conn.DB(); err != nil {
|
||||
return errors.Wrap(err, "get *sql.DB")
|
||||
}
|
||||
if err := sqlDB.Close(); err != nil {
|
||||
return errors.Wrap(err, "close *sql.DB")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) PutVulnerability(src, key string, value types.Vulnerability) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) PutPackage(src, key string, value map[string]types.Packages) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) PutCPEConfiguration(src, key string, value map[string]types.CPEConfigurations) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) PutRedHatRepoToCPE(src, key string, value types.RepositoryToCPE) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) PutWindowsSupercedence(src, key string, value types.Supercedence) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) GetVulnerability(ids []string) (map[string]map[string]types.Vulnerability, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (db *DB) GetPackage(family, release string, name string) (map[string]map[string]map[string]types.Package, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (db *DB) GetCPEConfiguration(partvendorproduct string) (map[string]map[string]map[string][]types.CPEConfiguration, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (db *DB) GetSupercedence(kb []string) (map[string][]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (db *DB) GetKBtoProduct(elease string, kb []string) ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
77
pkg/db/redis/redis.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package redis
|
||||
|
||||
import (
|
||||
"github.com/go-redis/redis/v9"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/future-architect/vuls/pkg/db/types"
|
||||
)
|
||||
|
||||
type options struct {
|
||||
}
|
||||
|
||||
type Option interface {
|
||||
apply(*options)
|
||||
}
|
||||
|
||||
type DB struct {
|
||||
conn *redis.Client
|
||||
}
|
||||
|
||||
func Open(dbPath string, debug bool, opts ...Option) (*DB, error) {
|
||||
redisOpts, err := redis.ParseURL(dbPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "parse redis URL")
|
||||
}
|
||||
return &DB{conn: redis.NewClient(redisOpts)}, nil
|
||||
}
|
||||
|
||||
func (db *DB) Close() error {
|
||||
if db.conn == nil {
|
||||
return nil
|
||||
}
|
||||
if err := db.conn.Close(); err != nil {
|
||||
return errors.Wrap(err, "close redis")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) PutVulnerability(src, key string, value types.Vulnerability) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) PutPackage(src, key string, value map[string]types.Packages) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) PutCPEConfiguration(src, key string, value map[string]types.CPEConfigurations) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) PutRedHatRepoToCPE(src, key string, value types.RepositoryToCPE) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) PutWindowsSupercedence(src, key string, value types.Supercedence) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) GetVulnerability(ids []string) (map[string]map[string]types.Vulnerability, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (db *DB) GetPackage(family, release string, name string) (map[string]map[string]map[string]types.Package, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (db *DB) GetCPEConfiguration(partvendorproduct string) (map[string]map[string]map[string][]types.CPEConfiguration, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (db *DB) GetSupercedence(kb []string) (map[string][]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (db *DB) GetKBtoProduct(release string, kb []string) ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
85
pkg/db/types/types.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package types
|
||||
|
||||
import "time"
|
||||
|
||||
type Vulnerability struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Advisory []string `json:"advisory,omitempty"`
|
||||
Title string `json:"title,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
CVSS []CVSS `json:"cvss,omitempty"`
|
||||
EPSS *EPSS `json:"epss,omitempty"`
|
||||
CWE []CWE `json:"cwe,omitempty"`
|
||||
Metasploit []Metasploit `json:"metasploit,omitempty"`
|
||||
Exploit []Exploit `json:"exploit,omitempty"`
|
||||
KEV bool `json:"kev,omitempty"`
|
||||
Published *time.Time `json:"published,omitempty"`
|
||||
Modified *time.Time `json:"modified,omitempty"`
|
||||
Reference []string `json:"reference,omitempty"`
|
||||
}
|
||||
|
||||
type CVSS struct {
|
||||
Source string `json:"source,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
Vector string `json:"vector,omitempty"`
|
||||
Score *float64 `json:"score,omitempty"`
|
||||
Severity string `json:"severity,omitempty"`
|
||||
}
|
||||
|
||||
type EPSS struct {
|
||||
EPSS *float64 `json:"epss,omitempty"`
|
||||
Percentile *float64 `json:"percentile,omitempty"`
|
||||
}
|
||||
|
||||
type CWE struct {
|
||||
Source []string `json:"source,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
type Metasploit struct {
|
||||
Title string `json:"title,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
}
|
||||
|
||||
type Exploit struct {
|
||||
Source []string `json:"source,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
}
|
||||
|
||||
type CPEConfigurations struct {
|
||||
ID string `json:"-,omitempty"`
|
||||
Configuration map[string][]CPEConfiguration `json:"configuration,omitempty"`
|
||||
}
|
||||
|
||||
type CPEConfiguration struct {
|
||||
Vulnerable CPE `json:"vulnerable,omitempty"`
|
||||
RunningOn []CPE `json:"running_on,omitempty"`
|
||||
}
|
||||
|
||||
type CPE struct {
|
||||
CPEVersion string `json:"cpe_version,omitempty"`
|
||||
CPE string `json:"cpe,omitempty"`
|
||||
Version []Version `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
type Packages struct {
|
||||
ID string `json:"-,omitempty"`
|
||||
Package map[string]Package `json:"package,omitempty"`
|
||||
}
|
||||
|
||||
type Package struct {
|
||||
Status string `json:"status,omitempty"`
|
||||
Version [][]Version `json:"version,omitempty"`
|
||||
Arch []string `json:"arch,omitempty"`
|
||||
Repository string `json:"repository,omitempty"`
|
||||
CPE []string `json:"cpe,omitempty"`
|
||||
}
|
||||
|
||||
type Version struct {
|
||||
Operator string `json:"operator,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
type RepositoryToCPE map[string][]string
|
||||
|
||||
type Supercedence map[string][]string
|
||||
1
pkg/db/util/util.go
Normal file
@@ -0,0 +1 @@
|
||||
package util
|
||||
179
pkg/detect/cpe/cpe.go
Normal file
@@ -0,0 +1,179 @@
|
||||
package cpe
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/go-version"
|
||||
"github.com/knqyf263/go-cpe/common"
|
||||
"github.com/knqyf263/go-cpe/matching"
|
||||
"github.com/knqyf263/go-cpe/naming"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
"github.com/future-architect/vuls/pkg/db"
|
||||
dbTypes "github.com/future-architect/vuls/pkg/db/types"
|
||||
"github.com/future-architect/vuls/pkg/types"
|
||||
"github.com/future-architect/vuls/pkg/util"
|
||||
)
|
||||
|
||||
type Detector struct{}
|
||||
|
||||
func (d Detector) Name() string {
|
||||
return "cpe detector"
|
||||
}
|
||||
|
||||
func (d Detector) Detect(ctx context.Context, host *types.Host) error {
|
||||
if host.ScannedCves == nil {
|
||||
host.ScannedCves = map[string]types.VulnInfo{}
|
||||
}
|
||||
|
||||
vulndb, err := db.Open("boltdb", host.Config.Detect.Path, false)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "open %s", host.Config.Detect.Path)
|
||||
}
|
||||
defer vulndb.Close()
|
||||
|
||||
for key, cpe := range host.Packages.CPE {
|
||||
installed, err := naming.UnbindFS(cpe.CPE)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unbind %s", cpe.CPE)
|
||||
}
|
||||
var runningOn common.WellFormedName
|
||||
if cpe.RunningOn != "" {
|
||||
runningOn, err = naming.UnbindFS(cpe.RunningOn)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unbind %s", cpe.RunningOn)
|
||||
}
|
||||
}
|
||||
|
||||
cpes, err := vulndb.GetCPEConfiguration(fmt.Sprintf("%s:%s:%s", installed.GetString(common.AttributePart), installed.GetString(common.AttributeVendor), installed.GetString(common.AttributeProduct)))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "get cpe configuration")
|
||||
}
|
||||
|
||||
for cveid, datasrcs := range cpes {
|
||||
for datasrc, orcs := range datasrcs {
|
||||
for id, andcs := range orcs {
|
||||
for _, c := range andcs {
|
||||
affected, err := compare(installed, &runningOn, c)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "compare")
|
||||
}
|
||||
if affected {
|
||||
vinfo, ok := host.ScannedCves[cveid]
|
||||
if !ok {
|
||||
host.ScannedCves[cveid] = types.VulnInfo{ID: cveid}
|
||||
}
|
||||
vinfo.AffectedPackages = append(vinfo.AffectedPackages, types.AffectedPackage{
|
||||
Name: key,
|
||||
Source: fmt.Sprintf("%s:%s", datasrc, id),
|
||||
})
|
||||
vinfo.AffectedPackages = util.Unique(vinfo.AffectedPackages)
|
||||
host.ScannedCves[cveid] = vinfo
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vulns, err := vulndb.GetVulnerability(maps.Keys(host.ScannedCves))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "get vulnerability")
|
||||
}
|
||||
for cveid, datasrcs := range vulns {
|
||||
vinfo := host.ScannedCves[cveid]
|
||||
vinfo.Content = map[string]dbTypes.Vulnerability{}
|
||||
for src, v := range datasrcs {
|
||||
vinfo.Content[src] = v
|
||||
}
|
||||
host.ScannedCves[cveid] = vinfo
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func compare(installedCPE common.WellFormedName, installedRunningOn *common.WellFormedName, target dbTypes.CPEConfiguration) (bool, error) {
|
||||
var (
|
||||
wfn common.WellFormedName
|
||||
err error
|
||||
)
|
||||
|
||||
if target.Vulnerable.CPEVersion == "2.3" {
|
||||
wfn, err = naming.UnbindFS(target.Vulnerable.CPE)
|
||||
} else {
|
||||
wfn, err = naming.UnbindURI(target.Vulnerable.CPE)
|
||||
}
|
||||
if err != nil {
|
||||
return false, errors.Wrapf(err, "unbind %s", target.Vulnerable.CPE)
|
||||
}
|
||||
if !matching.IsEqual(installedCPE, wfn) && !matching.IsSubset(installedCPE, wfn) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
for _, runningOn := range target.RunningOn {
|
||||
if runningOn.CPEVersion == "2.3" {
|
||||
wfn, err = naming.UnbindFS(runningOn.CPE)
|
||||
} else {
|
||||
wfn, err = naming.UnbindURI(runningOn.CPE)
|
||||
}
|
||||
if err != nil {
|
||||
return false, errors.Wrapf(err, "unbind %s", runningOn.CPE)
|
||||
}
|
||||
if !matching.IsEqual(*installedRunningOn, wfn) && !matching.IsSubset(*installedRunningOn, wfn) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(target.Vulnerable.Version) == 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
attrver := installedCPE.GetString(common.AttributeVersion)
|
||||
switch attrver {
|
||||
case "ANY":
|
||||
return true, nil
|
||||
case "NA":
|
||||
return false, nil
|
||||
default:
|
||||
v, err := version.NewVersion(strings.ReplaceAll(attrver, "\\", ""))
|
||||
if err != nil {
|
||||
return false, errors.Wrapf(err, "parse version in %s", installedCPE.GetString(common.AttributeVersion))
|
||||
}
|
||||
for _, vconf := range target.Vulnerable.Version {
|
||||
vconfv, err := version.NewVersion(vconf.Version)
|
||||
if err != nil {
|
||||
return false, errors.Wrapf(err, "parse version in %s", vconf.Version)
|
||||
}
|
||||
|
||||
switch vconf.Operator {
|
||||
case "eq":
|
||||
if !v.Equal(vconfv) {
|
||||
return false, nil
|
||||
}
|
||||
case "lt":
|
||||
if !v.LessThan(vconfv) {
|
||||
return false, nil
|
||||
}
|
||||
case "le":
|
||||
if !v.LessThanOrEqual(vconfv) {
|
||||
return false, nil
|
||||
}
|
||||
case "gt":
|
||||
if !v.GreaterThan(vconfv) {
|
||||
return false, nil
|
||||
}
|
||||
case "ge":
|
||||
if !v.GreaterThanOrEqual(vconfv) {
|
||||
return false, nil
|
||||
}
|
||||
default:
|
||||
return false, errors.New("not supported operator")
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||