Compare commits
	
		
			656 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					03c3189c02 | ||
| 
						 | 
					4a34dfe0e9 | ||
| 
						 | 
					4cf9a723fe | ||
| 
						 | 
					bd1b135db3 | ||
| 
						 | 
					8c3b305149 | ||
| 
						 | 
					a3719038b8 | ||
| 
						 | 
					c68a261c0b | ||
| 
						 | 
					75fea79ac1 | ||
| 
						 | 
					eb9f9680ec | ||
| 
						 | 
					3634afdb81 | ||
| 
						 | 
					77b5df896a | ||
| 
						 | 
					b81f64058c | ||
| 
						 | 
					a8a90d7c63 | ||
| 
						 | 
					17bb575002 | ||
| 
						 | 
					abcea1a14d | ||
| 
						 | 
					10942f7c08 | ||
| 
						 | 
					87ee829e80 | ||
| 
						 | 
					fcc2c1e4c7 | ||
| 
						 | 
					269095d034 | ||
| 
						 | 
					40492ee00a | ||
| 
						 | 
					64cdd5aedc | ||
| 
						 | 
					3bb650cb77 | ||
| 
						 | 
					774544c975 | ||
| 
						 | 
					299805a726 | ||
| 
						 | 
					276363e793 | ||
| 
						 | 
					e750bd53fc | ||
| 
						 | 
					98fee7b5d2 | ||
| 
						 | 
					53aaea9fe2 | ||
| 
						 | 
					824fbb6368 | ||
| 
						 | 
					80566b91ab | ||
| 
						 | 
					533d05a1b5 | ||
| 
						 | 
					6a1fc4fade | ||
| 
						 | 
					9008d0ddf0 | ||
| 
						 | 
					583f4577bc | ||
| 
						 | 
					e5716d5092 | ||
| 
						 | 
					7192ae1287 | ||
| 
						 | 
					99c65eff48 | ||
| 
						 | 
					91df593566 | ||
| 
						 | 
					07aeaeb989 | ||
| 
						 | 
					cfeecdacd0 | ||
| 
						 | 
					564dfa8b62 | ||
| 
						 | 
					75dd6f2010 | ||
| 
						 | 
					e26fd0b759 | ||
| 
						 | 
					d630680a51 | ||
| 
						 | 
					1723c3f6a0 | ||
| 
						 | 
					53dd90302e | ||
| 
						 | 
					5c6e06b05e | ||
| 
						 | 
					cf6fb0c8a5 | ||
| 
						 | 
					e0e71b2eae | ||
| 
						 | 
					53f4a29fb1 | ||
| 
						 | 
					89d58d1abc | ||
| 
						 | 
					d6b6969cb3 | ||
| 
						 | 
					e7bf6fa69d | ||
| 
						 | 
					6e51970b91 | ||
| 
						 | 
					56d7d43768 | ||
| 
						 | 
					256c99ffa2 | ||
| 
						 | 
					9c0bc3b13b | ||
| 
						 | 
					9b8a323d85 | ||
| 
						 | 
					3178c1e326 | ||
| 
						 | 
					321d68e03a | ||
| 
						 | 
					3d8753c621 | ||
| 
						 | 
					967c56909d | ||
| 
						 | 
					7c4831d2d1 | ||
| 
						 | 
					4b49e11a33 | ||
| 
						 | 
					d84a6a8627 | ||
| 
						 | 
					63b7f4a8db | ||
| 
						 | 
					ca2160264a | ||
| 
						 | 
					7842594f53 | ||
| 
						 | 
					7db056102c | ||
| 
						 | 
					a5a800fa0a | ||
| 
						 | 
					9147ec148d | ||
| 
						 | 
					b3260588c6 | ||
| 
						 | 
					7d31328271 | ||
| 
						 | 
					6e82981ee3 | ||
| 
						 | 
					9d7b115bb5 | ||
| 
						 | 
					8eae5002a3 | ||
| 
						 | 
					31bd6c0371 | ||
| 
						 | 
					7585f9d537 | ||
| 
						 | 
					76037cdf72 | ||
| 
						 | 
					98c5421edc | ||
| 
						 | 
					e63fc7e3f5 | ||
| 
						 | 
					6ed9cf3fb4 | ||
| 
						 | 
					9865eab2c0 | ||
| 
						 | 
					678e72a8b6 | ||
| 
						 | 
					ec41899089 | ||
| 
						 | 
					b2d913cc21 | ||
| 
						 | 
					bc86c24e6a | ||
| 
						 | 
					87a77dd95c | ||
| 
						 | 
					e8188f3432 | ||
| 
						 | 
					50506be546 | ||
| 
						 | 
					4ded028258 | ||
| 
						 | 
					6da8b3c4a1 | ||
| 
						 | 
					d5c92cbcb3 | ||
| 
						 | 
					ed5f98d6f0 | ||
| 
						 | 
					f854b8f908 | ||
| 
						 | 
					de7a6159d4 | ||
| 
						 | 
					6090a34037 | ||
| 
						 | 
					f566745479 | ||
| 
						 | 
					153234b623 | ||
| 
						 | 
					ac510d21ff | ||
| 
						 | 
					44fa2c5800 | ||
| 
						 | 
					d785fc2a54 | ||
| 
						 | 
					ea800e04bc | ||
| 
						 | 
					fe582ac635 | ||
| 
						 | 
					330edb3bce | ||
| 
						 | 
					212fec7115 | ||
| 
						 | 
					24d7021c47 | ||
| 
						 | 
					e3a01ff6a8 | ||
| 
						 | 
					81f2ba8a46 | ||
| 
						 | 
					9e9370b178 | ||
| 
						 | 
					ced6114a95 | ||
| 
						 | 
					3144faae5d | ||
| 
						 | 
					8960c67a82 | ||
| 
						 | 
					f8ca924434 | ||
| 
						 | 
					399a08775e | ||
| 
						 | 
					92f36ca558 | ||
| 
						 | 
					3dcc58205a | ||
| 
						 | 
					09779962cf | ||
| 
						 | 
					9cc78770a3 | ||
| 
						 | 
					f653ca9131 | ||
| 
						 | 
					6f9fd91849 | ||
| 
						 | 
					cb1aec4fc0 | ||
| 
						 | 
					7cebaf8a76 | ||
| 
						 | 
					241c943424 | ||
| 
						 | 
					d5d88d8cf0 | ||
| 
						 | 
					cf9d26068c | ||
| 
						 | 
					308a93dc72 | ||
| 
						 | 
					d6a7e65e4c | ||
| 
						 | 
					e0a5c5d3b8 | ||
| 
						 | 
					314f775243 | ||
| 
						 | 
					7a1644135a | ||
| 
						 | 
					5076326589 | ||
| 
						 | 
					ce56261b52 | ||
| 
						 | 
					baa0e897b2 | ||
| 
						 | 
					1d49c0e1ce | ||
| 
						 | 
					08755e446e | ||
| 
						 | 
					bb12d9dadb | ||
| 
						 | 
					fd1429fef0 | ||
| 
						 | 
					d3c421a4a8 | ||
| 
						 | 
					0c919da4b1 | ||
| 
						 | 
					9afbf1255f | ||
| 
						 | 
					50b105c4af | ||
| 
						 | 
					028508c1f7 | ||
| 
						 | 
					f0137a3695 | ||
| 
						 | 
					e6d3a1718c | ||
| 
						 | 
					86ba551e07 | ||
| 
						 | 
					26418be937 | ||
| 
						 | 
					092a19bdc1 | ||
| 
						 | 
					6d3398574c | ||
| 
						 | 
					b08969ad89 | ||
| 
						 | 
					0653656526 | ||
| 
						 | 
					7a5793c562 | ||
| 
						 | 
					562ff7807d | ||
| 
						 | 
					7971bdf7f7 | ||
| 
						 | 
					d926b7fd6d | ||
| 
						 | 
					c00404793a | ||
| 
						 | 
					a0e0ee6c1e | ||
| 
						 | 
					4ccbee705b | ||
| 
						 | 
					db43d55b2c | ||
| 
						 | 
					5a3a333eec | ||
| 
						 | 
					039edf1616 | ||
| 
						 | 
					47498bbf23 | ||
| 
						 | 
					cc28bf4ae2 | ||
| 
						 | 
					0e8736045e | ||
| 
						 | 
					19b581edef | ||
| 
						 | 
					295f6656d9 | ||
| 
						 | 
					1214d8c14d | ||
| 
						 | 
					b4cd96fc9a | ||
| 
						 | 
					3238a9b898 | ||
| 
						 | 
					c0f66320f6 | ||
| 
						 | 
					383220f384 | ||
| 
						 | 
					76a9c37e6b | ||
| 
						 | 
					e788e6a5ad | ||
| 
						 | 
					d00e912934 | ||
| 
						 | 
					8ebb663368 | ||
| 
						 | 
					445ffc4123 | ||
| 
						 | 
					6af49f4d55 | ||
| 
						 | 
					1de9e8c086 | ||
| 
						 | 
					59b0812adf | ||
| 
						 | 
					719785c1ed | ||
| 
						 | 
					8e5f627e59 | ||
| 
						 | 
					5ced3c72b8 | ||
| 
						 | 
					c002f0168c | ||
| 
						 | 
					00c690f516 | ||
| 
						 | 
					ab68ad5cc5 | ||
| 
						 | 
					5c84ebefab | ||
| 
						 | 
					eb2acaff22 | ||
| 
						 | 
					84d0655c52 | ||
| 
						 | 
					e137ebb9c2 | ||
| 
						 | 
					10d690d929 | ||
| 
						 | 
					14611d2fd9 | ||
| 
						 | 
					0665bfe15f | ||
| 
						 | 
					473096d35d | ||
| 
						 | 
					0eae26e261 | ||
| 
						 | 
					a32845f652 | ||
| 
						 | 
					15a0f7eadb | ||
| 
						 | 
					5a0a6abf11 | ||
| 
						 | 
					032b8d9572 | ||
| 
						 | 
					5798e3af83 | ||
| 
						 | 
					8e15b9ce1c | ||
| 
						 | 
					7a1f132c1f | ||
| 
						 | 
					a8483b2195 | ||
| 
						 | 
					83bbbd0cb0 | ||
| 
						 | 
					132432dce6 | ||
| 
						 | 
					e5eb8e42f5 | ||
| 
						 | 
					1095ebea24 | ||
| 
						 | 
					1541a602b2 | ||
| 
						 | 
					03a141c252 | ||
| 
						 | 
					5f2183fc8e | ||
| 
						 | 
					820831fa5d | ||
| 
						 | 
					6d2d767c52 | ||
| 
						 | 
					e0c3a728ae | ||
| 
						 | 
					ec92f7797f | ||
| 
						 | 
					0ba490c6df | ||
| 
						 | 
					cfd668e11d | ||
| 
						 | 
					a8bc25321e | ||
| 
						 | 
					fec13bcb86 | ||
| 
						 | 
					cb1c07f998 | ||
| 
						 | 
					6312b97faa | ||
| 
						 | 
					21f13b55eb | ||
| 
						 | 
					187598382b | ||
| 
						 | 
					551fdd5022 | ||
| 
						 | 
					58b0d03e28 | ||
| 
						 | 
					3790197699 | ||
| 
						 | 
					579fff122c | ||
| 
						 | 
					feb3f79a13 | ||
| 
						 | 
					b5cb08ac43 | ||
| 
						 | 
					4ac5d9e0da | ||
| 
						 | 
					93f741da35 | ||
| 
						 | 
					648a999514 | ||
| 
						 | 
					71490aebd9 | ||
| 
						 | 
					9e90c0f912 | ||
| 
						 | 
					de65073f61 | ||
| 
						 | 
					6129ac7bd4 | ||
| 
						 | 
					b5d4d27312 | ||
| 
						 | 
					823fcd91f4 | ||
| 
						 | 
					477e12d5cf | ||
| 
						 | 
					a36a226ae2 | ||
| 
						 | 
					886a21c633 | ||
| 
						 | 
					fd19fa2082 | ||
| 
						 | 
					843f1a462f | ||
| 
						 | 
					5c5b8a361d | ||
| 
						 | 
					417df0582d | ||
| 
						 | 
					999d8f5866 | ||
| 
						 | 
					47a444e795 | ||
| 
						 | 
					dbceca8780 | ||
| 
						 | 
					c66898e608 | ||
| 
						 | 
					ee20cb59a5 | ||
| 
						 | 
					5c51d83573 | ||
| 
						 | 
					47b3b3848b | ||
| 
						 | 
					95eb980f58 | ||
| 
						 | 
					f738622c28 | ||
| 
						 | 
					577509bbf9 | ||
| 
						 | 
					774c78add0 | ||
| 
						 | 
					b14406e329 | ||
| 
						 | 
					29cf4bb517 | ||
| 
						 | 
					a233e08929 | ||
| 
						 | 
					cbd1c12773 | ||
| 
						 | 
					0a3f0f9ffc | ||
| 
						 | 
					d3014025b0 | ||
| 
						 | 
					2887dc0d36 | ||
| 
						 | 
					5f49e7da8e | ||
| 
						 | 
					9e0032b258 | ||
| 
						 | 
					008da49b83 | ||
| 
						 | 
					9899cba816 | ||
| 
						 | 
					27724a2faf | ||
| 
						 | 
					8b6a283114 | ||
| 
						 | 
					4379b8bacf | ||
| 
						 | 
					56603dcfae | ||
| 
						 | 
					1752736714 | ||
| 
						 | 
					b1428b6758 | ||
| 
						 | 
					9b6d84def6 | ||
| 
						 | 
					ed162d7d6e | ||
| 
						 | 
					1aae425945 | ||
| 
						 | 
					26e447f11a | ||
| 
						 | 
					ffbaa0a508 | ||
| 
						 | 
					a9ebac3818 | ||
| 
						 | 
					738e9fb119 | ||
| 
						 | 
					7778783dd8 | ||
| 
						 | 
					c442a433b0 | ||
| 
						 | 
					f7aa85746d | ||
| 
						 | 
					1883da3b2a | ||
| 
						 | 
					997dd6022f | ||
| 
						 | 
					63394a2400 | ||
| 
						 | 
					a662b038dc | ||
| 
						 | 
					e9df2bfa01 | ||
| 
						 | 
					a7951b727c | ||
| 
						 | 
					c6ad9ea57a | ||
| 
						 | 
					a14810bbd4 | ||
| 
						 | 
					bc5a95ebb3 | ||
| 
						 | 
					306182e2ae | ||
| 
						 | 
					ad096196ee | ||
| 
						 | 
					af66e44427 | ||
| 
						 | 
					0a012273ec | ||
| 
						 | 
					73b011eba7 | ||
| 
						 | 
					a31974a3c0 | ||
| 
						 | 
					eb02bdd95a | ||
| 
						 | 
					74805c6be8 | ||
| 
						 | 
					d9bc4499a4 | ||
| 
						 | 
					9128e2748b | ||
| 
						 | 
					7f8c975bd7 | ||
| 
						 | 
					8b6c841b1e | ||
| 
						 | 
					4fcdea3ccb | ||
| 
						 | 
					3be11cf52f | ||
| 
						 | 
					b285cb0e57 | ||
| 
						 | 
					dd5a7920e5 | ||
| 
						 | 
					cfb848918f | ||
| 
						 | 
					b977558f38 | ||
| 
						 | 
					210e3dc990 | ||
| 
						 | 
					f36671784e | ||
| 
						 | 
					d626cc8a8b | ||
| 
						 | 
					f26b61d773 | ||
| 
						 | 
					12c2d3cbc6 | ||
| 
						 | 
					209ca704de | ||
| 
						 | 
					2e37d3adc1 | ||
| 
						 | 
					509fb045b6 | ||
| 
						 | 
					a2c364f9eb | ||
| 
						 | 
					17a4e532c1 | ||
| 
						 | 
					c103b79ec2 | ||
| 
						 | 
					b545b5d0a3 | ||
| 
						 | 
					342a1c6cff | ||
| 
						 | 
					aafbdcd34d | ||
| 
						 | 
					ec092501c3 | ||
| 
						 | 
					bb708db89f | ||
| 
						 | 
					085a9dcb79 | ||
| 
						 | 
					037e12b0bd | ||
| 
						 | 
					c9ab956f8f | ||
| 
						 | 
					587c87b3a0 | ||
| 
						 | 
					1a319859eb | ||
| 
						 | 
					c989c31aeb | ||
| 
						 | 
					e5d32c8764 | ||
| 
						 | 
					23c177ed4a | ||
| 
						 | 
					10a27042b5 | ||
| 
						 | 
					2cec20c7ee | ||
| 
						 | 
					7ecd09f497 | ||
| 
						 | 
					8bf7f6cac5 | ||
| 
						 | 
					067a2315df | ||
| 
						 | 
					fecd1ad464 | ||
| 
						 | 
					a3f2555bc1 | ||
| 
						 | 
					5bf4cd46ff | ||
| 
						 | 
					f878e225cc | ||
| 
						 | 
					eb2598f3b3 | ||
| 
						 | 
					e20a59b991 | ||
| 
						 | 
					703c142659 | ||
| 
						 | 
					8335b40368 | ||
| 
						 | 
					05884c2d29 | ||
| 
						 | 
					33b2aa2d52 | ||
| 
						 | 
					9ab0622886 | ||
| 
						 | 
					b33cd54916 | ||
| 
						 | 
					d4bec0dd9a | ||
| 
						 | 
					bdf6efeaac | ||
| 
						 | 
					74431ca63f | ||
| 
						 | 
					c90be385ef | ||
| 
						 | 
					b0d9c0b550 | ||
| 
						 | 
					9255132f9b | ||
| 
						 | 
					d5c0092fa3 | ||
| 
						 | 
					c7019debb9 | ||
| 
						 | 
					7131270cad | ||
| 
						 | 
					af5a1204bc | ||
| 
						 | 
					58afcfc49a | ||
| 
						 | 
					986762ca85 | ||
| 
						 | 
					6342cf79f5 | ||
| 
						 | 
					5fbf67f971 | ||
| 
						 | 
					e441e5a696 | ||
| 
						 | 
					d201efb029 | ||
| 
						 | 
					25960126c7 | ||
| 
						 | 
					63d5a6f584 | ||
| 
						 | 
					2030951a8f | ||
| 
						 | 
					cd841462cd | ||
| 
						 | 
					735aa835a6 | ||
| 
						 | 
					92e213ca32 | ||
| 
						 | 
					d077c29716 | ||
| 
						 | 
					d6eba48a50 | ||
| 
						 | 
					2a1608d1d2 | ||
| 
						 | 
					cc7d3dc2aa | ||
| 
						 | 
					a5c4c682f5 | ||
| 
						 | 
					688cfd6872 | ||
| 
						 | 
					7e268dbae1 | ||
| 
						 | 
					ce6a4231ef | ||
| 
						 | 
					e1de8ab626 | ||
| 
						 | 
					0058eaf357 | ||
| 
						 | 
					732d95098a | ||
| 
						 | 
					52f0943207 | ||
| 
						 | 
					41f99f2b65 | ||
| 
						 | 
					1f9e5c6263 | ||
| 
						 | 
					2f3eddd2ab | ||
| 
						 | 
					619a0ee700 | ||
| 
						 | 
					b1b5c2c9a0 | ||
| 
						 | 
					a86035c0bf | ||
| 
						 | 
					c66b0f4db4 | ||
| 
						 | 
					a4cf4bd314 | ||
| 
						 | 
					f1cd9383c1 | ||
| 
						 | 
					6fa57abe10 | ||
| 
						 | 
					6e77c714b5 | ||
| 
						 | 
					fbab020e6e | ||
| 
						 | 
					5581a5cce7 | ||
| 
						 | 
					b4be11775e | ||
| 
						 | 
					b079f5e52e | ||
| 
						 | 
					f9bf470a37 | ||
| 
						 | 
					9d783dd2ab | ||
| 
						 | 
					1b9aafbbaf | ||
| 
						 | 
					1d3ee6a241 | ||
| 
						 | 
					2f9c3071a6 | ||
| 
						 | 
					4b0be4f115 | ||
| 
						 | 
					1419c7c8c6 | ||
| 
						 | 
					851cecdd73 | ||
| 
						 | 
					753da3aad7 | ||
| 
						 | 
					65c10d6d8e | ||
| 
						 | 
					1b8b423131 | ||
| 
						 | 
					55b1264c7d | ||
| 
						 | 
					902a1888d4 | ||
| 
						 | 
					98151f7d0e | ||
| 
						 | 
					a6f0c559f8 | ||
| 
						 | 
					e7ec5b841d | ||
| 
						 | 
					d6f72ac0f3 | ||
| 
						 | 
					7e3a10025a | ||
| 
						 | 
					e16ec15226 | ||
| 
						 | 
					6935b56c9d | ||
| 
						 | 
					0e3a0b64e7 | ||
| 
						 | 
					74e6aee236 | ||
| 
						 | 
					db0602b7b8 | ||
| 
						 | 
					c9b7c3f179 | ||
| 
						 | 
					5bd9f4afb4 | ||
| 
						 | 
					9d2ba5912e | ||
| 
						 | 
					9986c4a6f3 | ||
| 
						 | 
					df2c9697ef | ||
| 
						 | 
					ab0388e882 | ||
| 
						 | 
					c05d8a36eb | ||
| 
						 | 
					492753d905 | ||
| 
						 | 
					6e08bd23f4 | ||
| 
						 | 
					a687c97808 | ||
| 
						 | 
					c6864289cb | ||
| 
						 | 
					97d85258c5 | ||
| 
						 | 
					bee25f5aa2 | ||
| 
						 | 
					386b97d2be | ||
| 
						 | 
					00660485b7 | ||
| 
						 | 
					1e8f24dedb | ||
| 
						 | 
					2be190f863 | ||
| 
						 | 
					ec7c6e6c85 | ||
| 
						 | 
					c52bc53fd8 | ||
| 
						 | 
					981631503a | ||
| 
						 | 
					48de3a6a4f | ||
| 
						 | 
					d1983a6978 | ||
| 
						 | 
					f821a26aec | ||
| 
						 | 
					3380e905de | ||
| 
						 | 
					b5c2718756 | ||
| 
						 | 
					a03a803b89 | ||
| 
						 | 
					e743177ae6 | ||
| 
						 | 
					6e12c69953 | ||
| 
						 | 
					019ab77466 | ||
| 
						 | 
					1730caf124 | ||
| 
						 | 
					59d1533795 | ||
| 
						 | 
					a6278ab7ea | ||
| 
						 | 
					42a6004c7d | ||
| 
						 | 
					6084c1b1d3 | ||
| 
						 | 
					c96fbc1dba | ||
| 
						 | 
					5546a8b093 | ||
| 
						 | 
					6b76b38dcd | ||
| 
						 | 
					941e50b460 | ||
| 
						 | 
					5a10e5c9ff | ||
| 
						 | 
					883fe13756 | ||
| 
						 | 
					2e7c34cf9f | ||
| 
						 | 
					9216efbd2f | ||
| 
						 | 
					6c8100e5b6 | ||
| 
						 | 
					e7ef50bedf | ||
| 
						 | 
					386ca3565a | ||
| 
						 | 
					2d854cd64d | ||
| 
						 | 
					49b4b8be22 | ||
| 
						 | 
					db975ebfee | ||
| 
						 | 
					d60a41139b | ||
| 
						 | 
					f62d869d27 | ||
| 
						 | 
					6cbe3cdb93 | ||
| 
						 | 
					b13e7b9da4 | ||
| 
						 | 
					8fe34c8474 | ||
| 
						 | 
					bef29be50f | ||
| 
						 | 
					20275a1063 | ||
| 
						 | 
					910385b084 | ||
| 
						 | 
					8e779374a7 | ||
| 
						 | 
					44fc6f728e | ||
| 
						 | 
					1f62dcf22a | ||
| 
						 | 
					0416c3b561 | ||
| 
						 | 
					a6912cae76 | ||
| 
						 | 
					63dfe8a952 | ||
| 
						 | 
					62d1b761bd | ||
| 
						 | 
					082b10a15b | ||
| 
						 | 
					1a6bcd82b0 | ||
| 
						 | 
					6ecd70220b | ||
| 
						 | 
					e9f55f5772 | ||
| 
						 | 
					155cadf901 | ||
| 
						 | 
					cb29289167 | ||
| 
						 | 
					e4db9d1d91 | ||
| 
						 | 
					7b2e2cb817 | ||
| 
						 | 
					c717f8d15d | ||
| 
						 | 
					8db147acab | ||
| 
						 | 
					e6de7aa9ca | ||
| 
						 | 
					46f96740a2 | ||
| 
						 | 
					8f9fb5c262 | ||
| 
						 | 
					171d6d6684 | ||
| 
						 | 
					f648b5ad0a | ||
| 
						 | 
					ef21376f0a | ||
| 
						 | 
					58958d68d8 | ||
| 
						 | 
					a06b565ee9 | ||
| 
						 | 
					a7db27ce5a | ||
| 
						 | 
					cda69dc7f0 | ||
| 
						 | 
					39f9594548 | ||
| 
						 | 
					6d82ad32a9 | ||
| 
						 | 
					cfcd8bf223 | ||
| 
						 | 
					8149ad00b5 | ||
| 
						 | 
					2310522806 | ||
| 
						 | 
					e40ef656d6 | ||
| 
						 | 
					e060d40a32 | ||
| 
						 | 
					a522218c4e | ||
| 
						 | 
					820455399c | ||
| 
						 | 
					959d612534 | ||
| 
						 | 
					cd81e6eab2 | ||
| 
						 | 
					e6ec6920ad | ||
| 
						 | 
					18a92fa1ca | ||
| 
						 | 
					f95af9897b | ||
| 
						 | 
					b61adcb1fd | ||
| 
						 | 
					1bbf320755 | ||
| 
						 | 
					159f26171c | ||
| 
						 | 
					8ac00f6c0d | ||
| 
						 | 
					ce2daf2493 | ||
| 
						 | 
					f014f8fd59 | ||
| 
						 | 
					f50a39a9e2 | ||
| 
						 | 
					e0d8147104 | ||
| 
						 | 
					c5cfac62da | ||
| 
						 | 
					83469ce5cc | ||
| 
						 | 
					7cd7b4a9a2 | ||
| 
						 | 
					7681b277cf | ||
| 
						 | 
					406efa96c0 | ||
| 
						 | 
					9a7a30c0bc | ||
| 
						 | 
					64bdfa0e80 | ||
| 
						 | 
					067089973c | ||
| 
						 | 
					85e6d753c7 | ||
| 
						 | 
					4094984642 | ||
| 
						 | 
					85c0009a43 | ||
| 
						 | 
					234e312ee2 | ||
| 
						 | 
					ce3ca64678 | ||
| 
						 | 
					b042a600c3 | ||
| 
						 | 
					686e9f07a9 | ||
| 
						 | 
					bb6725372b | ||
| 
						 | 
					6f012fc9c5 | ||
| 
						 | 
					4c82458481 | ||
| 
						 | 
					a0ac863998 | ||
| 
						 | 
					d23ef838f8 | ||
| 
						 | 
					f81ac197f5 | ||
| 
						 | 
					652b37e630 | ||
| 
						 | 
					c57e430393 | ||
| 
						 | 
					fff6047df9 | ||
| 
						 | 
					1e2b93d55b | ||
| 
						 | 
					66b27a7795 | ||
| 
						 | 
					63f0a272c4 | ||
| 
						 | 
					8d2180cf5a | ||
| 
						 | 
					1986f7e4dd | ||
| 
						 | 
					21beb396b4 | ||
| 
						 | 
					cb5a6f38d6 | ||
| 
						 | 
					67e4aaede0 | ||
| 
						 | 
					b42805d00c | ||
| 
						 | 
					95d6888c87 | ||
| 
						 | 
					549b315a65 | ||
| 
						 | 
					5b80b16684 | ||
| 
						 | 
					0cd0a4bf2b | ||
| 
						 | 
					b5cf06cad8 | ||
| 
						 | 
					b964d19d82 | ||
| 
						 | 
					cf7990d444 | ||
| 
						 | 
					738ccf7dbb | ||
| 
						 | 
					fc2ea48c1d | ||
| 
						 | 
					3af93b93d7 | ||
| 
						 | 
					f386c3be92 | ||
| 
						 | 
					239d910dbe | ||
| 
						 | 
					48929deabd | ||
| 
						 | 
					79523de1db | ||
| 
						 | 
					fbfc14dfeb | ||
| 
						 | 
					a8dc886f89 | ||
| 
						 | 
					cfc9e064b9 | ||
| 
						 | 
					e72fa3362a | ||
| 
						 | 
					26364421e8 | ||
| 
						 | 
					4a07974b54 | ||
| 
						 | 
					eaddc7f2ba | ||
| 
						 | 
					85056aaa00 | ||
| 
						 | 
					c077c740fa | ||
| 
						 | 
					c2eab87a3f | ||
| 
						 | 
					ea582d2d2e | ||
| 
						 | 
					2f89a24100 | ||
| 
						 | 
					73ebb94f67 | ||
| 
						 | 
					95bf387ecc | ||
| 
						 | 
					f17a8452f9 | ||
| 
						 | 
					920ffe1f33 | ||
| 
						 | 
					093bcb7477 | ||
| 
						 | 
					c06b3ec9eb | ||
| 
						 | 
					ac6fe6f9fc | ||
| 
						 | 
					2dffdaac42 | ||
| 
						 | 
					cb445c9504 | ||
| 
						 | 
					e3fc3aa9d1 | ||
| 
						 | 
					97c3f5d642 | ||
| 
						 | 
					0a52fc9a56 | ||
| 
						 | 
					c831339b0d | ||
| 
						 | 
					058ccf575f | ||
| 
						 | 
					92be12bc2f | ||
| 
						 | 
					1aa2f4b5b1 | ||
| 
						 | 
					bba9431985 | ||
| 
						 | 
					3c39f1e737 | ||
| 
						 | 
					e6f4d07a87 | ||
| 
						 | 
					e43358a0d2 | ||
| 
						 | 
					f0644e8a9d | ||
| 
						 | 
					11b010b281 | ||
| 
						 | 
					c751029127 | ||
| 
						 | 
					fb70d1b2f0 | ||
| 
						 | 
					3d68783b7f | ||
| 
						 | 
					0d77853912 | ||
| 
						 | 
					ea1b5dd8f7 | ||
| 
						 | 
					2dcb7d5ce1 | ||
| 
						 | 
					99cab34527 | ||
| 
						 | 
					f5eeed0bc2 | ||
| 
						 | 
					1b85e56961 | ||
| 
						 | 
					8a8ac5fd22 | ||
| 
						 | 
					00c0354a8e | ||
| 
						 | 
					a2a6973ba1 | ||
| 
						 | 
					dd1d3a05fa | ||
| 
						 | 
					2afe2d2640 | ||
| 
						 | 
					29678f9b59 | ||
| 
						 | 
					77edb251bb | ||
| 
						 | 
					29151fa267 | ||
| 
						 | 
					b3f13790bd | ||
| 
						 | 
					38857c3356 | ||
| 
						 | 
					d75990d9fd | ||
| 
						 | 
					ed063f6534 | ||
| 
						 | 
					c8a9bdc517 | ||
| 
						 | 
					595729cdf8 | ||
| 
						 | 
					6119f79748 | ||
| 
						 | 
					d4fb46c9ba | ||
| 
						 | 
					c41301afca | ||
| 
						 | 
					50fd80830e | ||
| 
						 | 
					1c203b4272 | ||
| 
						 | 
					c545e9045d | ||
| 
						 | 
					2721dc0647 | ||
| 
						 | 
					51d13f4234 | ||
| 
						 | 
					a60a5d6eab | ||
| 
						 | 
					5959235425 | ||
| 
						 | 
					d8e6d4e5fc | ||
| 
						 | 
					7dfc9815b3 | ||
| 
						 | 
					0c53b187a4 | ||
| 
						 | 
					42dadfed8f | ||
| 
						 | 
					a46c603c77 | ||
| 
						 | 
					ad0020d9a6 | ||
| 
						 | 
					a224f0bfd4 | ||
| 
						 | 
					d8dc3650d3 | ||
| 
						 | 
					30f7527f10 | ||
| 
						 | 
					b1f5bdd8b2 | ||
| 
						 | 
					c8e7c8b9fa | ||
| 
						 | 
					30bf3223f8 | ||
| 
						 | 
					886710ec30 | ||
| 
						 | 
					510dc8d828 | ||
| 
						 | 
					5ff7b2aab4 | ||
| 
						 | 
					1e33536205 | 
							
								
								
									
										7
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,7 @@
 | 
			
		||||
.dockerignore
 | 
			
		||||
Dockerfile
 | 
			
		||||
vendor/
 | 
			
		||||
cve.sqlite3*
 | 
			
		||||
oval.sqlite3*
 | 
			
		||||
setup/
 | 
			
		||||
img/
 | 
			
		||||
							
								
								
									
										43
									
								
								.github/ISSUE_TEMPLATE/BUG_REPORT.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,43 @@
 | 
			
		||||
---
 | 
			
		||||
name: Bug Report
 | 
			
		||||
labels: bug
 | 
			
		||||
about: If something isn't working as expected.
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
# What did you do? (required. The issue will be **closed** when not provided.)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# What did you expect to happen?
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# What happened instead?
 | 
			
		||||
 | 
			
		||||
* Current Output
 | 
			
		||||
 | 
			
		||||
Please re-run the command using ```-debug``` and provide the output below.
 | 
			
		||||
 | 
			
		||||
# Steps to reproduce the behaviour
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Configuration (**MUST** fill this out):
 | 
			
		||||
 | 
			
		||||
* Go version (`go version`):
 | 
			
		||||
 | 
			
		||||
* Go environment (`go env`):
 | 
			
		||||
 | 
			
		||||
* Vuls environment:
 | 
			
		||||
 | 
			
		||||
Hash : ____
 | 
			
		||||
 | 
			
		||||
To check the commit hash of HEAD
 | 
			
		||||
$ vuls -v
 | 
			
		||||
 | 
			
		||||
or
 | 
			
		||||
 | 
			
		||||
$ cd $GOPATH/src/github.com/future-architect/vuls 
 | 
			
		||||
$ git rev-parse --short HEAD 
 | 
			
		||||
 | 
			
		||||
* config.toml:
 | 
			
		||||
 | 
			
		||||
* command:
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,9 @@
 | 
			
		||||
---
 | 
			
		||||
name: Feature Request
 | 
			
		||||
labels: enhancement
 | 
			
		||||
about: I have a suggestion (and might want to implement myself)!
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
<!--
 | 
			
		||||
If this is a FEATURE REQUEST, request format does not matter!
 | 
			
		||||
-->
 | 
			
		||||
							
								
								
									
										10
									
								
								.github/ISSUE_TEMPLATE/SUPPORT_QUESTION.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,10 @@
 | 
			
		||||
---
 | 
			
		||||
name: Support Question
 | 
			
		||||
labels: question
 | 
			
		||||
about: If you have a question about Vuls.
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
<!--
 | 
			
		||||
If you have a trouble, feel free to ask.
 | 
			
		||||
Make sure you're not asking duplicate question by searching on the issues lists.
 | 
			
		||||
-->
 | 
			
		||||
							
								
								
									
										7
									
								
								.github/ISSUE_TEMPLATE/VULSREPO.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,7 @@
 | 
			
		||||
---
 | 
			
		||||
name: Vuls Repo
 | 
			
		||||
labels: vulsrepo
 | 
			
		||||
about: If something isn't working as expected.
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										40
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,40 @@
 | 
			
		||||
 | 
			
		||||
If this Pull Request is work in progress, Add a prefix of “[WIP]” in the title.
 | 
			
		||||
 | 
			
		||||
# What did you implement:
 | 
			
		||||
 | 
			
		||||
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. 
 | 
			
		||||
 | 
			
		||||
Fixes # (issue)
 | 
			
		||||
 | 
			
		||||
## Type of change
 | 
			
		||||
 | 
			
		||||
Please delete options that are not relevant.
 | 
			
		||||
 | 
			
		||||
- [ ] Bug fix (non-breaking change which fixes an issue)
 | 
			
		||||
- [ ] New feature (non-breaking change which adds functionality)
 | 
			
		||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
 | 
			
		||||
- [ ] This change requires a documentation update
 | 
			
		||||
 | 
			
		||||
# How Has This Been Tested?
 | 
			
		||||
 | 
			
		||||
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce.
 | 
			
		||||
 | 
			
		||||
# Checklist:
 | 
			
		||||
You don't have to satisfy all of the following.
 | 
			
		||||
 | 
			
		||||
- [ ] Write tests
 | 
			
		||||
- [ ] Write documentation
 | 
			
		||||
- [ ] Check that there aren't other open pull requests for the same issue/feature
 | 
			
		||||
- [ ] Format your source code by `make fmt`
 | 
			
		||||
- [ ] Pass the test by `make test`
 | 
			
		||||
- [ ] Provide verification config / commands
 | 
			
		||||
- [ ] Enable "Allow edits from maintainers" for this PR
 | 
			
		||||
- [ ] Update the messages below
 | 
			
		||||
 | 
			
		||||
***Is this ready for review?:*** NO  
 | 
			
		||||
 | 
			
		||||
# Reference
 | 
			
		||||
 | 
			
		||||
* https://blog.github.com/2015-01-21-how-to-write-the-perfect-pull-request/
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										8
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -2,7 +2,9 @@ vuls
 | 
			
		||||
.vscode
 | 
			
		||||
*.txt
 | 
			
		||||
*.json
 | 
			
		||||
*.sqlite3
 | 
			
		||||
*.sqlite3*
 | 
			
		||||
*.db
 | 
			
		||||
tags
 | 
			
		||||
.gitmodules
 | 
			
		||||
coverage.out
 | 
			
		||||
issues/
 | 
			
		||||
@@ -10,3 +12,7 @@ vendor/
 | 
			
		||||
log/
 | 
			
		||||
results/
 | 
			
		||||
*config.toml
 | 
			
		||||
!setup/docker/*
 | 
			
		||||
.DS_Store
 | 
			
		||||
dist/
 | 
			
		||||
.idea
 | 
			
		||||
							
								
								
									
										28
									
								
								.goreleaser.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,28 @@
 | 
			
		||||
project_name: vuls
 | 
			
		||||
env:
 | 
			
		||||
  - GO111MODULE=on
 | 
			
		||||
release:
 | 
			
		||||
  github:
 | 
			
		||||
    owner: future-architect
 | 
			
		||||
    name: vuls
 | 
			
		||||
builds:
 | 
			
		||||
- goos:
 | 
			
		||||
  - linux
 | 
			
		||||
  goarch:
 | 
			
		||||
  - amd64
 | 
			
		||||
  main: .
 | 
			
		||||
  flags:
 | 
			
		||||
      - -a
 | 
			
		||||
  ldflags: -s -w -X main.version={{.Version}} -X main.revision={{.Commit}} 
 | 
			
		||||
  binary: vuls
 | 
			
		||||
archive:
 | 
			
		||||
  format: tar.gz
 | 
			
		||||
  name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{
 | 
			
		||||
    .Arm }}{{ end }}'
 | 
			
		||||
  files:
 | 
			
		||||
  - LICENSE
 | 
			
		||||
  - NOTICE
 | 
			
		||||
  - README*
 | 
			
		||||
  - CHANGELOG.md
 | 
			
		||||
snapshot:
 | 
			
		||||
  name_template: SNAPSHOT-{{ .Commit }}
 | 
			
		||||
							
								
								
									
										7
									
								
								.travis.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,7 @@
 | 
			
		||||
language: go
 | 
			
		||||
 | 
			
		||||
go:
 | 
			
		||||
  - "1.12.x"
 | 
			
		||||
 | 
			
		||||
after_success:
 | 
			
		||||
  - test -n "$TRAVIS_TAG" && curl -sL https://git.io/goreleaser | bash
 | 
			
		||||
							
								
								
									
										349
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						@@ -1,5 +1,350 @@
 | 
			
		||||
# Change Log
 | 
			
		||||
 | 
			
		||||
## v0.4.1 and later, see [GitHub release](https://github.com/future-architect/vuls/releases)
 | 
			
		||||
 | 
			
		||||
## [v0.4.0](https://github.com/future-architect/vuls/tree/v0.4.0) (2017-08-25)
 | 
			
		||||
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.3.0...v0.4.0)
 | 
			
		||||
 | 
			
		||||
**Implemented enhancements:**
 | 
			
		||||
 | 
			
		||||
- Output changelog in report, TUI and JSON for RHEL [\#367](https://github.com/future-architect/vuls/issues/367)
 | 
			
		||||
- Output changelog in report, TUI and JSON for Amazon Linux [\#366](https://github.com/future-architect/vuls/issues/366)
 | 
			
		||||
- Improve scanning accuracy by checking package versions [\#256](https://github.com/future-architect/vuls/issues/256)
 | 
			
		||||
- Improve SSH [\#415](https://github.com/future-architect/vuls/issues/415)
 | 
			
		||||
- Enable to scan even if target server can not connect to the Internet [\#258](https://github.com/future-architect/vuls/issues/258)
 | 
			
		||||
- SSH Hostkey check [\#417](https://github.com/future-architect/vuls/pull/417) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- v0.4.0 [\#449](https://github.com/future-architect/vuls/pull/449) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Change default ssh method from go library to external command [\#416](https://github.com/future-architect/vuls/pull/416) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Add containers-only option to configtest [\#411](https://github.com/future-architect/vuls/pull/411) ([knqyf263](https://github.com/knqyf263))
 | 
			
		||||
 | 
			
		||||
**Fixed bugs:**
 | 
			
		||||
 | 
			
		||||
-  Running Vuls tui before vuls report does not show vulnerabilities checked by CPE [\#396](https://github.com/future-architect/vuls/issues/396)
 | 
			
		||||
- With a long package name, Local shell mode \(stty dont' work\) [\#444](https://github.com/future-architect/vuls/issues/444)
 | 
			
		||||
- Improve SSH [\#415](https://github.com/future-architect/vuls/issues/415)
 | 
			
		||||
- Report that a vulnerability exists in the wrong package [\#408](https://github.com/future-architect/vuls/issues/408)
 | 
			
		||||
- With a long package name, a parse error occurs. [\#391](https://github.com/future-architect/vuls/issues/391)
 | 
			
		||||
- Ubuntu failed to scan vulnerable packages [\#205](https://github.com/future-architect/vuls/issues/205)
 | 
			
		||||
- CVE-ID in changelog can't be picked up. [\#154](https://github.com/future-architect/vuls/issues/154)
 | 
			
		||||
- v0.4.0 [\#449](https://github.com/future-architect/vuls/pull/449) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix SSH dial error [\#413](https://github.com/future-architect/vuls/pull/413) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Update deps, Change deps tool from glide to dep [\#412](https://github.com/future-architect/vuls/pull/412) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- fix report option Loaded error-info [\#406](https://github.com/future-architect/vuls/pull/406) ([hogehogehugahuga](https://github.com/hogehogehugahuga))
 | 
			
		||||
- Add --user root to docker exec command [\#389](https://github.com/future-architect/vuls/pull/389) ([PaulFurtado](https://github.com/PaulFurtado))
 | 
			
		||||
 | 
			
		||||
**Closed issues:**
 | 
			
		||||
 | 
			
		||||
- README.md.ja not include "Oracle Linux, FreeBSD"  [\#465](https://github.com/future-architect/vuls/issues/465)
 | 
			
		||||
- Can't scan remote server - \(centos 7 - updated\) [\#451](https://github.com/future-architect/vuls/issues/451)
 | 
			
		||||
- An abnormality in the result of vuls tui [\#439](https://github.com/future-architect/vuls/issues/439)
 | 
			
		||||
- compile faild [\#436](https://github.com/future-architect/vuls/issues/436)
 | 
			
		||||
- Can't install vuls on CentOS 7 [\#432](https://github.com/future-architect/vuls/issues/432)
 | 
			
		||||
- Vuls scan doesn't show severity score in any of the vulnerable packages [\#430](https://github.com/future-architect/vuls/issues/430)
 | 
			
		||||
- Load config failedtoml: cannot load TOML value of type string into a Go slice [\#429](https://github.com/future-architect/vuls/issues/429)
 | 
			
		||||
- vuls scan not running check-update with sudo for Centos 7 [\#428](https://github.com/future-architect/vuls/issues/428)
 | 
			
		||||
- options for configtest not being activated [\#422](https://github.com/future-architect/vuls/issues/422)
 | 
			
		||||
- "could not find project Gopkg.toml, use dep init to initiate a manifest" when installing vuls [\#420](https://github.com/future-architect/vuls/issues/420)
 | 
			
		||||
- go get not get  [\#407](https://github.com/future-architect/vuls/issues/407)
 | 
			
		||||
- Failed to scan via docker. err: Unknown format [\#404](https://github.com/future-architect/vuls/issues/404)
 | 
			
		||||
- Failed to scan - kernel-xxx is an installed security update [\#403](https://github.com/future-architect/vuls/issues/403)
 | 
			
		||||
- 169.254.169.254 port 80: Connection refused [\#402](https://github.com/future-architect/vuls/issues/402)
 | 
			
		||||
- vuls scan --debug cause `invalid memory address` error [\#397](https://github.com/future-architect/vuls/issues/397)
 | 
			
		||||
- Provide a command line flag that will automatically install aptitude on debian? [\#390](https://github.com/future-architect/vuls/issues/390)
 | 
			
		||||
 | 
			
		||||
**Merged pull requests:**
 | 
			
		||||
 | 
			
		||||
- export fill cve info [\#467](https://github.com/future-architect/vuls/pull/467) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
 | 
			
		||||
- add oval docker [\#466](https://github.com/future-architect/vuls/pull/466) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
 | 
			
		||||
- fix typos in commands. [\#464](https://github.com/future-architect/vuls/pull/464) ([ymomoi](https://github.com/ymomoi))
 | 
			
		||||
- Update README [\#463](https://github.com/future-architect/vuls/pull/463) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- export FillWithOval [\#462](https://github.com/future-architect/vuls/pull/462) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
 | 
			
		||||
- add serveruuid field [\#458](https://github.com/future-architect/vuls/pull/458) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
 | 
			
		||||
- add s3 dirctory option [\#457](https://github.com/future-architect/vuls/pull/457) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
 | 
			
		||||
- Extract Advisory.Description on RHEL, Amazon, Oracle [\#450](https://github.com/future-architect/vuls/pull/450) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- nosudo on CentOS and Fetch Changelogs on Amazon, RHEL [\#448](https://github.com/future-architect/vuls/pull/448) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- change logrus package to lowercase and update other packages [\#446](https://github.com/future-architect/vuls/pull/446) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
 | 
			
		||||
- add db backend redis [\#445](https://github.com/future-architect/vuls/pull/445) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
 | 
			
		||||
- fast test [\#435](https://github.com/future-architect/vuls/pull/435) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
 | 
			
		||||
- fix typo [\#433](https://github.com/future-architect/vuls/pull/433) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
 | 
			
		||||
- Add support for PostgreSQL as a DB storage back-end [\#431](https://github.com/future-architect/vuls/pull/431) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
 | 
			
		||||
- typo README.js.md [\#426](https://github.com/future-architect/vuls/pull/426) ([ryurock](https://github.com/ryurock))
 | 
			
		||||
- Add TOC to README [\#425](https://github.com/future-architect/vuls/pull/425) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fixing \#420 where lock and manifest have moved to TOML [\#421](https://github.com/future-architect/vuls/pull/421) ([elfgoh](https://github.com/elfgoh))
 | 
			
		||||
- Define timeout for vulnerabilities scan and platform detection [\#414](https://github.com/future-architect/vuls/pull/414) ([s7anley](https://github.com/s7anley))
 | 
			
		||||
- Enable -timeout option when detecting OS [\#410](https://github.com/future-architect/vuls/pull/410) ([knqyf263](https://github.com/knqyf263))
 | 
			
		||||
- Remove duplicate command in README [\#401](https://github.com/future-architect/vuls/pull/401) ([knqyf263](https://github.com/knqyf263))
 | 
			
		||||
- Fix to read config.toml at tui [\#441](https://github.com/future-architect/vuls/pull/441) ([usiusi360](https://github.com/usiusi360))
 | 
			
		||||
- Change NVD URL to new one [\#419](https://github.com/future-architect/vuls/pull/419) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Add some testcases [\#418](https://github.com/future-architect/vuls/pull/418) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
 | 
			
		||||
## [v0.3.0](https://github.com/future-architect/vuls/tree/v0.3.0) (2017-03-24)
 | 
			
		||||
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.2.0...v0.3.0)
 | 
			
		||||
 | 
			
		||||
**Implemented enhancements:**
 | 
			
		||||
 | 
			
		||||
- Changelog parsing fails when package maintainers aren't consistent regarding versions [\#327](https://github.com/future-architect/vuls/issues/327)
 | 
			
		||||
- Docker scan doesn't report image name [\#325](https://github.com/future-architect/vuls/issues/325)
 | 
			
		||||
- vuls report -to-email only one E-Mail [\#295](https://github.com/future-architect/vuls/issues/295)
 | 
			
		||||
- Support RHEL5 [\#286](https://github.com/future-architect/vuls/issues/286)
 | 
			
		||||
- Continue scanning even when some hosts have tech issues? [\#264](https://github.com/future-architect/vuls/issues/264)
 | 
			
		||||
- Normalization of JSON output [\#259](https://github.com/future-architect/vuls/issues/259)
 | 
			
		||||
- Add report subcommand, change scan subcommand options [\#239](https://github.com/future-architect/vuls/issues/239)
 | 
			
		||||
- scan localhost? [\#210](https://github.com/future-architect/vuls/issues/210)
 | 
			
		||||
- Can Vuls show details about updateable packages [\#341](https://github.com/future-architect/vuls/issues/341)
 | 
			
		||||
- Scan all containers except [\#285](https://github.com/future-architect/vuls/issues/285)
 | 
			
		||||
- Notify the difference from the previous scan result [\#255](https://github.com/future-architect/vuls/issues/255)
 | 
			
		||||
- EC2RoleCreds support? [\#250](https://github.com/future-architect/vuls/issues/250)
 | 
			
		||||
- Output confidence score of detection accuracy and detection method to JSON or Reporting [\#350](https://github.com/future-architect/vuls/pull/350) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Avoid null slice being null in JSON [\#345](https://github.com/future-architect/vuls/pull/345) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Add -format-one-email option [\#331](https://github.com/future-architect/vuls/pull/331) ([knqyf263](https://github.com/knqyf263))
 | 
			
		||||
- Support Raspbian [\#330](https://github.com/future-architect/vuls/pull/330) ([knqyf263](https://github.com/knqyf263))
 | 
			
		||||
- Add leniancy to the version matching for debian to account for versio… [\#328](https://github.com/future-architect/vuls/pull/328) ([jsulinski](https://github.com/jsulinski))
 | 
			
		||||
- Add image information for docker containers [\#326](https://github.com/future-architect/vuls/pull/326) ([jsulinski](https://github.com/jsulinski))
 | 
			
		||||
- Continue scanning even when some hosts have tech issues [\#309](https://github.com/future-architect/vuls/pull/309) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Add -log-dir option [\#301](https://github.com/future-architect/vuls/pull/301) ([knqyf263](https://github.com/knqyf263))
 | 
			
		||||
- Use --assumeno option [\#300](https://github.com/future-architect/vuls/pull/300) ([knqyf263](https://github.com/knqyf263))
 | 
			
		||||
- Add local scan mode\(Scan without SSH when target server is localhost\) [\#291](https://github.com/future-architect/vuls/pull/291) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Support RHEL5 [\#289](https://github.com/future-architect/vuls/pull/289) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Add LXD support [\#288](https://github.com/future-architect/vuls/pull/288) ([jiazio](https://github.com/jiazio))
 | 
			
		||||
- Add timeout option to configtest [\#400](https://github.com/future-architect/vuls/pull/400) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Notify the difference from the previous scan result [\#392](https://github.com/future-architect/vuls/pull/392) ([knqyf263](https://github.com/knqyf263))
 | 
			
		||||
- Add Oracle Linux support [\#386](https://github.com/future-architect/vuls/pull/386) ([Djelibeybi](https://github.com/Djelibeybi))
 | 
			
		||||
- Change container scan format in config.toml [\#381](https://github.com/future-architect/vuls/pull/381) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Obsolete CentOS5 support [\#378](https://github.com/future-architect/vuls/pull/378) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Deprecate prepare subcommand to minimize the root authority defined by /etc/sudoers [\#375](https://github.com/future-architect/vuls/pull/375) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Support IAM role for report to S3. [\#370](https://github.com/future-architect/vuls/pull/370) ([ohsawa0515](https://github.com/ohsawa0515))
 | 
			
		||||
- Add .travis.yml [\#363](https://github.com/future-architect/vuls/pull/363) ([knqyf263](https://github.com/knqyf263))
 | 
			
		||||
- Output changelog in report, TUI and JSON for Ubuntu/Debian/CentOS [\#356](https://github.com/future-architect/vuls/pull/356) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
 | 
			
		||||
**Fixed bugs:**
 | 
			
		||||
 | 
			
		||||
- Debian scans failing in docker [\#323](https://github.com/future-architect/vuls/issues/323)
 | 
			
		||||
- Local CVE DB is still checked, even if a CVE Dictionary URL is defined [\#316](https://github.com/future-architect/vuls/issues/316)
 | 
			
		||||
- vuls needs gmake. [\#313](https://github.com/future-architect/vuls/issues/313)
 | 
			
		||||
- patch request for FreeBSD [\#312](https://github.com/future-architect/vuls/issues/312)
 | 
			
		||||
- Report: failed to read from json \(Docker\) [\#294](https://github.com/future-architect/vuls/issues/294)
 | 
			
		||||
- -report-mail option does not output required mail header [\#282](https://github.com/future-architect/vuls/issues/282)
 | 
			
		||||
- PackInfo not found error when vuls scan. [\#281](https://github.com/future-architect/vuls/issues/281)
 | 
			
		||||
- Normalize character set [\#279](https://github.com/future-architect/vuls/issues/279)
 | 
			
		||||
- The number of Updatable Packages is different from the number of yum check-update [\#373](https://github.com/future-architect/vuls/issues/373)
 | 
			
		||||
- sudo is needed when exec yum check-update on RHEL7 [\#371](https://github.com/future-architect/vuls/issues/371)
 | 
			
		||||
- `123-3ubuntu4` should be marked as ChangelogLenientMatch [\#362](https://github.com/future-architect/vuls/issues/362)
 | 
			
		||||
- CentOS  multi package invalid result [\#360](https://github.com/future-architect/vuls/issues/360)
 | 
			
		||||
- Parse error after check-update. \(Unknown format\) [\#359](https://github.com/future-architect/vuls/issues/359)
 | 
			
		||||
- Fix candidate to confidence. [\#354](https://github.com/future-architect/vuls/pull/354) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Bug fix: not send e-mail to cc address [\#346](https://github.com/future-architect/vuls/pull/346) ([knqyf263](https://github.com/knqyf263))
 | 
			
		||||
- Change the command used for os detection from uname to freebsd-version [\#340](https://github.com/future-architect/vuls/pull/340) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix error handling of detectOS [\#337](https://github.com/future-architect/vuls/pull/337) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix infinite retry at size overrun error in Slack report [\#329](https://github.com/future-architect/vuls/pull/329) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- aptitude changelog defaults to using more, which is not interactive a… [\#324](https://github.com/future-architect/vuls/pull/324) ([jsulinski](https://github.com/jsulinski))
 | 
			
		||||
- Do not use sudo when echo [\#322](https://github.com/future-architect/vuls/pull/322) ([knqyf263](https://github.com/knqyf263))
 | 
			
		||||
- Reduce privilege requirements for commands that don't need sudo on Ubuntu/Debian [\#319](https://github.com/future-architect/vuls/pull/319) ([jsulinski](https://github.com/jsulinski))
 | 
			
		||||
- Don't check for a CVE DB when CVE Dictionary URL is defined [\#317](https://github.com/future-architect/vuls/pull/317) ([jsulinski](https://github.com/jsulinski))
 | 
			
		||||
- Fix typo contianer -\> container [\#314](https://github.com/future-architect/vuls/pull/314) ([justyns](https://github.com/justyns))
 | 
			
		||||
- Fix the changelog cache logic for ubuntu/debian [\#305](https://github.com/future-architect/vuls/pull/305) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix yum updateinfo options [\#304](https://github.com/future-architect/vuls/pull/304) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Update glide.lock to fix create-log-dir error. [\#303](https://github.com/future-architect/vuls/pull/303) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix a bug in logging \(file output\) at scan command [\#302](https://github.com/future-architect/vuls/pull/302) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Add -pipe flag \#294 [\#299](https://github.com/future-architect/vuls/pull/299) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix RHEL5 scan stopped halfway [\#293](https://github.com/future-architect/vuls/pull/293) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix amazon linux scan stopped halfway [\#292](https://github.com/future-architect/vuls/pull/292) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix nil-ponter in TUI [\#388](https://github.com/future-architect/vuls/pull/388) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix Bug of Mysql Backend [\#384](https://github.com/future-architect/vuls/pull/384) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix scan confidence on Ubuntu/Debian/Raspbian \#362 [\#379](https://github.com/future-architect/vuls/pull/379) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix updatalbe packages count \#373 [\#374](https://github.com/future-architect/vuls/pull/374) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- sudo yum check-update on RHEL [\#372](https://github.com/future-architect/vuls/pull/372) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Change ssh option from -t to -tt [\#369](https://github.com/future-architect/vuls/pull/369) ([knqyf263](https://github.com/knqyf263))
 | 
			
		||||
- Increase the width of RequestPty [\#364](https://github.com/future-architect/vuls/pull/364) ([knqyf263](https://github.com/knqyf263))
 | 
			
		||||
 | 
			
		||||
**Closed issues:**
 | 
			
		||||
 | 
			
		||||
-  vuls configtest --debugがsudoのチェックで止まってしまう [\#395](https://github.com/future-architect/vuls/issues/395)
 | 
			
		||||
- Add support for Oracle Linux [\#385](https://github.com/future-architect/vuls/issues/385)
 | 
			
		||||
- error on install - Ubuntu 16.04 [\#376](https://github.com/future-architect/vuls/issues/376)
 | 
			
		||||
- Unknown OS Type [\#335](https://github.com/future-architect/vuls/issues/335)
 | 
			
		||||
- mac os 10.12.3 make install error [\#334](https://github.com/future-architect/vuls/issues/334)
 | 
			
		||||
- assumeYes doesn't work because there is no else condition [\#320](https://github.com/future-architect/vuls/issues/320)
 | 
			
		||||
- Debian scan uses sudo where unnecessary [\#318](https://github.com/future-architect/vuls/issues/318)
 | 
			
		||||
- Add FreeBSD 11 to supported OS on documents. [\#311](https://github.com/future-architect/vuls/issues/311)
 | 
			
		||||
- docker fetchnvd failing [\#274](https://github.com/future-architect/vuls/issues/274)
 | 
			
		||||
- Latest version of labstack echo breaks installation [\#268](https://github.com/future-architect/vuls/issues/268)
 | 
			
		||||
- fetchnvd Fails using example loop [\#267](https://github.com/future-architect/vuls/issues/267)
 | 
			
		||||
 | 
			
		||||
**Merged pull requests:**
 | 
			
		||||
 | 
			
		||||
- fix typo in README.ja.md [\#394](https://github.com/future-architect/vuls/pull/394) ([lv7777](https://github.com/lv7777))
 | 
			
		||||
- Update Tutorial in README [\#387](https://github.com/future-architect/vuls/pull/387) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix README [\#383](https://github.com/future-architect/vuls/pull/383) ([usiusi360](https://github.com/usiusi360))
 | 
			
		||||
- s/dictinary/dictionary typo [\#382](https://github.com/future-architect/vuls/pull/382) ([beuno](https://github.com/beuno))
 | 
			
		||||
- Fix Japanese typo [\#377](https://github.com/future-architect/vuls/pull/377) ([IMAI-Yuji](https://github.com/IMAI-Yuji))
 | 
			
		||||
- Improve kanji character [\#351](https://github.com/future-architect/vuls/pull/351) ([hasegawa-tomoki](https://github.com/hasegawa-tomoki))
 | 
			
		||||
- Add PULL\_REQUEST\_TEMPLATE.md [\#348](https://github.com/future-architect/vuls/pull/348) ([knqyf263](https://github.com/knqyf263))
 | 
			
		||||
- Update README [\#347](https://github.com/future-architect/vuls/pull/347) ([knqyf263](https://github.com/knqyf263))
 | 
			
		||||
- Fix test case [\#344](https://github.com/future-architect/vuls/pull/344) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix typo [\#343](https://github.com/future-architect/vuls/pull/343) ([knqyf263](https://github.com/knqyf263))
 | 
			
		||||
- Rename Makefile to GNUmakefile \#313 [\#339](https://github.com/future-architect/vuls/pull/339) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Update README [\#338](https://github.com/future-architect/vuls/pull/338) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- add error handling [\#332](https://github.com/future-architect/vuls/pull/332) ([kazuminn](https://github.com/kazuminn))
 | 
			
		||||
- Update readme [\#308](https://github.com/future-architect/vuls/pull/308) ([lapthorn](https://github.com/lapthorn))
 | 
			
		||||
- Update glide.lock to fix import error [\#306](https://github.com/future-architect/vuls/pull/306) ([knqyf263](https://github.com/knqyf263))
 | 
			
		||||
- Check whether echo is executable with nopasswd [\#298](https://github.com/future-architect/vuls/pull/298) ([knqyf263](https://github.com/knqyf263))
 | 
			
		||||
- Update docker README [\#297](https://github.com/future-architect/vuls/pull/297) ([knqyf263](https://github.com/knqyf263))
 | 
			
		||||
- update readme [\#296](https://github.com/future-architect/vuls/pull/296) ([galigalikun](https://github.com/galigalikun))
 | 
			
		||||
- remove unused import line. [\#358](https://github.com/future-architect/vuls/pull/358) ([ymomoi](https://github.com/ymomoi))
 | 
			
		||||
 | 
			
		||||
## [v0.2.0](https://github.com/future-architect/vuls/tree/v0.2.0) (2017-01-10)
 | 
			
		||||
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.7...v0.2.0)
 | 
			
		||||
 | 
			
		||||
**Implemented enhancements:**
 | 
			
		||||
 | 
			
		||||
- Add report subcommand, change scan options. \#239 [\#270](https://github.com/future-architect/vuls/pull/270) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Add --assume-yes to prepare \#260 [\#266](https://github.com/future-architect/vuls/pull/266) ([Code0x58](https://github.com/Code0x58))
 | 
			
		||||
- Use RFC3339 timestamps in the results [\#265](https://github.com/future-architect/vuls/pull/265) ([Code0x58](https://github.com/Code0x58))
 | 
			
		||||
 | 
			
		||||
**Fixed bugs:**
 | 
			
		||||
 | 
			
		||||
- vuls prepare failed to centos7 [\#275](https://github.com/future-architect/vuls/issues/275)
 | 
			
		||||
- Failed to scan on RHEL5 [\#94](https://github.com/future-architect/vuls/issues/94)
 | 
			
		||||
- Fix container os detection [\#287](https://github.com/future-architect/vuls/pull/287) ([jiazio](https://github.com/jiazio))
 | 
			
		||||
- Add date header to report mail. [\#283](https://github.com/future-architect/vuls/pull/283) ([ymomoi](https://github.com/ymomoi))
 | 
			
		||||
- Add Content-Type header to report/mail.go . [\#280](https://github.com/future-architect/vuls/pull/280) ([hogehogehugahuga](https://github.com/hogehogehugahuga))
 | 
			
		||||
- Keep output of "vuls scan -report-\*" to be same every times [\#272](https://github.com/future-architect/vuls/pull/272) ([yoheimuta](https://github.com/yoheimuta))
 | 
			
		||||
- Fix JSON-dir regex pattern \#265 [\#271](https://github.com/future-architect/vuls/pull/271) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Stop quietly ignoring `--ssh-external` on Windows [\#263](https://github.com/future-architect/vuls/pull/263) ([Code0x58](https://github.com/Code0x58))
 | 
			
		||||
- Fix non-interactive `apt-get install` \#251 [\#253](https://github.com/future-architect/vuls/pull/253) ([Code0x58](https://github.com/Code0x58))
 | 
			
		||||
 | 
			
		||||
**Closed issues:**
 | 
			
		||||
 | 
			
		||||
- gocui.NewGui now takes a parameter [\#261](https://github.com/future-architect/vuls/issues/261)
 | 
			
		||||
- Add a `--yes` flag to bypass interactive prompt for `vuls prepare` [\#260](https://github.com/future-architect/vuls/issues/260)
 | 
			
		||||
- `vuls prepare` doesn't work on Debian host due to apt-get confirmation prompt [\#251](https://github.com/future-architect/vuls/issues/251)
 | 
			
		||||
 | 
			
		||||
**Merged pull requests:**
 | 
			
		||||
 | 
			
		||||
- Fix gocui.NewGui after signature change \#261 [\#262](https://github.com/future-architect/vuls/pull/262) ([Code0x58](https://github.com/Code0x58))
 | 
			
		||||
- Replace inconsistent tabs with spaces [\#254](https://github.com/future-architect/vuls/pull/254) ([Code0x58](https://github.com/Code0x58))
 | 
			
		||||
- Fix README [\#249](https://github.com/future-architect/vuls/pull/249) ([usiusi360](https://github.com/usiusi360))
 | 
			
		||||
 | 
			
		||||
## [v0.1.7](https://github.com/future-architect/vuls/tree/v0.1.7) (2016-11-08)
 | 
			
		||||
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.6...v0.1.7)
 | 
			
		||||
 | 
			
		||||
**Implemented enhancements:**
 | 
			
		||||
 | 
			
		||||
- Enable to scan only docker container, without docker host [\#122](https://github.com/future-architect/vuls/issues/122)
 | 
			
		||||
- Add -skip-broken option \[CentOS only\] \#245 [\#248](https://github.com/future-architect/vuls/pull/248) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Display unknown CVEs to TUI [\#244](https://github.com/future-architect/vuls/pull/244) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Add the XML output [\#240](https://github.com/future-architect/vuls/pull/240) ([gleentea](https://github.com/gleentea))
 | 
			
		||||
- add '-ssh-external' option to prepare subcommand [\#234](https://github.com/future-architect/vuls/pull/234) ([mykstmhr](https://github.com/mykstmhr))
 | 
			
		||||
- Integrate OWASP Dependency Check [\#232](https://github.com/future-architect/vuls/pull/232) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Add support for reading CVE data from MySQL. [\#225](https://github.com/future-architect/vuls/pull/225) ([oswell](https://github.com/oswell))
 | 
			
		||||
- Remove base docker image, -v shows commit hash [\#223](https://github.com/future-architect/vuls/pull/223) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
 | 
			
		||||
- Support ignore CveIDs in config [\#222](https://github.com/future-architect/vuls/pull/222) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Confirm before installing dependencies on prepare [\#219](https://github.com/future-architect/vuls/pull/219) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Remove all.json [\#218](https://github.com/future-architect/vuls/pull/218) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Add GitHub issue template [\#217](https://github.com/future-architect/vuls/pull/217) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Improve makefile, -version shows git hash, fix README [\#216](https://github.com/future-architect/vuls/pull/216) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- change e-mail package from gomail to net/smtp [\#211](https://github.com/future-architect/vuls/pull/211) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
 | 
			
		||||
- Add only-containers option to scan subcommand \#122 [\#190](https://github.com/future-architect/vuls/pull/190) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix -results-dir option of scan subcommand [\#185](https://github.com/future-architect/vuls/pull/185) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Show error when no scannable servers are detected. [\#177](https://github.com/future-architect/vuls/pull/177) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Add sudo check to prepare subcommand [\#176](https://github.com/future-architect/vuls/pull/176) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Supports yum --enablerepo option \(supports only base,updates for now\) [\#147](https://github.com/future-architect/vuls/pull/147) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
 | 
			
		||||
**Fixed bugs:**
 | 
			
		||||
 | 
			
		||||
- Debian 8.6 \(jessie\) scan does not show vulnerable packages [\#235](https://github.com/future-architect/vuls/issues/235)
 | 
			
		||||
- panic: runtime error: index out of range - ubuntu 16.04 + vuls history [\#180](https://github.com/future-architect/vuls/issues/180)
 | 
			
		||||
- Moved golang.org/x/net/context to context [\#243](https://github.com/future-architect/vuls/pull/243) ([yoheimuta](https://github.com/yoheimuta))
 | 
			
		||||
- Fix changelog cache bug on Ubuntu and Debian \#235 [\#238](https://github.com/future-architect/vuls/pull/238) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- add '-ssh-external' option to prepare subcommand [\#234](https://github.com/future-architect/vuls/pull/234) ([mykstmhr](https://github.com/mykstmhr))
 | 
			
		||||
- Fixed error for the latest version of gocui [\#231](https://github.com/future-architect/vuls/pull/231) ([ymd38](https://github.com/ymd38))
 | 
			
		||||
- Handle the refactored gocui SetCurrentView method. [\#229](https://github.com/future-architect/vuls/pull/229) ([oswell](https://github.com/oswell))
 | 
			
		||||
- Fix locale env var LANG to LANGUAGE [\#215](https://github.com/future-architect/vuls/pull/215) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fixed bug with parsing update line on CentOS/RHEL [\#206](https://github.com/future-architect/vuls/pull/206) ([andyone](https://github.com/andyone))
 | 
			
		||||
- Fix defer cache.DB.close [\#201](https://github.com/future-architect/vuls/pull/201) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix a help message of -report-azure-blob option [\#195](https://github.com/future-architect/vuls/pull/195) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix error handling in tui [\#193](https://github.com/future-architect/vuls/pull/193) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix not working changelog cache on Container [\#189](https://github.com/future-architect/vuls/pull/189) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix release version detection on FreeBSD [\#184](https://github.com/future-architect/vuls/pull/184) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix defer cahce.DB.close\(\) [\#183](https://github.com/future-architect/vuls/pull/183) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix a mode of files/dir \(report, log\) [\#182](https://github.com/future-architect/vuls/pull/182) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix a error when no json dirs are found under results \#180 [\#181](https://github.com/future-architect/vuls/pull/181) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- ssh-external option of configtest is not working \#178 [\#179](https://github.com/future-architect/vuls/pull/179) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
 | 
			
		||||
**Closed issues:**
 | 
			
		||||
 | 
			
		||||
- --enable-repos of yum option [\#246](https://github.com/future-architect/vuls/issues/246)
 | 
			
		||||
- --skip-broken at yum option [\#245](https://github.com/future-architect/vuls/issues/245)
 | 
			
		||||
- Recent changes to gobui cause build failures [\#228](https://github.com/future-architect/vuls/issues/228)
 | 
			
		||||
- https://hub.docker.com/r/vuls/go-cve-dictionary/ is empty [\#208](https://github.com/future-architect/vuls/issues/208)
 | 
			
		||||
- Not able to install gomail fails [\#202](https://github.com/future-architect/vuls/issues/202)
 | 
			
		||||
- No results file created - vuls tui failed [\#199](https://github.com/future-architect/vuls/issues/199)
 | 
			
		||||
- Wrong file permissions for results/\*.json in official Docker container [\#197](https://github.com/future-architect/vuls/issues/197)
 | 
			
		||||
- Failed: Unknown OS Type [\#196](https://github.com/future-architect/vuls/issues/196)
 | 
			
		||||
- Segmentation fault with configtest [\#192](https://github.com/future-architect/vuls/issues/192)
 | 
			
		||||
- Failed to scan. err: No server defined. Check the configuration [\#187](https://github.com/future-architect/vuls/issues/187)
 | 
			
		||||
- vuls configtest -ssh-external doesnt work [\#178](https://github.com/future-architect/vuls/issues/178)
 | 
			
		||||
- apt-get update: time out [\#175](https://github.com/future-architect/vuls/issues/175)
 | 
			
		||||
- scanning on Centos6, but vuls recognizes debian. [\#174](https://github.com/future-architect/vuls/issues/174)
 | 
			
		||||
- Fix READMEja  \#164  [\#173](https://github.com/future-architect/vuls/issues/173)
 | 
			
		||||
 | 
			
		||||
**Merged pull requests:**
 | 
			
		||||
 | 
			
		||||
- Update README \#225 [\#242](https://github.com/future-architect/vuls/pull/242) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- fix readme [\#241](https://github.com/future-architect/vuls/pull/241) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
 | 
			
		||||
- Fix README \#234 [\#237](https://github.com/future-architect/vuls/pull/237) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Update glide files [\#236](https://github.com/future-architect/vuls/pull/236) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- fix README [\#226](https://github.com/future-architect/vuls/pull/226) ([usiusi360](https://github.com/usiusi360))
 | 
			
		||||
- fix some misspelling. [\#221](https://github.com/future-architect/vuls/pull/221) ([ymomoi](https://github.com/ymomoi))
 | 
			
		||||
- fix docker readme [\#214](https://github.com/future-architect/vuls/pull/214) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
 | 
			
		||||
- Fix ja document about typo [\#213](https://github.com/future-architect/vuls/pull/213) ([shokohara](https://github.com/shokohara))
 | 
			
		||||
- fix readme [\#212](https://github.com/future-architect/vuls/pull/212) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
 | 
			
		||||
- fix README [\#207](https://github.com/future-architect/vuls/pull/207) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
 | 
			
		||||
- fix typo [\#204](https://github.com/future-architect/vuls/pull/204) ([usiusi360](https://github.com/usiusi360))
 | 
			
		||||
- fix gitignore [\#191](https://github.com/future-architect/vuls/pull/191) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
 | 
			
		||||
- Update glide.lock [\#188](https://github.com/future-architect/vuls/pull/188) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix path in setup/docker/README [\#186](https://github.com/future-architect/vuls/pull/186) ([dladuke](https://github.com/dladuke))
 | 
			
		||||
- Vuls and vulsrepo are now separated [\#163](https://github.com/future-architect/vuls/pull/163) ([hikachan](https://github.com/hikachan))
 | 
			
		||||
 | 
			
		||||
## [v0.1.6](https://github.com/future-architect/vuls/tree/v0.1.6) (2016-09-12)
 | 
			
		||||
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.5...v0.1.6)
 | 
			
		||||
 | 
			
		||||
**Implemented enhancements:**
 | 
			
		||||
 | 
			
		||||
- High speed scan on Ubuntu/Debian [\#172](https://github.com/future-architect/vuls/pull/172) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Support CWE\(Common Weakness Enumeration\) [\#169](https://github.com/future-architect/vuls/pull/169) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Enable to scan without sudo on amazon linux [\#167](https://github.com/future-architect/vuls/pull/167) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Remove deprecated options -use-unattended-upgrades,-use-yum-plugin-security [\#161](https://github.com/future-architect/vuls/pull/161) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- delete sqlite3 [\#152](https://github.com/future-architect/vuls/pull/152) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
 | 
			
		||||
 | 
			
		||||
**Fixed bugs:**
 | 
			
		||||
 | 
			
		||||
- Failed to setup vuls docker [\#170](https://github.com/future-architect/vuls/issues/170)
 | 
			
		||||
- yum check-update error occurred when no reboot after kernel updating [\#165](https://github.com/future-architect/vuls/issues/165)
 | 
			
		||||
- error thrown from 'docker build .' [\#157](https://github.com/future-architect/vuls/issues/157)
 | 
			
		||||
- CVE-ID is truncated to 4 digits [\#153](https://github.com/future-architect/vuls/issues/153)
 | 
			
		||||
- 'yum update --changelog' stalled in 'vuls scan'. if ssh user is not 'root'. [\#150](https://github.com/future-architect/vuls/issues/150)
 | 
			
		||||
- Panic on packet scan [\#131](https://github.com/future-architect/vuls/issues/131)
 | 
			
		||||
- Update glide.lock \#170 [\#171](https://github.com/future-architect/vuls/pull/171) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix detecting a platform on Azure [\#168](https://github.com/future-architect/vuls/pull/168) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix parse error for yum check-update \#165 [\#166](https://github.com/future-architect/vuls/pull/166) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix bug: Vuls on Docker [\#159](https://github.com/future-architect/vuls/pull/159) ([tjinjin](https://github.com/tjinjin))
 | 
			
		||||
- Fix CVE-ID is truncated to 4 digits [\#155](https://github.com/future-architect/vuls/pull/155) ([usiusi360](https://github.com/usiusi360))
 | 
			
		||||
- Fix yum update --changelog stalled when non-root ssh user on CentOS \#150 [\#151](https://github.com/future-architect/vuls/pull/151) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
 | 
			
		||||
**Closed issues:**
 | 
			
		||||
 | 
			
		||||
- Support su for root privilege escalation [\#44](https://github.com/future-architect/vuls/issues/44)
 | 
			
		||||
- Support FreeBSD [\#34](https://github.com/future-architect/vuls/issues/34)
 | 
			
		||||
 | 
			
		||||
**Merged pull requests:**
 | 
			
		||||
 | 
			
		||||
- Change scripts for data fetching from jvn [\#164](https://github.com/future-architect/vuls/pull/164) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fix: setup vulsrepo [\#162](https://github.com/future-architect/vuls/pull/162) ([tjinjin](https://github.com/tjinjin))
 | 
			
		||||
- Fix-docker-vulsrepo-install [\#160](https://github.com/future-architect/vuls/pull/160) ([usiusi360](https://github.com/usiusi360))
 | 
			
		||||
- Reduce regular expression compilation [\#158](https://github.com/future-architect/vuls/pull/158) ([itchyny](https://github.com/itchyny))
 | 
			
		||||
- Add testcases for \#153 [\#156](https://github.com/future-architect/vuls/pull/156) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
 | 
			
		||||
## [v0.1.5](https://github.com/future-architect/vuls/tree/v0.1.5) (2016-08-16)
 | 
			
		||||
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.4...v0.1.5)
 | 
			
		||||
 | 
			
		||||
@@ -141,7 +486,7 @@
 | 
			
		||||
- Maximum 6 nodes available to scan [\#12](https://github.com/future-architect/vuls/issues/12)
 | 
			
		||||
- panic: runtime error: index out of range [\#5](https://github.com/future-architect/vuls/issues/5)
 | 
			
		||||
- Fix sudo option on RedHat like Linux and change some messages. [\#20](https://github.com/future-architect/vuls/pull/20) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Typo fix and updated readme [\#19](https://github.com/future-architect/vuls/pull/19) ([Euan-Kerr](https://github.com/Euan-Kerr))
 | 
			
		||||
- Typo fix and updated readme [\#19](https://github.com/future-architect/vuls/pull/19) ([EuanKerr](https://github.com/EuanKerr))
 | 
			
		||||
- remove a period at the end of error messages. [\#18](https://github.com/future-architect/vuls/pull/18) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- fix error while yum updateinfo --security update on rhel@aws [\#17](https://github.com/future-architect/vuls/pull/17) ([kotakanbe](https://github.com/kotakanbe))
 | 
			
		||||
- Fixed typos [\#15](https://github.com/future-architect/vuls/pull/15) ([radarhere](https://github.com/radarhere))
 | 
			
		||||
@@ -166,4 +511,4 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
 | 
			
		||||
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,33 @@
 | 
			
		||||
FROM golang:alpine as builder
 | 
			
		||||
 | 
			
		||||
RUN apk add --no-cache \
 | 
			
		||||
        git \
 | 
			
		||||
        make \
 | 
			
		||||
        gcc \
 | 
			
		||||
        musl-dev
 | 
			
		||||
 | 
			
		||||
ENV REPOSITORY github.com/future-architect/vuls
 | 
			
		||||
COPY . $GOPATH/src/$REPOSITORY
 | 
			
		||||
RUN cd $GOPATH/src/$REPOSITORY && make install
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FROM alpine:3.7
 | 
			
		||||
 | 
			
		||||
MAINTAINER hikachan sadayuki-matsuno
 | 
			
		||||
 | 
			
		||||
ENV LOGDIR /var/log/vuls
 | 
			
		||||
ENV WORKDIR /vuls
 | 
			
		||||
 | 
			
		||||
RUN apk add --no-cache \
 | 
			
		||||
        openssh-client \
 | 
			
		||||
        ca-certificates \
 | 
			
		||||
    && mkdir -p $WORKDIR $LOGDIR
 | 
			
		||||
 | 
			
		||||
COPY --from=builder /go/bin/vuls /usr/local/bin/
 | 
			
		||||
 | 
			
		||||
VOLUME ["$WORKDIR", "$LOGDIR"]
 | 
			
		||||
WORKDIR $WORKDIR
 | 
			
		||||
ENV PWD $WORKDIR
 | 
			
		||||
 | 
			
		||||
ENTRYPOINT ["vuls"]
 | 
			
		||||
CMD ["--help"]
 | 
			
		||||
							
								
								
									
										69
									
								
								GNUmakefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,69 @@
 | 
			
		||||
.PHONY: \
 | 
			
		||||
	build \
 | 
			
		||||
	install \
 | 
			
		||||
	all \
 | 
			
		||||
	vendor \
 | 
			
		||||
 	lint \
 | 
			
		||||
	vet \
 | 
			
		||||
	fmt \
 | 
			
		||||
	fmtcheck \
 | 
			
		||||
	pretest \
 | 
			
		||||
	test \
 | 
			
		||||
	cov \
 | 
			
		||||
	clean
 | 
			
		||||
 | 
			
		||||
SRCS = $(shell git ls-files '*.go')
 | 
			
		||||
PKGS = $(shell go list ./...)
 | 
			
		||||
VERSION := $(shell git describe --tags --abbrev=0)
 | 
			
		||||
REVISION := $(shell git rev-parse --short HEAD)
 | 
			
		||||
BUILDTIME := $(shell date "+%Y%m%d_%H%M%S")
 | 
			
		||||
LDFLAGS := -X 'github.com/future-architect/vuls/config.Version=$(VERSION)' \
 | 
			
		||||
    -X 'github.com/future-architect/vuls/config.Revision=build-$(BUILDTIME)_$(REVISION)'
 | 
			
		||||
GO := GO111MODULE=on go
 | 
			
		||||
GO_OFF := GO111MODULE=off go
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
all: build
 | 
			
		||||
 | 
			
		||||
build: main.go pretest
 | 
			
		||||
	$(GO) build -a -ldflags "$(LDFLAGS)" -o vuls $<
 | 
			
		||||
 | 
			
		||||
b: 	main.go pretest
 | 
			
		||||
	$(GO) build -ldflags "$(LDFLAGS)" -o vuls $<
 | 
			
		||||
 | 
			
		||||
install: main.go pretest
 | 
			
		||||
	$(GO) install -ldflags "$(LDFLAGS)"
 | 
			
		||||
 | 
			
		||||
lint:
 | 
			
		||||
	$(GO_OFF) get -u golang.org/x/lint/golint
 | 
			
		||||
	golint $(PKGS)
 | 
			
		||||
 | 
			
		||||
vet:
 | 
			
		||||
	echo $(PKGS) | xargs env $(GO) vet || exit;
 | 
			
		||||
 | 
			
		||||
fmt:
 | 
			
		||||
	gofmt -s -w $(SRCS)
 | 
			
		||||
 | 
			
		||||
mlint:
 | 
			
		||||
	$(foreach file,$(SRCS),gometalinter $(file) || exit;)
 | 
			
		||||
 | 
			
		||||
fmtcheck:
 | 
			
		||||
	$(foreach file,$(SRCS),gofmt -s -d $(file);)
 | 
			
		||||
 | 
			
		||||
pretest: lint vet fmtcheck
 | 
			
		||||
 | 
			
		||||
test: 
 | 
			
		||||
	$(GO) test -cover -v ./... || exit;
 | 
			
		||||
 | 
			
		||||
unused:
 | 
			
		||||
	$(foreach pkg,$(PKGS),unused $(pkg);)
 | 
			
		||||
 | 
			
		||||
cov:
 | 
			
		||||
	@ go get -v github.com/axw/gocov/gocov
 | 
			
		||||
	@ go get golang.org/x/tools/cmd/cover
 | 
			
		||||
	gocov test | gocov report
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	echo $(PKGS) | xargs go clean || exit;
 | 
			
		||||
	echo $(PKGS) | xargs go clean || exit;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										4
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						@@ -632,7 +632,7 @@ 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.
 | 
			
		||||
    Copyright (C) 2016  Future Corporation , 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
 | 
			
		||||
@@ -652,7 +652,7 @@ 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.
 | 
			
		||||
    Vuls  Copyright (C) 2016  Future Corporation , 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.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										51
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						@@ -1,51 +0,0 @@
 | 
			
		||||
.PHONY: \
 | 
			
		||||
	all \
 | 
			
		||||
	vendor \
 | 
			
		||||
	lint \
 | 
			
		||||
	vet \
 | 
			
		||||
	fmt \
 | 
			
		||||
	fmtcheck \
 | 
			
		||||
	pretest \
 | 
			
		||||
	test \
 | 
			
		||||
	cov \
 | 
			
		||||
	clean
 | 
			
		||||
 | 
			
		||||
SRCS = $(shell git ls-files '*.go')
 | 
			
		||||
PKGS = ./. ./db ./config ./models ./report ./cveapi ./scan ./util ./commands
 | 
			
		||||
 | 
			
		||||
all: test
 | 
			
		||||
 | 
			
		||||
#  vendor:
 | 
			
		||||
#          @ go get -v github.com/mjibson/party
 | 
			
		||||
#          party -d external -c -u
 | 
			
		||||
 | 
			
		||||
lint:
 | 
			
		||||
	@ go get -v github.com/golang/lint/golint
 | 
			
		||||
	$(foreach file,$(SRCS),golint $(file) || exit;)
 | 
			
		||||
 | 
			
		||||
vet:
 | 
			
		||||
	#  @-go get -v golang.org/x/tools/cmd/vet
 | 
			
		||||
	$(foreach pkg,$(PKGS),go vet $(pkg);)
 | 
			
		||||
 | 
			
		||||
fmt:
 | 
			
		||||
	gofmt -w $(SRCS)
 | 
			
		||||
 | 
			
		||||
fmtcheck:
 | 
			
		||||
	$(foreach file,$(SRCS),gofmt -d $(file);)
 | 
			
		||||
 | 
			
		||||
pretest: lint vet fmtcheck
 | 
			
		||||
 | 
			
		||||
test: pretest
 | 
			
		||||
	$(foreach pkg,$(PKGS),go test -v $(pkg) || exit;)
 | 
			
		||||
 | 
			
		||||
unused :
 | 
			
		||||
	$(foreach pkg,$(PKGS),unused $(pkg);)
 | 
			
		||||
 | 
			
		||||
cov:
 | 
			
		||||
	@ go get -v github.com/axw/gocov/gocov
 | 
			
		||||
	@ go get golang.org/x/tools/cmd/cover
 | 
			
		||||
	gocov test | gocov report
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	$(foreach pkg,$(PKGS),go clean $(pkg) || exit;)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								NOTICE
									
									
									
									
									
								
							
							
						
						@@ -1,2 +1,2 @@
 | 
			
		||||
Vuls Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
Vuls Copyright (C) 2016  Future Corporation , Japan.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
    - Cloud, auto-hébergement, Docker
 | 
			
		||||
- Scan d'intergiciels non inclus dans le gestionnaire de paquets de l'OS
 | 
			
		||||
    - Scan d'intergiciels, de libraries de language de programmation et framework pour des vulnérabilités
 | 
			
		||||
    - Supporte les logiciels inscrits au CPE
 | 
			
		||||
- Architecture sans agent
 | 
			
		||||
    - L'utilisateur doit seulement mettre en place VULS sur une seule machine qui se connectera aux autres via SSH
 | 
			
		||||
- Génération automatique des fichiers de configuration
 | 
			
		||||
    - Auto detection de serveurs via CIDR et génération de configuration
 | 
			
		||||
- Email et notification Slack possibles (supporte le Japonais) 
 | 
			
		||||
- Les résultats d'un scan sont accessibles dans un shell via TUI Viewer terminal.
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
# Ce que Vuls ne fait pas
 | 
			
		||||
 | 
			
		||||
- Vuls ne met pas à jour les programmes affectés par les vulnérabilités découvertes.
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
# Hello Vuls 
 | 
			
		||||
 | 
			
		||||
Ce tutoriel décrit la recherche de vulnérabilités sur une machine locale avec Vuls.
 | 
			
		||||
Voici les étapes à suivre. 
 | 
			
		||||
 | 
			
		||||
1. Démrarrage d'Amazon Linux
 | 
			
		||||
1. Autoriser les connexions SSH depuis localhost
 | 
			
		||||
1. Installation des prérequis
 | 
			
		||||
1. Déploiement de go-cve-dictionary
 | 
			
		||||
1. Deploiement de Vuls
 | 
			
		||||
1. Configuration
 | 
			
		||||
1. Préparation
 | 
			
		||||
1. Scan
 | 
			
		||||
1. TUI(Terminal-Based User Interface)
 | 
			
		||||
 | 
			
		||||
## Step1. Démrarrage d'Amazon Linux
 | 
			
		||||
 | 
			
		||||
- Nous utilisons dans cette exemple une vieille AMI (amzn-ami-hvm-2015.09.1.x86_64-gp2 - ami-383c1956)
 | 
			
		||||
- Taille de l'instance : t2.medium
 | 
			
		||||
    - La première fois, t2.medium et plus sont requis pour la récupération des CVE depuis NVD (2.3GB de mémoire utilisé)
 | 
			
		||||
    - Une fois la récupération initiale des données NVD terminée vous pouvez passer sur une instance t2.nano.
 | 
			
		||||
- Ajoutez la configuration suivante au cloud-init, afin d'éviter une mise à jour automatique lors du premier démarrage.
 | 
			
		||||
 | 
			
		||||
    - [Q: How do I disable the automatic installation of critical and important security updates on initial launch?](https://aws.amazon.com/amazon-linux-ami/faqs/?nc1=h_ls)
 | 
			
		||||
    ```
 | 
			
		||||
    #cloud-config
 | 
			
		||||
    repo_upgrade: none
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
## Step2. Paramètres SSH
 | 
			
		||||
 | 
			
		||||
Il est obligatoire que le serveur puisse se connecter à son propre serveur SSH
 | 
			
		||||
 | 
			
		||||
Générez une paire de clés SSH et ajoutez la clé publique dans le fichier authorized_keys
 | 
			
		||||
```bash
 | 
			
		||||
$ ssh-keygen -t rsa
 | 
			
		||||
$ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
 | 
			
		||||
$ chmod 600 ~/.ssh/authorized_keys
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Step3. Installation des prérequis
 | 
			
		||||
 | 
			
		||||
Vuls requiert l'installation des paquets suivants : 
 | 
			
		||||
 | 
			
		||||
- sqlite
 | 
			
		||||
- git
 | 
			
		||||
- gcc
 | 
			
		||||
- go v1.6
 | 
			
		||||
    - https://golang.org/doc/install
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
$ ssh ec2-user@52.100.100.100  -i ~/.ssh/private.pem
 | 
			
		||||
$ sudo yum -y install sqlite git gcc
 | 
			
		||||
$ wget https://storage.googleapis.com/golang/go1.6.linux-amd64.tar.gz
 | 
			
		||||
$ sudo tar -C /usr/local -xzf go1.6.linux-amd64.tar.gz
 | 
			
		||||
$ mkdir $HOME/go
 | 
			
		||||
```
 | 
			
		||||
Ajoutez les lignes suivantes dans /etc/profile.d/goenv.sh
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
export GOROOT=/usr/local/go
 | 
			
		||||
export GOPATH=$HOME/go
 | 
			
		||||
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Ajoutons ces nouvelles variables d’environnement au shell
 | 
			
		||||
```bash
 | 
			
		||||
$ source /etc/profile.d/goenv.sh
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Step4. Déploiement de [go-cve-dictionary](https://github.com/kotakanbe/go-cve-dictionary)
 | 
			
		||||
 | 
			
		||||
go get
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
$ sudo mkdir /var/log/vuls
 | 
			
		||||
$ sudo chown ec2-user /var/log/vuls
 | 
			
		||||
$ sudo chmod 700 /var/log/vuls
 | 
			
		||||
$ go get github.com/kotakanbe/go-cve-dictionary
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Démarrez go-cve-dictionary en mode serveur.
 | 
			
		||||
Lors de son premier démarrage go-cve-dictionary récupère la liste des vulnérabilités depuis NVD
 | 
			
		||||
Cette opération prend environ 10 minutes (sur AWS).  
 | 
			
		||||
 | 
			
		||||
## 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 Claculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-0494&vector=(AV:N/AC:L/Au:N/C:C/I:C/A:C)
 | 
			
		||||
RHEL-CVE        https://access.redhat.com/security/cve/CVE-2016-0494
 | 
			
		||||
ALAS-2016-643   https://alas.aws.amazon.com/ALAS-2016-643.html
 | 
			
		||||
Package/CPE     java-1.7.0-openjdk-1.7.0.91-2.6.2.2.63.amzn1 -> java-1.7.0-openjdk-1:1.7.0.95-2.6.4.0.65.amzn1
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Step9. TUI
 | 
			
		||||
 | 
			
		||||
Les résultats de Vuls peuvent etre affichés dans un Shell via TUI (Terminal-Based User Interface).
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
$ vuls tui
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
For more information see [README in English](https://github.com/future-architect/vuls/blob/master/README.md)  
 | 
			
		||||
							
								
								
									
										1201
									
								
								README.ja.md
									
									
									
									
									
								
							
							
						
						
							
								
								
									
										2897
									
								
								alert/alert_jp.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										920
									
								
								alert/alert_us.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,920 @@
 | 
			
		||||
package alert
 | 
			
		||||
 | 
			
		||||
// AlertDictEn has USCERT alerts
 | 
			
		||||
var AlertDictEn = map[string]Alert{
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA08-352A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA08-352A",
 | 
			
		||||
		Title: `Microsoft Internet Explorer Data Binding Vulnerability`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA08-350A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA08-350A",
 | 
			
		||||
		Title: `Apple Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA08-344A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA08-344A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA08-340A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA08-340A",
 | 
			
		||||
		Title: `Sun Java Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA08-319A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA08-319A",
 | 
			
		||||
		Title: `Mozilla Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-132A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-132A",
 | 
			
		||||
		Title: `Microsoft PowerPoint Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-041A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-041A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-343A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-343A",
 | 
			
		||||
		Title: `Adobe Flash Vulnerabilities Affect Flash Player and Adobe AIR`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-218A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-218A",
 | 
			
		||||
		Title: `Apple Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-195A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-195A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-342A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-342A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-286B": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-286B",
 | 
			
		||||
		Title: `Adobe Reader and Acrobat Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-160B": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-160B",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-069A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-069A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-223A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-223A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-013A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-013A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple SMB Protocol Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-294A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-294A",
 | 
			
		||||
		Title: `Oracle Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-020A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-020A",
 | 
			
		||||
		Title: `Microsoft Windows Does Not Disable AutoRun Properly`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-133A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-133A",
 | 
			
		||||
		Title: `Apple Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-022A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-022A",
 | 
			
		||||
		Title: `Apple QuickTime Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-051A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-051A",
 | 
			
		||||
		Title: `Adobe Acrobat and Reader Vulnerability`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-015A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-015A",
 | 
			
		||||
		Title: `Oracle Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-251A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-251A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-209A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-209A",
 | 
			
		||||
		Title: `Microsoft Windows, Internet Explorer, and Active Template Library (ATL) Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-204A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-204A",
 | 
			
		||||
		Title: `Adobe Flash Vulnerability Affects Flash Player and Other Adobe Products`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-161A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-161A",
 | 
			
		||||
		Title: `Adobe Acrobat and Reader Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-133B": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-133B",
 | 
			
		||||
		Title: `Adobe Reader and Acrobat JavaScript Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-088A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-088A",
 | 
			
		||||
		Title: `Conficker Worm Targets Microsoft Windows Systems`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-314A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-314A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-105A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-105A",
 | 
			
		||||
		Title: `Oracle Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-104A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-104A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-286A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-286A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA09-187A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA09-187A",
 | 
			
		||||
		Title: `Microsoft Video ActiveX Control Vulnerability`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-238A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-238A",
 | 
			
		||||
		Title: `Microsoft Windows Insecurely Loads Dynamic Libraries`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-159B": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-159B",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-103B": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-103B",
 | 
			
		||||
		Title: `Oracle Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-021A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-021A",
 | 
			
		||||
		Title: `Microsoft Internet Explorer Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-012A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-012A",
 | 
			
		||||
		Title: `Oracle Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-313A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-313A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-285A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-285A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-263A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-263A",
 | 
			
		||||
		Title: `Adobe Flash Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-103C": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-103C",
 | 
			
		||||
		Title: `Adobe Reader and Acrobat Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-040A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-040A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-194B": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-194B",
 | 
			
		||||
		Title: `Oracle Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-194A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-194A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-131A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-131A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-068A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-068A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-348A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-348A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-257A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-257A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-231A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-231A",
 | 
			
		||||
		Title: `Adobe Reader and Acrobat Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-222A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-222A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-162A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-162A",
 | 
			
		||||
		Title: `Adobe Flash and AIR Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-159A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-159A",
 | 
			
		||||
		Title: `Adobe Flash, Reader, and Acrobat Vulnerability`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-089A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-089A",
 | 
			
		||||
		Title: `Microsoft Internet Explorer Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-013A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-013A",
 | 
			
		||||
		Title: `Adobe Reader and Acrobat Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-287A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-287A",
 | 
			
		||||
		Title: `Oracle Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-279A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-279A",
 | 
			
		||||
		Title: `Adobe Reader and Acrobat Affected by Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-223A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-223A",
 | 
			
		||||
		Title: `Adobe Flash and AIR Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA10-012B": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA10-012B",
 | 
			
		||||
		Title: `Microsoft Windows EOT Font and Adobe Flash Player 6 Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA11-165A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA11-165A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA11-067A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA11-067A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA11-039A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA11-039A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA11-222A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA11-222A",
 | 
			
		||||
		Title: `Adobe Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA11-193A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA11-193A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA11-201A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA11-201A",
 | 
			
		||||
		Title: `Oracle Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA11-166A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA11-166A",
 | 
			
		||||
		Title: `Adobe Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA11-130A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA11-130A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA11-312A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA11-312A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA11-286A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA11-286A",
 | 
			
		||||
		Title: `Apple Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA11-350A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA11-350A",
 | 
			
		||||
		Title: `Adobe Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA11-221A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA11-221A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA11-256A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA11-256A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA11-200A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA11-200A",
 | 
			
		||||
		Title: `Security Recommendations to Prevent Cyber Intrusions`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA11-102A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA11-102A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA11-011A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA11-011A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA11-347A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA11-347A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA11-284A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA11-284A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA12-262A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA12-262A",
 | 
			
		||||
		Title: `Microsoft Security Advisory for Internet Explorer Exploit`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA12-240A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA12-240A",
 | 
			
		||||
		Title: `Oracle Java 7 Security Manager Bypass Vulnerability`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA12-227A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA12-227A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA12-129A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA12-129A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA12-101B": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA12-101B",
 | 
			
		||||
		Title: `Adobe Reader and Acrobat Security Updates and Architectural Improvements`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA12-010A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA12-010A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA12-006A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA12-006A",
 | 
			
		||||
		Title: `Wi-Fi Protected Setup (WPS) Vulnerable to Brute-Force Attack`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA12-265A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA12-265A",
 | 
			
		||||
		Title: `Microsoft Releases Patch for Internet Explorer Exploit`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA12-255A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA12-255A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA12-251A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA12-251A",
 | 
			
		||||
		Title: `Microsoft Update For Minimum Certificate Key Length`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA12-174A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA12-174A",
 | 
			
		||||
		Title: `Microsoft XML Core Services Attack Activity`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA12-164A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA12-164A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA12-101A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA12-101A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA12-318A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA12-318A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA12-283A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA12-283A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA12-346A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA12-346A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA12-192A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA12-192A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA12-073A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA12-073A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA12-045A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA12-045A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA12-024A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA12-024A",
 | 
			
		||||
		Title: `"Anonymous" DDoS Activity`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-134A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-134A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-043B": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-043B",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-008A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-008A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-051A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-051A",
 | 
			
		||||
		Title: `Oracle Java Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-043A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-043A",
 | 
			
		||||
		Title: `Adobe Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-225A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-225A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-207A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-207A",
 | 
			
		||||
		Title: `Risks of Using the Intelligent Platform Management Interface (IPMI)`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-141A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-141A",
 | 
			
		||||
		Title: `Washington, DC Radio Station Web Site Compromises`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-175A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-175A",
 | 
			
		||||
		Title: `Risks of Default Passwords on the Internet`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-169A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-169A",
 | 
			
		||||
		Title: `Oracle Releases Updates for Javadoc and Other Java SE Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-168A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-168A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-100A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-100A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-088A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-088A",
 | 
			
		||||
		Title: `DNS Amplification Attacks`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-317A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-317A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-309A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-309A",
 | 
			
		||||
		Title: `CryptoLocker Ransomware Infections`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-288A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-288A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-064A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-064A",
 | 
			
		||||
		Title: `Oracle Java Contains Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-032A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-032A",
 | 
			
		||||
		Title: `Oracle Java Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-024A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-024A",
 | 
			
		||||
		Title: `Content Management Systems Security and Associated Risks`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-107A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-107A",
 | 
			
		||||
		Title: `Oracle Has Released Multiple Updates for Java SE`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-071A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-071A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-015A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-015A",
 | 
			
		||||
		Title: `Microsoft Releases Update for Internet Explorer Vulnerability CVE-2012-4792`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-010A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-010A",
 | 
			
		||||
		Title: `Oracle Java 7 Security Manager Bypass Vulnerability`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-253A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-253A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-193A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-193A",
 | 
			
		||||
		Title: `Exploit Tool Targets Vulnerabilities in McAfee ePolicy Orchestrator (ePO)`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA13-190A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA13-190A",
 | 
			
		||||
		Title: `Microsoft Updates for Multiple Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA14-323A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA14-323A",
 | 
			
		||||
		Title: `Microsoft Windows Kerberos KDC Remote Privilege Escalation Vulnerability `,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA14-300A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA14-300A",
 | 
			
		||||
		Title: `Phishing Campaign Linked with “Dyre” Banking Malware`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA14-295A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA14-295A",
 | 
			
		||||
		Title: `Crypto Ransomware`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA14-318B": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA14-318B",
 | 
			
		||||
		Title: `Microsoft Windows OLE Automation Array Remote Code Execution Vulnerability `,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA14-317A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA14-317A",
 | 
			
		||||
		Title: `Apple iOS 'Masque Attack' Technique`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA14-290A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA14-290A",
 | 
			
		||||
		Title: `SSL 3.0 Protocol Vulnerability and POODLE Attack`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA14-017A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA14-017A",
 | 
			
		||||
		Title: `UDP-Based Amplification Attacks`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA14-002A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA14-002A",
 | 
			
		||||
		Title: `Malware Targeting Point of Sale Systems`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA14-318A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA14-318A",
 | 
			
		||||
		Title: `Microsoft Secure Channel (Schannel) Vulnerability (CVE-2014-6321) `,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA14-310A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA14-310A",
 | 
			
		||||
		Title: `Microsoft Ending Support for Windows Server 2003 Operating System`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA14-268A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA14-268A",
 | 
			
		||||
		Title: `GNU Bourne-Again Shell (Bash) ‘Shellshock’ Vulnerability (CVE-2014-6271, CVE-2014-7169, CVE-2014-7186, CVE-2014-7187, CVE-2014-6277 and CVE 2014-6278) `,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA14-098A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA14-098A",
 | 
			
		||||
		Title: `OpenSSL 'Heartbleed' vulnerability (CVE-2014-0160)`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA14-353A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA14-353A",
 | 
			
		||||
		Title: `Targeted Destructive Malware`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA14-329A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA14-329A",
 | 
			
		||||
		Title: `Regin Malware`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA14-212A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA14-212A",
 | 
			
		||||
		Title: `Backoff Point-of-Sale Malware`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA14-150A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA14-150A",
 | 
			
		||||
		Title: `GameOver Zeus P2P Malware`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA14-013A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA14-013A",
 | 
			
		||||
		Title: `NTP Amplification Attacks Using CVE-2013-5211`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA15-195A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA15-195A",
 | 
			
		||||
		Title: `Adobe Flash and Microsoft Windows Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA15-337A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA15-337A",
 | 
			
		||||
		Title: `Dorkbot`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA15-240A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA15-240A",
 | 
			
		||||
		Title: `Controlling Outbound DNS Access`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA15-213A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA15-213A",
 | 
			
		||||
		Title: `Recent Email Phishing Campaigns – Mitigation and Response Recommendations`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA15-120A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA15-120A",
 | 
			
		||||
		Title: `Securing End-to-End Communications`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA15-119A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA15-119A",
 | 
			
		||||
		Title: `Top 30 Targeted High Risk Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA15-105A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA15-105A",
 | 
			
		||||
		Title: `Simda Botnet`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA15-103A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA15-103A",
 | 
			
		||||
		Title: `DNS Zone Transfer AXFR Requests May Leak Domain Information`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA15-098A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA15-098A",
 | 
			
		||||
		Title: `AAEH`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA15-314A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA15-314A",
 | 
			
		||||
		Title: `Compromised Web Servers and Web Shells - Threat Awareness and Guidance `,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA15-286A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA15-286A",
 | 
			
		||||
		Title: `Dridex P2P Malware`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA15-051A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA15-051A",
 | 
			
		||||
		Title: `Lenovo Superfish Adware Vulnerable to HTTPS Spoofing`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA16-187A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA16-187A",
 | 
			
		||||
		Title: `Symantec and Norton Security Products Contain Critical Vulnerabilities`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA16-144A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA16-144A",
 | 
			
		||||
		Title: `WPAD Name Collision Vulnerability`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA16-132A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA16-132A",
 | 
			
		||||
		Title: `Exploitation of SAP Business Applications`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA16-105A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA16-105A",
 | 
			
		||||
		Title: `Apple Ends Support for QuickTime for Windows; New Vulnerabilities Announced`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA16-091A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA16-091A",
 | 
			
		||||
		Title: `Ransomware and Recent Variants`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA16-336A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA16-336A",
 | 
			
		||||
		Title: `Avalanche (crimeware-as-a-service infrastructure)`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA16-288A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA16-288A",
 | 
			
		||||
		Title: `Heightened DDoS Threat Posed by Mirai and Other Botnets`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA16-250A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA16-250A",
 | 
			
		||||
		Title: `The Increasing Threat to Network Infrastructure Devices and Recommended Mitigations`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA17-117A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA17-117A",
 | 
			
		||||
		Title: `Intrusions Affecting Multiple Victims Across Multiple Sectors`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA17-318B": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA17-318B",
 | 
			
		||||
		Title: `HIDDEN COBRA – North Korean Trojan: Volgmer`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA17-318A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA17-318A",
 | 
			
		||||
		Title: `HIDDEN COBRA – North Korean Remote Administration Tool: FALLCHILL`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA17-181A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA17-181A",
 | 
			
		||||
		Title: `Petya Ransomware `,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA17-132A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA17-132A",
 | 
			
		||||
		Title: `Indicators Associated With WannaCry Ransomware`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA17-075A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA17-075A",
 | 
			
		||||
		Title: `HTTPS Interception Weakens TLS Security`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA17-293A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA17-293A",
 | 
			
		||||
		Title: `Advanced Persistent Threat Activity Targeting Energy and Other Critical Infrastructure Sectors`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA17-164A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA17-164A",
 | 
			
		||||
		Title: `HIDDEN COBRA – North Korea’s DDoS Botnet Infrastructure`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA17-163A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA17-163A",
 | 
			
		||||
		Title: `CrashOverride Malware`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA17-156A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA17-156A",
 | 
			
		||||
		Title: `Reducing the Risk of SNMP Abuse`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA18-141A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA18-141A",
 | 
			
		||||
		Title: `Side-Channel Vulnerability Variants 3a and 4`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA18-086A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA18-086A",
 | 
			
		||||
		Title: `Brute Force Attacks Conducted by Cyber Actors `,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA18-004A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA18-004A",
 | 
			
		||||
		Title: `Meltdown and Spectre Side-Channel Vulnerability Guidance`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA18-331A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA18-331A",
 | 
			
		||||
		Title: `3ve – Major Online Ad Fraud Operation`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/AA18-284A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/AA18-284A",
 | 
			
		||||
		Title: `Publicly Available Tools Seen in Cyber Incidents Worldwide`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA18-276B": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA18-276B",
 | 
			
		||||
		Title: `Advanced Persistent Threat Activity Exploiting Managed Service Providers`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA18-275A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA18-275A",
 | 
			
		||||
		Title: `HIDDEN COBRA – FASTCash Campaign`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA18-201A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA18-201A",
 | 
			
		||||
		Title: `Emotet Malware`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA18-276A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA18-276A",
 | 
			
		||||
		Title: `Using Rigorous Credential Control to Mitigate Trusted Network Exploitation`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA18-149A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA18-149A",
 | 
			
		||||
		Title: `HIDDEN COBRA – Joanap Backdoor Trojan and Brambul Server Message Block Worm`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA18-145A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA18-145A",
 | 
			
		||||
		Title: `Cyber Actors Target Home and Office Routers and Networked Devices Worldwide`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA18-106A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA18-106A",
 | 
			
		||||
		Title: `Russian State-Sponsored Cyber Actors Targeting Network Infrastructure Devices`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
	"https://www.us-cert.gov/ncas/alerts/TA18-074A": {
 | 
			
		||||
		URL:   "https://www.us-cert.gov/ncas/alerts/TA18-074A",
 | 
			
		||||
		Title: `Russian Government Cyber Activity Targeting Energy and Other Critical Infrastructure Sectors`,
 | 
			
		||||
		Team:  "us",
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										534
									
								
								alert/cve_jp.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,534 @@
 | 
			
		||||
package alert
 | 
			
		||||
 | 
			
		||||
// CveDictJa has CVE-ID key which included JPCERT alerts
 | 
			
		||||
var CveDictJa = map[string][]string{
 | 
			
		||||
	"CVE-2006-0003":    {"https://www.jpcert.or.jp/at/2007/at070016.html"},
 | 
			
		||||
	"CVE-2006-0005":    {"https://www.jpcert.or.jp/at/2007/at070016.html"},
 | 
			
		||||
	"CVE-2006-1173":    {"https://www.jpcert.or.jp/at/2006/at060008.html"},
 | 
			
		||||
	"CVE-2006-3014":    {"https://www.jpcert.or.jp/at/2006/at060009.html"},
 | 
			
		||||
	"CVE-2006-3059":    {"https://www.jpcert.or.jp/at/2006/at060009.html"},
 | 
			
		||||
	"CVE-2006-3086":    {"https://www.jpcert.or.jp/at/2006/at060009.html"},
 | 
			
		||||
	"CVE-2006-3643":    {"https://www.jpcert.or.jp/at/2007/at070016.html"},
 | 
			
		||||
	"CVE-2006-3730":    {"https://www.jpcert.or.jp/at/2007/at070016.html"},
 | 
			
		||||
	"CVE-2006-3877":    {"https://www.jpcert.or.jp/at/2007/at070005.html"},
 | 
			
		||||
	"CVE-2006-5198":    {"https://www.jpcert.or.jp/at/2007/at070016.html"},
 | 
			
		||||
	"CVE-2006-5745":    {"https://www.jpcert.or.jp/at/2007/at070016.html"},
 | 
			
		||||
	"CVE-2007-0015":    {"https://www.jpcert.or.jp/at/2007/at070016.html"},
 | 
			
		||||
	"CVE-2007-0038":    {"https://www.jpcert.or.jp/at/2007/at070016.html"},
 | 
			
		||||
	"CVE-2008-4609":    {"https://www.jpcert.or.jp/at/2009/at090019.html"},
 | 
			
		||||
	"CVE-2010-0886":    {"https://www.jpcert.or.jp/at/2010/at100010.html"},
 | 
			
		||||
	"CVE-2010-0887":    {"https://www.jpcert.or.jp/at/2010/at100010.html"},
 | 
			
		||||
	"CVE-2011-1910":    {"https://www.jpcert.or.jp/at/2011/at110014.html"},
 | 
			
		||||
	"CVE-2011-2444":    {"https://www.jpcert.or.jp/at/2011/at110026.html"},
 | 
			
		||||
	"CVE-2011-2462":    {"https://www.jpcert.or.jp/at/2011/at110034.html"},
 | 
			
		||||
	"CVE-2011-2465":    {"https://www.jpcert.or.jp/at/2011/at110019.html"},
 | 
			
		||||
	"CVE-2011-3192":    {"https://www.jpcert.or.jp/at/2011/at110023.html"},
 | 
			
		||||
	"CVE-2011-3348":    {"https://www.jpcert.or.jp/at/2011/at110023.html"},
 | 
			
		||||
	"CVE-2011-3544":    {"https://www.jpcert.or.jp/at/2011/at110032.html"},
 | 
			
		||||
	"CVE-2011-4313":    {"https://www.jpcert.or.jp/at/2011/at110031.html"},
 | 
			
		||||
	"CVE-2012-0002":    {"https://www.jpcert.or.jp/at/2012/at120009.html"},
 | 
			
		||||
	"CVE-2012-0507":    {"https://www.jpcert.or.jp/at/2012/at120010.html"},
 | 
			
		||||
	"CVE-2012-0767":    {"https://www.jpcert.or.jp/at/2012/at120006.html"},
 | 
			
		||||
	"CVE-2012-0779":    {"https://www.jpcert.or.jp/at/2012/at120014.html"},
 | 
			
		||||
	"CVE-2012-0830":    {"https://www.jpcert.or.jp/at/2012/at120004.html"},
 | 
			
		||||
	"CVE-2012-2311":    {"https://www.jpcert.or.jp/at/2012/at120016.html"},
 | 
			
		||||
	"CVE-2012-4244":    {"https://www.jpcert.or.jp/at/2012/at120029.html"},
 | 
			
		||||
	"CVE-2012-4681":    {"https://www.jpcert.or.jp/at/2012/at120028.html"},
 | 
			
		||||
	"CVE-2012-4969":    {"https://www.jpcert.or.jp/at/2012/at120030.html"},
 | 
			
		||||
	"CVE-2012-5166":    {"https://www.jpcert.or.jp/at/2012/at120033.html"},
 | 
			
		||||
	"CVE-2013-0422":    {"https://www.jpcert.or.jp/at/2013/at130004.html"},
 | 
			
		||||
	"CVE-2013-1493":    {"https://www.jpcert.or.jp/at/2013/at130014.html"},
 | 
			
		||||
	"CVE-2013-2266":    {"https://www.jpcert.or.jp/at/2013/at130017.html"},
 | 
			
		||||
	"CVE-2013-2494":    {"https://www.jpcert.or.jp/at/2013/at130017.html"},
 | 
			
		||||
	"CVE-2013-3893":    {"https://www.jpcert.or.jp/at/2013/at130040.html", "https://www.jpcert.or.jp/at/2013/at130041.html"},
 | 
			
		||||
	"CVE-2013-3906":    {"https://www.jpcert.or.jp/at/2013/at130044.html"},
 | 
			
		||||
	"CVE-2013-3918":    {"https://www.jpcert.or.jp/at/2013/at130045.html"},
 | 
			
		||||
	"CVE-2013-3919":    {"https://www.jpcert.or.jp/at/2013/at130026.html"},
 | 
			
		||||
	"CVE-2013-4854":    {"https://www.jpcert.or.jp/at/2013/at130034.html"},
 | 
			
		||||
	"CVE-2014-0050":    {"https://www.jpcert.or.jp/at/2014/at140007.html"},
 | 
			
		||||
	"CVE-2014-0160":    {"https://www.jpcert.or.jp/at/2014/at140013.html"},
 | 
			
		||||
	"CVE-2014-0322":    {"https://www.jpcert.or.jp/at/2014/at140009.html"},
 | 
			
		||||
	"CVE-2014-1776":    {"https://www.jpcert.or.jp/at/2014/at140018.html", "https://www.jpcert.or.jp/at/2014/at140020.html"},
 | 
			
		||||
	"CVE-2014-3383":    {"https://www.jpcert.or.jp/at/2015/at150021.html"},
 | 
			
		||||
	"CVE-2014-3859":    {"https://www.jpcert.or.jp/at/2014/at140027.html"},
 | 
			
		||||
	"CVE-2014-4114":    {"https://www.jpcert.or.jp/at/2014/at140039.html"},
 | 
			
		||||
	"CVE-2014-6271":    {"https://www.jpcert.or.jp/at/2014/at140037.html", "https://www.jpcert.or.jp/at/2014/at140038.html"},
 | 
			
		||||
	"CVE-2014-6277":    {"https://www.jpcert.or.jp/at/2014/at140037.html"},
 | 
			
		||||
	"CVE-2014-6278":    {"https://www.jpcert.or.jp/at/2014/at140037.html"},
 | 
			
		||||
	"CVE-2014-6324":    {"https://www.jpcert.or.jp/at/2014/at140048.html"},
 | 
			
		||||
	"CVE-2014-6332":    {"https://www.jpcert.or.jp/at/2015/at150015.html"},
 | 
			
		||||
	"CVE-2014-6352":    {"https://www.jpcert.or.jp/at/2014/at140043.html"},
 | 
			
		||||
	"CVE-2014-7169":    {"https://www.jpcert.or.jp/at/2014/at140037.html"},
 | 
			
		||||
	"CVE-2014-7186":    {"https://www.jpcert.or.jp/at/2014/at140037.html"},
 | 
			
		||||
	"CVE-2014-7187":    {"https://www.jpcert.or.jp/at/2014/at140037.html"},
 | 
			
		||||
	"CVE-2014-8361":    {"https://www.jpcert.or.jp/at/2017/at170049.html"},
 | 
			
		||||
	"CVE-2014-8500":    {"https://www.jpcert.or.jp/at/2014/at140050.html"},
 | 
			
		||||
	"CVE-2014-9163":    {"https://www.jpcert.or.jp/at/2014/at140052.html"},
 | 
			
		||||
	"CVE-2015-0313":    {"https://www.jpcert.or.jp/at/2015/at150015.html"},
 | 
			
		||||
	"CVE-2015-1769":    {"https://www.jpcert.or.jp/at/2015/at150028.html"},
 | 
			
		||||
	"CVE-2015-5119":    {"https://www.jpcert.or.jp/at/2015/at150019.html"},
 | 
			
		||||
	"CVE-2015-5122":    {"https://www.jpcert.or.jp/at/2015/at150020.html"},
 | 
			
		||||
	"CVE-2015-5123":    {"https://www.jpcert.or.jp/at/2015/at150020.html"},
 | 
			
		||||
	"CVE-2015-5477":    {"https://www.jpcert.or.jp/at/2015/at150027.html"},
 | 
			
		||||
	"CVE-2015-5986":    {"https://www.jpcert.or.jp/at/2015/at150031.html"},
 | 
			
		||||
	"CVE-2015-6835":    {"https://www.jpcert.or.jp/at/2016/at160036.html"},
 | 
			
		||||
	"CVE-2015-7547":    {"https://www.jpcert.or.jp/at/2016/at160009.html"},
 | 
			
		||||
	"CVE-2015-7645":    {"https://www.jpcert.or.jp/at/2015/at150036.html", "https://www.jpcert.or.jp/at/2015/at150037.html"},
 | 
			
		||||
	"CVE-2015-8000":    {"https://www.jpcert.or.jp/at/2015/at150043.html"},
 | 
			
		||||
	"CVE-2015-8461":    {"https://www.jpcert.or.jp/at/2015/at150043.html"},
 | 
			
		||||
	"CVE-2015-8562":    {"https://www.jpcert.or.jp/at/2016/at160036.html"},
 | 
			
		||||
	"CVE-2015-8651":    {"https://www.jpcert.or.jp/at/2016/at160001.html"},
 | 
			
		||||
	"CVE-2015-8704":    {"https://www.jpcert.or.jp/at/2016/at160006.html"},
 | 
			
		||||
	"CVE-2015-8705":    {"https://www.jpcert.or.jp/at/2016/at160006.html"},
 | 
			
		||||
	"CVE-2016-0189":    {"https://www.jpcert.or.jp/at/2016/at160022.html"},
 | 
			
		||||
	"CVE-2016-0636":    {"https://www.jpcert.or.jp/at/2016/at160015.html"},
 | 
			
		||||
	"CVE-2016-0800":    {"https://www.jpcert.or.jp/at/2016/at160010.html"},
 | 
			
		||||
	"CVE-2016-1000109": {"https://www.jpcert.or.jp/at/2016/at160031.html"},
 | 
			
		||||
	"CVE-2016-1000110": {"https://www.jpcert.or.jp/at/2016/at160031.html"},
 | 
			
		||||
	"CVE-2016-1010":    {"https://www.jpcert.or.jp/at/2016/at160014.html"},
 | 
			
		||||
	"CVE-2016-1019":    {"https://www.jpcert.or.jp/at/2016/at160016.html"},
 | 
			
		||||
	"CVE-2016-1204":    {"https://www.jpcert.or.jp/at/2016/at160019.html"},
 | 
			
		||||
	"CVE-2016-1286":    {"https://www.jpcert.or.jp/at/2016/at160013.html", "https://www.jpcert.or.jp/at/2016/at160037.html"},
 | 
			
		||||
	"CVE-2016-2776":    {"https://www.jpcert.or.jp/at/2016/at160037.html"},
 | 
			
		||||
	"CVE-2016-3081":    {"https://www.jpcert.or.jp/at/2016/at160020.html"},
 | 
			
		||||
	"CVE-2016-3227":    {"https://www.jpcert.or.jp/at/2016/at160025.html"},
 | 
			
		||||
	"CVE-2016-3714":    {"https://www.jpcert.or.jp/at/2016/at160021.html"},
 | 
			
		||||
	"CVE-2016-3715":    {"https://www.jpcert.or.jp/at/2016/at160021.html"},
 | 
			
		||||
	"CVE-2016-3716":    {"https://www.jpcert.or.jp/at/2016/at160021.html"},
 | 
			
		||||
	"CVE-2016-3717":    {"https://www.jpcert.or.jp/at/2016/at160021.html"},
 | 
			
		||||
	"CVE-2016-3718":    {"https://www.jpcert.or.jp/at/2016/at160021.html"},
 | 
			
		||||
	"CVE-2016-4117":    {"https://www.jpcert.or.jp/at/2016/at160024.html"},
 | 
			
		||||
	"CVE-2016-4171":    {"https://www.jpcert.or.jp/at/2016/at160026.html"},
 | 
			
		||||
	"CVE-2016-4438":    {"https://www.jpcert.or.jp/at/2016/at160027.html"},
 | 
			
		||||
	"CVE-2016-5385":    {"https://www.jpcert.or.jp/at/2016/at160031.html"},
 | 
			
		||||
	"CVE-2016-5386":    {"https://www.jpcert.or.jp/at/2016/at160031.html"},
 | 
			
		||||
	"CVE-2016-5387":    {"https://www.jpcert.or.jp/at/2016/at160031.html"},
 | 
			
		||||
	"CVE-2016-5388":    {"https://www.jpcert.or.jp/at/2016/at160031.html"},
 | 
			
		||||
	"CVE-2016-6307":    {"https://www.jpcert.or.jp/at/2016/at160038.html"},
 | 
			
		||||
	"CVE-2016-6309":    {"https://www.jpcert.or.jp/at/2016/at160038.html"},
 | 
			
		||||
	"CVE-2016-7189":    {"https://www.jpcert.or.jp/at/2016/at160039.html"},
 | 
			
		||||
	"CVE-2016-7836":    {"https://www.jpcert.or.jp/at/2016/at160051.html", "https://www.jpcert.or.jp/at/2017/at170023.html"},
 | 
			
		||||
	"CVE-2016-7855":    {"https://www.jpcert.or.jp/at/2016/at160039.html", "https://www.jpcert.or.jp/at/2016/at160043.html"},
 | 
			
		||||
	"CVE-2016-7892":    {"https://www.jpcert.or.jp/at/2016/at160048.html", "https://www.jpcert.or.jp/at/2016/at160049.html"},
 | 
			
		||||
	"CVE-2016-8864":    {"https://www.jpcert.or.jp/at/2016/at160044.html"},
 | 
			
		||||
	"CVE-2016-9131":    {"https://www.jpcert.or.jp/at/2017/at170004.html"},
 | 
			
		||||
	"CVE-2016-9147":    {"https://www.jpcert.or.jp/at/2017/at170004.html"},
 | 
			
		||||
	"CVE-2016-9444":    {"https://www.jpcert.or.jp/at/2017/at170004.html"},
 | 
			
		||||
	"CVE-2016-9778":    {"https://www.jpcert.or.jp/at/2017/at170004.html"},
 | 
			
		||||
	"CVE-2017-0093":    {"https://www.jpcert.or.jp/at/2017/at170015.html"},
 | 
			
		||||
	"CVE-2017-0106":    {"https://www.jpcert.or.jp/at/2017/at170015.html"},
 | 
			
		||||
	"CVE-2017-0145":    {"https://www.jpcert.or.jp/at/2017/at170020.html"},
 | 
			
		||||
	"CVE-2017-0158":    {"https://www.jpcert.or.jp/at/2017/at170015.html"},
 | 
			
		||||
	"CVE-2017-0160":    {"https://www.jpcert.or.jp/at/2017/at170015.html"},
 | 
			
		||||
	"CVE-2017-0161":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-0162":    {"https://www.jpcert.or.jp/at/2017/at170015.html"},
 | 
			
		||||
	"CVE-2017-0163":    {"https://www.jpcert.or.jp/at/2017/at170015.html"},
 | 
			
		||||
	"CVE-2017-0180":    {"https://www.jpcert.or.jp/at/2017/at170015.html"},
 | 
			
		||||
	"CVE-2017-0181":    {"https://www.jpcert.or.jp/at/2017/at170015.html"},
 | 
			
		||||
	"CVE-2017-0199":    {"https://www.jpcert.or.jp/at/2017/at170015.html"},
 | 
			
		||||
	"CVE-2017-0200":    {"https://www.jpcert.or.jp/at/2017/at170015.html"},
 | 
			
		||||
	"CVE-2017-0201":    {"https://www.jpcert.or.jp/at/2017/at170015.html"},
 | 
			
		||||
	"CVE-2017-0202":    {"https://www.jpcert.or.jp/at/2017/at170015.html"},
 | 
			
		||||
	"CVE-2017-0205":    {"https://www.jpcert.or.jp/at/2017/at170015.html"},
 | 
			
		||||
	"CVE-2017-0210":    {"https://www.jpcert.or.jp/at/2017/at170015.html"},
 | 
			
		||||
	"CVE-2017-0221":    {"https://www.jpcert.or.jp/at/2017/at170019.html"},
 | 
			
		||||
	"CVE-2017-0222":    {"https://www.jpcert.or.jp/at/2017/at170019.html"},
 | 
			
		||||
	"CVE-2017-0224":    {"https://www.jpcert.or.jp/at/2017/at170019.html"},
 | 
			
		||||
	"CVE-2017-0227":    {"https://www.jpcert.or.jp/at/2017/at170019.html"},
 | 
			
		||||
	"CVE-2017-0228":    {"https://www.jpcert.or.jp/at/2017/at170019.html"},
 | 
			
		||||
	"CVE-2017-0229":    {"https://www.jpcert.or.jp/at/2017/at170019.html"},
 | 
			
		||||
	"CVE-2017-0235":    {"https://www.jpcert.or.jp/at/2017/at170019.html"},
 | 
			
		||||
	"CVE-2017-0236":    {"https://www.jpcert.or.jp/at/2017/at170019.html"},
 | 
			
		||||
	"CVE-2017-0240":    {"https://www.jpcert.or.jp/at/2017/at170019.html"},
 | 
			
		||||
	"CVE-2017-0250":    {"https://www.jpcert.or.jp/at/2017/at170032.html"},
 | 
			
		||||
	"CVE-2017-0261":    {"https://www.jpcert.or.jp/at/2017/at170019.html"},
 | 
			
		||||
	"CVE-2017-0263":    {"https://www.jpcert.or.jp/at/2017/at170019.html"},
 | 
			
		||||
	"CVE-2017-0266":    {"https://www.jpcert.or.jp/at/2017/at170019.html"},
 | 
			
		||||
	"CVE-2017-0272":    {"https://www.jpcert.or.jp/at/2017/at170019.html"},
 | 
			
		||||
	"CVE-2017-0277":    {"https://www.jpcert.or.jp/at/2017/at170019.html"},
 | 
			
		||||
	"CVE-2017-0278":    {"https://www.jpcert.or.jp/at/2017/at170019.html"},
 | 
			
		||||
	"CVE-2017-0279":    {"https://www.jpcert.or.jp/at/2017/at170019.html"},
 | 
			
		||||
	"CVE-2017-0283":    {"https://www.jpcert.or.jp/at/2017/at170022.html"},
 | 
			
		||||
	"CVE-2017-0290":    {"https://www.jpcert.or.jp/at/2017/at170019.html"},
 | 
			
		||||
	"CVE-2017-0291":    {"https://www.jpcert.or.jp/at/2017/at170022.html"},
 | 
			
		||||
	"CVE-2017-0292":    {"https://www.jpcert.or.jp/at/2017/at170022.html"},
 | 
			
		||||
	"CVE-2017-0293":    {"https://www.jpcert.or.jp/at/2017/at170032.html"},
 | 
			
		||||
	"CVE-2017-0294":    {"https://www.jpcert.or.jp/at/2017/at170022.html"},
 | 
			
		||||
	"CVE-2017-0781":    {"https://www.jpcert.or.jp/at/2017/at170037.html"},
 | 
			
		||||
	"CVE-2017-0782":    {"https://www.jpcert.or.jp/at/2017/at170037.html"},
 | 
			
		||||
	"CVE-2017-0783":    {"https://www.jpcert.or.jp/at/2017/at170037.html"},
 | 
			
		||||
	"CVE-2017-0785":    {"https://www.jpcert.or.jp/at/2017/at170037.html"},
 | 
			
		||||
	"CVE-2017-1000250": {"https://www.jpcert.or.jp/at/2017/at170037.html"},
 | 
			
		||||
	"CVE-2017-1000251": {"https://www.jpcert.or.jp/at/2017/at170037.html"},
 | 
			
		||||
	"CVE-2017-10271":   {"https://www.jpcert.or.jp/at/2018/at180004.html"},
 | 
			
		||||
	"CVE-2017-10845":   {"https://www.jpcert.or.jp/at/2017/at170034.html"},
 | 
			
		||||
	"CVE-2017-10846":   {"https://www.jpcert.or.jp/at/2017/at170034.html"},
 | 
			
		||||
	"CVE-2017-11223":   {"https://www.jpcert.or.jp/at/2017/at170031.html"},
 | 
			
		||||
	"CVE-2017-11292":   {"https://www.jpcert.or.jp/at/2017/at170040.html"},
 | 
			
		||||
	"CVE-2017-11762":   {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-11763":   {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-11764":   {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-11766":   {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-11771":   {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-11779":   {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-11792":   {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-11793":   {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-11796":   {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-11798":   {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-11799":   {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-11800":   {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-11802":   {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-11804":   {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-11805":   {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-11806":   {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-11807":   {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-11808":   {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-11809":   {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-11810":   {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-11811":   {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-11812":   {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-11813":   {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-11819":   {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-11821":   {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-11822":   {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-11826":   {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-11836":   {"https://www.jpcert.or.jp/at/2017/at170044.html"},
 | 
			
		||||
	"CVE-2017-11837":   {"https://www.jpcert.or.jp/at/2017/at170044.html"},
 | 
			
		||||
	"CVE-2017-11838":   {"https://www.jpcert.or.jp/at/2017/at170044.html"},
 | 
			
		||||
	"CVE-2017-11839":   {"https://www.jpcert.or.jp/at/2017/at170044.html"},
 | 
			
		||||
	"CVE-2017-11840":   {"https://www.jpcert.or.jp/at/2017/at170044.html"},
 | 
			
		||||
	"CVE-2017-11841":   {"https://www.jpcert.or.jp/at/2017/at170044.html"},
 | 
			
		||||
	"CVE-2017-11843":   {"https://www.jpcert.or.jp/at/2017/at170044.html"},
 | 
			
		||||
	"CVE-2017-11845":   {"https://www.jpcert.or.jp/at/2017/at170044.html"},
 | 
			
		||||
	"CVE-2017-11846":   {"https://www.jpcert.or.jp/at/2017/at170044.html"},
 | 
			
		||||
	"CVE-2017-11855":   {"https://www.jpcert.or.jp/at/2017/at170044.html"},
 | 
			
		||||
	"CVE-2017-11856":   {"https://www.jpcert.or.jp/at/2017/at170044.html"},
 | 
			
		||||
	"CVE-2017-11858":   {"https://www.jpcert.or.jp/at/2017/at170044.html"},
 | 
			
		||||
	"CVE-2017-11861":   {"https://www.jpcert.or.jp/at/2017/at170044.html"},
 | 
			
		||||
	"CVE-2017-11862":   {"https://www.jpcert.or.jp/at/2017/at170044.html"},
 | 
			
		||||
	"CVE-2017-11866":   {"https://www.jpcert.or.jp/at/2017/at170044.html"},
 | 
			
		||||
	"CVE-2017-11869":   {"https://www.jpcert.or.jp/at/2017/at170044.html"},
 | 
			
		||||
	"CVE-2017-11870":   {"https://www.jpcert.or.jp/at/2017/at170044.html"},
 | 
			
		||||
	"CVE-2017-11871":   {"https://www.jpcert.or.jp/at/2017/at170044.html"},
 | 
			
		||||
	"CVE-2017-11873":   {"https://www.jpcert.or.jp/at/2017/at170044.html"},
 | 
			
		||||
	"CVE-2017-11882":   {"https://www.jpcert.or.jp/at/2017/at170044.html"},
 | 
			
		||||
	"CVE-2017-11886":   {"https://www.jpcert.or.jp/at/2017/at170048.html"},
 | 
			
		||||
	"CVE-2017-11888":   {"https://www.jpcert.or.jp/at/2017/at170048.html"},
 | 
			
		||||
	"CVE-2017-11889":   {"https://www.jpcert.or.jp/at/2017/at170048.html"},
 | 
			
		||||
	"CVE-2017-11890":   {"https://www.jpcert.or.jp/at/2017/at170048.html"},
 | 
			
		||||
	"CVE-2017-11893":   {"https://www.jpcert.or.jp/at/2017/at170048.html"},
 | 
			
		||||
	"CVE-2017-11894":   {"https://www.jpcert.or.jp/at/2017/at170048.html"},
 | 
			
		||||
	"CVE-2017-11895":   {"https://www.jpcert.or.jp/at/2017/at170048.html"},
 | 
			
		||||
	"CVE-2017-11901":   {"https://www.jpcert.or.jp/at/2017/at170048.html"},
 | 
			
		||||
	"CVE-2017-11903":   {"https://www.jpcert.or.jp/at/2017/at170048.html"},
 | 
			
		||||
	"CVE-2017-11905":   {"https://www.jpcert.or.jp/at/2017/at170048.html"},
 | 
			
		||||
	"CVE-2017-11907":   {"https://www.jpcert.or.jp/at/2017/at170048.html"},
 | 
			
		||||
	"CVE-2017-11908":   {"https://www.jpcert.or.jp/at/2017/at170048.html"},
 | 
			
		||||
	"CVE-2017-11909":   {"https://www.jpcert.or.jp/at/2017/at170048.html"},
 | 
			
		||||
	"CVE-2017-11910":   {"https://www.jpcert.or.jp/at/2017/at170048.html"},
 | 
			
		||||
	"CVE-2017-11911":   {"https://www.jpcert.or.jp/at/2017/at170048.html"},
 | 
			
		||||
	"CVE-2017-11912":   {"https://www.jpcert.or.jp/at/2017/at170048.html"},
 | 
			
		||||
	"CVE-2017-11914":   {"https://www.jpcert.or.jp/at/2017/at170048.html"},
 | 
			
		||||
	"CVE-2017-11918":   {"https://www.jpcert.or.jp/at/2017/at170048.html"},
 | 
			
		||||
	"CVE-2017-11930":   {"https://www.jpcert.or.jp/at/2017/at170048.html"},
 | 
			
		||||
	"CVE-2017-11937":   {"https://www.jpcert.or.jp/at/2017/at170046.html"},
 | 
			
		||||
	"CVE-2017-12615":   {"https://www.jpcert.or.jp/at/2017/at170038.html"},
 | 
			
		||||
	"CVE-2017-12616":   {"https://www.jpcert.or.jp/at/2017/at170038.html"},
 | 
			
		||||
	"CVE-2017-12617":   {"https://www.jpcert.or.jp/at/2017/at170038.html"},
 | 
			
		||||
	"CVE-2017-13872":   {"https://www.jpcert.or.jp/at/2017/at170045.html"},
 | 
			
		||||
	"CVE-2017-14315":   {"https://www.jpcert.or.jp/at/2017/at170037.html"},
 | 
			
		||||
	"CVE-2017-3135":    {"https://www.jpcert.or.jp/at/2017/at170007.html"},
 | 
			
		||||
	"CVE-2017-3136":    {"https://www.jpcert.or.jp/at/2017/at170016.html"},
 | 
			
		||||
	"CVE-2017-3137":    {"https://www.jpcert.or.jp/at/2017/at170016.html"},
 | 
			
		||||
	"CVE-2017-3138":    {"https://www.jpcert.or.jp/at/2017/at170016.html"},
 | 
			
		||||
	"CVE-2017-3142":    {"https://www.jpcert.or.jp/at/2017/at170024.html"},
 | 
			
		||||
	"CVE-2017-3143":    {"https://www.jpcert.or.jp/at/2017/at170024.html"},
 | 
			
		||||
	"CVE-2017-3145":    {"https://www.jpcert.or.jp/at/2018/at180005.html"},
 | 
			
		||||
	"CVE-2017-5638":    {"https://www.jpcert.or.jp/at/2017/at170009.html"},
 | 
			
		||||
	"CVE-2017-6753":    {"https://www.jpcert.or.jp/at/2017/at170028.html"},
 | 
			
		||||
	"CVE-2017-8463":    {"https://www.jpcert.or.jp/at/2017/at170027.html"},
 | 
			
		||||
	"CVE-2017-8464":    {"https://www.jpcert.or.jp/at/2017/at170022.html"},
 | 
			
		||||
	"CVE-2017-8496":    {"https://www.jpcert.or.jp/at/2017/at170022.html"},
 | 
			
		||||
	"CVE-2017-8497":    {"https://www.jpcert.or.jp/at/2017/at170022.html"},
 | 
			
		||||
	"CVE-2017-8499":    {"https://www.jpcert.or.jp/at/2017/at170022.html"},
 | 
			
		||||
	"CVE-2017-8517":    {"https://www.jpcert.or.jp/at/2017/at170022.html"},
 | 
			
		||||
	"CVE-2017-8520":    {"https://www.jpcert.or.jp/at/2017/at170022.html"},
 | 
			
		||||
	"CVE-2017-8522":    {"https://www.jpcert.or.jp/at/2017/at170022.html"},
 | 
			
		||||
	"CVE-2017-8524":    {"https://www.jpcert.or.jp/at/2017/at170022.html"},
 | 
			
		||||
	"CVE-2017-8527":    {"https://www.jpcert.or.jp/at/2017/at170022.html"},
 | 
			
		||||
	"CVE-2017-8528":    {"https://www.jpcert.or.jp/at/2017/at170022.html"},
 | 
			
		||||
	"CVE-2017-8543":    {"https://www.jpcert.or.jp/at/2017/at170022.html"},
 | 
			
		||||
	"CVE-2017-8548":    {"https://www.jpcert.or.jp/at/2017/at170022.html"},
 | 
			
		||||
	"CVE-2017-8549":    {"https://www.jpcert.or.jp/at/2017/at170022.html"},
 | 
			
		||||
	"CVE-2017-8584":    {"https://www.jpcert.or.jp/at/2017/at170027.html"},
 | 
			
		||||
	"CVE-2017-8589":    {"https://www.jpcert.or.jp/at/2017/at170027.html"},
 | 
			
		||||
	"CVE-2017-8591":    {"https://www.jpcert.or.jp/at/2017/at170032.html"},
 | 
			
		||||
	"CVE-2017-8594":    {"https://www.jpcert.or.jp/at/2017/at170027.html"},
 | 
			
		||||
	"CVE-2017-8595":    {"https://www.jpcert.or.jp/at/2017/at170027.html"},
 | 
			
		||||
	"CVE-2017-8596":    {"https://www.jpcert.or.jp/at/2017/at170027.html"},
 | 
			
		||||
	"CVE-2017-8598":    {"https://www.jpcert.or.jp/at/2017/at170027.html"},
 | 
			
		||||
	"CVE-2017-8601":    {"https://www.jpcert.or.jp/at/2017/at170027.html"},
 | 
			
		||||
	"CVE-2017-8603":    {"https://www.jpcert.or.jp/at/2017/at170027.html"},
 | 
			
		||||
	"CVE-2017-8604":    {"https://www.jpcert.or.jp/at/2017/at170027.html"},
 | 
			
		||||
	"CVE-2017-8605":    {"https://www.jpcert.or.jp/at/2017/at170027.html"},
 | 
			
		||||
	"CVE-2017-8606":    {"https://www.jpcert.or.jp/at/2017/at170027.html"},
 | 
			
		||||
	"CVE-2017-8607":    {"https://www.jpcert.or.jp/at/2017/at170027.html"},
 | 
			
		||||
	"CVE-2017-8608":    {"https://www.jpcert.or.jp/at/2017/at170027.html"},
 | 
			
		||||
	"CVE-2017-8609":    {"https://www.jpcert.or.jp/at/2017/at170027.html"},
 | 
			
		||||
	"CVE-2017-8610":    {"https://www.jpcert.or.jp/at/2017/at170027.html"},
 | 
			
		||||
	"CVE-2017-8617":    {"https://www.jpcert.or.jp/at/2017/at170027.html"},
 | 
			
		||||
	"CVE-2017-8618":    {"https://www.jpcert.or.jp/at/2017/at170027.html"},
 | 
			
		||||
	"CVE-2017-8619":    {"https://www.jpcert.or.jp/at/2017/at170027.html"},
 | 
			
		||||
	"CVE-2017-8620":    {"https://www.jpcert.or.jp/at/2017/at170032.html"},
 | 
			
		||||
	"CVE-2017-8622":    {"https://www.jpcert.or.jp/at/2017/at170032.html"},
 | 
			
		||||
	"CVE-2017-8628":    {"https://www.jpcert.or.jp/at/2017/at170037.html"},
 | 
			
		||||
	"CVE-2017-8634":    {"https://www.jpcert.or.jp/at/2017/at170032.html"},
 | 
			
		||||
	"CVE-2017-8635":    {"https://www.jpcert.or.jp/at/2017/at170032.html"},
 | 
			
		||||
	"CVE-2017-8636":    {"https://www.jpcert.or.jp/at/2017/at170032.html"},
 | 
			
		||||
	"CVE-2017-8638":    {"https://www.jpcert.or.jp/at/2017/at170032.html"},
 | 
			
		||||
	"CVE-2017-8639":    {"https://www.jpcert.or.jp/at/2017/at170032.html"},
 | 
			
		||||
	"CVE-2017-8640":    {"https://www.jpcert.or.jp/at/2017/at170032.html"},
 | 
			
		||||
	"CVE-2017-8641":    {"https://www.jpcert.or.jp/at/2017/at170032.html"},
 | 
			
		||||
	"CVE-2017-8645":    {"https://www.jpcert.or.jp/at/2017/at170032.html"},
 | 
			
		||||
	"CVE-2017-8646":    {"https://www.jpcert.or.jp/at/2017/at170032.html"},
 | 
			
		||||
	"CVE-2017-8647":    {"https://www.jpcert.or.jp/at/2017/at170032.html"},
 | 
			
		||||
	"CVE-2017-8649":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-8653":    {"https://www.jpcert.or.jp/at/2017/at170032.html"},
 | 
			
		||||
	"CVE-2017-8655":    {"https://www.jpcert.or.jp/at/2017/at170032.html"},
 | 
			
		||||
	"CVE-2017-8656":    {"https://www.jpcert.or.jp/at/2017/at170032.html"},
 | 
			
		||||
	"CVE-2017-8657":    {"https://www.jpcert.or.jp/at/2017/at170032.html"},
 | 
			
		||||
	"CVE-2017-8660":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-8661":    {"https://www.jpcert.or.jp/at/2017/at170032.html"},
 | 
			
		||||
	"CVE-2017-8669":    {"https://www.jpcert.or.jp/at/2017/at170032.html"},
 | 
			
		||||
	"CVE-2017-8670":    {"https://www.jpcert.or.jp/at/2017/at170032.html"},
 | 
			
		||||
	"CVE-2017-8671":    {"https://www.jpcert.or.jp/at/2017/at170032.html"},
 | 
			
		||||
	"CVE-2017-8672":    {"https://www.jpcert.or.jp/at/2017/at170032.html"},
 | 
			
		||||
	"CVE-2017-8674":    {"https://www.jpcert.or.jp/at/2017/at170032.html"},
 | 
			
		||||
	"CVE-2017-8676":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-8682":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-8686":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-8696":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-8727":    {"https://www.jpcert.or.jp/at/2017/at170039.html"},
 | 
			
		||||
	"CVE-2017-8728":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-8729":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-8731":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-8734":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-8737":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-8738":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-8740":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-8741":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-8747":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-8748":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-8749":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-8750":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-8751":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-8752":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-8753":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-8755":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-8756":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-8757":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-8759":    {"https://www.jpcert.or.jp/at/2017/at170036.html"},
 | 
			
		||||
	"CVE-2017-9791":    {"https://www.jpcert.or.jp/at/2017/at170025.html"},
 | 
			
		||||
	"CVE-2017-9805":    {"https://www.jpcert.or.jp/at/2017/at170033.html"},
 | 
			
		||||
	"CVE-2018-0171":    {"https://www.jpcert.or.jp/at/2018/at180013.html"},
 | 
			
		||||
	"CVE-2018-0758":    {"https://www.jpcert.or.jp/at/2018/at180002.html"},
 | 
			
		||||
	"CVE-2018-0762":    {"https://www.jpcert.or.jp/at/2018/at180002.html"},
 | 
			
		||||
	"CVE-2018-0763":    {"https://www.jpcert.or.jp/at/2018/at180008.html"},
 | 
			
		||||
	"CVE-2018-0767":    {"https://www.jpcert.or.jp/at/2018/at180002.html"},
 | 
			
		||||
	"CVE-2018-0769":    {"https://www.jpcert.or.jp/at/2018/at180002.html"},
 | 
			
		||||
	"CVE-2018-0770":    {"https://www.jpcert.or.jp/at/2018/at180002.html"},
 | 
			
		||||
	"CVE-2018-0772":    {"https://www.jpcert.or.jp/at/2018/at180002.html"},
 | 
			
		||||
	"CVE-2018-0773":    {"https://www.jpcert.or.jp/at/2018/at180002.html"},
 | 
			
		||||
	"CVE-2018-0774":    {"https://www.jpcert.or.jp/at/2018/at180002.html"},
 | 
			
		||||
	"CVE-2018-0775":    {"https://www.jpcert.or.jp/at/2018/at180002.html"},
 | 
			
		||||
	"CVE-2018-0776":    {"https://www.jpcert.or.jp/at/2018/at180002.html"},
 | 
			
		||||
	"CVE-2018-0777":    {"https://www.jpcert.or.jp/at/2018/at180002.html"},
 | 
			
		||||
	"CVE-2018-0778":    {"https://www.jpcert.or.jp/at/2018/at180002.html"},
 | 
			
		||||
	"CVE-2018-0780":    {"https://www.jpcert.or.jp/at/2018/at180002.html"},
 | 
			
		||||
	"CVE-2018-0781":    {"https://www.jpcert.or.jp/at/2018/at180002.html"},
 | 
			
		||||
	"CVE-2018-0797":    {"https://www.jpcert.or.jp/at/2018/at180002.html"},
 | 
			
		||||
	"CVE-2018-0800":    {"https://www.jpcert.or.jp/at/2018/at180002.html"},
 | 
			
		||||
	"CVE-2018-0802":    {"https://www.jpcert.or.jp/at/2018/at180002.html"},
 | 
			
		||||
	"CVE-2018-0825":    {"https://www.jpcert.or.jp/at/2018/at180008.html"},
 | 
			
		||||
	"CVE-2018-0834":    {"https://www.jpcert.or.jp/at/2018/at180008.html"},
 | 
			
		||||
	"CVE-2018-0835":    {"https://www.jpcert.or.jp/at/2018/at180008.html"},
 | 
			
		||||
	"CVE-2018-0837":    {"https://www.jpcert.or.jp/at/2018/at180008.html"},
 | 
			
		||||
	"CVE-2018-0838":    {"https://www.jpcert.or.jp/at/2018/at180008.html"},
 | 
			
		||||
	"CVE-2018-0840":    {"https://www.jpcert.or.jp/at/2018/at180008.html"},
 | 
			
		||||
	"CVE-2018-0852":    {"https://www.jpcert.or.jp/at/2018/at180008.html"},
 | 
			
		||||
	"CVE-2018-0856":    {"https://www.jpcert.or.jp/at/2018/at180008.html"},
 | 
			
		||||
	"CVE-2018-0857":    {"https://www.jpcert.or.jp/at/2018/at180008.html"},
 | 
			
		||||
	"CVE-2018-0859":    {"https://www.jpcert.or.jp/at/2018/at180008.html"},
 | 
			
		||||
	"CVE-2018-0860":    {"https://www.jpcert.or.jp/at/2018/at180008.html"},
 | 
			
		||||
	"CVE-2018-0861":    {"https://www.jpcert.or.jp/at/2018/at180008.html"},
 | 
			
		||||
	"CVE-2018-0870":    {"https://www.jpcert.or.jp/at/2018/at180016.html"},
 | 
			
		||||
	"CVE-2018-0872":    {"https://www.jpcert.or.jp/at/2018/at180011.html"},
 | 
			
		||||
	"CVE-2018-0874":    {"https://www.jpcert.or.jp/at/2018/at180011.html"},
 | 
			
		||||
	"CVE-2018-0876":    {"https://www.jpcert.or.jp/at/2018/at180011.html"},
 | 
			
		||||
	"CVE-2018-0889":    {"https://www.jpcert.or.jp/at/2018/at180011.html"},
 | 
			
		||||
	"CVE-2018-0893":    {"https://www.jpcert.or.jp/at/2018/at180011.html"},
 | 
			
		||||
	"CVE-2018-0930":    {"https://www.jpcert.or.jp/at/2018/at180011.html"},
 | 
			
		||||
	"CVE-2018-0931":    {"https://www.jpcert.or.jp/at/2018/at180011.html"},
 | 
			
		||||
	"CVE-2018-0932":    {"https://www.jpcert.or.jp/at/2018/at180011.html"},
 | 
			
		||||
	"CVE-2018-0933":    {"https://www.jpcert.or.jp/at/2018/at180011.html"},
 | 
			
		||||
	"CVE-2018-0934":    {"https://www.jpcert.or.jp/at/2018/at180011.html"},
 | 
			
		||||
	"CVE-2018-0936":    {"https://www.jpcert.or.jp/at/2018/at180011.html"},
 | 
			
		||||
	"CVE-2018-0937":    {"https://www.jpcert.or.jp/at/2018/at180011.html"},
 | 
			
		||||
	"CVE-2018-0939":    {"https://www.jpcert.or.jp/at/2018/at180011.html"},
 | 
			
		||||
	"CVE-2018-0943":    {"https://www.jpcert.or.jp/at/2018/at180021.html"},
 | 
			
		||||
	"CVE-2018-0945":    {"https://www.jpcert.or.jp/at/2018/at180021.html"},
 | 
			
		||||
	"CVE-2018-0946":    {"https://www.jpcert.or.jp/at/2018/at180021.html"},
 | 
			
		||||
	"CVE-2018-0950":    {"https://www.jpcert.or.jp/at/2018/at180016.html"},
 | 
			
		||||
	"CVE-2018-0951":    {"https://www.jpcert.or.jp/at/2018/at180021.html"},
 | 
			
		||||
	"CVE-2018-0953":    {"https://www.jpcert.or.jp/at/2018/at180021.html"},
 | 
			
		||||
	"CVE-2018-0954":    {"https://www.jpcert.or.jp/at/2018/at180021.html"},
 | 
			
		||||
	"CVE-2018-0955":    {"https://www.jpcert.or.jp/at/2018/at180021.html"},
 | 
			
		||||
	"CVE-2018-0959":    {"https://www.jpcert.or.jp/at/2018/at180021.html"},
 | 
			
		||||
	"CVE-2018-0961":    {"https://www.jpcert.or.jp/at/2018/at180021.html"},
 | 
			
		||||
	"CVE-2018-0965":    {"https://www.jpcert.or.jp/at/2018/at180038.html"},
 | 
			
		||||
	"CVE-2018-0979":    {"https://www.jpcert.or.jp/at/2018/at180016.html"},
 | 
			
		||||
	"CVE-2018-0980":    {"https://www.jpcert.or.jp/at/2018/at180016.html"},
 | 
			
		||||
	"CVE-2018-0981":    {"https://www.jpcert.or.jp/at/2018/at180016.html"},
 | 
			
		||||
	"CVE-2018-0988":    {"https://www.jpcert.or.jp/at/2018/at180016.html"},
 | 
			
		||||
	"CVE-2018-0990":    {"https://www.jpcert.or.jp/at/2018/at180016.html"},
 | 
			
		||||
	"CVE-2018-0991":    {"https://www.jpcert.or.jp/at/2018/at180016.html"},
 | 
			
		||||
	"CVE-2018-0993":    {"https://www.jpcert.or.jp/at/2018/at180016.html"},
 | 
			
		||||
	"CVE-2018-0994":    {"https://www.jpcert.or.jp/at/2018/at180016.html"},
 | 
			
		||||
	"CVE-2018-0995":    {"https://www.jpcert.or.jp/at/2018/at180016.html"},
 | 
			
		||||
	"CVE-2018-0996":    {"https://www.jpcert.or.jp/at/2018/at180016.html"},
 | 
			
		||||
	"CVE-2018-1000":    {"https://www.jpcert.or.jp/at/2018/at180016.html"},
 | 
			
		||||
	"CVE-2018-1004":    {"https://www.jpcert.or.jp/at/2018/at180016.html"},
 | 
			
		||||
	"CVE-2018-1010":    {"https://www.jpcert.or.jp/at/2018/at180016.html"},
 | 
			
		||||
	"CVE-2018-1012":    {"https://www.jpcert.or.jp/at/2018/at180016.html"},
 | 
			
		||||
	"CVE-2018-1013":    {"https://www.jpcert.or.jp/at/2018/at180016.html"},
 | 
			
		||||
	"CVE-2018-1015":    {"https://www.jpcert.or.jp/at/2018/at180016.html"},
 | 
			
		||||
	"CVE-2018-1016":    {"https://www.jpcert.or.jp/at/2018/at180016.html"},
 | 
			
		||||
	"CVE-2018-1018":    {"https://www.jpcert.or.jp/at/2018/at180016.html"},
 | 
			
		||||
	"CVE-2018-1019":    {"https://www.jpcert.or.jp/at/2018/at180016.html"},
 | 
			
		||||
	"CVE-2018-1020":    {"https://www.jpcert.or.jp/at/2018/at180016.html"},
 | 
			
		||||
	"CVE-2018-1022":    {"https://www.jpcert.or.jp/at/2018/at180021.html"},
 | 
			
		||||
	"CVE-2018-1023":    {"https://www.jpcert.or.jp/at/2018/at180016.html"},
 | 
			
		||||
	"CVE-2018-11776":   {"https://www.jpcert.or.jp/at/2018/at180036.html"},
 | 
			
		||||
	"CVE-2018-1270":    {"https://www.jpcert.or.jp/at/2018/at180014.html"},
 | 
			
		||||
	"CVE-2018-1271":    {"https://www.jpcert.or.jp/at/2018/at180014.html"},
 | 
			
		||||
	"CVE-2018-1272":    {"https://www.jpcert.or.jp/at/2018/at180014.html"},
 | 
			
		||||
	"CVE-2018-1273":    {"https://www.jpcert.or.jp/at/2018/at180017.html"},
 | 
			
		||||
	"CVE-2018-1274":    {"https://www.jpcert.or.jp/at/2018/at180017.html"},
 | 
			
		||||
	"CVE-2018-1275":    {"https://www.jpcert.or.jp/at/2018/at180014.html"},
 | 
			
		||||
	"CVE-2018-12794":   {"https://www.jpcert.or.jp/at/2018/at180039.html"},
 | 
			
		||||
	"CVE-2018-1336":    {"https://www.jpcert.or.jp/at/2018/at180030.html"},
 | 
			
		||||
	"CVE-2018-15442":   {"https://www.jpcert.or.jp/at/2018/at180043.html"},
 | 
			
		||||
	"CVE-2018-15979":   {"https://www.jpcert.or.jp/at/2018/at180045.html"},
 | 
			
		||||
	"CVE-2018-2628":    {"https://www.jpcert.or.jp/at/2018/at180029.html"},
 | 
			
		||||
	"CVE-2018-2893":    {"https://www.jpcert.or.jp/at/2018/at180029.html"},
 | 
			
		||||
	"CVE-2018-2894":    {"https://www.jpcert.or.jp/at/2018/at180029.html"},
 | 
			
		||||
	"CVE-2018-2933":    {"https://www.jpcert.or.jp/at/2018/at180029.html"},
 | 
			
		||||
	"CVE-2018-2983":    {"https://www.jpcert.or.jp/at/2018/at180029.html"},
 | 
			
		||||
	"CVE-2018-2998":    {"https://www.jpcert.or.jp/at/2018/at180029.html"},
 | 
			
		||||
	"CVE-2018-4877":    {"https://www.jpcert.or.jp/at/2018/at180008.html"},
 | 
			
		||||
	"CVE-2018-4878":    {"https://www.jpcert.or.jp/at/2018/at180006.html", "https://www.jpcert.or.jp/at/2018/at180008.html"},
 | 
			
		||||
	"CVE-2018-4945":    {"https://www.jpcert.or.jp/at/2018/at180025.html"},
 | 
			
		||||
	"CVE-2018-5000":    {"https://www.jpcert.or.jp/at/2018/at180025.html"},
 | 
			
		||||
	"CVE-2018-5001":    {"https://www.jpcert.or.jp/at/2018/at180025.html"},
 | 
			
		||||
	"CVE-2018-5002":    {"https://www.jpcert.or.jp/at/2018/at180024.html", "https://www.jpcert.or.jp/at/2018/at180025.html"},
 | 
			
		||||
	"CVE-2018-5740":    {"https://www.jpcert.or.jp/at/2018/at180031.html"},
 | 
			
		||||
	"CVE-2018-7600":    {"https://www.jpcert.or.jp/at/2018/at180012.html"},
 | 
			
		||||
	"CVE-2018-7602":    {"https://www.jpcert.or.jp/at/2018/at180019.html"},
 | 
			
		||||
	"CVE-2018-8034":    {"https://www.jpcert.or.jp/at/2018/at180030.html"},
 | 
			
		||||
	"CVE-2018-8037":    {"https://www.jpcert.or.jp/at/2018/at180030.html"},
 | 
			
		||||
	"CVE-2018-8110":    {"https://www.jpcert.or.jp/at/2018/at180025.html"},
 | 
			
		||||
	"CVE-2018-8111":    {"https://www.jpcert.or.jp/at/2018/at180025.html"},
 | 
			
		||||
	"CVE-2018-8114":    {"https://www.jpcert.or.jp/at/2018/at180021.html"},
 | 
			
		||||
	"CVE-2018-8120":    {"https://www.jpcert.or.jp/at/2018/at180021.html"},
 | 
			
		||||
	"CVE-2018-8122":    {"https://www.jpcert.or.jp/at/2018/at180021.html"},
 | 
			
		||||
	"CVE-2018-8128":    {"https://www.jpcert.or.jp/at/2018/at180021.html"},
 | 
			
		||||
	"CVE-2018-8130":    {"https://www.jpcert.or.jp/at/2018/at180021.html"},
 | 
			
		||||
	"CVE-2018-8133":    {"https://www.jpcert.or.jp/at/2018/at180021.html"},
 | 
			
		||||
	"CVE-2018-8137":    {"https://www.jpcert.or.jp/at/2018/at180021.html"},
 | 
			
		||||
	"CVE-2018-8139":    {"https://www.jpcert.or.jp/at/2018/at180021.html"},
 | 
			
		||||
	"CVE-2018-8154":    {"https://www.jpcert.or.jp/at/2018/at180021.html"},
 | 
			
		||||
	"CVE-2018-8174":    {"https://www.jpcert.or.jp/at/2018/at180021.html"},
 | 
			
		||||
	"CVE-2018-8178":    {"https://www.jpcert.or.jp/at/2018/at180021.html"},
 | 
			
		||||
	"CVE-2018-8213":    {"https://www.jpcert.or.jp/at/2018/at180025.html"},
 | 
			
		||||
	"CVE-2018-8225":    {"https://www.jpcert.or.jp/at/2018/at180025.html"},
 | 
			
		||||
	"CVE-2018-8229":    {"https://www.jpcert.or.jp/at/2018/at180025.html"},
 | 
			
		||||
	"CVE-2018-8231":    {"https://www.jpcert.or.jp/at/2018/at180025.html"},
 | 
			
		||||
	"CVE-2018-8236":    {"https://www.jpcert.or.jp/at/2018/at180025.html"},
 | 
			
		||||
	"CVE-2018-8242":    {"https://www.jpcert.or.jp/at/2018/at180028.html"},
 | 
			
		||||
	"CVE-2018-8249":    {"https://www.jpcert.or.jp/at/2018/at180025.html"},
 | 
			
		||||
	"CVE-2018-8251":    {"https://www.jpcert.or.jp/at/2018/at180025.html"},
 | 
			
		||||
	"CVE-2018-8262":    {"https://www.jpcert.or.jp/at/2018/at180028.html"},
 | 
			
		||||
	"CVE-2018-8266":    {"https://www.jpcert.or.jp/at/2018/at180034.html"},
 | 
			
		||||
	"CVE-2018-8267":    {"https://www.jpcert.or.jp/at/2018/at180025.html"},
 | 
			
		||||
	"CVE-2018-8273":    {"https://www.jpcert.or.jp/at/2018/at180034.html"},
 | 
			
		||||
	"CVE-2018-8274":    {"https://www.jpcert.or.jp/at/2018/at180028.html"},
 | 
			
		||||
	"CVE-2018-8275":    {"https://www.jpcert.or.jp/at/2018/at180028.html"},
 | 
			
		||||
	"CVE-2018-8279":    {"https://www.jpcert.or.jp/at/2018/at180028.html"},
 | 
			
		||||
	"CVE-2018-8280":    {"https://www.jpcert.or.jp/at/2018/at180028.html"},
 | 
			
		||||
	"CVE-2018-8286":    {"https://www.jpcert.or.jp/at/2018/at180028.html"},
 | 
			
		||||
	"CVE-2018-8288":    {"https://www.jpcert.or.jp/at/2018/at180028.html"},
 | 
			
		||||
	"CVE-2018-8290":    {"https://www.jpcert.or.jp/at/2018/at180028.html"},
 | 
			
		||||
	"CVE-2018-8291":    {"https://www.jpcert.or.jp/at/2018/at180028.html"},
 | 
			
		||||
	"CVE-2018-8294":    {"https://www.jpcert.or.jp/at/2018/at180028.html"},
 | 
			
		||||
	"CVE-2018-8296":    {"https://www.jpcert.or.jp/at/2018/at180028.html"},
 | 
			
		||||
	"CVE-2018-8301":    {"https://www.jpcert.or.jp/at/2018/at180028.html"},
 | 
			
		||||
	"CVE-2018-8302":    {"https://www.jpcert.or.jp/at/2018/at180034.html"},
 | 
			
		||||
	"CVE-2018-8324":    {"https://www.jpcert.or.jp/at/2018/at180028.html"},
 | 
			
		||||
	"CVE-2018-8332":    {"https://www.jpcert.or.jp/at/2018/at180038.html"},
 | 
			
		||||
	"CVE-2018-8344":    {"https://www.jpcert.or.jp/at/2018/at180034.html"},
 | 
			
		||||
	"CVE-2018-8345":    {"https://www.jpcert.or.jp/at/2018/at180034.html"},
 | 
			
		||||
	"CVE-2018-8350":    {"https://www.jpcert.or.jp/at/2018/at180034.html"},
 | 
			
		||||
	"CVE-2018-8355":    {"https://www.jpcert.or.jp/at/2018/at180034.html"},
 | 
			
		||||
	"CVE-2018-8367":    {"https://www.jpcert.or.jp/at/2018/at180038.html"},
 | 
			
		||||
	"CVE-2018-8371":    {"https://www.jpcert.or.jp/at/2018/at180034.html"},
 | 
			
		||||
	"CVE-2018-8372":    {"https://www.jpcert.or.jp/at/2018/at180034.html"},
 | 
			
		||||
	"CVE-2018-8373":    {"https://www.jpcert.or.jp/at/2018/at180034.html"},
 | 
			
		||||
	"CVE-2018-8377":    {"https://www.jpcert.or.jp/at/2018/at180034.html"},
 | 
			
		||||
	"CVE-2018-8380":    {"https://www.jpcert.or.jp/at/2018/at180034.html"},
 | 
			
		||||
	"CVE-2018-8381":    {"https://www.jpcert.or.jp/at/2018/at180034.html"},
 | 
			
		||||
	"CVE-2018-8385":    {"https://www.jpcert.or.jp/at/2018/at180034.html"},
 | 
			
		||||
	"CVE-2018-8387":    {"https://www.jpcert.or.jp/at/2018/at180034.html"},
 | 
			
		||||
	"CVE-2018-8390":    {"https://www.jpcert.or.jp/at/2018/at180034.html"},
 | 
			
		||||
	"CVE-2018-8397":    {"https://www.jpcert.or.jp/at/2018/at180034.html"},
 | 
			
		||||
	"CVE-2018-8403":    {"https://www.jpcert.or.jp/at/2018/at180034.html"},
 | 
			
		||||
	"CVE-2018-8414":    {"https://www.jpcert.or.jp/at/2018/at180034.html"},
 | 
			
		||||
	"CVE-2018-8420":    {"https://www.jpcert.or.jp/at/2018/at180038.html"},
 | 
			
		||||
	"CVE-2018-8421":    {"https://www.jpcert.or.jp/at/2018/at180038.html"},
 | 
			
		||||
	"CVE-2018-8439":    {"https://www.jpcert.or.jp/at/2018/at180038.html"},
 | 
			
		||||
	"CVE-2018-8440":    {"https://www.jpcert.or.jp/at/2018/at180038.html"},
 | 
			
		||||
	"CVE-2018-8447":    {"https://www.jpcert.or.jp/at/2018/at180038.html"},
 | 
			
		||||
	"CVE-2018-8453":    {"https://www.jpcert.or.jp/at/2018/at180041.html"},
 | 
			
		||||
	"CVE-2018-8456":    {"https://www.jpcert.or.jp/at/2018/at180038.html"},
 | 
			
		||||
	"CVE-2018-8457":    {"https://www.jpcert.or.jp/at/2018/at180038.html"},
 | 
			
		||||
	"CVE-2018-8459":    {"https://www.jpcert.or.jp/at/2018/at180038.html"},
 | 
			
		||||
	"CVE-2018-8460":    {"https://www.jpcert.or.jp/at/2018/at180041.html"},
 | 
			
		||||
	"CVE-2018-8461":    {"https://www.jpcert.or.jp/at/2018/at180038.html"},
 | 
			
		||||
	"CVE-2018-8464":    {"https://www.jpcert.or.jp/at/2018/at180038.html"},
 | 
			
		||||
	"CVE-2018-8465":    {"https://www.jpcert.or.jp/at/2018/at180038.html"},
 | 
			
		||||
	"CVE-2018-8466":    {"https://www.jpcert.or.jp/at/2018/at180038.html"},
 | 
			
		||||
	"CVE-2018-8467":    {"https://www.jpcert.or.jp/at/2018/at180038.html"},
 | 
			
		||||
	"CVE-2018-8473":    {"https://www.jpcert.or.jp/at/2018/at180041.html"},
 | 
			
		||||
	"CVE-2018-8475":    {"https://www.jpcert.or.jp/at/2018/at180038.html"},
 | 
			
		||||
	"CVE-2018-8476":    {"https://www.jpcert.or.jp/at/2018/at180046.html"},
 | 
			
		||||
	"CVE-2018-8489":    {"https://www.jpcert.or.jp/at/2018/at180041.html"},
 | 
			
		||||
	"CVE-2018-8490":    {"https://www.jpcert.or.jp/at/2018/at180041.html"},
 | 
			
		||||
	"CVE-2018-8491":    {"https://www.jpcert.or.jp/at/2018/at180041.html"},
 | 
			
		||||
	"CVE-2018-8494":    {"https://www.jpcert.or.jp/at/2018/at180041.html"},
 | 
			
		||||
	"CVE-2018-8505":    {"https://www.jpcert.or.jp/at/2018/at180041.html"},
 | 
			
		||||
	"CVE-2018-8509":    {"https://www.jpcert.or.jp/at/2018/at180041.html"},
 | 
			
		||||
	"CVE-2018-8510":    {"https://www.jpcert.or.jp/at/2018/at180041.html"},
 | 
			
		||||
	"CVE-2018-8511":    {"https://www.jpcert.or.jp/at/2018/at180041.html"},
 | 
			
		||||
	"CVE-2018-8513":    {"https://www.jpcert.or.jp/at/2018/at180041.html"},
 | 
			
		||||
	"CVE-2018-8541":    {"https://www.jpcert.or.jp/at/2018/at180046.html"},
 | 
			
		||||
	"CVE-2018-8542":    {"https://www.jpcert.or.jp/at/2018/at180046.html"},
 | 
			
		||||
	"CVE-2018-8543":    {"https://www.jpcert.or.jp/at/2018/at180046.html"},
 | 
			
		||||
	"CVE-2018-8544":    {"https://www.jpcert.or.jp/at/2018/at180046.html"},
 | 
			
		||||
	"CVE-2018-8551":    {"https://www.jpcert.or.jp/at/2018/at180046.html"},
 | 
			
		||||
	"CVE-2018-8553":    {"https://www.jpcert.or.jp/at/2018/at180046.html"},
 | 
			
		||||
	"CVE-2018-8555":    {"https://www.jpcert.or.jp/at/2018/at180046.html"},
 | 
			
		||||
	"CVE-2018-8556":    {"https://www.jpcert.or.jp/at/2018/at180046.html"},
 | 
			
		||||
	"CVE-2018-8557":    {"https://www.jpcert.or.jp/at/2018/at180046.html"},
 | 
			
		||||
	"CVE-2018-8588":    {"https://www.jpcert.or.jp/at/2018/at180046.html"},
 | 
			
		||||
	"CVE-2018-8589":    {"https://www.jpcert.or.jp/at/2018/at180046.html"},
 | 
			
		||||
	"CVE-2018-8609":    {"https://www.jpcert.or.jp/at/2018/at180046.html"},
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										96
									
								
								alert/cve_us.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,96 @@
 | 
			
		||||
package alert
 | 
			
		||||
 | 
			
		||||
// CveDictEn has CVE-ID key which included USCERT alerts
 | 
			
		||||
var CveDictEn = map[string][]string{
 | 
			
		||||
	"CVE-1999-0532": {"https://www.us-cert.gov/ncas/alerts/TA15-103A"},
 | 
			
		||||
	"CVE-2006-3227": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2008-0015": {"https://www.us-cert.gov/ncas/alerts/TA09-195A", "https://www.us-cert.gov/ncas/alerts/TA09-209A"},
 | 
			
		||||
	"CVE-2008-2244": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2009-0658": {"https://www.us-cert.gov/ncas/alerts/TA09-051A"},
 | 
			
		||||
	"CVE-2009-0927": {"https://www.us-cert.gov/ncas/alerts/TA13-141A"},
 | 
			
		||||
	"CVE-2009-1492": {"https://www.us-cert.gov/ncas/alerts/TA09-133B"},
 | 
			
		||||
	"CVE-2009-1493": {"https://www.us-cert.gov/ncas/alerts/TA09-133B"},
 | 
			
		||||
	"CVE-2009-1537": {"https://www.us-cert.gov/ncas/alerts/TA09-195A"},
 | 
			
		||||
	"CVE-2009-3103": {"https://www.us-cert.gov/ncas/alerts/TA17-181A"},
 | 
			
		||||
	"CVE-2009-3129": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2009-3674": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2009-3953": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2010-0018": {"https://www.us-cert.gov/ncas/alerts/TA10-012B"},
 | 
			
		||||
	"CVE-2010-0188": {"https://www.us-cert.gov/ncas/alerts/TA13-141A", "https://www.us-cert.gov/ncas/alerts/TA14-300A", "https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2010-0806": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2010-1297": {"https://www.us-cert.gov/ncas/alerts/TA10-162A", "https://www.us-cert.gov/ncas/alerts/TA10-159A"},
 | 
			
		||||
	"CVE-2010-2883": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2010-3333": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2011-0101": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2011-0611": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2011-2462": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2012-0158": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2012-1723": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2012-1856": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2012-1889": {"https://www.us-cert.gov/ncas/alerts/TA12-174A"},
 | 
			
		||||
	"CVE-2012-3174": {"https://www.us-cert.gov/ncas/alerts/TA13-010A"},
 | 
			
		||||
	"CVE-2012-4681": {"https://www.us-cert.gov/ncas/alerts/TA12-240A"},
 | 
			
		||||
	"CVE-2012-4792": {"https://www.us-cert.gov/ncas/alerts/TA13-015A", "https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2013-0074": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2013-0140": {"https://www.us-cert.gov/ncas/alerts/TA13-193A"},
 | 
			
		||||
	"CVE-2013-0141": {"https://www.us-cert.gov/ncas/alerts/TA13-193A"},
 | 
			
		||||
	"CVE-2013-0422": {"https://www.us-cert.gov/ncas/alerts/TA13-141A", "https://www.us-cert.gov/ncas/alerts/TA13-010A"},
 | 
			
		||||
	"CVE-2013-0625": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2013-0632": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2013-0809": {"https://www.us-cert.gov/ncas/alerts/TA13-064A"},
 | 
			
		||||
	"CVE-2013-1347": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2013-1493": {"https://www.us-cert.gov/ncas/alerts/TA13-064A"},
 | 
			
		||||
	"CVE-2013-1571": {"https://www.us-cert.gov/ncas/alerts/TA13-169A"},
 | 
			
		||||
	"CVE-2013-2465": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2013-2729": {"https://www.us-cert.gov/ncas/alerts/TA14-300A", "https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2013-3336": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2013-5211": {"https://www.us-cert.gov/ncas/alerts/TA14-017A", "https://www.us-cert.gov/ncas/alerts/TA14-013A"},
 | 
			
		||||
	"CVE-2013-5326": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2014-0160": {"https://www.us-cert.gov/ncas/alerts/TA14-098A", "https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2014-0322": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2014-0564": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2014-1761": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2014-1776": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2014-3393": {"https://www.us-cert.gov/ncas/alerts/TA16-250A"},
 | 
			
		||||
	"CVE-2014-3566": {"https://www.us-cert.gov/ncas/alerts/TA14-290A", "https://www.us-cert.gov/ncas/alerts/TA15-120A"},
 | 
			
		||||
	"CVE-2014-4114": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
 | 
			
		||||
	"CVE-2014-6271": {"https://www.us-cert.gov/ncas/alerts/TA14-268A"},
 | 
			
		||||
	"CVE-2014-6277": {"https://www.us-cert.gov/ncas/alerts/TA14-268A"},
 | 
			
		||||
	"CVE-2014-6278": {"https://www.us-cert.gov/ncas/alerts/TA14-268A"},
 | 
			
		||||
	"CVE-2014-6321": {"https://www.us-cert.gov/ncas/alerts/TA14-318A"},
 | 
			
		||||
	"CVE-2014-6332": {"https://www.us-cert.gov/ncas/alerts/TA14-318B"},
 | 
			
		||||
	"CVE-2014-7169": {"https://www.us-cert.gov/ncas/alerts/TA14-268A"},
 | 
			
		||||
	"CVE-2014-7186": {"https://www.us-cert.gov/ncas/alerts/TA14-268A"},
 | 
			
		||||
	"CVE-2014-7187": {"https://www.us-cert.gov/ncas/alerts/TA14-268A"},
 | 
			
		||||
	"CVE-2014-8730": {"https://www.us-cert.gov/ncas/alerts/TA14-290A"},
 | 
			
		||||
	"CVE-2015-2387": {"https://www.us-cert.gov/ncas/alerts/TA15-195A"},
 | 
			
		||||
	"CVE-2015-5119": {"https://www.us-cert.gov/ncas/alerts/TA15-195A", "https://www.us-cert.gov/ncas/alerts/TA15-213A"},
 | 
			
		||||
	"CVE-2015-5122": {"https://www.us-cert.gov/ncas/alerts/TA15-195A"},
 | 
			
		||||
	"CVE-2015-5123": {"https://www.us-cert.gov/ncas/alerts/TA15-195A"},
 | 
			
		||||
	"CVE-2015-6585": {"https://www.us-cert.gov/ncas/alerts/TA17-164A"},
 | 
			
		||||
	"CVE-2015-8651": {"https://www.us-cert.gov/ncas/alerts/TA17-164A"},
 | 
			
		||||
	"CVE-2016-0034": {"https://www.us-cert.gov/ncas/alerts/TA17-164A"},
 | 
			
		||||
	"CVE-2016-1019": {"https://www.us-cert.gov/ncas/alerts/TA17-164A"},
 | 
			
		||||
	"CVE-2016-2207": {"https://www.us-cert.gov/ncas/alerts/TA16-187A"},
 | 
			
		||||
	"CVE-2016-2208": {"https://www.us-cert.gov/ncas/alerts/TA16-187A"},
 | 
			
		||||
	"CVE-2016-2209": {"https://www.us-cert.gov/ncas/alerts/TA16-187A"},
 | 
			
		||||
	"CVE-2016-2210": {"https://www.us-cert.gov/ncas/alerts/TA16-187A"},
 | 
			
		||||
	"CVE-2016-2211": {"https://www.us-cert.gov/ncas/alerts/TA16-187A"},
 | 
			
		||||
	"CVE-2016-3644": {"https://www.us-cert.gov/ncas/alerts/TA16-187A"},
 | 
			
		||||
	"CVE-2016-3645": {"https://www.us-cert.gov/ncas/alerts/TA16-187A"},
 | 
			
		||||
	"CVE-2016-4117": {"https://www.us-cert.gov/ncas/alerts/TA17-164A"},
 | 
			
		||||
	"CVE-2016-6366": {"https://www.us-cert.gov/ncas/alerts/TA16-250A"},
 | 
			
		||||
	"CVE-2016-6367": {"https://www.us-cert.gov/ncas/alerts/TA16-250A"},
 | 
			
		||||
	"CVE-2016-6415": {"https://www.us-cert.gov/ncas/alerts/TA16-250A"},
 | 
			
		||||
	"CVE-2016-6909": {"https://www.us-cert.gov/ncas/alerts/TA16-250A"},
 | 
			
		||||
	"CVE-2016-7089": {"https://www.us-cert.gov/ncas/alerts/TA16-250A"},
 | 
			
		||||
	"CVE-2017-0144": {"https://www.us-cert.gov/ncas/alerts/TA17-181A"},
 | 
			
		||||
	"CVE-2017-0145": {"https://www.us-cert.gov/ncas/alerts/TA17-181A"},
 | 
			
		||||
	"CVE-2017-3066": {"https://www.us-cert.gov/ncas/alerts/AA18-284A"},
 | 
			
		||||
	"CVE-2017-5715": {"https://www.us-cert.gov/ncas/alerts/TA18-141A", "https://www.us-cert.gov/ncas/alerts/TA18-004A"},
 | 
			
		||||
	"CVE-2017-5753": {"https://www.us-cert.gov/ncas/alerts/TA18-141A", "https://www.us-cert.gov/ncas/alerts/TA18-004A"},
 | 
			
		||||
	"CVE-2017-5754": {"https://www.us-cert.gov/ncas/alerts/TA18-141A", "https://www.us-cert.gov/ncas/alerts/TA18-004A"},
 | 
			
		||||
	"CVE-2018-1038": {"https://www.us-cert.gov/ncas/alerts/TA18-004A"},
 | 
			
		||||
	"CVE-2018-3639": {"https://www.us-cert.gov/ncas/alerts/TA18-141A"},
 | 
			
		||||
	"CVE-2018-3640": {"https://www.us-cert.gov/ncas/alerts/TA18-141A"},
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								alert/util.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,21 @@
 | 
			
		||||
package alert
 | 
			
		||||
 | 
			
		||||
// GenerateAlertDict returns XCERT alert slice by cveID
 | 
			
		||||
func GenerateAlertDict(cveID string, lang string) (alerts []Alert) {
 | 
			
		||||
	if lang == "ja" {
 | 
			
		||||
		if keys, ok := CveDictJa[cveID]; ok {
 | 
			
		||||
			for _, key := range keys {
 | 
			
		||||
				alerts = append(alerts, AlertDictJa[key])
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return alerts
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// default language is English
 | 
			
		||||
	if keys, ok := CveDictEn[cveID]; ok {
 | 
			
		||||
		for _, key := range keys {
 | 
			
		||||
			alerts = append(alerts, AlertDictEn[key])
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return alerts
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										188
									
								
								cache/bolt.go
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,188 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/boltdb/bolt"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Bolt holds a pointer of bolt.DB
 | 
			
		||||
// boltdb is used to store a cache of Changelogs of Ubuntu/Debian
 | 
			
		||||
type Bolt struct {
 | 
			
		||||
	Path string
 | 
			
		||||
	Log  *logrus.Entry
 | 
			
		||||
	db   *bolt.DB
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetupBolt opens a boltdb and creates a meta bucket if not exists.
 | 
			
		||||
func SetupBolt(path string, l *logrus.Entry) error {
 | 
			
		||||
	l.Infof("Open boltDB: %s", path)
 | 
			
		||||
	db, err := bolt.Open(path, 0600, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	b := Bolt{
 | 
			
		||||
		Path: path,
 | 
			
		||||
		Log:  l,
 | 
			
		||||
		db:   db,
 | 
			
		||||
	}
 | 
			
		||||
	if err = b.createBucketIfNotExists(metabucket); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	DB = b
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close a db.
 | 
			
		||||
func (b Bolt) Close() error {
 | 
			
		||||
	if b.db == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return b.db.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//  CreateBucketIfNotExists creates a buket that is specified by arg.
 | 
			
		||||
func (b *Bolt) createBucketIfNotExists(name string) error {
 | 
			
		||||
	return b.db.Update(func(tx *bolt.Tx) error {
 | 
			
		||||
		_, err := tx.CreateBucketIfNotExists([]byte(name))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to create bucket: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetMeta gets a Meta Information os the servername to boltdb.
 | 
			
		||||
func (b Bolt) GetMeta(serverName string) (meta Meta, found bool, err error) {
 | 
			
		||||
	err = b.db.View(func(tx *bolt.Tx) error {
 | 
			
		||||
		bkt := tx.Bucket([]byte(metabucket))
 | 
			
		||||
		v := bkt.Get([]byte(serverName))
 | 
			
		||||
		if len(v) == 0 {
 | 
			
		||||
			found = false
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		if e := json.Unmarshal(v, &meta); e != nil {
 | 
			
		||||
			return e
 | 
			
		||||
		}
 | 
			
		||||
		found = true
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RefreshMeta gets a Meta Information os the servername to boltdb.
 | 
			
		||||
func (b Bolt) RefreshMeta(meta Meta) error {
 | 
			
		||||
	meta.CreatedAt = time.Now()
 | 
			
		||||
	jsonBytes, err := json.Marshal(meta)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to marshal to JSON: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	return b.db.Update(func(tx *bolt.Tx) error {
 | 
			
		||||
		bkt := tx.Bucket([]byte(metabucket))
 | 
			
		||||
		if err := bkt.Put([]byte(meta.Name), jsonBytes); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		b.Log.Debugf("Refreshed Meta: %s", meta.Name)
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EnsureBuckets puts a Meta information and create a buket that holds changelogs.
 | 
			
		||||
func (b Bolt) EnsureBuckets(meta Meta) error {
 | 
			
		||||
	jsonBytes, err := json.Marshal(meta)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to marshal to JSON: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	return b.db.Update(func(tx *bolt.Tx) error {
 | 
			
		||||
		b.Log.Debugf("Put to meta: %s", meta.Name)
 | 
			
		||||
		bkt := tx.Bucket([]byte(metabucket))
 | 
			
		||||
		if err := bkt.Put([]byte(meta.Name), jsonBytes); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// re-create a bucket (bucket name: servername)
 | 
			
		||||
		bkt = tx.Bucket([]byte(meta.Name))
 | 
			
		||||
		if bkt != nil {
 | 
			
		||||
			b.Log.Debugf("Delete bucket: %s", meta.Name)
 | 
			
		||||
			if err := tx.DeleteBucket([]byte(meta.Name)); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			b.Log.Debugf("Bucket deleted: %s", meta.Name)
 | 
			
		||||
		}
 | 
			
		||||
		b.Log.Debugf("Create bucket: %s", meta.Name)
 | 
			
		||||
		if _, err := tx.CreateBucket([]byte(meta.Name)); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		b.Log.Debugf("Bucket created: %s", meta.Name)
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PrettyPrint is for debug
 | 
			
		||||
func (b Bolt) PrettyPrint(meta Meta) error {
 | 
			
		||||
	return b.db.View(func(tx *bolt.Tx) error {
 | 
			
		||||
		bkt := tx.Bucket([]byte(metabucket))
 | 
			
		||||
		v := bkt.Get([]byte(meta.Name))
 | 
			
		||||
		b.Log.Debugf("Meta: key:%s, value:%s", meta.Name, v)
 | 
			
		||||
 | 
			
		||||
		bkt = tx.Bucket([]byte(meta.Name))
 | 
			
		||||
		c := bkt.Cursor()
 | 
			
		||||
		for k, v := c.First(); k != nil; k, v = c.Next() {
 | 
			
		||||
			b.Log.Debugf("key:%s, len: %d, %s...",
 | 
			
		||||
				k, len(v), util.Truncate(string(v), 30))
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChangelog get the 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 xerrors.Errorf("Failed to get Bucket: %s", servername)
 | 
			
		||||
		}
 | 
			
		||||
		v := bkt.Get([]byte(packName))
 | 
			
		||||
		if v == nil {
 | 
			
		||||
			changelog = ""
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		changelog = string(v)
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PutChangelog put the changelgo of specified packName into the Bucket
 | 
			
		||||
func (b Bolt) PutChangelog(servername, packName, changelog string) error {
 | 
			
		||||
	return b.db.Update(func(tx *bolt.Tx) error {
 | 
			
		||||
		bkt := tx.Bucket([]byte(servername))
 | 
			
		||||
		if bkt == nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to get Bucket: %s", servername)
 | 
			
		||||
		}
 | 
			
		||||
		return bkt.Put([]byte(packName), []byte(changelog))
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										137
									
								
								cache/bolt_test.go
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,137 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,50 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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
 | 
			
		||||
}
 | 
			
		||||
@@ -1,21 +0,0 @@
 | 
			
		||||
package commands
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/howeyc/gopass"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func getPasswd(prompt string) (string, error) {
 | 
			
		||||
	for {
 | 
			
		||||
		fmt.Print(prompt)
 | 
			
		||||
		pass, err := gopass.GetPasswdMasked()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", fmt.Errorf("Failed to read password")
 | 
			
		||||
		}
 | 
			
		||||
		if 0 < len(pass) {
 | 
			
		||||
			return string(pass[:]), nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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
 | 
			
		||||
@@ -18,15 +18,14 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
package commands
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/google/subcommands"
 | 
			
		||||
	"golang.org/x/net/context"
 | 
			
		||||
 | 
			
		||||
	c "github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/scan"
 | 
			
		||||
@@ -37,9 +36,7 @@ import (
 | 
			
		||||
type ConfigtestCmd struct {
 | 
			
		||||
	configPath     string
 | 
			
		||||
	askKeyPassword bool
 | 
			
		||||
	sshExternal    bool
 | 
			
		||||
 | 
			
		||||
	debug bool
 | 
			
		||||
	timeoutSec     int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Name return subcommand name
 | 
			
		||||
@@ -52,12 +49,17 @@ func (*ConfigtestCmd) Synopsis() string { return "Test configuration" }
 | 
			
		||||
func (*ConfigtestCmd) Usage() string {
 | 
			
		||||
	return `configtest:
 | 
			
		||||
	configtest
 | 
			
		||||
		        [-config=/path/to/config.toml]
 | 
			
		||||
	        	[-ask-key-password]
 | 
			
		||||
	        	[-ssh-external]
 | 
			
		||||
		        [-debug]
 | 
			
		||||
			[-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]
 | 
			
		||||
			[-vvv]
 | 
			
		||||
 | 
			
		||||
		        [SERVER]...
 | 
			
		||||
			[SERVER]...
 | 
			
		||||
`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -67,59 +69,65 @@ func (p *ConfigtestCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
	defaultConfPath := filepath.Join(wd, "config.toml")
 | 
			
		||||
	f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&p.debug, "debug", false, "debug mode")
 | 
			
		||||
	defaultLogDir := util.GetDefaultLogDir()
 | 
			
		||||
	f.StringVar(&c.Conf.LogDir, "log-dir", defaultLogDir, "/path/to/log")
 | 
			
		||||
	f.BoolVar(&c.Conf.Debug, "debug", false, "debug mode")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(
 | 
			
		||||
		&p.askKeyPassword,
 | 
			
		||||
		"ask-key-password",
 | 
			
		||||
		false,
 | 
			
		||||
	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.sshExternal,
 | 
			
		||||
		"ssh-external",
 | 
			
		||||
		false,
 | 
			
		||||
		"Use external ssh command. Default: Use the Go native implementation")
 | 
			
		||||
	f.StringVar(&c.Conf.HTTPProxy, "http-proxy", "",
 | 
			
		||||
		"http://proxy-url:port (default: empty)")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.SSHNative, "ssh-native-insecure", false,
 | 
			
		||||
		"Use Native Go implementation of SSH. Default: Use the external command")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.SSHConfig, "ssh-config", false,
 | 
			
		||||
		"Use SSH options specified in ssh_config preferentially")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.ContainersOnly, "containers-only", false,
 | 
			
		||||
		"Test containers only. Default: Test both of hosts and containers")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.Vvv, "vvv", false, "ssh -vvv")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Execute execute
 | 
			
		||||
func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
 | 
			
		||||
	// Setup Logger
 | 
			
		||||
	util.Log = util.NewCustomLogger(c.ServerInfo{})
 | 
			
		||||
 | 
			
		||||
	if err := mkdirDotVuls(); err != nil {
 | 
			
		||||
		util.Log.Errorf("Failed to create .vuls. err: %+v", err)
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var keyPass string
 | 
			
		||||
	var err error
 | 
			
		||||
	if p.askKeyPassword {
 | 
			
		||||
		prompt := "SSH key password: "
 | 
			
		||||
		if keyPass, err = getPasswd(prompt); err != nil {
 | 
			
		||||
			logrus.Error(err)
 | 
			
		||||
			util.Log.Error(err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.Conf.Debug = p.debug
 | 
			
		||||
 | 
			
		||||
	err = c.Load(p.configPath, keyPass)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logrus.Errorf("Error loading %s, %s", p.configPath, err)
 | 
			
		||||
		msg := []string{
 | 
			
		||||
			fmt.Sprintf("Error loading %s", p.configPath),
 | 
			
		||||
			"If you update Vuls and get this error, there may be incompatible changes in config.toml",
 | 
			
		||||
			"Please check config.toml template : https://vuls.io/docs/en/usage-settings.html",
 | 
			
		||||
		}
 | 
			
		||||
		util.Log.Errorf("%s\n%+v", strings.Join(msg, "\n"), err)
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var servernames []string
 | 
			
		||||
	if 0 < len(f.Args()) {
 | 
			
		||||
		servernames = f.Args()
 | 
			
		||||
	} else {
 | 
			
		||||
		stat, _ := os.Stdin.Stat()
 | 
			
		||||
		if (stat.Mode() & os.ModeCharDevice) == 0 {
 | 
			
		||||
			bytes, err := ioutil.ReadAll(os.Stdin)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				logrus.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)
 | 
			
		||||
@@ -133,7 +141,7 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if !found {
 | 
			
		||||
			logrus.Errorf("%s is not in config", arg)
 | 
			
		||||
			util.Log.Errorf("%s is not in config", arg)
 | 
			
		||||
			return subcommands.ExitUsageError
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -141,22 +149,33 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa
 | 
			
		||||
		c.Conf.Servers = target
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// logger
 | 
			
		||||
	Log := util.NewCustomLogger(c.ServerInfo{})
 | 
			
		||||
 | 
			
		||||
	Log.Info("Validating Config...")
 | 
			
		||||
	if !c.Conf.Validate() {
 | 
			
		||||
	util.Log.Info("Validating config...")
 | 
			
		||||
	if !c.Conf.ValidateOnConfigtest() {
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Log.Info("Detecting Server/Contianer OS... ")
 | 
			
		||||
	scan.InitServers(Log)
 | 
			
		||||
 | 
			
		||||
	Log.Info("Checking sudo configuration... ")
 | 
			
		||||
	if err := scan.CheckIfSudoNoPasswd(Log); err != nil {
 | 
			
		||||
		Log.Errorf("Failed to sudo with nopassword via SSH. Define NOPASSWD in /etc/sudoers on target servers. err: %s", err)
 | 
			
		||||
	util.Log.Info("Detecting Server/Container OS... ")
 | 
			
		||||
	if err := scan.InitServers(p.timeoutSec); err != nil {
 | 
			
		||||
		util.Log.Errorf("Failed to init servers. err: %+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
	scan.PrintSSHableServerNames()
 | 
			
		||||
	return subcommands.ExitSuccess
 | 
			
		||||
 | 
			
		||||
	util.Log.Info("Checking Scan Modes...")
 | 
			
		||||
	if err := scan.CheckScanModes(); err != nil {
 | 
			
		||||
		util.Log.Errorf("Fix config.toml. err: %+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	util.Log.Info("Checking dependencies...")
 | 
			
		||||
	scan.CheckDependencies(p.timeoutSec)
 | 
			
		||||
 | 
			
		||||
	util.Log.Info("Checking sudo settings...")
 | 
			
		||||
	scan.CheckIfSudoNoPasswd(p.timeoutSec)
 | 
			
		||||
 | 
			
		||||
	util.Log.Info("It can be scanned with fast scan mode even if warn or err messages are displayed due to lack of dependent packages or sudo settings in fast-root or deep scan mode")
 | 
			
		||||
 | 
			
		||||
	if scan.PrintSSHableServerNames() {
 | 
			
		||||
		return subcommands.ExitSuccess
 | 
			
		||||
	}
 | 
			
		||||
	return subcommands.ExitFailure
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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
 | 
			
		||||
@@ -18,6 +18,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
package commands
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
@@ -25,10 +26,9 @@ import (
 | 
			
		||||
	"text/template"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/subcommands"
 | 
			
		||||
	"golang.org/x/net/context"
 | 
			
		||||
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
	ps "github.com/kotakanbe/go-pingscanner"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DiscoverCmd is Subcommand of host discovery mode
 | 
			
		||||
@@ -57,6 +57,7 @@ func (p *DiscoverCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
func (p *DiscoverCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
 | 
			
		||||
	// validate
 | 
			
		||||
	if len(f.Args()) == 0 {
 | 
			
		||||
		logrus.Errorf("Usage: " + p.Usage())
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -65,7 +66,6 @@ func (p *DiscoverCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface
 | 
			
		||||
			CIDR: cidr,
 | 
			
		||||
			PingOptions: []string{
 | 
			
		||||
				"-c1",
 | 
			
		||||
				"-t1",
 | 
			
		||||
			},
 | 
			
		||||
			NumOfConcurrency: 100,
 | 
			
		||||
		}
 | 
			
		||||
@@ -87,59 +87,157 @@ func (p *DiscoverCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface
 | 
			
		||||
	return subcommands.ExitSuccess
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Output the tmeplate of config.toml
 | 
			
		||||
// Output the template of config.toml
 | 
			
		||||
func printConfigToml(ips []string) (err error) {
 | 
			
		||||
	const tomlTempale = `
 | 
			
		||||
[slack]
 | 
			
		||||
hookURL      = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
 | 
			
		||||
channel      = "#channel-name"
 | 
			
		||||
#channel      = "${servername}"
 | 
			
		||||
iconEmoji    = ":ghost:"
 | 
			
		||||
authUser     = "username"
 | 
			
		||||
notifyUsers  = ["@username"]
 | 
			
		||||
	const tomlTemplate = `
 | 
			
		||||
 | 
			
		||||
[mail]
 | 
			
		||||
smtpAddr      = "smtp.gmail.com"
 | 
			
		||||
smtpPort      = "465"
 | 
			
		||||
user          = "username"
 | 
			
		||||
password      = "password"
 | 
			
		||||
from          = "from@address.com"
 | 
			
		||||
to            = ["to@address.com"]
 | 
			
		||||
cc            = ["cc@address.com"]
 | 
			
		||||
subjectPrefix = "[vuls]"
 | 
			
		||||
# https://vuls.io/docs/en/usage-settings.html
 | 
			
		||||
[cveDict]
 | 
			
		||||
type        = "sqlite3"
 | 
			
		||||
sqlite3Path = "/path/to/cve.sqlite3"
 | 
			
		||||
#url        = ""
 | 
			
		||||
 | 
			
		||||
[ovalDict]
 | 
			
		||||
type        = "sqlite3"
 | 
			
		||||
sqlite3Path = "/path/to/oval.sqlite3"
 | 
			
		||||
#url        = ""
 | 
			
		||||
 | 
			
		||||
[gost]
 | 
			
		||||
type        = "sqlite3"
 | 
			
		||||
sqlite3Path = "/path/to/gost.sqlite3"
 | 
			
		||||
#url        = ""
 | 
			
		||||
 | 
			
		||||
[exploit]
 | 
			
		||||
type        = "sqlite3"
 | 
			
		||||
sqlite3Path = "/path/to/go-exploitdb.sqlite3"
 | 
			
		||||
#url        = ""
 | 
			
		||||
 | 
			
		||||
# https://vuls.io/docs/en/usage-settings.html#slack-section
 | 
			
		||||
#[slack]
 | 
			
		||||
#hookURL      = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
 | 
			
		||||
##legacyToken = "xoxp-11111111111-222222222222-3333333333"
 | 
			
		||||
#channel      = "#channel-name"
 | 
			
		||||
##channel     = "${servername}"
 | 
			
		||||
#iconEmoji    = ":ghost:"
 | 
			
		||||
#authUser     = "username"
 | 
			
		||||
#notifyUsers  = ["@username"]
 | 
			
		||||
 | 
			
		||||
# https://vuls.io/docs/en/usage-settings.html#email-section
 | 
			
		||||
#[email]
 | 
			
		||||
#smtpAddr      = "smtp.example.com"
 | 
			
		||||
#smtpPort      = "587"
 | 
			
		||||
#user          = "username"
 | 
			
		||||
#password      = "password"
 | 
			
		||||
#from          = "from@example.com"
 | 
			
		||||
#to            = ["to@example.com"]
 | 
			
		||||
#cc            = ["cc@example.com"]
 | 
			
		||||
#subjectPrefix = "[vuls]"
 | 
			
		||||
 | 
			
		||||
# https://vuls.io/docs/en/usage-settings.html#http-section
 | 
			
		||||
#[http]
 | 
			
		||||
#url = "http://localhost:11234"
 | 
			
		||||
 | 
			
		||||
# https://vuls.io/docs/en/usage-settings.html#syslog-section
 | 
			
		||||
#[syslog]
 | 
			
		||||
#protocol    = "tcp"
 | 
			
		||||
#host        = "localhost"
 | 
			
		||||
#port        = "514"
 | 
			
		||||
#tag         = "vuls"
 | 
			
		||||
#facility    = "local0"
 | 
			
		||||
#severity    = "alert"
 | 
			
		||||
#verbose     = false
 | 
			
		||||
 | 
			
		||||
# https://vuls.io/docs/en/usage-report.html#example-put-results-in-s3-bucket
 | 
			
		||||
#[aws]
 | 
			
		||||
#profile                = "default"
 | 
			
		||||
#region                 = "ap-northeast-1"
 | 
			
		||||
#s3Bucket               = "vuls"
 | 
			
		||||
#s3ResultsDir           = "/path/to/result"
 | 
			
		||||
#s3ServerSideEncryption = "AES256"
 | 
			
		||||
 | 
			
		||||
# https://vuls.io/docs/en/usage-report.html#example-put-results-in-azure-blob-storage<Paste>
 | 
			
		||||
#[azure]
 | 
			
		||||
#accountName   = "default"
 | 
			
		||||
#accountKey    = "xxxxxxxxxxxxxx"
 | 
			
		||||
#containerName = "vuls"
 | 
			
		||||
 | 
			
		||||
# https://vuls.io/docs/en/usage-settings.html#stride-section
 | 
			
		||||
#[stride]
 | 
			
		||||
#hookURL   = "xxxxxxxxxxxxxxx"
 | 
			
		||||
#authToken = "xxxxxxxxxxxxxx"
 | 
			
		||||
 | 
			
		||||
# https://vuls.io/docs/en/usage-settings.html#hipchat-section
 | 
			
		||||
#[hipchat]
 | 
			
		||||
#room      = "vuls"
 | 
			
		||||
#authToken = "xxxxxxxxxxxxxx"
 | 
			
		||||
 | 
			
		||||
# https://vuls.io/docs/en/usage-settings.html#chatwork-section
 | 
			
		||||
#[chatwork]
 | 
			
		||||
#room     = "xxxxxxxxxxx"
 | 
			
		||||
#apiToken = "xxxxxxxxxxxxxxxxxx"
 | 
			
		||||
 | 
			
		||||
# https://vuls.io/docs/en/usage-settings.html#telegram-section
 | 
			
		||||
#[telegram]
 | 
			
		||||
#chatID     = "xxxxxxxxxxx"
 | 
			
		||||
#token = "xxxxxxxxxxxxxxxxxx"
 | 
			
		||||
 | 
			
		||||
# https://vuls.io/docs/en/usage-settings.html#default-section
 | 
			
		||||
[default]
 | 
			
		||||
#port        = "22"
 | 
			
		||||
#user        = "username"
 | 
			
		||||
#keyPath     = "/home/username/.ssh/id_rsa"
 | 
			
		||||
#port               = "22"
 | 
			
		||||
#user               = "username"
 | 
			
		||||
#keyPath            = "/home/username/.ssh/id_rsa"
 | 
			
		||||
#scanMode           = ["fast", "fast-root", "deep", "offline"]
 | 
			
		||||
#cpeNames = [
 | 
			
		||||
#  "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
 | 
			
		||||
#]
 | 
			
		||||
#containers = ["${running}"]
 | 
			
		||||
#optional = [
 | 
			
		||||
#    ["key", "value"],
 | 
			
		||||
#]
 | 
			
		||||
#owaspDCXMLPath     = "/tmp/dependency-check-report.xml"
 | 
			
		||||
#ignoreCves         = ["CVE-2014-6271"]
 | 
			
		||||
#containerType      = "docker" #or "lxd" or "lxc" default: docker
 | 
			
		||||
#containersIncluded = ["${running}"]
 | 
			
		||||
#containersExcluded = ["container_name_a"]
 | 
			
		||||
 | 
			
		||||
# https://vuls.io/docs/en/usage-settings.html#servers-section
 | 
			
		||||
[servers]
 | 
			
		||||
{{- $names:=  .Names}}
 | 
			
		||||
{{range $i, $ip := .IPs}}
 | 
			
		||||
[servers.{{index $names $i}}]
 | 
			
		||||
host         = "{{$ip}}"
 | 
			
		||||
#port        = "22"
 | 
			
		||||
#user        = "root"
 | 
			
		||||
#keyPath     = "/home/username/.ssh/id_rsa"
 | 
			
		||||
#cpeNames = [
 | 
			
		||||
#  "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
 | 
			
		||||
#]
 | 
			
		||||
#containers = ["${running}"]
 | 
			
		||||
#optional = [
 | 
			
		||||
#    ["key", "value"],
 | 
			
		||||
#]
 | 
			
		||||
host                = "{{$ip}}"
 | 
			
		||||
#port               = "22"
 | 
			
		||||
#user               = "root"
 | 
			
		||||
#keyPath            = "/home/username/.ssh/id_rsa"
 | 
			
		||||
#scanMode           = ["fast", "fast-root", "deep", "offline"]
 | 
			
		||||
#type               = "pseudo"
 | 
			
		||||
#memo               = "DB Server"
 | 
			
		||||
#cpeNames           = [ "cpe:/a:rubyonrails:ruby_on_rails:4.2.1" ]
 | 
			
		||||
#owaspDCXMLPath     = "/path/to/dependency-check-report.xml"
 | 
			
		||||
#ignoreCves         = ["CVE-2014-0160"]
 | 
			
		||||
#containerType      = "docker" #or "lxd" or "lxc" default: docker
 | 
			
		||||
#containersIncluded = ["${running}"]
 | 
			
		||||
#containersExcluded = ["container_name_a"]
 | 
			
		||||
 | 
			
		||||
#[servers.{{index $names $i}}.containers.container_name_a]
 | 
			
		||||
#cpeNames       = [ "cpe:/a:rubyonrails:ruby_on_rails:4.2.1" ]
 | 
			
		||||
#owaspDCXMLPath = "/path/to/dependency-check-report.xml"
 | 
			
		||||
#ignoreCves     = ["CVE-2014-0160"]
 | 
			
		||||
 | 
			
		||||
#[servers.{{index $names $i}}.githubs."owner/repo"]
 | 
			
		||||
#token   = "yourToken"
 | 
			
		||||
 | 
			
		||||
#[servers.{{index $names $i}}.wordpress]
 | 
			
		||||
#cmdPath = "/usr/local/bin/wp"
 | 
			
		||||
#osUser = "wordpress"
 | 
			
		||||
#docRoot = "/path/to/DocumentRoot/"
 | 
			
		||||
#wpVulnDBToken = "xxxxTokenxxxx"
 | 
			
		||||
#ignoreInactive = true
 | 
			
		||||
 | 
			
		||||
#[servers.{{index $names $i}}.optional]
 | 
			
		||||
#key = "value1"
 | 
			
		||||
 | 
			
		||||
{{end}}
 | 
			
		||||
 | 
			
		||||
`
 | 
			
		||||
	var tpl *template.Template
 | 
			
		||||
	if tpl, err = template.New("tempalte").Parse(tomlTempale); err != nil {
 | 
			
		||||
	if tpl, err = template.New("template").Parse(tomlTemplate); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -157,7 +255,7 @@ host         = "{{$ip}}"
 | 
			
		||||
	}
 | 
			
		||||
	a.Names = names
 | 
			
		||||
 | 
			
		||||
	fmt.Println("# Create config.toml using below and then ./vuls --config=/path/to/config.toml")
 | 
			
		||||
	fmt.Println("# Create config.toml using below and then ./vuls -config=/path/to/config.toml")
 | 
			
		||||
	if err = tpl.Execute(os.Stdout, a); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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
 | 
			
		||||
@@ -18,28 +18,21 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
package commands
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/net/context"
 | 
			
		||||
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
	c "github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/db"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/report"
 | 
			
		||||
	"github.com/google/subcommands"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// HistoryCmd is Subcommand of list scanned results
 | 
			
		||||
type HistoryCmd struct {
 | 
			
		||||
	debug    bool
 | 
			
		||||
	debugSQL bool
 | 
			
		||||
 | 
			
		||||
	dbpath string
 | 
			
		||||
}
 | 
			
		||||
type HistoryCmd struct{}
 | 
			
		||||
 | 
			
		||||
// Name return subcommand name
 | 
			
		||||
func (*HistoryCmd) Name() string { return "history" }
 | 
			
		||||
@@ -53,56 +46,46 @@ func (*HistoryCmd) Synopsis() string {
 | 
			
		||||
func (*HistoryCmd) Usage() string {
 | 
			
		||||
	return `history:
 | 
			
		||||
	history
 | 
			
		||||
		[-dbpath=/path/to/vuls.sqlite3]
 | 
			
		||||
		[-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")
 | 
			
		||||
	f.BoolVar(&c.Conf.DebugSQL, "debug-sql", false, "SQL debug mode")
 | 
			
		||||
 | 
			
		||||
	wd, _ := os.Getwd()
 | 
			
		||||
	defaultDBPath := filepath.Join(wd, "vuls.sqlite3")
 | 
			
		||||
	f.StringVar(&p.dbpath, "dbpath", defaultDBPath, "/path/to/sqlite3")
 | 
			
		||||
	defaultResultsDir := filepath.Join(wd, "results")
 | 
			
		||||
	f.StringVar(&c.Conf.ResultsDir, "results-dir", defaultResultsDir, "/path/to/results")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Execute execute
 | 
			
		||||
func (p *HistoryCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
 | 
			
		||||
 | 
			
		||||
	c.Conf.DebugSQL = p.debugSQL
 | 
			
		||||
	c.Conf.DBPath = p.dbpath
 | 
			
		||||
 | 
			
		||||
	//  _, err := scanHistories()
 | 
			
		||||
	histories, err := scanHistories()
 | 
			
		||||
	dirs, err := report.ListValidJSONDirs()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logrus.Error("Failed to select scan histories: ", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
	const timeLayout = "2006-01-02 15:04"
 | 
			
		||||
	for _, history := range histories {
 | 
			
		||||
		names := []string{}
 | 
			
		||||
		for _, result := range history.ScanResults {
 | 
			
		||||
			if 0 < len(result.Container.ContainerID) {
 | 
			
		||||
				names = append(names, result.Container.Name)
 | 
			
		||||
			} else {
 | 
			
		||||
				names = append(names, result.ServerName)
 | 
			
		||||
			}
 | 
			
		||||
	for _, d := range dirs {
 | 
			
		||||
		var files []os.FileInfo
 | 
			
		||||
		if files, err = ioutil.ReadDir(d); err != nil {
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Printf("%-3d %s scanned %d servers: %s\n",
 | 
			
		||||
			history.ID,
 | 
			
		||||
			history.ScannedAt.Format(timeLayout),
 | 
			
		||||
			len(history.ScanResults),
 | 
			
		||||
			strings.Join(names, ", "),
 | 
			
		||||
		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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func scanHistories() (histories []models.ScanHistory, err error) {
 | 
			
		||||
	if err := db.OpenDB(); err != nil {
 | 
			
		||||
		return histories, fmt.Errorf(
 | 
			
		||||
			"Failed to open DB. datafile: %s, err: %s", c.Conf.DBPath, err)
 | 
			
		||||
	}
 | 
			
		||||
	histories, err = db.SelectScanHistories()
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,164 +0,0 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package commands
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"flag"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
	c "github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/scan"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/google/subcommands"
 | 
			
		||||
	"golang.org/x/net/context"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// PrepareCmd is Subcommand of host discovery mode
 | 
			
		||||
type PrepareCmd struct {
 | 
			
		||||
	debug      bool
 | 
			
		||||
	configPath string
 | 
			
		||||
 | 
			
		||||
	askSudoPassword bool
 | 
			
		||||
	askKeyPassword  bool
 | 
			
		||||
 | 
			
		||||
	useUnattendedUpgrades bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Name return subcommand name
 | 
			
		||||
func (*PrepareCmd) Name() string { return "prepare" }
 | 
			
		||||
 | 
			
		||||
// Synopsis return synopsis
 | 
			
		||||
func (*PrepareCmd) Synopsis() string {
 | 
			
		||||
	//  return "Install packages Ubuntu: unattended-upgrade, CentOS: yum-plugin-security)"
 | 
			
		||||
	return `Install required packages to scan.
 | 
			
		||||
				CentOS: yum-plugin-security, yum-plugin-changelog
 | 
			
		||||
				Amazon: None
 | 
			
		||||
				RHEL:   TODO
 | 
			
		||||
				Ubuntu: None
 | 
			
		||||
 | 
			
		||||
	`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Usage return usage
 | 
			
		||||
func (*PrepareCmd) Usage() string {
 | 
			
		||||
	return `prepare:
 | 
			
		||||
	prepare
 | 
			
		||||
			[-config=/path/to/config.toml]
 | 
			
		||||
			[-ask-key-password]
 | 
			
		||||
			[-debug]
 | 
			
		||||
 | 
			
		||||
		    [SERVER]...
 | 
			
		||||
`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetFlags set flag
 | 
			
		||||
func (p *PrepareCmd) 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")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(
 | 
			
		||||
		&p.askKeyPassword,
 | 
			
		||||
		"ask-key-password",
 | 
			
		||||
		false,
 | 
			
		||||
		"Ask ssh privatekey password before scanning",
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(
 | 
			
		||||
		&p.askSudoPassword,
 | 
			
		||||
		"ask-sudo-password",
 | 
			
		||||
		false,
 | 
			
		||||
		"[Deprecated] THIS OPTION WAS REMOVED FOR SECURITY REASON. Define NOPASSWD in /etc/sudoers on tareget servers and use SSH key-based authentication",
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(
 | 
			
		||||
		&p.useUnattendedUpgrades,
 | 
			
		||||
		"use-unattended-upgrades",
 | 
			
		||||
		false,
 | 
			
		||||
		"[Deprecated] For Ubuntu, install unattended-upgrades",
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Execute execute
 | 
			
		||||
func (p *PrepareCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
 | 
			
		||||
	var keyPass string
 | 
			
		||||
	var err error
 | 
			
		||||
	if p.askKeyPassword {
 | 
			
		||||
		prompt := "SSH key password: "
 | 
			
		||||
		if keyPass, err = getPasswd(prompt); err != nil {
 | 
			
		||||
			logrus.Error(err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if p.askSudoPassword {
 | 
			
		||||
		logrus.Errorf("[Deprecated] -ask-sudo-password WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on tareget servers and use SSH key-based authentication")
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = c.Load(p.configPath, keyPass)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logrus.Errorf("Error loading %s, %s", p.configPath, err)
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logrus.Infof("Start Preparing (config: %s)", p.configPath)
 | 
			
		||||
	target := make(map[string]c.ServerInfo)
 | 
			
		||||
	for _, arg := range f.Args() {
 | 
			
		||||
		found := false
 | 
			
		||||
		for servername, info := range c.Conf.Servers {
 | 
			
		||||
			if servername == arg {
 | 
			
		||||
				target[servername] = info
 | 
			
		||||
				found = true
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if !found {
 | 
			
		||||
			logrus.Errorf("%s is not in config", arg)
 | 
			
		||||
			return subcommands.ExitUsageError
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if 0 < len(f.Args()) {
 | 
			
		||||
		c.Conf.Servers = target
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.Conf.Debug = p.debug
 | 
			
		||||
	c.Conf.UseUnattendedUpgrades = p.useUnattendedUpgrades
 | 
			
		||||
 | 
			
		||||
	// Set up custom logger
 | 
			
		||||
	logger := util.NewCustomLogger(c.ServerInfo{})
 | 
			
		||||
 | 
			
		||||
	logger.Info("Detecting OS... ")
 | 
			
		||||
	scan.InitServers(logger)
 | 
			
		||||
 | 
			
		||||
	logger.Info("Installing...")
 | 
			
		||||
	if errs := scan.Prepare(); 0 < len(errs) {
 | 
			
		||||
		for _, e := range errs {
 | 
			
		||||
			logger.Errorf("Failed: %s", e)
 | 
			
		||||
		}
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logger.Info("Success")
 | 
			
		||||
	return subcommands.ExitSuccess
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										438
									
								
								commands/report.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,438 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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/exploit"
 | 
			
		||||
	"github.com/future-architect/vuls/gost"
 | 
			
		||||
	"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"
 | 
			
		||||
	"github.com/k0kubun/pp"
 | 
			
		||||
	cvelog "github.com/kotakanbe/go-cve-dictionary/log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ReportCmd is subcommand for reporting
 | 
			
		||||
type ReportCmd struct {
 | 
			
		||||
	configPath  string
 | 
			
		||||
	cveDict     c.GoCveDictConf
 | 
			
		||||
	ovalDict    c.GovalDictConf
 | 
			
		||||
	gostConf    c.GostConf
 | 
			
		||||
	exploitConf c.ExploitConf
 | 
			
		||||
	httpConf    c.HTTPConf
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Name return subcommand name
 | 
			
		||||
func (*ReportCmd) Name() string { return "report" }
 | 
			
		||||
 | 
			
		||||
// Synopsis return synopsis
 | 
			
		||||
func (*ReportCmd) Synopsis() string { return "Reporting" }
 | 
			
		||||
 | 
			
		||||
// Usage return usage
 | 
			
		||||
func (*ReportCmd) Usage() string {
 | 
			
		||||
	return `report:
 | 
			
		||||
	report
 | 
			
		||||
		[-lang=en|ja]
 | 
			
		||||
		[-config=/path/to/config.toml]
 | 
			
		||||
		[-results-dir=/path/to/results]
 | 
			
		||||
		[-log-dir=/path/to/log]
 | 
			
		||||
		[-refresh-cve]
 | 
			
		||||
		[-cvss-over=7]
 | 
			
		||||
		[-diff]
 | 
			
		||||
		[-ignore-unscored-cves]
 | 
			
		||||
		[-ignore-unfixed]
 | 
			
		||||
		[-ignore-github-dismissed]
 | 
			
		||||
		[-to-email]
 | 
			
		||||
		[-to-http]
 | 
			
		||||
		[-to-slack]
 | 
			
		||||
		[-to-stride]
 | 
			
		||||
		[-to-hipchat]
 | 
			
		||||
		[-to-chatwork]
 | 
			
		||||
		[-to-telegram]
 | 
			
		||||
		[-to-localfile]
 | 
			
		||||
		[-to-s3]
 | 
			
		||||
		[-to-azure-blob]
 | 
			
		||||
		[-to-saas]
 | 
			
		||||
		[-format-json]
 | 
			
		||||
		[-format-xml]
 | 
			
		||||
		[-format-one-email]
 | 
			
		||||
		[-format-one-line-text]
 | 
			
		||||
		[-format-list]
 | 
			
		||||
		[-format-full-text]
 | 
			
		||||
		[-gzip]
 | 
			
		||||
		[-uuid]
 | 
			
		||||
		[-http-proxy=http://192.168.0.1:8080]
 | 
			
		||||
		[-debug]
 | 
			
		||||
		[-debug-sql]
 | 
			
		||||
		[-pipe]
 | 
			
		||||
		[-cvedb-type=sqlite3|mysql|postgres|redis|http]
 | 
			
		||||
		[-cvedb-sqlite3-path=/path/to/cve.sqlite3]
 | 
			
		||||
		[-cvedb-url=http://127.0.0.1:1323 or DB connection string]
 | 
			
		||||
		[-ovaldb-type=sqlite3|mysql|redis|http]
 | 
			
		||||
		[-ovaldb-sqlite3-path=/path/to/oval.sqlite3]
 | 
			
		||||
		[-ovaldb-url=http://127.0.0.1:1324 or DB connection string]
 | 
			
		||||
		[-gostdb-type=sqlite3|mysql|redis|http]
 | 
			
		||||
		[-gostdb-sqlite3-path=/path/to/gost.sqlite3]
 | 
			
		||||
		[-gostdb-url=http://127.0.0.1:1325 or DB connection string]
 | 
			
		||||
		[-exploitdb-type=sqlite3|mysql|redis|http]
 | 
			
		||||
		[-exploitdb-sqlite3-path=/path/to/exploitdb.sqlite3]
 | 
			
		||||
		[-exploitdb-url=http://127.0.0.1:1326 or DB connection string]
 | 
			
		||||
		[-http="http://vuls-report-server"]
 | 
			
		||||
 | 
			
		||||
		[RFC3339 datetime format under results dir]
 | 
			
		||||
`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetFlags set flag
 | 
			
		||||
func (p *ReportCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
	f.StringVar(&c.Conf.Lang, "lang", "en", "[en|ja]")
 | 
			
		||||
	f.BoolVar(&c.Conf.Debug, "debug", false, "debug mode")
 | 
			
		||||
	f.BoolVar(&c.Conf.DebugSQL, "debug-sql", false, "SQL debug mode")
 | 
			
		||||
 | 
			
		||||
	wd, _ := os.Getwd()
 | 
			
		||||
	defaultConfPath := filepath.Join(wd, "config.toml")
 | 
			
		||||
	f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
 | 
			
		||||
 | 
			
		||||
	defaultResultsDir := filepath.Join(wd, "results")
 | 
			
		||||
	f.StringVar(&c.Conf.ResultsDir, "results-dir", defaultResultsDir, "/path/to/results")
 | 
			
		||||
 | 
			
		||||
	defaultLogDir := util.GetDefaultLogDir()
 | 
			
		||||
	f.StringVar(&c.Conf.LogDir, "log-dir", defaultLogDir, "/path/to/log")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.RefreshCve, "refresh-cve", false,
 | 
			
		||||
		"Refresh CVE information in JSON file under results dir")
 | 
			
		||||
 | 
			
		||||
	f.Float64Var(&c.Conf.CvssScoreOver, "cvss-over", 0,
 | 
			
		||||
		"-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.Diff, "diff", false,
 | 
			
		||||
		"Difference between previous result and current result ")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.IgnoreUnscoredCves, "ignore-unscored-cves", false,
 | 
			
		||||
		"Don't report the unscored CVEs")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.IgnoreUnfixed, "ignore-unfixed", false,
 | 
			
		||||
		"Don't report the unfixed CVEs")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.IgnoreGitHubDismissed, "ignore-github-dismissed", false,
 | 
			
		||||
		"Don't report the dismissed CVEs on GitHub Security Alerts")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(
 | 
			
		||||
		&c.Conf.HTTPProxy, "http-proxy", "",
 | 
			
		||||
		"http://proxy-url:port (default: empty)")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.FormatJSON, "format-json", false, "JSON format")
 | 
			
		||||
	f.BoolVar(&c.Conf.FormatXML, "format-xml", false, "XML format")
 | 
			
		||||
	f.BoolVar(&c.Conf.FormatOneEMail, "format-one-email", false,
 | 
			
		||||
		"Send all the host report via only one EMail (Specify with -to-email)")
 | 
			
		||||
	f.BoolVar(&c.Conf.FormatOneLineText, "format-one-line-text", false,
 | 
			
		||||
		"One line summary in plain text")
 | 
			
		||||
	f.BoolVar(&c.Conf.FormatList, "format-list", false, "Display as list format")
 | 
			
		||||
	f.BoolVar(&c.Conf.FormatFullText, "format-full-text", false,
 | 
			
		||||
		"Detail report in plain text")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.ToSlack, "to-slack", false, "Send report via Slack")
 | 
			
		||||
	f.BoolVar(&c.Conf.ToStride, "to-stride", false, "Send report via Stride")
 | 
			
		||||
	f.BoolVar(&c.Conf.ToHipChat, "to-hipchat", false, "Send report via hipchat")
 | 
			
		||||
	f.BoolVar(&c.Conf.ToChatWork, "to-chatwork", false, "Send report via chatwork")
 | 
			
		||||
	f.BoolVar(&c.Conf.ToTelegram, "to-telegram", false, "Send report via Telegram")
 | 
			
		||||
	f.BoolVar(&c.Conf.ToEmail, "to-email", false, "Send report via Email")
 | 
			
		||||
	f.BoolVar(&c.Conf.ToSyslog, "to-syslog", false, "Send report via Syslog")
 | 
			
		||||
	f.BoolVar(&c.Conf.ToLocalFile, "to-localfile", false, "Write report to localfile")
 | 
			
		||||
	f.BoolVar(&c.Conf.ToS3, "to-s3", false,
 | 
			
		||||
		"Write report to S3 (bucket/yyyyMMdd_HHmm/servername.json/xml/txt)")
 | 
			
		||||
	f.BoolVar(&c.Conf.ToHTTP, "to-http", false, "Send report via HTTP POST")
 | 
			
		||||
	f.BoolVar(&c.Conf.ToAzureBlob, "to-azure-blob", false,
 | 
			
		||||
		"Write report to Azure Storage blob (container/yyyyMMdd_HHmm/servername.json/xml/txt)")
 | 
			
		||||
	f.BoolVar(&c.Conf.ToSaas, "to-saas", false,
 | 
			
		||||
		"Upload report to Future Vuls(https://vuls.biz/) before report")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.GZIP, "gzip", false, "gzip compression")
 | 
			
		||||
	f.BoolVar(&c.Conf.UUID, "uuid", false,
 | 
			
		||||
		"Auto generate of scan target servers and then write to config.toml and scan result")
 | 
			
		||||
	f.BoolVar(&c.Conf.Pipe, "pipe", false, "Use args passed via PIPE")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(&p.cveDict.Type, "cvedb-type", "",
 | 
			
		||||
		"DB type of go-cve-dictionary (sqlite3, mysql, postgres, redis or http)")
 | 
			
		||||
	f.StringVar(&p.cveDict.SQLite3Path, "cvedb-sqlite3-path", "", "/path/to/sqlite3")
 | 
			
		||||
	f.StringVar(&p.cveDict.URL, "cvedb-url", "",
 | 
			
		||||
		"http://go-cve-dictionary.com:1323 or DB connection string")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(&p.ovalDict.Type, "ovaldb-type", "",
 | 
			
		||||
		"DB type of goval-dictionary (sqlite3, mysql, postgres, redis or http)")
 | 
			
		||||
	f.StringVar(&p.ovalDict.SQLite3Path, "ovaldb-sqlite3-path", "", "/path/to/sqlite3")
 | 
			
		||||
	f.StringVar(&p.ovalDict.URL, "ovaldb-url", "",
 | 
			
		||||
		"http://goval-dictionary.com:1324 or DB connection string")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(&p.gostConf.Type, "gostdb-type", "",
 | 
			
		||||
		"DB type of gost (sqlite3, mysql, postgres, redis or http)")
 | 
			
		||||
	f.StringVar(&p.gostConf.SQLite3Path, "gostdb-sqlite3-path", "", "/path/to/sqlite3")
 | 
			
		||||
	f.StringVar(&p.gostConf.URL, "gostdb-url", "",
 | 
			
		||||
		"http://gost.com:1325 or DB connection string")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(&p.exploitConf.Type, "exploitdb-type", "",
 | 
			
		||||
		"DB type of exploit (sqlite3, mysql, postgres, redis or http)")
 | 
			
		||||
	f.StringVar(&p.exploitConf.SQLite3Path, "exploitdb-sqlite3-path", "", "/path/to/sqlite3")
 | 
			
		||||
	f.StringVar(&p.exploitConf.URL, "exploitdb-url", "",
 | 
			
		||||
		"http://exploit.com:1326 or DB connection string")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(&p.httpConf.URL, "http", "", "-to-http http://vuls-report")
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Execute execute
 | 
			
		||||
func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
 | 
			
		||||
	util.Log = util.NewCustomLogger(c.ServerInfo{})
 | 
			
		||||
	cvelog.SetLogger(c.Conf.LogDir, false, c.Conf.Debug, false)
 | 
			
		||||
 | 
			
		||||
	if err := c.Load(p.configPath, ""); err != nil {
 | 
			
		||||
		util.Log.Errorf("Error loading %s, %+v", p.configPath, err)
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.Conf.CveDict.Overwrite(p.cveDict)
 | 
			
		||||
	c.Conf.OvalDict.Overwrite(p.ovalDict)
 | 
			
		||||
	c.Conf.Gost.Overwrite(p.gostConf)
 | 
			
		||||
	c.Conf.Exploit.Overwrite(p.exploitConf)
 | 
			
		||||
	c.Conf.HTTP.Overwrite(p.httpConf)
 | 
			
		||||
 | 
			
		||||
	var dir string
 | 
			
		||||
	var err error
 | 
			
		||||
	if c.Conf.Diff {
 | 
			
		||||
		dir, err = report.JSONDir([]string{})
 | 
			
		||||
	} else {
 | 
			
		||||
		dir, err = report.JSONDir(f.Args())
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		util.Log.Errorf("Failed to read from JSON: %+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// report
 | 
			
		||||
	reports := []report.ResultWriter{
 | 
			
		||||
		report.StdoutWriter{},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.ToSlack {
 | 
			
		||||
		reports = append(reports, report.SlackWriter{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.ToStride {
 | 
			
		||||
		reports = append(reports, report.StrideWriter{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.ToHipChat {
 | 
			
		||||
		reports = append(reports, report.HipChatWriter{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.ToChatWork {
 | 
			
		||||
		reports = append(reports, report.ChatWorkWriter{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.ToTelegram {
 | 
			
		||||
		reports = append(reports, report.TelegramWriter{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.ToEmail {
 | 
			
		||||
		reports = append(reports, report.EMailWriter{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.ToSyslog {
 | 
			
		||||
		reports = append(reports, report.SyslogWriter{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.ToHTTP {
 | 
			
		||||
		reports = append(reports, report.HTTPRequestWriter{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.ToLocalFile {
 | 
			
		||||
		reports = append(reports, report.LocalFileWriter{
 | 
			
		||||
			CurrentDir: dir,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.ToS3 {
 | 
			
		||||
		if err := report.CheckIfBucketExists(); err != nil {
 | 
			
		||||
			util.Log.Errorf("Check if there is a bucket beforehand: %s, err: %+v",
 | 
			
		||||
				c.Conf.AWS.S3Bucket, err)
 | 
			
		||||
			return subcommands.ExitUsageError
 | 
			
		||||
		}
 | 
			
		||||
		reports = append(reports, report.S3Writer{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.ToAzureBlob {
 | 
			
		||||
		if len(c.Conf.Azure.AccountName) == 0 {
 | 
			
		||||
			c.Conf.Azure.AccountName = os.Getenv("AZURE_STORAGE_ACCOUNT")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(c.Conf.Azure.AccountKey) == 0 {
 | 
			
		||||
			c.Conf.Azure.AccountKey = os.Getenv("AZURE_STORAGE_ACCESS_KEY")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(c.Conf.Azure.ContainerName) == 0 {
 | 
			
		||||
			util.Log.Error("Azure storage container name is required with -azure-container option")
 | 
			
		||||
			return subcommands.ExitUsageError
 | 
			
		||||
		}
 | 
			
		||||
		if err := report.CheckIfAzureContainerExists(); err != nil {
 | 
			
		||||
			util.Log.Errorf("Check if there is a container beforehand: %s, err: %+v",
 | 
			
		||||
				c.Conf.Azure.ContainerName, err)
 | 
			
		||||
			return subcommands.ExitUsageError
 | 
			
		||||
		}
 | 
			
		||||
		reports = append(reports, report.AzureBlobWriter{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.ToSaas {
 | 
			
		||||
		if !c.Conf.UUID {
 | 
			
		||||
			util.Log.Errorf("If you use the -to-saas option, you need to enable the uuid option")
 | 
			
		||||
			return subcommands.ExitUsageError
 | 
			
		||||
		}
 | 
			
		||||
		reports = append(reports, report.SaasWriter{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !(c.Conf.FormatJSON || c.Conf.FormatOneLineText ||
 | 
			
		||||
		c.Conf.FormatList || c.Conf.FormatFullText || c.Conf.FormatXML) {
 | 
			
		||||
		c.Conf.FormatList = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	util.Log.Info("Validating config...")
 | 
			
		||||
	if !c.Conf.ValidateOnReport() {
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var loaded models.ScanResults
 | 
			
		||||
	if loaded, err = report.LoadScanResults(dir); err != nil {
 | 
			
		||||
		util.Log.Error(err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
	util.Log.Infof("Loaded: %s", dir)
 | 
			
		||||
 | 
			
		||||
	var res models.ScanResults
 | 
			
		||||
	hasError := false
 | 
			
		||||
	for _, r := range loaded {
 | 
			
		||||
		if len(r.Errors) == 0 {
 | 
			
		||||
			res = append(res, r)
 | 
			
		||||
		} else {
 | 
			
		||||
			util.Log.Errorf("Ignored since errors occurred during scanning: %s, err: %v",
 | 
			
		||||
				r.ServerName, r.Errors)
 | 
			
		||||
			hasError = true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(res) == 0 {
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, r := range res {
 | 
			
		||||
		util.Log.Debugf("%s: %s",
 | 
			
		||||
			r.ServerInfo(),
 | 
			
		||||
			pp.Sprintf("%s", c.Conf.Servers[r.ServerName]))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.UUID {
 | 
			
		||||
		// Ensure UUIDs of scan target servers in config.toml
 | 
			
		||||
		if err := report.EnsureUUIDs(p.configPath, res); err != nil {
 | 
			
		||||
			util.Log.Errorf("Failed to ensure UUIDs. err: %+v", err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !c.Conf.ToSaas {
 | 
			
		||||
		util.Log.Info("Validating db config...")
 | 
			
		||||
		if !c.Conf.ValidateOnReportDB() {
 | 
			
		||||
			return subcommands.ExitUsageError
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if c.Conf.CveDict.URL != "" {
 | 
			
		||||
			if err := report.CveClient.CheckHealth(); err != nil {
 | 
			
		||||
				util.Log.Errorf("CVE HTTP server is not running. err: %+v", err)
 | 
			
		||||
				util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with `-cvedb-type=sqlite3 -cvedb-sqlite3-path` option instead of -cvedb-url")
 | 
			
		||||
				return subcommands.ExitFailure
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if c.Conf.OvalDict.URL != "" {
 | 
			
		||||
			err := oval.Base{}.CheckHTTPHealth()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				util.Log.Errorf("OVAL HTTP server is not running. err: %+v", err)
 | 
			
		||||
				util.Log.Errorf("Run goval-dictionary as server mode before reporting or run with `-ovaldb-type=sqlite3 -ovaldb-sqlite3-path` option instead of -ovaldb-url")
 | 
			
		||||
				return subcommands.ExitFailure
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if c.Conf.Gost.URL != "" {
 | 
			
		||||
			util.Log.Infof("gost: %s", c.Conf.Gost.URL)
 | 
			
		||||
			err := gost.Base{}.CheckHTTPHealth()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				util.Log.Errorf("gost HTTP server is not running. err: %+v", err)
 | 
			
		||||
				util.Log.Errorf("Run gost as server mode before reporting or run with `-gostdb-type=sqlite3 -gostdb-sqlite3-path` option instead of -gostdb-url")
 | 
			
		||||
				return subcommands.ExitFailure
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if c.Conf.Exploit.URL != "" {
 | 
			
		||||
			err := exploit.CheckHTTPHealth()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				util.Log.Errorf("exploit HTTP server is not running. err: %+v", err)
 | 
			
		||||
				util.Log.Errorf("Run go-exploitdb as server mode before reporting")
 | 
			
		||||
				return subcommands.ExitFailure
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		dbclient, locked, err := report.NewDBClient(report.DBClientConf{
 | 
			
		||||
			CveDictCnf:  c.Conf.CveDict,
 | 
			
		||||
			OvalDictCnf: c.Conf.OvalDict,
 | 
			
		||||
			GostCnf:     c.Conf.Gost,
 | 
			
		||||
			ExploitCnf:  c.Conf.Exploit,
 | 
			
		||||
			DebugSQL:    c.Conf.DebugSQL,
 | 
			
		||||
		})
 | 
			
		||||
		if locked {
 | 
			
		||||
			util.Log.Errorf("SQLite3 is locked. Close other DB connections and try again. err: %+v", err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Errorf("Failed to init DB Clients. err: %+v", err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
		defer dbclient.CloseDB()
 | 
			
		||||
 | 
			
		||||
		if res, err = report.FillCveInfos(*dbclient, res, dir); err != nil {
 | 
			
		||||
			util.Log.Errorf("%+v", err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, w := range reports {
 | 
			
		||||
		if err := w.Write(res...); err != nil {
 | 
			
		||||
			util.Log.Errorf("Failed to report. err: %+v", err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if hasError {
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return subcommands.ExitSuccess
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										385
									
								
								commands/scan.go
									
									
									
									
									
								
							
							
						
						@@ -1,5 +1,5 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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
 | 
			
		||||
@@ -18,6 +18,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
package commands
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
@@ -25,56 +26,19 @@ import (
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
	c "github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/cveapi"
 | 
			
		||||
	"github.com/future-architect/vuls/db"
 | 
			
		||||
	"github.com/future-architect/vuls/report"
 | 
			
		||||
	"github.com/future-architect/vuls/scan"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/google/subcommands"
 | 
			
		||||
	"golang.org/x/net/context"
 | 
			
		||||
	"github.com/k0kubun/pp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ScanCmd is Subcommand of host discovery mode
 | 
			
		||||
type ScanCmd struct {
 | 
			
		||||
	lang     string
 | 
			
		||||
	debug    bool
 | 
			
		||||
	debugSQL bool
 | 
			
		||||
 | 
			
		||||
	configPath string
 | 
			
		||||
 | 
			
		||||
	dbpath           string
 | 
			
		||||
	cvedbpath        string
 | 
			
		||||
	cveDictionaryURL string
 | 
			
		||||
 | 
			
		||||
	cvssScoreOver      float64
 | 
			
		||||
	ignoreUnscoredCves bool
 | 
			
		||||
 | 
			
		||||
	httpProxy       string
 | 
			
		||||
	askSudoPassword bool
 | 
			
		||||
	askKeyPassword  bool
 | 
			
		||||
 | 
			
		||||
	// reporting
 | 
			
		||||
	reportSlack     bool
 | 
			
		||||
	reportMail      bool
 | 
			
		||||
	reportJSON      bool
 | 
			
		||||
	reportText      bool
 | 
			
		||||
	reportS3        bool
 | 
			
		||||
	reportAzureBlob bool
 | 
			
		||||
 | 
			
		||||
	awsProfile  string
 | 
			
		||||
	awsS3Bucket string
 | 
			
		||||
	awsRegion   string
 | 
			
		||||
 | 
			
		||||
	azureAccount   string
 | 
			
		||||
	azureKey       string
 | 
			
		||||
	azureContainer string
 | 
			
		||||
 | 
			
		||||
	useYumPluginSecurity  bool
 | 
			
		||||
	useUnattendedUpgrades bool
 | 
			
		||||
 | 
			
		||||
	sshExternal bool
 | 
			
		||||
	configPath     string
 | 
			
		||||
	askKeyPassword bool
 | 
			
		||||
	timeoutSec     int
 | 
			
		||||
	scanTimeoutSec int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Name return subcommand name
 | 
			
		||||
@@ -87,30 +51,21 @@ func (*ScanCmd) Synopsis() string { return "Scan vulnerabilities" }
 | 
			
		||||
func (*ScanCmd) Usage() string {
 | 
			
		||||
	return `scan:
 | 
			
		||||
	scan
 | 
			
		||||
		[-lang=en|ja]
 | 
			
		||||
		[-config=/path/to/config.toml]
 | 
			
		||||
		[-dbpath=/path/to/vuls.sqlite3]
 | 
			
		||||
		[-cve-dictionary-dbpath=/path/to/cve.sqlite3]
 | 
			
		||||
		[-cve-dictionary-url=http://127.0.0.1:1323]
 | 
			
		||||
		[-cvss-over=7]
 | 
			
		||||
		[-ignore-unscored-cves]
 | 
			
		||||
		[-ssh-external]
 | 
			
		||||
		[-report-azure-blob]
 | 
			
		||||
		[-report-json]
 | 
			
		||||
		[-report-mail]
 | 
			
		||||
		[-report-s3]
 | 
			
		||||
		[-report-slack]
 | 
			
		||||
		[-report-text]
 | 
			
		||||
		[-results-dir=/path/to/results]
 | 
			
		||||
		[-log-dir=/path/to/log]
 | 
			
		||||
		[-cachedb-path=/path/to/cache.db]
 | 
			
		||||
		[-ssh-native-insecure]
 | 
			
		||||
		[-ssh-config]
 | 
			
		||||
		[-containers-only]
 | 
			
		||||
		[-skip-broken]
 | 
			
		||||
		[-http-proxy=http://192.168.0.1:8080]
 | 
			
		||||
		[-ask-key-password]
 | 
			
		||||
		[-timeout=300]
 | 
			
		||||
		[-timeout-scan=7200]
 | 
			
		||||
		[-debug]
 | 
			
		||||
		[-debug-sql]
 | 
			
		||||
		[-aws-profile=default]
 | 
			
		||||
		[-aws-region=us-west-2]
 | 
			
		||||
		[-aws-s3-bucket=bucket_name]
 | 
			
		||||
		[-azure-account=accout]
 | 
			
		||||
		[-azure-key=key]
 | 
			
		||||
		[-azure-container=container]
 | 
			
		||||
		[-pipe]
 | 
			
		||||
		[-vvv]
 | 
			
		||||
 | 
			
		||||
		[SERVER]...
 | 
			
		||||
`
 | 
			
		||||
@@ -118,162 +73,102 @@ func (*ScanCmd) Usage() string {
 | 
			
		||||
 | 
			
		||||
// SetFlags set flag
 | 
			
		||||
func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
	f.StringVar(&p.lang, "lang", "en", "[en|ja]")
 | 
			
		||||
	f.BoolVar(&p.debug, "debug", false, "debug mode")
 | 
			
		||||
	f.BoolVar(&p.debugSQL, "debug-sql", false, "SQL debug mode")
 | 
			
		||||
	f.BoolVar(&c.Conf.Debug, "debug", false, "debug mode")
 | 
			
		||||
 | 
			
		||||
	wd, _ := os.Getwd()
 | 
			
		||||
 | 
			
		||||
	defaultConfPath := filepath.Join(wd, "config.toml")
 | 
			
		||||
	f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
 | 
			
		||||
 | 
			
		||||
	defaultDBPath := filepath.Join(wd, "vuls.sqlite3")
 | 
			
		||||
	f.StringVar(&p.dbpath, "dbpath", defaultDBPath, "/path/to/sqlite3")
 | 
			
		||||
	defaultResultsDir := filepath.Join(wd, "results")
 | 
			
		||||
	f.StringVar(&c.Conf.ResultsDir, "results-dir", defaultResultsDir, "/path/to/results")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(
 | 
			
		||||
		&p.cvedbpath,
 | 
			
		||||
		"cve-dictionary-dbpath",
 | 
			
		||||
		"",
 | 
			
		||||
		"/path/to/sqlite3 (For get cve detail from cve.sqlite3)")
 | 
			
		||||
	defaultLogDir := util.GetDefaultLogDir()
 | 
			
		||||
	f.StringVar(&c.Conf.LogDir, "log-dir", defaultLogDir, "/path/to/log")
 | 
			
		||||
 | 
			
		||||
	defaultURL := "http://127.0.0.1:1323"
 | 
			
		||||
	f.StringVar(
 | 
			
		||||
		&p.cveDictionaryURL,
 | 
			
		||||
		"cve-dictionary-url",
 | 
			
		||||
		defaultURL,
 | 
			
		||||
		"http://CVE.Dictionary")
 | 
			
		||||
	defaultCacheDBPath := filepath.Join(wd, "cache.db")
 | 
			
		||||
	f.StringVar(&c.Conf.CacheDBPath, "cachedb-path", defaultCacheDBPath,
 | 
			
		||||
		"/path/to/cache.db (local cache of changelog for Ubuntu/Debian)")
 | 
			
		||||
 | 
			
		||||
	f.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(&c.Conf.SSHNative, "ssh-native-insecure", false,
 | 
			
		||||
		"Use Native Go implementation of SSH. Default: Use the external command")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(
 | 
			
		||||
		&p.ignoreUnscoredCves,
 | 
			
		||||
		"ignore-unscored-cves",
 | 
			
		||||
		false,
 | 
			
		||||
		"Don't report the unscored CVEs")
 | 
			
		||||
	f.BoolVar(&c.Conf.SSHConfig, "ssh-config", false,
 | 
			
		||||
		"Use SSH options specified in ssh_config preferentially")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(
 | 
			
		||||
		&p.sshExternal,
 | 
			
		||||
		"ssh-external",
 | 
			
		||||
		false,
 | 
			
		||||
		"Use external ssh command. Default: Use the Go native implementation")
 | 
			
		||||
	f.BoolVar(&c.Conf.ContainersOnly, "containers-only", false,
 | 
			
		||||
		"Scan running containers only. Default: Scan both of hosts and running containers")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(
 | 
			
		||||
		&p.httpProxy,
 | 
			
		||||
		"http-proxy",
 | 
			
		||||
		"",
 | 
			
		||||
		"http://proxy-url:port (default: empty)",
 | 
			
		||||
	)
 | 
			
		||||
	f.BoolVar(&c.Conf.ImagesOnly, "images-only", false,
 | 
			
		||||
		"Scan container images only. Default: Scan both of hosts and images")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&p.reportSlack, "report-slack", false, "Send report via Slack")
 | 
			
		||||
	f.BoolVar(&p.reportMail, "report-mail", false, "Send report via Email")
 | 
			
		||||
	f.BoolVar(&p.reportJSON,
 | 
			
		||||
		"report-json",
 | 
			
		||||
		false,
 | 
			
		||||
		fmt.Sprintf("Write report to JSON files (%s/results/current)", wd),
 | 
			
		||||
	)
 | 
			
		||||
	f.BoolVar(&p.reportText,
 | 
			
		||||
		"report-text",
 | 
			
		||||
		false,
 | 
			
		||||
		fmt.Sprintf("Write report to text files (%s/results/current)", wd),
 | 
			
		||||
	)
 | 
			
		||||
	f.BoolVar(&c.Conf.SkipBroken, "skip-broken", false,
 | 
			
		||||
		"[For CentOS] yum update changelog with --skip-broken option")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&p.reportS3,
 | 
			
		||||
		"report-s3",
 | 
			
		||||
		false,
 | 
			
		||||
		"Write report to S3 (bucket/yyyyMMdd_HHmm/servername.json)",
 | 
			
		||||
	)
 | 
			
		||||
	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(&c.Conf.HTTPProxy, "http-proxy", "",
 | 
			
		||||
		"http://proxy-url:port (default: empty)")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&p.reportAzureBlob,
 | 
			
		||||
		"report-azure-blob",
 | 
			
		||||
		false,
 | 
			
		||||
		"Write report to S3 (container/yyyyMMdd_HHmm/servername.json)",
 | 
			
		||||
	)
 | 
			
		||||
	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.askKeyPassword,
 | 
			
		||||
		"ask-key-password",
 | 
			
		||||
		false,
 | 
			
		||||
	f.BoolVar(&p.askKeyPassword, "ask-key-password", false,
 | 
			
		||||
		"Ask ssh privatekey password before scanning",
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(
 | 
			
		||||
		&p.askSudoPassword,
 | 
			
		||||
		"ask-sudo-password",
 | 
			
		||||
		false,
 | 
			
		||||
		"[Deprecated] THIS OPTION WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on tareget servers and use SSH key-based authentication",
 | 
			
		||||
	f.BoolVar(&c.Conf.Pipe, "pipe", false, "Use stdin via PIPE")
 | 
			
		||||
	f.BoolVar(&c.Conf.Vvv, "vvv", false, "ssh -vvv")
 | 
			
		||||
 | 
			
		||||
	f.IntVar(&p.timeoutSec, "timeout", 5*60,
 | 
			
		||||
		"Number of seconds for processing other than scan",
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(
 | 
			
		||||
		&p.useYumPluginSecurity,
 | 
			
		||||
		"use-yum-plugin-security",
 | 
			
		||||
		false,
 | 
			
		||||
		"[Deprecated] For CentOS 5. Scan by yum-plugin-security or not (use yum check-update by default)",
 | 
			
		||||
	f.IntVar(&p.scanTimeoutSec, "timeout-scan", 120*60,
 | 
			
		||||
		"Number of seconds for scanning vulnerabilities for all servers",
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(
 | 
			
		||||
		&p.useUnattendedUpgrades,
 | 
			
		||||
		"use-unattended-upgrades",
 | 
			
		||||
		false,
 | 
			
		||||
		"[Deprecated] For Ubuntu. Scan by unattended-upgrades or not (use apt-get upgrade --dry-run by default)",
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Execute execute
 | 
			
		||||
func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
 | 
			
		||||
	// Setup Logger
 | 
			
		||||
	util.Log = util.NewCustomLogger(c.ServerInfo{})
 | 
			
		||||
 | 
			
		||||
	if err := mkdirDotVuls(); err != nil {
 | 
			
		||||
		util.Log.Errorf("Failed to create .vuls. err: %+v", err)
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var keyPass string
 | 
			
		||||
	var err error
 | 
			
		||||
	if p.askKeyPassword {
 | 
			
		||||
		prompt := "SSH key password: "
 | 
			
		||||
		if keyPass, err = getPasswd(prompt); err != nil {
 | 
			
		||||
			logrus.Error(err)
 | 
			
		||||
			util.Log.Error(err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if p.askSudoPassword {
 | 
			
		||||
		logrus.Errorf("[Deprecated] -ask-sudo-password WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on tareget servers and use SSH key-based authentication")
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = c.Load(p.configPath, keyPass)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logrus.Errorf("Error loading %s, %s", p.configPath, err)
 | 
			
		||||
		msg := []string{
 | 
			
		||||
			fmt.Sprintf("Error loading %s", p.configPath),
 | 
			
		||||
			"If you update Vuls and get this error, there may be incompatible changes in config.toml",
 | 
			
		||||
			"Please check config.toml template : https://vuls.io/docs/en/usage-settings.html",
 | 
			
		||||
		}
 | 
			
		||||
		util.Log.Errorf("%s\n%+v", strings.Join(msg, "\n"), err)
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logrus.Info("Start scanning")
 | 
			
		||||
	logrus.Infof("config: %s", p.configPath)
 | 
			
		||||
	if p.cvedbpath != "" {
 | 
			
		||||
		logrus.Infof("cve-dictionary: %s", p.cvedbpath)
 | 
			
		||||
	} else {
 | 
			
		||||
		logrus.Infof("cve-dictionary: %s", p.cveDictionaryURL)
 | 
			
		||||
	}
 | 
			
		||||
	util.Log.Info("Start scanning")
 | 
			
		||||
	util.Log.Infof("config: %s", p.configPath)
 | 
			
		||||
 | 
			
		||||
	var servernames []string
 | 
			
		||||
	if 0 < len(f.Args()) {
 | 
			
		||||
		servernames = f.Args()
 | 
			
		||||
	} else {
 | 
			
		||||
		stat, _ := os.Stdin.Stat()
 | 
			
		||||
		if (stat.Mode() & os.ModeCharDevice) == 0 {
 | 
			
		||||
			bytes, err := ioutil.ReadAll(os.Stdin)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				logrus.Errorf("Failed to read stdin: %s", err)
 | 
			
		||||
				return subcommands.ExitFailure
 | 
			
		||||
			}
 | 
			
		||||
			fields := strings.Fields(string(bytes))
 | 
			
		||||
			if 0 < len(fields) {
 | 
			
		||||
				servernames = fields
 | 
			
		||||
			}
 | 
			
		||||
	} else if c.Conf.Pipe {
 | 
			
		||||
		bytes, err := ioutil.ReadAll(os.Stdin)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Errorf("Failed to read stdin. err: %+v", err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
		fields := strings.Fields(string(bytes))
 | 
			
		||||
		if 0 < len(fields) {
 | 
			
		||||
			servernames = fields
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -288,143 +183,43 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if !found {
 | 
			
		||||
			logrus.Errorf("%s is not in config", arg)
 | 
			
		||||
			util.Log.Errorf("%s is not in config", arg)
 | 
			
		||||
			return subcommands.ExitUsageError
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if 0 < len(servernames) {
 | 
			
		||||
		c.Conf.Servers = target
 | 
			
		||||
	}
 | 
			
		||||
	util.Log.Debugf("%s", pp.Sprintf("%v", target))
 | 
			
		||||
 | 
			
		||||
	c.Conf.Lang = p.lang
 | 
			
		||||
	c.Conf.Debug = p.debug
 | 
			
		||||
	c.Conf.DebugSQL = p.debugSQL
 | 
			
		||||
 | 
			
		||||
	// logger
 | 
			
		||||
	Log := util.NewCustomLogger(c.ServerInfo{})
 | 
			
		||||
 | 
			
		||||
	// report
 | 
			
		||||
	reports := []report.ResultWriter{
 | 
			
		||||
		report.StdoutWriter{},
 | 
			
		||||
		report.LogrusWriter{},
 | 
			
		||||
	}
 | 
			
		||||
	if p.reportSlack {
 | 
			
		||||
		reports = append(reports, report.SlackWriter{})
 | 
			
		||||
	}
 | 
			
		||||
	if p.reportMail {
 | 
			
		||||
		reports = append(reports, report.MailWriter{})
 | 
			
		||||
	}
 | 
			
		||||
	if p.reportJSON {
 | 
			
		||||
		reports = append(reports, report.JSONWriter{})
 | 
			
		||||
	}
 | 
			
		||||
	if p.reportText {
 | 
			
		||||
		reports = append(reports, report.TextFileWriter{})
 | 
			
		||||
	}
 | 
			
		||||
	if p.reportS3 {
 | 
			
		||||
		c.Conf.AwsRegion = p.awsRegion
 | 
			
		||||
		c.Conf.AwsProfile = p.awsProfile
 | 
			
		||||
		c.Conf.S3Bucket = p.awsS3Bucket
 | 
			
		||||
		if err := report.CheckIfBucketExists(); err != nil {
 | 
			
		||||
			Log.Errorf("Failed to access to the S3 bucket. err: %s", err)
 | 
			
		||||
			Log.Error("Ensure the bucket or check AWS config before scanning")
 | 
			
		||||
			return subcommands.ExitUsageError
 | 
			
		||||
		}
 | 
			
		||||
		reports = append(reports, report.S3Writer{})
 | 
			
		||||
	}
 | 
			
		||||
	if p.reportAzureBlob {
 | 
			
		||||
		c.Conf.AzureAccount = p.azureAccount
 | 
			
		||||
		if c.Conf.AzureAccount == "" {
 | 
			
		||||
			c.Conf.AzureAccount = os.Getenv("AZURE_STORAGE_ACCOUNT")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		c.Conf.AzureKey = p.azureKey
 | 
			
		||||
		if c.Conf.AzureKey == "" {
 | 
			
		||||
			c.Conf.AzureKey = os.Getenv("AZURE_STORAGE_ACCESS_KEY")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		c.Conf.AzureContainer = p.azureContainer
 | 
			
		||||
		if c.Conf.AzureContainer == "" {
 | 
			
		||||
			Log.Error("Azure storage container name is requied with --azure-container option")
 | 
			
		||||
			return subcommands.ExitUsageError
 | 
			
		||||
		}
 | 
			
		||||
		if err := report.CheckIfAzureContainerExists(); err != nil {
 | 
			
		||||
			Log.Errorf("Failed to access to the Azure Blob container. err: %s", err)
 | 
			
		||||
			Log.Error("Ensure the container or check Azure config before scanning")
 | 
			
		||||
			return subcommands.ExitUsageError
 | 
			
		||||
		}
 | 
			
		||||
		reports = append(reports, report.AzureBlobWriter{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.Conf.DBPath = p.dbpath
 | 
			
		||||
	c.Conf.CveDBPath = p.cvedbpath
 | 
			
		||||
	c.Conf.CveDictionaryURL = p.cveDictionaryURL
 | 
			
		||||
	c.Conf.CvssScoreOver = p.cvssScoreOver
 | 
			
		||||
	c.Conf.IgnoreUnscoredCves = p.ignoreUnscoredCves
 | 
			
		||||
	c.Conf.SSHExternal = p.sshExternal
 | 
			
		||||
	c.Conf.HTTPProxy = p.httpProxy
 | 
			
		||||
	c.Conf.UseYumPluginSecurity = p.useYumPluginSecurity
 | 
			
		||||
	c.Conf.UseUnattendedUpgrades = p.useUnattendedUpgrades
 | 
			
		||||
 | 
			
		||||
	Log.Info("Validating Config...")
 | 
			
		||||
	if !c.Conf.Validate() {
 | 
			
		||||
	util.Log.Info("Validating config...")
 | 
			
		||||
	if !c.Conf.ValidateOnScan() {
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ok, err := cveapi.CveClient.CheckHealth(); !ok {
 | 
			
		||||
		Log.Errorf("CVE HTTP server is not running. err: %s", err)
 | 
			
		||||
		Log.Errorf("Run go-cve-dictionary as server mode or specify -cve-dictionary-dbpath option")
 | 
			
		||||
	util.Log.Info("Detecting Server/Container OS... ")
 | 
			
		||||
	if err := scan.InitServers(p.timeoutSec); err != nil {
 | 
			
		||||
		util.Log.Errorf("Failed to init servers: %+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Log.Info("Detecting Server/Contianer OS... ")
 | 
			
		||||
	scan.InitServers(Log)
 | 
			
		||||
 | 
			
		||||
	Log.Info("Checking sudo configuration... ")
 | 
			
		||||
	if err := scan.CheckIfSudoNoPasswd(Log); err != nil {
 | 
			
		||||
		Log.Errorf("Failed to sudo with nopassword via SSH. Define NOPASSWD in /etc/sudoers on target servers")
 | 
			
		||||
	util.Log.Info("Checking Scan Modes... ")
 | 
			
		||||
	if err := scan.CheckScanModes(); err != nil {
 | 
			
		||||
		util.Log.Errorf("Fix config.toml. err: %+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Log.Info("Detecting Platforms... ")
 | 
			
		||||
	scan.DetectPlatforms(Log)
 | 
			
		||||
	util.Log.Info("Detecting Platforms... ")
 | 
			
		||||
	scan.DetectPlatforms(p.timeoutSec)
 | 
			
		||||
 | 
			
		||||
	Log.Info("Scanning vulnerabilities... ")
 | 
			
		||||
	if errs := scan.Scan(); 0 < len(errs) {
 | 
			
		||||
		for _, e := range errs {
 | 
			
		||||
			Log.Errorf("Failed to scan. err: %s", e)
 | 
			
		||||
		}
 | 
			
		||||
	util.Log.Info("Scanning vulnerabilities... ")
 | 
			
		||||
	if err := scan.Scan(p.scanTimeoutSec); err != nil {
 | 
			
		||||
		util.Log.Errorf("Failed to scan. err: %+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	scanResults, err := scan.GetScanResults()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		Log.Fatal(err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Log.Info("Insert to DB...")
 | 
			
		||||
	if err := db.OpenDB(); err != nil {
 | 
			
		||||
		Log.Errorf("Failed to open DB. datafile: %s, err: %s", c.Conf.DBPath, err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
	if err := db.MigrateDB(); err != nil {
 | 
			
		||||
		Log.Errorf("Failed to migrate. err: %s", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := db.Insert(scanResults); err != nil {
 | 
			
		||||
		Log.Fatalf("Failed to insert. dbpath: %s, err: %s", c.Conf.DBPath, err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Log.Info("Reporting...")
 | 
			
		||||
	filtered := scanResults.FilterByCvssOver()
 | 
			
		||||
	for _, w := range reports {
 | 
			
		||||
		if err := w.Write(filtered); err != nil {
 | 
			
		||||
			Log.Fatalf("Failed to report, 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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										240
									
								
								commands/server.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,240 @@
 | 
			
		||||
/* 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"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	// "github.com/future-architect/vuls/Server"
 | 
			
		||||
 | 
			
		||||
	c "github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/exploit"
 | 
			
		||||
	"github.com/future-architect/vuls/gost"
 | 
			
		||||
	"github.com/future-architect/vuls/oval"
 | 
			
		||||
	"github.com/future-architect/vuls/report"
 | 
			
		||||
	"github.com/future-architect/vuls/server"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/google/subcommands"
 | 
			
		||||
	cvelog "github.com/kotakanbe/go-cve-dictionary/log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ServerCmd is subcommand for server
 | 
			
		||||
type ServerCmd struct {
 | 
			
		||||
	configPath  string
 | 
			
		||||
	listen      string
 | 
			
		||||
	cveDict     c.GoCveDictConf
 | 
			
		||||
	ovalDict    c.GovalDictConf
 | 
			
		||||
	gostConf    c.GostConf
 | 
			
		||||
	exploitConf c.ExploitConf
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Name return subcommand name
 | 
			
		||||
func (*ServerCmd) Name() string { return "server" }
 | 
			
		||||
 | 
			
		||||
// Synopsis return synopsis
 | 
			
		||||
func (*ServerCmd) Synopsis() string { return "Server" }
 | 
			
		||||
 | 
			
		||||
// Usage return usage
 | 
			
		||||
func (*ServerCmd) Usage() string {
 | 
			
		||||
	return `Server:
 | 
			
		||||
	Server
 | 
			
		||||
		[-lang=en|ja]
 | 
			
		||||
		[-config=/path/to/config.toml]
 | 
			
		||||
		[-log-dir=/path/to/log]
 | 
			
		||||
		[-cvss-over=7]
 | 
			
		||||
		[-ignore-unscored-cves]
 | 
			
		||||
		[-ignore-unfixed]
 | 
			
		||||
		[-to-localfile]
 | 
			
		||||
		[-format-json]
 | 
			
		||||
		[-http-proxy=http://192.168.0.1:8080]
 | 
			
		||||
		[-debug]
 | 
			
		||||
		[-debug-sql]
 | 
			
		||||
		[-listen=localhost:5515]
 | 
			
		||||
		[-cvedb-type=sqlite3|mysql|postgres|redis|http]
 | 
			
		||||
		[-cvedb-sqlite3-path=/path/to/cve.sqlite3]
 | 
			
		||||
		[-cvedb-url=http://127.0.0.1:1323 or DB connection string]
 | 
			
		||||
		[-ovaldb-type=sqlite3|mysql|redis|http]
 | 
			
		||||
		[-ovaldb-sqlite3-path=/path/to/oval.sqlite3]
 | 
			
		||||
		[-ovaldb-url=http://127.0.0.1:1324 or DB connection string]
 | 
			
		||||
		[-gostdb-type=sqlite3|mysql|redis|http]
 | 
			
		||||
		[-gostdb-sqlite3-path=/path/to/gost.sqlite3]
 | 
			
		||||
		[-gostdb-url=http://127.0.0.1:1325 or DB connection string]
 | 
			
		||||
		[-exploitdb-type=sqlite3|mysql|redis|http]
 | 
			
		||||
		[-exploitdb-sqlite3-path=/path/to/exploitdb.sqlite3]
 | 
			
		||||
		[-exploitdb-url=http://127.0.0.1:1326 or DB connection string]
 | 
			
		||||
 | 
			
		||||
		[RFC3339 datetime format under results dir]
 | 
			
		||||
`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetFlags set flag
 | 
			
		||||
func (p *ServerCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
	f.StringVar(&c.Conf.Lang, "lang", "en", "[en|ja]")
 | 
			
		||||
	f.BoolVar(&c.Conf.Debug, "debug", false, "debug mode")
 | 
			
		||||
	f.BoolVar(&c.Conf.DebugSQL, "debug-sql", false, "SQL debug mode")
 | 
			
		||||
 | 
			
		||||
	wd, _ := os.Getwd()
 | 
			
		||||
	f.StringVar(&p.configPath, "config", "", "/path/to/toml")
 | 
			
		||||
 | 
			
		||||
	defaultResultsDir := filepath.Join(wd, "results")
 | 
			
		||||
	f.StringVar(&c.Conf.ResultsDir, "results-dir", defaultResultsDir, "/path/to/results")
 | 
			
		||||
 | 
			
		||||
	defaultLogDir := util.GetDefaultLogDir()
 | 
			
		||||
	f.StringVar(&c.Conf.LogDir, "log-dir", defaultLogDir, "/path/to/log")
 | 
			
		||||
 | 
			
		||||
	f.Float64Var(&c.Conf.CvssScoreOver, "cvss-over", 0,
 | 
			
		||||
		"-cvss-over=6.5 means Servering CVSS Score 6.5 and over (default: 0 (means Server all))")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.IgnoreUnscoredCves, "ignore-unscored-cves", false,
 | 
			
		||||
		"Don't Server the unscored CVEs")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.IgnoreUnfixed, "ignore-unfixed", false,
 | 
			
		||||
		"Don't Server the unfixed CVEs")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(&c.Conf.HTTPProxy, "http-proxy", "",
 | 
			
		||||
		"http://proxy-url:port (default: empty)")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.FormatJSON, "format-json", false, "JSON format")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.ToLocalFile, "to-localfile", false, "Write report to localfile")
 | 
			
		||||
	f.StringVar(&p.listen, "listen", "localhost:5515",
 | 
			
		||||
		"host:port (default: localhost:5515)")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(&p.cveDict.Type, "cvedb-type", "",
 | 
			
		||||
		"DB type of go-cve-dictionary (sqlite3, mysql, postgres, redis or http)")
 | 
			
		||||
	f.StringVar(&p.cveDict.SQLite3Path, "cvedb-sqlite3-path", "", "/path/to/sqlite3")
 | 
			
		||||
	f.StringVar(&p.cveDict.URL, "cvedb-url", "",
 | 
			
		||||
		"http://go-cve-dictionary.com:1323 or DB connection string")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(&p.ovalDict.Type, "ovaldb-type", "",
 | 
			
		||||
		"DB type of goval-dictionary (sqlite3, mysql, postgres, redis or http)")
 | 
			
		||||
	f.StringVar(&p.ovalDict.SQLite3Path, "ovaldb-sqlite3-path", "", "/path/to/sqlite3")
 | 
			
		||||
	f.StringVar(&p.ovalDict.URL, "ovaldb-url", "",
 | 
			
		||||
		"http://goval-dictionary.com:1324 or DB connection string")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(&p.gostConf.Type, "gostdb-type", "",
 | 
			
		||||
		"DB type of gost (sqlite3, mysql, postgres, redis or http)")
 | 
			
		||||
	f.StringVar(&p.gostConf.SQLite3Path, "gostdb-sqlite3-path", "", "/path/to/sqlite3")
 | 
			
		||||
	f.StringVar(&p.gostConf.URL, "gostdb-url", "",
 | 
			
		||||
		"http://gost.com:1325 or DB connection string")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(&p.exploitConf.Type, "exploitdb-type", "",
 | 
			
		||||
		"DB type of exploit (sqlite3, mysql, postgres, redis or http)")
 | 
			
		||||
	f.StringVar(&p.exploitConf.SQLite3Path, "exploitdb-sqlite3-path", "", "/path/to/sqlite3")
 | 
			
		||||
	f.StringVar(&p.exploitConf.URL, "exploitdb-url", "",
 | 
			
		||||
		"http://exploit.com:1326 or DB connection string")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Execute execute
 | 
			
		||||
func (p *ServerCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
 | 
			
		||||
	util.Log = util.NewCustomLogger(c.ServerInfo{})
 | 
			
		||||
	cvelog.SetLogger(c.Conf.LogDir, false, c.Conf.Debug, false)
 | 
			
		||||
 | 
			
		||||
	if p.configPath != "" {
 | 
			
		||||
		if err := c.Load(p.configPath, ""); err != nil {
 | 
			
		||||
			util.Log.Errorf("Error loading %s. err: %+v", p.configPath, err)
 | 
			
		||||
			return subcommands.ExitUsageError
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.Conf.CveDict.Overwrite(p.cveDict)
 | 
			
		||||
	c.Conf.OvalDict.Overwrite(p.ovalDict)
 | 
			
		||||
	c.Conf.Gost.Overwrite(p.gostConf)
 | 
			
		||||
	c.Conf.Exploit.Overwrite(p.exploitConf)
 | 
			
		||||
 | 
			
		||||
	util.Log.Info("Validating config...")
 | 
			
		||||
	if !c.Conf.ValidateOnReport() {
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	util.Log.Info("Validating db config...")
 | 
			
		||||
	if !c.Conf.ValidateOnReportDB() {
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.CveDict.URL != "" {
 | 
			
		||||
		if err := report.CveClient.CheckHealth(); err != nil {
 | 
			
		||||
			util.Log.Errorf("CVE HTTP server is not running. err: %+v", err)
 | 
			
		||||
			util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with `-cvedb-type=sqlite3 -cvedb-sqlite3-path` option instead of -cvedb-url")
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.OvalDict.URL != "" {
 | 
			
		||||
		err := oval.Base{}.CheckHTTPHealth()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Errorf("OVAL HTTP server is not running. err: %s", err)
 | 
			
		||||
			util.Log.Errorf("Run goval-dictionary as server mode before reporting or run with `-ovaldb-type=sqlite3 -ovaldb-sqlite3-path` option instead of -ovaldb-url")
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.Gost.URL != "" {
 | 
			
		||||
		util.Log.Infof("gost: %s", c.Conf.Gost.URL)
 | 
			
		||||
		err := gost.Base{}.CheckHTTPHealth()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Errorf("gost HTTP server is not running. err: %+v", err)
 | 
			
		||||
			util.Log.Errorf("Run gost as server mode before reporting or run with `-gostdb-type=sqlite3 -gostdb-sqlite3-path` option instead of -gostdb-url")
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.Exploit.URL != "" {
 | 
			
		||||
		err := exploit.CheckHTTPHealth()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Errorf("exploit HTTP server is not running. err: %+v", err)
 | 
			
		||||
			util.Log.Errorf("Run go-exploitdb as server mode before reporting")
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbclient, locked, err := report.NewDBClient(report.DBClientConf{
 | 
			
		||||
		CveDictCnf:  c.Conf.CveDict,
 | 
			
		||||
		OvalDictCnf: c.Conf.OvalDict,
 | 
			
		||||
		GostCnf:     c.Conf.Gost,
 | 
			
		||||
		ExploitCnf:  c.Conf.Exploit,
 | 
			
		||||
		DebugSQL:    c.Conf.DebugSQL,
 | 
			
		||||
	})
 | 
			
		||||
	if locked {
 | 
			
		||||
		util.Log.Errorf("SQLite3 is locked. Close other DB connections and try again: %+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		util.Log.Errorf("Failed to init DB Clients. err: %+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer dbclient.CloseDB()
 | 
			
		||||
 | 
			
		||||
	http.Handle("/vuls", server.VulsHandler{DBclient: *dbclient})
 | 
			
		||||
	http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		fmt.Fprintf(w, "ok")
 | 
			
		||||
	})
 | 
			
		||||
	util.Log.Infof("Listening on %s", p.listen)
 | 
			
		||||
	if err := http.ListenAndServe(p.listen, nil); err != nil {
 | 
			
		||||
		util.Log.Errorf("Failed to start server. err: %+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
	return subcommands.ExitSuccess
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										232
									
								
								commands/tui.go
									
									
									
									
									
								
							
							
						
						@@ -1,5 +1,5 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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
 | 
			
		||||
@@ -18,38 +18,64 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
package commands
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	c "github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/exploit"
 | 
			
		||||
	"github.com/future-architect/vuls/gost"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/oval"
 | 
			
		||||
	"github.com/future-architect/vuls/report"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/google/subcommands"
 | 
			
		||||
	"golang.org/x/net/context"
 | 
			
		||||
	cvelog "github.com/kotakanbe/go-cve-dictionary/log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// TuiCmd is Subcommand of host discovery mode
 | 
			
		||||
type TuiCmd struct {
 | 
			
		||||
	lang     string
 | 
			
		||||
	debugSQL bool
 | 
			
		||||
	dbpath   string
 | 
			
		||||
	configPath  string
 | 
			
		||||
	cveDict     c.GoCveDictConf
 | 
			
		||||
	ovalDict    c.GovalDictConf
 | 
			
		||||
	gostConf    c.GostConf
 | 
			
		||||
	exploitConf c.ExploitConf
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Name return subcommand name
 | 
			
		||||
func (*TuiCmd) Name() string { return "tui" }
 | 
			
		||||
 | 
			
		||||
// Synopsis return synopsis
 | 
			
		||||
func (*TuiCmd) Synopsis() string { return "Run Tui view to anayze vulnerabilites" }
 | 
			
		||||
func (*TuiCmd) Synopsis() string { return "Run Tui view to analyze vulnerabilities" }
 | 
			
		||||
 | 
			
		||||
// Usage return usage
 | 
			
		||||
func (*TuiCmd) Usage() string {
 | 
			
		||||
	return `tui:
 | 
			
		||||
	tui [-dbpath=/path/to/vuls.sqlite3]
 | 
			
		||||
	tui
 | 
			
		||||
		[-refresh-cve]
 | 
			
		||||
		[-config=/path/to/config.toml]
 | 
			
		||||
		[-cvss-over=7]
 | 
			
		||||
		[-diff]
 | 
			
		||||
		[-ignore-unscored-cves]
 | 
			
		||||
		[-ignore-unfixed]
 | 
			
		||||
		[-results-dir=/path/to/results]
 | 
			
		||||
		[-log-dir=/path/to/log]
 | 
			
		||||
		[-debug]
 | 
			
		||||
		[-debug-sql]
 | 
			
		||||
		[-pipe]
 | 
			
		||||
		[-cvedb-type=sqlite3|mysql|postgres|redis|http]
 | 
			
		||||
		[-cvedb-sqlite3-path=/path/to/cve.sqlite3]
 | 
			
		||||
		[-cvedb-url=http://127.0.0.1:1323 or DB connection string]
 | 
			
		||||
		[-ovaldb-type=sqlite3|mysql|redis|http]
 | 
			
		||||
		[-ovaldb-sqlite3-path=/path/to/oval.sqlite3]
 | 
			
		||||
		[-ovaldb-url=http://127.0.0.1:1324 or DB connection string]
 | 
			
		||||
		[-gostdb-type=sqlite3|mysql|redis|http]
 | 
			
		||||
		[-gostdb-sqlite3-path=/path/to/gost.sqlite3]
 | 
			
		||||
		[-gostdb-url=http://127.0.0.1:1325 or DB connection string]
 | 
			
		||||
		[-exploitdb-type=sqlite3|mysql|redis|http]
 | 
			
		||||
		[-exploitdb-sqlite3-path=/path/to/exploitdb.sqlite3]
 | 
			
		||||
		[-exploitdb-url=http://127.0.0.1:1326 or DB connection string]
 | 
			
		||||
 | 
			
		||||
`
 | 
			
		||||
}
 | 
			
		||||
@@ -57,41 +83,175 @@ func (*TuiCmd) Usage() string {
 | 
			
		||||
// SetFlags set flag
 | 
			
		||||
func (p *TuiCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
	//  f.StringVar(&p.lang, "lang", "en", "[en|ja]")
 | 
			
		||||
	f.BoolVar(&p.debugSQL, "debug-sql", false, "debug SQL")
 | 
			
		||||
	f.BoolVar(&c.Conf.DebugSQL, "debug-sql", false, "debug SQL")
 | 
			
		||||
	f.BoolVar(&c.Conf.Debug, "debug", false, "debug mode")
 | 
			
		||||
 | 
			
		||||
	defaultLogDir := util.GetDefaultLogDir()
 | 
			
		||||
	f.StringVar(&c.Conf.LogDir, "log-dir", defaultLogDir, "/path/to/log")
 | 
			
		||||
 | 
			
		||||
	wd, _ := os.Getwd()
 | 
			
		||||
	defaultResultsDir := filepath.Join(wd, "results")
 | 
			
		||||
	f.StringVar(&c.Conf.ResultsDir, "results-dir", defaultResultsDir, "/path/to/results")
 | 
			
		||||
 | 
			
		||||
	defaultConfPath := filepath.Join(wd, "config.toml")
 | 
			
		||||
	f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.RefreshCve, "refresh-cve", false,
 | 
			
		||||
		"Refresh CVE information in JSON file under results dir")
 | 
			
		||||
 | 
			
		||||
	f.Float64Var(&c.Conf.CvssScoreOver, "cvss-over", 0,
 | 
			
		||||
		"-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.Diff, "diff", false,
 | 
			
		||||
		"Difference between previous result and current result ")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(
 | 
			
		||||
		&c.Conf.IgnoreUnscoredCves, "ignore-unscored-cves", false,
 | 
			
		||||
		"Don't report the unscored CVEs")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.IgnoreUnfixed, "ignore-unfixed", false,
 | 
			
		||||
		"Don't report the unfixed CVEs")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.Pipe, "pipe", false, "Use stdin via PIPE")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(&p.cveDict.Type, "cvedb-type", "",
 | 
			
		||||
		"DB type of go-cve-dictionary (sqlite3, mysql, postgres or redis)")
 | 
			
		||||
	f.StringVar(&p.cveDict.SQLite3Path, "cvedb-path", "", "/path/to/sqlite3")
 | 
			
		||||
	f.StringVar(&p.cveDict.URL, "cvedb-url", "",
 | 
			
		||||
		"http://go-cve-dictionary.com:1323 or DB connection string")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(&p.ovalDict.Type, "ovaldb-type", "",
 | 
			
		||||
		"DB type of goval-dictionary (sqlite3, mysql, postgres or redis)")
 | 
			
		||||
	f.StringVar(&p.ovalDict.SQLite3Path, "ovaldb-path", "", "/path/to/sqlite3")
 | 
			
		||||
	f.StringVar(&p.ovalDict.URL, "ovaldb-url", "",
 | 
			
		||||
		"http://goval-dictionary.com:1324 or DB connection string")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(&p.gostConf.Type, "gostdb-type", "",
 | 
			
		||||
		"DB type of gost (sqlite3, mysql, postgres or redis)")
 | 
			
		||||
	f.StringVar(&p.gostConf.SQLite3Path, "gostdb-path", "", "/path/to/sqlite3")
 | 
			
		||||
	f.StringVar(&p.gostConf.URL, "gostdb-url", "",
 | 
			
		||||
		"http://gost.com:1325 or DB connection string")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(&p.exploitConf.Type, "exploitdb-type", "",
 | 
			
		||||
		"DB type of exploit (sqlite3, mysql, postgres, redis or http)")
 | 
			
		||||
	f.StringVar(&p.exploitConf.SQLite3Path, "exploitdb-sqlite3-path", "", "/path/to/sqlite3")
 | 
			
		||||
	f.StringVar(&p.exploitConf.URL, "exploitdb-url", "",
 | 
			
		||||
		"http://exploit.com:1326 or DB connection string")
 | 
			
		||||
 | 
			
		||||
	defaultDBPath := filepath.Join(wd, "vuls.sqlite3")
 | 
			
		||||
	f.StringVar(&p.dbpath, "dbpath", defaultDBPath,
 | 
			
		||||
		fmt.Sprintf("/path/to/sqlite3 (default: %s)", defaultDBPath))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Execute execute
 | 
			
		||||
func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
 | 
			
		||||
	c.Conf.Lang = "en"
 | 
			
		||||
	c.Conf.DebugSQL = p.debugSQL
 | 
			
		||||
	c.Conf.DBPath = p.dbpath
 | 
			
		||||
 | 
			
		||||
	historyID := ""
 | 
			
		||||
	if 0 < len(f.Args()) {
 | 
			
		||||
		if _, err := strconv.Atoi(f.Args()[0]); err != nil {
 | 
			
		||||
			log.Errorf("First Argument have to be scan_histores record ID: %s", err)
 | 
			
		||||
	// Setup Logger
 | 
			
		||||
	util.Log = util.NewCustomLogger(c.ServerInfo{})
 | 
			
		||||
	cvelog.SetLogger(c.Conf.LogDir, false, c.Conf.Debug, false)
 | 
			
		||||
 | 
			
		||||
	if err := c.Load(p.configPath, ""); err != nil {
 | 
			
		||||
		util.Log.Errorf("Error loading %s, err: %+v", p.configPath, err)
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.Conf.CveDict.Overwrite(p.cveDict)
 | 
			
		||||
	c.Conf.OvalDict.Overwrite(p.ovalDict)
 | 
			
		||||
	c.Conf.Gost.Overwrite(p.gostConf)
 | 
			
		||||
	c.Conf.Exploit.Overwrite(p.exploitConf)
 | 
			
		||||
 | 
			
		||||
	var dir string
 | 
			
		||||
	var err error
 | 
			
		||||
	if c.Conf.Diff {
 | 
			
		||||
		dir, err = report.JSONDir([]string{})
 | 
			
		||||
	} else {
 | 
			
		||||
		dir, err = report.JSONDir(f.Args())
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		util.Log.Errorf("Failed to read from JSON. err: %+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	util.Log.Info("Validating config...")
 | 
			
		||||
	if !c.Conf.ValidateOnTui() {
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var res models.ScanResults
 | 
			
		||||
	if res, err = report.LoadScanResults(dir); err != nil {
 | 
			
		||||
		util.Log.Error(err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
	util.Log.Infof("Loaded: %s", dir)
 | 
			
		||||
 | 
			
		||||
	util.Log.Info("Validating db config...")
 | 
			
		||||
	if !c.Conf.ValidateOnReportDB() {
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.CveDict.URL != "" {
 | 
			
		||||
		if err := report.CveClient.CheckHealth(); err != nil {
 | 
			
		||||
			util.Log.Errorf("CVE HTTP server is not running. err: %+v", err)
 | 
			
		||||
			util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with `-cvedb-type=sqlite3 -cvedb-sqlite3-path` option instead of -cvedb-url")
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
		historyID = f.Args()[0]
 | 
			
		||||
	} else {
 | 
			
		||||
		stat, _ := os.Stdin.Stat()
 | 
			
		||||
		if (stat.Mode() & os.ModeCharDevice) == 0 {
 | 
			
		||||
			bytes, err := ioutil.ReadAll(os.Stdin)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Errorf("Failed to read stdin: %s", err)
 | 
			
		||||
				return subcommands.ExitFailure
 | 
			
		||||
			}
 | 
			
		||||
			fields := strings.Fields(string(bytes))
 | 
			
		||||
			if 0 < len(fields) {
 | 
			
		||||
				historyID = fields[0]
 | 
			
		||||
			}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.OvalDict.URL != "" {
 | 
			
		||||
		err := oval.Base{}.CheckHTTPHealth()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Errorf("OVAL HTTP server is not running. err: %+v", err)
 | 
			
		||||
			util.Log.Errorf("Run goval-dictionary as server mode before reporting or run with `-ovaldb-type=sqlite3 -ovaldb-sqlite3-path` option instead of -ovaldb-url")
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return report.RunTui(historyID)
 | 
			
		||||
 | 
			
		||||
	if c.Conf.Gost.URL != "" {
 | 
			
		||||
		util.Log.Infof("gost: %s", c.Conf.Gost.URL)
 | 
			
		||||
		err := gost.Base{}.CheckHTTPHealth()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Errorf("gost HTTP server is not running. err: %+v", err)
 | 
			
		||||
			util.Log.Errorf("Run gost as server mode before reporting or run with `-gostdb-type=sqlite3 -gostdb-sqlite3-path` option instead of -gostdb-url")
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.Exploit.URL != "" {
 | 
			
		||||
		err := exploit.CheckHTTPHealth()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Errorf("exploit HTTP server is not running. err: %+v", err)
 | 
			
		||||
			util.Log.Errorf("Run go-exploitdb as server mode before reporting")
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	dbclient, locked, err := report.NewDBClient(report.DBClientConf{
 | 
			
		||||
		CveDictCnf:  c.Conf.CveDict,
 | 
			
		||||
		OvalDictCnf: c.Conf.OvalDict,
 | 
			
		||||
		GostCnf:     c.Conf.Gost,
 | 
			
		||||
		ExploitCnf:  c.Conf.Exploit,
 | 
			
		||||
		DebugSQL:    c.Conf.DebugSQL,
 | 
			
		||||
	})
 | 
			
		||||
	if locked {
 | 
			
		||||
		util.Log.Errorf("SQLite3 is locked. Close other DB connections and try again: %+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		util.Log.Errorf("Failed to init DB Clients. err: %+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer dbclient.CloseDB()
 | 
			
		||||
 | 
			
		||||
	if res, err = report.FillCveInfos(*dbclient, res, dir); err != nil {
 | 
			
		||||
		util.Log.Error(err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, r := range res {
 | 
			
		||||
		if len(r.Warnings) != 0 {
 | 
			
		||||
			util.Log.Warnf("Warning: Some warnings occurred while scanning on %s: %s",
 | 
			
		||||
				r.FormatServerName(), r.Warnings)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return report.RunTui(res)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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
 | 
			
		||||
@@ -15,42 +15,42 @@ 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 report
 | 
			
		||||
package commands
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"runtime"
 | 
			
		||||
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	formatter "github.com/kotakanbe/logrus-prefixed-formatter"
 | 
			
		||||
	"github.com/howeyc/gopass"
 | 
			
		||||
	homedir "github.com/mitchellh/go-homedir"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// LogrusWriter write to logfile
 | 
			
		||||
type LogrusWriter struct {
 | 
			
		||||
func getPasswd(prompt string) (string, error) {
 | 
			
		||||
	for {
 | 
			
		||||
		fmt.Print(prompt)
 | 
			
		||||
		pass, err := gopass.GetPasswdMasked()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", xerrors.New("Failed to read a password")
 | 
			
		||||
		}
 | 
			
		||||
		if 0 < len(pass) {
 | 
			
		||||
			return string(pass), nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w LogrusWriter) Write(scanResults []models.ScanResult) error {
 | 
			
		||||
	path := "/var/log/vuls/report.log"
 | 
			
		||||
	if runtime.GOOS == "windows" {
 | 
			
		||||
		path = filepath.Join(os.Getenv("APPDATA"), "vuls", "report.log")
 | 
			
		||||
	}
 | 
			
		||||
	f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
 | 
			
		||||
func mkdirDotVuls() error {
 | 
			
		||||
	home, err := homedir.Dir()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	log := logrus.New()
 | 
			
		||||
	log.Formatter = &formatter.TextFormatter{}
 | 
			
		||||
	log.Out = f
 | 
			
		||||
	log.Level = logrus.InfoLevel
 | 
			
		||||
 | 
			
		||||
	for _, s := range scanResults {
 | 
			
		||||
		text, err := toPlainText(s)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
	dotVuls := filepath.Join(home, ".vuls")
 | 
			
		||||
	if _, err := os.Stat(dotVuls); os.IsNotExist(err) {
 | 
			
		||||
		if err := os.Mkdir(dotVuls, 0700); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		log.Infof(text)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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
 | 
			
		||||
@@ -15,10 +15,4 @@ 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 version
 | 
			
		||||
 | 
			
		||||
// Name is Vuls
 | 
			
		||||
const Name string = "vuls"
 | 
			
		||||
 | 
			
		||||
// Version of Vuls
 | 
			
		||||
const Version string = "0.1.5"
 | 
			
		||||
package commands
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1196
									
								
								config/config.go
									
									
									
									
									
								
							
							
						
						
							
								
								
									
										103
									
								
								config/config_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,103 @@
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestSyslogConfValidate(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		conf              SyslogConf
 | 
			
		||||
		expectedErrLength int
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			conf:              SyslogConf{},
 | 
			
		||||
			expectedErrLength: 0,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			conf: SyslogConf{
 | 
			
		||||
				Protocol: "tcp",
 | 
			
		||||
				Port:     "5140",
 | 
			
		||||
			},
 | 
			
		||||
			expectedErrLength: 0,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			conf: SyslogConf{
 | 
			
		||||
				Protocol: "udp",
 | 
			
		||||
				Port:     "12345",
 | 
			
		||||
				Severity: "emerg",
 | 
			
		||||
				Facility: "user",
 | 
			
		||||
			},
 | 
			
		||||
			expectedErrLength: 0,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			conf: SyslogConf{
 | 
			
		||||
				Protocol: "foo",
 | 
			
		||||
				Port:     "514",
 | 
			
		||||
			},
 | 
			
		||||
			expectedErrLength: 1,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			conf: SyslogConf{
 | 
			
		||||
				Protocol: "invalid",
 | 
			
		||||
				Port:     "-1",
 | 
			
		||||
			},
 | 
			
		||||
			expectedErrLength: 2,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			conf: SyslogConf{
 | 
			
		||||
				Protocol: "invalid",
 | 
			
		||||
				Port:     "invalid",
 | 
			
		||||
				Severity: "invalid",
 | 
			
		||||
				Facility: "invalid",
 | 
			
		||||
			},
 | 
			
		||||
			expectedErrLength: 4,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
		Conf.ToSyslog = true
 | 
			
		||||
		errs := tt.conf.Validate()
 | 
			
		||||
		if len(errs) != tt.expectedErrLength {
 | 
			
		||||
			t.Errorf("test: %d, expected %d, actual %d", i, tt.expectedErrLength, len(errs))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestMajorVersion(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in  Distro
 | 
			
		||||
		out int
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			in: Distro{
 | 
			
		||||
				Family:  Amazon,
 | 
			
		||||
				Release: "2 (2017.12)",
 | 
			
		||||
			},
 | 
			
		||||
			out: 2,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: Distro{
 | 
			
		||||
				Family:  Amazon,
 | 
			
		||||
				Release: "2017.12",
 | 
			
		||||
			},
 | 
			
		||||
			out: 1,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: Distro{
 | 
			
		||||
				Family:  CentOS,
 | 
			
		||||
				Release: "7.10",
 | 
			
		||||
			},
 | 
			
		||||
			out: 7,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
		ver, err := tt.in.MajorVersion()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("[%d] err occurred: %s", i, err)
 | 
			
		||||
		}
 | 
			
		||||
		if tt.out != ver {
 | 
			
		||||
			t.Errorf("[%d] expected %d, actual %d", i, tt.out, ver)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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
 | 
			
		||||
@@ -17,13 +17,13 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import "fmt"
 | 
			
		||||
import "golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
// JSONLoader loads configuration
 | 
			
		||||
type JSONLoader struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Load load the configuraiton JSON file specified by path arg.
 | 
			
		||||
// Load load the configuration JSON file specified by path arg.
 | 
			
		||||
func (c JSONLoader) Load(path, sudoPass, keyPass string) (err error) {
 | 
			
		||||
	return fmt.Errorf("Not implement yet")
 | 
			
		||||
	return xerrors.New("Not implement yet")
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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
 | 
			
		||||
@@ -18,28 +18,41 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/BurntSushi/toml"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/k0kubun/pp"
 | 
			
		||||
	"github.com/knqyf263/go-cpe/naming"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// TOMLLoader loads config
 | 
			
		||||
type TOMLLoader struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Load load the configuraiton TOML file specified by path arg.
 | 
			
		||||
func (c TOMLLoader) Load(pathToToml, keyPass string) (err error) {
 | 
			
		||||
// Load load the configuration TOML file specified by path arg.
 | 
			
		||||
func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
	var conf Config
 | 
			
		||||
	if _, err := toml.DecodeFile(pathToToml, &conf); err != nil {
 | 
			
		||||
		log.Error("Load config failed", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Conf.Mail = conf.Mail
 | 
			
		||||
	Conf.EMail = conf.EMail
 | 
			
		||||
	Conf.Slack = conf.Slack
 | 
			
		||||
	Conf.Stride = conf.Stride
 | 
			
		||||
	Conf.HipChat = conf.HipChat
 | 
			
		||||
	Conf.ChatWork = conf.ChatWork
 | 
			
		||||
	Conf.Telegram = conf.Telegram
 | 
			
		||||
	Conf.Saas = conf.Saas
 | 
			
		||||
	Conf.Syslog = conf.Syslog
 | 
			
		||||
	Conf.HTTP = conf.HTTP
 | 
			
		||||
	Conf.AWS = conf.AWS
 | 
			
		||||
	Conf.Azure = conf.Azure
 | 
			
		||||
 | 
			
		||||
	Conf.CveDict = conf.CveDict
 | 
			
		||||
	Conf.OvalDict = conf.OvalDict
 | 
			
		||||
	Conf.Gost = conf.Gost
 | 
			
		||||
	Conf.Exploit = conf.Exploit
 | 
			
		||||
 | 
			
		||||
	d := conf.Default
 | 
			
		||||
	Conf.Default = d
 | 
			
		||||
@@ -50,52 +63,88 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) (err error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	i := 0
 | 
			
		||||
	for name, v := range conf.Servers {
 | 
			
		||||
 | 
			
		||||
	for serverName, 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.")
 | 
			
		||||
			return xerrors.Errorf("[Deprecated] KEYPASSWORD IN CONFIG FILE ARE UNSECURE. REMOVE THEM IMMEDIATELY FOR A SECURITY REASONS. THEY WILL BE REMOVED IN A FUTURE RELEASE: %s", serverName)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s := ServerInfo{ServerName: name}
 | 
			
		||||
		s := ServerInfo{ServerName: serverName}
 | 
			
		||||
		s.Images = make(map[string]Image)
 | 
			
		||||
 | 
			
		||||
		switch {
 | 
			
		||||
		case v.User != "":
 | 
			
		||||
			s.User = v.User
 | 
			
		||||
		case d.User != "":
 | 
			
		||||
			s.User = d.User
 | 
			
		||||
		default:
 | 
			
		||||
			return fmt.Errorf("%s is invalid. User is empty", name)
 | 
			
		||||
		// image are able to set any server type
 | 
			
		||||
		for name, image := range v.Images {
 | 
			
		||||
			if err := IsValidImage(image); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			s.Images[name] = image
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.Host = v.Host
 | 
			
		||||
		if s.Host == "" {
 | 
			
		||||
			return fmt.Errorf("%s is invalid. host is empty", name)
 | 
			
		||||
		}
 | 
			
		||||
		if v.Type != ServerTypePseudo {
 | 
			
		||||
			s.Host = v.Host
 | 
			
		||||
			if len(s.Host) == 0 {
 | 
			
		||||
				return xerrors.Errorf("%s is invalid. host is empty", serverName)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		switch {
 | 
			
		||||
		case v.Port != "":
 | 
			
		||||
			s.Port = v.Port
 | 
			
		||||
		case d.Port != "":
 | 
			
		||||
			s.Port = d.Port
 | 
			
		||||
		default:
 | 
			
		||||
			s.Port = "22"
 | 
			
		||||
		}
 | 
			
		||||
			switch {
 | 
			
		||||
			case v.Port != "":
 | 
			
		||||
				s.Port = v.Port
 | 
			
		||||
			case d.Port != "":
 | 
			
		||||
				s.Port = d.Port
 | 
			
		||||
			default:
 | 
			
		||||
				s.Port = "22"
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		s.KeyPath = v.KeyPath
 | 
			
		||||
		if s.KeyPath == "" {
 | 
			
		||||
			s.KeyPath = d.KeyPath
 | 
			
		||||
		}
 | 
			
		||||
		if s.KeyPath != "" {
 | 
			
		||||
			if _, err := os.Stat(s.KeyPath); err != nil {
 | 
			
		||||
				return fmt.Errorf(
 | 
			
		||||
					"%s is invalid. keypath: %s not exists", name, s.KeyPath)
 | 
			
		||||
			switch {
 | 
			
		||||
			case v.User != "":
 | 
			
		||||
				s.User = v.User
 | 
			
		||||
			case d.User != "":
 | 
			
		||||
				s.User = d.User
 | 
			
		||||
			default:
 | 
			
		||||
				if s.Port != "local" {
 | 
			
		||||
					return xerrors.Errorf("%s is invalid. User is empty", serverName)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			s.KeyPath = v.KeyPath
 | 
			
		||||
			if len(s.KeyPath) == 0 {
 | 
			
		||||
				s.KeyPath = d.KeyPath
 | 
			
		||||
			}
 | 
			
		||||
			if s.KeyPath != "" {
 | 
			
		||||
				if _, err := os.Stat(s.KeyPath); err != nil {
 | 
			
		||||
					return xerrors.Errorf(
 | 
			
		||||
						"%s is invalid. keypath: %s not exists", serverName, s.KeyPath)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			s.KeyPassword = v.KeyPassword
 | 
			
		||||
			if len(s.KeyPassword) == 0 {
 | 
			
		||||
				s.KeyPassword = d.KeyPassword
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		//  s.KeyPassword = keyPass
 | 
			
		||||
		s.KeyPassword = v.KeyPassword
 | 
			
		||||
		if s.KeyPassword == "" {
 | 
			
		||||
			s.KeyPassword = d.KeyPassword
 | 
			
		||||
		s.ScanMode = v.ScanMode
 | 
			
		||||
		if len(s.ScanMode) == 0 {
 | 
			
		||||
			s.ScanMode = d.ScanMode
 | 
			
		||||
			if len(s.ScanMode) == 0 {
 | 
			
		||||
				s.ScanMode = []string{"fast"}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		for _, m := range s.ScanMode {
 | 
			
		||||
			switch m {
 | 
			
		||||
			case "fast":
 | 
			
		||||
				s.Mode.Set(Fast)
 | 
			
		||||
			case "fast-root":
 | 
			
		||||
				s.Mode.Set(FastRoot)
 | 
			
		||||
			case "deep":
 | 
			
		||||
				s.Mode.Set(Deep)
 | 
			
		||||
			case "offline":
 | 
			
		||||
				s.Mode.Set(Offline)
 | 
			
		||||
			default:
 | 
			
		||||
				return xerrors.Errorf("scanMode: %s of %s is invalie. Specify -fast, -fast-root, -deep or offline", m, serverName)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if err := s.Mode.validate(); err != nil {
 | 
			
		||||
			return xerrors.Errorf("%s in %s", err, serverName)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.CpeNames = v.CpeNames
 | 
			
		||||
@@ -103,32 +152,179 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) (err error) {
 | 
			
		||||
			s.CpeNames = d.CpeNames
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.Containers = v.Containers
 | 
			
		||||
		if len(s.Containers) == 0 {
 | 
			
		||||
			s.Containers = d.Containers
 | 
			
		||||
		s.Lockfiles = v.Lockfiles
 | 
			
		||||
		if len(s.Lockfiles) == 0 {
 | 
			
		||||
			s.Lockfiles = d.Lockfiles
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.Optional = v.Optional
 | 
			
		||||
		for _, dkv := range d.Optional {
 | 
			
		||||
		s.FindLock = v.FindLock
 | 
			
		||||
 | 
			
		||||
		for i, n := range s.CpeNames {
 | 
			
		||||
			uri, err := toCpeURI(n)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return xerrors.Errorf("Failed to parse CPENames %s in %s, err: %w", n, serverName, err)
 | 
			
		||||
			}
 | 
			
		||||
			s.CpeNames[i] = uri
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.ContainersIncluded = v.ContainersIncluded
 | 
			
		||||
		if len(s.ContainersIncluded) == 0 {
 | 
			
		||||
			s.ContainersIncluded = d.ContainersIncluded
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.ContainersExcluded = v.ContainersExcluded
 | 
			
		||||
		if len(s.ContainersExcluded) == 0 {
 | 
			
		||||
			s.ContainersExcluded = d.ContainersExcluded
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.ContainerType = v.ContainerType
 | 
			
		||||
		if len(s.ContainerType) == 0 {
 | 
			
		||||
			s.ContainerType = d.ContainerType
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.Containers = v.Containers
 | 
			
		||||
		for contName, cont := range s.Containers {
 | 
			
		||||
			cont.IgnoreCves = append(cont.IgnoreCves, d.IgnoreCves...)
 | 
			
		||||
			s.Containers[contName] = cont
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(v.DependencyCheckXMLPath) != 0 || len(d.DependencyCheckXMLPath) != 0 {
 | 
			
		||||
			return xerrors.Errorf("[DEPRECATED] dependencyCheckXMLPath IS DEPRECATED. USE owaspDCXMLPath INSTEAD: %s", serverName)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.OwaspDCXMLPath = v.OwaspDCXMLPath
 | 
			
		||||
		if len(s.OwaspDCXMLPath) == 0 {
 | 
			
		||||
			s.OwaspDCXMLPath = d.OwaspDCXMLPath
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.Memo = v.Memo
 | 
			
		||||
		if s.Memo == "" {
 | 
			
		||||
			s.Memo = d.Memo
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.IgnoreCves = v.IgnoreCves
 | 
			
		||||
		for _, cve := range d.IgnoreCves {
 | 
			
		||||
			found := false
 | 
			
		||||
			for _, kv := range s.Optional {
 | 
			
		||||
				if dkv[0] == kv[0] {
 | 
			
		||||
			for _, c := range s.IgnoreCves {
 | 
			
		||||
				if cve == c {
 | 
			
		||||
					found = true
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if !found {
 | 
			
		||||
				s.Optional = append(s.Optional, dkv)
 | 
			
		||||
				s.IgnoreCves = append(s.IgnoreCves, cve)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.IgnorePkgsRegexp = v.IgnorePkgsRegexp
 | 
			
		||||
		for _, pkg := range d.IgnorePkgsRegexp {
 | 
			
		||||
			found := false
 | 
			
		||||
			for _, p := range s.IgnorePkgsRegexp {
 | 
			
		||||
				if pkg == p {
 | 
			
		||||
					found = true
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if !found {
 | 
			
		||||
				s.IgnorePkgsRegexp = append(s.IgnorePkgsRegexp, pkg)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		for _, reg := range s.IgnorePkgsRegexp {
 | 
			
		||||
			_, err := regexp.Compile(reg)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return xerrors.Errorf("Faild to parse %s in %s. err: %w", reg, serverName, err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		for contName, cont := range s.Containers {
 | 
			
		||||
			for _, reg := range cont.IgnorePkgsRegexp {
 | 
			
		||||
				_, err := regexp.Compile(reg)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return xerrors.Errorf("Faild to parse %s in %s@%s. err: %w",
 | 
			
		||||
						reg, contName, serverName, err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		opt := map[string]interface{}{}
 | 
			
		||||
		for k, v := range d.Optional {
 | 
			
		||||
			opt[k] = v
 | 
			
		||||
		}
 | 
			
		||||
		for k, v := range v.Optional {
 | 
			
		||||
			opt[k] = v
 | 
			
		||||
		}
 | 
			
		||||
		s.Optional = opt
 | 
			
		||||
 | 
			
		||||
		s.Enablerepo = v.Enablerepo
 | 
			
		||||
		if len(s.Enablerepo) == 0 {
 | 
			
		||||
			s.Enablerepo = d.Enablerepo
 | 
			
		||||
		}
 | 
			
		||||
		if len(s.Enablerepo) != 0 {
 | 
			
		||||
			for _, repo := range s.Enablerepo {
 | 
			
		||||
				switch repo {
 | 
			
		||||
				case "base", "updates":
 | 
			
		||||
					// nop
 | 
			
		||||
				default:
 | 
			
		||||
					return xerrors.Errorf(
 | 
			
		||||
						"For now, enablerepo have to be base or updates: %s, servername: %s",
 | 
			
		||||
						s.Enablerepo, serverName)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.GitHubRepos = v.GitHubRepos
 | 
			
		||||
		for ownerRepo, githubSetting := range s.GitHubRepos {
 | 
			
		||||
			if ss := strings.Split(ownerRepo, "/"); len(ss) != 2 {
 | 
			
		||||
				return xerrors.Errorf("Failed to parse GitHub owner/repo: %s in %s",
 | 
			
		||||
					ownerRepo, serverName)
 | 
			
		||||
			}
 | 
			
		||||
			if githubSetting.Token == "" {
 | 
			
		||||
				return xerrors.Errorf("GitHub owner/repo: %s in %s token is empty",
 | 
			
		||||
					ownerRepo, serverName)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.UUIDs = v.UUIDs
 | 
			
		||||
		s.Type = v.Type
 | 
			
		||||
 | 
			
		||||
		s.WordPress.WPVulnDBToken = v.WordPress.WPVulnDBToken
 | 
			
		||||
		s.WordPress.CmdPath = v.WordPress.CmdPath
 | 
			
		||||
		s.WordPress.DocRoot = v.WordPress.DocRoot
 | 
			
		||||
		s.WordPress.OSUser = v.WordPress.OSUser
 | 
			
		||||
		s.WordPress.IgnoreInactive = v.WordPress.IgnoreInactive
 | 
			
		||||
 | 
			
		||||
		s.LogMsgAnsiColor = Colors[i%len(Colors)]
 | 
			
		||||
		i++
 | 
			
		||||
 | 
			
		||||
		servers[name] = s
 | 
			
		||||
		servers[serverName] = s
 | 
			
		||||
	}
 | 
			
		||||
	log.Debug("Config loaded")
 | 
			
		||||
	log.Debugf("%s", pp.Sprintf("%v", servers))
 | 
			
		||||
	Conf.Servers = servers
 | 
			
		||||
	return
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func toCpeURI(cpename string) (string, error) {
 | 
			
		||||
	if strings.HasPrefix(cpename, "cpe:2.3:") {
 | 
			
		||||
		wfn, err := naming.UnbindFS(cpename)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		return naming.BindToURI(wfn), nil
 | 
			
		||||
	} else if strings.HasPrefix(cpename, "cpe:/") {
 | 
			
		||||
		wfn, err := naming.UnbindURI(cpename)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		return naming.BindToURI(wfn), nil
 | 
			
		||||
	}
 | 
			
		||||
	return "", xerrors.Errorf("Unknow CPE format: %s", cpename)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsValidImage checks a container configuration
 | 
			
		||||
func IsValidImage(c Image) error {
 | 
			
		||||
	if c.Name == "" {
 | 
			
		||||
		return xerrors.New("Invalid arguments : no image name")
 | 
			
		||||
	}
 | 
			
		||||
	if c.Tag == "" {
 | 
			
		||||
		return xerrors.New("Invalid arguments : no image tag")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										44
									
								
								config/tomlloader_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,44 @@
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestToCpeURI(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in       string
 | 
			
		||||
		expected string
 | 
			
		||||
		err      bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			in:       "",
 | 
			
		||||
			expected: "",
 | 
			
		||||
			err:      true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in:       "cpe:/a:microsoft:internet_explorer:10",
 | 
			
		||||
			expected: "cpe:/a:microsoft:internet_explorer:10",
 | 
			
		||||
			err:      false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in:       "cpe:2.3:a:microsoft:internet_explorer:10:*:*:*:*:*:*:*",
 | 
			
		||||
			expected: "cpe:/a:microsoft:internet_explorer:10",
 | 
			
		||||
			err:      false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
		actual, err := toCpeURI(tt.in)
 | 
			
		||||
		if err != nil && !tt.err {
 | 
			
		||||
			t.Errorf("[%d] unexpected error occurred, in: %s act: %s, exp: %s",
 | 
			
		||||
				i, tt.in, actual, tt.expected)
 | 
			
		||||
		} else if err == nil && tt.err {
 | 
			
		||||
			t.Errorf("[%d] expected error is not occurred, in: %s act: %s, exp: %s",
 | 
			
		||||
				i, tt.in, actual, tt.expected)
 | 
			
		||||
		}
 | 
			
		||||
		if actual != tt.expected {
 | 
			
		||||
			t.Errorf("[%d] in: %s, actual: %s, expected: %s",
 | 
			
		||||
				i, tt.in, actual, tt.expected)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										66
									
								
								contrib/owasp-dependency-check/parser/parser.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,66 @@
 | 
			
		||||
package parser
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/xml"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
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 OWASP dependency check XML and collect list of cpe
 | 
			
		||||
func Parse(path string) ([]string, error) {
 | 
			
		||||
	file, err := os.Open(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Warnf("OWASP Dependency Check XML is not found: %s", path)
 | 
			
		||||
		return []string{}, nil
 | 
			
		||||
	}
 | 
			
		||||
	defer file.Close()
 | 
			
		||||
 | 
			
		||||
	b, err := ioutil.ReadAll(file)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Warnf("Failed to read OWASP Dependency Check XML: %s", path)
 | 
			
		||||
		return []string{}, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var anal analysis
 | 
			
		||||
	if err := xml.Unmarshal(b, &anal); err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to unmarshal: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cpes := []string{}
 | 
			
		||||
	for _, d := range anal.Dependencies {
 | 
			
		||||
		for _, ident := range d.Identifiers {
 | 
			
		||||
			if ident.Type == "cpe" {
 | 
			
		||||
				name := strings.TrimPrefix(ident.Name, "(")
 | 
			
		||||
				name = strings.TrimSuffix(name, ")")
 | 
			
		||||
				cpes = appendIfMissing(cpes, name)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return cpes, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -1,284 +0,0 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package cveapi
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cenkalti/backoff"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	cveconfig "github.com/kotakanbe/go-cve-dictionary/config"
 | 
			
		||||
	cvedb "github.com/kotakanbe/go-cve-dictionary/db"
 | 
			
		||||
	cve "github.com/kotakanbe/go-cve-dictionary/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CveClient is api client of CVE disctionary service.
 | 
			
		||||
var CveClient cvedictClient
 | 
			
		||||
 | 
			
		||||
type cvedictClient struct {
 | 
			
		||||
	//  httpProxy string
 | 
			
		||||
	baseURL string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api *cvedictClient) initialize() {
 | 
			
		||||
	api.baseURL = config.Conf.CveDictionaryURL
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api cvedictClient) CheckHealth() (ok bool, err error) {
 | 
			
		||||
	if config.Conf.CveDBPath != "" {
 | 
			
		||||
		log.Debugf("get cve-dictionary from sqlite3")
 | 
			
		||||
		return true, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	api.initialize()
 | 
			
		||||
	url := fmt.Sprintf("%s/health", api.baseURL)
 | 
			
		||||
	var errs []error
 | 
			
		||||
	var resp *http.Response
 | 
			
		||||
	resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
 | 
			
		||||
	//  resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
 | 
			
		||||
	if len(errs) > 0 || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
		return false, fmt.Errorf("Failed to request to CVE server. url: %s, errs: %v", url, errs)
 | 
			
		||||
	}
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type response struct {
 | 
			
		||||
	Key       string
 | 
			
		||||
	CveDetail cve.CveDetail
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api cvedictClient) FetchCveDetails(cveIDs []string) (cveDetails cve.CveDetails, err error) {
 | 
			
		||||
	if config.Conf.CveDBPath != "" {
 | 
			
		||||
		return api.FetchCveDetailsFromCveDB(cveIDs)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	api.baseURL = config.Conf.CveDictionaryURL
 | 
			
		||||
	reqChan := make(chan string, len(cveIDs))
 | 
			
		||||
	resChan := make(chan response, len(cveIDs))
 | 
			
		||||
	errChan := make(chan error, len(cveIDs))
 | 
			
		||||
	defer close(reqChan)
 | 
			
		||||
	defer close(resChan)
 | 
			
		||||
	defer close(errChan)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		for _, cveID := range cveIDs {
 | 
			
		||||
			reqChan <- cveID
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	concurrency := 10
 | 
			
		||||
	tasks := util.GenWorkers(concurrency)
 | 
			
		||||
	for range cveIDs {
 | 
			
		||||
		tasks <- func() {
 | 
			
		||||
			select {
 | 
			
		||||
			case cveID := <-reqChan:
 | 
			
		||||
				url, err := util.URLPathJoin(api.baseURL, "cves", cveID)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					errChan <- err
 | 
			
		||||
				} else {
 | 
			
		||||
					log.Debugf("HTTP Request to %s", url)
 | 
			
		||||
					api.httpGet(cveID, url, resChan, errChan)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	timeout := time.After(2 * 60 * time.Second)
 | 
			
		||||
	var errs []error
 | 
			
		||||
	for range cveIDs {
 | 
			
		||||
		select {
 | 
			
		||||
		case res := <-resChan:
 | 
			
		||||
			if len(res.CveDetail.CveID) == 0 {
 | 
			
		||||
				cveDetails = append(cveDetails, cve.CveDetail{
 | 
			
		||||
					CveID: res.Key,
 | 
			
		||||
				})
 | 
			
		||||
			} else {
 | 
			
		||||
				cveDetails = append(cveDetails, res.CveDetail)
 | 
			
		||||
			}
 | 
			
		||||
		case err := <-errChan:
 | 
			
		||||
			errs = append(errs, err)
 | 
			
		||||
		case <-timeout:
 | 
			
		||||
			return []cve.CveDetail{}, fmt.Errorf("Timeout Fetching CVE")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(errs) != 0 {
 | 
			
		||||
		return []cve.CveDetail{},
 | 
			
		||||
			fmt.Errorf("Failed to fetch CVE. err: %v", errs)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// order by CVE ID desc
 | 
			
		||||
	sort.Sort(cveDetails)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api cvedictClient) FetchCveDetailsFromCveDB(cveIDs []string) (cveDetails cve.CveDetails, err error) {
 | 
			
		||||
	log.Debugf("open cve-dictionary db")
 | 
			
		||||
	cveconfig.Conf.DBPath = config.Conf.CveDBPath
 | 
			
		||||
	if err := cvedb.OpenDB(); err != nil {
 | 
			
		||||
		return []cve.CveDetail{},
 | 
			
		||||
			fmt.Errorf("Failed to open DB. err: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	for _, cveID := range cveIDs {
 | 
			
		||||
		cveDetail := cvedb.Get(cveID)
 | 
			
		||||
		if len(cveDetail.CveID) == 0 {
 | 
			
		||||
			cveDetails = append(cveDetails, cve.CveDetail{
 | 
			
		||||
				CveID: cveID,
 | 
			
		||||
			})
 | 
			
		||||
		} else {
 | 
			
		||||
			cveDetails = append(cveDetails, cveDetail)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// order by CVE ID desc
 | 
			
		||||
	sort.Sort(cveDetails)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api cvedictClient) httpGet(key, url string, resChan chan<- response, errChan chan<- error) {
 | 
			
		||||
	var body string
 | 
			
		||||
	var errs []error
 | 
			
		||||
	var resp *http.Response
 | 
			
		||||
	f := func() (err error) {
 | 
			
		||||
		//  resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
 | 
			
		||||
		resp, body, errs = gorequest.New().Get(url).End()
 | 
			
		||||
		if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
			return fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v", errs, url, resp)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	notify := func(err error, t time.Duration) {
 | 
			
		||||
		log.Warnf("Failed to HTTP GET. retrying in %s seconds. err: %s", t, err)
 | 
			
		||||
	}
 | 
			
		||||
	err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		errChan <- fmt.Errorf("HTTP Error %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	cveDetail := cve.CveDetail{}
 | 
			
		||||
	if err := json.Unmarshal([]byte(body), &cveDetail); err != nil {
 | 
			
		||||
		errChan <- fmt.Errorf("Failed to Unmarshall. body: %s, err: %s", body, err)
 | 
			
		||||
	}
 | 
			
		||||
	resChan <- response{
 | 
			
		||||
		key,
 | 
			
		||||
		cveDetail,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//  func (api cvedictClient) httpGet(key, url string, query map[string]string, resChan chan<- response, errChan chan<- error) {
 | 
			
		||||
 | 
			
		||||
//      var body string
 | 
			
		||||
//      var errs []error
 | 
			
		||||
//      var resp *http.Response
 | 
			
		||||
//      f := func() (err error) {
 | 
			
		||||
//          req := gorequest.New().SetDebug(true).Proxy(api.httpProxy).Get(url)
 | 
			
		||||
//          for key := range query {
 | 
			
		||||
//              req = req.Query(fmt.Sprintf("%s=%s", key, query[key])).Set("Content-Type", "application/x-www-form-urlencoded")
 | 
			
		||||
//          }
 | 
			
		||||
//          pp.Println(req)
 | 
			
		||||
//          resp, body, errs = req.End()
 | 
			
		||||
//          if len(errs) > 0 || resp.StatusCode != 200 {
 | 
			
		||||
//              errChan <- fmt.Errorf("HTTP error. errs: %v, url: %s", errs, url)
 | 
			
		||||
//          }
 | 
			
		||||
//          return nil
 | 
			
		||||
//      }
 | 
			
		||||
//      notify := func(err error, t time.Duration) {
 | 
			
		||||
//          log.Warnf("Failed to get. retrying in %s seconds. err: %s", t, err)
 | 
			
		||||
//      }
 | 
			
		||||
//      err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
 | 
			
		||||
//      if err != nil {
 | 
			
		||||
//          errChan <- fmt.Errorf("HTTP Error %s", err)
 | 
			
		||||
//      }
 | 
			
		||||
//      //  resChan <- body
 | 
			
		||||
//      cveDetail := cve.CveDetail{}
 | 
			
		||||
//      if err := json.Unmarshal([]byte(body), &cveDetail); err != nil {
 | 
			
		||||
//          errChan <- fmt.Errorf("Failed to Unmarshall. body: %s, err: %s", body, err)
 | 
			
		||||
//      }
 | 
			
		||||
//      resChan <- response{
 | 
			
		||||
//          key,
 | 
			
		||||
//          cveDetail,
 | 
			
		||||
//      }
 | 
			
		||||
//  }
 | 
			
		||||
 | 
			
		||||
type responseGetCveDetailByCpeName struct {
 | 
			
		||||
	CpeName    string
 | 
			
		||||
	CveDetails []cve.CveDetail
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api cvedictClient) FetchCveDetailsByCpeName(cpeName string) ([]cve.CveDetail, error) {
 | 
			
		||||
	if config.Conf.CveDBPath != "" {
 | 
			
		||||
		return api.FetchCveDetailsByCpeNameFromDB(cpeName)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	api.baseURL = config.Conf.CveDictionaryURL
 | 
			
		||||
	url, err := util.URLPathJoin(api.baseURL, "cpes")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return []cve.CveDetail{}, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	query := map[string]string{"name": cpeName}
 | 
			
		||||
	log.Debugf("HTTP Request to %s, query: %#v", url, query)
 | 
			
		||||
	return api.httpPost(cpeName, url, query)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api cvedictClient) httpPost(key, url string, query map[string]string) ([]cve.CveDetail, error) {
 | 
			
		||||
	var body string
 | 
			
		||||
	var errs []error
 | 
			
		||||
	var resp *http.Response
 | 
			
		||||
	f := func() (err error) {
 | 
			
		||||
		req := gorequest.New().SetDebug(config.Conf.Debug).Post(url)
 | 
			
		||||
		for key := range query {
 | 
			
		||||
			req = req.Send(fmt.Sprintf("%s=%s", key, query[key])).Type("json")
 | 
			
		||||
		}
 | 
			
		||||
		resp, body, errs = req.End()
 | 
			
		||||
		if len(errs) > 0 || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
			return fmt.Errorf("HTTP POST error: %v, url: %s, resp: %v", errs, url, resp)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	notify := func(err error, t time.Duration) {
 | 
			
		||||
		log.Warnf("Failed to HTTP POST. retrying in %s seconds. err: %s", t, err)
 | 
			
		||||
	}
 | 
			
		||||
	err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return []cve.CveDetail{}, fmt.Errorf("HTTP Error %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cveDetails := []cve.CveDetail{}
 | 
			
		||||
	if err := json.Unmarshal([]byte(body), &cveDetails); err != nil {
 | 
			
		||||
		return []cve.CveDetail{},
 | 
			
		||||
			fmt.Errorf("Failed to Unmarshall. body: %s, err: %s", body, err)
 | 
			
		||||
	}
 | 
			
		||||
	return cveDetails, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api cvedictClient) FetchCveDetailsByCpeNameFromDB(cpeName string) ([]cve.CveDetail, error) {
 | 
			
		||||
	log.Debugf("open cve-dictionary db")
 | 
			
		||||
	cveconfig.Conf.DBPath = config.Conf.CveDBPath
 | 
			
		||||
	if err := cvedb.OpenDB(); err != nil {
 | 
			
		||||
		return []cve.CveDetail{},
 | 
			
		||||
			fmt.Errorf("Failed to open DB. err: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	return cvedb.GetByCpeName(cpeName), nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										65
									
								
								cwe/owasp.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,65 @@
 | 
			
		||||
package cwe
 | 
			
		||||
 | 
			
		||||
// OwaspTopTen2017 has CWE-ID in OWSP Top 10
 | 
			
		||||
var OwaspTopTen2017 = map[string]string{
 | 
			
		||||
	"77":  "1",
 | 
			
		||||
	"89":  "1",
 | 
			
		||||
	"564": "1",
 | 
			
		||||
	"917": "1",
 | 
			
		||||
 | 
			
		||||
	"287": "2",
 | 
			
		||||
	"384": "2",
 | 
			
		||||
 | 
			
		||||
	"220": "3",
 | 
			
		||||
	"310": "3",
 | 
			
		||||
	"312": "3",
 | 
			
		||||
	"319": "3",
 | 
			
		||||
	"326": "3",
 | 
			
		||||
	"359": "3",
 | 
			
		||||
 | 
			
		||||
	"611": "4",
 | 
			
		||||
 | 
			
		||||
	"22":  "5",
 | 
			
		||||
	"284": "5",
 | 
			
		||||
	"285": "5",
 | 
			
		||||
	"639": "5",
 | 
			
		||||
 | 
			
		||||
	"2":   "6",
 | 
			
		||||
	"16":  "6",
 | 
			
		||||
	"388": "6",
 | 
			
		||||
 | 
			
		||||
	"79": "7",
 | 
			
		||||
 | 
			
		||||
	"502": "8",
 | 
			
		||||
 | 
			
		||||
	"223": "10",
 | 
			
		||||
	"778": "10",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OwaspTopTen2017GitHubURLEn has GitHub links
 | 
			
		||||
var OwaspTopTen2017GitHubURLEn = map[string]string{
 | 
			
		||||
	"1":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa1-injection.md",
 | 
			
		||||
	"2":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa2-broken-authentication.md",
 | 
			
		||||
	"3":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa3-sensitive-data-disclosure.md",
 | 
			
		||||
	"4":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa4-xxe.md",
 | 
			
		||||
	"5":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa5-broken-access-control.md",
 | 
			
		||||
	"6":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa6-security-misconfiguration.md",
 | 
			
		||||
	"7":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa7-xss.md",
 | 
			
		||||
	"8":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa8-insecure-deserialization.md",
 | 
			
		||||
	"9":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa9-known-vulns.md<Paste>",
 | 
			
		||||
	"10": "https://github.com/OWASP/Top10/blob/master/2017/en/0xaa-logging-detection-response.md",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OwaspTopTen2017GitHubURLJa has GitHub links
 | 
			
		||||
var OwaspTopTen2017GitHubURLJa = map[string]string{
 | 
			
		||||
	"1":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa1-injection.md",
 | 
			
		||||
	"2":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa2-broken-authentication.md",
 | 
			
		||||
	"3":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa3-sensitive-data-disclosure.md",
 | 
			
		||||
	"4":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa4-xxe.md",
 | 
			
		||||
	"5":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa5-broken-access-control.md",
 | 
			
		||||
	"6":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa6-security-misconfiguration.md",
 | 
			
		||||
	"7":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa7-xss.md",
 | 
			
		||||
	"8":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa8-insecure-deserialization.md",
 | 
			
		||||
	"9":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa9-known-vulns.md<Paste>",
 | 
			
		||||
	"10": "https://github.com/OWASP/Top10/blob/master/2017/ja/0xaa-logging-detection-response.md",
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										324
									
								
								db/db.go
									
									
									
									
									
								
							
							
						
						@@ -1,324 +0,0 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package db
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	m "github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/jinzhu/gorm"
 | 
			
		||||
	cvedb "github.com/kotakanbe/go-cve-dictionary/db"
 | 
			
		||||
	cve "github.com/kotakanbe/go-cve-dictionary/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var db *gorm.DB
 | 
			
		||||
 | 
			
		||||
// OpenDB opens Database
 | 
			
		||||
func OpenDB() (err error) {
 | 
			
		||||
	db, err = gorm.Open("sqlite3", config.Conf.DBPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err = fmt.Errorf("Failed to open DB. datafile: %s, err: %s", config.Conf.DBPath, err)
 | 
			
		||||
		return
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
	db.LogMode(config.Conf.DebugSQL)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MigrateDB migrates Database
 | 
			
		||||
func MigrateDB() error {
 | 
			
		||||
	if err := db.AutoMigrate(
 | 
			
		||||
		&m.ScanHistory{},
 | 
			
		||||
		&m.ScanResult{},
 | 
			
		||||
		//  &m.NWLink{},
 | 
			
		||||
		&m.Container{},
 | 
			
		||||
		&m.CveInfo{},
 | 
			
		||||
		&m.CpeName{},
 | 
			
		||||
		&m.PackageInfo{},
 | 
			
		||||
		&m.DistroAdvisory{},
 | 
			
		||||
		&cve.CveDetail{},
 | 
			
		||||
		&cve.Jvn{},
 | 
			
		||||
		&cve.Nvd{},
 | 
			
		||||
		&cve.Reference{},
 | 
			
		||||
		&cve.Cpe{},
 | 
			
		||||
	).Error; err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to migrate. err: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	errMsg := "Failed to create index. err: %s"
 | 
			
		||||
	//  if err := db.Model(&m.NWLink{}).
 | 
			
		||||
	//      AddIndex("idx_n_w_links_scan_result_id", "scan_result_id").Error; err != nil {
 | 
			
		||||
	//      return fmt.Errorf(errMsg, err)
 | 
			
		||||
	//  }
 | 
			
		||||
	if err := db.Model(&m.Container{}).
 | 
			
		||||
		AddIndex("idx_containers_scan_result_id", "scan_result_id").Error; err != nil {
 | 
			
		||||
		return fmt.Errorf(errMsg, err)
 | 
			
		||||
	}
 | 
			
		||||
	if err := db.Model(&m.CveInfo{}).
 | 
			
		||||
		AddIndex("idx_cve_infos_scan_result_id", "scan_result_id").Error; err != nil {
 | 
			
		||||
		return fmt.Errorf(errMsg, err)
 | 
			
		||||
	}
 | 
			
		||||
	if err := db.Model(&m.CpeName{}).
 | 
			
		||||
		AddIndex("idx_cpe_names_cve_info_id", "cve_info_id").Error; err != nil {
 | 
			
		||||
		return fmt.Errorf(errMsg, err)
 | 
			
		||||
	}
 | 
			
		||||
	if err := db.Model(&m.PackageInfo{}).
 | 
			
		||||
		AddIndex("idx_package_infos_cve_info_id", "cve_info_id").Error; err != nil {
 | 
			
		||||
		return fmt.Errorf(errMsg, err)
 | 
			
		||||
	}
 | 
			
		||||
	if err := db.Model(&m.DistroAdvisory{}).
 | 
			
		||||
		//TODO check table name
 | 
			
		||||
		AddIndex("idx_distro_advisories_cve_info_id", "cve_info_id").Error; err != nil {
 | 
			
		||||
		return fmt.Errorf(errMsg, err)
 | 
			
		||||
	}
 | 
			
		||||
	if err := db.Model(&cve.CveDetail{}).
 | 
			
		||||
		AddIndex("idx_cve_details_cve_info_id", "cve_info_id").Error; err != nil {
 | 
			
		||||
		return fmt.Errorf(errMsg, err)
 | 
			
		||||
	}
 | 
			
		||||
	if err := db.Model(&cve.CveDetail{}).
 | 
			
		||||
		AddIndex("idx_cve_details_cveid", "cve_id").Error; err != nil {
 | 
			
		||||
		return fmt.Errorf(errMsg, err)
 | 
			
		||||
	}
 | 
			
		||||
	if err := db.Model(&cve.Nvd{}).
 | 
			
		||||
		AddIndex("idx_nvds_cve_detail_id", "cve_detail_id").Error; err != nil {
 | 
			
		||||
		return fmt.Errorf(errMsg, err)
 | 
			
		||||
	}
 | 
			
		||||
	if err := db.Model(&cve.Jvn{}).
 | 
			
		||||
		AddIndex("idx_jvns_cve_detail_id", "cve_detail_id").Error; err != nil {
 | 
			
		||||
		return fmt.Errorf(errMsg, err)
 | 
			
		||||
	}
 | 
			
		||||
	if err := db.Model(&cve.Cpe{}).
 | 
			
		||||
		AddIndex("idx_cpes_jvn_id", "jvn_id").Error; err != nil {
 | 
			
		||||
		return fmt.Errorf(errMsg, err)
 | 
			
		||||
	}
 | 
			
		||||
	if err := db.Model(&cve.Reference{}).
 | 
			
		||||
		AddIndex("idx_references_jvn_id", "jvn_id").Error; err != nil {
 | 
			
		||||
		return fmt.Errorf(errMsg, err)
 | 
			
		||||
	}
 | 
			
		||||
	if err := db.Model(&cve.Cpe{}).
 | 
			
		||||
		AddIndex("idx_cpes_nvd_id", "nvd_id").Error; err != nil {
 | 
			
		||||
		return fmt.Errorf(errMsg, err)
 | 
			
		||||
	}
 | 
			
		||||
	if err := db.Model(&cve.Reference{}).
 | 
			
		||||
		AddIndex("idx_references_nvd_id", "nvd_id").Error; err != nil {
 | 
			
		||||
		return fmt.Errorf(errMsg, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Insert inserts scan results into DB
 | 
			
		||||
func Insert(results []m.ScanResult) error {
 | 
			
		||||
	for _, r := range results {
 | 
			
		||||
		r.KnownCves = resetGormIDs(r.KnownCves)
 | 
			
		||||
		r.UnknownCves = resetGormIDs(r.UnknownCves)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	history := m.ScanHistory{
 | 
			
		||||
		ScanResults: results,
 | 
			
		||||
		ScannedAt:   time.Now(),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	db = db.Set("gorm:save_associations", false)
 | 
			
		||||
	if err := db.Create(&history).Error; err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	for _, scanResult := range history.ScanResults {
 | 
			
		||||
		scanResult.ScanHistoryID = history.ID
 | 
			
		||||
		if err := db.Create(&scanResult).Error; err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		scanResult.Container.ScanResultID = scanResult.ID
 | 
			
		||||
		if err := db.Create(&scanResult.Container).Error; err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if err := insertCveInfos(scanResult.ID, scanResult.KnownCves); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if err := insertCveInfos(scanResult.ID, scanResult.UnknownCves); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func insertCveInfos(scanResultID uint, infos []m.CveInfo) error {
 | 
			
		||||
	for _, cveInfo := range infos {
 | 
			
		||||
		cveInfo.ScanResultID = scanResultID
 | 
			
		||||
		if err := db.Create(&cveInfo).Error; err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, pack := range cveInfo.Packages {
 | 
			
		||||
			pack.CveInfoID = cveInfo.ID
 | 
			
		||||
			if err := db.Create(&pack).Error; err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, distroAdvisory := range cveInfo.DistroAdvisories {
 | 
			
		||||
			distroAdvisory.CveInfoID = cveInfo.ID
 | 
			
		||||
			if err := db.Create(&distroAdvisory).Error; err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, cpeName := range cveInfo.CpeNames {
 | 
			
		||||
			cpeName.CveInfoID = cveInfo.ID
 | 
			
		||||
			if err := db.Create(&cpeName).Error; err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		db = db.Set("gorm:save_associations", true)
 | 
			
		||||
		cveDetail := cveInfo.CveDetail
 | 
			
		||||
		cveDetail.CveInfoID = cveInfo.ID
 | 
			
		||||
		if err := db.Create(&cveDetail).Error; err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		db = db.Set("gorm:save_associations", false)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func resetGormIDs(infos []m.CveInfo) []m.CveInfo {
 | 
			
		||||
	for i := range infos {
 | 
			
		||||
		infos[i].CveDetail.ID = 0
 | 
			
		||||
		// NVD
 | 
			
		||||
		infos[i].CveDetail.Nvd.ID = 0
 | 
			
		||||
		for j := range infos[i].CveDetail.Nvd.Cpes {
 | 
			
		||||
			infos[i].CveDetail.Nvd.Cpes[j].ID = 0
 | 
			
		||||
		}
 | 
			
		||||
		for j := range infos[i].CveDetail.Nvd.References {
 | 
			
		||||
			infos[i].CveDetail.Nvd.References[j].ID = 0
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// JVN
 | 
			
		||||
		infos[i].CveDetail.Jvn.ID = 0
 | 
			
		||||
		for j := range infos[i].CveDetail.Jvn.Cpes {
 | 
			
		||||
			infos[i].CveDetail.Jvn.Cpes[j].ID = 0
 | 
			
		||||
		}
 | 
			
		||||
		for j := range infos[i].CveDetail.Jvn.References {
 | 
			
		||||
			infos[i].CveDetail.Jvn.References[j].ID = 0
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		//Packages
 | 
			
		||||
		for j := range infos[i].Packages {
 | 
			
		||||
			infos[i].Packages[j].ID = 0
 | 
			
		||||
			infos[i].Packages[j].CveInfoID = 0
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return infos
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SelectScanHistory select scan history from DB
 | 
			
		||||
func SelectScanHistory(historyID string) (m.ScanHistory, error) {
 | 
			
		||||
	var err error
 | 
			
		||||
 | 
			
		||||
	scanHistory := m.ScanHistory{}
 | 
			
		||||
	if historyID == "" {
 | 
			
		||||
		// select latest
 | 
			
		||||
		db.Order("scanned_at desc").First(&scanHistory)
 | 
			
		||||
	} else {
 | 
			
		||||
		var id int
 | 
			
		||||
		if id, err = strconv.Atoi(historyID); err != nil {
 | 
			
		||||
			return m.ScanHistory{},
 | 
			
		||||
				fmt.Errorf("historyID have to be numeric number: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
		db.First(&scanHistory, id)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if scanHistory.ID == 0 {
 | 
			
		||||
		return m.ScanHistory{}, fmt.Errorf("No scanHistory records")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//  results := []m.ScanResult{}
 | 
			
		||||
	results := m.ScanResults{}
 | 
			
		||||
	db.Model(&scanHistory).Related(&results, "ScanResults")
 | 
			
		||||
	scanHistory.ScanResults = results
 | 
			
		||||
 | 
			
		||||
	for i, r := range results {
 | 
			
		||||
		//  nw := []m.NWLink{}
 | 
			
		||||
		//  db.Model(&r).Related(&nw, "NWLinks")
 | 
			
		||||
		//  scanHistory.ScanResults[i].NWLinks = nw
 | 
			
		||||
 | 
			
		||||
		di := m.Container{}
 | 
			
		||||
		db.Model(&r).Related(&di, "Container")
 | 
			
		||||
		scanHistory.ScanResults[i].Container = di
 | 
			
		||||
 | 
			
		||||
		knownCves := selectCveInfos(&r, "KnownCves")
 | 
			
		||||
		sort.Sort(m.CveInfos(knownCves))
 | 
			
		||||
		scanHistory.ScanResults[i].KnownCves = knownCves
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sort.Sort(scanHistory.ScanResults)
 | 
			
		||||
	return scanHistory, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func selectCveInfos(result *m.ScanResult, fieldName string) []m.CveInfo {
 | 
			
		||||
	cveInfos := []m.CveInfo{}
 | 
			
		||||
	db.Model(&result).Related(&cveInfos, fieldName)
 | 
			
		||||
 | 
			
		||||
	for i, cveInfo := range cveInfos {
 | 
			
		||||
		cveDetail := cve.CveDetail{}
 | 
			
		||||
		db.Model(&cveInfo).Related(&cveDetail, "CveDetail")
 | 
			
		||||
		id := cveDetail.CveID
 | 
			
		||||
		filledCveDetail := cvedb.Get(id, db)
 | 
			
		||||
		cveInfos[i].CveDetail = filledCveDetail
 | 
			
		||||
 | 
			
		||||
		packs := []m.PackageInfo{}
 | 
			
		||||
		db.Model(&cveInfo).Related(&packs, "Packages")
 | 
			
		||||
		cveInfos[i].Packages = packs
 | 
			
		||||
 | 
			
		||||
		advisories := []m.DistroAdvisory{}
 | 
			
		||||
		db.Model(&cveInfo).Related(&advisories, "DistroAdvisories")
 | 
			
		||||
		cveInfos[i].DistroAdvisories = advisories
 | 
			
		||||
 | 
			
		||||
		names := []m.CpeName{}
 | 
			
		||||
		db.Model(&cveInfo).Related(&names, "CpeNames")
 | 
			
		||||
		cveInfos[i].CpeNames = names
 | 
			
		||||
	}
 | 
			
		||||
	return cveInfos
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SelectScanHistories select latest scan history from DB
 | 
			
		||||
func SelectScanHistories() ([]m.ScanHistory, error) {
 | 
			
		||||
	scanHistories := []m.ScanHistory{}
 | 
			
		||||
	db.Order("scanned_at desc").Find(&scanHistories)
 | 
			
		||||
 | 
			
		||||
	if len(scanHistories) == 0 {
 | 
			
		||||
		return []m.ScanHistory{}, fmt.Errorf("No scanHistory records")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, history := range scanHistories {
 | 
			
		||||
		results := m.ScanResults{}
 | 
			
		||||
		db.Model(&history).Related(&results, "ScanResults")
 | 
			
		||||
		scanHistories[i].ScanResults = results
 | 
			
		||||
 | 
			
		||||
		for j, r := range results {
 | 
			
		||||
			di := m.Container{}
 | 
			
		||||
			db.Model(&r).Related(&di, "Container")
 | 
			
		||||
			scanHistories[i].ScanResults[j].Container = di
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return scanHistories, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								errof/errof.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,27 @@
 | 
			
		||||
package errof
 | 
			
		||||
 | 
			
		||||
// ErrorCode is vuls error code
 | 
			
		||||
type ErrorCode string
 | 
			
		||||
 | 
			
		||||
// Error is vuls error
 | 
			
		||||
type Error struct {
 | 
			
		||||
	Code    ErrorCode
 | 
			
		||||
	Message string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e Error) Error() string {
 | 
			
		||||
	return e.Message
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// ErrFailedToAccessGithubAPI is error of github alert's api access
 | 
			
		||||
	ErrFailedToAccessGithubAPI ErrorCode = "ErrFailedToAccessGithubAPI"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// New :
 | 
			
		||||
func New(code ErrorCode, msg string) Error {
 | 
			
		||||
	return Error{
 | 
			
		||||
		Code:    code,
 | 
			
		||||
		Message: msg,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										131
									
								
								exploit/exploit.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,131 @@
 | 
			
		||||
/* 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 exploit
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	cnf "github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/mozqnet/go-exploitdb/db"
 | 
			
		||||
	exploitmodels "github.com/mozqnet/go-exploitdb/models"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FillWithExploit fills exploit information that has in Exploit
 | 
			
		||||
func FillWithExploit(driver db.DB, r *models.ScanResult) (nExploitCve int, err error) {
 | 
			
		||||
	if cnf.Conf.Exploit.IsFetchViaHTTP() {
 | 
			
		||||
		var cveIDs []string
 | 
			
		||||
		for cveID := range r.ScannedCves {
 | 
			
		||||
			cveIDs = append(cveIDs, cveID)
 | 
			
		||||
		}
 | 
			
		||||
		prefix, _ := util.URLPathJoin(cnf.Conf.Exploit.URL, "cves")
 | 
			
		||||
		responses, err := getCvesViaHTTP(cveIDs, prefix)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		for _, res := range responses {
 | 
			
		||||
			exps := []*exploitmodels.Exploit{}
 | 
			
		||||
			if err := json.Unmarshal([]byte(res.json), &exps); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
			}
 | 
			
		||||
			exploits := ConvertToModels(exps)
 | 
			
		||||
			v, ok := r.ScannedCves[res.request.cveID]
 | 
			
		||||
			if ok {
 | 
			
		||||
				v.Exploits = exploits
 | 
			
		||||
			}
 | 
			
		||||
			r.ScannedCves[res.request.cveID] = v
 | 
			
		||||
			nExploitCve++
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if driver == nil {
 | 
			
		||||
			return 0, nil
 | 
			
		||||
		}
 | 
			
		||||
		for cveID, vuln := range r.ScannedCves {
 | 
			
		||||
			es := driver.GetExploitByCveID(cveID)
 | 
			
		||||
			if len(es) == 0 {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			exploits := ConvertToModels(es)
 | 
			
		||||
			vuln.Exploits = exploits
 | 
			
		||||
			r.ScannedCves[cveID] = vuln
 | 
			
		||||
			nExploitCve++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nExploitCve, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConvertToModels converts gost model to vuls model
 | 
			
		||||
func ConvertToModels(es []*exploitmodels.Exploit) (exploits []models.Exploit) {
 | 
			
		||||
	for _, e := range es {
 | 
			
		||||
		var documentURL, shellURL *string
 | 
			
		||||
		if e.OffensiveSecurity != nil {
 | 
			
		||||
			os := e.OffensiveSecurity
 | 
			
		||||
			if os.Document != nil {
 | 
			
		||||
				documentURL = &os.Document.DocumentURL
 | 
			
		||||
			}
 | 
			
		||||
			if os.ShellCode != nil {
 | 
			
		||||
				shellURL = &os.ShellCode.ShellCodeURL
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		exploit := models.Exploit{
 | 
			
		||||
			ExploitType:  e.ExploitType,
 | 
			
		||||
			ID:           e.ExploitUniqueID,
 | 
			
		||||
			URL:          e.URL,
 | 
			
		||||
			Description:  e.Description,
 | 
			
		||||
			DocumentURL:  documentURL,
 | 
			
		||||
			ShellCodeURL: shellURL,
 | 
			
		||||
		}
 | 
			
		||||
		exploits = append(exploits, exploit)
 | 
			
		||||
	}
 | 
			
		||||
	return exploits
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckHTTPHealth do health check
 | 
			
		||||
func CheckHTTPHealth() error {
 | 
			
		||||
	if !cnf.Conf.Exploit.IsFetchViaHTTP() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	url := fmt.Sprintf("%s/health", cnf.Conf.Exploit.URL)
 | 
			
		||||
	var errs []error
 | 
			
		||||
	var resp *http.Response
 | 
			
		||||
	resp, _, errs = gorequest.New().Get(url).End()
 | 
			
		||||
	//  resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
 | 
			
		||||
	//  resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
 | 
			
		||||
	if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
		return xerrors.Errorf("Failed to connect to exploit server. url: %s, errs: %w", url, errs)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckIfExploitFetched checks if oval entries are in DB by family, release.
 | 
			
		||||
func CheckIfExploitFetched(driver db.DB, osFamily string) (fetched bool, err error) {
 | 
			
		||||
	//TODO
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckIfExploitFresh checks if oval entries are fresh enough
 | 
			
		||||
func CheckIfExploitFresh(driver db.DB, osFamily string) (ok bool, err error) {
 | 
			
		||||
	//TODO
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								exploit/exploit_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,8 @@
 | 
			
		||||
package exploit
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestSetPackageStates(t *testing.T) {
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										132
									
								
								exploit/util.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,132 @@
 | 
			
		||||
/* 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 exploit
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cenkalti/backoff"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type response struct {
 | 
			
		||||
	request request
 | 
			
		||||
	json    string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getCvesViaHTTP(cveIDs []string, urlPrefix string) (
 | 
			
		||||
	responses []response, err error) {
 | 
			
		||||
	nReq := len(cveIDs)
 | 
			
		||||
	reqChan := make(chan request, nReq)
 | 
			
		||||
	resChan := make(chan response, nReq)
 | 
			
		||||
	errChan := make(chan error, nReq)
 | 
			
		||||
	defer close(reqChan)
 | 
			
		||||
	defer close(resChan)
 | 
			
		||||
	defer close(errChan)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		for _, cveID := range cveIDs {
 | 
			
		||||
			reqChan <- request{
 | 
			
		||||
				cveID: cveID,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	concurrency := 10
 | 
			
		||||
	tasks := util.GenWorkers(concurrency)
 | 
			
		||||
	for i := 0; i < nReq; i++ {
 | 
			
		||||
		tasks <- func() {
 | 
			
		||||
			select {
 | 
			
		||||
			case req := <-reqChan:
 | 
			
		||||
				url, err := util.URLPathJoin(
 | 
			
		||||
					urlPrefix,
 | 
			
		||||
					req.cveID,
 | 
			
		||||
				)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					errChan <- err
 | 
			
		||||
				} else {
 | 
			
		||||
					util.Log.Debugf("HTTP Request to %s", url)
 | 
			
		||||
					httpGet(url, req, resChan, errChan)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	timeout := time.After(2 * 60 * time.Second)
 | 
			
		||||
	var errs []error
 | 
			
		||||
	for i := 0; i < nReq; i++ {
 | 
			
		||||
		select {
 | 
			
		||||
		case res := <-resChan:
 | 
			
		||||
			responses = append(responses, res)
 | 
			
		||||
		case err := <-errChan:
 | 
			
		||||
			errs = append(errs, err)
 | 
			
		||||
		case <-timeout:
 | 
			
		||||
			return nil, xerrors.New("Timeout Fetching OVAL")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(errs) != 0 {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to fetch OVAL. err: %w", errs)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type request struct {
 | 
			
		||||
	osMajorVersion string
 | 
			
		||||
	packName       string
 | 
			
		||||
	isSrcPack      bool
 | 
			
		||||
	cveID          string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func httpGet(url string, req request, resChan chan<- response, errChan chan<- error) {
 | 
			
		||||
	var body string
 | 
			
		||||
	var errs []error
 | 
			
		||||
	var resp *http.Response
 | 
			
		||||
	count, retryMax := 0, 3
 | 
			
		||||
	f := func() (err error) {
 | 
			
		||||
		//  resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
 | 
			
		||||
		resp, body, errs = gorequest.New().Get(url).End()
 | 
			
		||||
		if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
			count++
 | 
			
		||||
			if count == retryMax {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			return xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %w", url, resp, errs)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	notify := func(err error, t time.Duration) {
 | 
			
		||||
		util.Log.Warnf("Failed to HTTP GET. retrying in %s seconds. err: %s", t, err)
 | 
			
		||||
	}
 | 
			
		||||
	err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		errChan <- xerrors.Errorf("HTTP Error %w", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if count == retryMax {
 | 
			
		||||
		errChan <- xerrors.New("Retry count exceeded")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resChan <- response{
 | 
			
		||||
		request: req,
 | 
			
		||||
		json:    body,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										151
									
								
								github/github.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,151 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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 github
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"context"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/errof"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/k0kubun/pp"
 | 
			
		||||
	"golang.org/x/oauth2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FillGitHubSecurityAlerts access to owner/repo on GitHub and fetch scurity alerts of the repository via GitHub API v4 GraphQL and then set to the given ScanResult.
 | 
			
		||||
// https://help.github.com/articles/about-security-alerts-for-vulnerable-dependencies/
 | 
			
		||||
func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (nCVEs int, err error) {
 | 
			
		||||
	src := oauth2.StaticTokenSource(
 | 
			
		||||
		&oauth2.Token{AccessToken: token},
 | 
			
		||||
	)
 | 
			
		||||
	httpClient := oauth2.NewClient(context.Background(), src)
 | 
			
		||||
 | 
			
		||||
	// TODO Use `https://github.com/shurcooL/githubv4` if the tool supports vulnerabilityAlerts Endpoint
 | 
			
		||||
	const jsonfmt = `{"query":
 | 
			
		||||
	"query { repository(owner:\"%s\", name:\"%s\") { url, vulnerabilityAlerts(first: %d, %s) { pageInfo{ endCursor, hasNextPage, startCursor}, edges { node { id, externalIdentifier, externalReference, fixedIn, packageName,  dismissReason, dismissedAt } } } } }"}`
 | 
			
		||||
	after := ""
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		jsonStr := fmt.Sprintf(jsonfmt, owner, repo, 100, after)
 | 
			
		||||
		req, err := http.NewRequest("POST",
 | 
			
		||||
			"https://api.github.com/graphql",
 | 
			
		||||
			bytes.NewBuffer([]byte(jsonStr)),
 | 
			
		||||
		)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// https://developer.github.com/v4/previews/#repository-vulnerability-alerts
 | 
			
		||||
		// To toggle this preview and access data, need to provide a custom media type in the Accept header:
 | 
			
		||||
		// MEMO: I tried to get the affected version via GitHub API. Bit it seems difficult to determin the affected version if there are multiple dependency files such as package.json.
 | 
			
		||||
		// TODO remove this header if it is no longer preview status in the future.
 | 
			
		||||
		req.Header.Set("Accept", "application/vnd.github.vixen-preview+json")
 | 
			
		||||
		req.Header.Set("Content-Type", "application/json")
 | 
			
		||||
 | 
			
		||||
		resp, err := httpClient.Do(req)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		defer resp.Body.Close()
 | 
			
		||||
		alerts := SecurityAlerts{}
 | 
			
		||||
		if json.NewDecoder(resp.Body).Decode(&alerts); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		util.Log.Debugf("%s", pp.Sprint(alerts))
 | 
			
		||||
		if alerts.Data.Repository.URL == "" {
 | 
			
		||||
			return 0, errof.New(
 | 
			
		||||
				errof.ErrFailedToAccessGithubAPI,
 | 
			
		||||
				fmt.Sprintf("Failed to access to GitHub API. Response: %#v", alerts),
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, v := range alerts.Data.Repository.VulnerabilityAlerts.Edges {
 | 
			
		||||
			if config.Conf.IgnoreGitHubDismissed && v.Node.DismissReason != "" {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			pkgName := fmt.Sprintf("%s %s",
 | 
			
		||||
				alerts.Data.Repository.URL, v.Node.PackageName)
 | 
			
		||||
 | 
			
		||||
			m := models.GitHubSecurityAlert{
 | 
			
		||||
				PackageName:   pkgName,
 | 
			
		||||
				FixedIn:       v.Node.FixedIn,
 | 
			
		||||
				AffectedRange: v.Node.AffectedRange,
 | 
			
		||||
				Dismissed:     len(v.Node.DismissReason) != 0,
 | 
			
		||||
				DismissedAt:   v.Node.DismissedAt,
 | 
			
		||||
				DismissReason: v.Node.DismissReason,
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			cveID := v.Node.ExternalIdentifier
 | 
			
		||||
 | 
			
		||||
			if val, ok := r.ScannedCves[cveID]; ok {
 | 
			
		||||
				val.GitHubSecurityAlerts = val.GitHubSecurityAlerts.Add(m)
 | 
			
		||||
				r.ScannedCves[cveID] = val
 | 
			
		||||
				nCVEs++
 | 
			
		||||
			} else {
 | 
			
		||||
				v := models.VulnInfo{
 | 
			
		||||
					CveID:                cveID,
 | 
			
		||||
					Confidences:          models.Confidences{models.GitHubMatch},
 | 
			
		||||
					GitHubSecurityAlerts: models.GitHubSecurityAlerts{m},
 | 
			
		||||
				}
 | 
			
		||||
				r.ScannedCves[cveID] = v
 | 
			
		||||
				nCVEs++
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if !alerts.Data.Repository.VulnerabilityAlerts.PageInfo.HasNextPage {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		after = fmt.Sprintf(`after: \"%s\"`, alerts.Data.Repository.VulnerabilityAlerts.PageInfo.EndCursor)
 | 
			
		||||
	}
 | 
			
		||||
	return nCVEs, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//SecurityAlerts has detected CVE-IDs, PackageNames, Refs
 | 
			
		||||
type SecurityAlerts struct {
 | 
			
		||||
	Data struct {
 | 
			
		||||
		Repository struct {
 | 
			
		||||
			URL                 string `json:"url,omitempty"`
 | 
			
		||||
			VulnerabilityAlerts struct {
 | 
			
		||||
				PageInfo struct {
 | 
			
		||||
					EndCursor   string `json:"endCursor,omitempty"`
 | 
			
		||||
					HasNextPage bool   `json:"hasNextPage,omitempty"`
 | 
			
		||||
					StartCursor string `json:"startCursor,omitempty"`
 | 
			
		||||
				} `json:"pageInfo,omitempty"`
 | 
			
		||||
				Edges []struct {
 | 
			
		||||
					Node struct {
 | 
			
		||||
						ID                 string    `json:"id,omitempty"`
 | 
			
		||||
						ExternalIdentifier string    `json:"externalIdentifier,omitempty"`
 | 
			
		||||
						ExternalReference  string    `json:"externalReference,omitempty"`
 | 
			
		||||
						FixedIn            string    `json:"fixedIn,omitempty"`
 | 
			
		||||
						AffectedRange      string    `json:"affectedRange,omitempty"`
 | 
			
		||||
						PackageName        string    `json:"packageName,omitempty"`
 | 
			
		||||
						DismissReason      string    `json:"dismissReason,omitempty"`
 | 
			
		||||
						DismissedAt        time.Time `json:"dismissedAt,omitempty"`
 | 
			
		||||
					} `json:"node,omitempty"`
 | 
			
		||||
				} `json:"edges,omitempty"`
 | 
			
		||||
			} `json:"vulnerabilityAlerts,omitempty"`
 | 
			
		||||
		} `json:"repository,omitempty"`
 | 
			
		||||
	} `json:"data,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										117
									
								
								glide.lock
									
									
									
										generated
									
									
									
								
							
							
						
						@@ -1,117 +0,0 @@
 | 
			
		||||
hash: 9683c87b3cf998e7fac1b12c4a94bf2bd18cb5422e9108539811546e703a439a
 | 
			
		||||
updated: 2016-07-12T16:20:45.462913061+09:00
 | 
			
		||||
imports:
 | 
			
		||||
- name: github.com/asaskevich/govalidator
 | 
			
		||||
  version: df81827fdd59d8b4fb93d8910b286ab7a3919520
 | 
			
		||||
- name: github.com/aws/aws-sdk-go
 | 
			
		||||
  version: 90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - aws
 | 
			
		||||
  - aws/credentials
 | 
			
		||||
  - aws/session
 | 
			
		||||
  - service/s3
 | 
			
		||||
  - aws/awserr
 | 
			
		||||
  - aws/client
 | 
			
		||||
  - aws/corehandlers
 | 
			
		||||
  - aws/defaults
 | 
			
		||||
  - aws/request
 | 
			
		||||
  - private/endpoints
 | 
			
		||||
  - aws/awsutil
 | 
			
		||||
  - aws/client/metadata
 | 
			
		||||
  - aws/signer/v4
 | 
			
		||||
  - private/protocol
 | 
			
		||||
  - private/protocol/restxml
 | 
			
		||||
  - private/waiter
 | 
			
		||||
  - aws/credentials/ec2rolecreds
 | 
			
		||||
  - aws/ec2metadata
 | 
			
		||||
  - private/protocol/rest
 | 
			
		||||
  - private/protocol/query
 | 
			
		||||
  - private/protocol/xml/xmlutil
 | 
			
		||||
  - private/protocol/query/queryutil
 | 
			
		||||
- name: github.com/Azure/azure-sdk-for-go
 | 
			
		||||
  version: 58a13e378daf3b06e65925397185684b16321111
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - storage
 | 
			
		||||
- name: github.com/BurntSushi/toml
 | 
			
		||||
  version: ffaa107fbd880f6d18cd6fec9b511668dcad8639
 | 
			
		||||
- name: github.com/cenkalti/backoff
 | 
			
		||||
  version: cdf48bbc1eb78d1349cbda326a4a037f7ba565c6
 | 
			
		||||
- name: github.com/cheggaaa/pb
 | 
			
		||||
  version: 04b234c80d661c663dbcebd52fc7218fdacc6d0c
 | 
			
		||||
- name: github.com/go-ini/ini
 | 
			
		||||
  version: cf53f9204df4fbdd7ec4164b57fa6184ba168292
 | 
			
		||||
- name: github.com/google/subcommands
 | 
			
		||||
  version: 1c7173745a6001f67d8d96ab4e178284c77f7759
 | 
			
		||||
- name: github.com/gosuri/uitable
 | 
			
		||||
  version: 36ee7e946282a3fb1cfecd476ddc9b35d8847e42
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - util/strutil
 | 
			
		||||
  - util/wordwrap
 | 
			
		||||
- name: github.com/howeyc/gopass
 | 
			
		||||
  version: 66487b23f2880ba32e185121d2cd51a338ea069a
 | 
			
		||||
- name: github.com/jinzhu/gorm
 | 
			
		||||
  version: 613c0655691abb7691b70c5fda80a716d9e20b1b
 | 
			
		||||
- name: github.com/jinzhu/inflection
 | 
			
		||||
  version: 8f4d3a0d04ce0b7c0cf3126fb98524246d00d102
 | 
			
		||||
- name: github.com/jmespath/go-jmespath
 | 
			
		||||
  version: 0b12d6b521d83fc7f755e7cfc1b1fbdd35a01a74
 | 
			
		||||
- name: github.com/jroimartin/gocui
 | 
			
		||||
  version: 2dcda558bf18ec07c7065bf1eaf071b5305f7c0c
 | 
			
		||||
- name: github.com/k0kubun/pp
 | 
			
		||||
  version: f5dce6ed0ccf6c350f1679964ff6b61f3d6d2033
 | 
			
		||||
- name: github.com/kotakanbe/go-cve-dictionary
 | 
			
		||||
  version: 1a336b8ac785badfe89a175ee926d39574901232
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - config
 | 
			
		||||
  - db
 | 
			
		||||
  - models
 | 
			
		||||
  - log
 | 
			
		||||
  - jvn
 | 
			
		||||
  - nvd
 | 
			
		||||
- name: github.com/kotakanbe/go-pingscanner
 | 
			
		||||
  version: 58e188a3e4f6ab1a6371e33421e4502e26fa1e80
 | 
			
		||||
- name: github.com/kotakanbe/logrus-prefixed-formatter
 | 
			
		||||
  version: f4f7d41649cf1e75e736884da8d05324aa76ea25
 | 
			
		||||
- name: github.com/mattn/go-colorable
 | 
			
		||||
  version: 9056b7a9f2d1f2d96498d6d146acd1f9d5ed3d59
 | 
			
		||||
- name: github.com/mattn/go-isatty
 | 
			
		||||
  version: 56b76bdf51f7708750eac80fa38b952bb9f32639
 | 
			
		||||
- name: github.com/mattn/go-runewidth
 | 
			
		||||
  version: d6bea18f789704b5f83375793155289da36a3c7f
 | 
			
		||||
- name: github.com/mattn/go-sqlite3
 | 
			
		||||
  version: 38ee283dabf11c9cbdb968eebd79b1fa7acbabe6
 | 
			
		||||
- name: github.com/mgutz/ansi
 | 
			
		||||
  version: c286dcecd19ff979eeb73ea444e479b903f2cfcb
 | 
			
		||||
- name: github.com/moul/http2curl
 | 
			
		||||
  version: b1479103caacaa39319f75e7f57fc545287fca0d
 | 
			
		||||
- name: github.com/nsf/termbox-go
 | 
			
		||||
  version: c45773466a30b680355d6494cc8826113c93cd0f
 | 
			
		||||
- name: github.com/parnurzeal/gorequest
 | 
			
		||||
  version: 6e8ad4ebdee4bec2934ed5afaaa1c7b877832a17
 | 
			
		||||
- name: github.com/rifflock/lfshook
 | 
			
		||||
  version: 05a24e24fa8d3a2eca8c2baf23aa2d5a2c51490c
 | 
			
		||||
- name: github.com/Sirupsen/logrus
 | 
			
		||||
  version: f3cfb454f4c209e6668c95216c4744b8fddb2356
 | 
			
		||||
- name: golang.org/x/crypto
 | 
			
		||||
  version: c2f4947f41766b144bb09066e919466da5eddeae
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - ssh
 | 
			
		||||
  - ssh/agent
 | 
			
		||||
  - ssh/terminal
 | 
			
		||||
  - curve25519
 | 
			
		||||
  - ed25519
 | 
			
		||||
  - ed25519/internal/edwards25519
 | 
			
		||||
- name: golang.org/x/net
 | 
			
		||||
  version: f841c39de738b1d0df95b5a7187744f0e03d8112
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - context
 | 
			
		||||
  - publicsuffix
 | 
			
		||||
- name: golang.org/x/sys
 | 
			
		||||
  version: a408501be4d17ee978c04a618e7a1b22af058c0e
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - unix
 | 
			
		||||
- name: gopkg.in/alexcesaro/quotedprintable.v3
 | 
			
		||||
  version: 2caba252f4dc53eaf6b553000885530023f54623
 | 
			
		||||
- name: gopkg.in/gomail.v2
 | 
			
		||||
  version: 81ebce5c23dfd25c6c67194b37d3dd3f338c98b1
 | 
			
		||||
devImports: []
 | 
			
		||||
							
								
								
									
										39
									
								
								glide.yaml
									
									
									
									
									
								
							
							
						
						@@ -1,39 +0,0 @@
 | 
			
		||||
package: github.com/future-architect/vuls
 | 
			
		||||
import:
 | 
			
		||||
- package: github.com/Azure/azure-sdk-for-go
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - storage
 | 
			
		||||
- package: github.com/BurntSushi/toml
 | 
			
		||||
- package: github.com/Sirupsen/logrus
 | 
			
		||||
- package: github.com/asaskevich/govalidator
 | 
			
		||||
- package: github.com/aws/aws-sdk-go
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - aws
 | 
			
		||||
  - aws/credentials
 | 
			
		||||
  - aws/session
 | 
			
		||||
  - service/s3
 | 
			
		||||
- package: github.com/cenkalti/backoff
 | 
			
		||||
- package: github.com/google/subcommands
 | 
			
		||||
- package: github.com/gosuri/uitable
 | 
			
		||||
- package: github.com/howeyc/gopass
 | 
			
		||||
- package: github.com/jinzhu/gorm
 | 
			
		||||
- package: github.com/jroimartin/gocui
 | 
			
		||||
- package: github.com/k0kubun/pp
 | 
			
		||||
- package: github.com/kotakanbe/go-cve-dictionary
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - config
 | 
			
		||||
  - db
 | 
			
		||||
  - models
 | 
			
		||||
- package: github.com/kotakanbe/go-pingscanner
 | 
			
		||||
- package: github.com/kotakanbe/logrus-prefixed-formatter
 | 
			
		||||
- package: github.com/mattn/go-sqlite3
 | 
			
		||||
- package: github.com/parnurzeal/gorequest
 | 
			
		||||
- package: github.com/rifflock/lfshook
 | 
			
		||||
- package: golang.org/x/crypto
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - ssh
 | 
			
		||||
  - ssh/agent
 | 
			
		||||
- package: golang.org/x/net
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - context
 | 
			
		||||
- package: gopkg.in/gomail.v2
 | 
			
		||||
							
								
								
									
										110
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,110 @@
 | 
			
		||||
module github.com/future-architect/vuls
 | 
			
		||||
 | 
			
		||||
go 1.12
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	cloud.google.com/go v0.40.0 // indirect
 | 
			
		||||
	contrib.go.opencensus.io/exporter/ocagent v0.4.12 // indirect
 | 
			
		||||
	github.com/Azure/azure-sdk-for-go v28.1.0+incompatible
 | 
			
		||||
	github.com/Azure/go-autorest v12.0.0+incompatible // indirect
 | 
			
		||||
	github.com/BurntSushi/toml v0.3.1
 | 
			
		||||
	github.com/Microsoft/go-winio v0.4.12 // indirect
 | 
			
		||||
	github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91
 | 
			
		||||
	github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a
 | 
			
		||||
	github.com/aws/aws-sdk-go v1.19.24
 | 
			
		||||
	github.com/beorn7/perks v1.0.0 // indirect
 | 
			
		||||
	github.com/boltdb/bolt v1.3.1
 | 
			
		||||
	github.com/cenkalti/backoff v2.1.1+incompatible
 | 
			
		||||
	github.com/cheggaaa/pb v2.0.6+incompatible // indirect
 | 
			
		||||
	github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc // indirect
 | 
			
		||||
	github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 // indirect
 | 
			
		||||
	github.com/dnaeon/go-vcr v1.0.1 // indirect
 | 
			
		||||
	github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f // indirect
 | 
			
		||||
	github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f // indirect
 | 
			
		||||
	github.com/genuinetools/reg v0.16.1 // indirect
 | 
			
		||||
	github.com/go-redis/redis v6.15.2+incompatible // indirect
 | 
			
		||||
	github.com/gogo/protobuf v1.2.1 // indirect
 | 
			
		||||
	github.com/golang/mock v1.3.1 // indirect
 | 
			
		||||
	github.com/google/btree v1.0.0 // indirect
 | 
			
		||||
	github.com/google/pprof v0.0.0-20190515194954-54271f7e092f // indirect
 | 
			
		||||
	github.com/google/subcommands v1.0.1
 | 
			
		||||
	github.com/gorilla/mux v1.7.1 // indirect
 | 
			
		||||
	github.com/gorilla/websocket v1.4.0 // indirect
 | 
			
		||||
	github.com/gosuri/uitable v0.0.1
 | 
			
		||||
	github.com/grokify/html-strip-tags-go v0.0.0-20190424092004-025bd760b278 // indirect
 | 
			
		||||
	github.com/hashicorp/go-version v1.2.0
 | 
			
		||||
	github.com/hashicorp/uuid v0.0.0-20160311170451-ebb0a03e909c
 | 
			
		||||
	github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c
 | 
			
		||||
	github.com/jinzhu/gorm v1.9.8 // indirect
 | 
			
		||||
	github.com/jinzhu/inflection v0.0.0-20190603042836-f5c5f50e6090 // indirect
 | 
			
		||||
	github.com/jroimartin/gocui v0.4.0
 | 
			
		||||
	github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect
 | 
			
		||||
	github.com/k0kubun/pp v3.0.1+incompatible
 | 
			
		||||
	github.com/knqyf263/fanal v0.0.0-20190528042547-07e27879b658
 | 
			
		||||
	github.com/knqyf263/go-cpe v0.0.0-20180327054844-659663f6eca2
 | 
			
		||||
	github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d
 | 
			
		||||
	github.com/knqyf263/go-dep-parser v0.0.0-20190521150559-1ef8521d17a0
 | 
			
		||||
	github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936
 | 
			
		||||
	github.com/knqyf263/go-version v1.1.1
 | 
			
		||||
	github.com/knqyf263/gost v0.0.0-20190326022014-39175c0da9e3
 | 
			
		||||
	github.com/knqyf263/trivy v0.1.1
 | 
			
		||||
	github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
 | 
			
		||||
	github.com/kotakanbe/go-cve-dictionary v0.0.0-20190327053454-5fe52611f0b8
 | 
			
		||||
	github.com/kotakanbe/go-pingscanner v0.1.0
 | 
			
		||||
	github.com/kotakanbe/goval-dictionary v0.1.3-0.20190613053258-078b163b76ec
 | 
			
		||||
	github.com/kotakanbe/logrus-prefixed-formatter v0.0.0-20180123152602-928f7356cb96
 | 
			
		||||
	github.com/kr/pty v1.1.5 // indirect
 | 
			
		||||
	github.com/labstack/echo v3.3.10+incompatible // indirect
 | 
			
		||||
	github.com/labstack/gommon v0.2.9 // indirect
 | 
			
		||||
	github.com/lib/pq v1.1.1 // indirect
 | 
			
		||||
	github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5 // indirect
 | 
			
		||||
	github.com/lusis/slack-test v0.0.0-20190426140909-c40012f20018 // indirect
 | 
			
		||||
	github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
 | 
			
		||||
	github.com/mitchellh/go-homedir v1.1.0
 | 
			
		||||
	github.com/moul/http2curl v1.0.0 // indirect
 | 
			
		||||
	github.com/mozqnet/go-exploitdb v0.0.0-20190426034301-a055cc2c195d
 | 
			
		||||
	github.com/nlopes/slack v0.4.0
 | 
			
		||||
	github.com/nsf/termbox-go v0.0.0-20190325093121-288510b9734e // indirect
 | 
			
		||||
	github.com/olekukonko/tablewriter v0.0.1
 | 
			
		||||
	github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
 | 
			
		||||
	github.com/parnurzeal/gorequest v0.2.15
 | 
			
		||||
	github.com/pelletier/go-toml v1.4.0 // indirect
 | 
			
		||||
	github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 // indirect
 | 
			
		||||
	github.com/prometheus/common v0.3.0 // indirect
 | 
			
		||||
	github.com/prometheus/procfs v0.0.0-20190503130316-740c07785007 // indirect
 | 
			
		||||
	github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
 | 
			
		||||
	github.com/satori/go.uuid v1.2.0 // indirect
 | 
			
		||||
	github.com/sirupsen/logrus v1.2.0
 | 
			
		||||
	github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect
 | 
			
		||||
	github.com/spf13/afero v1.2.2 // indirect
 | 
			
		||||
	github.com/spf13/jwalterweatherman v1.1.0 // indirect
 | 
			
		||||
	github.com/spf13/viper v1.3.2 // indirect
 | 
			
		||||
	go.opencensus.io v0.22.0 // indirect
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8
 | 
			
		||||
	golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522 // indirect
 | 
			
		||||
	golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff // indirect
 | 
			
		||||
	golang.org/x/mobile v0.0.0-20190607214518-6fa95d984e88 // indirect
 | 
			
		||||
	golang.org/x/mod v0.1.0 // indirect
 | 
			
		||||
	golang.org/x/net v0.0.0-20190611141213-3f473d35a33a // indirect
 | 
			
		||||
	golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
 | 
			
		||||
	golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae // indirect
 | 
			
		||||
	golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
 | 
			
		||||
	golang.org/x/tools v0.0.0-20190612232758-d4e310b4a8a5 // indirect
 | 
			
		||||
	golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373
 | 
			
		||||
	google.golang.org/appengine v1.6.1 // indirect
 | 
			
		||||
	google.golang.org/genproto v0.0.0-20190611190212-a7e196e89fd3 // indirect
 | 
			
		||||
	google.golang.org/grpc v1.21.1 // indirect
 | 
			
		||||
	gopkg.in/VividCortex/ewma.v1 v1.1.1 // indirect
 | 
			
		||||
	gopkg.in/cheggaaa/pb.v2 v2.0.6 // indirect
 | 
			
		||||
	gopkg.in/fatih/color.v1 v1.7.0 // indirect
 | 
			
		||||
	gopkg.in/mattn/go-colorable.v0 v0.0.0-00010101000000-000000000000 // indirect
 | 
			
		||||
	gopkg.in/mattn/go-isatty.v0 v0.0.0-00010101000000-000000000000 // indirect
 | 
			
		||||
	gopkg.in/mattn/go-runewidth.v0 v0.0.4 // indirect
 | 
			
		||||
	honnef.co/go/tools v0.0.0-20190607181801-497c8f037f5a // indirect
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
replace github.com/genuinetools/reg => github.com/tomoyamachi/reg v0.16.2-0.20190418055600-c6010b917a55
 | 
			
		||||
 | 
			
		||||
replace gopkg.in/mattn/go-colorable.v0 => github.com/mattn/go-colorable v0.1.0
 | 
			
		||||
 | 
			
		||||
replace gopkg.in/mattn/go-isatty.v0 => github.com/mattn/go-isatty v0.0.6
 | 
			
		||||
							
								
								
									
										648
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,648 @@
 | 
			
		||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 | 
			
		||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 | 
			
		||||
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
 | 
			
		||||
cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
 | 
			
		||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
 | 
			
		||||
cloud.google.com/go v0.40.0 h1:FjSY7bOj+WzJe6TZRVtXI2b9kAYvtNg4lMbcH2+MUkk=
 | 
			
		||||
cloud.google.com/go v0.40.0/go.mod h1:Tk58MuI9rbLMKlAjeO/bDnteAx7tX2gJIXw4T5Jwlro=
 | 
			
		||||
contrib.go.opencensus.io/exporter/ocagent v0.4.12 h1:jGFvw3l57ViIVEPKKEUXPcLYIXJmQxLUh6ey1eJhwyc=
 | 
			
		||||
contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA=
 | 
			
		||||
github.com/Azure/azure-sdk-for-go v28.1.0+incompatible h1:uApF+FNMxRibKyoWxLatbrBJse505r7UVdrOm3dEtfk=
 | 
			
		||||
github.com/Azure/azure-sdk-for-go v28.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
 | 
			
		||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
 | 
			
		||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
 | 
			
		||||
github.com/Azure/go-autorest v12.0.0+incompatible h1:N+VqClcomLGD/sHb3smbSYYtNMgKpVV3Cd5r5i8z6bQ=
 | 
			
		||||
github.com/Azure/go-autorest v12.0.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
 | 
			
		||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 | 
			
		||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 | 
			
		||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 | 
			
		||||
github.com/DataDog/zstd v1.3.6-0.20190409195224-796139022798/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
 | 
			
		||||
github.com/DataDog/zstd v1.4.0/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
 | 
			
		||||
github.com/GoogleCloudPlatform/docker-credential-gcr v1.5.0 h1:wykTgKwhVr2t2qs+xI020s6W5dt614QqCHV+7W9dg64=
 | 
			
		||||
github.com/GoogleCloudPlatform/docker-credential-gcr v1.5.0/go.mod h1:BB1eHdMLYEFuFdBlRMb0N7YGVdM5s6Pt0njxgvfbGGs=
 | 
			
		||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
 | 
			
		||||
github.com/Microsoft/go-winio v0.4.12 h1:xAfWHN1IrQ0NJ9TBC0KBZoqLjzDTr1ML+4MywiUOryc=
 | 
			
		||||
github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
 | 
			
		||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
 | 
			
		||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
 | 
			
		||||
github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91 h1:vX+gnvBc56EbWYrmlhYbFYRaeikAke1GL84N4BEYOFE=
 | 
			
		||||
github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91/go.mod h1:cDLGBht23g0XQdLjzn6xOGXDkLK182YfINAaZEQLCHQ=
 | 
			
		||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
 | 
			
		||||
github.com/Shopify/sarama v1.22.1/go.mod h1:FRzlvRpMFO/639zY1SDxUxkqH97Y0ndM5CbGj6oG3As=
 | 
			
		||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
 | 
			
		||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
 | 
			
		||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
 | 
			
		||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 | 
			
		||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 | 
			
		||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
 | 
			
		||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
 | 
			
		||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
 | 
			
		||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 | 
			
		||||
github.com/asaskevich/govalidator v0.0.0-20180315120708-ccb8e960c48f h1:y2hSFdXeA1y5z5f0vfNO0Dg5qVY036qzlz3Pds0B92o=
 | 
			
		||||
github.com/asaskevich/govalidator v0.0.0-20180315120708-ccb8e960c48f/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 | 
			
		||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
 | 
			
		||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 | 
			
		||||
github.com/aws/aws-sdk-go v1.19.11/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
 | 
			
		||||
github.com/aws/aws-sdk-go v1.19.24 h1:qOIYaFxcFg07Vdn799ERpGiuUUIEi5MQ2vYib3CNMp4=
 | 
			
		||||
github.com/aws/aws-sdk-go v1.19.24/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
 | 
			
		||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 | 
			
		||||
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
 | 
			
		||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 | 
			
		||||
github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
 | 
			
		||||
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
 | 
			
		||||
github.com/briandowns/spinner v0.0.0-20190319032542-ac46072a5a91 h1:GMmnK0dvr0Sf0gx3DvTbln0c8DE07B7sPVD9dgHOqo4=
 | 
			
		||||
github.com/briandowns/spinner v0.0.0-20190319032542-ac46072a5a91/go.mod h1:hw/JEQBIE+c/BLI4aKM8UU8v+ZqrD3h7HC27kKt8JQU=
 | 
			
		||||
github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY=
 | 
			
		||||
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
 | 
			
		||||
github.com/census-instrumentation/opencensus-proto v0.2.0 h1:LzQXZOgg4CQfE6bFvXGM30YZL1WW/M337pXml+GrcZ4=
 | 
			
		||||
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 | 
			
		||||
github.com/cheggaaa/pb v2.0.6+incompatible h1:sutSx+mRaNbeJUMCAtyqNWU/tQ0B/xBm+hyb1JQmQYs=
 | 
			
		||||
github.com/cheggaaa/pb v2.0.6+incompatible/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
 | 
			
		||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 | 
			
		||||
github.com/containerd/continuity v0.0.0-20180921161001-7f53d412b9eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
 | 
			
		||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc h1:TP+534wVlf61smEIq1nwLLAjQVEK2EADoW3CX9AuT+8=
 | 
			
		||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
 | 
			
		||||
github.com/coreos/clair v0.0.0-20180919182544-44ae4bc9590a/go.mod h1:uXhHPWAoRqw0jJc2f8RrPCwRhIo9otQ8OEWUFtpCiwA=
 | 
			
		||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 | 
			
		||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
 | 
			
		||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 | 
			
		||||
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/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ=
 | 
			
		||||
github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
 | 
			
		||||
github.com/denisenkom/go-mssqldb v0.0.0-20190423183735-731ef375ac02 h1:PS3xfVPa8N84AzoWZHFCbA0+ikz4f4skktfjQoNMsgk=
 | 
			
		||||
github.com/denisenkom/go-mssqldb v0.0.0-20190423183735-731ef375ac02/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
 | 
			
		||||
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
 | 
			
		||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
 | 
			
		||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 | 
			
		||||
github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY=
 | 
			
		||||
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
 | 
			
		||||
github.com/docker/cli v0.0.0-20180920165730-54c19e67f69c h1:QlAVcyoF7QQVN7zV+xYBjgwtRVlRU3WCTCpb2mcqQrM=
 | 
			
		||||
github.com/docker/cli v0.0.0-20180920165730-54c19e67f69c/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 | 
			
		||||
github.com/docker/distribution v0.0.0-20180920194744-16128bbac47f h1:hYf+mPizfvpH6VgIxdntnOmQHd1F1mQUc1oG+j3Ol2g=
 | 
			
		||||
github.com/docker/distribution v0.0.0-20180920194744-16128bbac47f/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
 | 
			
		||||
github.com/docker/docker v0.0.0-20180924202107-a9c061deec0f h1:W4fbqg0JUwy6lLesoJaV/rE0fwAmtdtinMa64X1CEh0=
 | 
			
		||||
github.com/docker/docker v0.0.0-20180924202107-a9c061deec0f/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 | 
			
		||||
github.com/docker/docker-ce v0.0.0-20180924210327-f53bd8bb8e43 h1:gZ4lWixV821UVbYtr+oz1ZPCHkbtE+ivfmHyZRgyl2Y=
 | 
			
		||||
github.com/docker/docker-ce v0.0.0-20180924210327-f53bd8bb8e43/go.mod h1:l1FUGRYBvbjnZ8MS6A2xOji4aZFlY/Qmgz7p4oXH7ac=
 | 
			
		||||
github.com/docker/docker-credential-helpers v0.6.1 h1:Dq4iIfcM7cNtddhLVWe9h4QDjsi4OER3Z8voPu/I52g=
 | 
			
		||||
github.com/docker/docker-credential-helpers v0.6.1/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
 | 
			
		||||
github.com/docker/go-connections v0.0.0-20180821093606-97c2040d34df/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
 | 
			
		||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
 | 
			
		||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
 | 
			
		||||
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916 h1:yWHOI+vFjEsAakUTSrtqc/SAHrhSkmn48pqjidZX3QA=
 | 
			
		||||
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
 | 
			
		||||
github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk=
 | 
			
		||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 | 
			
		||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4=
 | 
			
		||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
 | 
			
		||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
 | 
			
		||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
 | 
			
		||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
 | 
			
		||||
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f h1:8GDPb0tCY8LQ+OJ3dbHb5sA6YZWXFORQYZx5sdsTlMs=
 | 
			
		||||
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
 | 
			
		||||
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f h1:AUj1VoZUfhPhOPHULCQQDnGhRelpFWHMLhQVWDsS0v4=
 | 
			
		||||
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
 | 
			
		||||
github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
 | 
			
		||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
 | 
			
		||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
 | 
			
		||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
 | 
			
		||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
 | 
			
		||||
github.com/etcd-io/bbolt v1.3.2 h1:RLRQ0TKLX7DlBRXAJHvbmXL17Q3KNnTBtZ9B6Qo+/Y0=
 | 
			
		||||
github.com/etcd-io/bbolt v1.3.2/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
 | 
			
		||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
 | 
			
		||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 | 
			
		||||
github.com/fernet/fernet-go v0.0.0-20180830025343-9eac43b88a5e/go.mod h1:2H9hjfbpSMHwY503FclkV/lZTBh2YlOmLLSda12uL8c=
 | 
			
		||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
 | 
			
		||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 | 
			
		||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 | 
			
		||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 | 
			
		||||
github.com/genuinetools/pkg v0.0.0-20180910213200-1c141f661797/go.mod h1:XTcrCYlXPxnxL2UpnwuRn7tcaTn9HAhxFoFJucootk8=
 | 
			
		||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 | 
			
		||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
 | 
			
		||||
github.com/gliderlabs/ssh v0.1.3 h1:cBU46h1lYQk5f2Z+jZbewFKy+1zzE2aUX/ilcPDAm9M=
 | 
			
		||||
github.com/gliderlabs/ssh v0.1.3/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
 | 
			
		||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 | 
			
		||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 | 
			
		||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 | 
			
		||||
github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
 | 
			
		||||
github.com/go-redis/redis v6.15.2+incompatible h1:9SpNVG76gr6InJGxoZ6IuuxaCOQwDAhzyXg+Bs+0Sb4=
 | 
			
		||||
github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
 | 
			
		||||
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
 | 
			
		||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
 | 
			
		||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
 | 
			
		||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 | 
			
		||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 | 
			
		||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 | 
			
		||||
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
 | 
			
		||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
 | 
			
		||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
 | 
			
		||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 | 
			
		||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
 | 
			
		||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 | 
			
		||||
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
 | 
			
		||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 | 
			
		||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
 | 
			
		||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
 | 
			
		||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 | 
			
		||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 | 
			
		||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 | 
			
		||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 | 
			
		||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
 | 
			
		||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 | 
			
		||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 | 
			
		||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 | 
			
		||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 | 
			
		||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 | 
			
		||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 | 
			
		||||
github.com/google/subcommands v0.0.0-20181012225330-46f0354f6315/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
 | 
			
		||||
github.com/google/subcommands v1.0.1 h1:/eqq+otEXm5vhfBrbREPCSVQbvofip6kIz+mX5TUH7k=
 | 
			
		||||
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
 | 
			
		||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 | 
			
		||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
 | 
			
		||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 | 
			
		||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
 | 
			
		||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 | 
			
		||||
github.com/gorilla/mux v1.7.1 h1:Dw4jY2nghMMRsh1ol8dv1axHkDwMQK2DHerMNJsIpJU=
 | 
			
		||||
github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 | 
			
		||||
github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 | 
			
		||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
 | 
			
		||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 | 
			
		||||
github.com/gosuri/uitable v0.0.1 h1:M9sMNgSZPyAu1FJZJLpJ16ofL8q5ko2EDUkICsynvlY=
 | 
			
		||||
github.com/gosuri/uitable v0.0.1/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo=
 | 
			
		||||
github.com/grokify/html-strip-tags-go v0.0.0-20190424092004-025bd760b278 h1:DZo48DQFIDo/YWjUeFip1dfJztBhRuaxfUnPd+gAfcs=
 | 
			
		||||
github.com/grokify/html-strip-tags-go v0.0.0-20190424092004-025bd760b278/go.mod h1:Xk7G0nwBiIloTMbLddk4WWJOqi4i/JLhadLd0HUXO30=
 | 
			
		||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
 | 
			
		||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJlb8Kqsd41CTE=
 | 
			
		||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 | 
			
		||||
github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
 | 
			
		||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 | 
			
		||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
			
		||||
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
 | 
			
		||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
			
		||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
 | 
			
		||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 | 
			
		||||
github.com/hashicorp/uuid v0.0.0-20160311170451-ebb0a03e909c h1:nQcv325vxv2fFHJsOt53eSRf1eINt6vOdYUFfXs4rgk=
 | 
			
		||||
github.com/hashicorp/uuid v0.0.0-20160311170451-ebb0a03e909c/go.mod h1:fHzc09UnyJyqyW+bFuq864eh+wC7dj65aXmXLRe5to0=
 | 
			
		||||
github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c h1:kQWxfPIHVLbgLzphqk3QUflDy9QdksZR4ygR807bpy0=
 | 
			
		||||
github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs=
 | 
			
		||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 | 
			
		||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 | 
			
		||||
github.com/htcat/htcat v1.0.2 h1:zro95dGwkKDeZOgq9ei+9szd5qurGxBGfHY8hRehA7k=
 | 
			
		||||
github.com/htcat/htcat v1.0.2/go.mod h1:i8ViQbjSi2+lJzM6Lx20FIxHENCz6mzJglK3HH06W3s=
 | 
			
		||||
github.com/inconshreveable/log15 v0.0.0-20180818164646-67afb5ed74ec h1:CGkYB1Q7DSsH/ku+to+foV4agt2F2miquaLUgF6L178=
 | 
			
		||||
github.com/inconshreveable/log15 v0.0.0-20180818164646-67afb5ed74ec/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
 | 
			
		||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
 | 
			
		||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
 | 
			
		||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
 | 
			
		||||
github.com/jinzhu/gorm v1.9.1/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
 | 
			
		||||
github.com/jinzhu/gorm v1.9.5 h1:sc+tBaUPibSnfkb6xezGWjUp45CtSwt4wsYt+LJan6w=
 | 
			
		||||
github.com/jinzhu/gorm v1.9.5/go.mod h1:bdqTT3q6dhSph2K3pWxrHP6nqxuAp2yQ3KFtc3U3F84=
 | 
			
		||||
github.com/jinzhu/gorm v1.9.8 h1:n5uvxqLepIP2R1XF7pudpt9Rv8I3m7G9trGxJVjLZ5k=
 | 
			
		||||
github.com/jinzhu/gorm v1.9.8/go.mod h1:bdqTT3q6dhSph2K3pWxrHP6nqxuAp2yQ3KFtc3U3F84=
 | 
			
		||||
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a h1:eeaG9XMUvRBYXJi4pg1ZKM7nxc5AfXfojeLLW7O5J3k=
 | 
			
		||||
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
 | 
			
		||||
github.com/jinzhu/inflection v0.0.0-20190603042836-f5c5f50e6090 h1:LIwA5USOJ9W/0hwiRH1MugeThGBHGqv+USXcDKWHIVY=
 | 
			
		||||
github.com/jinzhu/inflection v0.0.0-20190603042836-f5c5f50e6090/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
 | 
			
		||||
github.com/jinzhu/now v1.0.0 h1:6WV8LvwPpDhKjo5U9O6b4+xdG/jTXNPwlDme/MTo8Ns=
 | 
			
		||||
github.com/jinzhu/now v1.0.0/go.mod h1:oHTiXerJ20+SfYcrdlBO7rzZRJWGwSTQ0iUY2jI6Gfc=
 | 
			
		||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
 | 
			
		||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 | 
			
		||||
github.com/jroimartin/gocui v0.4.0 h1:52jnalstgmc25FmtGcWqa0tcbMEWS6RpFLsOIO+I+E8=
 | 
			
		||||
github.com/jroimartin/gocui v0.4.0/go.mod h1:7i7bbj99OgFHzo7kB2zPb8pXLqMBSQegY7azfqXMkyY=
 | 
			
		||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 | 
			
		||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 | 
			
		||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
 | 
			
		||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 | 
			
		||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 | 
			
		||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM=
 | 
			
		||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
 | 
			
		||||
github.com/k0kubun/pp v2.3.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
 | 
			
		||||
github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40=
 | 
			
		||||
github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
 | 
			
		||||
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e h1:RgQk53JHp/Cjunrr1WlsXSZpqXn+uREuHvUVcK82CV8=
 | 
			
		||||
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
 | 
			
		||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
 | 
			
		||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
 | 
			
		||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 | 
			
		||||
github.com/knqyf263/berkeleydb v0.0.0-20190501065933-fafe01fb9662 h1:UGS0RbPHwXJkq8tcba8OD0nvVUWLf2h7uUJznuHPPB0=
 | 
			
		||||
github.com/knqyf263/berkeleydb v0.0.0-20190501065933-fafe01fb9662/go.mod h1:bu1CcN4tUtoRcI/B/RFHhxMNKFHVq/c3SV+UTyduoXg=
 | 
			
		||||
github.com/knqyf263/fanal v0.0.0-20190521154631-a2dde7e171c6/go.mod h1:guPOH3Sfj5M4j/LvCOoWmuYCXnjReDIwJO+S89Fje1E=
 | 
			
		||||
github.com/knqyf263/fanal v0.0.0-20190528042547-07e27879b658 h1:m0FCzKmngHBMqrbBpxadWR8Py3/jWYnWiWxmOW7ovVU=
 | 
			
		||||
github.com/knqyf263/fanal v0.0.0-20190528042547-07e27879b658/go.mod h1:guPOH3Sfj5M4j/LvCOoWmuYCXnjReDIwJO+S89Fje1E=
 | 
			
		||||
github.com/knqyf263/go-cpe v0.0.0-20180327054844-659663f6eca2 h1:9CYbtr3i56D/rD6u6jJ/Aocsic9G+MupyVu7gb+QHF4=
 | 
			
		||||
github.com/knqyf263/go-cpe v0.0.0-20180327054844-659663f6eca2/go.mod h1:XM58Cg7dN+g0J9UPVmKjiXWlGi55lx+9IMs0IMoFWQo=
 | 
			
		||||
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/knqyf263/go-dep-parser v0.0.0-20190521150559-1ef8521d17a0 h1:DOQ2UbTciy48dV9vpZ25BOiShrWIWZwBdMOy7SD1Wow=
 | 
			
		||||
github.com/knqyf263/go-dep-parser v0.0.0-20190521150559-1ef8521d17a0/go.mod h1:gSiqSkOFPstUZu/qZ4wnNJS69PtQQnPl397vxKHJ5mQ=
 | 
			
		||||
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936 h1:HDjRqotkViMNcGMGicb7cgxklx8OwnjtCBmyWEqrRvM=
 | 
			
		||||
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0=
 | 
			
		||||
github.com/knqyf263/go-rpmdb v0.0.0-20190501070121-10a1c42a10dc h1:pumO9pqmRAjvic6oove22RGh9wDZQnj96XQjJSbSEPs=
 | 
			
		||||
github.com/knqyf263/go-rpmdb v0.0.0-20190501070121-10a1c42a10dc/go.mod h1:MrSSvdMpTSymaQWk1yFr9sxFSyQmKMj6jkbvGrchBV8=
 | 
			
		||||
github.com/knqyf263/go-version v1.1.1 h1:+MpcBC9b7rk5ihag8Y/FLG8get1H2GjniwKQ+9DxI2o=
 | 
			
		||||
github.com/knqyf263/go-version v1.1.1/go.mod h1:0tBvHvOBSf5TqGNcY+/ih9o8qo3R16iZCpB9rP0D3VM=
 | 
			
		||||
github.com/knqyf263/gost v0.0.0-20190326022014-39175c0da9e3 h1:TPlz2V0Hpgg3Ecw5hozTSBUDZF286CQ21P4QFSYPJvo=
 | 
			
		||||
github.com/knqyf263/gost v0.0.0-20190326022014-39175c0da9e3/go.mod h1:tktdrQ3uwKVTxlSF9kAgxjW2xkuaY8IrIHmjVQoslOc=
 | 
			
		||||
github.com/knqyf263/nested v0.0.1 h1:Sv26CegUMhjt19zqbBKntjwESdxe5hxVPSk0+AKjdUc=
 | 
			
		||||
github.com/knqyf263/nested v0.0.1/go.mod h1:zwhsIhMkBg90DTOJQvxPkKIypEHPYkgWHs4gybdlUmk=
 | 
			
		||||
github.com/knqyf263/trivy v0.1.1 h1:K7k9TsKTdN0rdSsL9103G71WC9NWCZQF6QhfsByOnHg=
 | 
			
		||||
github.com/knqyf263/trivy v0.1.1/go.mod h1:AjYD8rbiW7vw2KOv9urd0BqCVfyNNjRNBfVfevikqj8=
 | 
			
		||||
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 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
 | 
			
		||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 | 
			
		||||
github.com/kotakanbe/go-cve-dictionary v0.0.0-20190327053454-5fe52611f0b8 h1:0zo7jVQn8KjV0xT5AOHHNIzABmYBDJ2WWKVeqyOdTKc=
 | 
			
		||||
github.com/kotakanbe/go-cve-dictionary v0.0.0-20190327053454-5fe52611f0b8/go.mod h1:CNVaCVSeqjxCFQm93uCWPT8mR+a0514XHiiBJx9yrkQ=
 | 
			
		||||
github.com/kotakanbe/go-pingscanner v0.1.0 h1:VG4/9l0i8WeToXclj7bIGoAZAu7a07Z3qmQiIfU0gT0=
 | 
			
		||||
github.com/kotakanbe/go-pingscanner v0.1.0/go.mod h1:/761QZzuZFcfN8h/1QuawUA+pKukp3qcNj5mxJCOiAk=
 | 
			
		||||
github.com/kotakanbe/goval-dictionary v0.1.2 h1:XnninBr9KJcP3557PcR8qkUq9zlQbCukVIkU7AHWxd4=
 | 
			
		||||
github.com/kotakanbe/goval-dictionary v0.1.2/go.mod h1:lzsw634rJIxLteds6RAACIKZCoXKT06o/xHsTWf6v5o=
 | 
			
		||||
github.com/kotakanbe/goval-dictionary v0.1.3-0.20190612145907-3fbb67115698 h1:5/4vBHQiXPIejJSZEqRRUpd0HVqFMQrFZUZtSDMvMzc=
 | 
			
		||||
github.com/kotakanbe/goval-dictionary v0.1.3-0.20190612145907-3fbb67115698/go.mod h1:D0FzzGCYCJCgPy5+wGgEOvWTb8fxUxqdxkWM2JDwguA=
 | 
			
		||||
github.com/kotakanbe/goval-dictionary v0.1.3-0.20190613041505-2362c088a437 h1:gnwqfC+G78bmvVHETLvZOUKopUD/ljQAdwcvHiLKMKA=
 | 
			
		||||
github.com/kotakanbe/goval-dictionary v0.1.3-0.20190613041505-2362c088a437/go.mod h1:VupP39J8370MdBkmvQQVmuYf98VrcQzhiGo+UiNW4rs=
 | 
			
		||||
github.com/kotakanbe/goval-dictionary v0.1.3-0.20190613053258-078b163b76ec h1:gMji7JMOrnUYUorYUTM7TRlvy8D613WkQhayEQhBsFI=
 | 
			
		||||
github.com/kotakanbe/goval-dictionary v0.1.3-0.20190613053258-078b163b76ec/go.mod h1:VupP39J8370MdBkmvQQVmuYf98VrcQzhiGo+UiNW4rs=
 | 
			
		||||
github.com/kotakanbe/goval-dictionary v0.1.3-0.20190613053258-8b98657de17d h1:S2hGRg/3mxi8eR7DROKT9kqTEjGLgm4dDHm72/DIJrQ=
 | 
			
		||||
github.com/kotakanbe/goval-dictionary v0.1.3-0.20190613053258-8b98657de17d/go.mod h1:VupP39J8370MdBkmvQQVmuYf98VrcQzhiGo+UiNW4rs=
 | 
			
		||||
github.com/kotakanbe/logrus-prefixed-formatter v0.0.0-20180123152602-928f7356cb96 h1:xNVK0mQJdQjw+QYeaMM4G6fvucWr8rTGGIhlPakx1wU=
 | 
			
		||||
github.com/kotakanbe/logrus-prefixed-formatter v0.0.0-20180123152602-928f7356cb96/go.mod h1:ljq48H1V+0Vh0u7ucA3LjR4AfkAeCpxrf7LaaCk8Vmo=
 | 
			
		||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 | 
			
		||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 | 
			
		||||
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.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
 | 
			
		||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 | 
			
		||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 | 
			
		||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=
 | 
			
		||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
 | 
			
		||||
github.com/labstack/echo v2.2.0+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
 | 
			
		||||
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
 | 
			
		||||
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
 | 
			
		||||
github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0=
 | 
			
		||||
github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
 | 
			
		||||
github.com/labstack/gommon v0.2.9 h1:heVeuAYtevIQVYkGj6A41dtfT91LrvFG220lavpWhrU=
 | 
			
		||||
github.com/labstack/gommon v0.2.9/go.mod h1:E8ZTmW9vw5az5/ZyHWCp0Lw4OH2ecsaBP1C/NKavGG4=
 | 
			
		||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 | 
			
		||||
github.com/lib/pq v1.1.0 h1:/5u4a+KGJptBRqGzPvYQL9p0d/tPR4S31+Tnzj9lEO4=
 | 
			
		||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 | 
			
		||||
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
 | 
			
		||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 | 
			
		||||
github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5 h1:AsEBgzv3DhuYHI/GiQh2HxvTP71HCCE9E/tzGUzGdtU=
 | 
			
		||||
github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5/go.mod h1:c2mYKRyMb1BPkO5St0c/ps62L4S0W2NAkaTXj9qEI+0=
 | 
			
		||||
github.com/lusis/slack-test v0.0.0-20190426140909-c40012f20018 h1:MNApn+Z+fIT4NPZopPfCc1obT6aY3SVM6DOctz1A9ZU=
 | 
			
		||||
github.com/lusis/slack-test v0.0.0-20190426140909-c40012f20018/go.mod h1:sFlOUpQL1YcjhFVXhg1CG8ZASEs/Mf1oVb6H75JL/zg=
 | 
			
		||||
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
 | 
			
		||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 | 
			
		||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.0 h1:v2XXALHHh6zHfYTJ+cSkwtyffnaOyR1MXaA91mTrb8o=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.6 h1:SrwhHcpV4nWrMGdNcC2kXpMfcBVYGDuTArqyhocJgvA=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 | 
			
		||||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
 | 
			
		||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 | 
			
		||||
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
 | 
			
		||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 | 
			
		||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
 | 
			
		||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 | 
			
		||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
 | 
			
		||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
 | 
			
		||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 | 
			
		||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
 | 
			
		||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 | 
			
		||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
 | 
			
		||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
 | 
			
		||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 | 
			
		||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
			
		||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
			
		||||
github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
 | 
			
		||||
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
 | 
			
		||||
github.com/mozqnet/go-exploitdb v0.0.0-20190426034301-a055cc2c195d h1:ujS/a5AnCh6CNKIBfvrisDw2edwFa0TmHQHEQ6g5COg=
 | 
			
		||||
github.com/mozqnet/go-exploitdb v0.0.0-20190426034301-a055cc2c195d/go.mod h1:tqVnRPFR/8bkvCzGsGjwq+vb5dS6jwFFa+sEAbWPbDI=
 | 
			
		||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 | 
			
		||||
github.com/nlopes/slack v0.4.0 h1:OVnHm7lv5gGT5gkcHsZAyw++oHVFihbjWbL3UceUpiA=
 | 
			
		||||
github.com/nlopes/slack v0.4.0/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM=
 | 
			
		||||
github.com/nsf/termbox-go v0.0.0-20190325093121-288510b9734e h1:Vbib8wJAaMEF9jusI/kMSYMr/LtRzM7+F9MJgt/nH8k=
 | 
			
		||||
github.com/nsf/termbox-go v0.0.0-20190325093121-288510b9734e/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
 | 
			
		||||
github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88=
 | 
			
		||||
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
 | 
			
		||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
			
		||||
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
 | 
			
		||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
			
		||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
			
		||||
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 | 
			
		||||
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
 | 
			
		||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 | 
			
		||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 | 
			
		||||
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
 | 
			
		||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
 | 
			
		||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
 | 
			
		||||
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
 | 
			
		||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
 | 
			
		||||
github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
 | 
			
		||||
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
 | 
			
		||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
 | 
			
		||||
github.com/parnurzeal/gorequest v0.2.15 h1:oPjDCsF5IkD4gUk6vIgsxYNaSgvAnIh1EJeROn3HdJU=
 | 
			
		||||
github.com/parnurzeal/gorequest v0.2.15/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
 | 
			
		||||
github.com/pelletier/go-buffruneio v0.2.0 h1:U4t4R6YkofJ5xHm3dJzuRpPZ0mr5MMCoAWooScCR7aA=
 | 
			
		||||
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
 | 
			
		||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 | 
			
		||||
github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg=
 | 
			
		||||
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
 | 
			
		||||
github.com/peterhellberg/link v1.0.0 h1:mUWkiegowUXEcmlb+ybF75Q/8D2Y0BjZtR8cxoKhaQo=
 | 
			
		||||
github.com/peterhellberg/link v1.0.0/go.mod h1:gtSlOT4jmkY8P47hbTc8PTgiDDWpdPbFYl75keYyBB8=
 | 
			
		||||
github.com/pierrec/lz4 v0.0.0-20190327172049-315a67e90e41/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
 | 
			
		||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
 | 
			
		||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
			
		||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 | 
			
		||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
			
		||||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
 | 
			
		||||
github.com/pkg/profile v1.3.0/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
 | 
			
		||||
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/prometheus/client_golang v0.0.0-20180924113449-f69c853d21c1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 | 
			
		||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 | 
			
		||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 h1:D+CiwcpGTW6pL6bv6KI3KbyEyCKyS+1JWS2h8PNDnGA=
 | 
			
		||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
 | 
			
		||||
github.com/prometheus/client_golang v0.9.4/go.mod h1:oCXIBxdI62A4cR6aTRJCgetEjecSIYzOEaeAn4iYEpM=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 | 
			
		||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 | 
			
		||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 | 
			
		||||
github.com/prometheus/common v0.3.0 h1:taZ4h8Tkxv2kNyoSctBvfXEHmBmxrwmIidZTIaHons4=
 | 
			
		||||
github.com/prometheus/common v0.3.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 | 
			
		||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 | 
			
		||||
github.com/prometheus/procfs v0.0.0-20180920065004-418d78d0b9a7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 | 
			
		||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 | 
			
		||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 | 
			
		||||
github.com/prometheus/procfs v0.0.0-20190503130316-740c07785007 h1:gT4PpkbWSQM4J8fup/aXeQhY5jLDyHuPq8y2dHspqFw=
 | 
			
		||||
github.com/prometheus/procfs v0.0.0-20190503130316-740c07785007/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 | 
			
		||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 | 
			
		||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
 | 
			
		||||
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 h1:mZHayPoR0lNmnHyvtYjDeq0zlVHn9K/ZXoy17ylucdo=
 | 
			
		||||
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5/go.mod h1:GEXHk5HgEKCvEIIrSpFI3ozzG5xOKA2DVlEX/gGnewM=
 | 
			
		||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 | 
			
		||||
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
 | 
			
		||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 | 
			
		||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
 | 
			
		||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 | 
			
		||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
 | 
			
		||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 | 
			
		||||
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
 | 
			
		||||
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
 | 
			
		||||
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
 | 
			
		||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 | 
			
		||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 | 
			
		||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
 | 
			
		||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 | 
			
		||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
 | 
			
		||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 | 
			
		||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 | 
			
		||||
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
 | 
			
		||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
 | 
			
		||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
 | 
			
		||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 | 
			
		||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 | 
			
		||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
 | 
			
		||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
 | 
			
		||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
 | 
			
		||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 | 
			
		||||
github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
 | 
			
		||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 | 
			
		||||
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=
 | 
			
		||||
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
 | 
			
		||||
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/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
			
		||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 | 
			
		||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
			
		||||
github.com/tomoyamachi/reg v0.16.2-0.20190418055600-c6010b917a55 h1:O7Xl4zpk6zjYnwxUd7lubrx7xdzQ+PqfTgaxLE9nF+o=
 | 
			
		||||
github.com/tomoyamachi/reg v0.16.2-0.20190418055600-c6010b917a55/go.mod h1:12Fe9EIvK3dG/qWhNk5e9O96I8SGmCKLsJ8GsXUbk+Y=
 | 
			
		||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 | 
			
		||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
 | 
			
		||||
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 v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw=
 | 
			
		||||
github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8=
 | 
			
		||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
 | 
			
		||||
github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8=
 | 
			
		||||
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
 | 
			
		||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
 | 
			
		||||
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
 | 
			
		||||
github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
 | 
			
		||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
 | 
			
		||||
github.com/ymomoi/goval-parser v0.0.0-20170813122243-0a0be1dd9d08 h1:OsHsjWw5m3P0r+RJITvigJu9dn6L8812S54x42jxeII=
 | 
			
		||||
github.com/ymomoi/goval-parser v0.0.0-20170813122243-0a0be1dd9d08/go.mod h1:ox1Nt/rGgWuhVrNg+jKYonAs4BiQG1tRJwj4ue91iy4=
 | 
			
		||||
go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk=
 | 
			
		||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 | 
			
		||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
 | 
			
		||||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
 | 
			
		||||
go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
 | 
			
		||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 | 
			
		||||
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
 | 
			
		||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 | 
			
		||||
go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4=
 | 
			
		||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 | 
			
		||||
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
 | 
			
		||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
 | 
			
		||||
go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o=
 | 
			
		||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 h1:p/H982KKEjUnLJkM3tt/LemDnOc1GiZL5FCVlORJ5zo=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 | 
			
		||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 | 
			
		||||
golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 | 
			
		||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 | 
			
		||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 | 
			
		||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
 | 
			
		||||
golang.org/x/mobile v0.0.0-20190607214518-6fa95d984e88/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
 | 
			
		||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
 | 
			
		||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
 | 
			
		||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20180925072008-f04abc6bdfa7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
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-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190502183928-7f726cade0ab h1:9RfW3ktsOZxgo9YNbBAjq1FWzc/igwEcUzZz8IXgSbk=
 | 
			
		||||
golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 | 
			
		||||
golang.org/x/net v0.0.0-20190611141213-3f473d35a33a h1:+KkCgOMgnKSgenxTBoiwkMqTiouMIy/3o8RLdmSbGoY=
 | 
			
		||||
golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
			
		||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180925112736-b09afc3d579e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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-20190221075227-b4e8571b14e0/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/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-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 h1:vsphBvatvfbhlb4PO1BYSr9dzugGxJ/SQHoNufZJq1w=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae h1:xiXzMMEQdQcric9hXtr1QU98MHunKK7OTtsoU6bYWs4=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 | 
			
		||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 | 
			
		||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
			
		||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg=
 | 
			
		||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
			
		||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190530171427-2b03ca6e44eb/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190612180059-59534d075a87/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190612232758-d4e310b4a8a5/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373 h1:PPwnA7z1Pjf7XYaBP9GL1VAMZmcIWyFz7QCMSIIa3Bg=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
 | 
			
		||||
google.golang.org/api v0.4.0 h1:KKgc1aqhV8wDPbDzlDtpvyjZFY3vjz85FP7p4wcQUyI=
 | 
			
		||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
 | 
			
		||||
google.golang.org/api v0.6.0 h1:2tJEkRfnZL5g1GeBUlITh/rqT5HG3sFcoVCUUxmgJ2g=
 | 
			
		||||
google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4=
 | 
			
		||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 | 
			
		||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
			
		||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
			
		||||
google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
 | 
			
		||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
			
		||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20180924164928-221a8d4f7494/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 h1:nfPFGzJkUDX6uBmpN/pSw7MbOAWegH5QDQuoXFHedLg=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190611190212-a7e196e89fd3 h1:0LGHEA/u5XLibPOx6D7D8FBT/ax6wT57vNKY0QckCwo=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190611190212-a7e196e89fd3/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
 | 
			
		||||
google.golang.org/grpc v1.15.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
 | 
			
		||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
 | 
			
		||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 | 
			
		||||
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 | 
			
		||||
google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU=
 | 
			
		||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 | 
			
		||||
google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8=
 | 
			
		||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 | 
			
		||||
gopkg.in/VividCortex/ewma.v1 v1.1.1 h1:tWHEKkKq802K/JT9RiqGCBU5fW3raAPnJGTE9ostZvg=
 | 
			
		||||
gopkg.in/VividCortex/ewma.v1 v1.1.1/go.mod h1:TekXuFipeiHWiAlO1+wSS23vTcyFau5u3rxXUSXj710=
 | 
			
		||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
 | 
			
		||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk=
 | 
			
		||||
gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
 | 
			
		||||
gopkg.in/cheggaaa/pb.v2 v2.0.6 h1:L2KAo2l2ZQTzxmh8b9RdQpzgLpK2mX3paGCMJSUugBk=
 | 
			
		||||
gopkg.in/cheggaaa/pb.v2 v2.0.6/go.mod h1:0CiZ1p8pvtxBlQpLXkHuUTpdJ1shm3OqCF1QugkjHL4=
 | 
			
		||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 | 
			
		||||
gopkg.in/fatih/color.v1 v1.7.0 h1:bYGjb+HezBM6j/QmgBfgm1adxHpzzrss6bj4r9ROppk=
 | 
			
		||||
gopkg.in/fatih/color.v1 v1.7.0/go.mod h1:P7yosIhqIl/sX8J8UypY5M+dDpD2KmyfP5IRs5v/fo0=
 | 
			
		||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
 | 
			
		||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 | 
			
		||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
 | 
			
		||||
gopkg.in/mattn/go-runewidth.v0 v0.0.4 h1:r0P71TnzQDlNIcizCqvPSSANoFa3WVGtcNJf3TWurcY=
 | 
			
		||||
gopkg.in/mattn/go-runewidth.v0 v0.0.4/go.mod h1:BmXejnxvhwdaATwiJbB1vZ2dtXkQKZGu9yLFCZb4msQ=
 | 
			
		||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 | 
			
		||||
gopkg.in/src-d/go-billy.v4 v4.2.1/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
 | 
			
		||||
gopkg.in/src-d/go-billy.v4 v4.3.0 h1:KtlZ4c1OWbIs4jCv5ZXrTqG8EQocr0g/d4DjNg70aek=
 | 
			
		||||
gopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
 | 
			
		||||
gopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
 | 
			
		||||
gopkg.in/src-d/go-git-fixtures.v3 v3.4.0 h1:KFpaNTUcLHLoP/OkdcRXR+MA5p55MhA41YVb7Wd8EfM=
 | 
			
		||||
gopkg.in/src-d/go-git-fixtures.v3 v3.4.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
 | 
			
		||||
gopkg.in/src-d/go-git.v4 v4.10.0 h1:NWjTJTQnk8UpIGlssuefyDZ6JruEjo5s88vm88uASbw=
 | 
			
		||||
gopkg.in/src-d/go-git.v4 v4.10.0/go.mod h1:Vtut8izDyrM8BUVQnzJ+YvmNcem2J89EmfZYCkLokZk=
 | 
			
		||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 | 
			
		||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 | 
			
		||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
 | 
			
		||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gotest.tools v2.1.0+incompatible h1:5USw7CrJBYKqjg9R7QlA6jzqZKEAtvW82aNmsxxGPxw=
 | 
			
		||||
gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190607181801-497c8f037f5a/go.mod h1:JlmFZigtG9vBVR3QGIQ9g/Usz4BzH+Xm6Z8iHQWRYUw=
 | 
			
		||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 | 
			
		||||
							
								
								
									
										182
									
								
								gost/debian.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,182 @@
 | 
			
		||||
/* 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 gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/knqyf263/gost/db"
 | 
			
		||||
	gostmodels "github.com/knqyf263/gost/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Debian is Gost client for Debian GNU/Linux
 | 
			
		||||
type Debian struct {
 | 
			
		||||
	Base
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type packCves struct {
 | 
			
		||||
	packName  string
 | 
			
		||||
	isSrcPack bool
 | 
			
		||||
	cves      []models.CveContent
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillWithGost fills cve information that has in Gost
 | 
			
		||||
func (deb Debian) FillWithGost(driver db.DB, r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
	linuxImage := "linux-image-" + r.RunningKernel.Release
 | 
			
		||||
	// Add linux and set the version of running kernel to search OVAL.
 | 
			
		||||
	if r.Container.ContainerID == "" {
 | 
			
		||||
		newVer := ""
 | 
			
		||||
		if p, ok := r.Packages[linuxImage]; ok {
 | 
			
		||||
			newVer = p.NewVersion
 | 
			
		||||
		}
 | 
			
		||||
		r.Packages["linux"] = models.Package{
 | 
			
		||||
			Name:       "linux",
 | 
			
		||||
			Version:    r.RunningKernel.Version,
 | 
			
		||||
			NewVersion: newVer,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	packCvesList := []packCves{}
 | 
			
		||||
	if config.Conf.Gost.IsFetchViaHTTP() {
 | 
			
		||||
		url, _ := util.URLPathJoin(config.Conf.Gost.URL, "debian", major(r.Release), "pkgs")
 | 
			
		||||
		responses, err := getAllUnfixedCvesViaHTTP(r, url)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, res := range responses {
 | 
			
		||||
			debCves := map[string]gostmodels.DebianCVE{}
 | 
			
		||||
			if err := json.Unmarshal([]byte(res.json), &debCves); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
			}
 | 
			
		||||
			cves := []models.CveContent{}
 | 
			
		||||
			for _, debcve := range debCves {
 | 
			
		||||
				cves = append(cves, *deb.ConvertToModel(&debcve))
 | 
			
		||||
			}
 | 
			
		||||
			packCvesList = append(packCvesList, packCves{
 | 
			
		||||
				packName:  res.request.packName,
 | 
			
		||||
				isSrcPack: res.request.isSrcPack,
 | 
			
		||||
				cves:      cves,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if driver == nil {
 | 
			
		||||
			return 0, nil
 | 
			
		||||
		}
 | 
			
		||||
		for _, pack := range r.Packages {
 | 
			
		||||
			cveDebs := driver.GetUnfixedCvesDebian(major(r.Release), pack.Name)
 | 
			
		||||
			cves := []models.CveContent{}
 | 
			
		||||
			for _, cveDeb := range cveDebs {
 | 
			
		||||
				cves = append(cves, *deb.ConvertToModel(&cveDeb))
 | 
			
		||||
			}
 | 
			
		||||
			packCvesList = append(packCvesList, packCves{
 | 
			
		||||
				packName:  pack.Name,
 | 
			
		||||
				isSrcPack: false,
 | 
			
		||||
				cves:      cves,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// SrcPack
 | 
			
		||||
		for _, pack := range r.SrcPackages {
 | 
			
		||||
			cveDebs := driver.GetUnfixedCvesDebian(major(r.Release), pack.Name)
 | 
			
		||||
			cves := []models.CveContent{}
 | 
			
		||||
			for _, cveDeb := range cveDebs {
 | 
			
		||||
				cves = append(cves, *deb.ConvertToModel(&cveDeb))
 | 
			
		||||
			}
 | 
			
		||||
			packCvesList = append(packCvesList, packCves{
 | 
			
		||||
				packName:  pack.Name,
 | 
			
		||||
				isSrcPack: true,
 | 
			
		||||
				cves:      cves,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	delete(r.Packages, "linux")
 | 
			
		||||
 | 
			
		||||
	for _, p := range packCvesList {
 | 
			
		||||
		for _, cve := range p.cves {
 | 
			
		||||
			v, ok := r.ScannedCves[cve.CveID]
 | 
			
		||||
			if ok {
 | 
			
		||||
				if v.CveContents == nil {
 | 
			
		||||
					v.CveContents = models.NewCveContents(cve)
 | 
			
		||||
				} else {
 | 
			
		||||
					v.CveContents[models.DebianSecurityTracker] = cve
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				v = models.VulnInfo{
 | 
			
		||||
					CveID:       cve.CveID,
 | 
			
		||||
					CveContents: models.NewCveContents(cve),
 | 
			
		||||
					Confidences: models.Confidences{models.DebianSecurityTrackerMatch},
 | 
			
		||||
				}
 | 
			
		||||
				nCVEs++
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			names := []string{}
 | 
			
		||||
			if p.isSrcPack {
 | 
			
		||||
				if srcPack, ok := r.SrcPackages[p.packName]; ok {
 | 
			
		||||
					for _, binName := range srcPack.BinaryNames {
 | 
			
		||||
						if _, ok := r.Packages[binName]; ok {
 | 
			
		||||
							names = append(names, binName)
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				if p.packName == "linux" {
 | 
			
		||||
					names = append(names, linuxImage)
 | 
			
		||||
				} else {
 | 
			
		||||
					names = append(names, p.packName)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, name := range names {
 | 
			
		||||
				v.AffectedPackages = v.AffectedPackages.Store(models.PackageFixStatus{
 | 
			
		||||
					Name:        name,
 | 
			
		||||
					FixState:    "open",
 | 
			
		||||
					NotFixedYet: true,
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
			r.ScannedCves[cve.CveID] = v
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nCVEs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConvertToModel converts gost model to vuls model
 | 
			
		||||
func (deb Debian) ConvertToModel(cve *gostmodels.DebianCVE) *models.CveContent {
 | 
			
		||||
	severity := ""
 | 
			
		||||
	for _, p := range cve.Package {
 | 
			
		||||
		for _, r := range p.Release {
 | 
			
		||||
			severity = r.Urgency
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return &models.CveContent{
 | 
			
		||||
		Type:          models.DebianSecurityTracker,
 | 
			
		||||
		CveID:         cve.CveID,
 | 
			
		||||
		Summary:       cve.Description,
 | 
			
		||||
		Cvss2Severity: severity,
 | 
			
		||||
		Cvss3Severity: severity,
 | 
			
		||||
		SourceLink:    "https://security-tracker.debian.org/tracker/" + cve.CveID,
 | 
			
		||||
		Optional: map[string]string{
 | 
			
		||||
			"attack range": cve.Scope,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										104
									
								
								gost/gost.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,104 @@
 | 
			
		||||
/* 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 gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	cnf "github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/knqyf263/gost/db"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Client is the interface of OVAL client.
 | 
			
		||||
type Client interface {
 | 
			
		||||
	FillWithGost(db.DB, *models.ScanResult) (int, error)
 | 
			
		||||
 | 
			
		||||
	//TODO implement
 | 
			
		||||
	// CheckHTTPHealth() error
 | 
			
		||||
	// CheckIfGostFetched checks if Gost entries are fetched
 | 
			
		||||
	// CheckIfGostFetched(db.DB, string, string) (bool, error)
 | 
			
		||||
	// CheckIfGostFresh(db.DB, string, string) (bool, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewClient make Client by family
 | 
			
		||||
func NewClient(family string) Client {
 | 
			
		||||
	switch family {
 | 
			
		||||
	case cnf.RedHat, cnf.CentOS:
 | 
			
		||||
		return RedHat{}
 | 
			
		||||
	case cnf.Debian:
 | 
			
		||||
		return Debian{}
 | 
			
		||||
	case cnf.Windows:
 | 
			
		||||
		return Microsoft{}
 | 
			
		||||
	default:
 | 
			
		||||
		return Pseudo{}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Base is a base struct
 | 
			
		||||
type Base struct {
 | 
			
		||||
	family string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckHTTPHealth do health check
 | 
			
		||||
func (b Base) CheckHTTPHealth() error {
 | 
			
		||||
	if !cnf.Conf.Gost.IsFetchViaHTTP() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	url := fmt.Sprintf("%s/health", cnf.Conf.Gost.URL)
 | 
			
		||||
	var errs []error
 | 
			
		||||
	var resp *http.Response
 | 
			
		||||
	resp, _, errs = gorequest.New().Get(url).End()
 | 
			
		||||
	//  resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
 | 
			
		||||
	//  resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
 | 
			
		||||
	if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
		return xerrors.Errorf("Failed to connect to gost server. url: %s, errs: %w", url, errs)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckIfGostFetched checks if oval entries are in DB by family, release.
 | 
			
		||||
func (b Base) CheckIfGostFetched(driver db.DB, osFamily string) (fetched bool, err error) {
 | 
			
		||||
	//TODO
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckIfGostFresh checks if oval entries are fresh enough
 | 
			
		||||
func (b Base) CheckIfGostFresh(driver db.DB, osFamily string) (ok bool, err error) {
 | 
			
		||||
	//TODO
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Pseudo is Gost client except for RedHat family and Debian
 | 
			
		||||
type Pseudo struct {
 | 
			
		||||
	Base
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillWithGost fills cve information that has in Gost
 | 
			
		||||
func (pse Pseudo) FillWithGost(driver db.DB, r *models.ScanResult) (int, error) {
 | 
			
		||||
	return 0, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func major(osVer string) (majorVersion string) {
 | 
			
		||||
	return strings.Split(osVer, ".")[0]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										129
									
								
								gost/gost_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,129 @@
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	gostmodels "github.com/knqyf263/gost/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestSetPackageStates(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		pkgstats  []gostmodels.RedhatPackageState
 | 
			
		||||
		installed models.Packages
 | 
			
		||||
		release   string
 | 
			
		||||
		in        models.VulnInfo
 | 
			
		||||
		out       models.PackageFixStatuses
 | 
			
		||||
	}{
 | 
			
		||||
 | 
			
		||||
		//0 one
 | 
			
		||||
		{
 | 
			
		||||
			pkgstats: []gostmodels.RedhatPackageState{
 | 
			
		||||
				{
 | 
			
		||||
					FixState:    "Will not fix",
 | 
			
		||||
					PackageName: "bouncycastle",
 | 
			
		||||
					Cpe:         "cpe:/o:redhat:enterprise_linux:7",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			installed: models.Packages{
 | 
			
		||||
				"bouncycastle": models.Package{},
 | 
			
		||||
			},
 | 
			
		||||
			release: "7",
 | 
			
		||||
			in:      models.VulnInfo{},
 | 
			
		||||
			out: []models.PackageFixStatus{
 | 
			
		||||
				{
 | 
			
		||||
					Name:        "bouncycastle",
 | 
			
		||||
					FixState:    "Will not fix",
 | 
			
		||||
					NotFixedYet: true,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		//1 two
 | 
			
		||||
		{
 | 
			
		||||
			pkgstats: []gostmodels.RedhatPackageState{
 | 
			
		||||
				{
 | 
			
		||||
					FixState:    "Will not fix",
 | 
			
		||||
					PackageName: "bouncycastle",
 | 
			
		||||
					Cpe:         "cpe:/o:redhat:enterprise_linux:7",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					FixState:    "Fix deferred",
 | 
			
		||||
					PackageName: "pack_a",
 | 
			
		||||
					Cpe:         "cpe:/o:redhat:enterprise_linux:7",
 | 
			
		||||
				},
 | 
			
		||||
				// ignore not-installed-package
 | 
			
		||||
				{
 | 
			
		||||
					FixState:    "Fix deferred",
 | 
			
		||||
					PackageName: "pack_b",
 | 
			
		||||
					Cpe:         "cpe:/o:redhat:enterprise_linux:7",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			installed: models.Packages{
 | 
			
		||||
				"bouncycastle": models.Package{},
 | 
			
		||||
				"pack_a":       models.Package{},
 | 
			
		||||
			},
 | 
			
		||||
			release: "7",
 | 
			
		||||
			in:      models.VulnInfo{},
 | 
			
		||||
			out: []models.PackageFixStatus{
 | 
			
		||||
				{
 | 
			
		||||
					Name:        "bouncycastle",
 | 
			
		||||
					FixState:    "Will not fix",
 | 
			
		||||
					NotFixedYet: true,
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Name:        "pack_a",
 | 
			
		||||
					FixState:    "Fix deferred",
 | 
			
		||||
					NotFixedYet: true,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		//2 ignore affected
 | 
			
		||||
		{
 | 
			
		||||
			pkgstats: []gostmodels.RedhatPackageState{
 | 
			
		||||
				{
 | 
			
		||||
					FixState:    "affected",
 | 
			
		||||
					PackageName: "bouncycastle",
 | 
			
		||||
					Cpe:         "cpe:/o:redhat:enterprise_linux:7",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			installed: models.Packages{
 | 
			
		||||
				"bouncycastle": models.Package{},
 | 
			
		||||
			},
 | 
			
		||||
			release: "7",
 | 
			
		||||
			in: models.VulnInfo{
 | 
			
		||||
				AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
			},
 | 
			
		||||
			out: models.PackageFixStatuses{},
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		//3 look only the same os release.
 | 
			
		||||
		{
 | 
			
		||||
			pkgstats: []gostmodels.RedhatPackageState{
 | 
			
		||||
				{
 | 
			
		||||
					FixState:    "Will not fix",
 | 
			
		||||
					PackageName: "bouncycastle",
 | 
			
		||||
					Cpe:         "cpe:/o:redhat:enterprise_linux:6",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			installed: models.Packages{
 | 
			
		||||
				"bouncycastle": models.Package{},
 | 
			
		||||
			},
 | 
			
		||||
			release: "7",
 | 
			
		||||
			in: models.VulnInfo{
 | 
			
		||||
				AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
			},
 | 
			
		||||
			out: models.PackageFixStatuses{},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r := RedHat{}
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
		out := r.mergePackageStates(tt.in, tt.pkgstats, tt.installed, tt.release)
 | 
			
		||||
		if ok := reflect.DeepEqual(tt.out, out); !ok {
 | 
			
		||||
			t.Errorf("[%d]\nexpected: %v:%T\n  actual: %v:%T\n", i, tt.out, tt.out, out, out)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										116
									
								
								gost/microsoft.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,116 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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 gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/knqyf263/gost/db"
 | 
			
		||||
	gostmodels "github.com/knqyf263/gost/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Microsoft is Gost client for windows
 | 
			
		||||
type Microsoft struct {
 | 
			
		||||
	Base
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillWithGost fills cve information that has in Gost
 | 
			
		||||
func (ms Microsoft) FillWithGost(driver db.DB, r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
	if driver == nil {
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	}
 | 
			
		||||
	var cveIDs []string
 | 
			
		||||
	for cveID := range r.ScannedCves {
 | 
			
		||||
		cveIDs = append(cveIDs, cveID)
 | 
			
		||||
	}
 | 
			
		||||
	for cveID, msCve := range driver.GetMicrosoftMulti(cveIDs) {
 | 
			
		||||
		if _, ok := r.ScannedCves[cveID]; !ok {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		cveCont := ms.ConvertToModel(&msCve)
 | 
			
		||||
		v, _ := r.ScannedCves[cveID]
 | 
			
		||||
		if v.CveContents == nil {
 | 
			
		||||
			v.CveContents = models.CveContents{}
 | 
			
		||||
		}
 | 
			
		||||
		v.CveContents[models.Microsoft] = *cveCont
 | 
			
		||||
		r.ScannedCves[cveID] = v
 | 
			
		||||
	}
 | 
			
		||||
	return len(cveIDs), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConvertToModel converts gost model to vuls model
 | 
			
		||||
func (ms Microsoft) ConvertToModel(cve *gostmodels.MicrosoftCVE) *models.CveContent {
 | 
			
		||||
	v3score := 0.0
 | 
			
		||||
	var v3Vector string
 | 
			
		||||
	for _, scoreSet := range cve.ScoreSets {
 | 
			
		||||
		if v3score < scoreSet.BaseScore {
 | 
			
		||||
			v3score = scoreSet.BaseScore
 | 
			
		||||
			v3Vector = scoreSet.Vector
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var v3Severity string
 | 
			
		||||
	for _, s := range cve.Severity {
 | 
			
		||||
		v3Severity = s.Description
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var refs []models.Reference
 | 
			
		||||
	for _, r := range cve.References {
 | 
			
		||||
		if r.AttrType == "External" {
 | 
			
		||||
			refs = append(refs, models.Reference{Link: r.URL})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var cwe []string
 | 
			
		||||
	if 0 < len(cve.CWE) {
 | 
			
		||||
		cwe = []string{cve.CWE}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	option := map[string]string{}
 | 
			
		||||
	if 0 < len(cve.ExploitStatus) {
 | 
			
		||||
		option["exploit"] = cve.ExploitStatus
 | 
			
		||||
	}
 | 
			
		||||
	if 0 < len(cve.Workaround) {
 | 
			
		||||
		option["workaround"] = cve.Workaround
 | 
			
		||||
	}
 | 
			
		||||
	var kbids []string
 | 
			
		||||
	for _, kbid := range cve.KBIDs {
 | 
			
		||||
		kbids = append(kbids, kbid.KBID)
 | 
			
		||||
	}
 | 
			
		||||
	if 0 < len(kbids) {
 | 
			
		||||
		option["kbids"] = strings.Join(kbids, ",")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &models.CveContent{
 | 
			
		||||
		Type:          models.Microsoft,
 | 
			
		||||
		CveID:         cve.CveID,
 | 
			
		||||
		Title:         cve.Title,
 | 
			
		||||
		Summary:       cve.Description,
 | 
			
		||||
		Cvss3Score:    v3score,
 | 
			
		||||
		Cvss3Vector:   v3Vector,
 | 
			
		||||
		Cvss3Severity: v3Severity,
 | 
			
		||||
		References:    refs,
 | 
			
		||||
		CweIDs:        cwe,
 | 
			
		||||
		Mitigation:    cve.Mitigation,
 | 
			
		||||
		Published:     cve.PublishDate,
 | 
			
		||||
		LastModified:  cve.LastUpdateDate,
 | 
			
		||||
		SourceLink:    "https://portal.msrc.microsoft.com/ja-jp/security-guidance/advisory/" + cve.CveID,
 | 
			
		||||
		Optional:      option,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										290
									
								
								gost/redhat.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,290 @@
 | 
			
		||||
/* 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 gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/knqyf263/gost/db"
 | 
			
		||||
	gostmodels "github.com/knqyf263/gost/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RedHat is Gost client for RedHat family linux
 | 
			
		||||
type RedHat struct {
 | 
			
		||||
	Base
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillWithGost fills cve information that has in Gost
 | 
			
		||||
func (red RedHat) FillWithGost(driver db.DB, r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
	if nCVEs, err = red.fillUnfixed(driver, r); err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	return nCVEs, red.fillFixed(driver, r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (red RedHat) fillFixed(driver db.DB, r *models.ScanResult) error {
 | 
			
		||||
	var cveIDs []string
 | 
			
		||||
	for cveID, vuln := range r.ScannedCves {
 | 
			
		||||
		if _, ok := vuln.CveContents[models.RedHatAPI]; ok {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		cveIDs = append(cveIDs, cveID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if config.Conf.Gost.IsFetchViaHTTP() {
 | 
			
		||||
		prefix, _ := util.URLPathJoin(config.Conf.Gost.URL,
 | 
			
		||||
			"redhat", "cves")
 | 
			
		||||
		responses, err := getCvesViaHTTP(cveIDs, prefix)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		for _, res := range responses {
 | 
			
		||||
			redCve := gostmodels.RedhatCVE{}
 | 
			
		||||
			if err := json.Unmarshal([]byte(res.json), &redCve); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			if redCve.ID == 0 {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			cveCont := red.ConvertToModel(&redCve)
 | 
			
		||||
			v, ok := r.ScannedCves[res.request.cveID]
 | 
			
		||||
			if ok {
 | 
			
		||||
				if v.CveContents == nil {
 | 
			
		||||
					v.CveContents = models.NewCveContents(*cveCont)
 | 
			
		||||
				} else {
 | 
			
		||||
					v.CveContents[models.RedHatAPI] = *cveCont
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				v = models.VulnInfo{
 | 
			
		||||
					CveID:       cveCont.CveID,
 | 
			
		||||
					CveContents: models.NewCveContents(*cveCont),
 | 
			
		||||
					Confidences: models.Confidences{models.RedHatAPIMatch},
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			r.ScannedCves[res.request.cveID] = v
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if driver == nil {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		for cveID, redCve := range driver.GetRedhatMulti(cveIDs) {
 | 
			
		||||
			if redCve.ID == 0 {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			cveCont := red.ConvertToModel(&redCve)
 | 
			
		||||
			v, ok := r.ScannedCves[cveID]
 | 
			
		||||
			if ok {
 | 
			
		||||
				if v.CveContents == nil {
 | 
			
		||||
					v.CveContents = models.NewCveContents(*cveCont)
 | 
			
		||||
				} else {
 | 
			
		||||
					v.CveContents[models.RedHatAPI] = *cveCont
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				v = models.VulnInfo{
 | 
			
		||||
					CveID:       cveCont.CveID,
 | 
			
		||||
					CveContents: models.NewCveContents(*cveCont),
 | 
			
		||||
					Confidences: models.Confidences{models.RedHatAPIMatch},
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			r.ScannedCves[cveID] = v
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (red RedHat) fillUnfixed(driver db.DB, r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
	if config.Conf.Gost.IsFetchViaHTTP() {
 | 
			
		||||
		prefix, _ := util.URLPathJoin(config.Conf.Gost.URL,
 | 
			
		||||
			"redhat", major(r.Release), "pkgs")
 | 
			
		||||
		responses, err := getAllUnfixedCvesViaHTTP(r, prefix)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		for _, res := range responses {
 | 
			
		||||
			// CVE-ID: RedhatCVE
 | 
			
		||||
			cves := map[string]gostmodels.RedhatCVE{}
 | 
			
		||||
			if err := json.Unmarshal([]byte(res.json), &cves); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, cve := range cves {
 | 
			
		||||
				cveCont := red.ConvertToModel(&cve)
 | 
			
		||||
				v, ok := r.ScannedCves[cve.Name]
 | 
			
		||||
				if ok {
 | 
			
		||||
					if v.CveContents == nil {
 | 
			
		||||
						v.CveContents = models.NewCveContents(*cveCont)
 | 
			
		||||
					} else {
 | 
			
		||||
						v.CveContents[models.RedHatAPI] = *cveCont
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					v = models.VulnInfo{
 | 
			
		||||
						CveID:       cveCont.CveID,
 | 
			
		||||
						CveContents: models.NewCveContents(*cveCont),
 | 
			
		||||
						Confidences: models.Confidences{models.RedHatAPIMatch},
 | 
			
		||||
					}
 | 
			
		||||
					nCVEs++
 | 
			
		||||
				}
 | 
			
		||||
				pkgStats := red.mergePackageStates(v,
 | 
			
		||||
					cve.PackageState, r.Packages, r.Release)
 | 
			
		||||
				if 0 < len(pkgStats) {
 | 
			
		||||
					v.AffectedPackages = pkgStats
 | 
			
		||||
					r.ScannedCves[cve.Name] = v
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if driver == nil {
 | 
			
		||||
			return 0, nil
 | 
			
		||||
		}
 | 
			
		||||
		for _, pack := range r.Packages {
 | 
			
		||||
			// CVE-ID: RedhatCVE
 | 
			
		||||
			cves := map[string]gostmodels.RedhatCVE{}
 | 
			
		||||
			cves = driver.GetUnfixedCvesRedhat(major(r.Release), pack.Name)
 | 
			
		||||
			for _, cve := range cves {
 | 
			
		||||
				cveCont := red.ConvertToModel(&cve)
 | 
			
		||||
				v, ok := r.ScannedCves[cve.Name]
 | 
			
		||||
				if ok {
 | 
			
		||||
					if v.CveContents == nil {
 | 
			
		||||
						v.CveContents = models.NewCveContents(*cveCont)
 | 
			
		||||
					} else {
 | 
			
		||||
						v.CveContents[models.RedHatAPI] = *cveCont
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					v = models.VulnInfo{
 | 
			
		||||
						CveID:       cveCont.CveID,
 | 
			
		||||
						CveContents: models.NewCveContents(*cveCont),
 | 
			
		||||
						Confidences: models.Confidences{models.RedHatAPIMatch},
 | 
			
		||||
					}
 | 
			
		||||
					nCVEs++
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				pkgStats := red.mergePackageStates(v,
 | 
			
		||||
					cve.PackageState, r.Packages, r.Release)
 | 
			
		||||
				if 0 < len(pkgStats) {
 | 
			
		||||
					v.AffectedPackages = pkgStats
 | 
			
		||||
					r.ScannedCves[cve.Name] = v
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nCVEs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (red RedHat) mergePackageStates(v models.VulnInfo, ps []gostmodels.RedhatPackageState, installed models.Packages, release string) (pkgStats models.PackageFixStatuses) {
 | 
			
		||||
	pkgStats = v.AffectedPackages
 | 
			
		||||
	for _, pstate := range ps {
 | 
			
		||||
		if pstate.Cpe !=
 | 
			
		||||
			"cpe:/o:redhat:enterprise_linux:"+major(release) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !(pstate.FixState == "Will not fix" ||
 | 
			
		||||
			pstate.FixState == "Fix deferred" ||
 | 
			
		||||
			pstate.FixState == "Affected") {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if _, ok := installed[pstate.PackageName]; !ok {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		notFixedYet := false
 | 
			
		||||
		switch pstate.FixState {
 | 
			
		||||
		case "Will not fix", "Fix deferred", "Affected":
 | 
			
		||||
			notFixedYet = true
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pkgStats = pkgStats.Store(models.PackageFixStatus{
 | 
			
		||||
			Name:        pstate.PackageName,
 | 
			
		||||
			FixState:    pstate.FixState,
 | 
			
		||||
			NotFixedYet: notFixedYet,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (red RedHat) parseCwe(str string) (cwes []string) {
 | 
			
		||||
	if str != "" {
 | 
			
		||||
		s := strings.Replace(str, "(", "|", -1)
 | 
			
		||||
		s = strings.Replace(s, ")", "|", -1)
 | 
			
		||||
		s = strings.Replace(s, "->", "|", -1)
 | 
			
		||||
		for _, s := range strings.Split(s, "|") {
 | 
			
		||||
			if s != "" {
 | 
			
		||||
				cwes = append(cwes, s)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConvertToModel converts gost model to vuls model
 | 
			
		||||
func (red RedHat) ConvertToModel(cve *gostmodels.RedhatCVE) *models.CveContent {
 | 
			
		||||
	cwes := red.parseCwe(cve.Cwe)
 | 
			
		||||
 | 
			
		||||
	details := []string{}
 | 
			
		||||
	for _, detail := range cve.Details {
 | 
			
		||||
		details = append(details, detail.Detail)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	v2score := 0.0
 | 
			
		||||
	if cve.Cvss.CvssBaseScore != "" {
 | 
			
		||||
		v2score, _ = strconv.ParseFloat(cve.Cvss.CvssBaseScore, 64)
 | 
			
		||||
	}
 | 
			
		||||
	v2severity := ""
 | 
			
		||||
	if v2score != 0 {
 | 
			
		||||
		v2severity = cve.ThreatSeverity
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	v3score := 0.0
 | 
			
		||||
	if cve.Cvss3.Cvss3BaseScore != "" {
 | 
			
		||||
		v3score, _ = strconv.ParseFloat(cve.Cvss3.Cvss3BaseScore, 64)
 | 
			
		||||
	}
 | 
			
		||||
	v3severity := ""
 | 
			
		||||
	if v3score != 0 {
 | 
			
		||||
		v3severity = cve.ThreatSeverity
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var refs []models.Reference
 | 
			
		||||
	for _, r := range cve.References {
 | 
			
		||||
		refs = append(refs, models.Reference{Link: r.Reference})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &models.CveContent{
 | 
			
		||||
		Type:          models.RedHatAPI,
 | 
			
		||||
		CveID:         cve.Name,
 | 
			
		||||
		Title:         cve.Bugzilla.Description,
 | 
			
		||||
		Summary:       strings.Join(details, "\n"),
 | 
			
		||||
		Cvss2Score:    v2score,
 | 
			
		||||
		Cvss2Vector:   cve.Cvss.CvssScoringVector,
 | 
			
		||||
		Cvss2Severity: v2severity,
 | 
			
		||||
		Cvss3Score:    v3score,
 | 
			
		||||
		Cvss3Vector:   cve.Cvss3.Cvss3ScoringVector,
 | 
			
		||||
		Cvss3Severity: v3severity,
 | 
			
		||||
		References:    refs,
 | 
			
		||||
		CweIDs:        cwes,
 | 
			
		||||
		Mitigation:    cve.Mitigation,
 | 
			
		||||
		Published:     cve.PublicDate,
 | 
			
		||||
		SourceLink:    "https://access.redhat.com/security/cve/" + cve.Name,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										37
									
								
								gost/redhat_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,37 @@
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestParseCwe(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in  string
 | 
			
		||||
		out []string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			in:  "CWE-665->(CWE-200|CWE-89)",
 | 
			
		||||
			out: []string{"CWE-665", "CWE-200", "CWE-89"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in:  "CWE-841->CWE-770->CWE-454",
 | 
			
		||||
			out: []string{"CWE-841", "CWE-770", "CWE-454"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in:  "(CWE-122|CWE-125)",
 | 
			
		||||
			out: []string{"CWE-122", "CWE-125"},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r := RedHat{}
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
		out := r.parseCwe(tt.in)
 | 
			
		||||
		sort.Strings(out)
 | 
			
		||||
		sort.Strings(tt.out)
 | 
			
		||||
		if !reflect.DeepEqual(tt.out, out) {
 | 
			
		||||
			t.Errorf("[%d]expected: %s, actual: %s", i, tt.out, out)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										200
									
								
								gost/util.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,200 @@
 | 
			
		||||
/* 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 gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cenkalti/backoff"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type response struct {
 | 
			
		||||
	request request
 | 
			
		||||
	json    string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getCvesViaHTTP(cveIDs []string, urlPrefix string) (
 | 
			
		||||
	responses []response, err error) {
 | 
			
		||||
	nReq := len(cveIDs)
 | 
			
		||||
	reqChan := make(chan request, nReq)
 | 
			
		||||
	resChan := make(chan response, nReq)
 | 
			
		||||
	errChan := make(chan error, nReq)
 | 
			
		||||
	defer close(reqChan)
 | 
			
		||||
	defer close(resChan)
 | 
			
		||||
	defer close(errChan)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		for _, cveID := range cveIDs {
 | 
			
		||||
			reqChan <- request{
 | 
			
		||||
				cveID: cveID,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	concurrency := 10
 | 
			
		||||
	tasks := util.GenWorkers(concurrency)
 | 
			
		||||
	for i := 0; i < nReq; i++ {
 | 
			
		||||
		tasks <- func() {
 | 
			
		||||
			select {
 | 
			
		||||
			case req := <-reqChan:
 | 
			
		||||
				url, err := util.URLPathJoin(
 | 
			
		||||
					urlPrefix,
 | 
			
		||||
					req.cveID,
 | 
			
		||||
				)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					errChan <- err
 | 
			
		||||
				} else {
 | 
			
		||||
					util.Log.Debugf("HTTP Request to %s", url)
 | 
			
		||||
					httpGet(url, req, resChan, errChan)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	timeout := time.After(2 * 60 * time.Second)
 | 
			
		||||
	var errs []error
 | 
			
		||||
	for i := 0; i < nReq; i++ {
 | 
			
		||||
		select {
 | 
			
		||||
		case res := <-resChan:
 | 
			
		||||
			responses = append(responses, res)
 | 
			
		||||
		case err := <-errChan:
 | 
			
		||||
			errs = append(errs, err)
 | 
			
		||||
		case <-timeout:
 | 
			
		||||
			return nil, xerrors.New("Timeout Fetching OVAL")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(errs) != 0 {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to fetch OVAL. err: %w", errs)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type request struct {
 | 
			
		||||
	osMajorVersion string
 | 
			
		||||
	packName       string
 | 
			
		||||
	isSrcPack      bool
 | 
			
		||||
	cveID          string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getAllUnfixedCvesViaHTTP(r *models.ScanResult, urlPrefix string) (
 | 
			
		||||
	responses []response, err error) {
 | 
			
		||||
 | 
			
		||||
	nReq := len(r.Packages) + len(r.SrcPackages)
 | 
			
		||||
	reqChan := make(chan request, nReq)
 | 
			
		||||
	resChan := make(chan response, nReq)
 | 
			
		||||
	errChan := make(chan error, nReq)
 | 
			
		||||
	defer close(reqChan)
 | 
			
		||||
	defer close(resChan)
 | 
			
		||||
	defer close(errChan)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		for _, pack := range r.Packages {
 | 
			
		||||
			reqChan <- request{
 | 
			
		||||
				osMajorVersion: major(r.Release),
 | 
			
		||||
				packName:       pack.Name,
 | 
			
		||||
				isSrcPack:      false,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		for _, pack := range r.SrcPackages {
 | 
			
		||||
			reqChan <- request{
 | 
			
		||||
				osMajorVersion: major(r.Release),
 | 
			
		||||
				packName:       pack.Name,
 | 
			
		||||
				isSrcPack:      true,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	concurrency := 10
 | 
			
		||||
	tasks := util.GenWorkers(concurrency)
 | 
			
		||||
	for i := 0; i < nReq; i++ {
 | 
			
		||||
		tasks <- func() {
 | 
			
		||||
			select {
 | 
			
		||||
			case req := <-reqChan:
 | 
			
		||||
				url, err := util.URLPathJoin(
 | 
			
		||||
					urlPrefix,
 | 
			
		||||
					req.packName,
 | 
			
		||||
					"unfixed-cves",
 | 
			
		||||
				)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					errChan <- err
 | 
			
		||||
				} else {
 | 
			
		||||
					util.Log.Debugf("HTTP Request to %s", url)
 | 
			
		||||
					httpGet(url, req, resChan, errChan)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	timeout := time.After(2 * 60 * time.Second)
 | 
			
		||||
	var errs []error
 | 
			
		||||
	for i := 0; i < nReq; i++ {
 | 
			
		||||
		select {
 | 
			
		||||
		case res := <-resChan:
 | 
			
		||||
			responses = append(responses, res)
 | 
			
		||||
		case err := <-errChan:
 | 
			
		||||
			errs = append(errs, err)
 | 
			
		||||
		case <-timeout:
 | 
			
		||||
			return nil, xerrors.New("Timeout Fetching OVAL")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(errs) != 0 {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to fetch OVAL. err: %w", errs)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func httpGet(url string, req request, resChan chan<- response, errChan chan<- error) {
 | 
			
		||||
	var body string
 | 
			
		||||
	var errs []error
 | 
			
		||||
	var resp *http.Response
 | 
			
		||||
	count, retryMax := 0, 3
 | 
			
		||||
	f := func() (err error) {
 | 
			
		||||
		//  resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
 | 
			
		||||
		resp, body, errs = gorequest.New().Get(url).End()
 | 
			
		||||
		if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
			count++
 | 
			
		||||
			if count == retryMax {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			return xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %w", url, resp, errs)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	notify := func(err error, t time.Duration) {
 | 
			
		||||
		util.Log.Warnf("Failed to HTTP GET. retrying in %s seconds. err: %s", t, err)
 | 
			
		||||
	}
 | 
			
		||||
	err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		errChan <- xerrors.Errorf("HTTP Error %w", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if count == retryMax {
 | 
			
		||||
		errChan <- xerrors.New("Retry count exceeded")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resChan <- response{
 | 
			
		||||
		request: req,
 | 
			
		||||
		json:    body,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								img/vuls-abstract.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 297 KiB  | 
							
								
								
									
										1533
									
								
								img/vuls-architecture-localscan.graphml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								img/vuls-architecture-localscan.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 97 KiB  | 
| 
		 Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 91 KiB  | 
							
								
								
									
										414
									
								
								img/vuls-scan-flow-fast.graphml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,414 @@
 | 
			
		||||
<?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="right" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="88.796875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="170.763671875" x="48.61816406250006" y="0.8228014380530908">Get installed packages
 | 
			
		||||
Alpine: apk
 | 
			
		||||
Debian/Ubuntu: dpkg-query
 | 
			
		||||
Amazon/RHEL/CentOS: rpm
 | 
			
		||||
SUSE: zypper
 | 
			
		||||
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="right" 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="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="right" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="102.9296875" 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">Alpine Linux
 | 
			
		||||
CentOS
 | 
			
		||||
RHEL
 | 
			
		||||
Ubuntu
 | 
			
		||||
Debian
 | 
			
		||||
Oracle Linux
 | 
			
		||||
Suse<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="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="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="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="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>
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								img/vuls-scan-flow-fast.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 78 KiB  | 
@@ -1,6 +1,6 @@
 | 
			
		||||
<?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-->
 | 
			
		||||
  <!--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"/>
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
          <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" modelName="custom" textColor="#000000" visible="true" width="83.482421875" x="92.2587890625" y="18.93359375">Detect the OS<y:LabelModel>
 | 
			
		||||
          <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>
 | 
			
		||||
@@ -36,7 +36,7 @@
 | 
			
		||||
          <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" modelName="custom" textColor="#000000" visible="true" width="4.0" x="38.0" y="18.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>
 | 
			
		||||
@@ -53,10 +53,12 @@
 | 
			
		||||
          <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" modelName="custom" textColor="#000000" visible="true" width="170.763671875" x="48.61816406250006" y="14.95561393805309">Get installed packages
 | 
			
		||||
          <y:NodeLabel alignment="right" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="88.796875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="170.763671875" x="48.61816406250006" y="0.8228014380530908">Get installed packages
 | 
			
		||||
Alpine Linux: apk
 | 
			
		||||
Debian/Ubuntu: dpkg-query
 | 
			
		||||
Amazon/RHEL/CentOS: rpm
 | 
			
		||||
FreeBSD: pkg<y:LabelModel>
 | 
			
		||||
FreeBSD: pkg
 | 
			
		||||
SUSE: zypper<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
@@ -72,7 +74,7 @@ FreeBSD: pkg<y:LabelModel>
 | 
			
		||||
          <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" modelName="custom" textColor="#000000" visible="true" width="260.83984375" x="3.580078125" y="11.8671875">Get upgradable packages
 | 
			
		||||
          <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>
 | 
			
		||||
@@ -89,7 +91,7 @@ Debian/Ubuntu: apt-get upgrade --dry-run<y:LabelModel>
 | 
			
		||||
          <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" modelName="custom" textColor="#000000" visible="true" width="131.751953125" x="2.7201043477401754" y="9.422181178887513">foreach 
 | 
			
		||||
          <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>
 | 
			
		||||
@@ -106,7 +108,7 @@ upgradable  packages<y:LabelModel>
 | 
			
		||||
          <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" modelName="custom" textColor="#000000" visible="true" width="213.619140625" x="27.1904296875" y="11.8671875">Parse changelog and get  CVE IDs
 | 
			
		||||
          <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>
 | 
			
		||||
@@ -123,7 +125,7 @@ Debian/Ubuntu: aptitude changelog<y:LabelModel>
 | 
			
		||||
          <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" modelName="custom" textColor="#000000" visible="true" width="55.24609375" x="40.876953125" y="15.93359375">end loop<y:LabelModel>
 | 
			
		||||
          <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>
 | 
			
		||||
@@ -139,7 +141,7 @@ Debian/Ubuntu: aptitude changelog<y:LabelModel>
 | 
			
		||||
          <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" modelName="custom" textColor="#000000" visible="true" width="194.904296875" x="36.5478515625" y="18.93359375">Select the CVE detail information<y:LabelModel>
 | 
			
		||||
          <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>
 | 
			
		||||
@@ -155,7 +157,7 @@ Debian/Ubuntu: aptitude changelog<y:LabelModel>
 | 
			
		||||
          <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" modelName="custom" textColor="#000000" visible="true" width="232.744140625" x="17.6279296875" y="4.80078125">Get CVE IDs by using package manager
 | 
			
		||||
          <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"/>
 | 
			
		||||
@@ -168,45 +170,12 @@ FreeBSD: pkg audit<y:LabelModel>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n9">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.dataBase">
 | 
			
		||||
          <y:Geometry height="64.1719342604298" width="111.96965865992411" x="687.3850119398792" y="807.0697396491782"/>
 | 
			
		||||
          <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" modelName="custom" textColor="#000000" visible="true" width="48.56640625" x="31.701626204962054" y="23.019560880214726">Vuls DB<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="-8.881784197001252E-16" 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.dataBase">
 | 
			
		||||
          <y:Geometry height="65.22882427307195" width="136.83944374209864" x="411.5802781289507" y="687.385587863464"/>
 | 
			
		||||
          <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" modelName="custom" textColor="#000000" 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="n11">
 | 
			
		||||
      <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="32.265625" modelName="custom" textColor="#000000" visible="true" width="126.396484375" x="70.8017578125" y="11.8671875">Insert results into DB
 | 
			
		||||
Reporting<y:LabelModel>
 | 
			
		||||
          <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>
 | 
			
		||||
@@ -216,14 +185,14 @@ Reporting<y:LabelModel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n12">
 | 
			
		||||
    <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="287.8409153761062"/>
 | 
			
		||||
          <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" modelName="custom" textColor="#000000" visible="true" width="271.369140625" x="-1.6845703124999432" y="11.8671875">Get all changelogs by using package manager
 | 
			
		||||
CentOS: yum update --changelog<y:LabelModel>
 | 
			
		||||
          <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>
 | 
			
		||||
@@ -233,13 +202,13 @@ CentOS: yum update --changelog<y:LabelModel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n13">
 | 
			
		||||
    <node id="n11">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="309.6849206349206" y="373.8409153761062"/>
 | 
			
		||||
          <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" modelName="custom" textColor="#000000" visible="true" width="205.52734375" x="31.236328125000057" y="18.93359375">Parse changelogs and get CVE IDs <y:LabelModel>
 | 
			
		||||
          <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>
 | 
			
		||||
@@ -249,6 +218,86 @@ CentOS: yum update --changelog<y:LabelModel>
 | 
			
		||||
        </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="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>
 | 
			
		||||
@@ -267,12 +316,13 @@ CentOS: yum update --changelog<y:LabelModel>
 | 
			
		||||
          </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" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="46.697265625" x="-56.79057374984495" y="-34.26562148912808">Debian
 | 
			
		||||
Ubuntu<y:LabelModel>
 | 
			
		||||
          <y:EdgeLabel alignment="right" 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="-257.65322875976574" y="2.0000035108718635">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:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="1.9999999999998863" distanceToCenter="false" position="left" ratio="0.8652035780364729" 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>
 | 
			
		||||
@@ -330,13 +380,13 @@ Ubuntu<y:LabelModel>
 | 
			
		||||
          </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" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="51.806640625" x="10.125014629061297" y="-48.39843398912805">Amazon
 | 
			
		||||
          <y:EdgeLabel alignment="right" 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="200.87829463898197" y="4.000003510871693">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:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="6.999999999999886" distanceToCenter="false" position="right" ratio="0.8192728556300707" segment="-1"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
 | 
			
		||||
          </y:EdgeLabel>
 | 
			
		||||
@@ -344,17 +394,7 @@ FreeBSD<y:LabelModel>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e7" source="n8" target="n7">
 | 
			
		||||
      <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="e8" source="n0" target="n2">
 | 
			
		||||
    <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"/>
 | 
			
		||||
@@ -364,7 +404,7 @@ FreeBSD<y:LabelModel>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e9" source="n7" target="n11">
 | 
			
		||||
    <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"/>
 | 
			
		||||
@@ -374,40 +414,17 @@ FreeBSD<y:LabelModel>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e10" source="n7" target="n10">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="-134.01566143419018" sy="6.159084623893818" tx="0.0" ty="-29.333162136535975">
 | 
			
		||||
            <y:Point x="480.0" y="660.0"/>
 | 
			
		||||
          </y:Path>
 | 
			
		||||
          <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="e11" source="n11" target="n9">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="28.0" tx="0.0" ty="-28.86721713021484"/>
 | 
			
		||||
          <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="e12" source="n1" target="n12">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
    <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" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="46.708984375" x="-53.35447755843876" y="11.632816010871807">CentOS<y:LabelModel>
 | 
			
		||||
          <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.5" segment="0"/>
 | 
			
		||||
              <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>
 | 
			
		||||
@@ -415,8 +432,7 @@ FreeBSD<y:LabelModel>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e13" source="n12" target="n13">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
    <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"/>
 | 
			
		||||
@@ -426,12 +442,11 @@ FreeBSD<y:LabelModel>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e14" source="n13" target="n7">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
    <edge id="e11" source="n11" target="n7">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="134.00000000000006" sy="0.0" tx="0.0" ty="-28.0">
 | 
			
		||||
            <y:Point x="743.3698412698412" y="401.8409153761062"/>
 | 
			
		||||
          <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"/>
 | 
			
		||||
@@ -439,6 +454,60 @@ FreeBSD<y:LabelModel>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e12" source="n8" target="n12">
 | 
			
		||||
      <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="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="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="e15" 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="999.0" y="226.44247787610618"/>
 | 
			
		||||
            <y:Point x="999.0" y="570.8409153761062"/>
 | 
			
		||||
            <y:Point x="743.3698412698412" y="570.8409153761062"/>
 | 
			
		||||
          </y:Path>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="none"/>
 | 
			
		||||
          <y:EdgeLabel alignment="right" 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="76.8203125" x="422.923942251054" y="13.867191010871807">Alpine Linux
 | 
			
		||||
SUSE<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.8856709076027529" 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>
 | 
			
		||||
  </graph>
 | 
			
		||||
  <data key="d7">
 | 
			
		||||
    <y:Resources/>
 | 
			
		||||
 
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 90 KiB  | 
							
								
								
									
										33
									
								
								libmanager/libManager.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,33 @@
 | 
			
		||||
package libmanager
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/knqyf263/trivy/pkg/db"
 | 
			
		||||
	"github.com/knqyf263/trivy/pkg/log"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FillLibrary fills LibraryScanner informations
 | 
			
		||||
func FillLibrary(r *models.ScanResult) (totalCnt int, err error) {
 | 
			
		||||
	// initialize trivy's logger and db
 | 
			
		||||
	err = log.InitLogger(false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := db.Init(); err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	for _, lib := range r.LibraryScanners {
 | 
			
		||||
		vinfos, err := lib.Scan()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		for _, vinfo := range vinfos {
 | 
			
		||||
			r.ScannedCves[vinfo.CveID] = vinfo
 | 
			
		||||
		}
 | 
			
		||||
		totalCnt += len(vinfos)
 | 
			
		||||
	}
 | 
			
		||||
	db.Close()
 | 
			
		||||
 | 
			
		||||
	return totalCnt, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								main.go
									
									
									
									
									
								
							
							
						
						@@ -1,5 +1,5 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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
 | 
			
		||||
@@ -22,13 +22,11 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/net/context"
 | 
			
		||||
	"context"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/commands"
 | 
			
		||||
	"github.com/future-architect/vuls/version"
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/google/subcommands"
 | 
			
		||||
 | 
			
		||||
	_ "github.com/mattn/go-sqlite3"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
@@ -38,16 +36,17 @@ func main() {
 | 
			
		||||
	subcommands.Register(&commands.DiscoverCmd{}, "discover")
 | 
			
		||||
	subcommands.Register(&commands.TuiCmd{}, "tui")
 | 
			
		||||
	subcommands.Register(&commands.ScanCmd{}, "scan")
 | 
			
		||||
	subcommands.Register(&commands.PrepareCmd{}, "prepare")
 | 
			
		||||
	subcommands.Register(&commands.HistoryCmd{}, "history")
 | 
			
		||||
	subcommands.Register(&commands.ReportCmd{}, "report")
 | 
			
		||||
	subcommands.Register(&commands.ConfigtestCmd{}, "configtest")
 | 
			
		||||
	subcommands.Register(&commands.ServerCmd{}, "server")
 | 
			
		||||
 | 
			
		||||
	var v = flag.Bool("v", false, "Show version")
 | 
			
		||||
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	if *v {
 | 
			
		||||
		fmt.Printf("%s %s\n", version.Name, version.Version)
 | 
			
		||||
		fmt.Printf("vuls %s %s\n", config.Version, config.Revision)
 | 
			
		||||
		os.Exit(int(subcommands.ExitSuccess))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										371
									
								
								models/cvecontents.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,371 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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 (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/knqyf263/trivy/pkg/vulnsrc/vulnerability"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CveContents has CveContent
 | 
			
		||||
type CveContents map[CveContentType]CveContent
 | 
			
		||||
 | 
			
		||||
// NewCveContents create CveContents
 | 
			
		||||
func NewCveContents(conts ...CveContent) CveContents {
 | 
			
		||||
	m := CveContents{}
 | 
			
		||||
	for _, cont := range conts {
 | 
			
		||||
		m[cont.Type] = cont
 | 
			
		||||
	}
 | 
			
		||||
	return m
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CveContentStr has CveContentType and Value
 | 
			
		||||
type CveContentStr struct {
 | 
			
		||||
	Type  CveContentType
 | 
			
		||||
	Value string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Except returns CveContents except given keys for enumeration
 | 
			
		||||
func (v CveContents) Except(exceptCtypes ...CveContentType) (values CveContents) {
 | 
			
		||||
	values = CveContents{}
 | 
			
		||||
	for ctype, content := range v {
 | 
			
		||||
		found := false
 | 
			
		||||
		for _, exceptCtype := range exceptCtypes {
 | 
			
		||||
			if ctype == exceptCtype {
 | 
			
		||||
				found = true
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if !found {
 | 
			
		||||
			values[ctype] = content
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SourceLinks returns link of source
 | 
			
		||||
func (v CveContents) SourceLinks(lang, myFamily, cveID string) (values []CveContentStr) {
 | 
			
		||||
	if lang == "ja" {
 | 
			
		||||
		if cont, found := v[Jvn]; found && 0 < len(cont.SourceLink) {
 | 
			
		||||
			values = append(values, CveContentStr{Jvn, cont.SourceLink})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	order := CveContentTypes{Nvd, NvdXML, NewCveContentType(myFamily)}
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v[ctype]; found {
 | 
			
		||||
			if cont.SourceLink == "" {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			values = append(values, CveContentStr{ctype, cont.SourceLink})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(values) == 0 {
 | 
			
		||||
		return []CveContentStr{{
 | 
			
		||||
			Type:  Nvd,
 | 
			
		||||
			Value: "https://nvd.nist.gov/vuln/detail/" + cveID,
 | 
			
		||||
		}}
 | 
			
		||||
	}
 | 
			
		||||
	return values
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
// Severities returns Severities
 | 
			
		||||
func (v CveContents) Severities(myFamily string) (values []CveContentStr) {
 | 
			
		||||
	order := CveContentTypes{NVD, NewCveContentType(myFamily)}
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(append(order)...)...)
 | 
			
		||||
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v[ctype]; found && 0 < len(cont.Severity) {
 | 
			
		||||
			values = append(values, CveContentStr{
 | 
			
		||||
				Type:  ctype,
 | 
			
		||||
				Value: cont.Severity,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// CveContentCpes has CveContentType and Value
 | 
			
		||||
type CveContentCpes struct {
 | 
			
		||||
	Type  CveContentType
 | 
			
		||||
	Value []Cpe
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Cpes returns affected CPEs of this Vulnerability
 | 
			
		||||
func (v CveContents) Cpes(myFamily string) (values []CveContentCpes) {
 | 
			
		||||
	order := CveContentTypes{NewCveContentType(myFamily)}
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(order...)...)
 | 
			
		||||
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v[ctype]; found && 0 < len(cont.Cpes) {
 | 
			
		||||
			values = append(values, CveContentCpes{
 | 
			
		||||
				Type:  ctype,
 | 
			
		||||
				Value: cont.Cpes,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CveContentRefs has CveContentType and Cpes
 | 
			
		||||
type CveContentRefs struct {
 | 
			
		||||
	Type  CveContentType
 | 
			
		||||
	Value []Reference
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// References returns References
 | 
			
		||||
func (v CveContents) References(myFamily string) (values []CveContentRefs) {
 | 
			
		||||
	order := CveContentTypes{NewCveContentType(myFamily)}
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(order...)...)
 | 
			
		||||
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v[ctype]; found && 0 < len(cont.References) {
 | 
			
		||||
			values = append(values, CveContentRefs{
 | 
			
		||||
				Type:  ctype,
 | 
			
		||||
				Value: cont.References,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CweIDs returns related CweIDs of the vulnerability
 | 
			
		||||
func (v CveContents) CweIDs(myFamily string) (values []CveContentStr) {
 | 
			
		||||
	order := CveContentTypes{NewCveContentType(myFamily)}
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(order...)...)
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v[ctype]; found && 0 < len(cont.CweIDs) {
 | 
			
		||||
			for _, cweID := range cont.CweIDs {
 | 
			
		||||
				for _, val := range values {
 | 
			
		||||
					if val.Value == cweID {
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				values = append(values, CveContentStr{
 | 
			
		||||
					Type:  ctype,
 | 
			
		||||
					Value: cweID,
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UniqCweIDs returns Uniq CweIDs
 | 
			
		||||
func (v CveContents) UniqCweIDs(myFamily string) (values []CveContentStr) {
 | 
			
		||||
	uniq := map[string]CveContentStr{}
 | 
			
		||||
	for _, cwes := range v.CweIDs(myFamily) {
 | 
			
		||||
		uniq[cwes.Value] = cwes
 | 
			
		||||
	}
 | 
			
		||||
	for _, cwe := range uniq {
 | 
			
		||||
		values = append(values, cwe)
 | 
			
		||||
	}
 | 
			
		||||
	return values
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CveContent has abstraction of various vulnerability information
 | 
			
		||||
type CveContent struct {
 | 
			
		||||
	Type          CveContentType    `json:"type"`
 | 
			
		||||
	CveID         string            `json:"cveID"`
 | 
			
		||||
	Title         string            `json:"title"`
 | 
			
		||||
	Summary       string            `json:"summary"`
 | 
			
		||||
	Cvss2Score    float64           `json:"cvss2Score"`
 | 
			
		||||
	Cvss2Vector   string            `json:"cvss2Vector"`
 | 
			
		||||
	Cvss2Severity string            `json:"cvss2Severity"`
 | 
			
		||||
	Cvss3Score    float64           `json:"cvss3Score"`
 | 
			
		||||
	Cvss3Vector   string            `json:"cvss3Vector"`
 | 
			
		||||
	Cvss3Severity string            `json:"cvss3Severity"`
 | 
			
		||||
	SourceLink    string            `json:"sourceLink"`
 | 
			
		||||
	Cpes          []Cpe             `json:"cpes,omitempty"`
 | 
			
		||||
	References    References        `json:"references,omitempty"`
 | 
			
		||||
	CweIDs        []string          `json:"cweIDs,omitempty"`
 | 
			
		||||
	Published     time.Time         `json:"published"`
 | 
			
		||||
	LastModified  time.Time         `json:"lastModified"`
 | 
			
		||||
	Mitigation    string            `json:"mitigation"` // RedHat API
 | 
			
		||||
	Optional      map[string]string `json:"optional,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Empty checks the content is empty
 | 
			
		||||
func (c CveContent) Empty() bool {
 | 
			
		||||
	return c.Summary == ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CveContentType is a source of CVE information
 | 
			
		||||
type CveContentType string
 | 
			
		||||
 | 
			
		||||
// NewCveContentType create CveContentType
 | 
			
		||||
func NewCveContentType(name string) CveContentType {
 | 
			
		||||
	switch name {
 | 
			
		||||
	case "nvdxml":
 | 
			
		||||
		return NvdXML
 | 
			
		||||
	case "nvd":
 | 
			
		||||
		return Nvd
 | 
			
		||||
	case "jvn":
 | 
			
		||||
		return Jvn
 | 
			
		||||
	case "redhat", "centos":
 | 
			
		||||
		return RedHat
 | 
			
		||||
	case "oracle":
 | 
			
		||||
		return Oracle
 | 
			
		||||
	case "ubuntu":
 | 
			
		||||
		return Ubuntu
 | 
			
		||||
	case "debian", vulnerability.DebianOVAL:
 | 
			
		||||
		return Debian
 | 
			
		||||
	case "redhat_api":
 | 
			
		||||
		return RedHatAPI
 | 
			
		||||
	case "debian_security_tracker":
 | 
			
		||||
		return DebianSecurityTracker
 | 
			
		||||
	case "microsoft":
 | 
			
		||||
		return Microsoft
 | 
			
		||||
	case "wordpress":
 | 
			
		||||
		return WPVulnDB
 | 
			
		||||
	case "amazon":
 | 
			
		||||
		return Amazon
 | 
			
		||||
	case vulnerability.NodejsSecurityWg:
 | 
			
		||||
		return NodeSec
 | 
			
		||||
	case vulnerability.PythonSafetyDB:
 | 
			
		||||
		return PythonSec
 | 
			
		||||
	case vulnerability.RustSec:
 | 
			
		||||
		return RustSec
 | 
			
		||||
	case vulnerability.PhpSecurityAdvisories:
 | 
			
		||||
		return PhpSec
 | 
			
		||||
	case vulnerability.RubySec:
 | 
			
		||||
		return RubySec
 | 
			
		||||
	default:
 | 
			
		||||
		return Unknown
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// NvdXML is NvdXML
 | 
			
		||||
	NvdXML CveContentType = "nvdxml"
 | 
			
		||||
 | 
			
		||||
	// Nvd is Nvd
 | 
			
		||||
	Nvd CveContentType = "nvd"
 | 
			
		||||
 | 
			
		||||
	// Jvn is Jvn
 | 
			
		||||
	Jvn CveContentType = "jvn"
 | 
			
		||||
 | 
			
		||||
	// RedHat is RedHat
 | 
			
		||||
	RedHat CveContentType = "redhat"
 | 
			
		||||
 | 
			
		||||
	// RedHatAPI is RedHat
 | 
			
		||||
	RedHatAPI CveContentType = "redhat_api"
 | 
			
		||||
 | 
			
		||||
	// DebianSecurityTracker is Debian Secury tracker
 | 
			
		||||
	DebianSecurityTracker CveContentType = "debian_security_tracker"
 | 
			
		||||
 | 
			
		||||
	// Debian is Debian
 | 
			
		||||
	Debian CveContentType = "debian"
 | 
			
		||||
 | 
			
		||||
	// Ubuntu is Ubuntu
 | 
			
		||||
	Ubuntu CveContentType = "ubuntu"
 | 
			
		||||
 | 
			
		||||
	// Oracle is Oracle Linux
 | 
			
		||||
	Oracle CveContentType = "oracle"
 | 
			
		||||
 | 
			
		||||
	// Amazon is Amazon Linux
 | 
			
		||||
	Amazon CveContentType = "amazon"
 | 
			
		||||
 | 
			
		||||
	// SUSE is SUSE Linux
 | 
			
		||||
	SUSE CveContentType = "suse"
 | 
			
		||||
 | 
			
		||||
	// Microsoft is Microsoft
 | 
			
		||||
	Microsoft CveContentType = "microsoft"
 | 
			
		||||
 | 
			
		||||
	// WPVulnDB is WordPress
 | 
			
		||||
	WPVulnDB CveContentType = "wpvulndb"
 | 
			
		||||
 | 
			
		||||
	// NodeSec : for JS
 | 
			
		||||
	NodeSec CveContentType = "node"
 | 
			
		||||
 | 
			
		||||
	// PythonSec : for PHP
 | 
			
		||||
	PythonSec CveContentType = "python"
 | 
			
		||||
 | 
			
		||||
	// PhpSec : for PHP
 | 
			
		||||
	PhpSec CveContentType = "php"
 | 
			
		||||
 | 
			
		||||
	// RubySec : for Ruby
 | 
			
		||||
	RubySec CveContentType = "ruby"
 | 
			
		||||
 | 
			
		||||
	// RustSec : for Rust
 | 
			
		||||
	RustSec CveContentType = "rust"
 | 
			
		||||
 | 
			
		||||
	// Unknown is Unknown
 | 
			
		||||
	Unknown CveContentType = "unknown"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CveContentTypes has slide of CveContentType
 | 
			
		||||
type CveContentTypes []CveContentType
 | 
			
		||||
 | 
			
		||||
// AllCveContetTypes has all of CveContentTypes
 | 
			
		||||
var AllCveContetTypes = CveContentTypes{
 | 
			
		||||
	Nvd,
 | 
			
		||||
	NvdXML,
 | 
			
		||||
	Jvn,
 | 
			
		||||
	RedHat,
 | 
			
		||||
	RedHatAPI,
 | 
			
		||||
	Debian,
 | 
			
		||||
	Ubuntu,
 | 
			
		||||
	Amazon,
 | 
			
		||||
	SUSE,
 | 
			
		||||
	DebianSecurityTracker,
 | 
			
		||||
	WPVulnDB,
 | 
			
		||||
	NodeSec,
 | 
			
		||||
	PythonSec,
 | 
			
		||||
	PhpSec,
 | 
			
		||||
	RubySec,
 | 
			
		||||
	RustSec,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Except returns CveContentTypes except for given args
 | 
			
		||||
func (c CveContentTypes) Except(excepts ...CveContentType) (excepted CveContentTypes) {
 | 
			
		||||
	for _, ctype := range c {
 | 
			
		||||
		found := false
 | 
			
		||||
		for _, except := range excepts {
 | 
			
		||||
			if ctype == except {
 | 
			
		||||
				found = true
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if !found {
 | 
			
		||||
			excepted = append(excepted, ctype)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Cpe is Common Platform Enumeration
 | 
			
		||||
type Cpe struct {
 | 
			
		||||
	URI             string `json:"uri"`
 | 
			
		||||
	FormattedString string `json:"formattedString"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// References is a slice of Reference
 | 
			
		||||
type References []Reference
 | 
			
		||||
 | 
			
		||||
// Reference has a related link of the CVE
 | 
			
		||||
type Reference struct {
 | 
			
		||||
	Source string `json:"source"`
 | 
			
		||||
	Link   string `json:"link"`
 | 
			
		||||
	RefID  string `json:"refID"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										206
									
								
								models/cvecontents_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,206 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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",
 | 
			
		||||
					},
 | 
			
		||||
					NvdXML: {
 | 
			
		||||
						Type:       NvdXML,
 | 
			
		||||
						SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: []CveContentStr{
 | 
			
		||||
				{
 | 
			
		||||
					Type:  Jvn,
 | 
			
		||||
					Value: "https://jvn.jp/vu/JVNVU93610402/",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Type:  NvdXML,
 | 
			
		||||
					Value: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Type:  RedHat,
 | 
			
		||||
					Value: "https://access.redhat.com/security/cve/CVE-2017-6074",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		// lang: en
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				lang:  "en",
 | 
			
		||||
				cveID: "CVE-2017-6074",
 | 
			
		||||
				cont: CveContents{
 | 
			
		||||
					Jvn: {
 | 
			
		||||
						Type:       Jvn,
 | 
			
		||||
						SourceLink: "https://jvn.jp/vu/JVNVU93610402/",
 | 
			
		||||
					},
 | 
			
		||||
					RedHat: {
 | 
			
		||||
						Type:       RedHat,
 | 
			
		||||
						SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
 | 
			
		||||
					},
 | 
			
		||||
					NvdXML: {
 | 
			
		||||
						Type:       NvdXML,
 | 
			
		||||
						SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: []CveContentStr{
 | 
			
		||||
				{
 | 
			
		||||
					Type:  NvdXML,
 | 
			
		||||
					Value: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Type:  RedHat,
 | 
			
		||||
					Value: "https://access.redhat.com/security/cve/CVE-2017-6074",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		// lang: empty
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				lang:  "en",
 | 
			
		||||
				cveID: "CVE-2017-6074",
 | 
			
		||||
				cont:  CveContents{},
 | 
			
		||||
			},
 | 
			
		||||
			out: []CveContentStr{
 | 
			
		||||
				{
 | 
			
		||||
					Type:  Nvd,
 | 
			
		||||
					Value: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
		actual := tt.in.cont.SourceLinks(tt.in.lang, "redhat", tt.in.cveID)
 | 
			
		||||
		if !reflect.DeepEqual(tt.out, actual) {
 | 
			
		||||
			t.Errorf("\n[%d] expected: %v\n  actual: %v\n", i, tt.out, actual)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestVendorLink(t *testing.T) {
 | 
			
		||||
	type in struct {
 | 
			
		||||
		family string
 | 
			
		||||
		vinfo  VulnInfo
 | 
			
		||||
	}
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in  in
 | 
			
		||||
		out map[string]string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family: "redhat",
 | 
			
		||||
				vinfo: VulnInfo{
 | 
			
		||||
					CveID: "CVE-2017-6074",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						Jvn: {
 | 
			
		||||
							Type:       Jvn,
 | 
			
		||||
							SourceLink: "https://jvn.jp/vu/JVNVU93610402/",
 | 
			
		||||
						},
 | 
			
		||||
						RedHat: {
 | 
			
		||||
							Type:       RedHat,
 | 
			
		||||
							SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
 | 
			
		||||
						},
 | 
			
		||||
						NvdXML: {
 | 
			
		||||
							Type:       NvdXML,
 | 
			
		||||
							SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: map[string]string{
 | 
			
		||||
				"RHEL-CVE": "https://access.redhat.com/security/cve/CVE-2017-6074",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family: "ubuntu",
 | 
			
		||||
				vinfo: VulnInfo{
 | 
			
		||||
					CveID: "CVE-2017-6074",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						RedHat: {
 | 
			
		||||
							Type:       Ubuntu,
 | 
			
		||||
							SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: map[string]string{
 | 
			
		||||
				"Ubuntu-CVE": "http://people.ubuntu.com/~ubuntu-security/cve/CVE-2017-6074",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		actual := tt.in.vinfo.VendorLinks(tt.in.family)
 | 
			
		||||
		for k := range tt.out {
 | 
			
		||||
			if tt.out[k] != actual[k] {
 | 
			
		||||
				t.Errorf("\nexpected: %s\n  actual: %s\n", tt.out[k], actual[k])
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										141
									
								
								models/library.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,141 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/knqyf263/trivy/pkg/scanner/library"
 | 
			
		||||
	"github.com/knqyf263/trivy/pkg/vulnsrc/vulnerability"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/knqyf263/go-dep-parser/pkg/types"
 | 
			
		||||
	"github.com/knqyf263/go-version"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// LibraryScanner has libraries information
 | 
			
		||||
type LibraryScanner struct {
 | 
			
		||||
	Path string
 | 
			
		||||
	Libs []types.Library
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Scan : scan target library
 | 
			
		||||
func (s LibraryScanner) Scan() ([]VulnInfo, error) {
 | 
			
		||||
	scanner := library.NewScanner(filepath.Base(string(s.Path)))
 | 
			
		||||
	if scanner == nil {
 | 
			
		||||
		return nil, xerrors.New("unknown file type")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	util.Log.Info("Updating library db...")
 | 
			
		||||
	err := scanner.UpdateDB()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("failed to update %s advisories: %w", scanner.Type(), err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var vulnerabilities []VulnInfo
 | 
			
		||||
	for _, pkg := range s.Libs {
 | 
			
		||||
		v, err := version.NewVersion(pkg.Version)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Debugf("new version cant detected %s@%s", pkg.Name, pkg.Version)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		tvulns, err := scanner.Detect(pkg.Name, v)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("failed to detect %s vulnerabilities: %w", scanner.Type(), err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		vulns := s.convertFanalToVuln(tvulns)
 | 
			
		||||
		vulnerabilities = append(vulnerabilities, vulns...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return vulnerabilities, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s LibraryScanner) convertFanalToVuln(tvulns []vulnerability.DetectedVulnerability) (vulns []VulnInfo) {
 | 
			
		||||
	for _, tvuln := range tvulns {
 | 
			
		||||
		vinfo, _ := s.getVulnDetail(tvuln)
 | 
			
		||||
		vulns = append(vulns, vinfo)
 | 
			
		||||
	}
 | 
			
		||||
	return vulns
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s LibraryScanner) getVulnDetail(tvuln vulnerability.DetectedVulnerability) (vinfo VulnInfo, err error) {
 | 
			
		||||
	details, err := vulnerability.Get(tvuln.VulnerabilityID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return vinfo, err
 | 
			
		||||
	} else if len(details) == 0 {
 | 
			
		||||
		return vinfo, xerrors.Errorf("Unknown vulnID : %s", tvuln.VulnerabilityID)
 | 
			
		||||
	}
 | 
			
		||||
	vinfo.CveID = tvuln.VulnerabilityID
 | 
			
		||||
	vinfo.CveContents = getCveContents(details)
 | 
			
		||||
	if tvuln.FixedVersion != "" {
 | 
			
		||||
 | 
			
		||||
		vinfo.LibraryFixedIns = []LibraryFixedIn{
 | 
			
		||||
			{
 | 
			
		||||
				Key:     s.GetLibraryKey(),
 | 
			
		||||
				Name:    tvuln.PkgName,
 | 
			
		||||
				FixedIn: tvuln.FixedVersion,
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return vinfo, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getCveContents(details map[string]vulnerability.Vulnerability) (contents map[CveContentType]CveContent) {
 | 
			
		||||
	contents = map[CveContentType]CveContent{}
 | 
			
		||||
	for source, detail := range details {
 | 
			
		||||
		refs := []Reference{}
 | 
			
		||||
		for _, refURL := range detail.References {
 | 
			
		||||
			refs = append(refs, Reference{Source: refURL, Link: refURL})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		content := CveContent{
 | 
			
		||||
			Type:          NewCveContentType(source),
 | 
			
		||||
			CveID:         detail.ID,
 | 
			
		||||
			Title:         detail.Title,
 | 
			
		||||
			Summary:       detail.Description,
 | 
			
		||||
			Cvss3Score:    detail.CvssScoreV3,
 | 
			
		||||
			Cvss3Severity: string(detail.SeverityV3),
 | 
			
		||||
			Cvss2Score:    detail.CvssScore,
 | 
			
		||||
			Cvss2Severity: string(detail.Severity),
 | 
			
		||||
			References:    refs,
 | 
			
		||||
 | 
			
		||||
			//SourceLink    string            `json:"sourceLink"`
 | 
			
		||||
			//Cvss2Vector   string            `json:"cvss2Vector"`
 | 
			
		||||
			//Cvss3Vector   string            `json:"cvss3Vector"`
 | 
			
		||||
			//Cvss3Severity string            `json:"cvss3Severity"`
 | 
			
		||||
			//Cpes          []Cpe             `json:"cpes,omitempty"`
 | 
			
		||||
			//CweIDs        []string          `json:"cweIDs,omitempty"`
 | 
			
		||||
			//Published     time.Time         `json:"published"`
 | 
			
		||||
			//LastModified  time.Time         `json:"lastModified"`
 | 
			
		||||
			//Mitigation    string            `json:"mitigation"` // RedHat API
 | 
			
		||||
			//Optional      map[string]string `json:"optional,omitempty"`
 | 
			
		||||
		}
 | 
			
		||||
		contents[NewCveContentType(source)] = content
 | 
			
		||||
	}
 | 
			
		||||
	return contents
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LibraryMap is filename and library type
 | 
			
		||||
var LibraryMap = map[string]string{
 | 
			
		||||
	"package-lock.json": "node",
 | 
			
		||||
	"yarn.lock":         "node",
 | 
			
		||||
	"Gemfile.lock":      "ruby",
 | 
			
		||||
	"Cargo.lock":        "rust",
 | 
			
		||||
	"composer.json":     "php",
 | 
			
		||||
	"Pipfile.lock":      "python",
 | 
			
		||||
	"poetry.lock":       "python",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLibraryKey returns target library key
 | 
			
		||||
func (s LibraryScanner) GetLibraryKey() string {
 | 
			
		||||
	fileName := filepath.Base(s.Path)
 | 
			
		||||
	return LibraryMap[fileName]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LibraryFixedIn has library fixed information
 | 
			
		||||
type LibraryFixedIn struct {
 | 
			
		||||
	Key     string `json:"key,omitempty"`
 | 
			
		||||
	Name    string `json:"name,omitempty"`
 | 
			
		||||
	FixedIn string `json:"fixedIn,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										52
									
								
								models/library_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,52 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	godeptypes "github.com/knqyf263/go-dep-parser/pkg/types"
 | 
			
		||||
	"github.com/knqyf263/trivy/pkg/db"
 | 
			
		||||
	"github.com/knqyf263/trivy/pkg/log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestScan(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		path string
 | 
			
		||||
		pkgs []godeptypes.Library
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			path: "app/package-lock.json",
 | 
			
		||||
			pkgs: []godeptypes.Library{
 | 
			
		||||
				{
 | 
			
		||||
					Name:    "jquery",
 | 
			
		||||
					Version: "2.2.4",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Name:    "@babel/traverse",
 | 
			
		||||
					Version: "7.4.4",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := log.InitLogger(false); err != nil {
 | 
			
		||||
		t.Errorf("trivy logger failed")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := db.Init(); err != nil {
 | 
			
		||||
		t.Errorf("trivy db.Init failed")
 | 
			
		||||
	}
 | 
			
		||||
	for _, v := range tests {
 | 
			
		||||
		lib := LibraryScanner{
 | 
			
		||||
			Path: v.path,
 | 
			
		||||
			Libs: v.pkgs,
 | 
			
		||||
		}
 | 
			
		||||
		actual, err := lib.Scan()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("error occurred")
 | 
			
		||||
		}
 | 
			
		||||
		if len(actual) == 0 {
 | 
			
		||||
			t.Errorf("no vuln found : actual: %v\n", actual)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	db.Close()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										322
									
								
								models/models.go
									
									
									
									
									
								
							
							
						
						@@ -1,5 +1,5 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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
 | 
			
		||||
@@ -17,321 +17,5 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/jinzhu/gorm"
 | 
			
		||||
	cve "github.com/kotakanbe/go-cve-dictionary/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ScanHistory is the history of Scanning.
 | 
			
		||||
type ScanHistory struct {
 | 
			
		||||
	gorm.Model
 | 
			
		||||
	ScanResults ScanResults
 | 
			
		||||
	ScannedAt   time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ScanResults is slice of ScanResult.
 | 
			
		||||
type ScanResults []ScanResult
 | 
			
		||||
 | 
			
		||||
// Len implement Sort Interface
 | 
			
		||||
func (s ScanResults) Len() int {
 | 
			
		||||
	return len(s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Swap implement Sort Interface
 | 
			
		||||
func (s ScanResults) Swap(i, j int) {
 | 
			
		||||
	s[i], s[j] = s[j], s[i]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Less implement Sort Interface
 | 
			
		||||
func (s ScanResults) Less(i, j int) bool {
 | 
			
		||||
	if s[i].ServerName == s[j].ServerName {
 | 
			
		||||
		return s[i].Container.ContainerID < s[i].Container.ContainerID
 | 
			
		||||
	}
 | 
			
		||||
	return s[i].ServerName < s[j].ServerName
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FilterByCvssOver is filter function.
 | 
			
		||||
func (s ScanResults) FilterByCvssOver() (filtered ScanResults) {
 | 
			
		||||
	for _, result := range s {
 | 
			
		||||
		cveInfos := []CveInfo{}
 | 
			
		||||
		for _, cveInfo := range result.KnownCves {
 | 
			
		||||
			if config.Conf.CvssScoreOver < cveInfo.CveDetail.CvssScore(config.Conf.Lang) {
 | 
			
		||||
				cveInfos = append(cveInfos, cveInfo)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		result.KnownCves = cveInfos
 | 
			
		||||
		filtered = append(filtered, result)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ScanResult has the result of scanned CVE information.
 | 
			
		||||
type ScanResult struct {
 | 
			
		||||
	gorm.Model    `json:"-"`
 | 
			
		||||
	ScanHistoryID uint `json:"-"`
 | 
			
		||||
 | 
			
		||||
	ServerName string // TOML Section key
 | 
			
		||||
	//  Hostname    string
 | 
			
		||||
	Family  string
 | 
			
		||||
	Release string
 | 
			
		||||
 | 
			
		||||
	Container Container
 | 
			
		||||
 | 
			
		||||
	Platform Platform
 | 
			
		||||
 | 
			
		||||
	//  Fqdn        string
 | 
			
		||||
	//  NWLinks     []NWLink
 | 
			
		||||
	KnownCves   []CveInfo
 | 
			
		||||
	UnknownCves []CveInfo
 | 
			
		||||
 | 
			
		||||
	Optional [][]interface{} `gorm:"-"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ServerInfo returns server name one line
 | 
			
		||||
func (r ScanResult) ServerInfo() string {
 | 
			
		||||
	hostinfo := ""
 | 
			
		||||
	if r.Container.ContainerID == "" {
 | 
			
		||||
		hostinfo = fmt.Sprintf(
 | 
			
		||||
			"%s (%s%s)",
 | 
			
		||||
			r.ServerName,
 | 
			
		||||
			r.Family,
 | 
			
		||||
			r.Release,
 | 
			
		||||
		)
 | 
			
		||||
	} else {
 | 
			
		||||
		hostinfo = fmt.Sprintf(
 | 
			
		||||
			"%s / %s (%s%s) on %s",
 | 
			
		||||
			r.Container.Name,
 | 
			
		||||
			r.Container.ContainerID,
 | 
			
		||||
			r.Family,
 | 
			
		||||
			r.Release,
 | 
			
		||||
			r.ServerName,
 | 
			
		||||
		)
 | 
			
		||||
	}
 | 
			
		||||
	return hostinfo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ServerInfoTui returns server infromation for TUI sidebar
 | 
			
		||||
func (r ScanResult) ServerInfoTui() string {
 | 
			
		||||
	hostinfo := ""
 | 
			
		||||
	if r.Container.ContainerID == "" {
 | 
			
		||||
		hostinfo = fmt.Sprintf(
 | 
			
		||||
			"%s (%s%s)",
 | 
			
		||||
			r.ServerName,
 | 
			
		||||
			r.Family,
 | 
			
		||||
			r.Release,
 | 
			
		||||
		)
 | 
			
		||||
	} else {
 | 
			
		||||
		hostinfo = fmt.Sprintf(
 | 
			
		||||
			"|-- %s (%s%s)",
 | 
			
		||||
			r.Container.Name,
 | 
			
		||||
			r.Family,
 | 
			
		||||
			r.Release,
 | 
			
		||||
			//  r.Container.ContainerID,
 | 
			
		||||
		)
 | 
			
		||||
	}
 | 
			
		||||
	return hostinfo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CveSummary summarize the number of CVEs group by CVSSv2 Severity
 | 
			
		||||
func (r ScanResult) CveSummary() string {
 | 
			
		||||
	var high, middle, low, unknown int
 | 
			
		||||
	cves := append(r.KnownCves, r.UnknownCves...)
 | 
			
		||||
	for _, cveInfo := range cves {
 | 
			
		||||
		score := cveInfo.CveDetail.CvssScore(config.Conf.Lang)
 | 
			
		||||
		switch {
 | 
			
		||||
		case 7.0 < score:
 | 
			
		||||
			high++
 | 
			
		||||
		case 4.0 < score:
 | 
			
		||||
			middle++
 | 
			
		||||
		case 0 < score:
 | 
			
		||||
			low++
 | 
			
		||||
		default:
 | 
			
		||||
			unknown++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if config.Conf.IgnoreUnscoredCves {
 | 
			
		||||
		return fmt.Sprintf("Total: %d (High:%d Middle:%d Low:%d)",
 | 
			
		||||
			high+middle+low, high, middle, low)
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("Total: %d (High:%d Middle:%d Low:%d ?:%d)",
 | 
			
		||||
		high+middle+low+unknown, high, middle, low, unknown)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NWLink has network link information.
 | 
			
		||||
type NWLink struct {
 | 
			
		||||
	gorm.Model   `json:"-"`
 | 
			
		||||
	ScanResultID uint `json:"-"`
 | 
			
		||||
 | 
			
		||||
	IPAddress string
 | 
			
		||||
	Netmask   string
 | 
			
		||||
	DevName   string
 | 
			
		||||
	LinkState string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CveInfos is for sorting
 | 
			
		||||
type CveInfos []CveInfo
 | 
			
		||||
 | 
			
		||||
func (c CveInfos) Len() int {
 | 
			
		||||
	return len(c)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c CveInfos) Swap(i, j int) {
 | 
			
		||||
	c[i], c[j] = c[j], c[i]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c CveInfos) Less(i, j int) bool {
 | 
			
		||||
	lang := config.Conf.Lang
 | 
			
		||||
	if c[i].CveDetail.CvssScore(lang) == c[j].CveDetail.CvssScore(lang) {
 | 
			
		||||
		return c[i].CveDetail.CveID < c[j].CveDetail.CveID
 | 
			
		||||
	}
 | 
			
		||||
	return c[i].CveDetail.CvssScore(lang) > c[j].CveDetail.CvssScore(lang)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CveInfo has Cve Information.
 | 
			
		||||
type CveInfo struct {
 | 
			
		||||
	gorm.Model   `json:"-"`
 | 
			
		||||
	ScanResultID uint `json:"-"`
 | 
			
		||||
 | 
			
		||||
	CveDetail        cve.CveDetail
 | 
			
		||||
	Packages         []PackageInfo
 | 
			
		||||
	DistroAdvisories []DistroAdvisory
 | 
			
		||||
	CpeNames         []CpeName
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CpeName has CPE name
 | 
			
		||||
type CpeName struct {
 | 
			
		||||
	gorm.Model `json:"-"`
 | 
			
		||||
	CveInfoID  uint `json:"-"`
 | 
			
		||||
 | 
			
		||||
	Name string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PackageInfoList is slice of PackageInfo
 | 
			
		||||
type PackageInfoList []PackageInfo
 | 
			
		||||
 | 
			
		||||
// Exists returns true if exists the name
 | 
			
		||||
func (ps PackageInfoList) Exists(name string) bool {
 | 
			
		||||
	for _, p := range ps {
 | 
			
		||||
		if p.Name == name {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UniqByName be uniq by name.
 | 
			
		||||
func (ps PackageInfoList) UniqByName() (distincted PackageInfoList) {
 | 
			
		||||
	set := make(map[string]PackageInfo)
 | 
			
		||||
	for _, p := range ps {
 | 
			
		||||
		set[p.Name] = p
 | 
			
		||||
	}
 | 
			
		||||
	//sort by key
 | 
			
		||||
	keys := []string{}
 | 
			
		||||
	for key := range set {
 | 
			
		||||
		keys = append(keys, key)
 | 
			
		||||
	}
 | 
			
		||||
	sort.Strings(keys)
 | 
			
		||||
	for _, key := range keys {
 | 
			
		||||
		distincted = append(distincted, set[key])
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindByName search PackageInfo by name
 | 
			
		||||
func (ps PackageInfoList) FindByName(name string) (result PackageInfo, found bool) {
 | 
			
		||||
	for _, p := range ps {
 | 
			
		||||
		if p.Name == name {
 | 
			
		||||
			return p, true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return PackageInfo{}, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Find search PackageInfo by name-version-release
 | 
			
		||||
//  func (ps PackageInfoList) find(nameVersionRelease string) (PackageInfo, bool) {
 | 
			
		||||
//      for _, p := range ps {
 | 
			
		||||
//          joined := p.Name
 | 
			
		||||
//          if 0 < len(p.Version) {
 | 
			
		||||
//              joined = fmt.Sprintf("%s-%s", joined, p.Version)
 | 
			
		||||
//          }
 | 
			
		||||
//          if 0 < len(p.Release) {
 | 
			
		||||
//              joined = fmt.Sprintf("%s-%s", joined, p.Release)
 | 
			
		||||
//          }
 | 
			
		||||
//          if joined == nameVersionRelease {
 | 
			
		||||
//              return p, true
 | 
			
		||||
//          }
 | 
			
		||||
//      }
 | 
			
		||||
//      return PackageInfo{}, false
 | 
			
		||||
//  }
 | 
			
		||||
 | 
			
		||||
// PackageInfo has installed packages.
 | 
			
		||||
type PackageInfo struct {
 | 
			
		||||
	gorm.Model `json:"-"`
 | 
			
		||||
	CveInfoID  uint `json:"-"`
 | 
			
		||||
 | 
			
		||||
	Name    string
 | 
			
		||||
	Version string
 | 
			
		||||
	Release string
 | 
			
		||||
 | 
			
		||||
	NewVersion string
 | 
			
		||||
	NewRelease string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ToStringCurrentVersion returns package name-version-release
 | 
			
		||||
func (p PackageInfo) ToStringCurrentVersion() string {
 | 
			
		||||
	str := p.Name
 | 
			
		||||
	if 0 < len(p.Version) {
 | 
			
		||||
		str = fmt.Sprintf("%s-%s", str, p.Version)
 | 
			
		||||
	}
 | 
			
		||||
	if 0 < len(p.Release) {
 | 
			
		||||
		str = fmt.Sprintf("%s-%s", str, p.Release)
 | 
			
		||||
	}
 | 
			
		||||
	return str
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ToStringNewVersion returns package name-version-release
 | 
			
		||||
func (p PackageInfo) ToStringNewVersion() string {
 | 
			
		||||
	str := p.Name
 | 
			
		||||
	if 0 < len(p.NewVersion) {
 | 
			
		||||
		str = fmt.Sprintf("%s-%s", str, p.NewVersion)
 | 
			
		||||
	}
 | 
			
		||||
	if 0 < len(p.NewRelease) {
 | 
			
		||||
		str = fmt.Sprintf("%s-%s", str, p.NewRelease)
 | 
			
		||||
	}
 | 
			
		||||
	return str
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DistroAdvisory has Amazon Linux, RHEL, FreeBSD Security Advisory information.
 | 
			
		||||
type DistroAdvisory struct {
 | 
			
		||||
	gorm.Model `json:"-"`
 | 
			
		||||
	CveInfoID  uint `json:"-"`
 | 
			
		||||
 | 
			
		||||
	AdvisoryID string
 | 
			
		||||
	Severity   string
 | 
			
		||||
	Issued     time.Time
 | 
			
		||||
	Updated    time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Container has Container information
 | 
			
		||||
type Container struct {
 | 
			
		||||
	gorm.Model   `json:"-"`
 | 
			
		||||
	ScanResultID uint `json:"-"`
 | 
			
		||||
 | 
			
		||||
	ContainerID string
 | 
			
		||||
	Name        string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Platform has platform information
 | 
			
		||||
type Platform struct {
 | 
			
		||||
	gorm.Model   `json:"-"`
 | 
			
		||||
	ScanResultID uint `json:"-"`
 | 
			
		||||
 | 
			
		||||
	Name       string // aws or azure or gcp or other...
 | 
			
		||||
	InstanceID string
 | 
			
		||||
}
 | 
			
		||||
// JSONVersion is JSON Version
 | 
			
		||||
const JSONVersion = 4
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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
 | 
			
		||||
@@ -16,39 +16,3 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import "testing"
 | 
			
		||||
 | 
			
		||||
func TestPackageInfosUniqByName(t *testing.T) {
 | 
			
		||||
	var test = struct {
 | 
			
		||||
		in  PackageInfoList
 | 
			
		||||
		out PackageInfoList
 | 
			
		||||
	}{
 | 
			
		||||
		PackageInfoList{
 | 
			
		||||
			{
 | 
			
		||||
				Name: "hoge",
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Name: "fuga",
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Name: "hoge",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		PackageInfoList{
 | 
			
		||||
			{
 | 
			
		||||
				Name: "hoge",
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Name: "fuga",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	actual := test.in.UniqByName()
 | 
			
		||||
	for i, ePack := range test.out {
 | 
			
		||||
		if actual[i].Name == ePack.Name {
 | 
			
		||||
			t.Errorf("expected %#v, actual %#v", ePack.Name, actual[i].Name)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										239
									
								
								models/packages.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,239 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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 name, pack := range ps {
 | 
			
		||||
		pack.NewVersion = pack.Version
 | 
			
		||||
		pack.NewRelease = pack.Release
 | 
			
		||||
		ps[name] = pack
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindOne search a element
 | 
			
		||||
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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindByFQPN search a package by Fully-Qualified-Package-Name
 | 
			
		||||
func (ps Packages) FindByFQPN(nameVerRelArc string) (*Package, error) {
 | 
			
		||||
	for _, p := range ps {
 | 
			
		||||
		if nameVerRelArc == p.FQPN() {
 | 
			
		||||
			return &p, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil, xerrors.Errorf("Failed to find the package: %s", nameVerRelArc)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Package has installed binary packages.
 | 
			
		||||
type Package struct {
 | 
			
		||||
	Name             string               `json:"name"`
 | 
			
		||||
	Version          string               `json:"version"`
 | 
			
		||||
	Release          string               `json:"release"`
 | 
			
		||||
	NewVersion       string               `json:"newVersion"`
 | 
			
		||||
	NewRelease       string               `json:"newRelease"`
 | 
			
		||||
	Arch             string               `json:"arch"`
 | 
			
		||||
	Repository       string               `json:"repository"`
 | 
			
		||||
	Changelog        Changelog            `json:"changelog"`
 | 
			
		||||
	AffectedProcs    []AffectedProcess    `json:",omitempty"`
 | 
			
		||||
	NeedRestartProcs []NeedRestartProcess `json:",omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FQPN returns Fully-Qualified-Package-Name
 | 
			
		||||
// name-version-release.arch
 | 
			
		||||
func (p Package) FQPN() string {
 | 
			
		||||
	fqpn := p.Name
 | 
			
		||||
	if p.Version != "" {
 | 
			
		||||
		fqpn += fmt.Sprintf("-%s", p.Version)
 | 
			
		||||
	}
 | 
			
		||||
	if p.Release != "" {
 | 
			
		||||
		fqpn += fmt.Sprintf("-%s", p.Release)
 | 
			
		||||
	}
 | 
			
		||||
	if p.Arch != "" {
 | 
			
		||||
		fqpn += fmt.Sprintf(".%s", p.Arch)
 | 
			
		||||
	}
 | 
			
		||||
	return fqpn
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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, status string) string {
 | 
			
		||||
	to := p.FormatNewVer()
 | 
			
		||||
	if notFixedYet {
 | 
			
		||||
		if status != "" {
 | 
			
		||||
			to = status
 | 
			
		||||
		} else {
 | 
			
		||||
			to = "Not Fixed Yet"
 | 
			
		||||
		}
 | 
			
		||||
	} else if p.NewVersion == "" {
 | 
			
		||||
		to = "Unknown"
 | 
			
		||||
	}
 | 
			
		||||
	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 details, 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          `json:"contents"`
 | 
			
		||||
	Method   DetectionMethod `json:"method"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AffectedProcess keep a processes information affected by software update
 | 
			
		||||
type AffectedProcess struct {
 | 
			
		||||
	PID  string `json:"pid"`
 | 
			
		||||
	Name string `json:"name"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NeedRestartProcess keep a processes information affected by software update
 | 
			
		||||
type NeedRestartProcess struct {
 | 
			
		||||
	PID         string `json:"pid"`
 | 
			
		||||
	Path        string `json:"path"`
 | 
			
		||||
	ServiceName string `json:"serviceName"`
 | 
			
		||||
	InitSystem  string `json:"initSystem"`
 | 
			
		||||
	HasInit     bool   `json:"-"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SrcPackage has installed source package information.
 | 
			
		||||
// Debian based Linux has both of package and source information in dpkg.
 | 
			
		||||
// OVAL database often includes a source version (Not a binary version),
 | 
			
		||||
// so it is also needed to capture source version for OVAL version comparison.
 | 
			
		||||
// https://github.com/future-architect/vuls/issues/504
 | 
			
		||||
type SrcPackage struct {
 | 
			
		||||
	Name        string   `json:"name"`
 | 
			
		||||
	Version     string   `json:"version"`
 | 
			
		||||
	BinaryNames []string `json:"binaryNames"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddBinaryName add the name if not exists
 | 
			
		||||
func (s *SrcPackage) AddBinaryName(name string) {
 | 
			
		||||
	found := false
 | 
			
		||||
	for _, n := range s.BinaryNames {
 | 
			
		||||
		if n == name {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if !found {
 | 
			
		||||
		s.BinaryNames = append(s.BinaryNames, name)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SrcPackages is Map of SrcPackage
 | 
			
		||||
// { "package-name": SrcPackage }
 | 
			
		||||
type SrcPackages map[string]SrcPackage
 | 
			
		||||
 | 
			
		||||
// FindByBinName finds by bin-package-name
 | 
			
		||||
func (s SrcPackages) FindByBinName(name string) (*SrcPackage, bool) {
 | 
			
		||||
	for _, p := range s {
 | 
			
		||||
		for _, binName := range p.BinaryNames {
 | 
			
		||||
			if binName == name {
 | 
			
		||||
				return &p, true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil, false
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										193
									
								
								models/packages_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,193 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestAddBinaryName(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in       SrcPackage
 | 
			
		||||
		name     string
 | 
			
		||||
		expected SrcPackage
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			SrcPackage{Name: "hoge"},
 | 
			
		||||
			"curl",
 | 
			
		||||
			SrcPackage{
 | 
			
		||||
				Name:        "hoge",
 | 
			
		||||
				BinaryNames: []string{"curl"},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			SrcPackage{
 | 
			
		||||
				Name:        "hoge",
 | 
			
		||||
				BinaryNames: []string{"curl"},
 | 
			
		||||
			},
 | 
			
		||||
			"curl",
 | 
			
		||||
			SrcPackage{
 | 
			
		||||
				Name:        "hoge",
 | 
			
		||||
				BinaryNames: []string{"curl"},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			SrcPackage{
 | 
			
		||||
				Name:        "hoge",
 | 
			
		||||
				BinaryNames: []string{"curl"},
 | 
			
		||||
			},
 | 
			
		||||
			"openssh",
 | 
			
		||||
			SrcPackage{
 | 
			
		||||
				Name:        "hoge",
 | 
			
		||||
				BinaryNames: []string{"curl", "openssh"},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		tt.in.AddBinaryName(tt.name)
 | 
			
		||||
		if !reflect.DeepEqual(tt.in, tt.expected) {
 | 
			
		||||
			t.Errorf("expected %#v, actual %#v", tt.in, tt.expected)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestFindByBinName(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in       SrcPackages
 | 
			
		||||
		name     string
 | 
			
		||||
		expected *SrcPackage
 | 
			
		||||
		ok       bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			in: map[string]SrcPackage{
 | 
			
		||||
				"packA": {
 | 
			
		||||
					Name:        "srcA",
 | 
			
		||||
					BinaryNames: []string{"binA"},
 | 
			
		||||
					Version:     "1.0.0",
 | 
			
		||||
				},
 | 
			
		||||
				"packB": {
 | 
			
		||||
					Name:        "srcB",
 | 
			
		||||
					BinaryNames: []string{"binB"},
 | 
			
		||||
					Version:     "2.0.0",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			name: "binA",
 | 
			
		||||
			expected: &SrcPackage{
 | 
			
		||||
				Name:        "srcA",
 | 
			
		||||
				BinaryNames: []string{"binA"},
 | 
			
		||||
				Version:     "1.0.0",
 | 
			
		||||
			},
 | 
			
		||||
			ok: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: map[string]SrcPackage{
 | 
			
		||||
				"packA": {
 | 
			
		||||
					Name:        "srcA",
 | 
			
		||||
					BinaryNames: []string{"binA"},
 | 
			
		||||
					Version:     "1.0.0",
 | 
			
		||||
				},
 | 
			
		||||
				"packB": {
 | 
			
		||||
					Name:        "srcB",
 | 
			
		||||
					BinaryNames: []string{"binB"},
 | 
			
		||||
					Version:     "2.0.0",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			name:     "nobin",
 | 
			
		||||
			expected: nil,
 | 
			
		||||
			ok:       false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
		act, ok := tt.in.FindByBinName(tt.name)
 | 
			
		||||
		if ok != tt.ok {
 | 
			
		||||
			t.Errorf("[%d] expected %#v, actual %#v", i, tt.in, tt.expected)
 | 
			
		||||
		}
 | 
			
		||||
		if act != nil && !reflect.DeepEqual(*tt.expected, *act) {
 | 
			
		||||
			t.Errorf("[%d] expected %#v, actual %#v", i, tt.in, tt.expected)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										480
									
								
								models/scanresults.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,480 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/alert"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/cwe"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ScanResults is a slide of ScanResult
 | 
			
		||||
type ScanResults []ScanResult
 | 
			
		||||
 | 
			
		||||
// ScanResult has the result of scanned CVE information.
 | 
			
		||||
type ScanResult struct {
 | 
			
		||||
	JSONVersion      int       `json:"jsonVersion"`
 | 
			
		||||
	Lang             string    `json:"lang"`
 | 
			
		||||
	ServerUUID       string    `json:"serverUUID"`
 | 
			
		||||
	ServerName       string    `json:"serverName"` // TOML Section key
 | 
			
		||||
	Family           string    `json:"family"`
 | 
			
		||||
	Release          string    `json:"release"`
 | 
			
		||||
	Container        Container `json:"container"`
 | 
			
		||||
	Image            Image     `json:"image"`
 | 
			
		||||
	Platform         Platform  `json:"platform"`
 | 
			
		||||
	IPv4Addrs        []string  `json:"ipv4Addrs,omitempty"` // only global unicast address (https://golang.org/pkg/net/#IP.IsGlobalUnicast)
 | 
			
		||||
	IPv6Addrs        []string  `json:"ipv6Addrs,omitempty"` // only global unicast address (https://golang.org/pkg/net/#IP.IsGlobalUnicast)
 | 
			
		||||
	ScannedAt        time.Time `json:"scannedAt"`
 | 
			
		||||
	ScanMode         string    `json:"scanMode"`
 | 
			
		||||
	ScannedVersion   string    `json:"scannedVersion"`
 | 
			
		||||
	ScannedRevision  string    `json:"scannedRevision"`
 | 
			
		||||
	ScannedBy        string    `json:"scannedBy"`
 | 
			
		||||
	ScannedVia       string    `json:"scannedVia"`
 | 
			
		||||
	ScannedIPv4Addrs []string  `json:"scannedIpv4Addrs,omitempty"`
 | 
			
		||||
	ScannedIPv6Addrs []string  `json:"scannedIpv6Addrs,omitempty"`
 | 
			
		||||
	ReportedAt       time.Time `json:"reportedAt"`
 | 
			
		||||
	ReportedVersion  string    `json:"reportedVersion"`
 | 
			
		||||
	ReportedRevision string    `json:"reportedRevision"`
 | 
			
		||||
	ReportedBy       string    `json:"reportedBy"`
 | 
			
		||||
	Errors           []string  `json:"errors"`
 | 
			
		||||
	Warnings         []string  `json:"warnings"`
 | 
			
		||||
 | 
			
		||||
	ScannedCves       VulnInfos              `json:"scannedCves"`
 | 
			
		||||
	RunningKernel     Kernel                 `json:"runningKernel"`
 | 
			
		||||
	Packages          Packages               `json:"packages"`
 | 
			
		||||
	SrcPackages       SrcPackages            `json:",omitempty"`
 | 
			
		||||
	WordPressPackages *WordPressPackages     `json:",omitempty"`
 | 
			
		||||
	LibraryScanners   []LibraryScanner       `json:"libScanners"`
 | 
			
		||||
	CweDict           CweDict                `json:"cweDict,omitempty"`
 | 
			
		||||
	Optional          map[string]interface{} `json:",omitempty"`
 | 
			
		||||
	Config            struct {
 | 
			
		||||
		Scan   config.Config `json:"scan"`
 | 
			
		||||
		Report config.Config `json:"report"`
 | 
			
		||||
	} `json:"config"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CweDict is a dictionary for CWE
 | 
			
		||||
type CweDict map[string]CweDictEntry
 | 
			
		||||
 | 
			
		||||
// Get the name, url, top10URL for the specified cweID, lang
 | 
			
		||||
func (c CweDict) Get(cweID, lang string) (name, url, top10Rank, top10URL string) {
 | 
			
		||||
	cweNum := strings.TrimPrefix(cweID, "CWE-")
 | 
			
		||||
	switch config.Conf.Lang {
 | 
			
		||||
	case "ja":
 | 
			
		||||
		if dict, ok := c[cweNum]; ok && dict.OwaspTopTen2017 != "" {
 | 
			
		||||
			top10Rank = dict.OwaspTopTen2017
 | 
			
		||||
			top10URL = cwe.OwaspTopTen2017GitHubURLJa[dict.OwaspTopTen2017]
 | 
			
		||||
		}
 | 
			
		||||
		if dict, ok := cwe.CweDictJa[cweNum]; ok {
 | 
			
		||||
			name = dict.Name
 | 
			
		||||
			url = fmt.Sprintf("http://jvndb.jvn.jp/ja/cwe/%s.html", cweID)
 | 
			
		||||
		} else {
 | 
			
		||||
			if dict, ok := cwe.CweDictEn[cweNum]; ok {
 | 
			
		||||
				name = dict.Name
 | 
			
		||||
			}
 | 
			
		||||
			url = fmt.Sprintf("https://cwe.mitre.org/data/definitions/%s.html", cweID)
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		if dict, ok := c[cweNum]; ok && dict.OwaspTopTen2017 != "" {
 | 
			
		||||
			top10Rank = dict.OwaspTopTen2017
 | 
			
		||||
			top10URL = cwe.OwaspTopTen2017GitHubURLEn[dict.OwaspTopTen2017]
 | 
			
		||||
		}
 | 
			
		||||
		url = fmt.Sprintf("https://cwe.mitre.org/data/definitions/%s.html", cweID)
 | 
			
		||||
		if dict, ok := cwe.CweDictEn[cweNum]; ok {
 | 
			
		||||
			name = dict.Name
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CweDictEntry is a entry of CWE
 | 
			
		||||
type CweDictEntry struct {
 | 
			
		||||
	En              *cwe.Cwe `json:"en,omitempty"`
 | 
			
		||||
	Ja              *cwe.Cwe `json:"ja,omitempty"`
 | 
			
		||||
	OwaspTopTen2017 string   `json:"owaspTopTen2017"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetAlertsByCveID return alerts fetched by cveID
 | 
			
		||||
func GetAlertsByCveID(cveID string, lang string) (alerts []alert.Alert) {
 | 
			
		||||
	alerts = alert.GenerateAlertDict(cveID, lang)
 | 
			
		||||
	return alerts
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Kernel has the Release, version and whether need restart
 | 
			
		||||
type Kernel struct {
 | 
			
		||||
	Release        string `json:"release"`
 | 
			
		||||
	Version        string `json:"version"`
 | 
			
		||||
	RebootRequired bool   `json:"rebootRequired"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
	})
 | 
			
		||||
	r.ScannedCves = filtered
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FilterIgnoreCves is filter function.
 | 
			
		||||
func (r ScanResult) FilterIgnoreCves() ScanResult {
 | 
			
		||||
 | 
			
		||||
	ignoreCves := []string{}
 | 
			
		||||
	if len(r.Container.Name) == 0 {
 | 
			
		||||
		ignoreCves = config.Conf.Servers[r.ServerName].IgnoreCves
 | 
			
		||||
	} else {
 | 
			
		||||
		if s, ok := config.Conf.Servers[r.ServerName]; ok {
 | 
			
		||||
			if con, ok := s.Containers[r.Container.Name]; ok {
 | 
			
		||||
				ignoreCves = con.IgnoreCves
 | 
			
		||||
			} else {
 | 
			
		||||
				return r
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			util.Log.Errorf("%s is not found in config.toml",
 | 
			
		||||
				r.ServerName)
 | 
			
		||||
			return r
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	filtered := r.ScannedCves.Find(func(v VulnInfo) bool {
 | 
			
		||||
		for _, c := range ignoreCves {
 | 
			
		||||
			if v.CveID == c {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return true
 | 
			
		||||
	})
 | 
			
		||||
	r.ScannedCves = filtered
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FilterUnfixed is filter function.
 | 
			
		||||
func (r ScanResult) FilterUnfixed() ScanResult {
 | 
			
		||||
	if !config.Conf.IgnoreUnfixed {
 | 
			
		||||
		return r
 | 
			
		||||
	}
 | 
			
		||||
	filtered := r.ScannedCves.Find(func(v VulnInfo) bool {
 | 
			
		||||
		// Report cves detected by CPE because Vuls can't know 'fixed' or 'unfixed'
 | 
			
		||||
		if len(v.CpeURIs) != 0 {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
		NotFixedAll := true
 | 
			
		||||
		for _, p := range v.AffectedPackages {
 | 
			
		||||
			NotFixedAll = NotFixedAll && p.NotFixedYet
 | 
			
		||||
		}
 | 
			
		||||
		return !NotFixedAll
 | 
			
		||||
	})
 | 
			
		||||
	r.ScannedCves = filtered
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FilterIgnorePkgs is filter function.
 | 
			
		||||
func (r ScanResult) FilterIgnorePkgs() ScanResult {
 | 
			
		||||
	ignorePkgsRegexps := []string{}
 | 
			
		||||
	if len(r.Container.Name) == 0 {
 | 
			
		||||
		ignorePkgsRegexps = config.Conf.Servers[r.ServerName].IgnorePkgsRegexp
 | 
			
		||||
	} else {
 | 
			
		||||
		if s, ok := config.Conf.Servers[r.ServerName]; ok {
 | 
			
		||||
			if con, ok := s.Containers[r.Container.Name]; ok {
 | 
			
		||||
				ignorePkgsRegexps = con.IgnorePkgsRegexp
 | 
			
		||||
			} else {
 | 
			
		||||
				return r
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			util.Log.Errorf("%s is not found in config.toml",
 | 
			
		||||
				r.ServerName)
 | 
			
		||||
			return r
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	regexps := []*regexp.Regexp{}
 | 
			
		||||
	for _, pkgRegexp := range ignorePkgsRegexps {
 | 
			
		||||
		re, err := regexp.Compile(pkgRegexp)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Errorf("Faild to parse %s. err: %+v", pkgRegexp, err)
 | 
			
		||||
			continue
 | 
			
		||||
		} else {
 | 
			
		||||
			regexps = append(regexps, re)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(regexps) == 0 {
 | 
			
		||||
		return r
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	filtered := r.ScannedCves.Find(func(v VulnInfo) bool {
 | 
			
		||||
		if len(v.AffectedPackages) == 0 {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
		for _, p := range v.AffectedPackages {
 | 
			
		||||
			match := false
 | 
			
		||||
			for _, re := range regexps {
 | 
			
		||||
				if re.MatchString(p.Name) {
 | 
			
		||||
					match = true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if !match {
 | 
			
		||||
				return true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return false
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	r.ScannedCves = filtered
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FilterInactiveWordPressLibs is filter function.
 | 
			
		||||
func (r ScanResult) FilterInactiveWordPressLibs() ScanResult {
 | 
			
		||||
	if !config.Conf.Servers[r.ServerName].WordPress.IgnoreInactive {
 | 
			
		||||
		return r
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	filtered := r.ScannedCves.Find(func(v VulnInfo) bool {
 | 
			
		||||
		if len(v.WpPackageFixStats) == 0 {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
		// Ignore if all libs in this vulnInfo inactive
 | 
			
		||||
		for _, wp := range v.WpPackageFixStats {
 | 
			
		||||
			if p, ok := r.WordPressPackages.Find(wp.Name); ok {
 | 
			
		||||
				if p.Status != Inactive {
 | 
			
		||||
					return true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return false
 | 
			
		||||
	})
 | 
			
		||||
	r.ScannedCves = filtered
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 information 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 len(r.Warnings) != 0 {
 | 
			
		||||
			line = "[Warn] " + line
 | 
			
		||||
		}
 | 
			
		||||
		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 {
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	for i := 0; i < len(r.ServerInfo()); i++ {
 | 
			
		||||
		buf.WriteString("=")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return fmt.Sprintf("%s\n%s\n%s, %s, %s, %s, %s\n",
 | 
			
		||||
		r.ServerInfo(),
 | 
			
		||||
		buf.String(),
 | 
			
		||||
		r.ScannedCves.FormatCveSummary(),
 | 
			
		||||
		r.ScannedCves.FormatFixedStatus(r.Packages),
 | 
			
		||||
		r.FormatUpdatablePacksSummary(),
 | 
			
		||||
		r.FormatExploitCveSummary(),
 | 
			
		||||
		r.FormatAlertSummary(),
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FormatUpdatablePacksSummary returns a summary of updatable packages
 | 
			
		||||
func (r ScanResult) FormatUpdatablePacksSummary() string {
 | 
			
		||||
	if !r.isDisplayUpdatableNum() {
 | 
			
		||||
		return fmt.Sprintf("%d installed", len(r.Packages))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nUpdatable := 0
 | 
			
		||||
	for _, p := range r.Packages {
 | 
			
		||||
		if p.NewVersion == "" {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if p.Version != p.NewVersion || p.Release != p.NewRelease {
 | 
			
		||||
			nUpdatable++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%d installed, %d updatable",
 | 
			
		||||
		len(r.Packages),
 | 
			
		||||
		nUpdatable)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FormatExploitCveSummary returns a summary of exploit cve
 | 
			
		||||
func (r ScanResult) FormatExploitCveSummary() string {
 | 
			
		||||
	nExploitCve := 0
 | 
			
		||||
	for _, vuln := range r.ScannedCves {
 | 
			
		||||
		if 0 < len(vuln.Exploits) {
 | 
			
		||||
			nExploitCve++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%d exploits", nExploitCve)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FormatAlertSummary returns a summary of XCERT alerts
 | 
			
		||||
func (r ScanResult) FormatAlertSummary() string {
 | 
			
		||||
	jaCnt := 0
 | 
			
		||||
	enCnt := 0
 | 
			
		||||
	for _, vuln := range r.ScannedCves {
 | 
			
		||||
		if len(vuln.AlertDict.En) > 0 {
 | 
			
		||||
			enCnt += len(vuln.AlertDict.En)
 | 
			
		||||
		}
 | 
			
		||||
		if len(vuln.AlertDict.Ja) > 0 {
 | 
			
		||||
			jaCnt += len(vuln.AlertDict.Ja)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("en: %d, ja: %d alerts", enCnt, jaCnt)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r ScanResult) isDisplayUpdatableNum() bool {
 | 
			
		||||
	var mode config.ScanMode
 | 
			
		||||
	s, _ := config.Conf.Servers[r.ServerName]
 | 
			
		||||
	mode = s.Mode
 | 
			
		||||
 | 
			
		||||
	if mode.IsOffline() {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	if mode.IsFastRoot() || mode.IsDeep() {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	if mode.IsFast() {
 | 
			
		||||
		switch r.Family {
 | 
			
		||||
		case config.RedHat,
 | 
			
		||||
			config.Oracle,
 | 
			
		||||
			config.Debian,
 | 
			
		||||
			config.Ubuntu,
 | 
			
		||||
			config.Raspbian:
 | 
			
		||||
			return false
 | 
			
		||||
		default:
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsContainer returns whether this ServerInfo is about container
 | 
			
		||||
func (r ScanResult) IsContainer() bool {
 | 
			
		||||
	return 0 < len(r.Container.ContainerID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsImage returns whether this ServerInfo is about container
 | 
			
		||||
func (r ScanResult) IsImage() bool {
 | 
			
		||||
	return 0 < len(r.Image.Name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsDeepScanMode checks if the scan mode is deep scan mode.
 | 
			
		||||
func (r ScanResult) IsDeepScanMode() bool {
 | 
			
		||||
	for _, s := range r.Config.Scan.Servers {
 | 
			
		||||
		for _, m := range s.ScanMode {
 | 
			
		||||
			if m == "deep" {
 | 
			
		||||
				return true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Container has Container information
 | 
			
		||||
type Container struct {
 | 
			
		||||
	ContainerID string `json:"containerID"`
 | 
			
		||||
	Name        string `json:"name"`
 | 
			
		||||
	Image       string `json:"image"`
 | 
			
		||||
	Type        string `json:"type"`
 | 
			
		||||
	UUID        string `json:"uuid"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Image has Container information
 | 
			
		||||
type Image struct {
 | 
			
		||||
	Name string `json:"name"`
 | 
			
		||||
	Tag  string `json:"tag"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Platform has platform information
 | 
			
		||||
type Platform struct {
 | 
			
		||||
	Name       string `json:"name"` // aws or azure or gcp or other...
 | 
			
		||||
	InstanceID string `json:"instanceID"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										738
									
								
								models/scanresults_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,738 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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/future-architect/vuls/config"
 | 
			
		||||
	"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:         NvdXML,
 | 
			
		||||
									CveID:        "CVE-2017-0001",
 | 
			
		||||
									Cvss2Score:   7.1,
 | 
			
		||||
									LastModified: time.Time{},
 | 
			
		||||
								},
 | 
			
		||||
							),
 | 
			
		||||
						},
 | 
			
		||||
						"CVE-2017-0002": {
 | 
			
		||||
							CveID: "CVE-2017-0002",
 | 
			
		||||
							CveContents: NewCveContents(
 | 
			
		||||
								CveContent{
 | 
			
		||||
									Type:         NvdXML,
 | 
			
		||||
									CveID:        "CVE-2017-0002",
 | 
			
		||||
									Cvss2Score:   6.9,
 | 
			
		||||
									LastModified: time.Time{},
 | 
			
		||||
								},
 | 
			
		||||
							),
 | 
			
		||||
						},
 | 
			
		||||
						"CVE-2017-0003": {
 | 
			
		||||
							CveID: "CVE-2017-0003",
 | 
			
		||||
							CveContents: NewCveContents(
 | 
			
		||||
								CveContent{
 | 
			
		||||
									Type:         NvdXML,
 | 
			
		||||
									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:         NvdXML,
 | 
			
		||||
								CveID:        "CVE-2017-0001",
 | 
			
		||||
								Cvss2Score:   7.1,
 | 
			
		||||
								LastModified: time.Time{},
 | 
			
		||||
							},
 | 
			
		||||
						),
 | 
			
		||||
					},
 | 
			
		||||
					"CVE-2017-0003": {
 | 
			
		||||
						CveID: "CVE-2017-0003",
 | 
			
		||||
						CveContents: NewCveContents(
 | 
			
		||||
							CveContent{
 | 
			
		||||
								Type:         NvdXML,
 | 
			
		||||
								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",
 | 
			
		||||
									Cvss2Severity: "HIGH",
 | 
			
		||||
									LastModified:  time.Time{},
 | 
			
		||||
								},
 | 
			
		||||
							),
 | 
			
		||||
						},
 | 
			
		||||
						"CVE-2017-0002": {
 | 
			
		||||
							CveID: "CVE-2017-0002",
 | 
			
		||||
							CveContents: NewCveContents(
 | 
			
		||||
								CveContent{
 | 
			
		||||
									Type:          RedHat,
 | 
			
		||||
									CveID:         "CVE-2017-0002",
 | 
			
		||||
									Cvss2Severity: "CRITICAL",
 | 
			
		||||
									LastModified:  time.Time{},
 | 
			
		||||
								},
 | 
			
		||||
							),
 | 
			
		||||
						},
 | 
			
		||||
						"CVE-2017-0003": {
 | 
			
		||||
							CveID: "CVE-2017-0003",
 | 
			
		||||
							CveContents: NewCveContents(
 | 
			
		||||
								CveContent{
 | 
			
		||||
									Type:          Oracle,
 | 
			
		||||
									CveID:         "CVE-2017-0003",
 | 
			
		||||
									Cvss2Severity: "IMPORTANT",
 | 
			
		||||
									LastModified:  time.Time{},
 | 
			
		||||
								},
 | 
			
		||||
							),
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: ScanResult{
 | 
			
		||||
				ScannedCves: VulnInfos{
 | 
			
		||||
					"CVE-2017-0001": {
 | 
			
		||||
						CveID: "CVE-2017-0001",
 | 
			
		||||
						CveContents: NewCveContents(
 | 
			
		||||
							CveContent{
 | 
			
		||||
								Type:          Ubuntu,
 | 
			
		||||
								CveID:         "CVE-2017-0001",
 | 
			
		||||
								Cvss2Severity: "HIGH",
 | 
			
		||||
								LastModified:  time.Time{},
 | 
			
		||||
							},
 | 
			
		||||
						),
 | 
			
		||||
					},
 | 
			
		||||
					"CVE-2017-0002": {
 | 
			
		||||
						CveID: "CVE-2017-0002",
 | 
			
		||||
						CveContents: NewCveContents(
 | 
			
		||||
							CveContent{
 | 
			
		||||
								Type:          RedHat,
 | 
			
		||||
								CveID:         "CVE-2017-0002",
 | 
			
		||||
								Cvss2Severity: "CRITICAL",
 | 
			
		||||
								LastModified:  time.Time{},
 | 
			
		||||
							},
 | 
			
		||||
						),
 | 
			
		||||
					},
 | 
			
		||||
					"CVE-2017-0003": {
 | 
			
		||||
						CveID: "CVE-2017-0003",
 | 
			
		||||
						CveContents: NewCveContents(
 | 
			
		||||
							CveContent{
 | 
			
		||||
								Type:          Oracle,
 | 
			
		||||
								CveID:         "CVE-2017-0003",
 | 
			
		||||
								Cvss2Severity: "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{
 | 
			
		||||
					ServerName: "name",
 | 
			
		||||
					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{
 | 
			
		||||
				ServerName: "name",
 | 
			
		||||
				ScannedCves: VulnInfos{
 | 
			
		||||
					"CVE-2017-0001": {
 | 
			
		||||
						CveID: "CVE-2017-0001",
 | 
			
		||||
					},
 | 
			
		||||
					"CVE-2017-0003": {
 | 
			
		||||
						CveID: "CVE-2017-0003",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		config.Conf.Servers = map[string]config.ServerInfo{
 | 
			
		||||
			"name": {IgnoreCves: tt.in.cves},
 | 
			
		||||
		}
 | 
			
		||||
		actual := tt.in.rs.FilterIgnoreCves()
 | 
			
		||||
		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)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		for k := range actual.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 TestFilterIgnoreCveIDsContainer(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{
 | 
			
		||||
					ServerName: "name",
 | 
			
		||||
					Container:  Container{Name: "dockerA"},
 | 
			
		||||
					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{
 | 
			
		||||
				ServerName: "name",
 | 
			
		||||
				Container:  Container{Name: "dockerA"},
 | 
			
		||||
				ScannedCves: VulnInfos{
 | 
			
		||||
					"CVE-2017-0001": {
 | 
			
		||||
						CveID: "CVE-2017-0001",
 | 
			
		||||
					},
 | 
			
		||||
					"CVE-2017-0003": {
 | 
			
		||||
						CveID: "CVE-2017-0003",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		config.Conf.Servers = map[string]config.ServerInfo{
 | 
			
		||||
			"name": {
 | 
			
		||||
				Containers: map[string]config.ContainerSetting{
 | 
			
		||||
					"dockerA": {
 | 
			
		||||
						IgnoreCves: tt.in.cves,
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
		actual := tt.in.rs.FilterIgnoreCves()
 | 
			
		||||
		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)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		for k := range actual.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 TestFilterUnfixed(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in  ScanResult
 | 
			
		||||
		out ScanResult
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			in: ScanResult{
 | 
			
		||||
				ScannedCves: VulnInfos{
 | 
			
		||||
					"CVE-2017-0001": {
 | 
			
		||||
						CveID: "CVE-2017-0001",
 | 
			
		||||
						AffectedPackages: PackageFixStatuses{
 | 
			
		||||
							{
 | 
			
		||||
								Name:        "a",
 | 
			
		||||
								NotFixedYet: true,
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"CVE-2017-0002": {
 | 
			
		||||
						CveID: "CVE-2017-0002",
 | 
			
		||||
						AffectedPackages: PackageFixStatuses{
 | 
			
		||||
							{
 | 
			
		||||
								Name:        "b",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"CVE-2017-0003": {
 | 
			
		||||
						CveID: "CVE-2017-0003",
 | 
			
		||||
						AffectedPackages: PackageFixStatuses{
 | 
			
		||||
							{
 | 
			
		||||
								Name:        "c",
 | 
			
		||||
								NotFixedYet: true,
 | 
			
		||||
							},
 | 
			
		||||
							{
 | 
			
		||||
								Name:        "d",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: ScanResult{
 | 
			
		||||
				ScannedCves: VulnInfos{
 | 
			
		||||
					"CVE-2017-0002": {
 | 
			
		||||
						CveID: "CVE-2017-0002",
 | 
			
		||||
						AffectedPackages: PackageFixStatuses{
 | 
			
		||||
							{
 | 
			
		||||
								Name:        "b",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"CVE-2017-0003": {
 | 
			
		||||
						CveID: "CVE-2017-0003",
 | 
			
		||||
						AffectedPackages: PackageFixStatuses{
 | 
			
		||||
							{
 | 
			
		||||
								Name:        "c",
 | 
			
		||||
								NotFixedYet: true,
 | 
			
		||||
							},
 | 
			
		||||
							{
 | 
			
		||||
								Name:        "d",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
		config.Conf.IgnoreUnfixed = true
 | 
			
		||||
		actual := tt.in.FilterUnfixed()
 | 
			
		||||
		if !reflect.DeepEqual(tt.out.ScannedCves, actual.ScannedCves) {
 | 
			
		||||
			o := pp.Sprintf("%v", tt.out.ScannedCves)
 | 
			
		||||
			a := pp.Sprintf("%v", actual.ScannedCves)
 | 
			
		||||
			t.Errorf("[%d] expected: %v\n  actual: %v\n", i, o, a)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestFilterIgnorePkgs(t *testing.T) {
 | 
			
		||||
	type in struct {
 | 
			
		||||
		ignorePkgsRegexp []string
 | 
			
		||||
		rs               ScanResult
 | 
			
		||||
	}
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in  in
 | 
			
		||||
		out ScanResult
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				ignorePkgsRegexp: []string{"^kernel"},
 | 
			
		||||
				rs: ScanResult{
 | 
			
		||||
					ServerName: "name",
 | 
			
		||||
					ScannedCves: VulnInfos{
 | 
			
		||||
						"CVE-2017-0001": {
 | 
			
		||||
							CveID: "CVE-2017-0001",
 | 
			
		||||
							AffectedPackages: PackageFixStatuses{
 | 
			
		||||
								{Name: "kernel"},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						"CVE-2017-0002": {
 | 
			
		||||
							CveID: "CVE-2017-0002",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: ScanResult{
 | 
			
		||||
				ServerName: "name",
 | 
			
		||||
				ScannedCves: VulnInfos{
 | 
			
		||||
					"CVE-2017-0002": {
 | 
			
		||||
						CveID: "CVE-2017-0002",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				ignorePkgsRegexp: []string{"^kernel"},
 | 
			
		||||
				rs: ScanResult{
 | 
			
		||||
					ServerName: "name",
 | 
			
		||||
					ScannedCves: VulnInfos{
 | 
			
		||||
						"CVE-2017-0001": {
 | 
			
		||||
							CveID: "CVE-2017-0001",
 | 
			
		||||
							AffectedPackages: PackageFixStatuses{
 | 
			
		||||
								{Name: "kernel"},
 | 
			
		||||
								{Name: "vim"},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: ScanResult{
 | 
			
		||||
				ServerName: "name",
 | 
			
		||||
				ScannedCves: VulnInfos{
 | 
			
		||||
					"CVE-2017-0001": {
 | 
			
		||||
						CveID: "CVE-2017-0001",
 | 
			
		||||
						AffectedPackages: PackageFixStatuses{
 | 
			
		||||
							{Name: "kernel"},
 | 
			
		||||
							{Name: "vim"},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				ignorePkgsRegexp: []string{"^kernel", "^vim", "^bind"},
 | 
			
		||||
				rs: ScanResult{
 | 
			
		||||
					ServerName: "name",
 | 
			
		||||
					ScannedCves: VulnInfos{
 | 
			
		||||
						"CVE-2017-0001": {
 | 
			
		||||
							CveID: "CVE-2017-0001",
 | 
			
		||||
							AffectedPackages: PackageFixStatuses{
 | 
			
		||||
								{Name: "kernel"},
 | 
			
		||||
								{Name: "vim"},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: ScanResult{
 | 
			
		||||
				ServerName:  "name",
 | 
			
		||||
				ScannedCves: VulnInfos{},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		config.Conf.Servers = map[string]config.ServerInfo{
 | 
			
		||||
			"name": {IgnorePkgsRegexp: tt.in.ignorePkgsRegexp},
 | 
			
		||||
		}
 | 
			
		||||
		actual := tt.in.rs.FilterIgnorePkgs()
 | 
			
		||||
		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)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		for k := range actual.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 TestFilterIgnorePkgsContainer(t *testing.T) {
 | 
			
		||||
	type in struct {
 | 
			
		||||
		ignorePkgsRegexp []string
 | 
			
		||||
		rs               ScanResult
 | 
			
		||||
	}
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in  in
 | 
			
		||||
		out ScanResult
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				ignorePkgsRegexp: []string{"^kernel"},
 | 
			
		||||
				rs: ScanResult{
 | 
			
		||||
					ServerName: "name",
 | 
			
		||||
					Container:  Container{Name: "dockerA"},
 | 
			
		||||
					ScannedCves: VulnInfos{
 | 
			
		||||
						"CVE-2017-0001": {
 | 
			
		||||
							CveID: "CVE-2017-0001",
 | 
			
		||||
							AffectedPackages: PackageFixStatuses{
 | 
			
		||||
								{Name: "kernel"},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						"CVE-2017-0002": {
 | 
			
		||||
							CveID: "CVE-2017-0002",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: ScanResult{
 | 
			
		||||
				ServerName: "name",
 | 
			
		||||
				Container:  Container{Name: "dockerA"},
 | 
			
		||||
				ScannedCves: VulnInfos{
 | 
			
		||||
					"CVE-2017-0002": {
 | 
			
		||||
						CveID: "CVE-2017-0002",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				ignorePkgsRegexp: []string{"^kernel"},
 | 
			
		||||
				rs: ScanResult{
 | 
			
		||||
					ServerName: "name",
 | 
			
		||||
					Container:  Container{Name: "dockerA"},
 | 
			
		||||
					ScannedCves: VulnInfos{
 | 
			
		||||
						"CVE-2017-0001": {
 | 
			
		||||
							CveID: "CVE-2017-0001",
 | 
			
		||||
							AffectedPackages: PackageFixStatuses{
 | 
			
		||||
								{Name: "kernel"},
 | 
			
		||||
								{Name: "vim"},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: ScanResult{
 | 
			
		||||
				ServerName: "name",
 | 
			
		||||
				Container:  Container{Name: "dockerA"},
 | 
			
		||||
				ScannedCves: VulnInfos{
 | 
			
		||||
					"CVE-2017-0001": {
 | 
			
		||||
						CveID: "CVE-2017-0001",
 | 
			
		||||
						AffectedPackages: PackageFixStatuses{
 | 
			
		||||
							{Name: "kernel"},
 | 
			
		||||
							{Name: "vim"},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				ignorePkgsRegexp: []string{"^kernel", "^vim", "^bind"},
 | 
			
		||||
				rs: ScanResult{
 | 
			
		||||
					ServerName: "name",
 | 
			
		||||
					Container:  Container{Name: "dockerA"},
 | 
			
		||||
					ScannedCves: VulnInfos{
 | 
			
		||||
						"CVE-2017-0001": {
 | 
			
		||||
							CveID: "CVE-2017-0001",
 | 
			
		||||
							AffectedPackages: PackageFixStatuses{
 | 
			
		||||
								{Name: "kernel"},
 | 
			
		||||
								{Name: "vim"},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: ScanResult{
 | 
			
		||||
				ServerName:  "name",
 | 
			
		||||
				Container:   Container{Name: "dockerA"},
 | 
			
		||||
				ScannedCves: VulnInfos{},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		config.Conf.Servers = map[string]config.ServerInfo{
 | 
			
		||||
			"name": {
 | 
			
		||||
				Containers: map[string]config.ContainerSetting{
 | 
			
		||||
					"dockerA": {
 | 
			
		||||
						IgnorePkgsRegexp: tt.in.ignorePkgsRegexp,
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
		actual := tt.in.rs.FilterIgnorePkgs()
 | 
			
		||||
		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)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		for k := range actual.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 TestIsDisplayUpdatableNum(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		mode     []byte
 | 
			
		||||
		family   string
 | 
			
		||||
		expected bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			mode:     []byte{config.Offline},
 | 
			
		||||
			expected: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			mode:     []byte{config.FastRoot},
 | 
			
		||||
			expected: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			mode:     []byte{config.Deep},
 | 
			
		||||
			expected: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			mode:     []byte{config.Fast},
 | 
			
		||||
			family:   config.RedHat,
 | 
			
		||||
			expected: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			mode:     []byte{config.Fast},
 | 
			
		||||
			family:   config.Oracle,
 | 
			
		||||
			expected: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			mode:     []byte{config.Fast},
 | 
			
		||||
			family:   config.Debian,
 | 
			
		||||
			expected: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			mode:     []byte{config.Fast},
 | 
			
		||||
			family:   config.Ubuntu,
 | 
			
		||||
			expected: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			mode:     []byte{config.Fast},
 | 
			
		||||
			family:   config.Raspbian,
 | 
			
		||||
			expected: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			mode:     []byte{config.Fast},
 | 
			
		||||
			family:   config.CentOS,
 | 
			
		||||
			expected: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			mode:     []byte{config.Fast},
 | 
			
		||||
			family:   config.Amazon,
 | 
			
		||||
			expected: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			mode:     []byte{config.Fast},
 | 
			
		||||
			family:   config.FreeBSD,
 | 
			
		||||
			expected: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			mode:     []byte{config.Fast},
 | 
			
		||||
			family:   config.OpenSUSE,
 | 
			
		||||
			expected: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			mode:     []byte{config.Fast},
 | 
			
		||||
			family:   config.Alpine,
 | 
			
		||||
			expected: true,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
		mode := config.ScanMode{}
 | 
			
		||||
		for _, m := range tt.mode {
 | 
			
		||||
			mode.Set(m)
 | 
			
		||||
		}
 | 
			
		||||
		config.Conf.Servers = map[string]config.ServerInfo{
 | 
			
		||||
			"name": {Mode: mode},
 | 
			
		||||
		}
 | 
			
		||||
		r := ScanResult{
 | 
			
		||||
			ServerName: "name",
 | 
			
		||||
			Family:     tt.family,
 | 
			
		||||
		}
 | 
			
		||||
		act := r.isDisplayUpdatableNum()
 | 
			
		||||
		if tt.expected != act {
 | 
			
		||||
			t.Errorf("[%d] expected %#v, actual %#v", i, tt.expected, act)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										156
									
								
								models/utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,156 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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"
 | 
			
		||||
 | 
			
		||||
	cvedict "github.com/kotakanbe/go-cve-dictionary/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ConvertNvdXMLToModel convert NVD to CveContent
 | 
			
		||||
func ConvertNvdXMLToModel(cveID string, nvd *cvedict.NvdXML) *CveContent {
 | 
			
		||||
	if nvd == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	var cpes []Cpe
 | 
			
		||||
	for _, c := range nvd.Cpes {
 | 
			
		||||
		cpes = append(cpes, Cpe{
 | 
			
		||||
			FormattedString: c.FormattedString,
 | 
			
		||||
			URI:             c.URI,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var refs []Reference
 | 
			
		||||
	for _, r := range nvd.References {
 | 
			
		||||
		refs = append(refs, Reference{
 | 
			
		||||
			Link:   r.Link,
 | 
			
		||||
			Source: r.Source,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cweIDs := []string{}
 | 
			
		||||
	for _, cid := range nvd.Cwes {
 | 
			
		||||
		cweIDs = append(cweIDs, cid.CweID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &CveContent{
 | 
			
		||||
		Type:          Nvd,
 | 
			
		||||
		CveID:         cveID,
 | 
			
		||||
		Summary:       nvd.Summary,
 | 
			
		||||
		Cvss2Score:    nvd.Cvss2.BaseScore,
 | 
			
		||||
		Cvss2Vector:   nvd.Cvss2.VectorString,
 | 
			
		||||
		Cvss2Severity: nvd.Cvss2.Severity,
 | 
			
		||||
		SourceLink:    "https://nvd.nist.gov/vuln/detail/" + cveID,
 | 
			
		||||
		// Cpes:          cpes,
 | 
			
		||||
		CweIDs:       cweIDs,
 | 
			
		||||
		References:   refs,
 | 
			
		||||
		Published:    nvd.PublishedDate,
 | 
			
		||||
		LastModified: nvd.LastModifiedDate,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConvertJvnToModel convert JVN to CveContent
 | 
			
		||||
func ConvertJvnToModel(cveID string, jvn *cvedict.Jvn) *CveContent {
 | 
			
		||||
	if jvn == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	var cpes []Cpe
 | 
			
		||||
	for _, c := range jvn.Cpes {
 | 
			
		||||
		cpes = append(cpes, Cpe{
 | 
			
		||||
			FormattedString: c.FormattedString,
 | 
			
		||||
			URI:             c.URI,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	refs := []Reference{}
 | 
			
		||||
	for _, r := range jvn.References {
 | 
			
		||||
		refs = append(refs, Reference{
 | 
			
		||||
			Link:   r.Link,
 | 
			
		||||
			Source: r.Source,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &CveContent{
 | 
			
		||||
		Type:          Jvn,
 | 
			
		||||
		CveID:         cveID,
 | 
			
		||||
		Title:         jvn.Title,
 | 
			
		||||
		Summary:       jvn.Summary,
 | 
			
		||||
		Cvss2Score:    jvn.Cvss2.BaseScore,
 | 
			
		||||
		Cvss2Vector:   jvn.Cvss2.VectorString,
 | 
			
		||||
		Cvss2Severity: jvn.Cvss2.Severity,
 | 
			
		||||
		Cvss3Score:    jvn.Cvss3.BaseScore,
 | 
			
		||||
		Cvss3Vector:   jvn.Cvss3.VectorString,
 | 
			
		||||
		Cvss3Severity: jvn.Cvss3.BaseSeverity,
 | 
			
		||||
		SourceLink:    jvn.JvnLink,
 | 
			
		||||
		// Cpes:          cpes,
 | 
			
		||||
		References:   refs,
 | 
			
		||||
		Published:    jvn.PublishedDate,
 | 
			
		||||
		LastModified: jvn.LastModifiedDate,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConvertNvdJSONToModel convert NVD to CveContent
 | 
			
		||||
func ConvertNvdJSONToModel(cveID string, nvd *cvedict.NvdJSON) *CveContent {
 | 
			
		||||
	if nvd == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	var cpes []Cpe
 | 
			
		||||
	for _, c := range nvd.Cpes {
 | 
			
		||||
		cpes = append(cpes, Cpe{
 | 
			
		||||
			FormattedString: c.FormattedString,
 | 
			
		||||
			URI:             c.URI,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var refs []Reference
 | 
			
		||||
	for _, r := range nvd.References {
 | 
			
		||||
		refs = append(refs, Reference{
 | 
			
		||||
			Link:   r.Link,
 | 
			
		||||
			Source: r.Source,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cweIDs := []string{}
 | 
			
		||||
	for _, cid := range nvd.Cwes {
 | 
			
		||||
		cweIDs = append(cweIDs, cid.CweID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	desc := []string{}
 | 
			
		||||
	for _, d := range nvd.Descriptions {
 | 
			
		||||
		desc = append(desc, d.Value)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &CveContent{
 | 
			
		||||
		Type:          Nvd,
 | 
			
		||||
		CveID:         cveID,
 | 
			
		||||
		Summary:       strings.Join(desc, "\n"),
 | 
			
		||||
		Cvss2Score:    nvd.Cvss2.BaseScore,
 | 
			
		||||
		Cvss2Vector:   nvd.Cvss2.VectorString,
 | 
			
		||||
		Cvss2Severity: nvd.Cvss2.Severity,
 | 
			
		||||
		Cvss3Score:    nvd.Cvss3.BaseScore,
 | 
			
		||||
		Cvss3Vector:   nvd.Cvss3.VectorString,
 | 
			
		||||
		Cvss3Severity: nvd.Cvss3.BaseSeverity,
 | 
			
		||||
		SourceLink:    "https://nvd.nist.gov/vuln/detail/" + cveID,
 | 
			
		||||
		// Cpes:          cpes,
 | 
			
		||||
		CweIDs:       cweIDs,
 | 
			
		||||
		References:   refs,
 | 
			
		||||
		Published:    nvd.PublishedDate,
 | 
			
		||||
		LastModified: nvd.LastModifiedDate,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										915
									
								
								models/vulninfos.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,915 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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/alert"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	exploitmodels "github.com/mozqnet/go-exploitdb/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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"])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FormatFixedStatus summarize the number of cves are fixed.
 | 
			
		||||
func (v VulnInfos) FormatFixedStatus(packs Packages) string {
 | 
			
		||||
	total, fixed := 0, 0
 | 
			
		||||
	for _, vInfo := range v {
 | 
			
		||||
		if len(vInfo.CpeURIs) != 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		total++
 | 
			
		||||
		if vInfo.PatchStatus(packs) == "fixed" {
 | 
			
		||||
			fixed++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%d/%d Fixed", fixed, total)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PackageFixStatuses is a list of PackageStatus
 | 
			
		||||
type PackageFixStatuses []PackageFixStatus
 | 
			
		||||
 | 
			
		||||
// Names return a slice of package names
 | 
			
		||||
func (ps PackageFixStatuses) Names() (names []string) {
 | 
			
		||||
	for _, p := range ps {
 | 
			
		||||
		names = append(names, p.Name)
 | 
			
		||||
	}
 | 
			
		||||
	return names
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Store insert given pkg if missing, update pkg if exists
 | 
			
		||||
func (ps PackageFixStatuses) Store(pkg PackageFixStatus) PackageFixStatuses {
 | 
			
		||||
	for i, p := range ps {
 | 
			
		||||
		if p.Name == pkg.Name {
 | 
			
		||||
			ps[i] = pkg
 | 
			
		||||
			return ps
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	ps = append(ps, pkg)
 | 
			
		||||
	return ps
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sort by Name
 | 
			
		||||
func (ps PackageFixStatuses) Sort() {
 | 
			
		||||
	sort.Slice(ps, func(i, j int) bool {
 | 
			
		||||
		return ps[i].Name < ps[j].Name
 | 
			
		||||
	})
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PackageFixStatus has name and other status abount the package
 | 
			
		||||
type PackageFixStatus struct {
 | 
			
		||||
	Name        string `json:"name"`
 | 
			
		||||
	NotFixedYet bool   `json:"notFixedYet"`
 | 
			
		||||
	FixState    string `json:"fixState"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// VulnInfo has a vulnerability information and unsecure packages
 | 
			
		||||
type VulnInfo struct {
 | 
			
		||||
	CveID                string               `json:"cveID,omitempty"`
 | 
			
		||||
	Confidences          Confidences          `json:"confidences,omitempty"`
 | 
			
		||||
	AffectedPackages     PackageFixStatuses   `json:"affectedPackages,omitempty"`
 | 
			
		||||
	DistroAdvisories     DistroAdvisories     `json:"distroAdvisories,omitempty"` // for Aamazon, RHEL, FreeBSD
 | 
			
		||||
	CveContents          CveContents          `json:"cveContents,omitempty"`
 | 
			
		||||
	Exploits             []Exploit            `json:"exploits,omitempty"`
 | 
			
		||||
	AlertDict            AlertDict            `json:"alertDict,omitempty"`
 | 
			
		||||
	CpeURIs              []string             `json:"cpeURIs,omitempty"` // CpeURIs related to this CVE defined in config.toml
 | 
			
		||||
	GitHubSecurityAlerts GitHubSecurityAlerts `json:"gitHubSecurityAlerts,omitempty"`
 | 
			
		||||
	WpPackageFixStats    WpPackageFixStats    `json:"wpPackageFixStats,omitempty"`
 | 
			
		||||
	LibraryFixedIns      LibraryFixedIns      `json:"libraryFixedIns,omitempty"`
 | 
			
		||||
 | 
			
		||||
	VulnType string `json:"vulnType,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GitHubSecurityAlerts is a list of GitHubSecurityAlert
 | 
			
		||||
type GitHubSecurityAlerts []GitHubSecurityAlert
 | 
			
		||||
 | 
			
		||||
// Add adds given arg to the slice and return the slice (immutable)
 | 
			
		||||
func (g GitHubSecurityAlerts) Add(alert GitHubSecurityAlert) GitHubSecurityAlerts {
 | 
			
		||||
	for _, a := range g {
 | 
			
		||||
		if a.PackageName == alert.PackageName {
 | 
			
		||||
			return g
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return append(g, alert)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Names return a slice of lib names
 | 
			
		||||
func (g GitHubSecurityAlerts) Names() (names []string) {
 | 
			
		||||
	for _, a := range g {
 | 
			
		||||
		names = append(names, a.PackageName)
 | 
			
		||||
	}
 | 
			
		||||
	return names
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GitHubSecurityAlert has detected CVE-ID, PackageName, Status fetched via GitHub API
 | 
			
		||||
type GitHubSecurityAlert struct {
 | 
			
		||||
	PackageName   string    `json:"packageName"`
 | 
			
		||||
	FixedIn       string    `json:"fixedIn"`
 | 
			
		||||
	AffectedRange string    `json:"affectedRange"`
 | 
			
		||||
	Dismissed     bool      `json:"dismissed"`
 | 
			
		||||
	DismissedAt   time.Time `json:"dismissedAt"`
 | 
			
		||||
	DismissReason string    `json:"dismissReason"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LibraryFixedIns is a list of Library's FixedIn
 | 
			
		||||
type LibraryFixedIns []LibraryFixedIn
 | 
			
		||||
 | 
			
		||||
// WpPackageFixStats is a list of WpPackageFixStatus
 | 
			
		||||
type WpPackageFixStats []WpPackageFixStatus
 | 
			
		||||
 | 
			
		||||
// Names return a slice of names
 | 
			
		||||
func (ws WpPackageFixStats) Names() (names []string) {
 | 
			
		||||
	for _, w := range ws {
 | 
			
		||||
		names = append(names, w.Name)
 | 
			
		||||
	}
 | 
			
		||||
	return names
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WpPackages has a list of WpPackage
 | 
			
		||||
type WpPackages []WpPackage
 | 
			
		||||
 | 
			
		||||
// Add adds given arg to the slice and return the slice (immutable)
 | 
			
		||||
func (g WpPackages) Add(pkg WpPackage) WpPackages {
 | 
			
		||||
	for _, a := range g {
 | 
			
		||||
		if a.Name == pkg.Name {
 | 
			
		||||
			return g
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return append(g, pkg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// RedHat API has one line title.
 | 
			
		||||
	if cont, found := v.CveContents[RedHatAPI]; found && 0 < len(cont.Title) {
 | 
			
		||||
		values = append(values, CveContentStr{RedHatAPI, cont.Title})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	order := CveContentTypes{Nvd, NvdXML, 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, NvdXML, 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 v, ok := v.CveContents[WPVulnDB]; ok {
 | 
			
		||||
		values = append(values, CveContentStr{
 | 
			
		||||
			Type:  "WPVDB",
 | 
			
		||||
			Value: v.Title,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(values) == 0 {
 | 
			
		||||
		return []CveContentStr{{
 | 
			
		||||
			Type:  Unknown,
 | 
			
		||||
			Value: "-",
 | 
			
		||||
		}}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Mitigations returns mitigations
 | 
			
		||||
func (v VulnInfo) Mitigations(myFamily string) (values []CveContentStr) {
 | 
			
		||||
	order := CveContentTypes{RedHatAPI}
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v.CveContents[ctype]; found && 0 < len(cont.Mitigation) {
 | 
			
		||||
			values = append(values, CveContentStr{
 | 
			
		||||
				Type:  ctype,
 | 
			
		||||
				Value: cont.Mitigation,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(values) == 0 {
 | 
			
		||||
		return []CveContentStr{{
 | 
			
		||||
			Type:  Unknown,
 | 
			
		||||
			Value: "-",
 | 
			
		||||
		}}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Cvss2Scores returns CVSS V2 Scores
 | 
			
		||||
func (v VulnInfo) Cvss2Scores(myFamily string) (values []CveContentCvss) {
 | 
			
		||||
	order := []CveContentType{Nvd, NvdXML, RedHatAPI, RedHat, Jvn}
 | 
			
		||||
	if myFamily != config.RedHat && myFamily != config.CentOS {
 | 
			
		||||
		order = append(order, NewCveContentType(myFamily))
 | 
			
		||||
	}
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v.CveContents[ctype]; found {
 | 
			
		||||
			if cont.Cvss2Score == 0 || cont.Cvss2Severity == "" {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			// https://nvd.nist.gov/vuln-metrics/cvss
 | 
			
		||||
			values = append(values, CveContentCvss{
 | 
			
		||||
				Type: ctype,
 | 
			
		||||
				Value: Cvss{
 | 
			
		||||
					Type:     CVSS2,
 | 
			
		||||
					Score:    cont.Cvss2Score,
 | 
			
		||||
					Vector:   cont.Cvss2Vector,
 | 
			
		||||
					Severity: strings.ToUpper(cont.Cvss2Severity),
 | 
			
		||||
				},
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, adv := range v.DistroAdvisories {
 | 
			
		||||
		if adv.Severity != "" {
 | 
			
		||||
			values = append(values, CveContentCvss{
 | 
			
		||||
				Type: "Advisory",
 | 
			
		||||
				Value: Cvss{
 | 
			
		||||
					Type:                 CVSS2,
 | 
			
		||||
					Score:                severityToV2ScoreRoughly(adv.Severity),
 | 
			
		||||
					CalculatedBySeverity: true,
 | 
			
		||||
					Vector:               "-",
 | 
			
		||||
					Severity:             strings.ToUpper(adv.Severity),
 | 
			
		||||
				},
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// An OVAL entry in Ubuntu and Debian has only severity (CVSS score isn't included).
 | 
			
		||||
	// Show severity and dummy score calculated roughly.
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(order...)...)
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v.CveContents[ctype]; found &&
 | 
			
		||||
			cont.Cvss2Score == 0 &&
 | 
			
		||||
			cont.Cvss3Score == 0 &&
 | 
			
		||||
			cont.Cvss2Severity != "" {
 | 
			
		||||
 | 
			
		||||
			values = append(values, CveContentCvss{
 | 
			
		||||
				Type: cont.Type,
 | 
			
		||||
				Value: Cvss{
 | 
			
		||||
					Type:                 CVSS2,
 | 
			
		||||
					Score:                severityToV2ScoreRoughly(cont.Cvss2Severity),
 | 
			
		||||
					CalculatedBySeverity: true,
 | 
			
		||||
					Vector:               "-",
 | 
			
		||||
					Severity:             strings.ToUpper(cont.Cvss2Severity),
 | 
			
		||||
				},
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Cvss3Scores returns CVSS V3 Score
 | 
			
		||||
func (v VulnInfo) Cvss3Scores() (values []CveContentCvss) {
 | 
			
		||||
	order := []CveContentType{Nvd, RedHatAPI, RedHat, Jvn}
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v.CveContents[ctype]; found {
 | 
			
		||||
			// https://nvd.nist.gov/vuln-metrics/cvss
 | 
			
		||||
			values = append(values, CveContentCvss{
 | 
			
		||||
				Type: ctype,
 | 
			
		||||
				Value: Cvss{
 | 
			
		||||
					Type:     CVSS3,
 | 
			
		||||
					Score:    cont.Cvss3Score,
 | 
			
		||||
					Vector:   cont.Cvss3Vector,
 | 
			
		||||
					Severity: strings.ToUpper(cont.Cvss3Severity),
 | 
			
		||||
				},
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MaxCvss3Score returns Max CVSS V3 Score
 | 
			
		||||
func (v VulnInfo) MaxCvss3Score() CveContentCvss {
 | 
			
		||||
	order := []CveContentType{Nvd, RedHat, RedHatAPI, Jvn}
 | 
			
		||||
	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
 | 
			
		||||
			value = CveContentCvss{
 | 
			
		||||
				Type: ctype,
 | 
			
		||||
				Value: Cvss{
 | 
			
		||||
					Type:     CVSS3,
 | 
			
		||||
					Score:    cont.Cvss3Score,
 | 
			
		||||
					Vector:   cont.Cvss3Vector,
 | 
			
		||||
					Severity: strings.ToUpper(cont.Cvss3Severity),
 | 
			
		||||
				},
 | 
			
		||||
			}
 | 
			
		||||
			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, NvdXML, RedHat, RedHatAPI, 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
 | 
			
		||||
			value = CveContentCvss{
 | 
			
		||||
				Type: ctype,
 | 
			
		||||
				Value: Cvss{
 | 
			
		||||
					Type:     CVSS2,
 | 
			
		||||
					Score:    cont.Cvss2Score,
 | 
			
		||||
					Vector:   cont.Cvss2Vector,
 | 
			
		||||
					Severity: strings.ToUpper(cont.Cvss2Severity),
 | 
			
		||||
				},
 | 
			
		||||
			}
 | 
			
		||||
			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.Cvss2Severity) {
 | 
			
		||||
			score := severityToV2ScoreRoughly(cont.Cvss2Severity)
 | 
			
		||||
			if max < score {
 | 
			
		||||
				value = CveContentCvss{
 | 
			
		||||
					Type: ctype,
 | 
			
		||||
					Value: Cvss{
 | 
			
		||||
						Type:                 CVSS2,
 | 
			
		||||
						Score:                score,
 | 
			
		||||
						CalculatedBySeverity: true,
 | 
			
		||||
						Vector:               cont.Cvss2Vector,
 | 
			
		||||
						Severity:             strings.ToUpper(cont.Cvss2Severity),
 | 
			
		||||
					},
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AttackVector returns attack vector string
 | 
			
		||||
func (v VulnInfo) AttackVector() string {
 | 
			
		||||
	for _, cnt := range v.CveContents {
 | 
			
		||||
		if strings.HasPrefix(cnt.Cvss2Vector, "AV:N") ||
 | 
			
		||||
			strings.HasPrefix(cnt.Cvss3Vector, "CVSS:3.0/AV:N") {
 | 
			
		||||
			return "N"
 | 
			
		||||
		} else if strings.HasPrefix(cnt.Cvss2Vector, "AV:A") ||
 | 
			
		||||
			strings.HasPrefix(cnt.Cvss3Vector, "CVSS:3.0/AV:A") {
 | 
			
		||||
			return "A"
 | 
			
		||||
		} else if strings.HasPrefix(cnt.Cvss2Vector, "AV:L") ||
 | 
			
		||||
			strings.HasPrefix(cnt.Cvss3Vector, "CVSS:3.0/AV:L") {
 | 
			
		||||
			return "L"
 | 
			
		||||
		} else if strings.HasPrefix(cnt.Cvss3Vector, "CVSS:3.0/AV:P") {
 | 
			
		||||
			return "P"
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if cont, found := v.CveContents[DebianSecurityTracker]; found {
 | 
			
		||||
		if attackRange, found := cont.Optional["attack range"]; found {
 | 
			
		||||
			return attackRange
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PatchStatus returns fixed or unfixed string
 | 
			
		||||
func (v VulnInfo) PatchStatus(packs Packages) string {
 | 
			
		||||
	// Vuls don't know patch status of the CPE
 | 
			
		||||
	if len(v.CpeURIs) != 0 {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	for _, p := range v.AffectedPackages {
 | 
			
		||||
		if p.NotFixedYet {
 | 
			
		||||
			return "unfixed"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// fast, offline mode doesn't have new version
 | 
			
		||||
		if pack, ok := packs[p.Name]; ok {
 | 
			
		||||
			if pack.NewVersion == "" {
 | 
			
		||||
				return "unknown"
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return "fixed"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CveContentCvss has CVSS information
 | 
			
		||||
type CveContentCvss struct {
 | 
			
		||||
	Type  CveContentType `json:"type"`
 | 
			
		||||
	Value Cvss           `json:"value"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 `json:"type"`
 | 
			
		||||
	Score                float64  `json:"score"`
 | 
			
		||||
	CalculatedBySeverity bool     `json:"calculatedBySeverity"`
 | 
			
		||||
	Vector               string   `json:"vector"`
 | 
			
		||||
	Severity             string   `json:"severity"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Format CVSS Score and Vector
 | 
			
		||||
func (c Cvss) Format() string {
 | 
			
		||||
	if c.Score == 0 || c.Vector == "" {
 | 
			
		||||
		return c.Severity
 | 
			
		||||
	}
 | 
			
		||||
	switch c.Type {
 | 
			
		||||
	case CVSS2:
 | 
			
		||||
		return fmt.Sprintf("%3.1f/%s %s", c.Score, c.Vector, c.Severity)
 | 
			
		||||
	case CVSS3:
 | 
			
		||||
		return fmt.Sprintf("%3.1f/%s %s", c.Score, c.Vector, c.Severity)
 | 
			
		||||
	}
 | 
			
		||||
	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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FormatMaxCvssScore returns Max CVSS Score
 | 
			
		||||
func (v VulnInfo) FormatMaxCvssScore() string {
 | 
			
		||||
	max := v.MaxCvssScore()
 | 
			
		||||
	return fmt.Sprintf("%3.1f %s (%s)",
 | 
			
		||||
		max.Value.Score,
 | 
			
		||||
		strings.ToUpper(max.Value.Severity),
 | 
			
		||||
		max.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{}
 | 
			
		||||
	if strings.HasPrefix(v.CveID, "WPVDBID") {
 | 
			
		||||
		links["WPVulnDB"] = fmt.Sprintf("https://wpvulndb.com/vulnerabilities/%s",
 | 
			
		||||
			strings.TrimPrefix(v.CveID, "WPVDBID-"))
 | 
			
		||||
		return links
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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 {
 | 
			
		||||
			if strings.HasPrefix(advisory.AdvisoryID, "ALAS2") {
 | 
			
		||||
				links[advisory.AdvisoryID] =
 | 
			
		||||
					fmt.Sprintf("https://alas.aws.amazon.com/AL2/%s.html",
 | 
			
		||||
						strings.Replace(advisory.AdvisoryID, "ALAS2", "ALAS", -1))
 | 
			
		||||
			} else {
 | 
			
		||||
				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.SUSEEnterpriseServer:
 | 
			
		||||
		links["SUSE-CVE"] = "https://www.suse.com/security/cve/" + 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DistroAdvisories is a list of DistroAdvisory
 | 
			
		||||
type DistroAdvisories []DistroAdvisory
 | 
			
		||||
 | 
			
		||||
// AppendIfMissing appends if missing
 | 
			
		||||
func (advs *DistroAdvisories) AppendIfMissing(adv *DistroAdvisory) bool {
 | 
			
		||||
	for _, a := range *advs {
 | 
			
		||||
		if a.AdvisoryID == adv.AdvisoryID {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	*advs = append(*advs, *adv)
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DistroAdvisory has Amazon Linux, RHEL, FreeBSD Security Advisory information.
 | 
			
		||||
type DistroAdvisory struct {
 | 
			
		||||
	AdvisoryID  string    `json:"advisoryID"`
 | 
			
		||||
	Severity    string    `json:"severity"`
 | 
			
		||||
	Issued      time.Time `json:"issued"`
 | 
			
		||||
	Updated     time.Time `json:"updated"`
 | 
			
		||||
	Description string    `json:"description"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Exploit :
 | 
			
		||||
type Exploit struct {
 | 
			
		||||
	ExploitType  exploitmodels.ExploitType `json:"exploitType"`
 | 
			
		||||
	ID           string                    `json:"id"`
 | 
			
		||||
	URL          string                    `json:"url"`
 | 
			
		||||
	Description  string                    `json:"description"`
 | 
			
		||||
	DocumentURL  *string                   `json:"documentURL,omitempty"`
 | 
			
		||||
	ShellCodeURL *string                   `json:"shellCodeURL,omitempty"`
 | 
			
		||||
	BinaryURL    *string                   `json:"binaryURL,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AlertDict has target cve's JPCERT and USCERT alert data
 | 
			
		||||
type AlertDict struct {
 | 
			
		||||
	Ja []alert.Alert `json:"ja"`
 | 
			
		||||
	En []alert.Alert `json:"en"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasAlert returns whether or not it has En or Ja entries.
 | 
			
		||||
func (a AlertDict) HasAlert() bool {
 | 
			
		||||
	return len(a.En) != 0 || len(a.Ja) != 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FormatSource returns which source has this alert
 | 
			
		||||
func (a AlertDict) FormatSource() string {
 | 
			
		||||
	s := []string{}
 | 
			
		||||
	if len(a.En) != 0 {
 | 
			
		||||
		s = append(s, "USCERT")
 | 
			
		||||
	}
 | 
			
		||||
	if len(a.Ja) != 0 {
 | 
			
		||||
		s = append(s, "JPCERT")
 | 
			
		||||
	}
 | 
			
		||||
	return strings.Join(s, "/")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Confidences is a list of Confidence
 | 
			
		||||
type Confidences []Confidence
 | 
			
		||||
 | 
			
		||||
// AppendIfMissing appends confidence to the list if missiong
 | 
			
		||||
func (cs *Confidences) AppendIfMissing(confidence Confidence) {
 | 
			
		||||
	for _, c := range *cs {
 | 
			
		||||
		if c.DetectionMethod == confidence.DetectionMethod {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	*cs = append(*cs, confidence)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SortByConfident sorts Confidences
 | 
			
		||||
func (cs Confidences) SortByConfident() Confidences {
 | 
			
		||||
	sort.Slice(cs, func(i, j int) bool {
 | 
			
		||||
		return cs[i].SortOrder < cs[j].SortOrder
 | 
			
		||||
	})
 | 
			
		||||
	return cs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Confidence is a ranking how confident the CVE-ID was deteted correctly
 | 
			
		||||
// Score: 0 - 100
 | 
			
		||||
type Confidence struct {
 | 
			
		||||
	Score           int             `json:"score"`
 | 
			
		||||
	DetectionMethod DetectionMethod `json:"detectionMethod"`
 | 
			
		||||
	SortOrder       int             `json:"-"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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"
 | 
			
		||||
 | 
			
		||||
	// RedHatAPIStr is a String representation of RedHatAPIMatch
 | 
			
		||||
	RedHatAPIStr = "RedHatAPIMatch"
 | 
			
		||||
 | 
			
		||||
	// DebianSecurityTrackerMatchStr is a String representation of DebianSecurityTrackerMatch
 | 
			
		||||
	DebianSecurityTrackerMatchStr = "DebianSecurityTrackerMatch"
 | 
			
		||||
 | 
			
		||||
	// ChangelogExactMatchStr is a String representation of ChangelogExactMatch
 | 
			
		||||
	ChangelogExactMatchStr = "ChangelogExactMatch"
 | 
			
		||||
 | 
			
		||||
	// ChangelogLenientMatchStr is a String representation of ChangelogLenientMatch
 | 
			
		||||
	ChangelogLenientMatchStr = "ChangelogLenientMatch"
 | 
			
		||||
 | 
			
		||||
	// GitHubMatchStr is a String representation of GitHubMatch
 | 
			
		||||
	GitHubMatchStr = "GitHubMatch"
 | 
			
		||||
 | 
			
		||||
	// WPVulnDBMatchStr is a String representation of WordPress VulnDB scanning
 | 
			
		||||
	WPVulnDBMatchStr = "WPVulnDBMatch"
 | 
			
		||||
 | 
			
		||||
	// 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, 1}
 | 
			
		||||
 | 
			
		||||
	// YumUpdateSecurityMatch is a ranking how confident the CVE-ID was deteted correctly
 | 
			
		||||
	YumUpdateSecurityMatch = Confidence{100, YumUpdateSecurityMatchStr, 2}
 | 
			
		||||
 | 
			
		||||
	// PkgAuditMatch is a ranking how confident the CVE-ID was deteted correctly
 | 
			
		||||
	PkgAuditMatch = Confidence{100, PkgAuditMatchStr, 2}
 | 
			
		||||
 | 
			
		||||
	// OvalMatch is a ranking how confident the CVE-ID was deteted correctly
 | 
			
		||||
	OvalMatch = Confidence{100, OvalMatchStr, 0}
 | 
			
		||||
 | 
			
		||||
	// RedHatAPIMatch ranking how confident the CVE-ID was deteted correctly
 | 
			
		||||
	RedHatAPIMatch = Confidence{100, RedHatAPIStr, 0}
 | 
			
		||||
 | 
			
		||||
	// DebianSecurityTrackerMatch ranking how confident the CVE-ID was deteted correctly
 | 
			
		||||
	DebianSecurityTrackerMatch = Confidence{100, DebianSecurityTrackerMatchStr, 0}
 | 
			
		||||
 | 
			
		||||
	// ChangelogExactMatch is a ranking how confident the CVE-ID was deteted correctly
 | 
			
		||||
	ChangelogExactMatch = Confidence{95, ChangelogExactMatchStr, 3}
 | 
			
		||||
 | 
			
		||||
	// ChangelogLenientMatch is a ranking how confident the CVE-ID was deteted correctly
 | 
			
		||||
	ChangelogLenientMatch = Confidence{50, ChangelogLenientMatchStr, 4}
 | 
			
		||||
 | 
			
		||||
	// GitHubMatch is a ranking how confident the CVE-ID was deteted correctly
 | 
			
		||||
	GitHubMatch = Confidence{97, GitHubMatchStr, 2}
 | 
			
		||||
 | 
			
		||||
	// WPVulnDBMatch is a ranking how confident the CVE-ID was deteted correctly
 | 
			
		||||
	WPVulnDBMatch = Confidence{100, WPVulnDBMatchStr, 0}
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										1098
									
								
								models/vulninfos_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										88
									
								
								models/wordpress.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,88 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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
 | 
			
		||||
 | 
			
		||||
// WordPressPackages has Core version, plugins and themes.
 | 
			
		||||
type WordPressPackages []WpPackage
 | 
			
		||||
 | 
			
		||||
// CoreVersion returns the core version of the installed WordPress
 | 
			
		||||
func (w WordPressPackages) CoreVersion() string {
 | 
			
		||||
	for _, p := range w {
 | 
			
		||||
		if p.Type == WPCore {
 | 
			
		||||
			return p.Version
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Plugins returns a slice of plugins of the installed WordPress
 | 
			
		||||
func (w WordPressPackages) Plugins() (ps []WpPackage) {
 | 
			
		||||
	for _, p := range w {
 | 
			
		||||
		if p.Type == WPPlugin {
 | 
			
		||||
			ps = append(ps, p)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Themes returns a slice of themes of the installed WordPress
 | 
			
		||||
func (w WordPressPackages) Themes() (ps []WpPackage) {
 | 
			
		||||
	for _, p := range w {
 | 
			
		||||
		if p.Type == WPTheme {
 | 
			
		||||
			ps = append(ps, p)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Find searches by specified name
 | 
			
		||||
func (w WordPressPackages) Find(name string) (ps *WpPackage, found bool) {
 | 
			
		||||
	for _, p := range w {
 | 
			
		||||
		if p.Name == name {
 | 
			
		||||
			return &p, true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// WPCore is a type `core` in WPPackage struct
 | 
			
		||||
	WPCore = "core"
 | 
			
		||||
	// WPPlugin is a type `plugin` in WPPackage struct
 | 
			
		||||
	WPPlugin = "plugin"
 | 
			
		||||
	// WPTheme is a type `theme` in WPPackage struct
 | 
			
		||||
	WPTheme = "theme"
 | 
			
		||||
 | 
			
		||||
	// Inactive is a inactive status in WPPackage struct
 | 
			
		||||
	Inactive = "inactive"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// WpPackage has a details of plugin and theme
 | 
			
		||||
type WpPackage struct {
 | 
			
		||||
	Name    string `json:"name,omitempty"`
 | 
			
		||||
	Status  string `json:"status,omitempty"` // active, inactive or must-use
 | 
			
		||||
	Update  string `json:"update,omitempty"` // available or none
 | 
			
		||||
	Version string `json:"version,omitempty"`
 | 
			
		||||
	Type    string `json:"type,omitempty"` // core, plugin, theme
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WpPackageFixStatus is used in Vulninfo.WordPress
 | 
			
		||||
type WpPackageFixStatus struct {
 | 
			
		||||
	Name    string `json:"name,omitempty"`
 | 
			
		||||
	FixedIn string `json:"fixedIn,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										74
									
								
								oval/alpine.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,74 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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"
 | 
			
		||||
	"github.com/kotakanbe/goval-dictionary/db"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Alpine is the struct of Alpine Linux
 | 
			
		||||
type Alpine struct {
 | 
			
		||||
	Base
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewAlpine creates OVAL client for SUSE
 | 
			
		||||
func NewAlpine() Alpine {
 | 
			
		||||
	return Alpine{
 | 
			
		||||
		Base{
 | 
			
		||||
			family: config.Alpine,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillWithOval returns scan result after updating CVE info by OVAL
 | 
			
		||||
func (o Alpine) FillWithOval(driver db.DB, r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
	var relatedDefs ovalResult
 | 
			
		||||
	if config.Conf.OvalDict.IsFetchViaHTTP() {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, defPacks := range relatedDefs.entries {
 | 
			
		||||
		o.update(r, defPacks)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return len(relatedDefs.entries), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o Alpine) update(r *models.ScanResult, defPacks defPacks) {
 | 
			
		||||
	cveID := defPacks.def.Advisory.Cves[0].CveID
 | 
			
		||||
	vinfo, ok := r.ScannedCves[cveID]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		util.Log.Debugf("%s is newly detected by OVAL", cveID)
 | 
			
		||||
		vinfo = models.VulnInfo{
 | 
			
		||||
			CveID:       cveID,
 | 
			
		||||
			Confidences: []models.Confidence{models.OvalMatch},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vinfo.AffectedPackages = defPacks.toPackStatuses()
 | 
			
		||||
	vinfo.AffectedPackages.Sort()
 | 
			
		||||
	r.ScannedCves[cveID] = vinfo
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										351
									
								
								oval/debian.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,351 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/kotakanbe/goval-dictionary/db"
 | 
			
		||||
	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,
 | 
			
		||||
			Confidences: []models.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{}
 | 
			
		||||
		}
 | 
			
		||||
		vinfo.Confidences.AppendIfMissing(models.OvalMatch)
 | 
			
		||||
		cveContents[ctype] = ovalContent
 | 
			
		||||
		vinfo.CveContents = cveContents
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
 | 
			
		||||
	for _, pack := range vinfo.AffectedPackages {
 | 
			
		||||
		defPacks.actuallyAffectedPackNames[pack.Name] = pack.NotFixedYet
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// update notFixedYet of SrcPackage
 | 
			
		||||
	for binName := range defPacks.actuallyAffectedPackNames {
 | 
			
		||||
		if srcPack, ok := r.SrcPackages.FindByBinName(binName); ok {
 | 
			
		||||
			for _, p := range defPacks.def.AffectedPacks {
 | 
			
		||||
				if p.Name == srcPack.Name {
 | 
			
		||||
					defPacks.actuallyAffectedPackNames[binName] = p.NotFixedYet
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vinfo.AffectedPackages = defPacks.toPackStatuses()
 | 
			
		||||
	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,
 | 
			
		||||
		Cvss2Severity: def.Advisory.Severity,
 | 
			
		||||
		Cvss3Severity: 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(driver db.DB, r *models.ScanResult) (nCVEs int, 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 == "" {
 | 
			
		||||
		newVer := ""
 | 
			
		||||
		if p, ok := r.Packages[linuxImage]; ok {
 | 
			
		||||
			newVer = p.NewVersion
 | 
			
		||||
		}
 | 
			
		||||
		r.Packages["linux"] = models.Package{
 | 
			
		||||
			Name:       "linux",
 | 
			
		||||
			Version:    r.RunningKernel.Version,
 | 
			
		||||
			NewVersion: newVer,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var relatedDefs ovalResult
 | 
			
		||||
	if config.Conf.OvalDict.IsFetchViaHTTP() {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	delete(r.Packages, "linux")
 | 
			
		||||
 | 
			
		||||
	for _, defPacks := range relatedDefs.entries {
 | 
			
		||||
		// Remove "linux" added above for oval search
 | 
			
		||||
		// linux is not a real package name (key of affected packages in OVAL)
 | 
			
		||||
		if notFixedYet, ok := defPacks.actuallyAffectedPackNames["linux"]; ok {
 | 
			
		||||
			defPacks.actuallyAffectedPackNames[linuxImage] = notFixedYet
 | 
			
		||||
			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 len(relatedDefs.entries), 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(driver db.DB, r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
	switch major(r.Release) {
 | 
			
		||||
	case "14":
 | 
			
		||||
		kernelNamesInOval := []string{
 | 
			
		||||
			"linux",
 | 
			
		||||
			"linux-aws",
 | 
			
		||||
			"linux-azure",
 | 
			
		||||
			"linux-firmware",
 | 
			
		||||
			"linux-lts-utopic",
 | 
			
		||||
			"linux-lts-vivid",
 | 
			
		||||
			"linux-lts-wily",
 | 
			
		||||
			"linux-lts-xenial",
 | 
			
		||||
		}
 | 
			
		||||
		return o.fillWithOval(driver, r, kernelNamesInOval)
 | 
			
		||||
	case "16":
 | 
			
		||||
		kernelNamesInOval := []string{
 | 
			
		||||
			"linux-image-aws",
 | 
			
		||||
			"linux-image-aws-hwe",
 | 
			
		||||
			"linux-image-azure",
 | 
			
		||||
			"linux-image-extra-virtual",
 | 
			
		||||
			"linux-image-extra-virtual-lts-utopic",
 | 
			
		||||
			"linux-image-extra-virtual-lts-vivid",
 | 
			
		||||
			"linux-image-extra-virtual-lts-wily",
 | 
			
		||||
			"linux-image-extra-virtual-lts-xenial",
 | 
			
		||||
			"linux-image-gcp",
 | 
			
		||||
			"linux-image-generic-lpae",
 | 
			
		||||
			"linux-image-generic-lpae-hwe-16.04",
 | 
			
		||||
			"linux-image-generic-lpae-lts-utopic",
 | 
			
		||||
			"linux-image-generic-lpae-lts-vivid",
 | 
			
		||||
			"linux-image-generic-lpae-lts-wily",
 | 
			
		||||
			"linux-image-generic-lpae-lts-xenial",
 | 
			
		||||
			"linux-image-generic-lts-utopic",
 | 
			
		||||
			"linux-image-generic-lts-vivid",
 | 
			
		||||
			"linux-image-generic-lts-wily",
 | 
			
		||||
			"linux-image-generic-lts-xenial",
 | 
			
		||||
			"linux-image-gke",
 | 
			
		||||
			"linux-image-hwe-generic-trusty",
 | 
			
		||||
			"linux-image-hwe-virtual-trusty",
 | 
			
		||||
			"linux-image-kvm",
 | 
			
		||||
			"linux-image-lowlatency",
 | 
			
		||||
			"linux-image-lowlatency-lts-utopic",
 | 
			
		||||
			"linux-image-lowlatency-lts-vivid",
 | 
			
		||||
			"linux-image-lowlatency-lts-wily",
 | 
			
		||||
		}
 | 
			
		||||
		return o.fillWithOval(driver, r, kernelNamesInOval)
 | 
			
		||||
	case "18":
 | 
			
		||||
		kernelNamesInOval := []string{
 | 
			
		||||
			"linux-image-aws",
 | 
			
		||||
			"linux-image-azure",
 | 
			
		||||
			"linux-image-extra-virtual",
 | 
			
		||||
			"linux-image-gcp",
 | 
			
		||||
			"linux-image-generic-lpae",
 | 
			
		||||
			"linux-image-kvm",
 | 
			
		||||
			"linux-image-lowlatency",
 | 
			
		||||
			"linux-image-oem",
 | 
			
		||||
			"linux-image-oracle",
 | 
			
		||||
			"linux-image-raspi2",
 | 
			
		||||
			"linux-image-snapdragon",
 | 
			
		||||
			"linux-image-virtual",
 | 
			
		||||
		}
 | 
			
		||||
		return o.fillWithOval(driver, r, kernelNamesInOval)
 | 
			
		||||
	}
 | 
			
		||||
	return 0, fmt.Errorf("Ubuntu %s is not support for now", r.Release)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o Ubuntu) fillWithOval(driver db.DB, r *models.ScanResult, kernelNamesInOval []string) (nCVEs int, err error) {
 | 
			
		||||
	// kernel names in OVAL except for linux-image-generic
 | 
			
		||||
	linuxImage := "linux-image-" + r.RunningKernel.Release
 | 
			
		||||
	runningKernelVersion := ""
 | 
			
		||||
	kernelPkgInOVAL := ""
 | 
			
		||||
	isOVALKernelPkgAdded := true
 | 
			
		||||
	unusedKernels := []models.Package{}
 | 
			
		||||
 | 
			
		||||
	if r.Container.ContainerID == "" {
 | 
			
		||||
		if v, ok := r.Packages[linuxImage]; ok {
 | 
			
		||||
			runningKernelVersion = v.Version
 | 
			
		||||
		} else {
 | 
			
		||||
			util.Log.Warnf("Unable to detect vulns of running kernel because the version of the runnning kernel is unknown. server: %s",
 | 
			
		||||
				r.ServerName)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, n := range kernelNamesInOval {
 | 
			
		||||
			if p, ok := r.Packages[n]; ok {
 | 
			
		||||
				kernelPkgInOVAL = p.Name
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// remove unused kernels from packages to prevent detecting vulns of unused kernel
 | 
			
		||||
		for _, n := range kernelNamesInOval {
 | 
			
		||||
			if v, ok := r.Packages[n]; ok {
 | 
			
		||||
				unusedKernels = append(unusedKernels, v)
 | 
			
		||||
				delete(r.Packages, n)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if kernelPkgInOVAL == "" {
 | 
			
		||||
			if r.Release == "14" {
 | 
			
		||||
				kernelPkgInOVAL = "linux"
 | 
			
		||||
			} else if _, ok := r.Packages["linux-image-generic"]; !ok {
 | 
			
		||||
				util.Log.Warnf("The OVAL name of the running kernel image %s is not found. So vulns of linux-image-generic wll be detected. server: %s",
 | 
			
		||||
					r.RunningKernel.Version, r.ServerName)
 | 
			
		||||
				kernelPkgInOVAL = "linux-image-generic"
 | 
			
		||||
			} else {
 | 
			
		||||
				isOVALKernelPkgAdded = false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if runningKernelVersion != "" {
 | 
			
		||||
			r.Packages[kernelPkgInOVAL] = models.Package{
 | 
			
		||||
				Name:    kernelPkgInOVAL,
 | 
			
		||||
				Version: runningKernelVersion,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var relatedDefs ovalResult
 | 
			
		||||
	if config.Conf.OvalDict.IsFetchViaHTTP() {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if isOVALKernelPkgAdded {
 | 
			
		||||
		delete(r.Packages, kernelPkgInOVAL)
 | 
			
		||||
	}
 | 
			
		||||
	for _, p := range unusedKernels {
 | 
			
		||||
		r.Packages[p.Name] = p
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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 nfy, ok := defPacks.actuallyAffectedPackNames[kernelPkgInOVAL]; isOVALKernelPkgAdded && ok {
 | 
			
		||||
			defPacks.actuallyAffectedPackNames[linuxImage] = nfy
 | 
			
		||||
			delete(defPacks.actuallyAffectedPackNames, kernelPkgInOVAL)
 | 
			
		||||
			for i, p := range defPacks.def.AffectedPacks {
 | 
			
		||||
				if p.Name == kernelPkgInOVAL {
 | 
			
		||||
					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 len(relatedDefs.entries), nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										79
									
								
								oval/debian_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,79 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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.PackageFixStatuses{
 | 
			
		||||
							{Name: "packA"},
 | 
			
		||||
							{Name: "packC"},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			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.PackageFixStatuses{
 | 
			
		||||
							{Name: "packA"},
 | 
			
		||||
							{Name: "packB", NotFixedYet: true},
 | 
			
		||||
							{Name: "packC"},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										116
									
								
								oval/oval.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,116 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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"
 | 
			
		||||
 | 
			
		||||
	cnf "github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/kotakanbe/goval-dictionary/db"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Client is the interface of OVAL client.
 | 
			
		||||
type Client interface {
 | 
			
		||||
	CheckHTTPHealth() error
 | 
			
		||||
	FillWithOval(db.DB, *models.ScanResult) (int, error)
 | 
			
		||||
 | 
			
		||||
	// CheckIfOvalFetched checks if oval entries are in DB by family, release.
 | 
			
		||||
	CheckIfOvalFetched(db.DB, string, string) (bool, error)
 | 
			
		||||
	CheckIfOvalFresh(db.DB, string, string) (bool, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Base is a base struct
 | 
			
		||||
type Base struct {
 | 
			
		||||
	family string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckHTTPHealth do health check
 | 
			
		||||
func (b Base) CheckHTTPHealth() error {
 | 
			
		||||
	if !cnf.Conf.OvalDict.IsFetchViaHTTP() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	url := fmt.Sprintf("%s/health", cnf.Conf.OvalDict.URL)
 | 
			
		||||
	var errs []error
 | 
			
		||||
	var resp *http.Response
 | 
			
		||||
	resp, _, errs = gorequest.New().Get(url).End()
 | 
			
		||||
	//  resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
 | 
			
		||||
	//  resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
 | 
			
		||||
	if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
		return xerrors.Errorf("Failed to request to OVAL server. url: %s, errs: %w",
 | 
			
		||||
			url, errs)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckIfOvalFetched checks if oval entries are in DB by family, release.
 | 
			
		||||
func (b Base) CheckIfOvalFetched(driver db.DB, osFamily, release string) (fetched bool, err error) {
 | 
			
		||||
	if !cnf.Conf.OvalDict.IsFetchViaHTTP() {
 | 
			
		||||
		count, err := driver.CountDefs(osFamily, release)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, xerrors.Errorf("Failed to count OVAL defs: %s, %s, %w", osFamily, release, err)
 | 
			
		||||
		}
 | 
			
		||||
		return 0 < count, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	url, _ := util.URLPathJoin(cnf.Conf.OvalDict.URL, "count", osFamily, release)
 | 
			
		||||
	resp, body, errs := gorequest.New().Get(url).End()
 | 
			
		||||
	if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
		return false, xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %w", url, resp, errs)
 | 
			
		||||
	}
 | 
			
		||||
	count := 0
 | 
			
		||||
	if err := json.Unmarshal([]byte(body), &count); err != nil {
 | 
			
		||||
		return false, xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
 | 
			
		||||
	}
 | 
			
		||||
	return 0 < count, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckIfOvalFresh checks if oval entries are fresh enough
 | 
			
		||||
func (b Base) CheckIfOvalFresh(driver db.DB, osFamily, release string) (ok bool, err error) {
 | 
			
		||||
	var lastModified time.Time
 | 
			
		||||
	if !cnf.Conf.OvalDict.IsFetchViaHTTP() {
 | 
			
		||||
		lastModified = driver.GetLastModified(osFamily, release)
 | 
			
		||||
	} else {
 | 
			
		||||
		url, _ := util.URLPathJoin(cnf.Conf.OvalDict.URL, "lastmodified", osFamily, release)
 | 
			
		||||
		resp, body, errs := gorequest.New().Get(url).End()
 | 
			
		||||
		if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
			return false, xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %w", url, resp, errs)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := json.Unmarshal([]byte(body), &lastModified); err != nil {
 | 
			
		||||
			return false, xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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, release, lastModified)
 | 
			
		||||
		return false, nil
 | 
			
		||||
	}
 | 
			
		||||
	util.Log.Infof("OVAL is fresh: %s %s ", osFamily, release)
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										312
									
								
								oval/redhat.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,312 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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"
 | 
			
		||||
	"github.com/kotakanbe/goval-dictionary/db"
 | 
			
		||||
	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(driver db.DB, r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
	var relatedDefs ovalResult
 | 
			
		||||
	if config.Conf.OvalDict.IsFetchViaHTTP() {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, defPacks := range relatedDefs.entries {
 | 
			
		||||
		nCVEs += 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 nCVEs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var kernelRelatedPackNames = map[string]bool{
 | 
			
		||||
	"kernel":                  true,
 | 
			
		||||
	"kernel-aarch64":          true,
 | 
			
		||||
	"kernel-abi-whitelists":   true,
 | 
			
		||||
	"kernel-bootwrapper":      true,
 | 
			
		||||
	"kernel-debug":            true,
 | 
			
		||||
	"kernel-debug-devel":      true,
 | 
			
		||||
	"kernel-devel":            true,
 | 
			
		||||
	"kernel-doc":              true,
 | 
			
		||||
	"kernel-headers":          true,
 | 
			
		||||
	"kernel-kdump":            true,
 | 
			
		||||
	"kernel-kdump-devel":      true,
 | 
			
		||||
	"kernel-rt":               true,
 | 
			
		||||
	"kernel-rt-debug":         true,
 | 
			
		||||
	"kernel-rt-debug-devel":   true,
 | 
			
		||||
	"kernel-rt-debug-kvm":     true,
 | 
			
		||||
	"kernel-rt-devel":         true,
 | 
			
		||||
	"kernel-rt-doc":           true,
 | 
			
		||||
	"kernel-rt-kvm":           true,
 | 
			
		||||
	"kernel-rt-trace":         true,
 | 
			
		||||
	"kernel-rt-trace-devel":   true,
 | 
			
		||||
	"kernel-rt-trace-kvm":     true,
 | 
			
		||||
	"kernel-rt-virt":          true,
 | 
			
		||||
	"kernel-rt-virt-devel":    true,
 | 
			
		||||
	"kernel-tools":            true,
 | 
			
		||||
	"kernel-tools-libs":       true,
 | 
			
		||||
	"kernel-tools-libs-devel": true,
 | 
			
		||||
	"perf":                    true,
 | 
			
		||||
	"python-perf":             true,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o RedHatBase) update(r *models.ScanResult, defPacks defPacks) (nCVEs int) {
 | 
			
		||||
	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,
 | 
			
		||||
				Confidences: models.Confidences{models.OvalMatch},
 | 
			
		||||
				CveContents: models.NewCveContents(ovalContent),
 | 
			
		||||
			}
 | 
			
		||||
			nCVEs++
 | 
			
		||||
		} else {
 | 
			
		||||
			cveContents := vinfo.CveContents
 | 
			
		||||
			if v, ok := vinfo.CveContents[ctype]; ok {
 | 
			
		||||
				if v.LastModified.After(ovalContent.LastModified) {
 | 
			
		||||
					util.Log.Debugf("%s, OvalID: %d ignroed: ",
 | 
			
		||||
						cve.CveID, defPacks.def.ID)
 | 
			
		||||
				} else {
 | 
			
		||||
					util.Log.Debugf("%s OVAL will be overwritten", cve.CveID)
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				util.Log.Debugf("%s also detected by OVAL", cve.CveID)
 | 
			
		||||
				cveContents = models.CveContents{}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			vinfo.Confidences.AppendIfMissing(models.OvalMatch)
 | 
			
		||||
			cveContents[ctype] = ovalContent
 | 
			
		||||
			vinfo.CveContents = cveContents
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		vinfo.DistroAdvisories.AppendIfMissing(
 | 
			
		||||
			o.convertToDistroAdvisory(&defPacks.def))
 | 
			
		||||
 | 
			
		||||
		// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
 | 
			
		||||
		for _, pack := range vinfo.AffectedPackages {
 | 
			
		||||
			if nfy, ok := defPacks.actuallyAffectedPackNames[pack.Name]; !ok {
 | 
			
		||||
				defPacks.actuallyAffectedPackNames[pack.Name] = pack.NotFixedYet
 | 
			
		||||
			} else if nfy {
 | 
			
		||||
				defPacks.actuallyAffectedPackNames[pack.Name] = true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		vinfo.AffectedPackages = defPacks.toPackStatuses()
 | 
			
		||||
		vinfo.AffectedPackages.Sort()
 | 
			
		||||
		r.ScannedCves[cve.CveID] = vinfo
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o RedHatBase) convertToDistroAdvisory(def *ovalmodels.Definition) *models.DistroAdvisory {
 | 
			
		||||
	advisoryID := def.Title
 | 
			
		||||
	if o.family == config.RedHat || o.family == config.CentOS {
 | 
			
		||||
		ss := strings.Fields(def.Title)
 | 
			
		||||
		advisoryID = strings.TrimSuffix(ss[0], ":")
 | 
			
		||||
	}
 | 
			
		||||
	return &models.DistroAdvisory{
 | 
			
		||||
		AdvisoryID:  advisoryID,
 | 
			
		||||
		Severity:    def.Advisory.Severity,
 | 
			
		||||
		Issued:      def.Advisory.Issued,
 | 
			
		||||
		Updated:     def.Advisory.Updated,
 | 
			
		||||
		Description: def.Description,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		sev2, sev3 := "", ""
 | 
			
		||||
		if score2 == 0 {
 | 
			
		||||
			sev2 = severity
 | 
			
		||||
		}
 | 
			
		||||
		if score3 == 0 {
 | 
			
		||||
			sev3 = severity
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// CWE-ID in RedHat OVAL may have multiple cweIDs separated by space
 | 
			
		||||
		cwes := strings.Fields(cve.Cwe)
 | 
			
		||||
 | 
			
		||||
		return &models.CveContent{
 | 
			
		||||
			Type:          models.NewCveContentType(o.family),
 | 
			
		||||
			CveID:         cve.CveID,
 | 
			
		||||
			Title:         def.Title,
 | 
			
		||||
			Summary:       def.Description,
 | 
			
		||||
			Cvss2Score:    score2,
 | 
			
		||||
			Cvss2Vector:   vec2,
 | 
			
		||||
			Cvss2Severity: sev2,
 | 
			
		||||
			Cvss3Score:    score3,
 | 
			
		||||
			Cvss3Vector:   vec3,
 | 
			
		||||
			Cvss3Severity: sev3,
 | 
			
		||||
			References:    refs,
 | 
			
		||||
			CweIDs:        cwes,
 | 
			
		||||
			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:], "/")
 | 
			
		||||
	}
 | 
			
		||||
	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, fmt.Sprintf("CVSS:3.0/%s", ss[1])
 | 
			
		||||
	}
 | 
			
		||||
	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 Oracle OVAL
 | 
			
		||||
type Oracle struct {
 | 
			
		||||
	RedHatBase
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewOracle creates OVAL client for Oracle
 | 
			
		||||
func NewOracle() Oracle {
 | 
			
		||||
	return Oracle{
 | 
			
		||||
		RedHatBase{
 | 
			
		||||
			Base{
 | 
			
		||||
				family: config.Oracle,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Amazon is the interface for RedhatBase OVAL
 | 
			
		||||
type Amazon struct {
 | 
			
		||||
	// Base
 | 
			
		||||
	RedHatBase
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewAmazon creates OVAL client for Amazon Linux
 | 
			
		||||
func NewAmazon() Amazon {
 | 
			
		||||
	return Amazon{
 | 
			
		||||
		RedHatBase{
 | 
			
		||||
			Base{
 | 
			
		||||
				family: config.Amazon,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										148
									
								
								oval/redhat_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,148 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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: "CVSS:3.0/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.PackageFixStatuses{
 | 
			
		||||
							{Name: "packA"},
 | 
			
		||||
							{Name: "packB", NotFixedYet: false},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			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.PackageFixStatuses{
 | 
			
		||||
							{Name: "packA"},
 | 
			
		||||
							{Name: "packB", NotFixedYet: true},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										118
									
								
								oval/suse.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,118 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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"
 | 
			
		||||
	"github.com/kotakanbe/goval-dictionary/db"
 | 
			
		||||
	ovalmodels "github.com/kotakanbe/goval-dictionary/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SUSE is the struct of SUSE Linux
 | 
			
		||||
type SUSE struct {
 | 
			
		||||
	Base
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewSUSE creates OVAL client for SUSE
 | 
			
		||||
func NewSUSE() SUSE {
 | 
			
		||||
	// TODO implement other family
 | 
			
		||||
	return SUSE{
 | 
			
		||||
		Base{
 | 
			
		||||
			family: config.SUSEEnterpriseServer,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillWithOval returns scan result after updating CVE info by OVAL
 | 
			
		||||
func (o SUSE) FillWithOval(driver db.DB, r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
	var relatedDefs ovalResult
 | 
			
		||||
	if config.Conf.OvalDict.IsFetchViaHTTP() {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, defPacks := range relatedDefs.entries {
 | 
			
		||||
		o.update(r, defPacks)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, vuln := range r.ScannedCves {
 | 
			
		||||
		if cont, ok := vuln.CveContents[models.SUSE]; ok {
 | 
			
		||||
			cont.SourceLink = "https://security-tracker.debian.org/tracker/" + cont.CveID
 | 
			
		||||
			vuln.CveContents[models.SUSE] = cont
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return len(relatedDefs.entries), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o SUSE) update(r *models.ScanResult, defPacks defPacks) {
 | 
			
		||||
	ovalContent := *o.convertToModel(&defPacks.def)
 | 
			
		||||
	ovalContent.Type = models.NewCveContentType(o.family)
 | 
			
		||||
	vinfo, ok := r.ScannedCves[defPacks.def.Title]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		util.Log.Debugf("%s is newly detected by OVAL", defPacks.def.Title)
 | 
			
		||||
		vinfo = models.VulnInfo{
 | 
			
		||||
			CveID:       defPacks.def.Title,
 | 
			
		||||
			Confidences: models.Confidences{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.Title)
 | 
			
		||||
		} else {
 | 
			
		||||
			util.Log.Debugf("%s is also detected by OVAL", defPacks.def.Title)
 | 
			
		||||
			cveContents = models.CveContents{}
 | 
			
		||||
		}
 | 
			
		||||
		vinfo.Confidences.AppendIfMissing(models.OvalMatch)
 | 
			
		||||
		cveContents[ctype] = ovalContent
 | 
			
		||||
		vinfo.CveContents = cveContents
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
 | 
			
		||||
	for _, pack := range vinfo.AffectedPackages {
 | 
			
		||||
		defPacks.actuallyAffectedPackNames[pack.Name] = pack.NotFixedYet
 | 
			
		||||
	}
 | 
			
		||||
	vinfo.AffectedPackages = defPacks.toPackStatuses()
 | 
			
		||||
	vinfo.AffectedPackages.Sort()
 | 
			
		||||
	r.ScannedCves[defPacks.def.Title] = vinfo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o SUSE) 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.Title,
 | 
			
		||||
		Title:      def.Title,
 | 
			
		||||
		Summary:    def.Description,
 | 
			
		||||
		References: refs,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										374
									
								
								oval/util.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,374 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , 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"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"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"
 | 
			
		||||
	ovalmodels "github.com/kotakanbe/goval-dictionary/models"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ovalResult struct {
 | 
			
		||||
	entries []defPacks
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type defPacks struct {
 | 
			
		||||
	def ovalmodels.Definition
 | 
			
		||||
 | 
			
		||||
	// BinaryPackageName : NotFixedYet
 | 
			
		||||
	actuallyAffectedPackNames map[string]bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e defPacks) toPackStatuses() (ps models.PackageFixStatuses) {
 | 
			
		||||
	for name, notFixedYet := range e.actuallyAffectedPackNames {
 | 
			
		||||
		ps = append(ps, models.PackageFixStatus{
 | 
			
		||||
			Name:        name,
 | 
			
		||||
			NotFixedYet: notFixedYet,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *ovalResult) upsert(def ovalmodels.Definition, packName string, notFixedYet bool) (upserted bool) {
 | 
			
		||||
	// alpine's entry is empty since Alpine secdb is not OVAL format
 | 
			
		||||
	if def.DefinitionID != "" {
 | 
			
		||||
		for i, entry := range e.entries {
 | 
			
		||||
			if entry.def.DefinitionID == def.DefinitionID {
 | 
			
		||||
				e.entries[i].actuallyAffectedPackNames[packName] = notFixedYet
 | 
			
		||||
				return true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	e.entries = append(e.entries, defPacks{
 | 
			
		||||
		def:                       def,
 | 
			
		||||
		actuallyAffectedPackNames: map[string]bool{packName: notFixedYet},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type request struct {
 | 
			
		||||
	packName          string
 | 
			
		||||
	versionRelease    string
 | 
			
		||||
	newVersionRelease string
 | 
			
		||||
	arch              string
 | 
			
		||||
	binaryPackNames   []string
 | 
			
		||||
	isSrcPack         bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type response struct {
 | 
			
		||||
	request request
 | 
			
		||||
	defs    []ovalmodels.Definition
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getDefsByPackNameViaHTTP fetches OVAL information via HTTP
 | 
			
		||||
func getDefsByPackNameViaHTTP(r *models.ScanResult) (
 | 
			
		||||
	relatedDefs ovalResult, err error) {
 | 
			
		||||
 | 
			
		||||
	nReq := len(r.Packages) + len(r.SrcPackages)
 | 
			
		||||
	reqChan := make(chan request, nReq)
 | 
			
		||||
	resChan := make(chan response, nReq)
 | 
			
		||||
	errChan := make(chan error, nReq)
 | 
			
		||||
	defer close(reqChan)
 | 
			
		||||
	defer close(resChan)
 | 
			
		||||
	defer close(errChan)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		for _, pack := range r.Packages {
 | 
			
		||||
			reqChan <- request{
 | 
			
		||||
				packName:          pack.Name,
 | 
			
		||||
				versionRelease:    pack.FormatVer(),
 | 
			
		||||
				newVersionRelease: pack.FormatVer(),
 | 
			
		||||
				isSrcPack:         false,
 | 
			
		||||
				arch:              pack.Arch,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		for _, pack := range r.SrcPackages {
 | 
			
		||||
			reqChan <- request{
 | 
			
		||||
				packName:        pack.Name,
 | 
			
		||||
				binaryPackNames: pack.BinaryNames,
 | 
			
		||||
				versionRelease:  pack.Version,
 | 
			
		||||
				isSrcPack:       true,
 | 
			
		||||
				// arch:            pack.Arch,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	concurrency := 10
 | 
			
		||||
	tasks := util.GenWorkers(concurrency)
 | 
			
		||||
	for i := 0; i < nReq; i++ {
 | 
			
		||||
		tasks <- func() {
 | 
			
		||||
			select {
 | 
			
		||||
			case req := <-reqChan:
 | 
			
		||||
				url, err := util.URLPathJoin(
 | 
			
		||||
					config.Conf.OvalDict.URL,
 | 
			
		||||
					"packs",
 | 
			
		||||
					r.Family,
 | 
			
		||||
					r.Release,
 | 
			
		||||
					req.packName,
 | 
			
		||||
				)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					errChan <- err
 | 
			
		||||
				} else {
 | 
			
		||||
					util.Log.Debugf("HTTP Request to %s", url)
 | 
			
		||||
					httpGet(url, req, resChan, errChan)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	timeout := time.After(2 * 60 * time.Second)
 | 
			
		||||
	var errs []error
 | 
			
		||||
	for i := 0; i < nReq; i++ {
 | 
			
		||||
		select {
 | 
			
		||||
		case res := <-resChan:
 | 
			
		||||
			for _, def := range res.defs {
 | 
			
		||||
				affected, notFixedYet := isOvalDefAffected(def, res.request, r.Family, r.RunningKernel)
 | 
			
		||||
				if !affected {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if res.request.isSrcPack {
 | 
			
		||||
					for _, n := range res.request.binaryPackNames {
 | 
			
		||||
						relatedDefs.upsert(def, n, false)
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					relatedDefs.upsert(def, res.request.packName, notFixedYet)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		case err := <-errChan:
 | 
			
		||||
			errs = append(errs, err)
 | 
			
		||||
		case <-timeout:
 | 
			
		||||
			return relatedDefs, xerrors.New("Timeout Fetching OVAL")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(errs) != 0 {
 | 
			
		||||
		return relatedDefs, xerrors.Errorf("Failed to fetch OVAL. err: %w", errs)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func httpGet(url string, req request, resChan chan<- response, errChan chan<- error) {
 | 
			
		||||
	var body string
 | 
			
		||||
	var errs []error
 | 
			
		||||
	var resp *http.Response
 | 
			
		||||
	count, retryMax := 0, 3
 | 
			
		||||
	f := func() (err error) {
 | 
			
		||||
		//  resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
 | 
			
		||||
		resp, body, errs = gorequest.New().Get(url).End()
 | 
			
		||||
		if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
			count++
 | 
			
		||||
			if count == retryMax {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			return xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %w", url, resp, errs)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	notify := func(err error, t time.Duration) {
 | 
			
		||||
		util.Log.Warnf("Failed to HTTP GET. retrying in %s seconds. err: %s", t, err)
 | 
			
		||||
	}
 | 
			
		||||
	err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		errChan <- xerrors.Errorf("HTTP Error %w", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if count == retryMax {
 | 
			
		||||
		errChan <- xerrors.New("HRetry count exceeded")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defs := []ovalmodels.Definition{}
 | 
			
		||||
	if err := json.Unmarshal([]byte(body), &defs); err != nil {
 | 
			
		||||
		errChan <- xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	resChan <- response{
 | 
			
		||||
		request: req,
 | 
			
		||||
		defs:    defs,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getDefsByPackNameFromOvalDB(driver db.DB, r *models.ScanResult) (relatedDefs ovalResult, err error) {
 | 
			
		||||
	requests := []request{}
 | 
			
		||||
	for _, pack := range r.Packages {
 | 
			
		||||
		requests = append(requests, request{
 | 
			
		||||
			packName:          pack.Name,
 | 
			
		||||
			versionRelease:    pack.FormatVer(),
 | 
			
		||||
			newVersionRelease: pack.FormatNewVer(),
 | 
			
		||||
			arch:              pack.Arch,
 | 
			
		||||
			isSrcPack:         false,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	for _, pack := range r.SrcPackages {
 | 
			
		||||
		requests = append(requests, request{
 | 
			
		||||
			packName:        pack.Name,
 | 
			
		||||
			binaryPackNames: pack.BinaryNames,
 | 
			
		||||
			versionRelease:  pack.Version,
 | 
			
		||||
			isSrcPack:       true,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, req := range requests {
 | 
			
		||||
		definitions, err := driver.GetByPackName(r.Release, req.packName, req.arch)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return relatedDefs, xerrors.Errorf("Failed to get %s OVAL info by package: %#v, err: %w", r.Family, req, err)
 | 
			
		||||
		}
 | 
			
		||||
		for _, def := range definitions {
 | 
			
		||||
			affected, notFixedYet := isOvalDefAffected(def, req, r.Family, r.RunningKernel)
 | 
			
		||||
			if !affected {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if req.isSrcPack {
 | 
			
		||||
				for _, n := range req.binaryPackNames {
 | 
			
		||||
					relatedDefs.upsert(def, n, false)
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				relatedDefs.upsert(def, req.packName, notFixedYet)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func major(version string) string {
 | 
			
		||||
	ss := strings.SplitN(version, ":", 2)
 | 
			
		||||
	ver := ""
 | 
			
		||||
	if len(ss) == 1 {
 | 
			
		||||
		ver = ss[0]
 | 
			
		||||
	} else {
 | 
			
		||||
		ver = ss[1]
 | 
			
		||||
	}
 | 
			
		||||
	return ver[0:strings.Index(ver, ".")]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isOvalDefAffected(def ovalmodels.Definition, req request, family string, running models.Kernel) (affected, notFixedYet bool) {
 | 
			
		||||
	for _, ovalPack := range def.AffectedPacks {
 | 
			
		||||
		if req.packName != ovalPack.Name {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if running.Release != "" {
 | 
			
		||||
			switch family {
 | 
			
		||||
			case config.RedHat, config.CentOS:
 | 
			
		||||
				// For kernel related packages, ignore OVAL information with different major versions
 | 
			
		||||
				if _, ok := kernelRelatedPackNames[ovalPack.Name]; ok {
 | 
			
		||||
					if major(ovalPack.Version) != major(running.Release) {
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if ovalPack.NotFixedYet {
 | 
			
		||||
			return true, true
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Compare between the installed version vs the version in OVAL
 | 
			
		||||
		less, err := lessThan(family, req.versionRelease, ovalPack)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Debugf("Failed to parse versions: %s, Ver: %#v, OVAL: %#v, DefID: %s",
 | 
			
		||||
				err, req.versionRelease, ovalPack, def.DefinitionID)
 | 
			
		||||
			return false, false
 | 
			
		||||
		}
 | 
			
		||||
		if less {
 | 
			
		||||
			// If the version of installed is less than in OVAL
 | 
			
		||||
			switch family {
 | 
			
		||||
			case config.RedHat,
 | 
			
		||||
				config.Amazon,
 | 
			
		||||
				config.SUSEEnterpriseServer,
 | 
			
		||||
				config.Debian,
 | 
			
		||||
				config.Ubuntu:
 | 
			
		||||
				// Use fixed state in OVAL for these distros.
 | 
			
		||||
				return true, false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// But CentOS can't judge whether fixed or unfixed.
 | 
			
		||||
			// Because fixed state in RHEL's OVAL is different.
 | 
			
		||||
			// So, it have to be judged version comparison.
 | 
			
		||||
 | 
			
		||||
			// `offline` or `fast` scan mode can't get a updatable version.
 | 
			
		||||
			// In these mode, the blow field was set empty.
 | 
			
		||||
			// Vuls can not judge fixed or unfixed.
 | 
			
		||||
			if req.newVersionRelease == "" {
 | 
			
		||||
				return true, false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// compare version: newVer vs oval
 | 
			
		||||
			less, err := lessThan(family, req.newVersionRelease, ovalPack)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				util.Log.Debugf("Failed to parse versions: %s, NewVer: %#v, OVAL: %#v, DefID: %s",
 | 
			
		||||
					err, req.newVersionRelease, ovalPack, def.DefinitionID)
 | 
			
		||||
				return false, false
 | 
			
		||||
			}
 | 
			
		||||
			return true, less
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var centosVerPattern = regexp.MustCompile(`\.[es]l(\d+)(?:_\d+)?(?:\.centos)?`)
 | 
			
		||||
var esVerPattern = regexp.MustCompile(`\.el(\d+)(?:_\d+)?`)
 | 
			
		||||
 | 
			
		||||
func lessThan(family, versionRelease string, packB ovalmodels.Package) (bool, error) {
 | 
			
		||||
	switch family {
 | 
			
		||||
	case config.Debian,
 | 
			
		||||
		config.Ubuntu:
 | 
			
		||||
		vera, err := debver.NewVersion(versionRelease)
 | 
			
		||||
		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.Oracle,
 | 
			
		||||
		config.SUSEEnterpriseServer,
 | 
			
		||||
		config.Alpine,
 | 
			
		||||
		config.Amazon:
 | 
			
		||||
		vera := rpmver.NewVersion(versionRelease)
 | 
			
		||||
		verb := rpmver.NewVersion(packB.Version)
 | 
			
		||||
		return vera.LessThan(verb), nil
 | 
			
		||||
 | 
			
		||||
	case config.RedHat,
 | 
			
		||||
		config.CentOS:
 | 
			
		||||
		vera := rpmver.NewVersion(centosVerPattern.ReplaceAllString(versionRelease, ".el$1"))
 | 
			
		||||
		verb := rpmver.NewVersion(esVerPattern.ReplaceAllString(packB.Version, ".el$1"))
 | 
			
		||||
		return vera.LessThan(verb), nil
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		util.Log.Errorf("Not implemented yet: %s", family)
 | 
			
		||||
	}
 | 
			
		||||
	return false, xerrors.Errorf("Package version comparison not supported: %s", family)
 | 
			
		||||
}
 | 
			
		||||