Compare commits
	
		
			316 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					0a440ca629 | ||
| 
						 | 
					eff1dbf95b | ||
| 
						 | 
					9a32a94806 | ||
| 
						 | 
					2534098509 | ||
| 
						 | 
					9497365758 | ||
| 
						 | 
					101c44c9c0 | ||
| 
						 | 
					ffd745c004 | ||
| 
						 | 
					5fea4eaef8 | ||
| 
						 | 
					1f610043cf | ||
| 
						 | 
					3f8de02683 | ||
| 
						 | 
					d02535d053 | ||
| 
						 | 
					75fceff5f7 | ||
| 
						 | 
					ebd3834a35 | ||
| 
						 | 
					93059b74c3 | ||
| 
						 | 
					2fc3462d35 | ||
| 
						 | 
					f78dab50cb | ||
| 
						 | 
					edb324c3d9 | ||
| 
						 | 
					83bcca6e66 | ||
| 
						 | 
					a124518d78 | ||
| 
						 | 
					94bf630e29 | ||
| 
						 | 
					31bb33fd90 | ||
| 
						 | 
					4b680b9960 | ||
| 
						 | 
					8a8ab8cb18 | ||
| 
						 | 
					8146f5fd1b | ||
| 
						 | 
					425c585e47 | ||
| 
						 | 
					4f1578b2d6 | ||
| 
						 | 
					7969b343b0 | ||
| 
						 | 
					58cf1f4c8e | ||
| 
						 | 
					a5b87af862 | ||
| 
						 | 
					a0e592b934 | ||
| 
						 | 
					7eccc538bb | ||
| 
						 | 
					59daa8570a | ||
| 
						 | 
					3f52d318bc | ||
| 
						 | 
					11a7a0c934 | ||
| 
						 | 
					89f49b0e29 | ||
| 
						 | 
					72457cbf8e | ||
| 
						 | 
					c11ba27509 | ||
| 
						 | 
					8a611f9ba6 | ||
| 
						 | 
					4a73875e4d | ||
| 
						 | 
					d9d5e612ff | ||
| 
						 | 
					4d8599e4fc | ||
| 
						 | 
					59c7061d29 | ||
| 
						 | 
					996557c667 | ||
| 
						 | 
					519fb19a77 | ||
| 
						 | 
					36456cb151 | ||
| 
						 | 
					4ae87cc36c | ||
| 
						 | 
					b37df89fb1 | ||
| 
						 | 
					d18e7a751d | ||
| 
						 | 
					8d5ea98e50 | ||
| 
						 | 
					835dc08049 | ||
| 
						 | 
					62c9409fe9 | ||
| 
						 | 
					2374f578ed | ||
| 
						 | 
					34e2f033d8 | ||
| 
						 | 
					420825cacc | ||
| 
						 | 
					466ec93d8e | ||
| 
						 | 
					3f5bb6ab29 | ||
| 
						 | 
					ebe5f858c8 | ||
| 
						 | 
					9dd025437b | ||
| 
						 | 
					c0ebac305a | ||
| 
						 | 
					1f23ab7ba4 | ||
| 
						 | 
					ea3b63998d | ||
| 
						 | 
					3093426458 | ||
| 
						 | 
					37716feac7 | ||
| 
						 | 
					56b12c38d2 | ||
| 
						 | 
					749ead5d4a | ||
| 
						 | 
					3be50ab8da | ||
| 
						 | 
					649f4a6991 | ||
| 
						 | 
					0ff7641471 | ||
| 
						 | 
					1679bfae20 | ||
| 
						 | 
					45aa364436 | ||
| 
						 | 
					778516c4d9 | ||
| 
						 | 
					464d523c42 | ||
| 
						 | 
					0f6a1987d4 | ||
| 
						 | 
					20c6247ce5 | ||
| 
						 | 
					a10dd67e0f | ||
| 
						 | 
					5729ad6026 | ||
| 
						 | 
					9aa0d87a21 | ||
| 
						 | 
					fe3f1b9924 | ||
| 
						 | 
					00e52a88fa | ||
| 
						 | 
					5811dffe7a | ||
| 
						 | 
					7278982af4 | ||
| 
						 | 
					c17b4154ec | ||
| 
						 | 
					d6e74cce08 | ||
| 
						 | 
					3f80749241 | ||
| 
						 | 
					7f72b6ac69 | ||
| 
						 | 
					03e7b90b9f | ||
| 
						 | 
					7936b3533b | ||
| 
						 | 
					bd7e61d7cc | ||
| 
						 | 
					69214e0c22 | ||
| 
						 | 
					45bff26558 | ||
| 
						 | 
					b2e429ccc6 | ||
| 
						 | 
					76363c227b | ||
| 
						 | 
					d5a3e5c2c5 | ||
| 
						 | 
					2b02807ef0 | ||
| 
						 | 
					be659ae094 | ||
| 
						 | 
					b2c105adbc | ||
| 
						 | 
					c61f462948 | ||
| 
						 | 
					3ffed18e02 | ||
| 
						 | 
					f54e7257d1 | ||
| 
						 | 
					cc13b6a27c | ||
| 
						 | 
					8877db1979 | ||
| 
						 | 
					af58122c91 | ||
| 
						 | 
					b7ca5e5590 | ||
| 
						 | 
					69b6d875e6 | ||
| 
						 | 
					1fbd516b83 | ||
| 
						 | 
					dec5d3b165 | ||
| 
						 | 
					d5e2040cef | ||
| 
						 | 
					4326befdec | ||
| 
						 | 
					3d4a5d9917 | ||
| 
						 | 
					d770034788 | ||
| 
						 | 
					a977533c78 | ||
| 
						 | 
					c5e13dd5e4 | ||
| 
						 | 
					a8040fe4d2 | ||
| 
						 | 
					9e066008c3 | ||
| 
						 | 
					22c6601526 | ||
| 
						 | 
					425464fd76 | ||
| 
						 | 
					ccb0751ffd | ||
| 
						 | 
					f832de81b7 | ||
| 
						 | 
					8a37de0686 | ||
| 
						 | 
					836e4704f8 | ||
| 
						 | 
					3e5390309c | ||
| 
						 | 
					f8c0b38716 | ||
| 
						 | 
					65e6070e5f | ||
| 
						 | 
					7b78ebbc42 | ||
| 
						 | 
					03c3189c02 | ||
| 
						 | 
					4a34dfe0e9 | ||
| 
						 | 
					4cf9a723fe | ||
| 
						 | 
					bd1b135db3 | ||
| 
						 | 
					8c3b305149 | ||
| 
						 | 
					a3719038b8 | ||
| 
						 | 
					c68a261c0b | ||
| 
						 | 
					75fea79ac1 | ||
| 
						 | 
					eb9f9680ec | ||
| 
						 | 
					3634afdb81 | ||
| 
						 | 
					77b5df896a | ||
| 
						 | 
					b81f64058c | ||
| 
						 | 
					a8a90d7c63 | ||
| 
						 | 
					17bb575002 | ||
| 
						 | 
					abcea1a14d | ||
| 
						 | 
					10942f7c08 | ||
| 
						 | 
					87ee829e80 | ||
| 
						 | 
					fcc2c1e4c7 | ||
| 
						 | 
					269095d034 | ||
| 
						 | 
					40492ee00a | ||
| 
						 | 
					64cdd5aedc | ||
| 
						 | 
					3bb650cb77 | ||
| 
						 | 
					774544c975 | ||
| 
						 | 
					299805a726 | ||
| 
						 | 
					276363e793 | ||
| 
						 | 
					e750bd53fc | ||
| 
						 | 
					98fee7b5d2 | ||
| 
						 | 
					53aaea9fe2 | ||
| 
						 | 
					824fbb6368 | ||
| 
						 | 
					80566b91ab | ||
| 
						 | 
					533d05a1b5 | ||
| 
						 | 
					6a1fc4fade | ||
| 
						 | 
					9008d0ddf0 | ||
| 
						 | 
					583f4577bc | ||
| 
						 | 
					e5716d5092 | ||
| 
						 | 
					7192ae1287 | ||
| 
						 | 
					99c65eff48 | ||
| 
						 | 
					91df593566 | ||
| 
						 | 
					07aeaeb989 | ||
| 
						 | 
					cfeecdacd0 | ||
| 
						 | 
					564dfa8b62 | ||
| 
						 | 
					75dd6f2010 | ||
| 
						 | 
					e26fd0b759 | ||
| 
						 | 
					d630680a51 | ||
| 
						 | 
					1723c3f6a0 | ||
| 
						 | 
					53dd90302e | ||
| 
						 | 
					5c6e06b05e | ||
| 
						 | 
					cf6fb0c8a5 | ||
| 
						 | 
					e0e71b2eae | ||
| 
						 | 
					53f4a29fb1 | ||
| 
						 | 
					89d58d1abc | ||
| 
						 | 
					d6b6969cb3 | ||
| 
						 | 
					e7bf6fa69d | ||
| 
						 | 
					6e51970b91 | ||
| 
						 | 
					56d7d43768 | ||
| 
						 | 
					256c99ffa2 | ||
| 
						 | 
					9c0bc3b13b | ||
| 
						 | 
					9b8a323d85 | ||
| 
						 | 
					3178c1e326 | ||
| 
						 | 
					321d68e03a | ||
| 
						 | 
					3d8753c621 | ||
| 
						 | 
					967c56909d | ||
| 
						 | 
					7c4831d2d1 | ||
| 
						 | 
					4b49e11a33 | ||
| 
						 | 
					d84a6a8627 | ||
| 
						 | 
					63b7f4a8db | ||
| 
						 | 
					ca2160264a | ||
| 
						 | 
					7842594f53 | ||
| 
						 | 
					7db056102c | ||
| 
						 | 
					a5a800fa0a | ||
| 
						 | 
					9147ec148d | ||
| 
						 | 
					b3260588c6 | ||
| 
						 | 
					7d31328271 | ||
| 
						 | 
					6e82981ee3 | ||
| 
						 | 
					9d7b115bb5 | ||
| 
						 | 
					8eae5002a3 | ||
| 
						 | 
					31bd6c0371 | ||
| 
						 | 
					7585f9d537 | ||
| 
						 | 
					76037cdf72 | ||
| 
						 | 
					98c5421edc | ||
| 
						 | 
					e63fc7e3f5 | ||
| 
						 | 
					6ed9cf3fb4 | ||
| 
						 | 
					9865eab2c0 | ||
| 
						 | 
					678e72a8b6 | ||
| 
						 | 
					ec41899089 | ||
| 
						 | 
					b2d913cc21 | ||
| 
						 | 
					bc86c24e6a | ||
| 
						 | 
					87a77dd95c | ||
| 
						 | 
					e8188f3432 | ||
| 
						 | 
					50506be546 | ||
| 
						 | 
					4ded028258 | ||
| 
						 | 
					6da8b3c4a1 | ||
| 
						 | 
					d5c92cbcb3 | ||
| 
						 | 
					ed5f98d6f0 | ||
| 
						 | 
					f854b8f908 | ||
| 
						 | 
					de7a6159d4 | ||
| 
						 | 
					6090a34037 | ||
| 
						 | 
					f566745479 | ||
| 
						 | 
					153234b623 | ||
| 
						 | 
					ac510d21ff | ||
| 
						 | 
					44fa2c5800 | ||
| 
						 | 
					d785fc2a54 | ||
| 
						 | 
					ea800e04bc | ||
| 
						 | 
					fe582ac635 | ||
| 
						 | 
					330edb3bce | ||
| 
						 | 
					212fec7115 | ||
| 
						 | 
					24d7021c47 | ||
| 
						 | 
					e3a01ff6a8 | ||
| 
						 | 
					81f2ba8a46 | ||
| 
						 | 
					9e9370b178 | ||
| 
						 | 
					ced6114a95 | ||
| 
						 | 
					3144faae5d | ||
| 
						 | 
					8960c67a82 | ||
| 
						 | 
					f8ca924434 | ||
| 
						 | 
					399a08775e | ||
| 
						 | 
					92f36ca558 | ||
| 
						 | 
					3dcc58205a | ||
| 
						 | 
					09779962cf | ||
| 
						 | 
					9cc78770a3 | ||
| 
						 | 
					f653ca9131 | ||
| 
						 | 
					6f9fd91849 | ||
| 
						 | 
					cb1aec4fc0 | ||
| 
						 | 
					7cebaf8a76 | ||
| 
						 | 
					241c943424 | ||
| 
						 | 
					d5d88d8cf0 | ||
| 
						 | 
					cf9d26068c | ||
| 
						 | 
					308a93dc72 | ||
| 
						 | 
					d6a7e65e4c | ||
| 
						 | 
					e0a5c5d3b8 | ||
| 
						 | 
					314f775243 | ||
| 
						 | 
					7a1644135a | ||
| 
						 | 
					5076326589 | ||
| 
						 | 
					ce56261b52 | ||
| 
						 | 
					baa0e897b2 | ||
| 
						 | 
					1d49c0e1ce | ||
| 
						 | 
					08755e446e | ||
| 
						 | 
					bb12d9dadb | ||
| 
						 | 
					fd1429fef0 | ||
| 
						 | 
					d3c421a4a8 | ||
| 
						 | 
					0c919da4b1 | ||
| 
						 | 
					9afbf1255f | ||
| 
						 | 
					50b105c4af | ||
| 
						 | 
					028508c1f7 | ||
| 
						 | 
					f0137a3695 | ||
| 
						 | 
					e6d3a1718c | ||
| 
						 | 
					86ba551e07 | ||
| 
						 | 
					26418be937 | ||
| 
						 | 
					092a19bdc1 | ||
| 
						 | 
					6d3398574c | ||
| 
						 | 
					b08969ad89 | ||
| 
						 | 
					0653656526 | ||
| 
						 | 
					7a5793c562 | ||
| 
						 | 
					562ff7807d | ||
| 
						 | 
					7971bdf7f7 | ||
| 
						 | 
					d926b7fd6d | ||
| 
						 | 
					c00404793a | ||
| 
						 | 
					a0e0ee6c1e | ||
| 
						 | 
					4ccbee705b | ||
| 
						 | 
					db43d55b2c | ||
| 
						 | 
					5a3a333eec | ||
| 
						 | 
					039edf1616 | ||
| 
						 | 
					47498bbf23 | ||
| 
						 | 
					cc28bf4ae2 | ||
| 
						 | 
					0e8736045e | ||
| 
						 | 
					19b581edef | ||
| 
						 | 
					295f6656d9 | ||
| 
						 | 
					1214d8c14d | ||
| 
						 | 
					b4cd96fc9a | ||
| 
						 | 
					3238a9b898 | ||
| 
						 | 
					c0f66320f6 | ||
| 
						 | 
					383220f384 | ||
| 
						 | 
					76a9c37e6b | ||
| 
						 | 
					e788e6a5ad | ||
| 
						 | 
					d00e912934 | ||
| 
						 | 
					8ebb663368 | ||
| 
						 | 
					445ffc4123 | ||
| 
						 | 
					6af49f4d55 | ||
| 
						 | 
					1de9e8c086 | ||
| 
						 | 
					59b0812adf | ||
| 
						 | 
					719785c1ed | ||
| 
						 | 
					8e5f627e59 | ||
| 
						 | 
					5ced3c72b8 | ||
| 
						 | 
					c002f0168c | ||
| 
						 | 
					00c690f516 | ||
| 
						 | 
					ab68ad5cc5 | ||
| 
						 | 
					5c84ebefab | ||
| 
						 | 
					eb2acaff22 | ||
| 
						 | 
					84d0655c52 | ||
| 
						 | 
					e137ebb9c2 | ||
| 
						 | 
					10d690d929 | ||
| 
						 | 
					14611d2fd9 | ||
| 
						 | 
					0665bfe15f | 
							
								
								
									
										7
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,7 @@
 | 
			
		||||
.dockerignore
 | 
			
		||||
Dockerfile
 | 
			
		||||
vendor/
 | 
			
		||||
cve.sqlite3*
 | 
			
		||||
oval.sqlite3*
 | 
			
		||||
setup/
 | 
			
		||||
img/
 | 
			
		||||
							
								
								
									
										3
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,3 @@
 | 
			
		||||
# These are supported funding model platforms
 | 
			
		||||
 | 
			
		||||
github: kotakanbe
 | 
			
		||||
							
								
								
									
										36
									
								
								.github/ISSUE_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -1,36 +0,0 @@
 | 
			
		||||
 | 
			
		||||
# Environment
 | 
			
		||||
 | 
			
		||||
## Vuls
 | 
			
		||||
 | 
			
		||||
Hash : ____
 | 
			
		||||
 | 
			
		||||
To check the commit hash of HEAD
 | 
			
		||||
$ vuls -v
 | 
			
		||||
 | 
			
		||||
or
 | 
			
		||||
$ cd $GOPATH/src/github.com/future-architect/vuls 
 | 
			
		||||
$ git rev-parse --short HEAD 
 | 
			
		||||
 | 
			
		||||
## OS
 | 
			
		||||
- Target Server: Write here
 | 
			
		||||
- Vuls Server: Write here
 | 
			
		||||
 | 
			
		||||
## Go
 | 
			
		||||
- Go version: here
 | 
			
		||||
 | 
			
		||||
# Current Output
 | 
			
		||||
 | 
			
		||||
Please re-run the command using ```-debug``` and provide the output below.
 | 
			
		||||
 | 
			
		||||
# Addition Details
 | 
			
		||||
 | 
			
		||||
Can you also please fill in each of the remaining sections.
 | 
			
		||||
 | 
			
		||||
## Expected Behavior
 | 
			
		||||
 | 
			
		||||
## Actual Behavior
 | 
			
		||||
 | 
			
		||||
## Steps to reproduce the behaviour
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										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.
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										28
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -1,14 +1,26 @@
 | 
			
		||||
## What did you implement:
 | 
			
		||||
 | 
			
		||||
Closes #XXXXX
 | 
			
		||||
If this Pull Request is work in progress, Add a prefix of “[WIP]” in the title.
 | 
			
		||||
 | 
			
		||||
## How did you implement it:
 | 
			
		||||
# What did you implement:
 | 
			
		||||
 | 
			
		||||
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. 
 | 
			
		||||
 | 
			
		||||
## How can we verify it:
 | 
			
		||||
Fixes # (issue)
 | 
			
		||||
 | 
			
		||||
## Type of change
 | 
			
		||||
 | 
			
		||||
## Todos:
 | 
			
		||||
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
 | 
			
		||||
@@ -21,4 +33,8 @@ You don't have to satisfy all of the following.
 | 
			
		||||
- [ ] Update the messages below
 | 
			
		||||
 | 
			
		||||
***Is this ready for review?:*** NO  
 | 
			
		||||
***Is it a breaking change?:*** NO
 | 
			
		||||
 | 
			
		||||
# Reference
 | 
			
		||||
 | 
			
		||||
* https://blog.github.com/2015-01-21-how-to-write-the-perfect-pull-request/
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								.github/workflows/golangci.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,29 @@
 | 
			
		||||
name: golangci-lint
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    tags:
 | 
			
		||||
      - v*
 | 
			
		||||
    branches:
 | 
			
		||||
      - master
 | 
			
		||||
  pull_request:
 | 
			
		||||
jobs:
 | 
			
		||||
  golangci:
 | 
			
		||||
    name: lint
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
      - name: golangci-lint
 | 
			
		||||
        uses: golangci/golangci-lint-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
 | 
			
		||||
          version: v1.32
 | 
			
		||||
          args: --timeout=10m
 | 
			
		||||
          
 | 
			
		||||
          # Optional: working directory, useful for monorepos
 | 
			
		||||
          # working-directory: somedir
 | 
			
		||||
 | 
			
		||||
          # Optional: golangci-lint command line arguments.
 | 
			
		||||
          # args: --issues-exit-code=0
 | 
			
		||||
 | 
			
		||||
          # Optional: show only new issues if it's a pull request. The default value is `false`.
 | 
			
		||||
          # only-new-issues: true
 | 
			
		||||
							
								
								
									
										31
									
								
								.github/workflows/goreleaser.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,31 @@
 | 
			
		||||
name: goreleaser
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    tags:
 | 
			
		||||
      - '*'
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  goreleaser:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      -
 | 
			
		||||
        name: Checkout
 | 
			
		||||
        uses: actions/checkout@v2
 | 
			
		||||
      -
 | 
			
		||||
        name: Unshallow
 | 
			
		||||
        run: git fetch --prune --unshallow
 | 
			
		||||
      -
 | 
			
		||||
        name: Set up Go
 | 
			
		||||
        uses: actions/setup-go@v2
 | 
			
		||||
        with:
 | 
			
		||||
          go-version: 1.14
 | 
			
		||||
      -
 | 
			
		||||
        name: Run GoReleaser
 | 
			
		||||
        uses: goreleaser/goreleaser-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          version: latest
 | 
			
		||||
          args: release --rm-dist
 | 
			
		||||
        env:
 | 
			
		||||
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										21
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,21 @@
 | 
			
		||||
name: Test
 | 
			
		||||
 | 
			
		||||
on: [pull_request]
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  build:
 | 
			
		||||
    name: Build
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
 | 
			
		||||
    - name: Set up Go 1.x
 | 
			
		||||
      uses: actions/setup-go@v2
 | 
			
		||||
      with:
 | 
			
		||||
        go-version: 1.14.x
 | 
			
		||||
      id: go
 | 
			
		||||
 | 
			
		||||
    - name: Check out code into the Go module directory
 | 
			
		||||
      uses: actions/checkout@v2
 | 
			
		||||
 | 
			
		||||
    - name: Test
 | 
			
		||||
      run: make test
 | 
			
		||||
							
								
								
									
										22
									
								
								.github/workflows/tidy.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,22 @@
 | 
			
		||||
name: go-mod-tidy-pr
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  schedule:
 | 
			
		||||
    - cron: "0 0 * * 1" # Weekly build
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  go-mod-tidy-pr:
 | 
			
		||||
    name: go-mod-tidy-pr
 | 
			
		||||
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
 | 
			
		||||
      - name: Run go-mod-tidy-pr
 | 
			
		||||
        uses: sue445/go-mod-tidy-pr@master
 | 
			
		||||
        with:
 | 
			
		||||
          github_token: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
          git_user_name: kotakanbe
 | 
			
		||||
          git_user_email: kotakanbe@gmail.com
 | 
			
		||||
          go_version: 1.15.6
 | 
			
		||||
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -1,6 +1,6 @@
 | 
			
		||||
vuls
 | 
			
		||||
.vscode
 | 
			
		||||
*.txt
 | 
			
		||||
*.json
 | 
			
		||||
*.sqlite3*
 | 
			
		||||
*.db
 | 
			
		||||
tags
 | 
			
		||||
@@ -12,3 +12,6 @@ log/
 | 
			
		||||
results/
 | 
			
		||||
*config.toml
 | 
			
		||||
!setup/docker/*
 | 
			
		||||
.DS_Store
 | 
			
		||||
dist/
 | 
			
		||||
.idea
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								.golangci.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,17 @@
 | 
			
		||||
name: golang-ci
 | 
			
		||||
 | 
			
		||||
linters-settings:
 | 
			
		||||
  errcheck:
 | 
			
		||||
    #exclude: /path/to/file.txt
 | 
			
		||||
 | 
			
		||||
linters:
 | 
			
		||||
  disable-all: true
 | 
			
		||||
  enable:
 | 
			
		||||
    - goimports
 | 
			
		||||
    - golint
 | 
			
		||||
    - govet
 | 
			
		||||
    - misspell
 | 
			
		||||
    - errcheck
 | 
			
		||||
    - staticcheck
 | 
			
		||||
    - prealloc
 | 
			
		||||
    - ineffassign
 | 
			
		||||
							
								
								
									
										102
									
								
								.goreleaser.yml
									
									
									
									
									
								
							
							
						
						@@ -1,20 +1,110 @@
 | 
			
		||||
project_name: vuls
 | 
			
		||||
env:
 | 
			
		||||
  - GO111MODULE=on
 | 
			
		||||
release:
 | 
			
		||||
  github:
 | 
			
		||||
    owner: future-architect
 | 
			
		||||
    name: vuls
 | 
			
		||||
builds:
 | 
			
		||||
- goos:
 | 
			
		||||
- id: vuls
 | 
			
		||||
  goos:
 | 
			
		||||
  - linux
 | 
			
		||||
  goarch:
 | 
			
		||||
  - amd64
 | 
			
		||||
  main: .
 | 
			
		||||
  ldflags: -s -w -X main.version={{.Version}} -X main.revision={{.Commit}}
 | 
			
		||||
  main: ./cmd/vuls/main.go
 | 
			
		||||
  flags:
 | 
			
		||||
  - -a
 | 
			
		||||
  ldflags: 
 | 
			
		||||
  - -s -w -X github.com/future-architect/vuls/config.Version={{.Version}} -X github.com/future-architect/vuls/config.Revision={{.Commit}}-{{ .CommitDate }}
 | 
			
		||||
  binary: vuls
 | 
			
		||||
archive:
 | 
			
		||||
 | 
			
		||||
- id: vuls-scanner
 | 
			
		||||
  env:
 | 
			
		||||
  - CGO_ENABLED=0
 | 
			
		||||
  goos:
 | 
			
		||||
  - linux
 | 
			
		||||
  goarch:
 | 
			
		||||
  - 386
 | 
			
		||||
  - amd64
 | 
			
		||||
  - arm
 | 
			
		||||
  - arm64
 | 
			
		||||
  main: ./cmd/scanner/main.go
 | 
			
		||||
  flags:
 | 
			
		||||
  - -a
 | 
			
		||||
  - -tags=scanner
 | 
			
		||||
  ldflags: 
 | 
			
		||||
  - -s -w -X github.com/future-architect/vuls/config.Version={{.Version}} -X github.com/future-architect/vuls/config.Revision={{.Commit}}-{{ .CommitDate }}
 | 
			
		||||
  binary: vuls-scanner
 | 
			
		||||
 | 
			
		||||
- id: trivy-to-vuls
 | 
			
		||||
  env:
 | 
			
		||||
  - CGO_ENABLED=0
 | 
			
		||||
  goos:
 | 
			
		||||
  - linux
 | 
			
		||||
  goarch:
 | 
			
		||||
  - 386
 | 
			
		||||
  - amd64
 | 
			
		||||
  - arm
 | 
			
		||||
  - arm64
 | 
			
		||||
  main: ./contrib/trivy/cmd/main.go
 | 
			
		||||
  binary: trivy-to-vuls
 | 
			
		||||
 | 
			
		||||
- id: future-vuls
 | 
			
		||||
  env:
 | 
			
		||||
  - CGO_ENABLED=0
 | 
			
		||||
  goos:
 | 
			
		||||
  - linux
 | 
			
		||||
  goarch:
 | 
			
		||||
  - 386
 | 
			
		||||
  - amd64
 | 
			
		||||
  - arm
 | 
			
		||||
  - arm64
 | 
			
		||||
  flags:
 | 
			
		||||
  - -a
 | 
			
		||||
  - -tags=scanner
 | 
			
		||||
  main: ./contrib/future-vuls/cmd/main.go
 | 
			
		||||
  binary: future-vuls
 | 
			
		||||
 | 
			
		||||
archives:
 | 
			
		||||
 | 
			
		||||
- id: vuls
 | 
			
		||||
  name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
 | 
			
		||||
  builds:
 | 
			
		||||
  - vuls
 | 
			
		||||
  format: tar.gz
 | 
			
		||||
  files:
 | 
			
		||||
  - LICENSE
 | 
			
		||||
  - NOTICE
 | 
			
		||||
  - README*
 | 
			
		||||
  - CHANGELOG.md
 | 
			
		||||
 | 
			
		||||
- id: vuls-scanner
 | 
			
		||||
  name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
 | 
			
		||||
  builds:
 | 
			
		||||
  - vuls-scanner
 | 
			
		||||
  format: tar.gz
 | 
			
		||||
  files:
 | 
			
		||||
  - LICENSE
 | 
			
		||||
  - NOTICE
 | 
			
		||||
  - README*
 | 
			
		||||
  - CHANGELOG.md
 | 
			
		||||
 | 
			
		||||
- id: trivy-to-vuls
 | 
			
		||||
  name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
 | 
			
		||||
  builds:
 | 
			
		||||
  - trivy-to-vuls
 | 
			
		||||
  format: tar.gz
 | 
			
		||||
  files:
 | 
			
		||||
  - LICENSE
 | 
			
		||||
  - NOTICE
 | 
			
		||||
  - README*
 | 
			
		||||
  - CHANGELOG.md
 | 
			
		||||
 | 
			
		||||
- id: future-vuls
 | 
			
		||||
  name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
 | 
			
		||||
  builds:
 | 
			
		||||
  - future-vuls
 | 
			
		||||
  format: tar.gz
 | 
			
		||||
  name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{
 | 
			
		||||
    .Arm }}{{ end }}'
 | 
			
		||||
  files:
 | 
			
		||||
  - LICENSE
 | 
			
		||||
  - NOTICE
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
language: go
 | 
			
		||||
 | 
			
		||||
go:
 | 
			
		||||
  - 1.8
 | 
			
		||||
 | 
			
		||||
after_success:
 | 
			
		||||
  - test -n "$TRAVIS_TAG" && curl -sL https://git.io/goreleaser | bash
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
# 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)
 | 
			
		||||
 | 
			
		||||
@@ -509,4 +511,4 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
 | 
			
		||||
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										34
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,34 @@
 | 
			
		||||
FROM golang:alpine as builder
 | 
			
		||||
 | 
			
		||||
RUN apk add --no-cache \
 | 
			
		||||
        git \
 | 
			
		||||
        make \
 | 
			
		||||
        gcc \
 | 
			
		||||
        musl-dev
 | 
			
		||||
 | 
			
		||||
ENV REPOSITORY github.com/future-architect/vuls
 | 
			
		||||
COPY . $GOPATH/src/$REPOSITORY
 | 
			
		||||
RUN cd $GOPATH/src/$REPOSITORY && make install
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FROM alpine:3.11
 | 
			
		||||
 | 
			
		||||
MAINTAINER hikachan sadayuki-matsuno
 | 
			
		||||
 | 
			
		||||
ENV LOGDIR /var/log/vuls
 | 
			
		||||
ENV WORKDIR /vuls
 | 
			
		||||
 | 
			
		||||
RUN apk add --no-cache \
 | 
			
		||||
        openssh-client \
 | 
			
		||||
        ca-certificates \
 | 
			
		||||
        git \
 | 
			
		||||
    && mkdir -p $WORKDIR $LOGDIR
 | 
			
		||||
 | 
			
		||||
COPY --from=builder /go/bin/vuls /usr/local/bin/
 | 
			
		||||
 | 
			
		||||
VOLUME ["$WORKDIR", "$LOGDIR"]
 | 
			
		||||
WORKDIR $WORKDIR
 | 
			
		||||
ENV PWD $WORKDIR
 | 
			
		||||
 | 
			
		||||
ENTRYPOINT ["vuls"]
 | 
			
		||||
CMD ["--help"]
 | 
			
		||||
							
								
								
									
										58
									
								
								GNUmakefile
									
									
									
									
									
								
							
							
						
						@@ -1,11 +1,9 @@
 | 
			
		||||
.PHONY: \
 | 
			
		||||
	dep \
 | 
			
		||||
	depup \
 | 
			
		||||
	build \
 | 
			
		||||
	install \
 | 
			
		||||
	all \
 | 
			
		||||
	vendor \
 | 
			
		||||
	lint \
 | 
			
		||||
 	lint \
 | 
			
		||||
	vet \
 | 
			
		||||
	fmt \
 | 
			
		||||
	fmtcheck \
 | 
			
		||||
@@ -15,50 +13,53 @@
 | 
			
		||||
	clean
 | 
			
		||||
 | 
			
		||||
SRCS = $(shell git ls-files '*.go')
 | 
			
		||||
PKGS = ./. ./cache ./commands ./config ./models ./oval ./report ./scan ./util 
 | 
			
		||||
PKGS = $(shell go list ./...)
 | 
			
		||||
VERSION := $(shell git describe --tags --abbrev=0)
 | 
			
		||||
REVISION := $(shell git rev-parse --short HEAD)
 | 
			
		||||
LDFLAGS := -X 'main.version=$(VERSION)' \
 | 
			
		||||
	-X 'main.revision=$(REVISION)'
 | 
			
		||||
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
 | 
			
		||||
CGO_UNABLED := CGO_ENABLED=0 go
 | 
			
		||||
GO_OFF := GO111MODULE=off go
 | 
			
		||||
 | 
			
		||||
all: dep build test
 | 
			
		||||
 | 
			
		||||
dep:
 | 
			
		||||
	go get -u github.com/golang/dep/...
 | 
			
		||||
	dep ensure
 | 
			
		||||
all: build
 | 
			
		||||
 | 
			
		||||
depup:
 | 
			
		||||
	go get -u github.com/golang/dep/...
 | 
			
		||||
	dep ensure -update
 | 
			
		||||
build: ./cmd/vuls/main.go pretest fmt
 | 
			
		||||
	$(GO) build -a -ldflags "$(LDFLAGS)" -o vuls ./cmd/vuls
 | 
			
		||||
 | 
			
		||||
build: main.go dep pretest
 | 
			
		||||
	go build -ldflags "$(LDFLAGS)" -o vuls $<
 | 
			
		||||
install: ./cmd/vuls/main.go pretest fmt
 | 
			
		||||
	$(GO) install -ldflags "$(LDFLAGS)" ./cmd/vuls
 | 
			
		||||
 | 
			
		||||
install: main.go dep pretest
 | 
			
		||||
	go install -ldflags "$(LDFLAGS)"
 | 
			
		||||
build-scanner: ./cmd/scanner/main.go pretest fmt
 | 
			
		||||
	$(CGO_UNABLED) build -tags=scanner -a -ldflags "$(LDFLAGS)" -o vuls ./cmd/scanner
 | 
			
		||||
 | 
			
		||||
install-scanner: ./cmd/scanner/main.go pretest fmt
 | 
			
		||||
	$(CGO_UNABLED) install -tags=scanner -ldflags "$(LDFLAGS)" ./cmd/scanner
 | 
			
		||||
 | 
			
		||||
lint:
 | 
			
		||||
	@ go get -v github.com/golang/lint/golint
 | 
			
		||||
	$(foreach file,$(SRCS),golint $(file) || exit;)
 | 
			
		||||
	$(GO_OFF) get -u golang.org/x/lint/golint
 | 
			
		||||
	golint $(PKGS)
 | 
			
		||||
 | 
			
		||||
vet:
 | 
			
		||||
	#  @-go get -v golang.org/x/tools/cmd/vet
 | 
			
		||||
	echo $(PKGS) | xargs go vet || exit;
 | 
			
		||||
	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: pretest
 | 
			
		||||
	go install
 | 
			
		||||
	echo $(PKGS) | xargs go test -cover -v || exit;
 | 
			
		||||
test: 
 | 
			
		||||
	$(GO) test -cover -v ./... || exit;
 | 
			
		||||
 | 
			
		||||
unused :
 | 
			
		||||
unused:
 | 
			
		||||
	$(foreach pkg,$(PKGS),unused $(pkg);)
 | 
			
		||||
 | 
			
		||||
cov:
 | 
			
		||||
@@ -69,3 +70,10 @@ cov:
 | 
			
		||||
clean:
 | 
			
		||||
	echo $(PKGS) | xargs go clean || exit;
 | 
			
		||||
 | 
			
		||||
# trivy-to-vuls
 | 
			
		||||
build-trivy-to-vuls: pretest fmt
 | 
			
		||||
	$(GO) build -o trivy-to-vuls contrib/trivy/cmd/*.go
 | 
			
		||||
 | 
			
		||||
# future-vuls
 | 
			
		||||
build-future-vuls: pretest fmt
 | 
			
		||||
	$(GO) build -o future-vuls contrib/future-vuls/cmd/*.go
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										272
									
								
								Gopkg.lock
									
									
									
										generated
									
									
									
								
							
							
						
						@@ -1,272 +0,0 @@
 | 
			
		||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/Azure/azure-sdk-for-go"
 | 
			
		||||
  packages = ["storage"]
 | 
			
		||||
  revision = "df4dd90d076ebbf6e87d08d3f00bfac8ff4bde1a"
 | 
			
		||||
  version = "v10.3.1-beta"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/Azure/go-autorest"
 | 
			
		||||
  packages = ["autorest","autorest/adal","autorest/azure","autorest/date"]
 | 
			
		||||
  revision = "f6be1abbb5abd0517522f850dd785990d373da7e"
 | 
			
		||||
  version = "v8.4.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/BurntSushi/toml"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "b26d9c308763d68093482582cea63d69be07a0f0"
 | 
			
		||||
  version = "v0.3.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/asaskevich/govalidator"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "4918b99a7cb949bb295f3c7bbaf24b577d806e35"
 | 
			
		||||
  version = "v6"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/aws/aws-sdk-go"
 | 
			
		||||
  packages = ["aws","aws/awserr","aws/awsutil","aws/client","aws/client/metadata","aws/corehandlers","aws/credentials","aws/credentials/ec2rolecreds","aws/credentials/endpointcreds","aws/credentials/stscreds","aws/defaults","aws/ec2metadata","aws/endpoints","aws/request","aws/session","aws/signer/v4","internal/shareddefaults","private/protocol","private/protocol/query","private/protocol/query/queryutil","private/protocol/rest","private/protocol/restxml","private/protocol/xml/xmlutil","service/s3","service/sts"]
 | 
			
		||||
  revision = "c652f9369083515c3ddf1fbaf6df68da2c101545"
 | 
			
		||||
  version = "v1.12.1"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/boltdb/bolt"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "2f1ce7a837dcb8da3ec595b1dac9d0632f0f99e8"
 | 
			
		||||
  version = "v1.3.1"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/cenkalti/backoff"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "61153c768f31ee5f130071d08fc82b85208528de"
 | 
			
		||||
  version = "v1.1.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/cheggaaa/pb"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "657164d0228d6bebe316fdf725c69f131a50fb10"
 | 
			
		||||
  version = "v1.0.18"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/dgrijalva/jwt-go"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "d2709f9f1f31ebcda9651b03077758c1f3a0018c"
 | 
			
		||||
  version = "v3.0.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/go-ini/ini"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "20b96f641a5ea98f2f8619ff4f3e061cff4833bd"
 | 
			
		||||
  version = "v1.28.2"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/go-redis/redis"
 | 
			
		||||
  packages = [".","internal","internal/consistenthash","internal/hashtag","internal/pool","internal/proto"]
 | 
			
		||||
  revision = "975882d73d21759d45a4eb49652064083bc23e61"
 | 
			
		||||
  version = "v6.7.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/go-sql-driver/mysql"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "a0583e0143b1624142adab07e0e97fe106d99561"
 | 
			
		||||
  version = "v1.3"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/google/subcommands"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "ce3d4cfc062faac7115d44e5befec8b5a08c3faa"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/gosuri/uitable"
 | 
			
		||||
  packages = [".","util/strutil","util/wordwrap"]
 | 
			
		||||
  revision = "36ee7e946282a3fb1cfecd476ddc9b35d8847e42"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/howeyc/gopass"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "bf9dde6d0d2c004a008c27aaee91170c786f6db8"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/jinzhu/gorm"
 | 
			
		||||
  packages = [".","dialects/mysql","dialects/postgres","dialects/sqlite"]
 | 
			
		||||
  revision = "5174cc5c242a728b435ea2be8a2f7f998e15429b"
 | 
			
		||||
  version = "v1.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/jinzhu/inflection"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "1c35d901db3da928c72a72d8458480cc9ade058f"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/jmespath/go-jmespath"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "0b12d6b5"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/jroimartin/gocui"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "4e9ce9a8e26f2ef33dfe297dbdfca148733b6b9b"
 | 
			
		||||
  version = "v0.3.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/k0kubun/pp"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "e057ee7a28277be4d2af303443b6da377768181f"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/knqyf263/go-deb-version"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "9865fe14d09b1c729188ac810466dde90f897ee3"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/knqyf263/go-rpm-version"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "74609b86c936dff800c69ec89fcf4bc52d5f13a4"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/kotakanbe/go-cve-dictionary"
 | 
			
		||||
  packages = ["config","db","jvn","log","models","nvd","util"]
 | 
			
		||||
  revision = "f5406ffe8226f01f64544723339c6a17b2bd74af"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/kotakanbe/go-pingscanner"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "641dc2cc2d3cbf295dad356667b74c69bcbd6f70"
 | 
			
		||||
  version = "v0.1.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/kotakanbe/goval-dictionary"
 | 
			
		||||
  packages = ["config","db","db/rdb","log","models"]
 | 
			
		||||
  revision = "fd8ff5a6343912117d1b7db16fbd5fa1f4116c3a"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/kotakanbe/logrus-prefixed-formatter"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "75edb2e85a38873f0318be05a458446681d1022f"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/lib/pq"
 | 
			
		||||
  packages = [".","hstore","oid"]
 | 
			
		||||
  revision = "b77235e3890a962fe8a6f8c4c7198679ca7814e7"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/mattn/go-colorable"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
 | 
			
		||||
  version = "v0.0.9"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/mattn/go-isatty"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
 | 
			
		||||
  version = "v0.0.3"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/mattn/go-runewidth"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "9e777a8366cce605130a531d2cd6363d07ad7317"
 | 
			
		||||
  version = "v0.0.2"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/mattn/go-sqlite3"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "ca5e3819723d8eeaf170ad510e7da1d6d2e94a08"
 | 
			
		||||
  version = "v1.2.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/mgutz/ansi"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "9520e82c474b0a04dd04f8a40959027271bab992"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/moul/http2curl"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "9ac6cf4d929b2fa8fd2d2e6dec5bb0feb4f4911d"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/nsf/termbox-go"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "4ed959e0540971545eddb8c75514973d670cf739"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/parnurzeal/gorequest"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "a578a48e8d6ca8b01a3b18314c43c6716bb5f5a3"
 | 
			
		||||
  version = "v0.2.15"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/pkg/errors"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
 | 
			
		||||
  version = "v0.8.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/rifflock/lfshook"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "6844c808343cb8fa357d7f141b1b990e05d24e41"
 | 
			
		||||
  version = "1.7"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/satori/uuid"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "879c5887cd475cd7864858769793b2ceb0d44feb"
 | 
			
		||||
  version = "v1.1.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/sirupsen/logrus"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "89742aefa4b206dcf400792f3bd35b542998eb3b"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/ymomoi/goval-parser"
 | 
			
		||||
  packages = ["oval"]
 | 
			
		||||
  revision = "0a0be1dd9d0855b50be0be5a10ad3085382b6d59"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "golang.org/x/crypto"
 | 
			
		||||
  packages = ["curve25519","ed25519","ed25519/internal/edwards25519","ssh","ssh/agent","ssh/terminal"]
 | 
			
		||||
  revision = "c84b36c635ad003a10f0c755dff5685ceef18c71"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "golang.org/x/net"
 | 
			
		||||
  packages = ["context","idna","publicsuffix"]
 | 
			
		||||
  revision = "0a9397675ba34b2845f758fe3cd68828369c6517"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "golang.org/x/sys"
 | 
			
		||||
  packages = ["unix","windows"]
 | 
			
		||||
  revision = "314a259e304ff91bd6985da2a7149bbf91237993"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "golang.org/x/text"
 | 
			
		||||
  packages = ["collate","collate/build","internal/colltab","internal/gen","internal/tag","internal/triegen","internal/ucd","language","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"]
 | 
			
		||||
  revision = "1cbadb444a806fd9430d14ad08967ed91da4fa0a"
 | 
			
		||||
 | 
			
		||||
[solve-meta]
 | 
			
		||||
  analyzer-name = "dep"
 | 
			
		||||
  analyzer-version = 1
 | 
			
		||||
  inputs-digest = "36d700add80d36c56484ed310b9a7e622b3e308ab22eb42bdfb02fd8f5c90407"
 | 
			
		||||
  solver-name = "gps-cdcl"
 | 
			
		||||
  solver-version = 1
 | 
			
		||||
							
								
								
									
										90
									
								
								Gopkg.toml
									
									
									
									
									
								
							
							
						
						@@ -1,90 +0,0 @@
 | 
			
		||||
 | 
			
		||||
# Gopkg.toml example
 | 
			
		||||
#
 | 
			
		||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
 | 
			
		||||
# for detailed Gopkg.toml documentation.
 | 
			
		||||
#
 | 
			
		||||
# required = ["github.com/user/thing/cmd/thing"]
 | 
			
		||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
 | 
			
		||||
#
 | 
			
		||||
# [[constraint]]
 | 
			
		||||
#   name = "github.com/user/project"
 | 
			
		||||
#   version = "1.0.0"
 | 
			
		||||
#
 | 
			
		||||
# [[constraint]]
 | 
			
		||||
#   name = "github.com/user/project2"
 | 
			
		||||
#   branch = "dev"
 | 
			
		||||
#   source = "github.com/myfork/project2"
 | 
			
		||||
#
 | 
			
		||||
# [[override]]
 | 
			
		||||
#  name = "github.com/x/y"
 | 
			
		||||
#  version = "2.4.0"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  name = "github.com/BurntSushi/toml"
 | 
			
		||||
  version = "0.3.0"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  name = "github.com/asaskevich/govalidator"
 | 
			
		||||
  version = "6.0.0"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  name = "github.com/boltdb/bolt"
 | 
			
		||||
  version = "1.3.1"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  name = "github.com/cenkalti/backoff"
 | 
			
		||||
  version = "1.0.0"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/google/subcommands"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/gosuri/uitable"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/howeyc/gopass"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  name = "github.com/jroimartin/gocui"
 | 
			
		||||
  version = "0.3.0"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/k0kubun/pp"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/knqyf263/go-deb-version"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/knqyf263/go-rpm-version"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  name = "github.com/kotakanbe/go-pingscanner"
 | 
			
		||||
  version = "0.1.0"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/kotakanbe/logrus-prefixed-formatter"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  name = "github.com/parnurzeal/gorequest"
 | 
			
		||||
  version = "0.2.15"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  name = "github.com/rifflock/lfshook"
 | 
			
		||||
  version = "1.7.0"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/sirupsen/logrus"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/kotakanbe/go-cve-dictionary"
 | 
			
		||||
							
								
								
									
										153
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						@@ -1,23 +1,21 @@
 | 
			
		||||
                    GNU GENERAL PUBLIC LICENSE
 | 
			
		||||
                       Version 3, 29 June 2007
 | 
			
		||||
                    GNU AFFERO GENERAL PUBLIC LICENSE
 | 
			
		||||
                       Version 3, 19 November 2007
 | 
			
		||||
 | 
			
		||||
 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 | 
			
		||||
 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
 | 
			
		||||
 Everyone is permitted to copy and distribute verbatim copies
 | 
			
		||||
 of this license document, but changing it is not allowed.
 | 
			
		||||
 | 
			
		||||
                            Preamble
 | 
			
		||||
 | 
			
		||||
  The GNU General Public License is a free, copyleft license for
 | 
			
		||||
software and other kinds of works.
 | 
			
		||||
  The GNU Affero General Public License is a free, copyleft license for
 | 
			
		||||
software and other kinds of works, specifically designed to ensure
 | 
			
		||||
cooperation with the community in the case of network server software.
 | 
			
		||||
 | 
			
		||||
  The licenses for most software and other practical works are designed
 | 
			
		||||
to take away your freedom to share and change the works.  By contrast,
 | 
			
		||||
the GNU General Public License is intended to guarantee your freedom to
 | 
			
		||||
our General Public Licenses are intended to guarantee your freedom to
 | 
			
		||||
share and change all versions of a program--to make sure it remains free
 | 
			
		||||
software for all its users.  We, the Free Software Foundation, use the
 | 
			
		||||
GNU General Public License for most of our software; it applies also to
 | 
			
		||||
any other work released this way by its authors.  You can apply it to
 | 
			
		||||
your programs, too.
 | 
			
		||||
software for all its users.
 | 
			
		||||
 | 
			
		||||
  When we speak of free software, we are referring to freedom, not
 | 
			
		||||
price.  Our General Public Licenses are designed to make sure that you
 | 
			
		||||
@@ -26,44 +24,34 @@ them if you wish), that you receive source code or can get it if you
 | 
			
		||||
want it, that you can change the software or use pieces of it in new
 | 
			
		||||
free programs, and that you know you can do these things.
 | 
			
		||||
 | 
			
		||||
  To protect your rights, we need to prevent others from denying you
 | 
			
		||||
these rights or asking you to surrender the rights.  Therefore, you have
 | 
			
		||||
certain responsibilities if you distribute copies of the software, or if
 | 
			
		||||
you modify it: responsibilities to respect the freedom of others.
 | 
			
		||||
  Developers that use our General Public Licenses protect your rights
 | 
			
		||||
with two steps: (1) assert copyright on the software, and (2) offer
 | 
			
		||||
you this License which gives you legal permission to copy, distribute
 | 
			
		||||
and/or modify the software.
 | 
			
		||||
 | 
			
		||||
  For example, if you distribute copies of such a program, whether
 | 
			
		||||
gratis or for a fee, you must pass on to the recipients the same
 | 
			
		||||
freedoms that you received.  You must make sure that they, too, receive
 | 
			
		||||
or can get the source code.  And you must show them these terms so they
 | 
			
		||||
know their rights.
 | 
			
		||||
  A secondary benefit of defending all users' freedom is that
 | 
			
		||||
improvements made in alternate versions of the program, if they
 | 
			
		||||
receive widespread use, become available for other developers to
 | 
			
		||||
incorporate.  Many developers of free software are heartened and
 | 
			
		||||
encouraged by the resulting cooperation.  However, in the case of
 | 
			
		||||
software used on network servers, this result may fail to come about.
 | 
			
		||||
The GNU General Public License permits making a modified version and
 | 
			
		||||
letting the public access it on a server without ever releasing its
 | 
			
		||||
source code to the public.
 | 
			
		||||
 | 
			
		||||
  Developers that use the GNU GPL protect your rights with two steps:
 | 
			
		||||
(1) assert copyright on the software, and (2) offer you this License
 | 
			
		||||
giving you legal permission to copy, distribute and/or modify it.
 | 
			
		||||
  The GNU Affero General Public License is designed specifically to
 | 
			
		||||
ensure that, in such cases, the modified source code becomes available
 | 
			
		||||
to the community.  It requires the operator of a network server to
 | 
			
		||||
provide the source code of the modified version running there to the
 | 
			
		||||
users of that server.  Therefore, public use of a modified version, on
 | 
			
		||||
a publicly accessible server, gives the public access to the source
 | 
			
		||||
code of the modified version.
 | 
			
		||||
 | 
			
		||||
  For the developers' and authors' protection, the GPL clearly explains
 | 
			
		||||
that there is no warranty for this free software.  For both users' and
 | 
			
		||||
authors' sake, the GPL requires that modified versions be marked as
 | 
			
		||||
changed, so that their problems will not be attributed erroneously to
 | 
			
		||||
authors of previous versions.
 | 
			
		||||
 | 
			
		||||
  Some devices are designed to deny users access to install or run
 | 
			
		||||
modified versions of the software inside them, although the manufacturer
 | 
			
		||||
can do so.  This is fundamentally incompatible with the aim of
 | 
			
		||||
protecting users' freedom to change the software.  The systematic
 | 
			
		||||
pattern of such abuse occurs in the area of products for individuals to
 | 
			
		||||
use, which is precisely where it is most unacceptable.  Therefore, we
 | 
			
		||||
have designed this version of the GPL to prohibit the practice for those
 | 
			
		||||
products.  If such problems arise substantially in other domains, we
 | 
			
		||||
stand ready to extend this provision to those domains in future versions
 | 
			
		||||
of the GPL, as needed to protect the freedom of users.
 | 
			
		||||
 | 
			
		||||
  Finally, every program is threatened constantly by software patents.
 | 
			
		||||
States should not allow patents to restrict development and use of
 | 
			
		||||
software on general-purpose computers, but in those that do, we wish to
 | 
			
		||||
avoid the special danger that patents applied to a free program could
 | 
			
		||||
make it effectively proprietary.  To prevent this, the GPL assures that
 | 
			
		||||
patents cannot be used to render the program non-free.
 | 
			
		||||
  An older license, called the Affero General Public License and
 | 
			
		||||
published by Affero, was designed to accomplish similar goals.  This is
 | 
			
		||||
a different license, not a version of the Affero GPL, but Affero has
 | 
			
		||||
released a new version of the Affero GPL which permits relicensing under
 | 
			
		||||
this license.
 | 
			
		||||
 | 
			
		||||
  The precise terms and conditions for copying, distribution and
 | 
			
		||||
modification follow.
 | 
			
		||||
@@ -72,7 +60,7 @@ modification follow.
 | 
			
		||||
 | 
			
		||||
  0. Definitions.
 | 
			
		||||
 | 
			
		||||
  "This License" refers to version 3 of the GNU General Public License.
 | 
			
		||||
  "This License" refers to version 3 of the GNU Affero General Public License.
 | 
			
		||||
 | 
			
		||||
  "Copyright" also means copyright-like laws that apply to other kinds of
 | 
			
		||||
works, such as semiconductor masks.
 | 
			
		||||
@@ -549,35 +537,45 @@ to collect a royalty for further conveying from those to whom you convey
 | 
			
		||||
the Program, the only way you could satisfy both those terms and this
 | 
			
		||||
License would be to refrain entirely from conveying the Program.
 | 
			
		||||
 | 
			
		||||
  13. Use with the GNU Affero General Public License.
 | 
			
		||||
  13. Remote Network Interaction; Use with the GNU General Public License.
 | 
			
		||||
 | 
			
		||||
  Notwithstanding any other provision of this License, if you modify the
 | 
			
		||||
Program, your modified version must prominently offer all users
 | 
			
		||||
interacting with it remotely through a computer network (if your version
 | 
			
		||||
supports such interaction) an opportunity to receive the Corresponding
 | 
			
		||||
Source of your version by providing access to the Corresponding Source
 | 
			
		||||
from a network server at no charge, through some standard or customary
 | 
			
		||||
means of facilitating copying of software.  This Corresponding Source
 | 
			
		||||
shall include the Corresponding Source for any work covered by version 3
 | 
			
		||||
of the GNU General Public License that is incorporated pursuant to the
 | 
			
		||||
following paragraph.
 | 
			
		||||
 | 
			
		||||
  Notwithstanding any other provision of this License, you have
 | 
			
		||||
permission to link or combine any covered work with a work licensed
 | 
			
		||||
under version 3 of the GNU Affero General Public License into a single
 | 
			
		||||
under version 3 of the GNU General Public License into a single
 | 
			
		||||
combined work, and to convey the resulting work.  The terms of this
 | 
			
		||||
License will continue to apply to the part which is the covered work,
 | 
			
		||||
but the special requirements of the GNU Affero General Public License,
 | 
			
		||||
section 13, concerning interaction through a network will apply to the
 | 
			
		||||
combination as such.
 | 
			
		||||
but the work with which it is combined will remain governed by version
 | 
			
		||||
3 of the GNU General Public License.
 | 
			
		||||
 | 
			
		||||
  14. Revised Versions of this License.
 | 
			
		||||
 | 
			
		||||
  The Free Software Foundation may publish revised and/or new versions of
 | 
			
		||||
the GNU General Public License from time to time.  Such new versions will
 | 
			
		||||
be similar in spirit to the present version, but may differ in detail to
 | 
			
		||||
the GNU Affero General Public License from time to time.  Such new versions
 | 
			
		||||
will be similar in spirit to the present version, but may differ in detail to
 | 
			
		||||
address new problems or concerns.
 | 
			
		||||
 | 
			
		||||
  Each version is given a distinguishing version number.  If the
 | 
			
		||||
Program specifies that a certain numbered version of the GNU General
 | 
			
		||||
Program specifies that a certain numbered version of the GNU Affero General
 | 
			
		||||
Public License "or any later version" applies to it, you have the
 | 
			
		||||
option of following the terms and conditions either of that numbered
 | 
			
		||||
version or of any later version published by the Free Software
 | 
			
		||||
Foundation.  If the Program does not specify a version number of the
 | 
			
		||||
GNU General Public License, you may choose any version ever published
 | 
			
		||||
GNU Affero General Public License, you may choose any version ever published
 | 
			
		||||
by the Free Software Foundation.
 | 
			
		||||
 | 
			
		||||
  If the Program specifies that a proxy can decide which future
 | 
			
		||||
versions of the GNU General Public License can be used, that proxy's
 | 
			
		||||
versions of the GNU Affero General Public License can be used, that proxy's
 | 
			
		||||
public statement of acceptance of a version permanently authorizes you
 | 
			
		||||
to choose that version for the Program.
 | 
			
		||||
 | 
			
		||||
@@ -631,44 +629,33 @@ to attach them to the start of each source file to most effectively
 | 
			
		||||
state the exclusion of warranty; and each file should have at least
 | 
			
		||||
the "copyright" line and a pointer to where the full notice is found.
 | 
			
		||||
 | 
			
		||||
    Vuls - Vulnerability Scanner
 | 
			
		||||
    Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
    <one line to give the program's name and a brief idea of what it does.>
 | 
			
		||||
    Copyright (C) <year>  <name of author>
 | 
			
		||||
 | 
			
		||||
    This program is free software: you can redistribute it and/or modify
 | 
			
		||||
    it under the terms of the GNU General Public License as published by
 | 
			
		||||
    the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
    it under the terms of the GNU Affero General Public License as published
 | 
			
		||||
    by the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
    (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
    This program is distributed in the hope that it will be useful,
 | 
			
		||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
    GNU General Public License for more details.
 | 
			
		||||
    GNU Affero General Public License for more details.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License
 | 
			
		||||
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
    You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
Also add information on how to contact you by electronic and paper mail.
 | 
			
		||||
 | 
			
		||||
  If the program does terminal interaction, make it output a short
 | 
			
		||||
notice like this when it starts in an interactive mode:
 | 
			
		||||
 | 
			
		||||
    Vuls  Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
 | 
			
		||||
    This is free software, and you are welcome to redistribute it
 | 
			
		||||
    under certain conditions; type `show c' for details.
 | 
			
		||||
 | 
			
		||||
The hypothetical commands `show w' and `show c' should show the appropriate
 | 
			
		||||
parts of the General Public License.  Of course, your program's commands
 | 
			
		||||
might be different; for a GUI interface, you would use an "about box".
 | 
			
		||||
  If your software can interact with users remotely through a computer
 | 
			
		||||
network, you should also make sure that it provides a way for users to
 | 
			
		||||
get its source.  For example, if your program is a web application, its
 | 
			
		||||
interface could display a "Source" link that leads users to an archive
 | 
			
		||||
of the code.  There are many ways you could offer source, and different
 | 
			
		||||
solutions will be better for different programs; see section 13 for the
 | 
			
		||||
specific requirements.
 | 
			
		||||
 | 
			
		||||
  You should also get your employer (if you work as a programmer) or school,
 | 
			
		||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
 | 
			
		||||
For more information on this, and how to apply and follow the GNU GPL, see
 | 
			
		||||
<http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
  The GNU General Public License does not permit incorporating your program
 | 
			
		||||
into proprietary programs.  If your program is a subroutine library, you
 | 
			
		||||
may consider it more useful to permit linking proprietary applications with
 | 
			
		||||
the library.  If this is what you want to do, use the GNU Lesser General
 | 
			
		||||
Public License instead of this License.  But first, please read
 | 
			
		||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
 | 
			
		||||
For more information on this, and how to apply and follow the GNU AGPL, see
 | 
			
		||||
<https://www.gnu.org/licenses/>.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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, Raspbian
 | 
			
		||||
    - Cloud, auto-hébergement, Docker
 | 
			
		||||
- Scan d'intergiciels non inclus dans le gestionnaire de paquets de l'OS
 | 
			
		||||
    - Scan d'intergiciels, de libraries de language de programmation et framework pour des vulnérabilités
 | 
			
		||||
    - Supporte les logiciels inscrits au CPE
 | 
			
		||||
- Architecture sans agent
 | 
			
		||||
    - L'utilisateur doit seulement mettre en place VULS sur une seule machine qui se connectera aux autres via SSH
 | 
			
		||||
- Génération automatique des fichiers de configuration
 | 
			
		||||
    - Auto detection de serveurs via CIDR et génération de configuration
 | 
			
		||||
- Email et notification Slack possibles (supporte le Japonais) 
 | 
			
		||||
- Les résultats d'un scan sont accessibles dans un shell via TUI Viewer terminal.
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
# Ce que Vuls ne fait pas
 | 
			
		||||
 | 
			
		||||
- Vuls ne met pas à jour les programmes affectés par les vulnérabilités découvertes.
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
# Hello Vuls 
 | 
			
		||||
 | 
			
		||||
Ce tutoriel décrit la recherche de vulnérabilités sur une machine locale avec Vuls.
 | 
			
		||||
Voici les étapes à suivre. 
 | 
			
		||||
 | 
			
		||||
1. Démrarrage d'Amazon Linux
 | 
			
		||||
1. Autoriser les connexions SSH depuis localhost
 | 
			
		||||
1. Installation des prérequis
 | 
			
		||||
1. Déploiement de go-cve-dictionary
 | 
			
		||||
1. Deploiement de Vuls
 | 
			
		||||
1. Configuration
 | 
			
		||||
1. Préparation
 | 
			
		||||
1. Scan
 | 
			
		||||
1. TUI(Terminal-Based User Interface)
 | 
			
		||||
 | 
			
		||||
## Step1. Démrarrage d'Amazon Linux
 | 
			
		||||
 | 
			
		||||
- Nous utilisons dans cette exemple une vieille AMI (amzn-ami-hvm-2015.09.1.x86_64-gp2 - ami-383c1956)
 | 
			
		||||
- Taille de l'instance : t2.medium
 | 
			
		||||
    - La première fois, t2.medium et plus sont requis pour la récupération des CVE depuis NVD (2.3GB de mémoire utilisé)
 | 
			
		||||
    - Une fois la récupération initiale des données NVD terminée vous pouvez passer sur une instance t2.nano.
 | 
			
		||||
- Ajoutez la configuration suivante au cloud-init, afin d'éviter une mise à jour automatique lors du premier démarrage.
 | 
			
		||||
 | 
			
		||||
    - [Q: How do I disable the automatic installation of critical and important security updates on initial launch?](https://aws.amazon.com/amazon-linux-ami/faqs/?nc1=h_ls)
 | 
			
		||||
    ```
 | 
			
		||||
    #cloud-config
 | 
			
		||||
    repo_upgrade: none
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
## Step2. Paramètres SSH
 | 
			
		||||
 | 
			
		||||
Il est obligatoire que le serveur puisse se connecter à son propre serveur SSH
 | 
			
		||||
 | 
			
		||||
Générez une paire de clés SSH et ajoutez la clé publique dans le fichier authorized_keys
 | 
			
		||||
```bash
 | 
			
		||||
$ ssh-keygen -t rsa
 | 
			
		||||
$ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
 | 
			
		||||
$ chmod 600 ~/.ssh/authorized_keys
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Step3. Installation des prérequis
 | 
			
		||||
 | 
			
		||||
Vuls requiert l'installation des paquets suivants : 
 | 
			
		||||
 | 
			
		||||
- sqlite
 | 
			
		||||
- git
 | 
			
		||||
- gcc
 | 
			
		||||
- go v1.7.1 or later
 | 
			
		||||
    - https://golang.org/doc/install
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
$ ssh ec2-user@52.100.100.100  -i ~/.ssh/private.pem
 | 
			
		||||
$ sudo yum -y install sqlite git gcc
 | 
			
		||||
$ wget https://storage.googleapis.com/golang/go1.7.1.linux-amd64.tar.gz
 | 
			
		||||
$ sudo tar -C /usr/local -xzf go1.7.1.linux-amd64.tar.gz
 | 
			
		||||
$ mkdir $HOME/go
 | 
			
		||||
```
 | 
			
		||||
Ajoutez les lignes suivantes dans /etc/profile.d/goenv.sh
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
export GOROOT=/usr/local/go
 | 
			
		||||
export GOPATH=$HOME/go
 | 
			
		||||
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Ajoutons ces nouvelles variables d’environnement au shell
 | 
			
		||||
```bash
 | 
			
		||||
$ source /etc/profile.d/goenv.sh
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Step4. Déploiement de [go-cve-dictionary](https://github.com/kotakanbe/go-cve-dictionary)
 | 
			
		||||
 | 
			
		||||
go get
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
$ sudo mkdir /var/log/vuls
 | 
			
		||||
$ sudo chown ec2-user /var/log/vuls
 | 
			
		||||
$ sudo chmod 700 /var/log/vuls
 | 
			
		||||
$ go get github.com/kotakanbe/go-cve-dictionary
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Démarrez go-cve-dictionary en mode serveur.
 | 
			
		||||
Lors de son premier démarrage go-cve-dictionary récupère la liste des vulnérabilités depuis NVD
 | 
			
		||||
Cette opération prend environ 10 minutes (sur AWS).  
 | 
			
		||||
 | 
			
		||||
## Step5. Déploiement de Vuls
 | 
			
		||||
 | 
			
		||||
Ouvrez un second terminal, connectez vous à l'instance ec2 via SSH
 | 
			
		||||
 | 
			
		||||
go get
 | 
			
		||||
```
 | 
			
		||||
$ go get github.com/future-architect/vuls
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Step6. Configuration
 | 
			
		||||
 | 
			
		||||
Créez un fichier de configuration (TOML format).
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
$ cat config.toml
 | 
			
		||||
[servers]
 | 
			
		||||
 | 
			
		||||
[servers.172-31-4-82]
 | 
			
		||||
host         = "172.31.4.82"
 | 
			
		||||
port        = "22"
 | 
			
		||||
user        = "ec2-user"
 | 
			
		||||
keyPath     = "/home/ec2-user/.ssh/id_rsa"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Step7. Configuration des serveurs cibles vuls  
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
$ vuls prepare
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Step8. Scan
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
$ vuls scan -cve-dictionary-dbpath=$PWD/cve.sqlite3
 | 
			
		||||
INFO[0000] Begin scanning (config: /home/ec2-user/config.toml)
 | 
			
		||||
 | 
			
		||||
... snip ...
 | 
			
		||||
 | 
			
		||||
172-31-4-82 (amazon 2015.09)
 | 
			
		||||
============================
 | 
			
		||||
CVE-2016-0494   10.0    Unspecified vulnerability in the Java SE and Java SE Embedded components in Oracle
 | 
			
		||||
                        Java SE 6u105, 7u91, and 8u66 and Java SE Embedded 8u65 allows remote attackers to
 | 
			
		||||
                        affect confidentiality, integrity, and availability via unknown vectors related to
 | 
			
		||||
                        2D.
 | 
			
		||||
... snip ...
 | 
			
		||||
 | 
			
		||||
CVE-2016-0494
 | 
			
		||||
-------------
 | 
			
		||||
Score           10.0 (High)
 | 
			
		||||
Vector          (AV:N/AC:L/Au:N/C:C/I:C/A:C)
 | 
			
		||||
Summary         Unspecified vulnerability in the Java SE and Java SE Embedded components in Oracle Java SE 6u105,
 | 
			
		||||
                7u91, and 8u66 and Java SE Embedded 8u65 allows remote attackers to affect confidentiality,
 | 
			
		||||
                integrity, and availability via unknown vectors related to 2D.
 | 
			
		||||
NVD             https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-0494
 | 
			
		||||
MITRE           https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-0494
 | 
			
		||||
CVE Details     http://www.cvedetails.com/cve/CVE-2016-0494
 | 
			
		||||
CVSS Calculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-0494&vector=(AV:N/AC:L/Au:N/C:C/I:C/A:C)
 | 
			
		||||
RHEL-CVE        https://access.redhat.com/security/cve/CVE-2016-0494
 | 
			
		||||
ALAS-2016-643   https://alas.aws.amazon.com/ALAS-2016-643.html
 | 
			
		||||
Package/CPE     java-1.7.0-openjdk-1.7.0.91-2.6.2.2.63.amzn1 -> java-1.7.0-openjdk-1:1.7.0.95-2.6.4.0.65.amzn1
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Step9. TUI
 | 
			
		||||
 | 
			
		||||
Les résultats de Vuls peuvent etre affichés dans un Shell via TUI (Terminal-Based User Interface).
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
$ vuls tui
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
For more information see [README in English](https://github.com/future-architect/vuls/blob/master/README.md)  
 | 
			
		||||
							
								
								
									
										1896
									
								
								README.ja.md
									
									
									
									
									
								
							
							
						
						
							
								
								
									
										36
									
								
								cache/bolt.go
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -1,30 +1,13 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package cache
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"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
 | 
			
		||||
@@ -69,7 +52,7 @@ func (b *Bolt) createBucketIfNotExists(name string) error {
 | 
			
		||||
	return b.db.Update(func(tx *bolt.Tx) error {
 | 
			
		||||
		_, err := tx.CreateBucketIfNotExists([]byte(name))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("Failed to create bucket: %s", err)
 | 
			
		||||
			return xerrors.Errorf("Failed to create bucket: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
@@ -98,7 +81,7 @@ func (b Bolt) RefreshMeta(meta Meta) error {
 | 
			
		||||
	meta.CreatedAt = time.Now()
 | 
			
		||||
	jsonBytes, err := json.Marshal(meta)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to marshal to JSON: %s", err)
 | 
			
		||||
		return xerrors.Errorf("Failed to marshal to JSON: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	return b.db.Update(func(tx *bolt.Tx) error {
 | 
			
		||||
		bkt := tx.Bucket([]byte(metabucket))
 | 
			
		||||
@@ -114,7 +97,7 @@ func (b Bolt) RefreshMeta(meta Meta) error {
 | 
			
		||||
func (b Bolt) EnsureBuckets(meta Meta) error {
 | 
			
		||||
	jsonBytes, err := json.Marshal(meta)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to marshal to JSON: %s", err)
 | 
			
		||||
		return 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)
 | 
			
		||||
@@ -158,12 +141,12 @@ func (b Bolt) PrettyPrint(meta Meta) error {
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChangelog get the changelgo of specified packName from the Bucket
 | 
			
		||||
// GetChangelog get the changelog of specified packName from the Bucket
 | 
			
		||||
func (b Bolt) GetChangelog(servername, packName string) (changelog string, err error) {
 | 
			
		||||
	err = b.db.View(func(tx *bolt.Tx) error {
 | 
			
		||||
		bkt := tx.Bucket([]byte(servername))
 | 
			
		||||
		if bkt == nil {
 | 
			
		||||
			return fmt.Errorf("Failed to get Bucket: %s", servername)
 | 
			
		||||
			return xerrors.Errorf("Failed to get Bucket: %s", servername)
 | 
			
		||||
		}
 | 
			
		||||
		v := bkt.Get([]byte(packName))
 | 
			
		||||
		if v == nil {
 | 
			
		||||
@@ -181,11 +164,8 @@ func (b Bolt) PutChangelog(servername, packName, changelog string) error {
 | 
			
		||||
	return b.db.Update(func(tx *bolt.Tx) error {
 | 
			
		||||
		bkt := tx.Bucket([]byte(servername))
 | 
			
		||||
		if bkt == nil {
 | 
			
		||||
			return fmt.Errorf("Failed to get Bucket: %s", servername)
 | 
			
		||||
			return xerrors.Errorf("Failed to get Bucket: %s", servername)
 | 
			
		||||
		}
 | 
			
		||||
		if err := bkt.Put([]byte(packName), []byte(changelog)); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
		return bkt.Put([]byte(packName), []byte(changelog))
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										21
									
								
								cache/bolt_test.go
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -1,20 +1,3 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package cache
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
@@ -63,7 +46,7 @@ func TestSetupBolt(t *testing.T) {
 | 
			
		||||
		t.Errorf("Failed to open bolt: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	db.View(func(tx *bolt.Tx) error {
 | 
			
		||||
	_ = db.View(func(tx *bolt.Tx) error {
 | 
			
		||||
		bkt := tx.Bucket([]byte(metabucket))
 | 
			
		||||
		if bkt == nil {
 | 
			
		||||
			t.Errorf("Meta bucket nof found")
 | 
			
		||||
@@ -104,7 +87,7 @@ func TestEnsureBuckets(t *testing.T) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Failed to open bolt: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	db.View(func(tx *bolt.Tx) error {
 | 
			
		||||
	_ = db.View(func(tx *bolt.Tx) error {
 | 
			
		||||
		bkt := tx.Bucket([]byte(servername))
 | 
			
		||||
		if bkt == nil {
 | 
			
		||||
			t.Errorf("Meta bucket nof found")
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								cache/db.go
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -1,20 +1,3 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package cache
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										36
									
								
								cmd/scanner/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,36 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"context"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	commands "github.com/future-architect/vuls/subcmds"
 | 
			
		||||
	"github.com/google/subcommands"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	subcommands.Register(subcommands.HelpCommand(), "")
 | 
			
		||||
	subcommands.Register(subcommands.FlagsCommand(), "")
 | 
			
		||||
	subcommands.Register(subcommands.CommandsCommand(), "")
 | 
			
		||||
	subcommands.Register(&commands.DiscoverCmd{}, "discover")
 | 
			
		||||
	subcommands.Register(&commands.ScanCmd{}, "scan")
 | 
			
		||||
	subcommands.Register(&commands.HistoryCmd{}, "history")
 | 
			
		||||
	subcommands.Register(&commands.ConfigtestCmd{}, "configtest")
 | 
			
		||||
	subcommands.Register(&commands.SaaSCmd{}, "saas")
 | 
			
		||||
 | 
			
		||||
	var v = flag.Bool("v", false, "Show version")
 | 
			
		||||
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	if *v {
 | 
			
		||||
		fmt.Printf("vuls %s %s\n", config.Version, config.Revision)
 | 
			
		||||
		os.Exit(int(subcommands.ExitSuccess))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
	os.Exit(int(subcommands.Execute(ctx)))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										38
									
								
								cmd/vuls/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,38 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"context"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	commands "github.com/future-architect/vuls/subcmds"
 | 
			
		||||
	"github.com/google/subcommands"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	subcommands.Register(subcommands.HelpCommand(), "")
 | 
			
		||||
	subcommands.Register(subcommands.FlagsCommand(), "")
 | 
			
		||||
	subcommands.Register(subcommands.CommandsCommand(), "")
 | 
			
		||||
	subcommands.Register(&commands.DiscoverCmd{}, "discover")
 | 
			
		||||
	subcommands.Register(&commands.TuiCmd{}, "tui")
 | 
			
		||||
	subcommands.Register(&commands.ScanCmd{}, "scan")
 | 
			
		||||
	subcommands.Register(&commands.HistoryCmd{}, "history")
 | 
			
		||||
	subcommands.Register(&commands.ReportCmd{}, "report")
 | 
			
		||||
	subcommands.Register(&commands.ConfigtestCmd{}, "configtest")
 | 
			
		||||
	subcommands.Register(&commands.ServerCmd{}, "server")
 | 
			
		||||
 | 
			
		||||
	var v = flag.Bool("v", false, "Show version")
 | 
			
		||||
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	if *v {
 | 
			
		||||
		fmt.Printf("vuls %s %s\n", config.Version, config.Revision)
 | 
			
		||||
		os.Exit(int(subcommands.ExitSuccess))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
	os.Exit(int(subcommands.Execute(ctx)))
 | 
			
		||||
}
 | 
			
		||||
@@ -1,175 +0,0 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package commands
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"text/template"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/subcommands"
 | 
			
		||||
 | 
			
		||||
	ps "github.com/kotakanbe/go-pingscanner"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DiscoverCmd is Subcommand of host discovery mode
 | 
			
		||||
type DiscoverCmd struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Name return subcommand name
 | 
			
		||||
func (*DiscoverCmd) Name() string { return "discover" }
 | 
			
		||||
 | 
			
		||||
// Synopsis return synopsis
 | 
			
		||||
func (*DiscoverCmd) Synopsis() string { return "Host discovery in the CIDR" }
 | 
			
		||||
 | 
			
		||||
// Usage return usage
 | 
			
		||||
func (*DiscoverCmd) Usage() string {
 | 
			
		||||
	return `discover:
 | 
			
		||||
	discover 192.168.0.0/24
 | 
			
		||||
 | 
			
		||||
`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetFlags set flag
 | 
			
		||||
func (p *DiscoverCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Execute execute
 | 
			
		||||
func (p *DiscoverCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
 | 
			
		||||
	// validate
 | 
			
		||||
	if len(f.Args()) == 0 {
 | 
			
		||||
		logrus.Errorf("Usage: " + p.Usage())
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, cidr := range f.Args() {
 | 
			
		||||
		scanner := ps.PingScanner{
 | 
			
		||||
			CIDR: cidr,
 | 
			
		||||
			PingOptions: []string{
 | 
			
		||||
				"-c1",
 | 
			
		||||
			},
 | 
			
		||||
			NumOfConcurrency: 100,
 | 
			
		||||
		}
 | 
			
		||||
		hosts, err := scanner.Scan()
 | 
			
		||||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Errorf("Host Discovery failed. err: %s", err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(hosts) < 1 {
 | 
			
		||||
			logrus.Errorf("Active hosts not found in %s", cidr)
 | 
			
		||||
			return subcommands.ExitSuccess
 | 
			
		||||
		} else if err := printConfigToml(hosts); err != nil {
 | 
			
		||||
			logrus.Errorf("Failed to parse template. err: %s", err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return subcommands.ExitSuccess
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Output the template of config.toml
 | 
			
		||||
func printConfigToml(ips []string) (err error) {
 | 
			
		||||
	const tomlTemplate = `
 | 
			
		||||
[slack]
 | 
			
		||||
hookURL      = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
 | 
			
		||||
channel      = "#channel-name"
 | 
			
		||||
#channel      = "${servername}"
 | 
			
		||||
iconEmoji    = ":ghost:"
 | 
			
		||||
authUser     = "username"
 | 
			
		||||
notifyUsers  = ["@username"]
 | 
			
		||||
 | 
			
		||||
[email]
 | 
			
		||||
smtpAddr      = "smtp.example.com"
 | 
			
		||||
smtpPort      = "587"
 | 
			
		||||
user          = "username"
 | 
			
		||||
password      = "password"
 | 
			
		||||
from          = "from@example.com"
 | 
			
		||||
to            = ["to@example.com"]
 | 
			
		||||
cc            = ["cc@example.com"]
 | 
			
		||||
subjectPrefix = "[vuls]"
 | 
			
		||||
 | 
			
		||||
[default]
 | 
			
		||||
#port        = "22"
 | 
			
		||||
#user        = "username"
 | 
			
		||||
#keyPath     = "/home/username/.ssh/id_rsa"
 | 
			
		||||
#cpeNames = [
 | 
			
		||||
#  "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
 | 
			
		||||
#]
 | 
			
		||||
#dependencyCheckXMLPath = "/tmp/dependency-check-report.xml"
 | 
			
		||||
#ignoreCves = ["CVE-2014-6271"]
 | 
			
		||||
#optional = [
 | 
			
		||||
#    ["key", "value"],
 | 
			
		||||
#]
 | 
			
		||||
#containers = ["${running}"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[servers]
 | 
			
		||||
{{- $names:=  .Names}}
 | 
			
		||||
{{range $i, $ip := .IPs}}
 | 
			
		||||
[servers.{{index $names $i}}]
 | 
			
		||||
host         = "{{$ip}}"
 | 
			
		||||
#port        = "22"
 | 
			
		||||
#user        = "root"
 | 
			
		||||
#keyPath     = "/home/username/.ssh/id_rsa"
 | 
			
		||||
#cpeNames = [
 | 
			
		||||
#  "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
 | 
			
		||||
#]
 | 
			
		||||
#dependencyCheckXMLPath = "/tmp/dependency-check-report.xml"
 | 
			
		||||
#ignoreCves = ["CVE-2014-0160"]
 | 
			
		||||
#optional = [
 | 
			
		||||
#    ["key", "value"],
 | 
			
		||||
#]
 | 
			
		||||
#[servers.{{index $names $i}}.containers]
 | 
			
		||||
#type = "docker" #or "lxd" default: docker
 | 
			
		||||
#includes = ["${running}"]
 | 
			
		||||
#excludes = ["container_name_a", "4aa37a8b63b9"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
{{end}}
 | 
			
		||||
 | 
			
		||||
`
 | 
			
		||||
	var tpl *template.Template
 | 
			
		||||
	if tpl, err = template.New("template").Parse(tomlTemplate); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	type activeHosts struct {
 | 
			
		||||
		IPs   []string
 | 
			
		||||
		Names []string
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	a := activeHosts{IPs: ips}
 | 
			
		||||
	names := []string{}
 | 
			
		||||
	for _, ip := range ips {
 | 
			
		||||
		// TOML section header must not contain "."
 | 
			
		||||
		name := strings.Replace(ip, ".", "-", -1)
 | 
			
		||||
		names = append(names, name)
 | 
			
		||||
	}
 | 
			
		||||
	a.Names = names
 | 
			
		||||
 | 
			
		||||
	fmt.Println("# Create config.toml using below and then ./vuls -config=/path/to/config.toml")
 | 
			
		||||
	if err = tpl.Execute(os.Stdout, a); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
@@ -1,454 +0,0 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package commands
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	c "github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/oval"
 | 
			
		||||
	"github.com/future-architect/vuls/report"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/google/subcommands"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ReportCmd is subcommand for reporting
 | 
			
		||||
type ReportCmd struct {
 | 
			
		||||
	lang       string
 | 
			
		||||
	debug      bool
 | 
			
		||||
	debugSQL   bool
 | 
			
		||||
	configPath string
 | 
			
		||||
	resultsDir string
 | 
			
		||||
	logDir     string
 | 
			
		||||
	refreshCve bool
 | 
			
		||||
 | 
			
		||||
	cvssScoreOver      float64
 | 
			
		||||
	ignoreUnscoredCves bool
 | 
			
		||||
	ignoreUnfixed      bool
 | 
			
		||||
 | 
			
		||||
	httpProxy string
 | 
			
		||||
 | 
			
		||||
	cveDBType string
 | 
			
		||||
	cveDBPath string
 | 
			
		||||
	cveDBURL  string
 | 
			
		||||
 | 
			
		||||
	ovalDBType string
 | 
			
		||||
	ovalDBPath string
 | 
			
		||||
	ovalDBURL  string
 | 
			
		||||
 | 
			
		||||
	toSlack     bool
 | 
			
		||||
	toEMail     bool
 | 
			
		||||
	toLocalFile bool
 | 
			
		||||
	toS3        bool
 | 
			
		||||
	toAzureBlob bool
 | 
			
		||||
 | 
			
		||||
	formatJSON        bool
 | 
			
		||||
	formatXML         bool
 | 
			
		||||
	formatOneEMail    bool
 | 
			
		||||
	formatOneLineText bool
 | 
			
		||||
	formatShortText   bool
 | 
			
		||||
	formatFullText    bool
 | 
			
		||||
 | 
			
		||||
	gzip bool
 | 
			
		||||
 | 
			
		||||
	awsProfile      string
 | 
			
		||||
	awsS3Bucket     string
 | 
			
		||||
	awsS3ResultsDir string
 | 
			
		||||
	awsRegion       string
 | 
			
		||||
 | 
			
		||||
	azureAccount   string
 | 
			
		||||
	azureKey       string
 | 
			
		||||
	azureContainer string
 | 
			
		||||
 | 
			
		||||
	pipe bool
 | 
			
		||||
 | 
			
		||||
	diff bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Name return subcommand name
 | 
			
		||||
func (*ReportCmd) Name() string { return "report" }
 | 
			
		||||
 | 
			
		||||
// Synopsis return synopsis
 | 
			
		||||
func (*ReportCmd) Synopsis() string { return "Reporting" }
 | 
			
		||||
 | 
			
		||||
// Usage return usage
 | 
			
		||||
func (*ReportCmd) Usage() string {
 | 
			
		||||
	return `report:
 | 
			
		||||
	report
 | 
			
		||||
		[-lang=en|ja]
 | 
			
		||||
		[-config=/path/to/config.toml]
 | 
			
		||||
		[-results-dir=/path/to/results]
 | 
			
		||||
		[-log-dir=/path/to/log]
 | 
			
		||||
		[-refresh-cve]
 | 
			
		||||
		[-cvedb-type=sqlite3|mysql|postgres]
 | 
			
		||||
		[-cvedb-path=/path/to/cve.sqlite3]
 | 
			
		||||
		[-cvedb-url=http://127.0.0.1:1323 or DB connection string]
 | 
			
		||||
		[-ovaldb-type=sqlite3|mysql]
 | 
			
		||||
		[-ovaldb-path=/path/to/oval.sqlite3]
 | 
			
		||||
		[-ovaldb-url=http://127.0.0.1:1324 or DB connection string]
 | 
			
		||||
		[-cvss-over=7]
 | 
			
		||||
		[-diff]
 | 
			
		||||
		[-ignore-unscored-cves]
 | 
			
		||||
		[-ignore-unfixed]
 | 
			
		||||
		[-to-email]
 | 
			
		||||
		[-to-slack]
 | 
			
		||||
		[-to-localfile]
 | 
			
		||||
		[-to-s3]
 | 
			
		||||
		[-to-azure-blob]
 | 
			
		||||
		[-format-json]
 | 
			
		||||
		[-format-xml]
 | 
			
		||||
		[-format-one-email]
 | 
			
		||||
		[-format-one-line-text]
 | 
			
		||||
		[-format-short-text]
 | 
			
		||||
		[-format-full-text]
 | 
			
		||||
		[-gzip]
 | 
			
		||||
		[-aws-profile=default]
 | 
			
		||||
		[-aws-region=us-west-2]
 | 
			
		||||
		[-aws-s3-bucket=bucket_name]
 | 
			
		||||
		[-aws-s3-results-dir=/bucket/path/to/results]
 | 
			
		||||
		[-azure-account=account]
 | 
			
		||||
		[-azure-key=key]
 | 
			
		||||
		[-azure-container=container]
 | 
			
		||||
		[-http-proxy=http://192.168.0.1:8080]
 | 
			
		||||
		[-debug]
 | 
			
		||||
		[-debug-sql]
 | 
			
		||||
		[-pipe]
 | 
			
		||||
 | 
			
		||||
		[RFC3339 datetime format under results dir]
 | 
			
		||||
`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetFlags set flag
 | 
			
		||||
func (p *ReportCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
	f.StringVar(&p.lang, "lang", "en", "[en|ja]")
 | 
			
		||||
	f.BoolVar(&p.debug, "debug", false, "debug mode")
 | 
			
		||||
	f.BoolVar(&p.debugSQL, "debug-sql", false, "SQL debug mode")
 | 
			
		||||
 | 
			
		||||
	wd, _ := os.Getwd()
 | 
			
		||||
 | 
			
		||||
	defaultConfPath := filepath.Join(wd, "config.toml")
 | 
			
		||||
	f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
 | 
			
		||||
 | 
			
		||||
	defaultResultsDir := filepath.Join(wd, "results")
 | 
			
		||||
	f.StringVar(&p.resultsDir, "results-dir", defaultResultsDir, "/path/to/results")
 | 
			
		||||
 | 
			
		||||
	defaultLogDir := util.GetDefaultLogDir()
 | 
			
		||||
	f.StringVar(&p.logDir, "log-dir", defaultLogDir, "/path/to/log")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(
 | 
			
		||||
		&p.refreshCve,
 | 
			
		||||
		"refresh-cve",
 | 
			
		||||
		false,
 | 
			
		||||
		"Refresh CVE information in JSON file under results dir")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(
 | 
			
		||||
		&p.cveDBType,
 | 
			
		||||
		"cvedb-type",
 | 
			
		||||
		"sqlite3",
 | 
			
		||||
		"DB type for fetching CVE dictionary (sqlite3, mysql or postgres)")
 | 
			
		||||
 | 
			
		||||
	defaultCveDBPath := filepath.Join(wd, "cve.sqlite3")
 | 
			
		||||
	f.StringVar(
 | 
			
		||||
		&p.cveDBPath,
 | 
			
		||||
		"cvedb-path",
 | 
			
		||||
		defaultCveDBPath,
 | 
			
		||||
		"/path/to/sqlite3 (For get cve detail from cve.sqlite3)")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(
 | 
			
		||||
		&p.cveDBURL,
 | 
			
		||||
		"cvedb-url",
 | 
			
		||||
		"",
 | 
			
		||||
		"http://cve-dictionary.com:1323 or mysql connection string")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(
 | 
			
		||||
		&p.ovalDBType,
 | 
			
		||||
		"ovaldb-type",
 | 
			
		||||
		"sqlite3",
 | 
			
		||||
		"DB type for fetching OVAL dictionary (sqlite3 or mysql)")
 | 
			
		||||
 | 
			
		||||
	defaultOvalDBPath := filepath.Join(wd, "oval.sqlite3")
 | 
			
		||||
	f.StringVar(
 | 
			
		||||
		&p.ovalDBPath,
 | 
			
		||||
		"ovaldb-path",
 | 
			
		||||
		defaultOvalDBPath,
 | 
			
		||||
		"/path/to/sqlite3 (For get oval detail from oval.sqlite3)")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(
 | 
			
		||||
		&p.ovalDBURL,
 | 
			
		||||
		"ovaldb-url",
 | 
			
		||||
		"",
 | 
			
		||||
		"http://goval-dictionary.com:1324 or mysql connection string")
 | 
			
		||||
 | 
			
		||||
	f.Float64Var(
 | 
			
		||||
		&p.cvssScoreOver,
 | 
			
		||||
		"cvss-over",
 | 
			
		||||
		0,
 | 
			
		||||
		"-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&p.diff,
 | 
			
		||||
		"diff",
 | 
			
		||||
		false,
 | 
			
		||||
		fmt.Sprintf("Difference between previous result and current result "))
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(
 | 
			
		||||
		&p.ignoreUnscoredCves,
 | 
			
		||||
		"ignore-unscored-cves",
 | 
			
		||||
		false,
 | 
			
		||||
		"Don't report the unscored CVEs")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(
 | 
			
		||||
		&p.ignoreUnfixed,
 | 
			
		||||
		"ignore-unfixed",
 | 
			
		||||
		false,
 | 
			
		||||
		"Don't report the unfixed CVEs")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(
 | 
			
		||||
		&p.httpProxy,
 | 
			
		||||
		"http-proxy",
 | 
			
		||||
		"",
 | 
			
		||||
		"http://proxy-url:port (default: empty)")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&p.formatJSON,
 | 
			
		||||
		"format-json",
 | 
			
		||||
		false,
 | 
			
		||||
		fmt.Sprintf("JSON format"))
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&p.formatXML,
 | 
			
		||||
		"format-xml",
 | 
			
		||||
		false,
 | 
			
		||||
		fmt.Sprintf("XML format"))
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&p.formatOneEMail,
 | 
			
		||||
		"format-one-email",
 | 
			
		||||
		false,
 | 
			
		||||
		"Send all the host report via only one EMail (Specify with -to-email)")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&p.formatOneLineText,
 | 
			
		||||
		"format-one-line-text",
 | 
			
		||||
		false,
 | 
			
		||||
		fmt.Sprintf("One line summary in plain text"))
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&p.formatShortText,
 | 
			
		||||
		"format-short-text",
 | 
			
		||||
		false,
 | 
			
		||||
		fmt.Sprintf("Summary in plain text"))
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&p.formatFullText,
 | 
			
		||||
		"format-full-text",
 | 
			
		||||
		false,
 | 
			
		||||
		fmt.Sprintf("Detail report in plain text"))
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&p.gzip, "gzip", false, "gzip compression")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&p.toSlack, "to-slack", false, "Send report via Slack")
 | 
			
		||||
	f.BoolVar(&p.toEMail, "to-email", false, "Send report via Email")
 | 
			
		||||
	f.BoolVar(&p.toLocalFile,
 | 
			
		||||
		"to-localfile",
 | 
			
		||||
		false,
 | 
			
		||||
		fmt.Sprintf("Write report to localfile"))
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&p.toS3,
 | 
			
		||||
		"to-s3",
 | 
			
		||||
		false,
 | 
			
		||||
		"Write report to S3 (bucket/yyyyMMdd_HHmm/servername.json/xml/txt)")
 | 
			
		||||
	f.StringVar(&p.awsProfile, "aws-profile", "default", "AWS profile to use")
 | 
			
		||||
	f.StringVar(&p.awsRegion, "aws-region", "us-east-1", "AWS region to use")
 | 
			
		||||
	f.StringVar(&p.awsS3Bucket, "aws-s3-bucket", "", "S3 bucket name")
 | 
			
		||||
	f.StringVar(&p.awsS3ResultsDir, "aws-s3-results-dir", "", "/bucket/path/to/results")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&p.toAzureBlob,
 | 
			
		||||
		"to-azure-blob",
 | 
			
		||||
		false,
 | 
			
		||||
		"Write report to Azure Storage blob (container/yyyyMMdd_HHmm/servername.json/xml/txt)")
 | 
			
		||||
	f.StringVar(&p.azureAccount,
 | 
			
		||||
		"azure-account",
 | 
			
		||||
		"",
 | 
			
		||||
		"Azure account name to use. AZURE_STORAGE_ACCOUNT environment variable is used if not specified")
 | 
			
		||||
	f.StringVar(&p.azureKey,
 | 
			
		||||
		"azure-key",
 | 
			
		||||
		"",
 | 
			
		||||
		"Azure account key to use. AZURE_STORAGE_ACCESS_KEY environment variable is used if not specified")
 | 
			
		||||
	f.StringVar(&p.azureContainer, "azure-container", "", "Azure storage container name")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(
 | 
			
		||||
		&p.pipe,
 | 
			
		||||
		"pipe",
 | 
			
		||||
		false,
 | 
			
		||||
		"Use args passed via PIPE")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Execute execute
 | 
			
		||||
func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
 | 
			
		||||
	c.Conf.Debug = p.debug
 | 
			
		||||
	c.Conf.DebugSQL = p.debugSQL
 | 
			
		||||
	c.Conf.LogDir = p.logDir
 | 
			
		||||
	util.Log = util.NewCustomLogger(c.ServerInfo{})
 | 
			
		||||
 | 
			
		||||
	if err := c.Load(p.configPath, ""); err != nil {
 | 
			
		||||
		util.Log.Errorf("Error loading %s, %s", p.configPath, err)
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.Conf.Lang = p.lang
 | 
			
		||||
	c.Conf.ResultsDir = p.resultsDir
 | 
			
		||||
	c.Conf.RefreshCve = p.refreshCve
 | 
			
		||||
	c.Conf.Diff = p.diff
 | 
			
		||||
	c.Conf.CveDBType = p.cveDBType
 | 
			
		||||
	c.Conf.CveDBPath = p.cveDBPath
 | 
			
		||||
	c.Conf.CveDBURL = p.cveDBURL
 | 
			
		||||
	c.Conf.OvalDBType = p.ovalDBType
 | 
			
		||||
	c.Conf.OvalDBPath = p.ovalDBPath
 | 
			
		||||
	c.Conf.OvalDBURL = p.ovalDBURL
 | 
			
		||||
	c.Conf.CvssScoreOver = p.cvssScoreOver
 | 
			
		||||
	c.Conf.IgnoreUnscoredCves = p.ignoreUnscoredCves
 | 
			
		||||
	c.Conf.IgnoreUnfixed = p.ignoreUnfixed
 | 
			
		||||
	c.Conf.HTTPProxy = p.httpProxy
 | 
			
		||||
 | 
			
		||||
	c.Conf.FormatXML = p.formatXML
 | 
			
		||||
	c.Conf.FormatJSON = p.formatJSON
 | 
			
		||||
	c.Conf.FormatOneEMail = p.formatOneEMail
 | 
			
		||||
	c.Conf.FormatOneLineText = p.formatOneLineText
 | 
			
		||||
	c.Conf.FormatShortText = p.formatShortText
 | 
			
		||||
	c.Conf.FormatFullText = p.formatFullText
 | 
			
		||||
 | 
			
		||||
	c.Conf.GZIP = p.gzip
 | 
			
		||||
	c.Conf.Diff = p.diff
 | 
			
		||||
	c.Conf.Pipe = p.pipe
 | 
			
		||||
 | 
			
		||||
	var dir string
 | 
			
		||||
	var err error
 | 
			
		||||
	if p.diff {
 | 
			
		||||
		dir, err = report.JSONDir([]string{})
 | 
			
		||||
	} else {
 | 
			
		||||
		dir, err = report.JSONDir(f.Args())
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		util.Log.Errorf("Failed to read from JSON: %s", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// report
 | 
			
		||||
	reports := []report.ResultWriter{
 | 
			
		||||
		report.StdoutWriter{},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p.toSlack {
 | 
			
		||||
		reports = append(reports, report.SlackWriter{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p.toEMail {
 | 
			
		||||
		reports = append(reports, report.EMailWriter{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p.toLocalFile {
 | 
			
		||||
		reports = append(reports, report.LocalFileWriter{
 | 
			
		||||
			CurrentDir: dir,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p.toS3 {
 | 
			
		||||
		c.Conf.AwsRegion = p.awsRegion
 | 
			
		||||
		c.Conf.AwsProfile = p.awsProfile
 | 
			
		||||
		c.Conf.S3Bucket = p.awsS3Bucket
 | 
			
		||||
		c.Conf.S3ResultsDir = p.awsS3ResultsDir
 | 
			
		||||
		if err := report.CheckIfBucketExists(); err != nil {
 | 
			
		||||
			util.Log.Errorf("Check if there is a bucket beforehand: %s, err: %s", c.Conf.S3Bucket, err)
 | 
			
		||||
			return subcommands.ExitUsageError
 | 
			
		||||
		}
 | 
			
		||||
		reports = append(reports, report.S3Writer{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p.toAzureBlob {
 | 
			
		||||
		c.Conf.AzureAccount = p.azureAccount
 | 
			
		||||
		if len(c.Conf.AzureAccount) == 0 {
 | 
			
		||||
			c.Conf.AzureAccount = os.Getenv("AZURE_STORAGE_ACCOUNT")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		c.Conf.AzureKey = p.azureKey
 | 
			
		||||
		if len(c.Conf.AzureKey) == 0 {
 | 
			
		||||
			c.Conf.AzureKey = os.Getenv("AZURE_STORAGE_ACCESS_KEY")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		c.Conf.AzureContainer = p.azureContainer
 | 
			
		||||
		if len(c.Conf.AzureContainer) == 0 {
 | 
			
		||||
			util.Log.Error("Azure storage container name is required with -azure-container option")
 | 
			
		||||
			return subcommands.ExitUsageError
 | 
			
		||||
		}
 | 
			
		||||
		if err := report.CheckIfAzureContainerExists(); err != nil {
 | 
			
		||||
			util.Log.Errorf("Check if there is a container beforehand: %s, err: %s", c.Conf.AzureContainer, err)
 | 
			
		||||
			return subcommands.ExitUsageError
 | 
			
		||||
		}
 | 
			
		||||
		reports = append(reports, report.AzureBlobWriter{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !(p.formatJSON || p.formatOneLineText ||
 | 
			
		||||
		p.formatShortText || p.formatFullText || p.formatXML) {
 | 
			
		||||
		c.Conf.FormatShortText = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	util.Log.Info("Validating config...")
 | 
			
		||||
	if !c.Conf.ValidateOnReport() {
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
	if err := report.CveClient.CheckHealth(); err != nil {
 | 
			
		||||
		util.Log.Errorf("CVE HTTP server is not running. err: %s", err)
 | 
			
		||||
		util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with -cvedb-path option")
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
	if c.Conf.CveDBURL != "" {
 | 
			
		||||
		util.Log.Infof("cve-dictionary: %s", c.Conf.CveDBURL)
 | 
			
		||||
	} else {
 | 
			
		||||
		if c.Conf.CveDBType == "sqlite3" {
 | 
			
		||||
			util.Log.Infof("cve-dictionary: %s", c.Conf.CveDBPath)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.OvalDBURL != "" {
 | 
			
		||||
		err := oval.Base{}.CheckHTTPHealth()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Errorf("OVAL HTTP server is not running. err: %s", err)
 | 
			
		||||
			util.Log.Errorf("Run goval-dictionary as server mode before reporting or run with -ovaldb-path option")
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var res models.ScanResults
 | 
			
		||||
	if res, err = report.LoadScanResults(dir); err != nil {
 | 
			
		||||
		util.Log.Error(err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
	util.Log.Infof("Loaded: %s", dir)
 | 
			
		||||
 | 
			
		||||
	if res, err = report.FillCveInfos(res, dir); err != nil {
 | 
			
		||||
		util.Log.Error(err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, w := range reports {
 | 
			
		||||
		if err := w.Write(res...); err != nil {
 | 
			
		||||
			util.Log.Errorf("Failed to report: %s", err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return subcommands.ExitSuccess
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										225
									
								
								commands/tui.go
									
									
									
									
									
								
							
							
						
						@@ -1,225 +0,0 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package commands
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	c "github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/report"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/google/subcommands"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// TuiCmd is Subcommand of host discovery mode
 | 
			
		||||
type TuiCmd struct {
 | 
			
		||||
	lang       string
 | 
			
		||||
	debugSQL   bool
 | 
			
		||||
	debug      bool
 | 
			
		||||
	configPath string
 | 
			
		||||
	logDir     string
 | 
			
		||||
 | 
			
		||||
	resultsDir string
 | 
			
		||||
	refreshCve bool
 | 
			
		||||
 | 
			
		||||
	cvedbtype        string
 | 
			
		||||
	cvedbpath        string
 | 
			
		||||
	cveDictionaryURL string
 | 
			
		||||
 | 
			
		||||
	ovalDBType string
 | 
			
		||||
	ovalDBPath string
 | 
			
		||||
	ovalDBURL  string
 | 
			
		||||
 | 
			
		||||
	cvssScoreOver      float64
 | 
			
		||||
	ignoreUnscoredCves bool
 | 
			
		||||
	ignoreUnfixed      bool
 | 
			
		||||
 | 
			
		||||
	pipe bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Name return subcommand name
 | 
			
		||||
func (*TuiCmd) Name() string { return "tui" }
 | 
			
		||||
 | 
			
		||||
// Synopsis return synopsis
 | 
			
		||||
func (*TuiCmd) Synopsis() string { return "Run Tui view to analyze vulnerabilities" }
 | 
			
		||||
 | 
			
		||||
// Usage return usage
 | 
			
		||||
func (*TuiCmd) Usage() string {
 | 
			
		||||
	return `tui:
 | 
			
		||||
	tui
 | 
			
		||||
		[-refresh-cve]
 | 
			
		||||
		[-config=/path/to/config.toml]
 | 
			
		||||
		[-cvedb-type=sqlite3|mysql|postgres]
 | 
			
		||||
		[-cvedb-path=/path/to/cve.sqlite3]
 | 
			
		||||
		[-cvedb-url=http://127.0.0.1:1323 or DB connection string]
 | 
			
		||||
		[-ovaldb-type=sqlite3|mysql]
 | 
			
		||||
		[-ovaldb-path=/path/to/oval.sqlite3]
 | 
			
		||||
		[-ovaldb-url=http://127.0.0.1:1324 or DB connection string]
 | 
			
		||||
		[-cvss-over=7]
 | 
			
		||||
		[-ignore-unscored-cves]
 | 
			
		||||
		[-ignore-unfixed]
 | 
			
		||||
		[-results-dir=/path/to/results]
 | 
			
		||||
		[-log-dir=/path/to/log]
 | 
			
		||||
		[-debug]
 | 
			
		||||
		[-debug-sql]
 | 
			
		||||
		[-pipe]
 | 
			
		||||
 | 
			
		||||
`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetFlags set flag
 | 
			
		||||
func (p *TuiCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
	//  f.StringVar(&p.lang, "lang", "en", "[en|ja]")
 | 
			
		||||
	f.BoolVar(&p.debugSQL, "debug-sql", false, "debug SQL")
 | 
			
		||||
	f.BoolVar(&p.debug, "debug", false, "debug mode")
 | 
			
		||||
 | 
			
		||||
	defaultLogDir := util.GetDefaultLogDir()
 | 
			
		||||
	f.StringVar(&p.logDir, "log-dir", defaultLogDir, "/path/to/log")
 | 
			
		||||
 | 
			
		||||
	wd, _ := os.Getwd()
 | 
			
		||||
	defaultResultsDir := filepath.Join(wd, "results")
 | 
			
		||||
	f.StringVar(&p.resultsDir, "results-dir", defaultResultsDir, "/path/to/results")
 | 
			
		||||
 | 
			
		||||
	defaultConfPath := filepath.Join(wd, "config.toml")
 | 
			
		||||
	f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(
 | 
			
		||||
		&p.refreshCve,
 | 
			
		||||
		"refresh-cve",
 | 
			
		||||
		false,
 | 
			
		||||
		"Refresh CVE information in JSON file under results dir")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(
 | 
			
		||||
		&p.cvedbtype,
 | 
			
		||||
		"cvedb-type",
 | 
			
		||||
		"sqlite3",
 | 
			
		||||
		"DB type for fetching CVE dictionary (sqlite3, mysql or postgres)")
 | 
			
		||||
 | 
			
		||||
	defaultCveDBPath := filepath.Join(wd, "cve.sqlite3")
 | 
			
		||||
	f.StringVar(
 | 
			
		||||
		&p.cvedbpath,
 | 
			
		||||
		"cvedb-path",
 | 
			
		||||
		defaultCveDBPath,
 | 
			
		||||
		"/path/to/sqlite3 (For get cve detail from cve.sqlite3)")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(
 | 
			
		||||
		&p.cveDictionaryURL,
 | 
			
		||||
		"cvedb-url",
 | 
			
		||||
		"",
 | 
			
		||||
		"http://cve-dictionary.example.com:1323 or mysql connection string")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(
 | 
			
		||||
		&p.ovalDBType,
 | 
			
		||||
		"ovaldb-type",
 | 
			
		||||
		"sqlite3",
 | 
			
		||||
		"DB type for fetching OVAL dictionary (sqlite3 or mysql)")
 | 
			
		||||
 | 
			
		||||
	defaultOvalDBPath := filepath.Join(wd, "oval.sqlite3")
 | 
			
		||||
	f.StringVar(
 | 
			
		||||
		&p.ovalDBPath,
 | 
			
		||||
		"ovaldb-path",
 | 
			
		||||
		defaultOvalDBPath,
 | 
			
		||||
		"/path/to/sqlite3 (For get oval detail from oval.sqlite3)")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(
 | 
			
		||||
		&p.ovalDBURL,
 | 
			
		||||
		"ovaldb-url",
 | 
			
		||||
		"",
 | 
			
		||||
		"http://goval-dictionary.example.com:1324 or mysql connection string")
 | 
			
		||||
 | 
			
		||||
	f.Float64Var(
 | 
			
		||||
		&p.cvssScoreOver,
 | 
			
		||||
		"cvss-over",
 | 
			
		||||
		0,
 | 
			
		||||
		"-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(
 | 
			
		||||
		&p.ignoreUnscoredCves,
 | 
			
		||||
		"ignore-unscored-cves",
 | 
			
		||||
		false,
 | 
			
		||||
		"Don't report the unscored CVEs")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(
 | 
			
		||||
		&p.ignoreUnfixed,
 | 
			
		||||
		"ignore-unfixed",
 | 
			
		||||
		false,
 | 
			
		||||
		"Don't report the unfixed CVEs")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(
 | 
			
		||||
		&p.pipe,
 | 
			
		||||
		"pipe",
 | 
			
		||||
		false,
 | 
			
		||||
		"Use stdin via PIPE")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Execute execute
 | 
			
		||||
func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
 | 
			
		||||
	c.Conf.Lang = "en"
 | 
			
		||||
 | 
			
		||||
	// Setup Logger
 | 
			
		||||
	c.Conf.Debug = p.debug
 | 
			
		||||
	c.Conf.DebugSQL = p.debugSQL
 | 
			
		||||
	c.Conf.LogDir = p.logDir
 | 
			
		||||
	util.Log = util.NewCustomLogger(c.ServerInfo{})
 | 
			
		||||
	log := util.Log
 | 
			
		||||
 | 
			
		||||
	if err := c.Load(p.configPath, ""); err != nil {
 | 
			
		||||
		util.Log.Errorf("Error loading %s, %s", p.configPath, err)
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.Conf.ResultsDir = p.resultsDir
 | 
			
		||||
	c.Conf.CveDBType = p.cvedbtype
 | 
			
		||||
	c.Conf.CveDBPath = p.cvedbpath
 | 
			
		||||
	c.Conf.CveDBURL = p.cveDictionaryURL
 | 
			
		||||
	c.Conf.OvalDBType = p.ovalDBType
 | 
			
		||||
	c.Conf.OvalDBPath = p.ovalDBPath
 | 
			
		||||
	c.Conf.OvalDBURL = p.ovalDBURL
 | 
			
		||||
	c.Conf.CvssScoreOver = p.cvssScoreOver
 | 
			
		||||
	c.Conf.IgnoreUnscoredCves = p.ignoreUnscoredCves
 | 
			
		||||
	c.Conf.IgnoreUnfixed = p.ignoreUnfixed
 | 
			
		||||
 | 
			
		||||
	log.Info("Validating config...")
 | 
			
		||||
	if !c.Conf.ValidateOnTui() {
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.Conf.Pipe = p.pipe
 | 
			
		||||
 | 
			
		||||
	dir, err := report.JSONDir(f.Args())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		util.Log.Errorf("Failed to read from JSON: %s", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
	var res models.ScanResults
 | 
			
		||||
	if res, err = report.LoadScanResults(dir); err != nil {
 | 
			
		||||
		util.Log.Error(err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
	util.Log.Infof("Loaded: %s", dir)
 | 
			
		||||
 | 
			
		||||
	if res, err = report.FillCveInfos(res, dir); err != nil {
 | 
			
		||||
		util.Log.Error(err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
	return report.RunTui(res)
 | 
			
		||||
}
 | 
			
		||||
@@ -1,38 +0,0 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package commands
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/howeyc/gopass"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func getPasswd(prompt string) (string, error) {
 | 
			
		||||
	for {
 | 
			
		||||
		fmt.Print(prompt)
 | 
			
		||||
		pass, err := gopass.GetPasswdMasked()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", fmt.Errorf("Failed to read password")
 | 
			
		||||
		}
 | 
			
		||||
		if 0 < len(pass) {
 | 
			
		||||
			return string(pass[:]), nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,18 +0,0 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package commands
 | 
			
		||||
@@ -1,20 +1,3 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1165
									
								
								config/config.go
									
									
									
									
									
								
							
							
						
						
							
								
								
									
										103
									
								
								config/config_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,103 @@
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestSyslogConfValidate(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		conf              SyslogConf
 | 
			
		||||
		expectedErrLength int
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			conf:              SyslogConf{},
 | 
			
		||||
			expectedErrLength: 0,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			conf: SyslogConf{
 | 
			
		||||
				Protocol: "tcp",
 | 
			
		||||
				Port:     "5140",
 | 
			
		||||
			},
 | 
			
		||||
			expectedErrLength: 0,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			conf: SyslogConf{
 | 
			
		||||
				Protocol: "udp",
 | 
			
		||||
				Port:     "12345",
 | 
			
		||||
				Severity: "emerg",
 | 
			
		||||
				Facility: "user",
 | 
			
		||||
			},
 | 
			
		||||
			expectedErrLength: 0,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			conf: SyslogConf{
 | 
			
		||||
				Protocol: "foo",
 | 
			
		||||
				Port:     "514",
 | 
			
		||||
			},
 | 
			
		||||
			expectedErrLength: 1,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			conf: SyslogConf{
 | 
			
		||||
				Protocol: "invalid",
 | 
			
		||||
				Port:     "-1",
 | 
			
		||||
			},
 | 
			
		||||
			expectedErrLength: 2,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			conf: SyslogConf{
 | 
			
		||||
				Protocol: "invalid",
 | 
			
		||||
				Port:     "invalid",
 | 
			
		||||
				Severity: "invalid",
 | 
			
		||||
				Facility: "invalid",
 | 
			
		||||
			},
 | 
			
		||||
			expectedErrLength: 4,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
		Conf.ToSyslog = true
 | 
			
		||||
		errs := tt.conf.Validate()
 | 
			
		||||
		if len(errs) != tt.expectedErrLength {
 | 
			
		||||
			t.Errorf("test: %d, expected %d, actual %d", i, tt.expectedErrLength, len(errs))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestDistro_MajorVersion(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in  Distro
 | 
			
		||||
		out int
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			in: Distro{
 | 
			
		||||
				Family:  Amazon,
 | 
			
		||||
				Release: "2 (2017.12)",
 | 
			
		||||
			},
 | 
			
		||||
			out: 2,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: Distro{
 | 
			
		||||
				Family:  Amazon,
 | 
			
		||||
				Release: "2017.12",
 | 
			
		||||
			},
 | 
			
		||||
			out: 1,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: Distro{
 | 
			
		||||
				Family:  CentOS,
 | 
			
		||||
				Release: "7.10",
 | 
			
		||||
			},
 | 
			
		||||
			out: 7,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
		ver, err := tt.in.MajorVersion()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("[%d] err occurred: %s", i, err)
 | 
			
		||||
		}
 | 
			
		||||
		if tt.out != ver {
 | 
			
		||||
			t.Errorf("[%d] expected %d, actual %d", i, tt.out, ver)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								config/ips.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,9 @@
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
// IPS is
 | 
			
		||||
type IPS string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// DeepSecurity is
 | 
			
		||||
	DeepSecurity IPS = "deepsecurity"
 | 
			
		||||
)
 | 
			
		||||
@@ -1,29 +1,12 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import "fmt"
 | 
			
		||||
import "golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
// JSONLoader loads configuration
 | 
			
		||||
type JSONLoader struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Load load the configuraiton JSON file specified by path arg.
 | 
			
		||||
// Load load the configuration JSON file specified by path arg.
 | 
			
		||||
func (c JSONLoader) Load(path, sudoPass, keyPass string) (err error) {
 | 
			
		||||
	return fmt.Errorf("Not implement yet")
 | 
			
		||||
	return xerrors.New("Not implement yet")
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,20 +1,3 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
// Load loads configuration
 | 
			
		||||
 
 | 
			
		||||
@@ -1,49 +1,41 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/BurntSushi/toml"
 | 
			
		||||
	"github.com/future-architect/vuls/contrib/owasp-dependency-check/parser"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
	"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.
 | 
			
		||||
// Load load the configuration TOML file specified by path arg.
 | 
			
		||||
func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
	if Conf.Debug {
 | 
			
		||||
		log.SetLevel(log.DebugLevel)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var conf Config
 | 
			
		||||
	if _, err := toml.DecodeFile(pathToToml, &conf); err != nil {
 | 
			
		||||
		log.Error("Load config failed", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Conf.EMail = conf.EMail
 | 
			
		||||
	Conf.Slack = conf.Slack
 | 
			
		||||
	Conf.Stride = conf.Stride
 | 
			
		||||
	Conf.HipChat = conf.HipChat
 | 
			
		||||
	Conf.ChatWork = conf.ChatWork
 | 
			
		||||
	Conf.Telegram = conf.Telegram
 | 
			
		||||
	Conf.Saas = conf.Saas
 | 
			
		||||
	Conf.Syslog = conf.Syslog
 | 
			
		||||
	Conf.HTTP = conf.HTTP
 | 
			
		||||
	Conf.AWS = conf.AWS
 | 
			
		||||
	Conf.Azure = conf.Azure
 | 
			
		||||
 | 
			
		||||
	Conf.CveDict = conf.CveDict
 | 
			
		||||
	Conf.OvalDict = conf.OvalDict
 | 
			
		||||
	Conf.Gost = conf.Gost
 | 
			
		||||
	Conf.Exploit = conf.Exploit
 | 
			
		||||
	Conf.Metasploit = conf.Metasploit
 | 
			
		||||
 | 
			
		||||
	d := conf.Default
 | 
			
		||||
	Conf.Default = d
 | 
			
		||||
@@ -53,54 +45,82 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
		d.KeyPassword = keyPass
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	i := 0
 | 
			
		||||
	for name, v := range conf.Servers {
 | 
			
		||||
	index := 0
 | 
			
		||||
	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}
 | 
			
		||||
		if v.Type != ServerTypePseudo {
 | 
			
		||||
			s.Host = v.Host
 | 
			
		||||
			if len(s.Host) == 0 {
 | 
			
		||||
				return xerrors.Errorf("%s is invalid. host is empty", serverName)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		s.Host = v.Host
 | 
			
		||||
		if len(s.Host) == 0 {
 | 
			
		||||
			return fmt.Errorf("%s is invalid. host is empty", name)
 | 
			
		||||
		}
 | 
			
		||||
			s.JumpServer = v.JumpServer
 | 
			
		||||
			if len(s.JumpServer) == 0 {
 | 
			
		||||
				s.JumpServer = d.JumpServer
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		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"
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		switch {
 | 
			
		||||
		case v.User != "":
 | 
			
		||||
			s.User = v.User
 | 
			
		||||
		case d.User != "":
 | 
			
		||||
			s.User = d.User
 | 
			
		||||
		default:
 | 
			
		||||
			if s.Port != "local" {
 | 
			
		||||
				return fmt.Errorf("%s is invalid. User is empty", name)
 | 
			
		||||
			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.SSHConfigPath = v.SSHConfigPath
 | 
			
		||||
			if len(s.SSHConfigPath) == 0 {
 | 
			
		||||
				s.SSHConfigPath = d.SSHConfigPath
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			s.KeyPath = v.KeyPath
 | 
			
		||||
			if len(s.KeyPath) == 0 {
 | 
			
		||||
				s.KeyPath = d.KeyPath
 | 
			
		||||
			}
 | 
			
		||||
			s.KeyPassword = v.KeyPassword
 | 
			
		||||
			if len(s.KeyPassword) == 0 {
 | 
			
		||||
				s.KeyPassword = d.KeyPassword
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.KeyPath = v.KeyPath
 | 
			
		||||
		if len(s.KeyPath) == 0 {
 | 
			
		||||
			s.KeyPath = d.KeyPath
 | 
			
		||||
		}
 | 
			
		||||
		if s.KeyPath != "" {
 | 
			
		||||
			if _, err := os.Stat(s.KeyPath); err != nil {
 | 
			
		||||
				return fmt.Errorf(
 | 
			
		||||
					"%s is invalid. keypath: %s not exists", name, s.KeyPath)
 | 
			
		||||
		s.ScanMode = v.ScanMode
 | 
			
		||||
		if len(s.ScanMode) == 0 {
 | 
			
		||||
			s.ScanMode = d.ScanMode
 | 
			
		||||
			if len(s.ScanMode) == 0 {
 | 
			
		||||
				s.ScanMode = []string{"fast"}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		//  s.KeyPassword = keyPass
 | 
			
		||||
		s.KeyPassword = v.KeyPassword
 | 
			
		||||
		if len(s.KeyPassword) == 0 {
 | 
			
		||||
			s.KeyPassword = d.KeyPassword
 | 
			
		||||
		for _, m := range s.ScanMode {
 | 
			
		||||
			switch m {
 | 
			
		||||
			case "fast":
 | 
			
		||||
				s.Mode.Set(Fast)
 | 
			
		||||
			case "fast-root":
 | 
			
		||||
				s.Mode.Set(FastRoot)
 | 
			
		||||
			case "deep":
 | 
			
		||||
				s.Mode.Set(Deep)
 | 
			
		||||
			case "offline":
 | 
			
		||||
				s.Mode.Set(Offline)
 | 
			
		||||
			default:
 | 
			
		||||
				return xerrors.Errorf("scanMode: %s of %s is invalid. Specify -fast, -fast-root, -deep or offline", m, serverName)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if err := s.Mode.validate(); err != nil {
 | 
			
		||||
			return xerrors.Errorf("%s in %s", err, serverName)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.CpeNames = v.CpeNames
 | 
			
		||||
@@ -108,26 +128,54 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
			s.CpeNames = d.CpeNames
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.DependencyCheckXMLPath = v.DependencyCheckXMLPath
 | 
			
		||||
		if len(s.DependencyCheckXMLPath) == 0 {
 | 
			
		||||
			s.DependencyCheckXMLPath = d.DependencyCheckXMLPath
 | 
			
		||||
		s.Lockfiles = v.Lockfiles
 | 
			
		||||
		if len(s.Lockfiles) == 0 {
 | 
			
		||||
			s.Lockfiles = d.Lockfiles
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Load CPEs from OWASP Dependency Check XML
 | 
			
		||||
		if len(s.DependencyCheckXMLPath) != 0 {
 | 
			
		||||
			cpes, err := parser.Parse(s.DependencyCheckXMLPath)
 | 
			
		||||
		s.FindLock = v.FindLock
 | 
			
		||||
 | 
			
		||||
		for i, n := range s.CpeNames {
 | 
			
		||||
			uri, err := toCpeURI(n)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return fmt.Errorf(
 | 
			
		||||
					"Failed to read OWASP Dependency Check XML: %s", err)
 | 
			
		||||
				return xerrors.Errorf("Failed to parse CPENames %s in %s, err: %w", n, serverName, err)
 | 
			
		||||
			}
 | 
			
		||||
			log.Debugf("Loaded from OWASP Dependency Check XML: %s",
 | 
			
		||||
				s.ServerName)
 | 
			
		||||
			s.CpeNames = append(s.CpeNames, cpes...)
 | 
			
		||||
			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
 | 
			
		||||
		if len(s.Containers.Includes) == 0 {
 | 
			
		||||
			s.Containers = d.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
 | 
			
		||||
@@ -144,19 +192,43 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.Optional = v.Optional
 | 
			
		||||
		for _, dkv := range d.Optional {
 | 
			
		||||
		s.IgnorePkgsRegexp = v.IgnorePkgsRegexp
 | 
			
		||||
		for _, pkg := range d.IgnorePkgsRegexp {
 | 
			
		||||
			found := false
 | 
			
		||||
			for _, kv := range s.Optional {
 | 
			
		||||
				if dkv[0] == kv[0] {
 | 
			
		||||
			for _, p := range s.IgnorePkgsRegexp {
 | 
			
		||||
				if pkg == p {
 | 
			
		||||
					found = true
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if !found {
 | 
			
		||||
				s.Optional = append(s.Optional, dkv)
 | 
			
		||||
				s.IgnorePkgsRegexp = append(s.IgnorePkgsRegexp, pkg)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		for _, reg := range s.IgnorePkgsRegexp {
 | 
			
		||||
			_, err := regexp.Compile(reg)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return xerrors.Errorf("Failed to parse %s in %s. err: %w", reg, serverName, err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		for contName, cont := range s.Containers {
 | 
			
		||||
			for _, reg := range cont.IgnorePkgsRegexp {
 | 
			
		||||
				_, err := regexp.Compile(reg)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return xerrors.Errorf("Failed to parse %s in %s@%s. err: %w",
 | 
			
		||||
						reg, contName, serverName, err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		opt := map[string]interface{}{}
 | 
			
		||||
		for k, v := range d.Optional {
 | 
			
		||||
			opt[k] = v
 | 
			
		||||
		}
 | 
			
		||||
		for k, v := range v.Optional {
 | 
			
		||||
			opt[k] = v
 | 
			
		||||
		}
 | 
			
		||||
		s.Optional = opt
 | 
			
		||||
 | 
			
		||||
		s.Enablerepo = v.Enablerepo
 | 
			
		||||
		if len(s.Enablerepo) == 0 {
 | 
			
		||||
@@ -168,18 +240,61 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
				case "base", "updates":
 | 
			
		||||
					// nop
 | 
			
		||||
				default:
 | 
			
		||||
					return fmt.Errorf(
 | 
			
		||||
					return xerrors.Errorf(
 | 
			
		||||
						"For now, enablerepo have to be base or updates: %s, servername: %s",
 | 
			
		||||
						s.Enablerepo, name)
 | 
			
		||||
						s.Enablerepo, serverName)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.LogMsgAnsiColor = Colors[i%len(Colors)]
 | 
			
		||||
		i++
 | 
			
		||||
		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)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		servers[name] = s
 | 
			
		||||
		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.IgnoredJSONKeys = v.IgnoredJSONKeys
 | 
			
		||||
		if len(s.IgnoredJSONKeys) == 0 {
 | 
			
		||||
			s.IgnoredJSONKeys = d.IgnoredJSONKeys
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.LogMsgAnsiColor = Colors[index%len(Colors)]
 | 
			
		||||
		index++
 | 
			
		||||
 | 
			
		||||
		servers[serverName] = s
 | 
			
		||||
	}
 | 
			
		||||
	Conf.Servers = servers
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func toCpeURI(cpename string) (string, error) {
 | 
			
		||||
	if strings.HasPrefix(cpename, "cpe:2.3:") {
 | 
			
		||||
		wfn, err := naming.UnbindFS(cpename)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		return naming.BindToURI(wfn), nil
 | 
			
		||||
	} else if strings.HasPrefix(cpename, "cpe:/") {
 | 
			
		||||
		wfn, err := naming.UnbindURI(cpename)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		return naming.BindToURI(wfn), nil
 | 
			
		||||
	}
 | 
			
		||||
	return "", xerrors.Errorf("Unknown CPE format: %s", cpename)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										44
									
								
								config/tomlloader_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,44 @@
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestToCpeURI(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in       string
 | 
			
		||||
		expected string
 | 
			
		||||
		err      bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			in:       "",
 | 
			
		||||
			expected: "",
 | 
			
		||||
			err:      true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in:       "cpe:/a:microsoft:internet_explorer:10",
 | 
			
		||||
			expected: "cpe:/a:microsoft:internet_explorer:10",
 | 
			
		||||
			err:      false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in:       "cpe:2.3:a:microsoft:internet_explorer:10:*:*:*:*:*:*:*",
 | 
			
		||||
			expected: "cpe:/a:microsoft:internet_explorer:10",
 | 
			
		||||
			err:      false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
		actual, err := toCpeURI(tt.in)
 | 
			
		||||
		if err != nil && !tt.err {
 | 
			
		||||
			t.Errorf("[%d] unexpected error occurred, in: %s act: %s, exp: %s",
 | 
			
		||||
				i, tt.in, actual, tt.expected)
 | 
			
		||||
		} else if err == nil && tt.err {
 | 
			
		||||
			t.Errorf("[%d] expected error is not occurred, in: %s act: %s, exp: %s",
 | 
			
		||||
				i, tt.in, actual, tt.expected)
 | 
			
		||||
		}
 | 
			
		||||
		if actual != tt.expected {
 | 
			
		||||
			t.Errorf("[%d] in: %s, actual: %s, expected: %s",
 | 
			
		||||
				i, tt.in, actual, tt.expected)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										38
									
								
								contrib/future-vuls/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,38 @@
 | 
			
		||||
# future-vuls
 | 
			
		||||
 | 
			
		||||
## Main Features
 | 
			
		||||
 | 
			
		||||
- upload vuls results json to future-vuls
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
git clone https://github.com/future-architect/vuls.git
 | 
			
		||||
make build-future-vuls
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Command Reference
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
Upload to FutureVuls
 | 
			
		||||
 | 
			
		||||
Usage:
 | 
			
		||||
  future-vuls upload [flags]
 | 
			
		||||
 | 
			
		||||
Flags:
 | 
			
		||||
      --config string   config file (default is $HOME/.cobra.yaml)
 | 
			
		||||
  -g, --group-id int    future vuls group id, ENV: VULS_GROUP_ID
 | 
			
		||||
  -h, --help            help for upload
 | 
			
		||||
  -s, --stdin           input from stdin. ENV: VULS_STDIN
 | 
			
		||||
  -t, --token string    future vuls token
 | 
			
		||||
      --url string      future vuls upload url
 | 
			
		||||
      --uuid string     server uuid. ENV: VULS_SERVER_UUID
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Usage
 | 
			
		||||
 | 
			
		||||
- update results json
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 cat results.json | future-vuls upload --stdin --token xxxx --url https://xxxx --group-id 1 --uuid xxxx
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										98
									
								
								contrib/future-vuls/cmd/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,98 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/saas"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	configFile string
 | 
			
		||||
	stdIn      bool
 | 
			
		||||
	jsonDir    string
 | 
			
		||||
	serverUUID string
 | 
			
		||||
	groupID    int64
 | 
			
		||||
	token      string
 | 
			
		||||
	url        string
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	var err error
 | 
			
		||||
	var cmdFvulsUploader = &cobra.Command{
 | 
			
		||||
		Use:   "upload",
 | 
			
		||||
		Short: "Upload to FutureVuls",
 | 
			
		||||
		Long:  `Upload to FutureVuls`,
 | 
			
		||||
		Run: func(cmd *cobra.Command, args []string) {
 | 
			
		||||
			if len(serverUUID) == 0 {
 | 
			
		||||
				serverUUID = os.Getenv("VULS_SERVER_UUID")
 | 
			
		||||
			}
 | 
			
		||||
			if groupID == 0 {
 | 
			
		||||
				envGroupID := os.Getenv("VULS_GROUP_ID")
 | 
			
		||||
				if groupID, err = strconv.ParseInt(envGroupID, 10, 64); err != nil {
 | 
			
		||||
					fmt.Printf("Invalid GroupID: %s\n", envGroupID)
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if len(url) == 0 {
 | 
			
		||||
				url = os.Getenv("VULS_URL")
 | 
			
		||||
			}
 | 
			
		||||
			if len(token) == 0 {
 | 
			
		||||
				token = os.Getenv("VULS_TOKEN")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var scanResultJSON []byte
 | 
			
		||||
			if stdIn {
 | 
			
		||||
				reader := bufio.NewReader(os.Stdin)
 | 
			
		||||
				buf := new(bytes.Buffer)
 | 
			
		||||
				if _, err = buf.ReadFrom(reader); err != nil {
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				scanResultJSON = buf.Bytes()
 | 
			
		||||
			} else {
 | 
			
		||||
				fmt.Println("use --stdin option")
 | 
			
		||||
				os.Exit(1)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var scanResult models.ScanResult
 | 
			
		||||
			if err = json.Unmarshal(scanResultJSON, &scanResult); err != nil {
 | 
			
		||||
				fmt.Println("Failed to parse json", err)
 | 
			
		||||
				os.Exit(1)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			scanResult.ServerUUID = serverUUID
 | 
			
		||||
 | 
			
		||||
			config.Conf.Saas.GroupID = groupID
 | 
			
		||||
			config.Conf.Saas.Token = token
 | 
			
		||||
			config.Conf.Saas.URL = url
 | 
			
		||||
			if err = (saas.Writer{}).Write(scanResult); err != nil {
 | 
			
		||||
				fmt.Println(err)
 | 
			
		||||
				os.Exit(1)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			return
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	cmdFvulsUploader.PersistentFlags().StringVar(&serverUUID, "uuid", "", "server uuid. ENV: VULS_SERVER_UUID")
 | 
			
		||||
	cmdFvulsUploader.PersistentFlags().StringVar(&configFile, "config", "", "config file (default is $HOME/.cobra.yaml)")
 | 
			
		||||
	cmdFvulsUploader.PersistentFlags().BoolVarP(&stdIn, "stdin", "s", false, "input from stdin. ENV: VULS_STDIN")
 | 
			
		||||
	// TODO Read JSON file from directory
 | 
			
		||||
	//	cmdFvulsUploader.Flags().StringVarP(&jsonDir, "results-dir", "d", "./", "vuls scan results json dir")
 | 
			
		||||
	cmdFvulsUploader.PersistentFlags().Int64VarP(&groupID, "group-id", "g", 0, "future vuls group id, ENV: VULS_GROUP_ID")
 | 
			
		||||
	cmdFvulsUploader.PersistentFlags().StringVarP(&token, "token", "t", "", "future vuls token")
 | 
			
		||||
	cmdFvulsUploader.PersistentFlags().StringVar(&url, "url", "", "future vuls upload url")
 | 
			
		||||
 | 
			
		||||
	var rootCmd = &cobra.Command{Use: "future-vuls"}
 | 
			
		||||
	rootCmd.AddCommand(cmdFvulsUploader)
 | 
			
		||||
	if err = rootCmd.Execute(); err != nil {
 | 
			
		||||
		fmt.Println("Failed to execute command", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -2,10 +2,13 @@ package parser
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/xml"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/knqyf263/go-cpe/naming"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type analysis struct {
 | 
			
		||||
@@ -13,12 +16,11 @@ type analysis struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type dependency struct {
 | 
			
		||||
	Identifiers []identifier `xml:"identifiers>identifier"`
 | 
			
		||||
	Identifiers []vulnerabilityID `xml:"identifiers>vulnerabilityIds"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type identifier struct {
 | 
			
		||||
	Name string `xml:"name"`
 | 
			
		||||
	Type string `xml:"type,attr"`
 | 
			
		||||
type vulnerabilityID struct {
 | 
			
		||||
	ID string `xml:"id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func appendIfMissing(slice []string, str string) []string {
 | 
			
		||||
@@ -30,32 +32,39 @@ func appendIfMissing(slice []string, str string) []string {
 | 
			
		||||
	return append(slice, str)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Parse parses XML and collect list of cpe
 | 
			
		||||
// 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 {
 | 
			
		||||
		return nil, fmt.Errorf("Failed to open: %s", err)
 | 
			
		||||
		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 {
 | 
			
		||||
		return nil, fmt.Errorf("Failed to read: %s", err)
 | 
			
		||||
		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, fmt.Errorf("Failed to unmarshal: %s", err)
 | 
			
		||||
		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)
 | 
			
		||||
			id := ident.ID // Start with cpe:2.3:
 | 
			
		||||
			// Convert from CPE 2.3 to CPE 2.2
 | 
			
		||||
			if strings.HasPrefix(id, "cpe:2.3:") {
 | 
			
		||||
				wfn, err := naming.UnbindFS(id)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return []string{}, err
 | 
			
		||||
				}
 | 
			
		||||
				id = naming.BindToURI(wfn)
 | 
			
		||||
			}
 | 
			
		||||
			cpes = appendIfMissing(cpes, id)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return cpes, nil
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										35
									
								
								contrib/trivy/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,35 @@
 | 
			
		||||
# trivy-to-vuls
 | 
			
		||||
 | 
			
		||||
## Main Features
 | 
			
		||||
 | 
			
		||||
- convert trivy's results json to vuls's report json
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
git clone https://github.com/future-architect/vuls.git
 | 
			
		||||
make build-trivy-to-vuls
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Command Reference
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
Parse trivy json to vuls results
 | 
			
		||||
 | 
			
		||||
Usage:
 | 
			
		||||
  trivy-to-vuls parse [flags]
 | 
			
		||||
 | 
			
		||||
Flags:
 | 
			
		||||
  -h, --help                          help for parse
 | 
			
		||||
  -s, --stdin                         input from stdin
 | 
			
		||||
  -d, --trivy-json-dir string         trivy json dir (default "./")
 | 
			
		||||
  -f, --trivy-json-file-name string   trivy json file name (default "results.json")
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Usage
 | 
			
		||||
 | 
			
		||||
- use trivy output
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 trivy -q image -f=json python:3.4-alpine | trivy-to-vuls parse --stdin
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										78
									
								
								contrib/trivy/cmd/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,78 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/contrib/trivy/parser"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	serverUUID   string
 | 
			
		||||
	stdIn        bool
 | 
			
		||||
	jsonDir      string
 | 
			
		||||
	jsonFileName string
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	var err error
 | 
			
		||||
	var cmdTrivyToVuls = &cobra.Command{
 | 
			
		||||
		Use:   "parse",
 | 
			
		||||
		Short: "Parse trivy json to vuls results",
 | 
			
		||||
		Long:  `Parse trivy json to vuls results`,
 | 
			
		||||
		Run: func(cmd *cobra.Command, args []string) {
 | 
			
		||||
			jsonFilePath := filepath.Join(jsonDir, jsonFileName)
 | 
			
		||||
			var trivyJSON []byte
 | 
			
		||||
			if stdIn {
 | 
			
		||||
				reader := bufio.NewReader(os.Stdin)
 | 
			
		||||
				buf := new(bytes.Buffer)
 | 
			
		||||
				if _, err = buf.ReadFrom(reader); err != nil {
 | 
			
		||||
					os.Exit(1)
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				trivyJSON = buf.Bytes()
 | 
			
		||||
			} else {
 | 
			
		||||
				if trivyJSON, err = ioutil.ReadFile(jsonFilePath); err != nil {
 | 
			
		||||
					fmt.Println("Failed to read file", err)
 | 
			
		||||
					os.Exit(1)
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			scanResult := &models.ScanResult{
 | 
			
		||||
				JSONVersion: models.JSONVersion,
 | 
			
		||||
				ScannedCves: models.VulnInfos{},
 | 
			
		||||
			}
 | 
			
		||||
			if scanResult, err = parser.Parse(trivyJSON, scanResult); err != nil {
 | 
			
		||||
				fmt.Println("Failed to execute command", err)
 | 
			
		||||
				os.Exit(1)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			var resultJSON []byte
 | 
			
		||||
			if resultJSON, err = json.MarshalIndent(scanResult, "", "   "); err != nil {
 | 
			
		||||
				fmt.Println("Failed to create json", err)
 | 
			
		||||
				os.Exit(1)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			fmt.Println(string(resultJSON))
 | 
			
		||||
			return
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	cmdTrivyToVuls.Flags().BoolVarP(&stdIn, "stdin", "s", false, "input from stdin")
 | 
			
		||||
	cmdTrivyToVuls.Flags().StringVarP(&jsonDir, "trivy-json-dir", "d", "./", "trivy json dir")
 | 
			
		||||
	cmdTrivyToVuls.Flags().StringVarP(&jsonFileName, "trivy-json-file-name", "f", "results.json", "trivy json file name")
 | 
			
		||||
 | 
			
		||||
	var rootCmd = &cobra.Command{Use: "trivy-to-vuls"}
 | 
			
		||||
	rootCmd.AddCommand(cmdTrivyToVuls)
 | 
			
		||||
	if err = rootCmd.Execute(); err != nil {
 | 
			
		||||
		fmt.Println("Failed to execute command", err)
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										164
									
								
								contrib/trivy/parser/parser.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,164 @@
 | 
			
		||||
package parser
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/aquasecurity/fanal/analyzer/os"
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/report"
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/types"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Parse :
 | 
			
		||||
func Parse(vulnJSON []byte, scanResult *models.ScanResult) (result *models.ScanResult, err error) {
 | 
			
		||||
	var trivyResults report.Results
 | 
			
		||||
	if err = json.Unmarshal(vulnJSON, &trivyResults); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pkgs := models.Packages{}
 | 
			
		||||
	vulnInfos := models.VulnInfos{}
 | 
			
		||||
	uniqueLibraryScannerPaths := map[string]models.LibraryScanner{}
 | 
			
		||||
	for _, trivyResult := range trivyResults {
 | 
			
		||||
		for _, vuln := range trivyResult.Vulnerabilities {
 | 
			
		||||
			if _, ok := vulnInfos[vuln.VulnerabilityID]; !ok {
 | 
			
		||||
				vulnInfos[vuln.VulnerabilityID] = models.VulnInfo{
 | 
			
		||||
					CveID: vuln.VulnerabilityID,
 | 
			
		||||
					Confidences: models.Confidences{
 | 
			
		||||
						{
 | 
			
		||||
							Score:           100,
 | 
			
		||||
							DetectionMethod: models.TrivyMatchStr,
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
					CveContents:      models.CveContents{},
 | 
			
		||||
					LibraryFixedIns:  models.LibraryFixedIns{},
 | 
			
		||||
					// VulnType : "",
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			vulnInfo := vulnInfos[vuln.VulnerabilityID]
 | 
			
		||||
			var notFixedYet bool
 | 
			
		||||
			fixState := ""
 | 
			
		||||
			if len(vuln.FixedVersion) == 0 {
 | 
			
		||||
				notFixedYet = true
 | 
			
		||||
				fixState = "Affected"
 | 
			
		||||
			}
 | 
			
		||||
			var references models.References
 | 
			
		||||
			for _, reference := range vuln.References {
 | 
			
		||||
				references = append(references, models.Reference{
 | 
			
		||||
					Source: "trivy",
 | 
			
		||||
					Link:   reference,
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			sort.Slice(references, func(i, j int) bool {
 | 
			
		||||
				return references[i].Link < references[j].Link
 | 
			
		||||
			})
 | 
			
		||||
 | 
			
		||||
			vulnInfo.CveContents = models.CveContents{
 | 
			
		||||
				models.Trivy: models.CveContent{
 | 
			
		||||
					Cvss3Severity: vuln.Severity,
 | 
			
		||||
					References:    references,
 | 
			
		||||
					Title:         vuln.Title,
 | 
			
		||||
					Summary:       vuln.Description,
 | 
			
		||||
				},
 | 
			
		||||
			}
 | 
			
		||||
			// do only if image type is Vuln
 | 
			
		||||
			if IsTrivySupportedOS(trivyResult.Type) {
 | 
			
		||||
				pkgs[vuln.PkgName] = models.Package{
 | 
			
		||||
					Name:    vuln.PkgName,
 | 
			
		||||
					Version: vuln.InstalledVersion,
 | 
			
		||||
				}
 | 
			
		||||
				vulnInfo.AffectedPackages = append(vulnInfo.AffectedPackages, models.PackageFixStatus{
 | 
			
		||||
					Name:        vuln.PkgName,
 | 
			
		||||
					NotFixedYet: notFixedYet,
 | 
			
		||||
					FixState:    fixState,
 | 
			
		||||
					FixedIn:     vuln.FixedVersion,
 | 
			
		||||
				})
 | 
			
		||||
 | 
			
		||||
				// overwrite every time if os package
 | 
			
		||||
				scanResult.Family = trivyResult.Type
 | 
			
		||||
				scanResult.ServerName = trivyResult.Target
 | 
			
		||||
				scanResult.Optional = map[string]interface{}{
 | 
			
		||||
					"trivy-target": trivyResult.Target,
 | 
			
		||||
				}
 | 
			
		||||
				scanResult.ScannedAt = time.Now()
 | 
			
		||||
				scanResult.ScannedBy = "trivy"
 | 
			
		||||
				scanResult.ScannedVia = "trivy"
 | 
			
		||||
			} else {
 | 
			
		||||
				// LibraryScanの結果
 | 
			
		||||
				vulnInfo.LibraryFixedIns = append(vulnInfo.LibraryFixedIns, models.LibraryFixedIn{
 | 
			
		||||
					Key:     trivyResult.Type,
 | 
			
		||||
					Name:    vuln.PkgName,
 | 
			
		||||
					Path:    trivyResult.Target,
 | 
			
		||||
					FixedIn: vuln.FixedVersion,
 | 
			
		||||
				})
 | 
			
		||||
				libScanner := uniqueLibraryScannerPaths[trivyResult.Target]
 | 
			
		||||
				libScanner.Libs = append(libScanner.Libs, types.Library{
 | 
			
		||||
					Name:    vuln.PkgName,
 | 
			
		||||
					Version: vuln.InstalledVersion,
 | 
			
		||||
				})
 | 
			
		||||
				uniqueLibraryScannerPaths[trivyResult.Target] = libScanner
 | 
			
		||||
			}
 | 
			
		||||
			vulnInfos[vuln.VulnerabilityID] = vulnInfo
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// flatten and unique libraries
 | 
			
		||||
	libraryScanners := make([]models.LibraryScanner, 0, len(uniqueLibraryScannerPaths))
 | 
			
		||||
	for path, v := range uniqueLibraryScannerPaths {
 | 
			
		||||
		uniqueLibrary := map[string]types.Library{}
 | 
			
		||||
		for _, lib := range v.Libs {
 | 
			
		||||
			uniqueLibrary[lib.Name+lib.Version] = lib
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var libraries []types.Library
 | 
			
		||||
		for _, library := range uniqueLibrary {
 | 
			
		||||
			libraries = append(libraries, library)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		sort.Slice(libraries, func(i, j int) bool {
 | 
			
		||||
			return libraries[i].Name < libraries[j].Name
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		libscanner := models.LibraryScanner{
 | 
			
		||||
			Path: path,
 | 
			
		||||
			Libs: libraries,
 | 
			
		||||
		}
 | 
			
		||||
		libraryScanners = append(libraryScanners, libscanner)
 | 
			
		||||
	}
 | 
			
		||||
	sort.Slice(libraryScanners, func(i, j int) bool {
 | 
			
		||||
		return libraryScanners[i].Path < libraryScanners[j].Path
 | 
			
		||||
	})
 | 
			
		||||
	scanResult.ScannedCves = vulnInfos
 | 
			
		||||
	scanResult.Packages = pkgs
 | 
			
		||||
	scanResult.LibraryScanners = libraryScanners
 | 
			
		||||
	return scanResult, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsTrivySupportedOS :
 | 
			
		||||
func IsTrivySupportedOS(family string) bool {
 | 
			
		||||
	supportedFamilies := []string{
 | 
			
		||||
		os.RedHat,
 | 
			
		||||
		os.Debian,
 | 
			
		||||
		os.Ubuntu,
 | 
			
		||||
		os.CentOS,
 | 
			
		||||
		os.Fedora,
 | 
			
		||||
		os.Amazon,
 | 
			
		||||
		os.Oracle,
 | 
			
		||||
		os.Windows,
 | 
			
		||||
		os.OpenSUSE,
 | 
			
		||||
		os.OpenSUSELeap,
 | 
			
		||||
		os.OpenSUSETumbleweed,
 | 
			
		||||
		os.SLES,
 | 
			
		||||
		os.Photon,
 | 
			
		||||
		os.Alpine,
 | 
			
		||||
	}
 | 
			
		||||
	for _, supportedFamily := range supportedFamilies {
 | 
			
		||||
		if family == supportedFamily {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5482
									
								
								contrib/trivy/parser/parser_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										33
									
								
								cwe/cwe.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,33 @@
 | 
			
		||||
package cwe
 | 
			
		||||
 | 
			
		||||
// CweTopTwentyfive2019 has CWE-ID in CWE Top 25
 | 
			
		||||
var CweTopTwentyfive2019 = map[string]string{
 | 
			
		||||
	"119": "1",
 | 
			
		||||
	"79":  "2",
 | 
			
		||||
	"20":  "3",
 | 
			
		||||
	"200": "4",
 | 
			
		||||
	"125": "5",
 | 
			
		||||
	"89":  "6",
 | 
			
		||||
	"416": "7",
 | 
			
		||||
	"190": "8",
 | 
			
		||||
	"352": "9",
 | 
			
		||||
	"22":  "10",
 | 
			
		||||
	"78":  "11",
 | 
			
		||||
	"787": "12",
 | 
			
		||||
	"287": "13",
 | 
			
		||||
	"476": "14",
 | 
			
		||||
	"732": "16",
 | 
			
		||||
	"434": "16",
 | 
			
		||||
	"611": "17",
 | 
			
		||||
	"94":  "18",
 | 
			
		||||
	"798": "19",
 | 
			
		||||
	"400": "20",
 | 
			
		||||
	"772": "21",
 | 
			
		||||
	"426": "22",
 | 
			
		||||
	"502": "23",
 | 
			
		||||
	"269": "24",
 | 
			
		||||
	"295": "25",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CweTopTwentyfive2019URL has CWE Top25 links
 | 
			
		||||
var CweTopTwentyfive2019URL = "https://cwe.mitre.org/top25/archive/2019/2019_cwe_top25.html"
 | 
			
		||||
							
								
								
									
										65
									
								
								cwe/owasp.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,65 @@
 | 
			
		||||
package cwe
 | 
			
		||||
 | 
			
		||||
// OwaspTopTen2017 has CWE-ID in OWSP Top 10
 | 
			
		||||
var OwaspTopTen2017 = map[string]string{
 | 
			
		||||
	"77":  "1",
 | 
			
		||||
	"89":  "1",
 | 
			
		||||
	"564": "1",
 | 
			
		||||
	"917": "1",
 | 
			
		||||
 | 
			
		||||
	"287": "2",
 | 
			
		||||
	"384": "2",
 | 
			
		||||
 | 
			
		||||
	"220": "3",
 | 
			
		||||
	"310": "3",
 | 
			
		||||
	"312": "3",
 | 
			
		||||
	"319": "3",
 | 
			
		||||
	"326": "3",
 | 
			
		||||
	"359": "3",
 | 
			
		||||
 | 
			
		||||
	"611": "4",
 | 
			
		||||
 | 
			
		||||
	"22":  "5",
 | 
			
		||||
	"284": "5",
 | 
			
		||||
	"285": "5",
 | 
			
		||||
	"639": "5",
 | 
			
		||||
 | 
			
		||||
	"2":   "6",
 | 
			
		||||
	"16":  "6",
 | 
			
		||||
	"388": "6",
 | 
			
		||||
 | 
			
		||||
	"79": "7",
 | 
			
		||||
 | 
			
		||||
	"502": "8",
 | 
			
		||||
 | 
			
		||||
	"223": "10",
 | 
			
		||||
	"778": "10",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OwaspTopTen2017GitHubURLEn has GitHub links
 | 
			
		||||
var OwaspTopTen2017GitHubURLEn = map[string]string{
 | 
			
		||||
	"1":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa1-injection.md",
 | 
			
		||||
	"2":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa2-broken-authentication.md",
 | 
			
		||||
	"3":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa3-sensitive-data-disclosure.md",
 | 
			
		||||
	"4":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa4-xxe.md",
 | 
			
		||||
	"5":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa5-broken-access-control.md",
 | 
			
		||||
	"6":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa6-security-misconfiguration.md",
 | 
			
		||||
	"7":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa7-xss.md",
 | 
			
		||||
	"8":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa8-insecure-deserialization.md",
 | 
			
		||||
	"9":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa9-known-vulns.md<Paste>",
 | 
			
		||||
	"10": "https://github.com/OWASP/Top10/blob/master/2017/en/0xaa-logging-detection-response.md",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OwaspTopTen2017GitHubURLJa has GitHub links
 | 
			
		||||
var OwaspTopTen2017GitHubURLJa = map[string]string{
 | 
			
		||||
	"1":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa1-injection.md",
 | 
			
		||||
	"2":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa2-broken-authentication.md",
 | 
			
		||||
	"3":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa3-sensitive-data-disclosure.md",
 | 
			
		||||
	"4":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa4-xxe.md",
 | 
			
		||||
	"5":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa5-broken-access-control.md",
 | 
			
		||||
	"6":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa6-security-misconfiguration.md",
 | 
			
		||||
	"7":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa7-xss.md",
 | 
			
		||||
	"8":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa8-insecure-deserialization.md",
 | 
			
		||||
	"9":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa9-known-vulns.md<Paste>",
 | 
			
		||||
	"10": "https://github.com/OWASP/Top10/blob/master/2017/ja/0xaa-logging-detection-response.md",
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								cwe/sans.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,33 @@
 | 
			
		||||
package cwe
 | 
			
		||||
 | 
			
		||||
// SansTopTwentyfive has CWE-ID in CWE/SANS Top 25
 | 
			
		||||
var SansTopTwentyfive = map[string]string{
 | 
			
		||||
	"89":  "1",
 | 
			
		||||
	"78":  "2",
 | 
			
		||||
	"120": "3",
 | 
			
		||||
	"79":  "4",
 | 
			
		||||
	"306": "5",
 | 
			
		||||
	"862": "6",
 | 
			
		||||
	"798": "7",
 | 
			
		||||
	"311": "8",
 | 
			
		||||
	"434": "9",
 | 
			
		||||
	"807": "10",
 | 
			
		||||
	"250": "11",
 | 
			
		||||
	"352": "12",
 | 
			
		||||
	"22":  "13",
 | 
			
		||||
	"494": "14",
 | 
			
		||||
	"863": "15",
 | 
			
		||||
	"829": "16",
 | 
			
		||||
	"732": "17",
 | 
			
		||||
	"676": "18",
 | 
			
		||||
	"327": "19",
 | 
			
		||||
	"131": "20",
 | 
			
		||||
	"307": "21",
 | 
			
		||||
	"601": "22",
 | 
			
		||||
	"134": "23",
 | 
			
		||||
	"190": "24",
 | 
			
		||||
	"759": "25",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SansTopTwentyfiveURL is a URL of sans 25
 | 
			
		||||
var SansTopTwentyfiveURL = "https://www.sans.org/top25-software-errors/"
 | 
			
		||||
							
								
								
									
										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,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										119
									
								
								exploit/exploit.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,119 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package exploit
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	cnf "github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/mozqnet/go-exploitdb/db"
 | 
			
		||||
	exploitmodels "github.com/mozqnet/go-exploitdb/models"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FillWithExploit fills exploit information that has in Exploit
 | 
			
		||||
func FillWithExploit(driver db.DB, r *models.ScanResult) (nExploitCve int, err error) {
 | 
			
		||||
	if cnf.Conf.Exploit.IsFetchViaHTTP() {
 | 
			
		||||
		var cveIDs []string
 | 
			
		||||
		for cveID := range r.ScannedCves {
 | 
			
		||||
			cveIDs = append(cveIDs, cveID)
 | 
			
		||||
		}
 | 
			
		||||
		prefix, _ := util.URLPathJoin(cnf.Conf.Exploit.URL, "cves")
 | 
			
		||||
		responses, err := getCvesViaHTTP(cveIDs, prefix)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		for _, res := range responses {
 | 
			
		||||
			exps := []*exploitmodels.Exploit{}
 | 
			
		||||
			if err := json.Unmarshal([]byte(res.json), &exps); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
			}
 | 
			
		||||
			exploits := ConvertToModels(exps)
 | 
			
		||||
			v, ok := r.ScannedCves[res.request.cveID]
 | 
			
		||||
			if ok {
 | 
			
		||||
				v.Exploits = exploits
 | 
			
		||||
			}
 | 
			
		||||
			r.ScannedCves[res.request.cveID] = v
 | 
			
		||||
			nExploitCve++
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if driver == nil {
 | 
			
		||||
			return 0, nil
 | 
			
		||||
		}
 | 
			
		||||
		for cveID, vuln := range r.ScannedCves {
 | 
			
		||||
			if cveID == "" {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			es := driver.GetExploitByCveID(cveID)
 | 
			
		||||
			if len(es) == 0 {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			exploits := ConvertToModels(es)
 | 
			
		||||
			vuln.Exploits = exploits
 | 
			
		||||
			r.ScannedCves[cveID] = vuln
 | 
			
		||||
			nExploitCve++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nExploitCve, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConvertToModels converts gost model to vuls model
 | 
			
		||||
func ConvertToModels(es []*exploitmodels.Exploit) (exploits []models.Exploit) {
 | 
			
		||||
	for _, e := range es {
 | 
			
		||||
		var documentURL, shellURL *string
 | 
			
		||||
		if e.OffensiveSecurity != nil {
 | 
			
		||||
			os := e.OffensiveSecurity
 | 
			
		||||
			if os.Document != nil {
 | 
			
		||||
				documentURL = &os.Document.DocumentURL
 | 
			
		||||
			}
 | 
			
		||||
			if os.ShellCode != nil {
 | 
			
		||||
				shellURL = &os.ShellCode.ShellCodeURL
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		exploit := models.Exploit{
 | 
			
		||||
			ExploitType:  e.ExploitType,
 | 
			
		||||
			ID:           e.ExploitUniqueID,
 | 
			
		||||
			URL:          e.URL,
 | 
			
		||||
			Description:  e.Description,
 | 
			
		||||
			DocumentURL:  documentURL,
 | 
			
		||||
			ShellCodeURL: shellURL,
 | 
			
		||||
		}
 | 
			
		||||
		exploits = append(exploits, exploit)
 | 
			
		||||
	}
 | 
			
		||||
	return exploits
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckHTTPHealth do health check
 | 
			
		||||
func CheckHTTPHealth() error {
 | 
			
		||||
	if !cnf.Conf.Exploit.IsFetchViaHTTP() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	url := fmt.Sprintf("%s/health", cnf.Conf.Exploit.URL)
 | 
			
		||||
	var errs []error
 | 
			
		||||
	var resp *http.Response
 | 
			
		||||
	resp, _, errs = gorequest.New().Get(url).End()
 | 
			
		||||
	//  resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
 | 
			
		||||
	//  resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
 | 
			
		||||
	if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
		return xerrors.Errorf("Failed to connect to exploit server. url: %s, errs: %w", url, errs)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckIfExploitFetched checks if oval entries are in DB by family, release.
 | 
			
		||||
func CheckIfExploitFetched(driver db.DB, osFamily string) (fetched bool, err error) {
 | 
			
		||||
	//TODO
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckIfExploitFresh checks if oval entries are fresh enough
 | 
			
		||||
func CheckIfExploitFresh(driver db.DB, osFamily string) (ok bool, err error) {
 | 
			
		||||
	//TODO
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										115
									
								
								exploit/util.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,115 @@
 | 
			
		||||
package exploit
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cenkalti/backoff"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type response struct {
 | 
			
		||||
	request request
 | 
			
		||||
	json    string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getCvesViaHTTP(cveIDs []string, urlPrefix string) (
 | 
			
		||||
	responses []response, err error) {
 | 
			
		||||
	nReq := len(cveIDs)
 | 
			
		||||
	reqChan := make(chan request, nReq)
 | 
			
		||||
	resChan := make(chan response, nReq)
 | 
			
		||||
	errChan := make(chan error, nReq)
 | 
			
		||||
	defer close(reqChan)
 | 
			
		||||
	defer close(resChan)
 | 
			
		||||
	defer close(errChan)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		for _, cveID := range cveIDs {
 | 
			
		||||
			reqChan <- request{
 | 
			
		||||
				cveID: cveID,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	concurrency := 10
 | 
			
		||||
	tasks := util.GenWorkers(concurrency)
 | 
			
		||||
	for i := 0; i < nReq; i++ {
 | 
			
		||||
		tasks <- func() {
 | 
			
		||||
			select {
 | 
			
		||||
			case req := <-reqChan:
 | 
			
		||||
				url, err := util.URLPathJoin(
 | 
			
		||||
					urlPrefix,
 | 
			
		||||
					req.cveID,
 | 
			
		||||
				)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					errChan <- err
 | 
			
		||||
				} else {
 | 
			
		||||
					util.Log.Debugf("HTTP Request to %s", url)
 | 
			
		||||
					httpGet(url, req, resChan, errChan)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	timeout := time.After(2 * 60 * time.Second)
 | 
			
		||||
	var errs []error
 | 
			
		||||
	for i := 0; i < nReq; i++ {
 | 
			
		||||
		select {
 | 
			
		||||
		case res := <-resChan:
 | 
			
		||||
			responses = append(responses, res)
 | 
			
		||||
		case err := <-errChan:
 | 
			
		||||
			errs = append(errs, err)
 | 
			
		||||
		case <-timeout:
 | 
			
		||||
			return nil, xerrors.New("Timeout Fetching OVAL")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(errs) != 0 {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to fetch OVAL. err: %w", errs)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type request struct {
 | 
			
		||||
	osMajorVersion string
 | 
			
		||||
	packName       string
 | 
			
		||||
	isSrcPack      bool
 | 
			
		||||
	cveID          string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func httpGet(url string, req request, resChan chan<- response, errChan chan<- error) {
 | 
			
		||||
	var body string
 | 
			
		||||
	var errs []error
 | 
			
		||||
	var resp *http.Response
 | 
			
		||||
	count, retryMax := 0, 3
 | 
			
		||||
	f := func() (err error) {
 | 
			
		||||
		//  resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
 | 
			
		||||
		resp, body, errs = gorequest.New().Get(url).End()
 | 
			
		||||
		if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
			count++
 | 
			
		||||
			if count == retryMax {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			return xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %w", url, resp, errs)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	notify := func(err error, t time.Duration) {
 | 
			
		||||
		util.Log.Warnf("Failed to HTTP GET. retrying in %s seconds. err: %s", t, err)
 | 
			
		||||
	}
 | 
			
		||||
	err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		errChan <- xerrors.Errorf("HTTP Error %w", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if count == retryMax {
 | 
			
		||||
		errChan <- xerrors.New("Retry count exceeded")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resChan <- response{
 | 
			
		||||
		request: req,
 | 
			
		||||
		json:    body,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										179
									
								
								github/github.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,179 @@
 | 
			
		||||
package github
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"context"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/errof"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"golang.org/x/oauth2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FillGitHubSecurityAlerts access to owner/repo on GitHub and fetch security alerts of the repository via GitHub API v4 GraphQL and then set to the given ScanResult.
 | 
			
		||||
// https://help.github.com/articles/about-security-alerts-for-vulnerable-dependencies/
 | 
			
		||||
func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (nCVEs int, err error) {
 | 
			
		||||
	src := oauth2.StaticTokenSource(
 | 
			
		||||
		&oauth2.Token{AccessToken: token},
 | 
			
		||||
	)
 | 
			
		||||
	httpClient := oauth2.NewClient(context.Background(), src)
 | 
			
		||||
 | 
			
		||||
	// TODO Use `https://github.com/shurcooL/githubv4` if the tool supports vulnerabilityAlerts Endpoint
 | 
			
		||||
	// Memo : https://developer.github.com/v4/explorer/
 | 
			
		||||
	const jsonfmt = `{"query":
 | 
			
		||||
	"query { repository(owner:\"%s\", name:\"%s\") { url vulnerabilityAlerts(first: %d, %s) { pageInfo { endCursor hasNextPage startCursor } edges { node { id dismissReason dismissedAt securityVulnerability{ package { name ecosystem } severity vulnerableVersionRange firstPatchedVersion { identifier } } securityAdvisory { description ghsaId permalink publishedAt summary updatedAt withdrawnAt origin severity references { url } identifiers { type value } } } } } } } "}`
 | 
			
		||||
	after := ""
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		jsonStr := fmt.Sprintf(jsonfmt, owner, repo, 100, after)
 | 
			
		||||
		req, err := http.NewRequest("POST",
 | 
			
		||||
			"https://api.github.com/graphql",
 | 
			
		||||
			bytes.NewBuffer([]byte(jsonStr)),
 | 
			
		||||
		)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// https://developer.github.com/v4/previews/#repository-vulnerability-alerts
 | 
			
		||||
		// To toggle this preview and access data, need to provide a custom media type in the Accept header:
 | 
			
		||||
		// MEMO: I tried to get the affected version via GitHub API. Bit it seems difficult to determin the affected version if there are multiple dependency files such as package.json.
 | 
			
		||||
		// TODO remove this header if it is no longer preview status in the future.
 | 
			
		||||
		req.Header.Set("Accept", "application/vnd.github.package-deletes-preview+json")
 | 
			
		||||
		req.Header.Set("Content-Type", "application/json")
 | 
			
		||||
 | 
			
		||||
		resp, err := httpClient.Do(req)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		defer resp.Body.Close()
 | 
			
		||||
 | 
			
		||||
		body, err := ioutil.ReadAll(resp.Body)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		alerts := SecurityAlerts{}
 | 
			
		||||
		if err := json.Unmarshal(body, &alerts); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// util.Log.Debugf("%s", pp.Sprint(alerts))
 | 
			
		||||
		// util.Log.Debugf("%s", string(body))
 | 
			
		||||
		if alerts.Data.Repository.URL == "" {
 | 
			
		||||
			return 0, errof.New(
 | 
			
		||||
				errof.ErrFailedToAccessGithubAPI,
 | 
			
		||||
				fmt.Sprintf("Failed to access to GitHub API. Response: %s", string(body)),
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, v := range alerts.Data.Repository.VulnerabilityAlerts.Edges {
 | 
			
		||||
			if config.Conf.IgnoreGitHubDismissed && v.Node.DismissReason != "" {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			pkgName := fmt.Sprintf("%s %s",
 | 
			
		||||
				alerts.Data.Repository.URL, v.Node.SecurityVulnerability.Package.Name)
 | 
			
		||||
 | 
			
		||||
			m := models.GitHubSecurityAlert{
 | 
			
		||||
				PackageName:   pkgName,
 | 
			
		||||
				FixedIn:       v.Node.SecurityVulnerability.FirstPatchedVersion.Identifier,
 | 
			
		||||
				AffectedRange: v.Node.SecurityVulnerability.VulnerableVersionRange,
 | 
			
		||||
				Dismissed:     len(v.Node.DismissReason) != 0,
 | 
			
		||||
				DismissedAt:   v.Node.DismissedAt,
 | 
			
		||||
				DismissReason: v.Node.DismissReason,
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			cveIDs, other := []string{}, []string{}
 | 
			
		||||
			for _, identifier := range v.Node.SecurityAdvisory.Identifiers {
 | 
			
		||||
				if identifier.Type == "CVE" {
 | 
			
		||||
					cveIDs = append(cveIDs, identifier.Value)
 | 
			
		||||
				} else {
 | 
			
		||||
					other = append(other, identifier.Value)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// If CVE-ID has not been assigned, use the GHSA ID etc as a ID.
 | 
			
		||||
			if len(cveIDs) == 0 {
 | 
			
		||||
				cveIDs = other
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, cveID := range cveIDs {
 | 
			
		||||
				if val, ok := r.ScannedCves[cveID]; ok {
 | 
			
		||||
					val.GitHubSecurityAlerts = val.GitHubSecurityAlerts.Add(m)
 | 
			
		||||
					r.ScannedCves[cveID] = val
 | 
			
		||||
					nCVEs++
 | 
			
		||||
				} else {
 | 
			
		||||
					v := models.VulnInfo{
 | 
			
		||||
						CveID:                cveID,
 | 
			
		||||
						Confidences:          models.Confidences{models.GitHubMatch},
 | 
			
		||||
						GitHubSecurityAlerts: models.GitHubSecurityAlerts{m},
 | 
			
		||||
					}
 | 
			
		||||
					r.ScannedCves[cveID] = v
 | 
			
		||||
					nCVEs++
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if !alerts.Data.Repository.VulnerabilityAlerts.PageInfo.HasNextPage {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		after = fmt.Sprintf(`after: \"%s\"`, alerts.Data.Repository.VulnerabilityAlerts.PageInfo.EndCursor)
 | 
			
		||||
	}
 | 
			
		||||
	return nCVEs, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//SecurityAlerts has detected CVE-IDs, PackageNames, Refs
 | 
			
		||||
type SecurityAlerts struct {
 | 
			
		||||
	Data struct {
 | 
			
		||||
		Repository struct {
 | 
			
		||||
			URL                 string `json:"url"`
 | 
			
		||||
			VulnerabilityAlerts struct {
 | 
			
		||||
				PageInfo struct {
 | 
			
		||||
					EndCursor   string `json:"endCursor"`
 | 
			
		||||
					HasNextPage bool   `json:"hasNextPage"`
 | 
			
		||||
					StartCursor string `json:"startCursor"`
 | 
			
		||||
				} `json:"pageInfo"`
 | 
			
		||||
				Edges []struct {
 | 
			
		||||
					Node struct {
 | 
			
		||||
						ID                    string    `json:"id"`
 | 
			
		||||
						DismissReason         string    `json:"dismissReason"`
 | 
			
		||||
						DismissedAt           time.Time `json:"dismissedAt"`
 | 
			
		||||
						SecurityVulnerability struct {
 | 
			
		||||
							Package struct {
 | 
			
		||||
								Name      string `json:"name"`
 | 
			
		||||
								Ecosystem string `json:"ecosystem"`
 | 
			
		||||
							} `json:"package"`
 | 
			
		||||
							Severity               string `json:"severity"`
 | 
			
		||||
							VulnerableVersionRange string `json:"vulnerableVersionRange"`
 | 
			
		||||
							FirstPatchedVersion    struct {
 | 
			
		||||
								Identifier string `json:"identifier"`
 | 
			
		||||
							} `json:"firstPatchedVersion"`
 | 
			
		||||
						} `json:"securityVulnerability"`
 | 
			
		||||
						SecurityAdvisory struct {
 | 
			
		||||
							Description string    `json:"description"`
 | 
			
		||||
							GhsaID      string    `json:"ghsaId"`
 | 
			
		||||
							Permalink   string    `json:"permalink"`
 | 
			
		||||
							PublishedAt time.Time `json:"publishedAt"`
 | 
			
		||||
							Summary     string    `json:"summary"`
 | 
			
		||||
							UpdatedAt   time.Time `json:"updatedAt"`
 | 
			
		||||
							WithdrawnAt time.Time `json:"withdrawnAt"`
 | 
			
		||||
							Origin      string    `json:"origin"`
 | 
			
		||||
							Severity    string    `json:"severity"`
 | 
			
		||||
							References  []struct {
 | 
			
		||||
								URL string `json:"url"`
 | 
			
		||||
							} `json:"references"`
 | 
			
		||||
							Identifiers []struct {
 | 
			
		||||
								Type  string `json:"type"`
 | 
			
		||||
								Value string `json:"value"`
 | 
			
		||||
							} `json:"identifiers"`
 | 
			
		||||
						} `json:"securityAdvisory"`
 | 
			
		||||
					} `json:"node"`
 | 
			
		||||
				} `json:"edges"`
 | 
			
		||||
			} `json:"vulnerabilityAlerts"`
 | 
			
		||||
		} `json:"repository"`
 | 
			
		||||
	} `json:"data"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										64
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,64 @@
 | 
			
		||||
module github.com/future-architect/vuls
 | 
			
		||||
 | 
			
		||||
go 1.14
 | 
			
		||||
 | 
			
		||||
replace (
 | 
			
		||||
	gopkg.in/mattn/go-colorable.v0 => github.com/mattn/go-colorable v0.1.0
 | 
			
		||||
	gopkg.in/mattn/go-isatty.v0 => github.com/mattn/go-isatty v0.0.6
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/Azure/azure-sdk-for-go v43.3.0+incompatible
 | 
			
		||||
	github.com/BurntSushi/toml v0.3.1
 | 
			
		||||
	github.com/Masterminds/semver/v3 v3.1.0
 | 
			
		||||
	github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91
 | 
			
		||||
	github.com/aquasecurity/fanal v0.0.0-20200820074632-6de62ef86882
 | 
			
		||||
	github.com/aquasecurity/trivy v0.12.0
 | 
			
		||||
	github.com/aquasecurity/trivy-db v0.0.0-20200826140828-6da6467703aa
 | 
			
		||||
	github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef
 | 
			
		||||
	github.com/aws/aws-sdk-go v1.33.21
 | 
			
		||||
	github.com/boltdb/bolt v1.3.1
 | 
			
		||||
	github.com/cenkalti/backoff v2.2.1+incompatible
 | 
			
		||||
	github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b
 | 
			
		||||
	github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21
 | 
			
		||||
	github.com/emersion/go-smtp v0.13.0
 | 
			
		||||
	github.com/google/subcommands v1.2.0
 | 
			
		||||
	github.com/gosuri/uitable v0.0.4
 | 
			
		||||
	github.com/hashicorp/go-uuid v1.0.2
 | 
			
		||||
	github.com/hashicorp/go-version v1.2.1
 | 
			
		||||
	github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c
 | 
			
		||||
	github.com/inconshreveable/log15 v0.0.0-20201112154412-8562bdadbbac // indirect
 | 
			
		||||
	github.com/jesseduffield/gocui v0.3.0
 | 
			
		||||
	github.com/k0kubun/pp v3.0.1+incompatible
 | 
			
		||||
	github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f
 | 
			
		||||
	github.com/knqyf263/go-cpe v0.0.0-20180327054844-659663f6eca2
 | 
			
		||||
	github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d
 | 
			
		||||
	github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936
 | 
			
		||||
	github.com/knqyf263/gost v0.1.7
 | 
			
		||||
	github.com/kotakanbe/go-cve-dictionary v0.5.5
 | 
			
		||||
	github.com/kotakanbe/go-pingscanner v0.1.0
 | 
			
		||||
	github.com/kotakanbe/goval-dictionary v0.2.15
 | 
			
		||||
	github.com/kotakanbe/logrus-prefixed-formatter v0.0.0-20180123152602-928f7356cb96
 | 
			
		||||
	github.com/lib/pq v1.8.0 // indirect
 | 
			
		||||
	github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
 | 
			
		||||
	github.com/mitchellh/go-homedir v1.1.0
 | 
			
		||||
	github.com/mozqnet/go-exploitdb v0.1.2
 | 
			
		||||
	github.com/nlopes/slack v0.6.0
 | 
			
		||||
	github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1 // indirect
 | 
			
		||||
	github.com/olekukonko/tablewriter v0.0.4
 | 
			
		||||
	github.com/parnurzeal/gorequest v0.2.16
 | 
			
		||||
	github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
 | 
			
		||||
	github.com/sirupsen/logrus v1.6.0
 | 
			
		||||
	github.com/spf13/afero v1.3.0
 | 
			
		||||
	github.com/spf13/cobra v1.0.0
 | 
			
		||||
	github.com/takuzoo3868/go-msfdb v0.1.3
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9
 | 
			
		||||
	golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
 | 
			
		||||
	golang.org/x/net v0.0.0-20201110031124-69a78807bb2b // indirect
 | 
			
		||||
	golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
 | 
			
		||||
	golang.org/x/sys v0.0.0-20201117222635-ba5294a509c7 // indirect
 | 
			
		||||
	golang.org/x/text v0.3.4 // indirect
 | 
			
		||||
	golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
 | 
			
		||||
	google.golang.org/appengine v1.6.2 // indirect
 | 
			
		||||
	k8s.io/utils v0.0.0-20200619165400-6e3d28b6ed19
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										53
									
								
								gost/base.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,53 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	cnf "github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/knqyf263/gost/db"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Base is a base struct
 | 
			
		||||
type Base struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillCVEsWithRedHat fills cve information that has in Gost
 | 
			
		||||
func (b Base) FillCVEsWithRedHat(driver db.DB, r *models.ScanResult) error {
 | 
			
		||||
	return RedHat{}.fillFixed(driver, r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckHTTPHealth do health check
 | 
			
		||||
func (b Base) CheckHTTPHealth() error {
 | 
			
		||||
	if !cnf.Conf.Gost.IsFetchViaHTTP() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	url := fmt.Sprintf("%s/health", cnf.Conf.Gost.URL)
 | 
			
		||||
	var errs []error
 | 
			
		||||
	var resp *http.Response
 | 
			
		||||
	resp, _, errs = gorequest.New().Get(url).End()
 | 
			
		||||
	//  resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
 | 
			
		||||
	//  resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
 | 
			
		||||
	if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
		return xerrors.Errorf("Failed to connect to gost server. url: %s, errs: %w", url, errs)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckIfGostFetched checks if oval entries are in DB by family, release.
 | 
			
		||||
func (b Base) CheckIfGostFetched(driver db.DB, osFamily string) (fetched bool, err error) {
 | 
			
		||||
	//TODO
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckIfGostFresh checks if oval entries are fresh enough
 | 
			
		||||
func (b Base) CheckIfGostFresh(driver db.DB, osFamily string) (ok bool, err error) {
 | 
			
		||||
	//TODO
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										190
									
								
								gost/debian.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,190 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/knqyf263/gost/db"
 | 
			
		||||
	gostmodels "github.com/knqyf263/gost/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Debian is Gost client for Debian GNU/Linux
 | 
			
		||||
type Debian struct {
 | 
			
		||||
	Base
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type packCves struct {
 | 
			
		||||
	packName  string
 | 
			
		||||
	isSrcPack bool
 | 
			
		||||
	cves      []models.CveContent
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (deb Debian) supported(major string) bool {
 | 
			
		||||
	_, ok := map[string]string{
 | 
			
		||||
		"8":  "jessie",
 | 
			
		||||
		"9":  "stretch",
 | 
			
		||||
		"10": "buster",
 | 
			
		||||
	}[major]
 | 
			
		||||
	return ok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DetectUnfixed fills cve information that has in Gost
 | 
			
		||||
func (deb Debian) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (nCVEs int, err error) {
 | 
			
		||||
	if !deb.supported(major(r.Release)) {
 | 
			
		||||
		// only logging
 | 
			
		||||
		util.Log.Warnf("Debian %s is not supported yet", r.Release)
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	linuxImage := "linux-image-" + r.RunningKernel.Release
 | 
			
		||||
	// Add linux and set the version of running kernel to search OVAL.
 | 
			
		||||
	if r.Container.ContainerID == "" {
 | 
			
		||||
		newVer := ""
 | 
			
		||||
		if p, ok := r.Packages[linuxImage]; ok {
 | 
			
		||||
			newVer = p.NewVersion
 | 
			
		||||
		}
 | 
			
		||||
		r.Packages["linux"] = models.Package{
 | 
			
		||||
			Name:       "linux",
 | 
			
		||||
			Version:    r.RunningKernel.Version,
 | 
			
		||||
			NewVersion: newVer,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Debian Security Tracker does not support Package for Raspbian, so skip it.
 | 
			
		||||
	var scanResult models.ScanResult
 | 
			
		||||
	if r.Family != config.Raspbian {
 | 
			
		||||
		scanResult = *r
 | 
			
		||||
	} else {
 | 
			
		||||
		scanResult = r.RemoveRaspbianPackFromResult()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	packCvesList := []packCves{}
 | 
			
		||||
	if config.Conf.Gost.IsFetchViaHTTP() {
 | 
			
		||||
		url, _ := util.URLPathJoin(config.Conf.Gost.URL, "debian", major(scanResult.Release), "pkgs")
 | 
			
		||||
		responses, err := getAllUnfixedCvesViaHTTP(r, url)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, res := range responses {
 | 
			
		||||
			debCves := map[string]gostmodels.DebianCVE{}
 | 
			
		||||
			if err := json.Unmarshal([]byte(res.json), &debCves); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
			}
 | 
			
		||||
			cves := []models.CveContent{}
 | 
			
		||||
			for _, debcve := range debCves {
 | 
			
		||||
				cves = append(cves, *deb.ConvertToModel(&debcve))
 | 
			
		||||
			}
 | 
			
		||||
			packCvesList = append(packCvesList, packCves{
 | 
			
		||||
				packName:  res.request.packName,
 | 
			
		||||
				isSrcPack: res.request.isSrcPack,
 | 
			
		||||
				cves:      cves,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if driver == nil {
 | 
			
		||||
			return 0, nil
 | 
			
		||||
		}
 | 
			
		||||
		for _, pack := range scanResult.Packages {
 | 
			
		||||
			cveDebs := driver.GetUnfixedCvesDebian(major(scanResult.Release), pack.Name)
 | 
			
		||||
			cves := []models.CveContent{}
 | 
			
		||||
			for _, cveDeb := range cveDebs {
 | 
			
		||||
				cves = append(cves, *deb.ConvertToModel(&cveDeb))
 | 
			
		||||
			}
 | 
			
		||||
			packCvesList = append(packCvesList, packCves{
 | 
			
		||||
				packName:  pack.Name,
 | 
			
		||||
				isSrcPack: false,
 | 
			
		||||
				cves:      cves,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// SrcPack
 | 
			
		||||
		for _, pack := range scanResult.SrcPackages {
 | 
			
		||||
			cveDebs := driver.GetUnfixedCvesDebian(major(scanResult.Release), pack.Name)
 | 
			
		||||
			cves := []models.CveContent{}
 | 
			
		||||
			for _, cveDeb := range cveDebs {
 | 
			
		||||
				cves = append(cves, *deb.ConvertToModel(&cveDeb))
 | 
			
		||||
			}
 | 
			
		||||
			packCvesList = append(packCvesList, packCves{
 | 
			
		||||
				packName:  pack.Name,
 | 
			
		||||
				isSrcPack: true,
 | 
			
		||||
				cves:      cves,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	delete(r.Packages, "linux")
 | 
			
		||||
 | 
			
		||||
	for _, p := range packCvesList {
 | 
			
		||||
		for _, cve := range p.cves {
 | 
			
		||||
			v, ok := r.ScannedCves[cve.CveID]
 | 
			
		||||
			if ok {
 | 
			
		||||
				if v.CveContents == nil {
 | 
			
		||||
					v.CveContents = models.NewCveContents(cve)
 | 
			
		||||
				} else {
 | 
			
		||||
					v.CveContents[models.DebianSecurityTracker] = cve
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				v = models.VulnInfo{
 | 
			
		||||
					CveID:       cve.CveID,
 | 
			
		||||
					CveContents: models.NewCveContents(cve),
 | 
			
		||||
					Confidences: models.Confidences{models.DebianSecurityTrackerMatch},
 | 
			
		||||
				}
 | 
			
		||||
				nCVEs++
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			names := []string{}
 | 
			
		||||
			if p.isSrcPack {
 | 
			
		||||
				if srcPack, ok := r.SrcPackages[p.packName]; ok {
 | 
			
		||||
					for _, binName := range srcPack.BinaryNames {
 | 
			
		||||
						if _, ok := r.Packages[binName]; ok {
 | 
			
		||||
							names = append(names, binName)
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				if p.packName == "linux" {
 | 
			
		||||
					names = append(names, linuxImage)
 | 
			
		||||
				} else {
 | 
			
		||||
					names = append(names, p.packName)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, name := range names {
 | 
			
		||||
				v.AffectedPackages = v.AffectedPackages.Store(models.PackageFixStatus{
 | 
			
		||||
					Name:        name,
 | 
			
		||||
					FixState:    "open",
 | 
			
		||||
					NotFixedYet: true,
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
			r.ScannedCves[cve.CveID] = v
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nCVEs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConvertToModel converts gost model to vuls model
 | 
			
		||||
func (deb Debian) ConvertToModel(cve *gostmodels.DebianCVE) *models.CveContent {
 | 
			
		||||
	severity := ""
 | 
			
		||||
	for _, p := range cve.Package {
 | 
			
		||||
		for _, r := range p.Release {
 | 
			
		||||
			severity = r.Urgency
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return &models.CveContent{
 | 
			
		||||
		Type:          models.DebianSecurityTracker,
 | 
			
		||||
		CveID:         cve.CveID,
 | 
			
		||||
		Summary:       cve.Description,
 | 
			
		||||
		Cvss2Severity: severity,
 | 
			
		||||
		Cvss3Severity: severity,
 | 
			
		||||
		SourceLink:    "https://security-tracker.debian.org/tracker/" + cve.CveID,
 | 
			
		||||
		Optional: map[string]string{
 | 
			
		||||
			"attack range": cve.Scope,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										61
									
								
								gost/debian_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,61 @@
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import "testing"
 | 
			
		||||
 | 
			
		||||
func TestDebian_Supported(t *testing.T) {
 | 
			
		||||
	type fields struct {
 | 
			
		||||
		Base Base
 | 
			
		||||
	}
 | 
			
		||||
	type args struct {
 | 
			
		||||
		major string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name string
 | 
			
		||||
		args args
 | 
			
		||||
		want bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "8 is supported",
 | 
			
		||||
			args: args{
 | 
			
		||||
				major: "8",
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "9 is supported",
 | 
			
		||||
			args: args{
 | 
			
		||||
				major: "9",
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "10 is supported",
 | 
			
		||||
			args: args{
 | 
			
		||||
				major: "10",
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "11 is not supported yet",
 | 
			
		||||
			args: args{
 | 
			
		||||
				major: "11",
 | 
			
		||||
			},
 | 
			
		||||
			want: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "empty string is not supported yet",
 | 
			
		||||
			args: args{
 | 
			
		||||
				major: "",
 | 
			
		||||
			},
 | 
			
		||||
			want: false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			deb := Debian{}
 | 
			
		||||
			if got := deb.supported(tt.args.major); got != tt.want {
 | 
			
		||||
				t.Errorf("Debian.Supported() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										35
									
								
								gost/gost.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,35 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	cnf "github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/knqyf263/gost/db"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Client is the interface of OVAL client.
 | 
			
		||||
type Client interface {
 | 
			
		||||
	DetectUnfixed(db.DB, *models.ScanResult, bool) (int, error)
 | 
			
		||||
	FillCVEsWithRedHat(db.DB, *models.ScanResult) error
 | 
			
		||||
 | 
			
		||||
	//TODO implement
 | 
			
		||||
	// CheckHTTPHealth() error
 | 
			
		||||
	// CheckIfGostFetched checks if Gost entries are fetched
 | 
			
		||||
	// CheckIfGostFetched(db.DB, string, string) (bool, error)
 | 
			
		||||
	// CheckIfGostFresh(db.DB, string, string) (bool, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewClient make Client by family
 | 
			
		||||
func NewClient(family string) Client {
 | 
			
		||||
	switch family {
 | 
			
		||||
	case cnf.RedHat, cnf.CentOS:
 | 
			
		||||
		return RedHat{}
 | 
			
		||||
	case cnf.Debian, cnf.Raspbian:
 | 
			
		||||
		return Debian{}
 | 
			
		||||
	case cnf.Windows:
 | 
			
		||||
		return Microsoft{}
 | 
			
		||||
	default:
 | 
			
		||||
		return Pseudo{}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										129
									
								
								gost/gost_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,129 @@
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	gostmodels "github.com/knqyf263/gost/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestSetPackageStates(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		pkgstats  []gostmodels.RedhatPackageState
 | 
			
		||||
		installed models.Packages
 | 
			
		||||
		release   string
 | 
			
		||||
		in        models.VulnInfo
 | 
			
		||||
		out       models.PackageFixStatuses
 | 
			
		||||
	}{
 | 
			
		||||
 | 
			
		||||
		//0 one
 | 
			
		||||
		{
 | 
			
		||||
			pkgstats: []gostmodels.RedhatPackageState{
 | 
			
		||||
				{
 | 
			
		||||
					FixState:    "Will not fix",
 | 
			
		||||
					PackageName: "bouncycastle",
 | 
			
		||||
					Cpe:         "cpe:/o:redhat:enterprise_linux:7",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			installed: models.Packages{
 | 
			
		||||
				"bouncycastle": models.Package{},
 | 
			
		||||
			},
 | 
			
		||||
			release: "7",
 | 
			
		||||
			in:      models.VulnInfo{},
 | 
			
		||||
			out: []models.PackageFixStatus{
 | 
			
		||||
				{
 | 
			
		||||
					Name:        "bouncycastle",
 | 
			
		||||
					FixState:    "Will not fix",
 | 
			
		||||
					NotFixedYet: true,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		//1 two
 | 
			
		||||
		{
 | 
			
		||||
			pkgstats: []gostmodels.RedhatPackageState{
 | 
			
		||||
				{
 | 
			
		||||
					FixState:    "Will not fix",
 | 
			
		||||
					PackageName: "bouncycastle",
 | 
			
		||||
					Cpe:         "cpe:/o:redhat:enterprise_linux:7",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					FixState:    "Fix deferred",
 | 
			
		||||
					PackageName: "pack_a",
 | 
			
		||||
					Cpe:         "cpe:/o:redhat:enterprise_linux:7",
 | 
			
		||||
				},
 | 
			
		||||
				// ignore not-installed-package
 | 
			
		||||
				{
 | 
			
		||||
					FixState:    "Fix deferred",
 | 
			
		||||
					PackageName: "pack_b",
 | 
			
		||||
					Cpe:         "cpe:/o:redhat:enterprise_linux:7",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			installed: models.Packages{
 | 
			
		||||
				"bouncycastle": models.Package{},
 | 
			
		||||
				"pack_a":       models.Package{},
 | 
			
		||||
			},
 | 
			
		||||
			release: "7",
 | 
			
		||||
			in:      models.VulnInfo{},
 | 
			
		||||
			out: []models.PackageFixStatus{
 | 
			
		||||
				{
 | 
			
		||||
					Name:        "bouncycastle",
 | 
			
		||||
					FixState:    "Will not fix",
 | 
			
		||||
					NotFixedYet: true,
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Name:        "pack_a",
 | 
			
		||||
					FixState:    "Fix deferred",
 | 
			
		||||
					NotFixedYet: true,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		//2 ignore affected
 | 
			
		||||
		{
 | 
			
		||||
			pkgstats: []gostmodels.RedhatPackageState{
 | 
			
		||||
				{
 | 
			
		||||
					FixState:    "affected",
 | 
			
		||||
					PackageName: "bouncycastle",
 | 
			
		||||
					Cpe:         "cpe:/o:redhat:enterprise_linux:7",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			installed: models.Packages{
 | 
			
		||||
				"bouncycastle": models.Package{},
 | 
			
		||||
			},
 | 
			
		||||
			release: "7",
 | 
			
		||||
			in: models.VulnInfo{
 | 
			
		||||
				AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
			},
 | 
			
		||||
			out: models.PackageFixStatuses{},
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		//3 look only the same os release.
 | 
			
		||||
		{
 | 
			
		||||
			pkgstats: []gostmodels.RedhatPackageState{
 | 
			
		||||
				{
 | 
			
		||||
					FixState:    "Will not fix",
 | 
			
		||||
					PackageName: "bouncycastle",
 | 
			
		||||
					Cpe:         "cpe:/o:redhat:enterprise_linux:6",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			installed: models.Packages{
 | 
			
		||||
				"bouncycastle": models.Package{},
 | 
			
		||||
			},
 | 
			
		||||
			release: "7",
 | 
			
		||||
			in: models.VulnInfo{
 | 
			
		||||
				AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
			},
 | 
			
		||||
			out: models.PackageFixStatuses{},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r := RedHat{}
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
		out := r.mergePackageStates(tt.in, tt.pkgstats, tt.installed, tt.release)
 | 
			
		||||
		if ok := reflect.DeepEqual(tt.out, out); !ok {
 | 
			
		||||
			t.Errorf("[%d]\nexpected: %v:%T\n  actual: %v:%T\n", i, tt.out, tt.out, out, out)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										101
									
								
								gost/microsoft.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,101 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/knqyf263/gost/db"
 | 
			
		||||
	gostmodels "github.com/knqyf263/gost/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Microsoft is Gost client for windows
 | 
			
		||||
type Microsoft struct {
 | 
			
		||||
	Base
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DetectUnfixed fills cve information that has in Gost
 | 
			
		||||
func (ms Microsoft) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (nCVEs int, err error) {
 | 
			
		||||
	if driver == nil {
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	}
 | 
			
		||||
	cveIDs := []string{}
 | 
			
		||||
	for cveID := range r.ScannedCves {
 | 
			
		||||
		cveIDs = append(cveIDs, cveID)
 | 
			
		||||
	}
 | 
			
		||||
	for cveID, msCve := range driver.GetMicrosoftMulti(cveIDs) {
 | 
			
		||||
		if _, ok := r.ScannedCves[cveID]; !ok {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		cveCont := ms.ConvertToModel(&msCve)
 | 
			
		||||
		v, _ := r.ScannedCves[cveID]
 | 
			
		||||
		if v.CveContents == nil {
 | 
			
		||||
			v.CveContents = models.CveContents{}
 | 
			
		||||
		}
 | 
			
		||||
		v.CveContents[models.Microsoft] = *cveCont
 | 
			
		||||
		r.ScannedCves[cveID] = v
 | 
			
		||||
	}
 | 
			
		||||
	return len(cveIDs), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConvertToModel converts gost model to vuls model
 | 
			
		||||
func (ms Microsoft) ConvertToModel(cve *gostmodels.MicrosoftCVE) *models.CveContent {
 | 
			
		||||
	v3score := 0.0
 | 
			
		||||
	var v3Vector string
 | 
			
		||||
	for _, scoreSet := range cve.ScoreSets {
 | 
			
		||||
		if v3score < scoreSet.BaseScore {
 | 
			
		||||
			v3score = scoreSet.BaseScore
 | 
			
		||||
			v3Vector = scoreSet.Vector
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var v3Severity string
 | 
			
		||||
	for _, s := range cve.Severity {
 | 
			
		||||
		v3Severity = s.Description
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var refs []models.Reference
 | 
			
		||||
	for _, r := range cve.References {
 | 
			
		||||
		if r.AttrType == "External" {
 | 
			
		||||
			refs = append(refs, models.Reference{Link: r.URL})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var cwe []string
 | 
			
		||||
	if 0 < len(cve.CWE) {
 | 
			
		||||
		cwe = []string{cve.CWE}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	option := map[string]string{}
 | 
			
		||||
	if 0 < len(cve.ExploitStatus) {
 | 
			
		||||
		option["exploit"] = cve.ExploitStatus
 | 
			
		||||
	}
 | 
			
		||||
	if 0 < len(cve.Workaround) {
 | 
			
		||||
		option["workaround"] = cve.Workaround
 | 
			
		||||
	}
 | 
			
		||||
	kbids := []string{}
 | 
			
		||||
	for _, kbid := range cve.KBIDs {
 | 
			
		||||
		kbids = append(kbids, kbid.KBID)
 | 
			
		||||
	}
 | 
			
		||||
	if 0 < len(kbids) {
 | 
			
		||||
		option["kbids"] = strings.Join(kbids, ",")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &models.CveContent{
 | 
			
		||||
		Type:          models.Microsoft,
 | 
			
		||||
		CveID:         cve.CveID,
 | 
			
		||||
		Title:         cve.Title,
 | 
			
		||||
		Summary:       cve.Description,
 | 
			
		||||
		Cvss3Score:    v3score,
 | 
			
		||||
		Cvss3Vector:   v3Vector,
 | 
			
		||||
		Cvss3Severity: v3Severity,
 | 
			
		||||
		References:    refs,
 | 
			
		||||
		CweIDs:        cwe,
 | 
			
		||||
		Mitigation:    cve.Mitigation,
 | 
			
		||||
		Published:     cve.PublishDate,
 | 
			
		||||
		LastModified:  cve.LastUpdateDate,
 | 
			
		||||
		SourceLink:    "https://portal.msrc.microsoft.com/ja-jp/security-guidance/advisory/" + cve.CveID,
 | 
			
		||||
		Optional:      option,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								gost/pseudo.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,18 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/knqyf263/gost/db"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Pseudo is Gost client except for RedHat family and Debian
 | 
			
		||||
type Pseudo struct {
 | 
			
		||||
	Base
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DetectUnfixed fills cve information that has in Gost
 | 
			
		||||
func (pse Pseudo) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (int, error) {
 | 
			
		||||
	return 0, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										271
									
								
								gost/redhat.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,271 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/knqyf263/gost/db"
 | 
			
		||||
	gostmodels "github.com/knqyf263/gost/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RedHat is Gost client for RedHat family linux
 | 
			
		||||
type RedHat struct {
 | 
			
		||||
	Base
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DetectUnfixed fills cve information that has in Gost
 | 
			
		||||
func (red RedHat) DetectUnfixed(driver db.DB, r *models.ScanResult, ignoreWillNotFix bool) (nCVEs int, err error) {
 | 
			
		||||
	return red.fillUnfixed(driver, r, ignoreWillNotFix)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (red RedHat) fillFixed(driver db.DB, r *models.ScanResult) error {
 | 
			
		||||
	cveIDs := []string{}
 | 
			
		||||
	for cveID, vuln := range r.ScannedCves {
 | 
			
		||||
		if _, ok := vuln.CveContents[models.RedHatAPI]; ok {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		cveIDs = append(cveIDs, cveID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if config.Conf.Gost.IsFetchViaHTTP() {
 | 
			
		||||
		prefix, _ := util.URLPathJoin(config.Conf.Gost.URL,
 | 
			
		||||
			"redhat", "cves")
 | 
			
		||||
		responses, err := getCvesViaHTTP(cveIDs, prefix)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		for _, res := range responses {
 | 
			
		||||
			redCve := gostmodels.RedhatCVE{}
 | 
			
		||||
			if err := json.Unmarshal([]byte(res.json), &redCve); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			if redCve.ID == 0 {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			cveCont := red.ConvertToModel(&redCve)
 | 
			
		||||
			v, ok := r.ScannedCves[res.request.cveID]
 | 
			
		||||
			if ok {
 | 
			
		||||
				if v.CveContents == nil {
 | 
			
		||||
					v.CveContents = models.NewCveContents(*cveCont)
 | 
			
		||||
				} else {
 | 
			
		||||
					v.CveContents[models.RedHatAPI] = *cveCont
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				v = models.VulnInfo{
 | 
			
		||||
					CveID:       cveCont.CveID,
 | 
			
		||||
					CveContents: models.NewCveContents(*cveCont),
 | 
			
		||||
					Confidences: models.Confidences{models.RedHatAPIMatch},
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			r.ScannedCves[res.request.cveID] = v
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if driver == nil {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		for cveID, redCve := range driver.GetRedhatMulti(cveIDs) {
 | 
			
		||||
			if len(redCve.Name) == 0 {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			cveCont := red.ConvertToModel(&redCve)
 | 
			
		||||
			v, ok := r.ScannedCves[cveID]
 | 
			
		||||
			if ok {
 | 
			
		||||
				if v.CveContents == nil {
 | 
			
		||||
					v.CveContents = models.NewCveContents(*cveCont)
 | 
			
		||||
				} else {
 | 
			
		||||
					v.CveContents[models.RedHatAPI] = *cveCont
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				v = models.VulnInfo{
 | 
			
		||||
					CveID:       cveCont.CveID,
 | 
			
		||||
					CveContents: models.NewCveContents(*cveCont),
 | 
			
		||||
					Confidences: models.Confidences{models.RedHatAPIMatch},
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			r.ScannedCves[cveID] = v
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (red RedHat) fillUnfixed(driver db.DB, r *models.ScanResult, ignoreWillNotFix bool) (nCVEs int, err error) {
 | 
			
		||||
	if config.Conf.Gost.IsFetchViaHTTP() {
 | 
			
		||||
		prefix, _ := util.URLPathJoin(config.Conf.Gost.URL,
 | 
			
		||||
			"redhat", major(r.Release), "pkgs")
 | 
			
		||||
		responses, err := getAllUnfixedCvesViaHTTP(r, prefix)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		for _, res := range responses {
 | 
			
		||||
			// CVE-ID: RedhatCVE
 | 
			
		||||
			cves := map[string]gostmodels.RedhatCVE{}
 | 
			
		||||
			if err := json.Unmarshal([]byte(res.json), &cves); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, cve := range cves {
 | 
			
		||||
				cveCont := red.ConvertToModel(&cve)
 | 
			
		||||
				v, ok := r.ScannedCves[cve.Name]
 | 
			
		||||
				if ok {
 | 
			
		||||
					if v.CveContents == nil {
 | 
			
		||||
						v.CveContents = models.NewCveContents(*cveCont)
 | 
			
		||||
					} else {
 | 
			
		||||
						v.CveContents[models.RedHatAPI] = *cveCont
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					v = models.VulnInfo{
 | 
			
		||||
						CveID:       cveCont.CveID,
 | 
			
		||||
						CveContents: models.NewCveContents(*cveCont),
 | 
			
		||||
						Confidences: models.Confidences{models.RedHatAPIMatch},
 | 
			
		||||
					}
 | 
			
		||||
					nCVEs++
 | 
			
		||||
				}
 | 
			
		||||
				pkgStats := red.mergePackageStates(v,
 | 
			
		||||
					cve.PackageState, r.Packages, r.Release)
 | 
			
		||||
				if 0 < len(pkgStats) {
 | 
			
		||||
					v.AffectedPackages = pkgStats
 | 
			
		||||
					r.ScannedCves[cve.Name] = v
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if driver == nil {
 | 
			
		||||
			return 0, nil
 | 
			
		||||
		}
 | 
			
		||||
		for _, pack := range r.Packages {
 | 
			
		||||
			// CVE-ID: RedhatCVE
 | 
			
		||||
			cves := driver.GetUnfixedCvesRedhat(major(r.Release), pack.Name, ignoreWillNotFix)
 | 
			
		||||
			for _, cve := range cves {
 | 
			
		||||
				cveCont := red.ConvertToModel(&cve)
 | 
			
		||||
				v, ok := r.ScannedCves[cve.Name]
 | 
			
		||||
				if ok {
 | 
			
		||||
					if v.CveContents == nil {
 | 
			
		||||
						v.CveContents = models.NewCveContents(*cveCont)
 | 
			
		||||
					} else {
 | 
			
		||||
						v.CveContents[models.RedHatAPI] = *cveCont
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					v = models.VulnInfo{
 | 
			
		||||
						CveID:       cveCont.CveID,
 | 
			
		||||
						CveContents: models.NewCveContents(*cveCont),
 | 
			
		||||
						Confidences: models.Confidences{models.RedHatAPIMatch},
 | 
			
		||||
					}
 | 
			
		||||
					nCVEs++
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				pkgStats := red.mergePackageStates(v,
 | 
			
		||||
					cve.PackageState, r.Packages, r.Release)
 | 
			
		||||
				if 0 < len(pkgStats) {
 | 
			
		||||
					v.AffectedPackages = pkgStats
 | 
			
		||||
					r.ScannedCves[cve.Name] = v
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nCVEs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (red RedHat) mergePackageStates(v models.VulnInfo, ps []gostmodels.RedhatPackageState, installed models.Packages, release string) (pkgStats models.PackageFixStatuses) {
 | 
			
		||||
	pkgStats = v.AffectedPackages
 | 
			
		||||
	for _, pstate := range ps {
 | 
			
		||||
		if pstate.Cpe !=
 | 
			
		||||
			"cpe:/o:redhat:enterprise_linux:"+major(release) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !(pstate.FixState == "Will not fix" ||
 | 
			
		||||
			pstate.FixState == "Fix deferred" ||
 | 
			
		||||
			pstate.FixState == "Affected") {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if _, ok := installed[pstate.PackageName]; !ok {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		notFixedYet := false
 | 
			
		||||
		switch pstate.FixState {
 | 
			
		||||
		case "Will not fix", "Fix deferred", "Affected":
 | 
			
		||||
			notFixedYet = true
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pkgStats = pkgStats.Store(models.PackageFixStatus{
 | 
			
		||||
			Name:        pstate.PackageName,
 | 
			
		||||
			FixState:    pstate.FixState,
 | 
			
		||||
			NotFixedYet: notFixedYet,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (red RedHat) parseCwe(str string) (cwes []string) {
 | 
			
		||||
	if str != "" {
 | 
			
		||||
		s := strings.Replace(str, "(", "|", -1)
 | 
			
		||||
		s = strings.Replace(s, ")", "|", -1)
 | 
			
		||||
		s = strings.Replace(s, "->", "|", -1)
 | 
			
		||||
		for _, s := range strings.Split(s, "|") {
 | 
			
		||||
			if s != "" {
 | 
			
		||||
				cwes = append(cwes, s)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConvertToModel converts gost model to vuls model
 | 
			
		||||
func (red RedHat) ConvertToModel(cve *gostmodels.RedhatCVE) *models.CveContent {
 | 
			
		||||
	cwes := red.parseCwe(cve.Cwe)
 | 
			
		||||
 | 
			
		||||
	details := []string{}
 | 
			
		||||
	for _, detail := range cve.Details {
 | 
			
		||||
		details = append(details, detail.Detail)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	v2score := 0.0
 | 
			
		||||
	if cve.Cvss.CvssBaseScore != "" {
 | 
			
		||||
		v2score, _ = strconv.ParseFloat(cve.Cvss.CvssBaseScore, 64)
 | 
			
		||||
	}
 | 
			
		||||
	v2severity := ""
 | 
			
		||||
	if v2score != 0 {
 | 
			
		||||
		v2severity = cve.ThreatSeverity
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	v3score := 0.0
 | 
			
		||||
	if cve.Cvss3.Cvss3BaseScore != "" {
 | 
			
		||||
		v3score, _ = strconv.ParseFloat(cve.Cvss3.Cvss3BaseScore, 64)
 | 
			
		||||
	}
 | 
			
		||||
	v3severity := ""
 | 
			
		||||
	if v3score != 0 {
 | 
			
		||||
		v3severity = cve.ThreatSeverity
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	refs := []models.Reference{}
 | 
			
		||||
	for _, r := range cve.References {
 | 
			
		||||
		refs = append(refs, models.Reference{Link: r.Reference})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &models.CveContent{
 | 
			
		||||
		Type:          models.RedHatAPI,
 | 
			
		||||
		CveID:         cve.Name,
 | 
			
		||||
		Title:         cve.Bugzilla.Description,
 | 
			
		||||
		Summary:       strings.Join(details, "\n"),
 | 
			
		||||
		Cvss2Score:    v2score,
 | 
			
		||||
		Cvss2Vector:   cve.Cvss.CvssScoringVector,
 | 
			
		||||
		Cvss2Severity: v2severity,
 | 
			
		||||
		Cvss3Score:    v3score,
 | 
			
		||||
		Cvss3Vector:   cve.Cvss3.Cvss3ScoringVector,
 | 
			
		||||
		Cvss3Severity: v3severity,
 | 
			
		||||
		References:    refs,
 | 
			
		||||
		CweIDs:        cwes,
 | 
			
		||||
		Mitigation:    cve.Mitigation,
 | 
			
		||||
		Published:     cve.PublicDate,
 | 
			
		||||
		SourceLink:    "https://access.redhat.com/security/cve/" + cve.Name,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										37
									
								
								gost/redhat_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,37 @@
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestParseCwe(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in  string
 | 
			
		||||
		out []string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			in:  "CWE-665->(CWE-200|CWE-89)",
 | 
			
		||||
			out: []string{"CWE-665", "CWE-200", "CWE-89"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in:  "CWE-841->CWE-770->CWE-454",
 | 
			
		||||
			out: []string{"CWE-841", "CWE-770", "CWE-454"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in:  "(CWE-122|CWE-125)",
 | 
			
		||||
			out: []string{"CWE-122", "CWE-125"},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r := RedHat{}
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
		out := r.parseCwe(tt.in)
 | 
			
		||||
		sort.Strings(out)
 | 
			
		||||
		sort.Strings(tt.out)
 | 
			
		||||
		if !reflect.DeepEqual(tt.out, out) {
 | 
			
		||||
			t.Errorf("[%d]expected: %s, actual: %s", i, tt.out, out)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										188
									
								
								gost/util.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,188 @@
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"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,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func major(osVer string) (majorVersion string) {
 | 
			
		||||
	return strings.Split(osVer, ".")[0]
 | 
			
		||||
}
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 247 KiB  | 
| 
		 Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 297 KiB  | 
| 
		 Before Width: | Height: | Size: 97 KiB  | 
| 
		 Before Width: | Height: | Size: 91 KiB  | 
@@ -1,415 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
 | 
			
		||||
  <!--Created by yEd 3.17-->
 | 
			
		||||
  <key attr.name="Description" attr.type="string" for="graph" id="d0"/>
 | 
			
		||||
  <key for="port" id="d1" yfiles.type="portgraphics"/>
 | 
			
		||||
  <key for="port" id="d2" yfiles.type="portgeometry"/>
 | 
			
		||||
  <key for="port" id="d3" yfiles.type="portuserdata"/>
 | 
			
		||||
  <key attr.name="url" attr.type="string" for="node" id="d4"/>
 | 
			
		||||
  <key attr.name="description" attr.type="string" for="node" id="d5"/>
 | 
			
		||||
  <key for="node" id="d6" yfiles.type="nodegraphics"/>
 | 
			
		||||
  <key for="graphml" id="d7" yfiles.type="resources"/>
 | 
			
		||||
  <key attr.name="url" attr.type="string" for="edge" id="d8"/>
 | 
			
		||||
  <key attr.name="description" attr.type="string" for="edge" id="d9"/>
 | 
			
		||||
  <key for="edge" id="d10" yfiles.type="edgegraphics"/>
 | 
			
		||||
  <graph edgedefault="directed" id="G">
 | 
			
		||||
    <data key="d0"/>
 | 
			
		||||
    <node id="n0">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="309.6849206349206" y="0.0"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="83.482421875" x="92.2587890625" y="18.93359375">Detect the OS<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="0.1619001116071429" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n1">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.decision">
 | 
			
		||||
          <y:Geometry height="40.0" width="80.0" x="403.6849206349206" y="206.44247787610618"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="38.0" y="18.0">
 | 
			
		||||
            <y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n2">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="90.44247787610618" width="268.0" x="309.6849206349206" y="86.0"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="60.53125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="170.763671875" x="48.61816406250006" y="14.95561393805309">Get installed packages
 | 
			
		||||
Debian/Ubuntu: dpkg-query
 | 
			
		||||
Amazon/RHEL/CentOS: rpm
 | 
			
		||||
FreeBSD: pkg<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="2.220446049250313E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n3">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="630.0546766682629"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="152.634765625" x="57.6826171875" y="18.93359375">Write results to JSON files<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="0.1619001116071429" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n4">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="287.8409153761062"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="232.744140625" x="17.6279296875" y="4.80078125">Get CVE IDs by using package manager
 | 
			
		||||
Amazon: yum plugin security
 | 
			
		||||
FreeBSD: pkg audit<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n5">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="750.4705298628534"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="42.595703125" x="112.7021484375" y="18.93359375">Report<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n6" yfiles.foldertype="group">
 | 
			
		||||
      <data key="d4"/>
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:ProxyAutoBoundsNode>
 | 
			
		||||
          <y:Realizers active="0">
 | 
			
		||||
            <y:GroupNode>
 | 
			
		||||
              <y:Geometry height="116.89483989807195" width="333.6788874841973" x="234.29467728596296" y="709.1901021013174"/>
 | 
			
		||||
              <y:Fill color="#F5F5F5" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="333.6788874841973" x="0.0" y="0.0">Vulnerability Database</y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
              <y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
 | 
			
		||||
              <y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
 | 
			
		||||
              <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
 | 
			
		||||
            </y:GroupNode>
 | 
			
		||||
            <y:GroupNode>
 | 
			
		||||
              <y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
 | 
			
		||||
              <y:Fill color="#F5F5F5" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 1</y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
              <y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
 | 
			
		||||
              <y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
 | 
			
		||||
              <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
 | 
			
		||||
            </y:GroupNode>
 | 
			
		||||
          </y:Realizers>
 | 
			
		||||
        </y:ProxyAutoBoundsNode>
 | 
			
		||||
      </data>
 | 
			
		||||
      <graph edgedefault="directed" id="n6:">
 | 
			
		||||
        <node id="n6::n0">
 | 
			
		||||
          <data key="d6">
 | 
			
		||||
            <y:GenericNode configuration="com.yworks.flowchart.dataBase">
 | 
			
		||||
              <y:Geometry height="65.22882427307195" width="136.83944374209864" x="416.1341210280616" y="745.8561177263174"/>
 | 
			
		||||
              <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="117.970703125" x="9.434370308549205" y="23.548005886535975">CVE DB (NVD / JVN)<y:LabelModel>
 | 
			
		||||
                  <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
                </y:LabelModel>
 | 
			
		||||
                <y:ModelParameter>
 | 
			
		||||
                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-8.326672684688674E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
                </y:ModelParameter>
 | 
			
		||||
              </y:NodeLabel>
 | 
			
		||||
            </y:GenericNode>
 | 
			
		||||
          </data>
 | 
			
		||||
        </node>
 | 
			
		||||
        <node id="n6::n1">
 | 
			
		||||
          <data key="d6">
 | 
			
		||||
            <y:GenericNode configuration="com.yworks.flowchart.dataBase">
 | 
			
		||||
              <y:Geometry height="65.22882427307195" width="136.83944374209864" x="249.29467728596296" y="745.8561177263174"/>
 | 
			
		||||
              <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="55.533203125" x="40.653120308549205" y="23.548005886535975">OVAL DB<y:LabelModel>
 | 
			
		||||
                  <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
                </y:LabelModel>
 | 
			
		||||
                <y:ModelParameter>
 | 
			
		||||
                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-8.326672684688674E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
                </y:ModelParameter>
 | 
			
		||||
              </y:NodeLabel>
 | 
			
		||||
            </y:GenericNode>
 | 
			
		||||
          </data>
 | 
			
		||||
        </node>
 | 
			
		||||
      </graph>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n7">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="27.144753476611868" y="287.8409153761062"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="260.83984375" x="3.580078125" y="11.8671875">Check upgradable packages
 | 
			
		||||
Debian/Ubuntu: apt-get upgrade --dry-run<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n8">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.loopLimit">
 | 
			
		||||
          <y:Geometry height="51.10998735777497" width="137.19216182048035" x="92.54867256637169" y="376.28592169721867"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="131.751953125" x="2.7201043477401754" y="9.422181178887513">foreach 
 | 
			
		||||
upgradable  packages<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="5.551115123125783E-16" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n9">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="27.144753476611868" y="459.8409153761062"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="213.619140625" x="27.1904296875" y="11.8671875">Parse changelog and get  CVE IDs
 | 
			
		||||
Debian/Ubuntu: aptitude changelog<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n10">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.loopLimitEnd">
 | 
			
		||||
          <y:Geometry height="50.0" width="137.0" x="92.64475347661187" y="545.8409153761062"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="55.24609375" x="40.876953125" y="15.93359375">end loop<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <edge id="e0" source="n2" target="n1">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="45.22123893805309" tx="0.0" ty="-20.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="none"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e1" source="n1" target="n4">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="40.0" sy="0.0" tx="0.0" ty="-28.0">
 | 
			
		||||
            <y:Point x="743.3698412698412" y="226.44247787610618"/>
 | 
			
		||||
          </y:Path>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="51.806640625" x="183.35883739927397" y="2.000003510871693">Amazon
 | 
			
		||||
FreeBSD<y:LabelModel>
 | 
			
		||||
              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="1.9999999999998863" distanceToCenter="false" position="right" ratio="0.7796030035582084" segment="0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
 | 
			
		||||
          </y:EdgeLabel>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e2" source="n0" target="n2">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="28.0" tx="0.0" ty="-45.22123893805309"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e3" source="n5" target="n6">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="10.8330078125"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="none"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e4" source="n1" target="n3">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="-123.36984126984123" ty="0.0">
 | 
			
		||||
            <y:Point x="443.6849206349206" y="658.0546766682629"/>
 | 
			
		||||
          </y:Path>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="74.6640625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="77.078125" x="-97.68364242524859" y="5.005267793098369">CentOS
 | 
			
		||||
RHEL
 | 
			
		||||
Ubuntu
 | 
			
		||||
Debian
 | 
			
		||||
Oracle Linux<y:LabelModel>
 | 
			
		||||
              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="59.14459455430983" distanceToCenter="true" position="right" ratio="0.0" segment="0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
 | 
			
		||||
          </y:EdgeLabel>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e5" source="n4" target="n3">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e6" source="n7" target="n8">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="28.0" tx="0.0" ty="-25.554993678887485"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e7" source="n8" target="n9">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="25.554993678887485" tx="0.0" ty="-28.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e8" source="n9" target="n10">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="28.0" tx="0.0" ty="-25.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e9" source="n3" target="n5">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e10" source="n1" target="n7">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
 | 
			
		||||
            <y:Point x="161.14475347661187" y="226.44247787610618"/>
 | 
			
		||||
          </y:Path>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="56.98046875" x="-196.80057112212188" y="20.933597260871807">Raspbian<y:LabelModel>
 | 
			
		||||
              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="left" ratio="0.6447921222409765" segment="0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
 | 
			
		||||
          </y:EdgeLabel>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e11" source="n10" target="n3">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="-125.78842258255952" ty="0.0">
 | 
			
		||||
            <y:Point x="161.14475347661187" y="658.0546766682629"/>
 | 
			
		||||
          </y:Path>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
  </graph>
 | 
			
		||||
  <data key="d7">
 | 
			
		||||
    <y:Resources/>
 | 
			
		||||
  </data>
 | 
			
		||||
</graphml>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 74 KiB  | 
@@ -1,494 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
 | 
			
		||||
  <!--Created by yEd 3.17-->
 | 
			
		||||
  <key attr.name="Description" attr.type="string" for="graph" id="d0"/>
 | 
			
		||||
  <key for="port" id="d1" yfiles.type="portgraphics"/>
 | 
			
		||||
  <key for="port" id="d2" yfiles.type="portgeometry"/>
 | 
			
		||||
  <key for="port" id="d3" yfiles.type="portuserdata"/>
 | 
			
		||||
  <key attr.name="url" attr.type="string" for="node" id="d4"/>
 | 
			
		||||
  <key attr.name="description" attr.type="string" for="node" id="d5"/>
 | 
			
		||||
  <key for="node" id="d6" yfiles.type="nodegraphics"/>
 | 
			
		||||
  <key for="graphml" id="d7" yfiles.type="resources"/>
 | 
			
		||||
  <key attr.name="url" attr.type="string" for="edge" id="d8"/>
 | 
			
		||||
  <key attr.name="description" attr.type="string" for="edge" id="d9"/>
 | 
			
		||||
  <key for="edge" id="d10" yfiles.type="edgegraphics"/>
 | 
			
		||||
  <graph edgedefault="directed" id="G">
 | 
			
		||||
    <data key="d0"/>
 | 
			
		||||
    <node id="n0">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="309.6849206349206" y="0.0"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="83.482421875" x="92.2587890625" y="18.93359375">Detect the OS<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="0.1619001116071429" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n1">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.decision">
 | 
			
		||||
          <y:Geometry height="40.0" width="80.0" x="403.6849206349206" y="206.44247787610618"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="38.0" y="18.0">
 | 
			
		||||
            <y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n2">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="90.44247787610618" width="268.0" x="309.6849206349206" y="86.0"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="60.53125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="170.763671875" x="48.61816406250006" y="14.95561393805309">Get installed packages
 | 
			
		||||
Debian/Ubuntu: dpkg-query
 | 
			
		||||
Amazon/RHEL/CentOS: rpm
 | 
			
		||||
FreeBSD: pkg<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="2.220446049250313E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n3">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="10.0" y="287.8409153761062"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="260.83984375" x="3.580078125" y="11.8671875">Check upgradable packages
 | 
			
		||||
Debian/Ubuntu: apt-get upgrade --dry-run<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n4">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.loopLimit">
 | 
			
		||||
          <y:Geometry height="51.10998735777497" width="137.19216182048035" x="75.40391908975982" y="376.28592169721867"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="131.751953125" x="2.7201043477401754" y="9.422181178887513">foreach 
 | 
			
		||||
upgradable  packages<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="5.551115123125783E-16" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n5">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="10.0" y="459.8409153761062"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="213.619140625" x="27.1904296875" y="11.8671875">Parse changelog and get  CVE IDs
 | 
			
		||||
Debian/Ubuntu: aptitude changelog<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n6">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.loopLimitEnd">
 | 
			
		||||
          <y:Geometry height="50.0" width="137.0" x="75.5" y="545.8409153761062"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="55.24609375" x="40.876953125" y="15.93359375">end loop<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n7">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="625.8409153761062"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="152.634765625" x="57.6826171875" y="18.93359375">Write results to JSON files<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="0.1619001116071429" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n8">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="287.8409153761062"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="232.744140625" x="17.6279296875" y="4.80078125">Get CVE IDs by using package manager
 | 
			
		||||
Amazon/RHEL: yum plugin security
 | 
			
		||||
FreeBSD: pkg audit<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n9">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="716.4553275126422"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="42.595703125" x="112.7021484375" y="18.93359375">Report<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n10">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="309.6849206349206" y="371.39590905499364"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="293.06640625" x="-12.533203124999943" y="11.8671875">Get all changelogs of updatable packages at once
 | 
			
		||||
yum changelog<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="2.220446049250313E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n11">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="309.68492063492056" y="459.8409153761062"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="205.52734375" x="31.236328125000057" y="18.93359375">Parse changelogs and get CVE IDs <y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="2.220446049250313E-16" nodeRatioY="0.1619001116071429" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n12">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="373.8409153761062"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="293.06640625" x="-12.533203124999886" y="11.8671875">Get all changelogs of updatable packages at once
 | 
			
		||||
Amazon / RHEL: yum changelog<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="2.220446049250313E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n13" yfiles.foldertype="group">
 | 
			
		||||
      <data key="d4"/>
 | 
			
		||||
      <data key="d5"/>
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:ProxyAutoBoundsNode>
 | 
			
		||||
          <y:Realizers active="0">
 | 
			
		||||
            <y:GroupNode>
 | 
			
		||||
              <y:Geometry height="116.89483989807195" width="333.6788874841973" x="229.74083438685204" y="675.1748997511062"/>
 | 
			
		||||
              <y:Fill color="#F5F5F5" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="333.6788874841973" x="0.0" y="0.0">Vulnerability Database</y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
              <y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
 | 
			
		||||
              <y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
 | 
			
		||||
              <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
 | 
			
		||||
            </y:GroupNode>
 | 
			
		||||
            <y:GroupNode>
 | 
			
		||||
              <y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
 | 
			
		||||
              <y:Fill color="#F5F5F5" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 1</y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
              <y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
 | 
			
		||||
              <y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
 | 
			
		||||
              <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
 | 
			
		||||
            </y:GroupNode>
 | 
			
		||||
          </y:Realizers>
 | 
			
		||||
        </y:ProxyAutoBoundsNode>
 | 
			
		||||
      </data>
 | 
			
		||||
      <graph edgedefault="directed" id="n13:">
 | 
			
		||||
        <node id="n13::n0">
 | 
			
		||||
          <data key="d6">
 | 
			
		||||
            <y:GenericNode configuration="com.yworks.flowchart.dataBase">
 | 
			
		||||
              <y:Geometry height="65.22882427307195" width="136.83944374209864" x="411.5802781289507" y="711.8409153761062"/>
 | 
			
		||||
              <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="117.970703125" x="9.434370308549205" y="23.548005886535975">CVE DB (NVD / JVN)<y:LabelModel>
 | 
			
		||||
                  <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
                </y:LabelModel>
 | 
			
		||||
                <y:ModelParameter>
 | 
			
		||||
                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-8.326672684688674E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
                </y:ModelParameter>
 | 
			
		||||
              </y:NodeLabel>
 | 
			
		||||
            </y:GenericNode>
 | 
			
		||||
          </data>
 | 
			
		||||
        </node>
 | 
			
		||||
        <node id="n13::n1">
 | 
			
		||||
          <data key="d6">
 | 
			
		||||
            <y:GenericNode configuration="com.yworks.flowchart.dataBase">
 | 
			
		||||
              <y:Geometry height="65.22882427307195" width="136.83944374209864" x="244.74083438685204" y="711.8409153761062"/>
 | 
			
		||||
              <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="55.533203125" x="40.653120308549205" y="23.548005886535975">OVAL DB<y:LabelModel>
 | 
			
		||||
                  <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
                </y:LabelModel>
 | 
			
		||||
                <y:ModelParameter>
 | 
			
		||||
                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-8.326672684688674E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
                </y:ModelParameter>
 | 
			
		||||
              </y:NodeLabel>
 | 
			
		||||
            </y:GenericNode>
 | 
			
		||||
          </data>
 | 
			
		||||
        </node>
 | 
			
		||||
      </graph>
 | 
			
		||||
    </node>
 | 
			
		||||
    <edge id="e0" source="n2" target="n1">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="45.22123893805309" tx="0.0" ty="-20.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="none"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e1" source="n1" target="n3">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="-40.0" sy="0.0" tx="0.0" ty="-28.0">
 | 
			
		||||
            <y:Point x="144.0" y="226.44247787610618"/>
 | 
			
		||||
          </y:Path>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="56.98046875" x="-66.95987036992159" y="-48.39843398912808">Debian
 | 
			
		||||
Ubuntu
 | 
			
		||||
Raspbian<y:LabelModel>
 | 
			
		||||
              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="1.9999999999998863" distanceToCenter="false" position="right" ratio="0.02215389573439544" segment="0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
 | 
			
		||||
          </y:EdgeLabel>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e2" source="n3" target="n4">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="28.0" tx="0.0" ty="-25.554993678887485"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e3" source="n4" target="n5">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="25.554993678887485" tx="0.0" ty="-28.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e4" source="n5" target="n6">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="28.0" tx="0.0" ty="-25.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e5" source="n6" target="n7">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="68.5" sy="0.0" tx="0.0" ty="-28.0">
 | 
			
		||||
            <y:Point x="743.3698412698412" y="570.8409153761062"/>
 | 
			
		||||
          </y:Path>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e6" source="n1" target="n8">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="40.0" sy="0.0" tx="0.0" ty="-28.0">
 | 
			
		||||
            <y:Point x="743.3698412698412" y="226.44247787610618"/>
 | 
			
		||||
          </y:Path>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="51.806640625" x="10.125014629061297" y="-48.39843398912805">Amazon
 | 
			
		||||
RHEL
 | 
			
		||||
FreeBSD<y:LabelModel>
 | 
			
		||||
              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="1.9999999999998863" distanceToCenter="false" position="left" ratio="0.022401276994204813" segment="0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
 | 
			
		||||
          </y:EdgeLabel>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e7" source="n0" target="n2">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="28.0" tx="0.0" ty="-45.22123893805309"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e8" source="n7" target="n9">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="28.0" tx="0.0" ty="-28.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e9" source="n1" target="n10">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="20.0" tx="0.0" ty="-28.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="46.708984375" x="-53.35447755843876" y="5.000003510871807">CentOS<y:LabelModel>
 | 
			
		||||
              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.0" segment="0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
 | 
			
		||||
          </y:EdgeLabel>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e10" source="n10" target="n11">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="28.0" tx="0.0" ty="-28.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e11" source="n11" target="n7">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="-24.34091537610618">
 | 
			
		||||
            <y:Point x="743.3698412698412" y="487.8409153761062"/>
 | 
			
		||||
          </y:Path>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e12" source="n8" target="n12">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e13" source="n12" target="n7">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e14" source="n9" target="n13">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="10.8330078125"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="none"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
  </graph>
 | 
			
		||||
  <data key="d7">
 | 
			
		||||
    <y:Resources/>
 | 
			
		||||
  </data>
 | 
			
		||||
</graphml>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 85 KiB  | 
| 
		 Before Width: | Height: | Size: 179 KiB  | 
@@ -1,265 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
 | 
			
		||||
  <!--Created by yEd 3.14.2-->
 | 
			
		||||
  <key attr.name="Description" attr.type="string" for="graph" id="d0"/>
 | 
			
		||||
  <key for="port" id="d1" yfiles.type="portgraphics"/>
 | 
			
		||||
  <key for="port" id="d2" yfiles.type="portgeometry"/>
 | 
			
		||||
  <key for="port" id="d3" yfiles.type="portuserdata"/>
 | 
			
		||||
  <key attr.name="url" attr.type="string" for="node" id="d4"/>
 | 
			
		||||
  <key attr.name="description" attr.type="string" for="node" id="d5"/>
 | 
			
		||||
  <key for="node" id="d6" yfiles.type="nodegraphics"/>
 | 
			
		||||
  <key for="graphml" id="d7" yfiles.type="resources"/>
 | 
			
		||||
  <key attr.name="url" attr.type="string" for="edge" id="d8"/>
 | 
			
		||||
  <key attr.name="description" attr.type="string" for="edge" id="d9"/>
 | 
			
		||||
  <key for="edge" id="d10" yfiles.type="edgegraphics"/>
 | 
			
		||||
  <graph edgedefault="directed" id="G">
 | 
			
		||||
    <data key="d0"/>
 | 
			
		||||
    <node id="n0">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:ShapeNode>
 | 
			
		||||
          <y:Geometry height="50.0" width="73.25" x="478.6165008544913" y="1358.206868489578"/>
 | 
			
		||||
          <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="28.87890625" x="22.185546875" y="15.93359375">Vuls<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
          <y:Shape type="roundrectangle"/>
 | 
			
		||||
        </y:ShapeNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n1">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:ShapeNode>
 | 
			
		||||
          <y:Geometry height="50.0" width="73.25" x="711.9623756408686" y="1043.7241210937468"/>
 | 
			
		||||
          <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="38.623046875" x="17.3134765625" y="15.93359375">Nginx<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
          <y:Shape type="roundrectangle"/>
 | 
			
		||||
        </y:ShapeNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n2">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:ShapeNode>
 | 
			
		||||
          <y:Geometry height="50.0" width="73.25" x="711.9623756408686" y="1287.206868489578"/>
 | 
			
		||||
          <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="42.7890625" x="15.23046875" y="15.93359375">MySQL<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
          <y:Shape type="roundrectangle"/>
 | 
			
		||||
        </y:ShapeNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n3" yfiles.foldertype="group">
 | 
			
		||||
      <data key="d4"/>
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:ProxyAutoBoundsNode>
 | 
			
		||||
          <y:Realizers active="0">
 | 
			
		||||
            <y:GroupNode>
 | 
			
		||||
              <y:Geometry height="101.666015625" width="291.7208747863772" x="602.72693824768" y="1146.2994791666624"/>
 | 
			
		||||
              <y:Fill color="#F5F5F5" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="291.7208747863772" x="0.0" y="0.0">Web/App</y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
              <y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
 | 
			
		||||
              <y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
 | 
			
		||||
              <y:BorderInsets bottom="0" bottomF="0.0" left="23" leftF="23.0" right="0" rightF="0.0" top="0" topF="0.0"/>
 | 
			
		||||
            </y:GroupNode>
 | 
			
		||||
            <y:GroupNode>
 | 
			
		||||
              <y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
 | 
			
		||||
              <y:Fill color="#F5F5F5" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 5</y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
              <y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
 | 
			
		||||
              <y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
 | 
			
		||||
              <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
 | 
			
		||||
            </y:GroupNode>
 | 
			
		||||
          </y:Realizers>
 | 
			
		||||
        </y:ProxyAutoBoundsNode>
 | 
			
		||||
      </data>
 | 
			
		||||
      <graph edgedefault="directed" id="n3:">
 | 
			
		||||
        <node id="n3::n0">
 | 
			
		||||
          <data key="d6">
 | 
			
		||||
            <y:ShapeNode>
 | 
			
		||||
              <y:Geometry height="50.0" width="73.25" x="640.72693824768" y="1182.9654947916624"/>
 | 
			
		||||
              <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
 | 
			
		||||
                  <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
                </y:LabelModel>
 | 
			
		||||
                <y:ModelParameter>
 | 
			
		||||
                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
                </y:ModelParameter>
 | 
			
		||||
              </y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
            </y:ShapeNode>
 | 
			
		||||
          </data>
 | 
			
		||||
        </node>
 | 
			
		||||
        <node id="n3::n1">
 | 
			
		||||
          <data key="d6">
 | 
			
		||||
            <y:ShapeNode>
 | 
			
		||||
              <y:Geometry height="50.0" width="73.25" x="723.4623756408686" y="1182.9654947916624"/>
 | 
			
		||||
              <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
 | 
			
		||||
                  <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
                </y:LabelModel>
 | 
			
		||||
                <y:ModelParameter>
 | 
			
		||||
                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
                </y:ModelParameter>
 | 
			
		||||
              </y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
            </y:ShapeNode>
 | 
			
		||||
          </data>
 | 
			
		||||
        </node>
 | 
			
		||||
        <node id="n3::n2">
 | 
			
		||||
          <data key="d6">
 | 
			
		||||
            <y:ShapeNode>
 | 
			
		||||
              <y:Geometry height="50.0" width="73.25" x="806.1978130340572" y="1182.9654947916624"/>
 | 
			
		||||
              <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
 | 
			
		||||
                  <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
                </y:LabelModel>
 | 
			
		||||
                <y:ModelParameter>
 | 
			
		||||
                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
                </y:ModelParameter>
 | 
			
		||||
              </y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
            </y:ShapeNode>
 | 
			
		||||
          </data>
 | 
			
		||||
        </node>
 | 
			
		||||
      </graph>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n4">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:ShapeNode>
 | 
			
		||||
          <y:Geometry height="50.0" width="73.25" x="821.1978130340572" y="1287.206868489578"/>
 | 
			
		||||
          <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="35.412109375" x="18.9189453125" y="15.93359375">Redis<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
          <y:Shape type="roundrectangle"/>
 | 
			
		||||
        </y:ShapeNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <edge id="e0" source="n3" target="n1">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="none"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e1" source="n3" target="n2">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="none"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e2" source="n0" target="n3::n0">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e3" source="n0" target="n3::n1">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e4" source="n0" target="n3::n2">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e5" source="n3" target="n4">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="none"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e6" source="n0" target="n4">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e7" source="n0" target="n1">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e8" source="n0" target="n2">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
  </graph>
 | 
			
		||||
  <data key="d7">
 | 
			
		||||
    <y:Resources/>
 | 
			
		||||
  </data>
 | 
			
		||||
</graphml>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 14 KiB  | 
@@ -1,194 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
 | 
			
		||||
  <!--Created by yEd 3.14.2-->
 | 
			
		||||
  <key attr.name="Description" attr.type="string" for="graph" id="d0"/>
 | 
			
		||||
  <key for="port" id="d1" yfiles.type="portgraphics"/>
 | 
			
		||||
  <key for="port" id="d2" yfiles.type="portgeometry"/>
 | 
			
		||||
  <key for="port" id="d3" yfiles.type="portuserdata"/>
 | 
			
		||||
  <key attr.name="url" attr.type="string" for="node" id="d4"/>
 | 
			
		||||
  <key attr.name="description" attr.type="string" for="node" id="d5"/>
 | 
			
		||||
  <key for="node" id="d6" yfiles.type="nodegraphics"/>
 | 
			
		||||
  <key for="graphml" id="d7" yfiles.type="resources"/>
 | 
			
		||||
  <key attr.name="url" attr.type="string" for="edge" id="d8"/>
 | 
			
		||||
  <key attr.name="description" attr.type="string" for="edge" id="d9"/>
 | 
			
		||||
  <key for="edge" id="d10" yfiles.type="edgegraphics"/>
 | 
			
		||||
  <graph edgedefault="directed" id="G">
 | 
			
		||||
    <data key="d0"/>
 | 
			
		||||
    <node id="n0">
 | 
			
		||||
      <data key="d5"/>
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:ShapeNode>
 | 
			
		||||
          <y:Geometry height="50.0" width="73.25" x="508.30825042724564" y="1132.4827473958312"/>
 | 
			
		||||
          <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="28.87890625" x="22.185546875" y="15.93359375">Vuls<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
          <y:Shape type="roundrectangle"/>
 | 
			
		||||
        </y:ShapeNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n1">
 | 
			
		||||
      <data key="d5"/>
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:ShapeNode>
 | 
			
		||||
          <y:Geometry height="50.0" width="73.25" x="749.6541252136229" y="993.2413736979156"/>
 | 
			
		||||
          <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="23.8046875" x="24.72265625" y="15.93359375">ELB<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
          <y:Shape type="roundrectangle"/>
 | 
			
		||||
        </y:ShapeNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n2">
 | 
			
		||||
      <data key="d5"/>
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:ShapeNode>
 | 
			
		||||
          <y:Geometry height="50.0" width="73.25" x="749.6541252136229" y="1236.7241210937468"/>
 | 
			
		||||
          <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="27.0390625" x="23.10546875" y="15.93359375">RDS<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
          <y:Shape type="roundrectangle"/>
 | 
			
		||||
        </y:ShapeNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n3" yfiles.foldertype="group">
 | 
			
		||||
      <data key="d4"/>
 | 
			
		||||
      <data key="d5"/>
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:ProxyAutoBoundsNode>
 | 
			
		||||
          <y:Realizers active="0">
 | 
			
		||||
            <y:GroupNode>
 | 
			
		||||
              <y:Geometry height="101.666015625" width="291.7208747863772" x="640.4186878204343" y="1095.8167317708312"/>
 | 
			
		||||
              <y:Fill color="#F5F5F5" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="291.7208747863772" x="0.0" y="0.0">Web/App</y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
              <y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
 | 
			
		||||
              <y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
 | 
			
		||||
              <y:BorderInsets bottom="0" bottomF="0.0" left="23" leftF="23.0" right="0" rightF="0.0" top="0" topF="0.0"/>
 | 
			
		||||
            </y:GroupNode>
 | 
			
		||||
            <y:GroupNode>
 | 
			
		||||
              <y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
 | 
			
		||||
              <y:Fill color="#F5F5F5" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 5</y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
              <y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
 | 
			
		||||
              <y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
 | 
			
		||||
              <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
 | 
			
		||||
            </y:GroupNode>
 | 
			
		||||
          </y:Realizers>
 | 
			
		||||
        </y:ProxyAutoBoundsNode>
 | 
			
		||||
      </data>
 | 
			
		||||
      <graph edgedefault="directed" id="n3:">
 | 
			
		||||
        <node id="n3::n0">
 | 
			
		||||
          <data key="d5"/>
 | 
			
		||||
          <data key="d6">
 | 
			
		||||
            <y:ShapeNode>
 | 
			
		||||
              <y:Geometry height="50.0" width="73.25" x="678.4186878204343" y="1132.4827473958312"/>
 | 
			
		||||
              <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
 | 
			
		||||
                  <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
                </y:LabelModel>
 | 
			
		||||
                <y:ModelParameter>
 | 
			
		||||
                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
                </y:ModelParameter>
 | 
			
		||||
              </y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
            </y:ShapeNode>
 | 
			
		||||
          </data>
 | 
			
		||||
        </node>
 | 
			
		||||
        <node id="n3::n1">
 | 
			
		||||
          <data key="d5"/>
 | 
			
		||||
          <data key="d6">
 | 
			
		||||
            <y:ShapeNode>
 | 
			
		||||
              <y:Geometry height="50.0" width="73.25" x="761.1541252136229" y="1132.4827473958312"/>
 | 
			
		||||
              <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
 | 
			
		||||
                  <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
                </y:LabelModel>
 | 
			
		||||
                <y:ModelParameter>
 | 
			
		||||
                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
                </y:ModelParameter>
 | 
			
		||||
              </y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
            </y:ShapeNode>
 | 
			
		||||
          </data>
 | 
			
		||||
        </node>
 | 
			
		||||
        <node id="n3::n2">
 | 
			
		||||
          <data key="d5"/>
 | 
			
		||||
          <data key="d6">
 | 
			
		||||
            <y:ShapeNode>
 | 
			
		||||
              <y:Geometry height="50.0" width="73.25" x="843.8895626068115" y="1132.4827473958312"/>
 | 
			
		||||
              <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
 | 
			
		||||
                  <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
                </y:LabelModel>
 | 
			
		||||
                <y:ModelParameter>
 | 
			
		||||
                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
                </y:ModelParameter>
 | 
			
		||||
              </y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
            </y:ShapeNode>
 | 
			
		||||
          </data>
 | 
			
		||||
        </node>
 | 
			
		||||
      </graph>
 | 
			
		||||
    </node>
 | 
			
		||||
    <edge id="e0" source="n3" target="n1">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="none"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e1" source="n3" target="n2">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="none"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e2" source="n0" target="n3::n0">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
  </graph>
 | 
			
		||||
  <data key="d7">
 | 
			
		||||
    <y:Resources/>
 | 
			
		||||
  </data>
 | 
			
		||||
</graphml>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 5.6 KiB  | 
							
								
								
									
										108
									
								
								libmanager/libManager.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,108 @@
 | 
			
		||||
package libmanager
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
 | 
			
		||||
	db2 "github.com/aquasecurity/trivy-db/pkg/db"
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/db"
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/github"
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/indicator"
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/log"
 | 
			
		||||
	"github.com/spf13/afero"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
	"k8s.io/utils/clock"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DetectLibsCves fills LibraryScanner information
 | 
			
		||||
func DetectLibsCves(r *models.ScanResult) (totalCnt int, err error) {
 | 
			
		||||
	if len(r.LibraryScanners) == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// initialize trivy's logger and db
 | 
			
		||||
	err = log.InitLogger(false, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	util.Log.Info("Updating library db...")
 | 
			
		||||
	if err := downloadDB(config.Version, config.Conf.TrivyCacheDBDir, config.Conf.NoProgress, false, false); err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := db2.Init(config.Conf.TrivyCacheDBDir); err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	defer db2.Close()
 | 
			
		||||
 | 
			
		||||
	for _, lib := range r.LibraryScanners {
 | 
			
		||||
		vinfos, err := lib.Scan()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		for _, vinfo := range vinfos {
 | 
			
		||||
			vinfo.Confidences.AppendIfMissing(models.TrivyMatch)
 | 
			
		||||
			if v, ok := r.ScannedCves[vinfo.CveID]; !ok {
 | 
			
		||||
				r.ScannedCves[vinfo.CveID] = vinfo
 | 
			
		||||
			} else {
 | 
			
		||||
				v.LibraryFixedIns = append(v.LibraryFixedIns, vinfo.LibraryFixedIns...)
 | 
			
		||||
				r.ScannedCves[vinfo.CveID] = v
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		totalCnt += len(vinfos)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return totalCnt, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func downloadDB(appVersion, cacheDir string, quiet, light, skipUpdate bool) error {
 | 
			
		||||
	client := initializeDBClient(cacheDir, quiet)
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
	needsUpdate, err := client.NeedsUpdate(appVersion, light, skipUpdate)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("database error: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if needsUpdate {
 | 
			
		||||
		util.Log.Info("Need to update DB")
 | 
			
		||||
		util.Log.Info("Downloading DB...")
 | 
			
		||||
		if err := client.Download(ctx, cacheDir, light); err != nil {
 | 
			
		||||
			return xerrors.Errorf("failed to download vulnerability DB: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		if err = client.UpdateMetadata(cacheDir); err != nil {
 | 
			
		||||
			return xerrors.Errorf("unable to update database metadata: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// for debug
 | 
			
		||||
	if err := showDBInfo(cacheDir); err != nil {
 | 
			
		||||
		return xerrors.Errorf("failed to show database info: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func initializeDBClient(cacheDir string, quiet bool) db.Client {
 | 
			
		||||
	config := db2.Config{}
 | 
			
		||||
	client := github.NewClient()
 | 
			
		||||
	progressBar := indicator.NewProgressBar(quiet)
 | 
			
		||||
	realClock := clock.RealClock{}
 | 
			
		||||
	fs := afero.NewOsFs()
 | 
			
		||||
	metadata := db.NewMetadata(fs, cacheDir)
 | 
			
		||||
	dbClient := db.NewClient(config, client, progressBar, realClock, metadata)
 | 
			
		||||
	return dbClient
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func showDBInfo(cacheDir string) error {
 | 
			
		||||
	m := db.NewMetadata(afero.NewOsFs(), cacheDir)
 | 
			
		||||
	metadata, err := m.Get()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("something wrong with DB: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	util.Log.Debugf("DB Schema: %d, Type: %d, UpdatedAt: %s, NextUpdate: %s",
 | 
			
		||||
		metadata.Version, metadata.Type, metadata.UpdatedAt, metadata.NextUpdate)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										59
									
								
								main.go
									
									
									
									
									
								
							
							
						
						@@ -1,59 +0,0 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"context"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/commands"
 | 
			
		||||
	"github.com/google/subcommands"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Version of Vuls
 | 
			
		||||
var version = "0.4.0"
 | 
			
		||||
 | 
			
		||||
// Revision of Git
 | 
			
		||||
var revision string
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	subcommands.Register(subcommands.HelpCommand(), "")
 | 
			
		||||
	subcommands.Register(subcommands.FlagsCommand(), "")
 | 
			
		||||
	subcommands.Register(subcommands.CommandsCommand(), "")
 | 
			
		||||
	subcommands.Register(&commands.DiscoverCmd{}, "discover")
 | 
			
		||||
	subcommands.Register(&commands.TuiCmd{}, "tui")
 | 
			
		||||
	subcommands.Register(&commands.ScanCmd{}, "scan")
 | 
			
		||||
	subcommands.Register(&commands.HistoryCmd{}, "history")
 | 
			
		||||
	subcommands.Register(&commands.ReportCmd{}, "report")
 | 
			
		||||
	subcommands.Register(&commands.ConfigtestCmd{}, "configtest")
 | 
			
		||||
 | 
			
		||||
	var v = flag.Bool("v", false, "Show version")
 | 
			
		||||
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	if *v {
 | 
			
		||||
		fmt.Printf("vuls %s %s\n", version, revision)
 | 
			
		||||
		os.Exit(int(subcommands.ExitSuccess))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
	os.Exit(int(subcommands.Execute(ctx)))
 | 
			
		||||
}
 | 
			
		||||
@@ -1,25 +1,9 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CveContents has CveContent
 | 
			
		||||
@@ -61,21 +45,24 @@ func (v CveContents) Except(exceptCtypes ...CveContentType) (values CveContents)
 | 
			
		||||
// 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})
 | 
			
		||||
		if cont, found := v[Jvn]; found && 0 < len(cont.SourceLink) {
 | 
			
		||||
			values = append(values, CveContentStr{Jvn, cont.SourceLink})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	order := CveContentTypes{NVD, NewCveContentType(myFamily)}
 | 
			
		||||
	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,
 | 
			
		||||
			Type:  Nvd,
 | 
			
		||||
			Value: "https://nvd.nist.gov/vuln/detail/" + cveID,
 | 
			
		||||
		}}
 | 
			
		||||
	}
 | 
			
		||||
@@ -109,7 +96,7 @@ type CveContentCpes struct {
 | 
			
		||||
// Cpes returns affected CPEs of this Vulnerability
 | 
			
		||||
func (v CveContents) Cpes(myFamily string) (values []CveContentCpes) {
 | 
			
		||||
	order := CveContentTypes{NewCveContentType(myFamily)}
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(append(order)...)...)
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(order...)...)
 | 
			
		||||
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v[ctype]; found && 0 < len(cont.Cpes) {
 | 
			
		||||
@@ -131,7 +118,7 @@ type CveContentRefs struct {
 | 
			
		||||
// References returns References
 | 
			
		||||
func (v CveContents) References(myFamily string) (values []CveContentRefs) {
 | 
			
		||||
	order := CveContentTypes{NewCveContentType(myFamily)}
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(append(order)...)...)
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(order...)...)
 | 
			
		||||
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v[ctype]; found && 0 < len(cont.References) {
 | 
			
		||||
@@ -141,18 +128,22 @@ func (v CveContents) References(myFamily string) (values []CveContentRefs) {
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CweIDs returns related CweIDs of the vulnerability
 | 
			
		||||
func (v CveContents) CweIDs(myFamily string) (values []CveContentStr) {
 | 
			
		||||
	order := CveContentTypes{NewCveContentType(myFamily)}
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(append(order)...)...)
 | 
			
		||||
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(order...)...)
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v[ctype]; found && 0 < len(cont.CweID) {
 | 
			
		||||
			// RedHat's OVAL sometimes contains multiple CWE-IDs separated by spaces
 | 
			
		||||
			for _, cweID := range strings.Fields(cont.CweID) {
 | 
			
		||||
		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,
 | 
			
		||||
@@ -163,23 +154,38 @@ func (v CveContents) CweIDs(myFamily string) (values []CveContentStr) {
 | 
			
		||||
	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
 | 
			
		||||
	CveID        string
 | 
			
		||||
	Title        string
 | 
			
		||||
	Summary      string
 | 
			
		||||
	Severity     string
 | 
			
		||||
	Cvss2Score   float64
 | 
			
		||||
	Cvss2Vector  string
 | 
			
		||||
	Cvss3Score   float64
 | 
			
		||||
	Cvss3Vector  string
 | 
			
		||||
	SourceLink   string
 | 
			
		||||
	Cpes         []Cpe
 | 
			
		||||
	References   References
 | 
			
		||||
	CweID        string
 | 
			
		||||
	Published    time.Time
 | 
			
		||||
	LastModified time.Time
 | 
			
		||||
	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
 | 
			
		||||
@@ -193,33 +199,66 @@ type CveContentType string
 | 
			
		||||
// NewCveContentType create CveContentType
 | 
			
		||||
func NewCveContentType(name string) CveContentType {
 | 
			
		||||
	switch name {
 | 
			
		||||
	case "nvdxml":
 | 
			
		||||
		return NvdXML
 | 
			
		||||
	case "nvd":
 | 
			
		||||
		return NVD
 | 
			
		||||
		return Nvd
 | 
			
		||||
	case "jvn":
 | 
			
		||||
		return JVN
 | 
			
		||||
		return Jvn
 | 
			
		||||
	case "redhat", "centos":
 | 
			
		||||
		return RedHat
 | 
			
		||||
	case "oracle":
 | 
			
		||||
		return Oracle
 | 
			
		||||
	case "ubuntu":
 | 
			
		||||
		return Ubuntu
 | 
			
		||||
	case "debian":
 | 
			
		||||
	case "debian", vulnerability.DebianOVAL:
 | 
			
		||||
		return Debian
 | 
			
		||||
	case "redhat_api":
 | 
			
		||||
		return RedHatAPI
 | 
			
		||||
	case "debian_security_tracker":
 | 
			
		||||
		return DebianSecurityTracker
 | 
			
		||||
	case "microsoft":
 | 
			
		||||
		return Microsoft
 | 
			
		||||
	case "wordpress":
 | 
			
		||||
		return WPVulnDB
 | 
			
		||||
	case "amazon":
 | 
			
		||||
		return Amazon
 | 
			
		||||
	case "trivy":
 | 
			
		||||
		return Trivy
 | 
			
		||||
	// case vulnerability.NodejsSecurityWg:
 | 
			
		||||
	// 	return NodeSec
 | 
			
		||||
	// case vulnerability.PythonSafetyDB:
 | 
			
		||||
	// 	return PythonSec
 | 
			
		||||
	// case vulnerability.RustSec:
 | 
			
		||||
	// 	return RustSec
 | 
			
		||||
	// case vulnerability.PhpSecurityAdvisories:
 | 
			
		||||
	// 	return PhpSec
 | 
			
		||||
	// case vulnerability.RubySec:
 | 
			
		||||
	// 	return RubySec
 | 
			
		||||
	default:
 | 
			
		||||
		return Unknown
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// NVD is NVD
 | 
			
		||||
	NVD CveContentType = "nvd"
 | 
			
		||||
	// NvdXML is NvdXML
 | 
			
		||||
	NvdXML CveContentType = "nvdxml"
 | 
			
		||||
 | 
			
		||||
	// JVN is JVN
 | 
			
		||||
	JVN CveContentType = "jvn"
 | 
			
		||||
	// Nvd is Nvd
 | 
			
		||||
	Nvd CveContentType = "nvd"
 | 
			
		||||
 | 
			
		||||
	// Jvn is Jvn
 | 
			
		||||
	Jvn CveContentType = "jvn"
 | 
			
		||||
 | 
			
		||||
	// RedHat is RedHat
 | 
			
		||||
	RedHat CveContentType = "redhat"
 | 
			
		||||
 | 
			
		||||
	// RedHatAPI is RedHat
 | 
			
		||||
	RedHatAPI CveContentType = "redhat_api"
 | 
			
		||||
 | 
			
		||||
	// DebianSecurityTracker is Debian Security tracker
 | 
			
		||||
	DebianSecurityTracker CveContentType = "debian_security_tracker"
 | 
			
		||||
 | 
			
		||||
	// Debian is Debian
 | 
			
		||||
	Debian CveContentType = "debian"
 | 
			
		||||
 | 
			
		||||
@@ -229,9 +268,36 @@ const (
 | 
			
		||||
	// Oracle is Oracle Linux
 | 
			
		||||
	Oracle CveContentType = "oracle"
 | 
			
		||||
 | 
			
		||||
	// Amazon is Amazon Linux
 | 
			
		||||
	Amazon CveContentType = "amazon"
 | 
			
		||||
 | 
			
		||||
	// SUSE is SUSE Linux
 | 
			
		||||
	SUSE CveContentType = "suse"
 | 
			
		||||
 | 
			
		||||
	// Microsoft is Microsoft
 | 
			
		||||
	Microsoft CveContentType = "microsoft"
 | 
			
		||||
 | 
			
		||||
	// WPVulnDB is WordPress
 | 
			
		||||
	WPVulnDB CveContentType = "wpvulndb"
 | 
			
		||||
 | 
			
		||||
	// Trivy is Trivy
 | 
			
		||||
	Trivy CveContentType = "trivy"
 | 
			
		||||
 | 
			
		||||
	// NodeSec : for JS
 | 
			
		||||
	// NodeSec CveContentType = "node"
 | 
			
		||||
 | 
			
		||||
	// // PythonSec : for PHP
 | 
			
		||||
	// PythonSec CveContentType = "python"
 | 
			
		||||
 | 
			
		||||
	// // PhpSec : for PHP
 | 
			
		||||
	// PhpSec CveContentType = "php"
 | 
			
		||||
 | 
			
		||||
	// // RubySec : for Ruby
 | 
			
		||||
	// RubySec CveContentType = "ruby"
 | 
			
		||||
 | 
			
		||||
	// // RustSec : for Rust
 | 
			
		||||
	// RustSec CveContentType = "rust"
 | 
			
		||||
 | 
			
		||||
	// Unknown is Unknown
 | 
			
		||||
	Unknown CveContentType = "unknown"
 | 
			
		||||
)
 | 
			
		||||
@@ -240,7 +306,25 @@ const (
 | 
			
		||||
type CveContentTypes []CveContentType
 | 
			
		||||
 | 
			
		||||
// AllCveContetTypes has all of CveContentTypes
 | 
			
		||||
var AllCveContetTypes = CveContentTypes{NVD, JVN, RedHat, Debian, Ubuntu}
 | 
			
		||||
var AllCveContetTypes = CveContentTypes{
 | 
			
		||||
	Nvd,
 | 
			
		||||
	NvdXML,
 | 
			
		||||
	Jvn,
 | 
			
		||||
	RedHat,
 | 
			
		||||
	RedHatAPI,
 | 
			
		||||
	Debian,
 | 
			
		||||
	Ubuntu,
 | 
			
		||||
	Amazon,
 | 
			
		||||
	SUSE,
 | 
			
		||||
	DebianSecurityTracker,
 | 
			
		||||
	WPVulnDB,
 | 
			
		||||
	Trivy,
 | 
			
		||||
	// NodeSec,
 | 
			
		||||
	// PythonSec,
 | 
			
		||||
	// PhpSec,
 | 
			
		||||
	// RubySec,
 | 
			
		||||
	// RustSec,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Except returns CveContentTypes except for given args
 | 
			
		||||
func (c CveContentTypes) Except(excepts ...CveContentType) (excepted CveContentTypes) {
 | 
			
		||||
@@ -261,7 +345,8 @@ func (c CveContentTypes) Except(excepts ...CveContentType) (excepted CveContentT
 | 
			
		||||
 | 
			
		||||
// Cpe is Common Platform Enumeration
 | 
			
		||||
type Cpe struct {
 | 
			
		||||
	CpeName string
 | 
			
		||||
	URI             string `json:"uri"`
 | 
			
		||||
	FormattedString string `json:"formattedString"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// References is a slice of Reference
 | 
			
		||||
@@ -269,7 +354,7 @@ type References []Reference
 | 
			
		||||
 | 
			
		||||
// Reference has a related link of the CVE
 | 
			
		||||
type Reference struct {
 | 
			
		||||
	Source string
 | 
			
		||||
	Link   string
 | 
			
		||||
	RefID  string
 | 
			
		||||
	Source string `json:"source"`
 | 
			
		||||
	Link   string `json:"link"`
 | 
			
		||||
	RefID  string `json:"refID"`
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,3 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
@@ -60,27 +44,27 @@ func TestSourceLinks(t *testing.T) {
 | 
			
		||||
				lang:  "ja",
 | 
			
		||||
				cveID: "CVE-2017-6074",
 | 
			
		||||
				cont: CveContents{
 | 
			
		||||
					JVN: {
 | 
			
		||||
						Type:       JVN,
 | 
			
		||||
					Jvn: {
 | 
			
		||||
						Type:       Jvn,
 | 
			
		||||
						SourceLink: "https://jvn.jp/vu/JVNVU93610402/",
 | 
			
		||||
					},
 | 
			
		||||
					RedHat: {
 | 
			
		||||
						Type:       RedHat,
 | 
			
		||||
						SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
 | 
			
		||||
					},
 | 
			
		||||
					NVD: {
 | 
			
		||||
						Type:       NVD,
 | 
			
		||||
					NvdXML: {
 | 
			
		||||
						Type:       NvdXML,
 | 
			
		||||
						SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: []CveContentStr{
 | 
			
		||||
				{
 | 
			
		||||
					Type:  JVN,
 | 
			
		||||
					Type:  Jvn,
 | 
			
		||||
					Value: "https://jvn.jp/vu/JVNVU93610402/",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Type:  NVD,
 | 
			
		||||
					Type:  NvdXML,
 | 
			
		||||
					Value: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
@@ -95,23 +79,23 @@ func TestSourceLinks(t *testing.T) {
 | 
			
		||||
				lang:  "en",
 | 
			
		||||
				cveID: "CVE-2017-6074",
 | 
			
		||||
				cont: CveContents{
 | 
			
		||||
					JVN: {
 | 
			
		||||
						Type:       JVN,
 | 
			
		||||
					Jvn: {
 | 
			
		||||
						Type:       Jvn,
 | 
			
		||||
						SourceLink: "https://jvn.jp/vu/JVNVU93610402/",
 | 
			
		||||
					},
 | 
			
		||||
					RedHat: {
 | 
			
		||||
						Type:       RedHat,
 | 
			
		||||
						SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
 | 
			
		||||
					},
 | 
			
		||||
					NVD: {
 | 
			
		||||
						Type:       NVD,
 | 
			
		||||
					NvdXML: {
 | 
			
		||||
						Type:       NvdXML,
 | 
			
		||||
						SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: []CveContentStr{
 | 
			
		||||
				{
 | 
			
		||||
					Type:  NVD,
 | 
			
		||||
					Type:  NvdXML,
 | 
			
		||||
					Value: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
@@ -129,16 +113,16 @@ func TestSourceLinks(t *testing.T) {
 | 
			
		||||
			},
 | 
			
		||||
			out: []CveContentStr{
 | 
			
		||||
				{
 | 
			
		||||
					Type:  NVD,
 | 
			
		||||
					Type:  Nvd,
 | 
			
		||||
					Value: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
	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("\nexpected: %v\n  actual: %v\n", tt.out, actual)
 | 
			
		||||
			t.Errorf("\n[%d] expected: %v\n  actual: %v\n", i, tt.out, actual)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -158,16 +142,16 @@ func TestVendorLink(t *testing.T) {
 | 
			
		||||
				vinfo: VulnInfo{
 | 
			
		||||
					CveID: "CVE-2017-6074",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						JVN: {
 | 
			
		||||
							Type:       JVN,
 | 
			
		||||
						Jvn: {
 | 
			
		||||
							Type:       Jvn,
 | 
			
		||||
							SourceLink: "https://jvn.jp/vu/JVNVU93610402/",
 | 
			
		||||
						},
 | 
			
		||||
						RedHat: {
 | 
			
		||||
							Type:       RedHat,
 | 
			
		||||
							SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
 | 
			
		||||
						},
 | 
			
		||||
						NVD: {
 | 
			
		||||
							Type:       NVD,
 | 
			
		||||
						NvdXML: {
 | 
			
		||||
							Type:       NvdXML,
 | 
			
		||||
							SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										144
									
								
								models/library.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,144 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	"github.com/Masterminds/semver/v3"
 | 
			
		||||
	"github.com/aquasecurity/trivy-db/pkg/db"
 | 
			
		||||
	trivyDBTypes "github.com/aquasecurity/trivy-db/pkg/types"
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/detector/library"
 | 
			
		||||
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/types"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
	// "github.com/aquasecurity/go-dep-parser/pkg/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// LibraryScanners is an array of LibraryScanner
 | 
			
		||||
type LibraryScanners []LibraryScanner
 | 
			
		||||
 | 
			
		||||
// Find : find by name
 | 
			
		||||
func (lss LibraryScanners) Find(path, name string) map[string]types.Library {
 | 
			
		||||
	filtered := map[string]types.Library{}
 | 
			
		||||
	for _, ls := range lss {
 | 
			
		||||
		for _, lib := range ls.Libs {
 | 
			
		||||
			if ls.Path == path && lib.Name == name {
 | 
			
		||||
				filtered[ls.Path] = lib
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return filtered
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LibraryScanner has libraries information
 | 
			
		||||
type LibraryScanner struct {
 | 
			
		||||
	Path string
 | 
			
		||||
	Libs []types.Library
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Scan : scan target library
 | 
			
		||||
func (s LibraryScanner) Scan() ([]VulnInfo, error) {
 | 
			
		||||
	scanner, err := library.DriverFactory{}.NewDriver(filepath.Base(string(s.Path)))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to new a library driver: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	var vulnerabilities = []VulnInfo{}
 | 
			
		||||
	for _, pkg := range s.Libs {
 | 
			
		||||
		v, err := semver.StrictNewVersion(pkg.Version)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Debugf("new version cant detected %s@%s", pkg.Name, pkg.Version)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		tvulns, err := scanner.Detect(pkg.Name, v)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("failed to detect %s vulnerabilities: %w", scanner.Type(), err)
 | 
			
		||||
		}
 | 
			
		||||
		if len(tvulns) == 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		vulns := s.convertFanalToVuln(tvulns)
 | 
			
		||||
		vulnerabilities = append(vulnerabilities, vulns...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return vulnerabilities, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s LibraryScanner) convertFanalToVuln(tvulns []types.DetectedVulnerability) (vulns []VulnInfo) {
 | 
			
		||||
	for _, tvuln := range tvulns {
 | 
			
		||||
		vinfo, err := s.getVulnDetail(tvuln)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Debugf("failed to getVulnDetail. err: %s, tvuln: %#v", err, tvuln)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		vulns = append(vulns, vinfo)
 | 
			
		||||
	}
 | 
			
		||||
	return vulns
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s LibraryScanner) getVulnDetail(tvuln types.DetectedVulnerability) (vinfo VulnInfo, err error) {
 | 
			
		||||
	vul, err := db.Config{}.GetVulnerability(tvuln.VulnerabilityID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return vinfo, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vinfo.CveID = tvuln.VulnerabilityID
 | 
			
		||||
	vinfo.CveContents = getCveContents(tvuln.VulnerabilityID, vul)
 | 
			
		||||
	if tvuln.FixedVersion != "" {
 | 
			
		||||
		vinfo.LibraryFixedIns = []LibraryFixedIn{
 | 
			
		||||
			{
 | 
			
		||||
				Key:     s.GetLibraryKey(),
 | 
			
		||||
				Name:    tvuln.PkgName,
 | 
			
		||||
				FixedIn: tvuln.FixedVersion,
 | 
			
		||||
				Path:    s.Path,
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return vinfo, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getCveContents(cveID string, vul trivyDBTypes.Vulnerability) (contents map[CveContentType]CveContent) {
 | 
			
		||||
	contents = map[CveContentType]CveContent{}
 | 
			
		||||
	refs := []Reference{}
 | 
			
		||||
	for _, refURL := range vul.References {
 | 
			
		||||
		refs = append(refs, Reference{Source: "trivy", Link: refURL})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	content := CveContent{
 | 
			
		||||
		Type:          Trivy,
 | 
			
		||||
		CveID:         cveID,
 | 
			
		||||
		Title:         vul.Title,
 | 
			
		||||
		Summary:       vul.Description,
 | 
			
		||||
		Cvss3Severity: string(vul.Severity),
 | 
			
		||||
		References:    refs,
 | 
			
		||||
	}
 | 
			
		||||
	contents[Trivy] = content
 | 
			
		||||
	return contents
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LibraryMap is filename and library type
 | 
			
		||||
var LibraryMap = map[string]string{
 | 
			
		||||
	"package-lock.json": "node",
 | 
			
		||||
	"yarn.lock":         "node",
 | 
			
		||||
	"Gemfile.lock":      "ruby",
 | 
			
		||||
	"Cargo.lock":        "rust",
 | 
			
		||||
	"composer.lock":     "php",
 | 
			
		||||
	"Pipfile.lock":      "python",
 | 
			
		||||
	"poetry.lock":       "python",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLibraryKey returns target library key
 | 
			
		||||
func (s LibraryScanner) GetLibraryKey() string {
 | 
			
		||||
	fileName := filepath.Base(s.Path)
 | 
			
		||||
	return LibraryMap[fileName]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LibraryFixedIn has library fixed information
 | 
			
		||||
type LibraryFixedIn struct {
 | 
			
		||||
	Key     string `json:"key,omitempty"`
 | 
			
		||||
	Name    string `json:"name,omitempty"`
 | 
			
		||||
	FixedIn string `json:"fixedIn,omitempty"`
 | 
			
		||||
	Path    string `json:"path,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										96
									
								
								models/library_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,96 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestLibraryScanners_Find(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		path string
 | 
			
		||||
		name string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name string
 | 
			
		||||
		lss  LibraryScanners
 | 
			
		||||
		args args
 | 
			
		||||
		want map[string]types.Library
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "single file",
 | 
			
		||||
			lss: LibraryScanners{
 | 
			
		||||
				{
 | 
			
		||||
					Path: "/pathA",
 | 
			
		||||
					Libs: []types.Library{
 | 
			
		||||
						{
 | 
			
		||||
							Name:    "libA",
 | 
			
		||||
							Version: "1.0.0",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			args: args{"/pathA", "libA"},
 | 
			
		||||
			want: map[string]types.Library{
 | 
			
		||||
				"/pathA": {
 | 
			
		||||
					Name:    "libA",
 | 
			
		||||
					Version: "1.0.0",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "multi file",
 | 
			
		||||
			lss: LibraryScanners{
 | 
			
		||||
				{
 | 
			
		||||
					Path: "/pathA",
 | 
			
		||||
					Libs: []types.Library{
 | 
			
		||||
						{
 | 
			
		||||
							Name:    "libA",
 | 
			
		||||
							Version: "1.0.0",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Path: "/pathB",
 | 
			
		||||
					Libs: []types.Library{
 | 
			
		||||
						{
 | 
			
		||||
							Name:    "libA",
 | 
			
		||||
							Version: "1.0.5",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			args: args{"/pathA", "libA"},
 | 
			
		||||
			want: map[string]types.Library{
 | 
			
		||||
				"/pathA": {
 | 
			
		||||
					Name:    "libA",
 | 
			
		||||
					Version: "1.0.0",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "miss",
 | 
			
		||||
			lss: LibraryScanners{
 | 
			
		||||
				{
 | 
			
		||||
					Path: "/pathA",
 | 
			
		||||
					Libs: []types.Library{
 | 
			
		||||
						{
 | 
			
		||||
							Name:    "libA",
 | 
			
		||||
							Version: "1.0.0",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			args: args{"/pathA", "libB"},
 | 
			
		||||
			want: map[string]types.Library{},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			if got := tt.lss.Find(tt.args.path, tt.args.name); !reflect.DeepEqual(got, tt.want) {
 | 
			
		||||
				t.Errorf("LibraryScanners.Find() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,21 +1,4 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
// JSONVersion is JSON Version
 | 
			
		||||
const JSONVersion = 3
 | 
			
		||||
const JSONVersion = 4
 | 
			
		||||
 
 | 
			
		||||
@@ -1,18 +0,0 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package models
 | 
			
		||||
@@ -1,26 +1,12 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Packages is Map of Package
 | 
			
		||||
@@ -38,6 +24,12 @@ func NewPackages(packs ...Package) Packages {
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
@@ -60,18 +52,7 @@ func (ps Packages) Merge(other Packages) Packages {
 | 
			
		||||
	return merged
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FormatUpdatablePacksSummary returns a summary of updatable packages
 | 
			
		||||
func (ps Packages) FormatUpdatablePacksSummary() string {
 | 
			
		||||
	nUpdatable := 0
 | 
			
		||||
	for _, p := range ps {
 | 
			
		||||
		if p.NewVersion != "" {
 | 
			
		||||
			nUpdatable++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%d updatable packages", nUpdatable)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindOne search a element by name-newver-newrel-arch
 | 
			
		||||
// FindOne search a element
 | 
			
		||||
func (ps Packages) FindOne(f func(Package) bool) (string, Package, bool) {
 | 
			
		||||
	for key, p := range ps {
 | 
			
		||||
		if f(p) {
 | 
			
		||||
@@ -81,16 +62,44 @@ func (ps Packages) FindOne(f func(Package) bool) (string, Package, bool) {
 | 
			
		||||
	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
 | 
			
		||||
	Version    string
 | 
			
		||||
	Release    string
 | 
			
		||||
	NewVersion string
 | 
			
		||||
	NewRelease string
 | 
			
		||||
	Arch       string
 | 
			
		||||
	Repository string
 | 
			
		||||
	Changelog  Changelog
 | 
			
		||||
	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
 | 
			
		||||
@@ -112,14 +121,23 @@ func (p Package) FormatNewVer() string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FormatVersionFromTo formats installed and new package version
 | 
			
		||||
func (p Package) FormatVersionFromTo(notFixedYet bool) string {
 | 
			
		||||
func (p Package) FormatVersionFromTo(stat PackageFixStatus) string {
 | 
			
		||||
	to := p.FormatNewVer()
 | 
			
		||||
	if notFixedYet {
 | 
			
		||||
		to = "Not Fixed Yet"
 | 
			
		||||
	if stat.NotFixedYet {
 | 
			
		||||
		if stat.FixState != "" {
 | 
			
		||||
			to = stat.FixState
 | 
			
		||||
		} else {
 | 
			
		||||
			to = "Not Fixed Yet"
 | 
			
		||||
		}
 | 
			
		||||
	} else if p.NewVersion == "" {
 | 
			
		||||
		to = "Unknown"
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%s-%s -> %s", p.Name, p.FormatVer(), to)
 | 
			
		||||
	var fixedIn string
 | 
			
		||||
	if stat.FixedIn != "" {
 | 
			
		||||
		fixedIn = fmt.Sprintf(" (FixedIn: %s)", stat.FixedIn)
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%s-%s -> %s%s",
 | 
			
		||||
		p.Name, p.FormatVer(), to, fixedIn)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FormatChangelog formats the changelog
 | 
			
		||||
@@ -141,7 +159,7 @@ func (p Package) FormatChangelog() string {
 | 
			
		||||
	case FailedToGetChangelog:
 | 
			
		||||
		clog = "No changelogs"
 | 
			
		||||
	case FailedToFindVersionInChangelog:
 | 
			
		||||
		clog = "Failed to parse changelogs. For detials, check yourself"
 | 
			
		||||
		clog = "Failed to parse changelogs. For details, check yourself"
 | 
			
		||||
	}
 | 
			
		||||
	buf = append(buf, packVer, delim.String(), clog)
 | 
			
		||||
	return strings.Join(buf, "\n")
 | 
			
		||||
@@ -150,8 +168,59 @@ func (p Package) FormatChangelog() string {
 | 
			
		||||
// Changelog has contents of changelog and how to get it.
 | 
			
		||||
// Method: models.detectionMethodStr
 | 
			
		||||
type Changelog struct {
 | 
			
		||||
	Contents string
 | 
			
		||||
	Method   DetectionMethod
 | 
			
		||||
	Contents string          `json:"contents"`
 | 
			
		||||
	Method   DetectionMethod `json:"method"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AffectedProcess keep a processes information affected by software update
 | 
			
		||||
type AffectedProcess struct {
 | 
			
		||||
	PID             string     `json:"pid,omitempty"`
 | 
			
		||||
	Name            string     `json:"name,omitempty"`
 | 
			
		||||
	ListenPorts     []string   `json:"listenPorts,omitempty"`
 | 
			
		||||
	ListenPortStats []PortStat `json:"listenPortStats,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PortStat has the result of parsing the port information to the address and port.
 | 
			
		||||
type PortStat struct {
 | 
			
		||||
	BindAddress     string   `json:"bindAddress"`
 | 
			
		||||
	Port            string   `json:"port"`
 | 
			
		||||
	PortReachableTo []string `json:"portReachableTo"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewPortStat create a PortStat from ipPort str
 | 
			
		||||
func NewPortStat(ipPort string) (*PortStat, error) {
 | 
			
		||||
	if ipPort == "" {
 | 
			
		||||
		return &PortStat{}, nil
 | 
			
		||||
	}
 | 
			
		||||
	sep := strings.LastIndex(ipPort, ":")
 | 
			
		||||
	if sep == -1 {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to parse IP:Port: %s", ipPort)
 | 
			
		||||
	}
 | 
			
		||||
	return &PortStat{
 | 
			
		||||
		BindAddress: ipPort[:sep],
 | 
			
		||||
		Port:        ipPort[sep+1:],
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasReachablePort checks if Package.AffectedProcs has PortReachableTo
 | 
			
		||||
func (p Package) HasReachablePort() bool {
 | 
			
		||||
	for _, ap := range p.AffectedProcs {
 | 
			
		||||
		for _, lp := range ap.ListenPortStats {
 | 
			
		||||
			if len(lp.PortReachableTo) > 0 {
 | 
			
		||||
				return true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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.
 | 
			
		||||
@@ -160,9 +229,10 @@ type Changelog struct {
 | 
			
		||||
// 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
 | 
			
		||||
	Version     string
 | 
			
		||||
	BinaryNames []string
 | 
			
		||||
	Name        string   `json:"name"`
 | 
			
		||||
	Version     string   `json:"version"`
 | 
			
		||||
	Arch        string   `json:"arch"`
 | 
			
		||||
	BinaryNames []string `json:"binaryNames"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddBinaryName add the name if not exists
 | 
			
		||||
@@ -181,3 +251,40 @@ func (s *SrcPackage) AddBinaryName(name string) {
 | 
			
		||||
// 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// raspiPackNamePattern is a regular expression pattern to detect the Raspberry Pi specific package from the package name.
 | 
			
		||||
// e.g. libraspberrypi-dev, rpi-eeprom, python3-rpi.gpio, pi-bluetooth
 | 
			
		||||
var raspiPackNamePattern = regexp.MustCompile(`(.*raspberry.*|^rpi.*|.*-rpi.*|^pi-.*)`)
 | 
			
		||||
 | 
			
		||||
// raspiPackNamePattern is a regular expression pattern to detect the Raspberry Pi specific package from the version.
 | 
			
		||||
// e.g. ffmpeg 7:4.1.4-1+rpt7~deb10u1, vlc 3.0.10-0+deb10u1+rpt2
 | 
			
		||||
var raspiPackVersionPattern = regexp.MustCompile(`.+\+rp(t|i)\d+`)
 | 
			
		||||
 | 
			
		||||
// raspiPackNameList is a package name array of Raspberry Pi specific packages that are difficult to detect with regular expressions.
 | 
			
		||||
var raspiPackNameList = []string{"piclone", "pipanel", "pishutdown", "piwiz", "pixflat-icons"}
 | 
			
		||||
 | 
			
		||||
// IsRaspbianPackage judges whether it is a package related to Raspberry Pi from the package name and version
 | 
			
		||||
func IsRaspbianPackage(name, version string) bool {
 | 
			
		||||
	if raspiPackNamePattern.MatchString(name) || raspiPackVersionPattern.MatchString(version) {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	for _, n := range raspiPackNameList {
 | 
			
		||||
		if n == name {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,3 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
@@ -133,3 +117,314 @@ func TestAddBinaryName(t *testing.T) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestPackage_FormatVersionFromTo(t *testing.T) {
 | 
			
		||||
	type fields struct {
 | 
			
		||||
		Name             string
 | 
			
		||||
		Version          string
 | 
			
		||||
		Release          string
 | 
			
		||||
		NewVersion       string
 | 
			
		||||
		NewRelease       string
 | 
			
		||||
		Arch             string
 | 
			
		||||
		Repository       string
 | 
			
		||||
		Changelog        Changelog
 | 
			
		||||
		AffectedProcs    []AffectedProcess
 | 
			
		||||
		NeedRestartProcs []NeedRestartProcess
 | 
			
		||||
	}
 | 
			
		||||
	type args struct {
 | 
			
		||||
		stat PackageFixStatus
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name   string
 | 
			
		||||
		fields fields
 | 
			
		||||
		args   args
 | 
			
		||||
		want   string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "fixed",
 | 
			
		||||
			fields: fields{
 | 
			
		||||
				Name:       "packA",
 | 
			
		||||
				Version:    "1.0.0",
 | 
			
		||||
				Release:    "a",
 | 
			
		||||
				NewVersion: "1.0.1",
 | 
			
		||||
				NewRelease: "b",
 | 
			
		||||
			},
 | 
			
		||||
			args: args{
 | 
			
		||||
				stat: PackageFixStatus{
 | 
			
		||||
					NotFixedYet: false,
 | 
			
		||||
					FixedIn:     "1.0.1-b",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			want: "packA-1.0.0-a -> 1.0.1-b (FixedIn: 1.0.1-b)",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "nfy",
 | 
			
		||||
			fields: fields{
 | 
			
		||||
				Name:    "packA",
 | 
			
		||||
				Version: "1.0.0",
 | 
			
		||||
				Release: "a",
 | 
			
		||||
			},
 | 
			
		||||
			args: args{
 | 
			
		||||
				stat: PackageFixStatus{
 | 
			
		||||
					NotFixedYet: true,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			want: "packA-1.0.0-a -> Not Fixed Yet",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "nfy",
 | 
			
		||||
			fields: fields{
 | 
			
		||||
				Name:    "packA",
 | 
			
		||||
				Version: "1.0.0",
 | 
			
		||||
				Release: "a",
 | 
			
		||||
			},
 | 
			
		||||
			args: args{
 | 
			
		||||
				stat: PackageFixStatus{
 | 
			
		||||
					NotFixedYet: false,
 | 
			
		||||
					FixedIn:     "1.0.1-b",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			want: "packA-1.0.0-a -> Unknown (FixedIn: 1.0.1-b)",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "nfy2",
 | 
			
		||||
			fields: fields{
 | 
			
		||||
				Name:    "packA",
 | 
			
		||||
				Version: "1.0.0",
 | 
			
		||||
				Release: "a",
 | 
			
		||||
			},
 | 
			
		||||
			args: args{
 | 
			
		||||
				stat: PackageFixStatus{
 | 
			
		||||
					NotFixedYet: true,
 | 
			
		||||
					FixedIn:     "1.0.1-b",
 | 
			
		||||
					FixState:    "open",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			want: "packA-1.0.0-a -> open (FixedIn: 1.0.1-b)",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "nfy3",
 | 
			
		||||
			fields: fields{
 | 
			
		||||
				Name:    "packA",
 | 
			
		||||
				Version: "1.0.0",
 | 
			
		||||
				Release: "a",
 | 
			
		||||
			},
 | 
			
		||||
			args: args{
 | 
			
		||||
				stat: PackageFixStatus{
 | 
			
		||||
					NotFixedYet: true,
 | 
			
		||||
					FixedIn:     "1.0.1-b",
 | 
			
		||||
					FixState:    "open",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			want: "packA-1.0.0-a -> open (FixedIn: 1.0.1-b)",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			p := Package{
 | 
			
		||||
				Name:             tt.fields.Name,
 | 
			
		||||
				Version:          tt.fields.Version,
 | 
			
		||||
				Release:          tt.fields.Release,
 | 
			
		||||
				NewVersion:       tt.fields.NewVersion,
 | 
			
		||||
				NewRelease:       tt.fields.NewRelease,
 | 
			
		||||
				Arch:             tt.fields.Arch,
 | 
			
		||||
				Repository:       tt.fields.Repository,
 | 
			
		||||
				Changelog:        tt.fields.Changelog,
 | 
			
		||||
				AffectedProcs:    tt.fields.AffectedProcs,
 | 
			
		||||
				NeedRestartProcs: tt.fields.NeedRestartProcs,
 | 
			
		||||
			}
 | 
			
		||||
			if got := p.FormatVersionFromTo(tt.args.stat); got != tt.want {
 | 
			
		||||
				t.Errorf("Package.FormatVersionFromTo() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_IsRaspbianPackage(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		name string
 | 
			
		||||
		ver  string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name   string
 | 
			
		||||
		in     []args
 | 
			
		||||
		expect []bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "nameRegExp",
 | 
			
		||||
			in: []args{
 | 
			
		||||
				{
 | 
			
		||||
					name: "libraspberrypi-dev",
 | 
			
		||||
					ver:  "1.20200811-1",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					name: "rpi-eeprom",
 | 
			
		||||
					ver:  "7.10-1",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					name: "python3-rpi.gpio",
 | 
			
		||||
					ver:  "0.7.0-0.1~bpo10+1",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					name: "arping",
 | 
			
		||||
					ver:  "2.19-6",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					name: "pi-bluetooth",
 | 
			
		||||
					ver:  "0.1.14",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: []bool{true, true, true, false, true, false},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "verRegExp",
 | 
			
		||||
			in: []args{
 | 
			
		||||
				{
 | 
			
		||||
					name: "ffmpeg",
 | 
			
		||||
					ver:  "7:4.1.6-1~deb10u1+rpt1",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					name: "gcc",
 | 
			
		||||
					ver:  "4:8.3.0-1+rpi2",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: []bool{true, true},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "nameList",
 | 
			
		||||
			in: []args{
 | 
			
		||||
				{
 | 
			
		||||
					name: "piclone",
 | 
			
		||||
					ver:  "0.16",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: []bool{true},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "debianPackage",
 | 
			
		||||
			in: []args{
 | 
			
		||||
				{
 | 
			
		||||
					name: "apt",
 | 
			
		||||
					ver:  "1.8.2.1",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: []bool{false},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			for i, p := range tt.in {
 | 
			
		||||
				ret := IsRaspbianPackage(p.name, p.ver)
 | 
			
		||||
				if !reflect.DeepEqual(ret, tt.expect[i]) {
 | 
			
		||||
					t.Errorf("[%s->%s] expected: %t, actual: %t, in: %#v", tt.name, tt.in[i].name, tt.expect[i], ret, tt.in[i])
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_parseListenPorts(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name   string
 | 
			
		||||
		args   string
 | 
			
		||||
		expect PortStat
 | 
			
		||||
	}{{
 | 
			
		||||
		name: "empty",
 | 
			
		||||
		args: "",
 | 
			
		||||
		expect: PortStat{
 | 
			
		||||
			BindAddress: "",
 | 
			
		||||
			Port:        "",
 | 
			
		||||
		},
 | 
			
		||||
	}, {
 | 
			
		||||
		name: "normal",
 | 
			
		||||
		args: "127.0.0.1:22",
 | 
			
		||||
		expect: PortStat{
 | 
			
		||||
			BindAddress: "127.0.0.1",
 | 
			
		||||
			Port:        "22",
 | 
			
		||||
		},
 | 
			
		||||
	}, {
 | 
			
		||||
		name: "asterisk",
 | 
			
		||||
		args: "*:22",
 | 
			
		||||
		expect: PortStat{
 | 
			
		||||
			BindAddress: "*",
 | 
			
		||||
			Port:        "22",
 | 
			
		||||
		},
 | 
			
		||||
	}, {
 | 
			
		||||
		name: "ipv6_loopback",
 | 
			
		||||
		args: "[::1]:22",
 | 
			
		||||
		expect: PortStat{
 | 
			
		||||
			BindAddress: "[::1]",
 | 
			
		||||
			Port:        "22",
 | 
			
		||||
		},
 | 
			
		||||
	}}
 | 
			
		||||
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			listenPort, err := NewPortStat(tt.args)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Errorf("unexpected error occurred: %s", err)
 | 
			
		||||
			} else if !reflect.DeepEqual(*listenPort, tt.expect) {
 | 
			
		||||
				t.Errorf("base.parseListenPorts() = %v, want %v", *listenPort, tt.expect)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,28 +1,16 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/cwe"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ScanResults is a slide of ScanResult
 | 
			
		||||
@@ -30,38 +18,110 @@ type ScanResults []ScanResult
 | 
			
		||||
 | 
			
		||||
// ScanResult has the result of scanned CVE information.
 | 
			
		||||
type ScanResult struct {
 | 
			
		||||
	ScannedAt   time.Time
 | 
			
		||||
	ReportedAt  time.Time
 | 
			
		||||
	JSONVersion int
 | 
			
		||||
	Lang        string
 | 
			
		||||
	ServerUUID  string
 | 
			
		||||
	ServerName  string // TOML Section key
 | 
			
		||||
	Family      string
 | 
			
		||||
	Release     string
 | 
			
		||||
	Container   Container
 | 
			
		||||
	Platform    Platform
 | 
			
		||||
	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"`
 | 
			
		||||
	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)
 | 
			
		||||
	IPSIdentifiers   map[config.IPS]string `json:"ipsIdentifiers,omitempty"`
 | 
			
		||||
	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"`
 | 
			
		||||
 | 
			
		||||
	// Scanned Vulns by SSH scan + CPE + OVAL
 | 
			
		||||
	ScannedCves VulnInfos
 | 
			
		||||
	ScannedCves       VulnInfos              `json:"scannedCves"`
 | 
			
		||||
	RunningKernel     Kernel                 `json:"runningKernel"`
 | 
			
		||||
	Packages          Packages               `json:"packages"`
 | 
			
		||||
	SrcPackages       SrcPackages            `json:",omitempty"`
 | 
			
		||||
	WordPressPackages *WordPressPackages     `json:",omitempty"`
 | 
			
		||||
	LibraryScanners   LibraryScanners        `json:"libraries,omitempty"`
 | 
			
		||||
	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"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	RunningKernel Kernel
 | 
			
		||||
	Packages      Packages
 | 
			
		||||
	SrcPackages   SrcPackages
 | 
			
		||||
// CweDict is a dictionary for CWE
 | 
			
		||||
type CweDict map[string]CweDictEntry
 | 
			
		||||
 | 
			
		||||
	Errors   []string
 | 
			
		||||
	Optional [][]interface{}
 | 
			
		||||
 | 
			
		||||
	Config struct {
 | 
			
		||||
		Scan   config.Config
 | 
			
		||||
		Report config.Config
 | 
			
		||||
// Get the name, url, top10URL for the specified cweID, lang
 | 
			
		||||
func (c CweDict) Get(cweID, lang string) (name, url, top10Rank, top10URL, cweTop25Rank, cweTop25URL, sansTop25Rank, sansTop25URL 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 := c[cweNum]; ok && dict.CweTopTwentyfive2019 != "" {
 | 
			
		||||
			cweTop25Rank = dict.CweTopTwentyfive2019
 | 
			
		||||
			cweTop25URL = cwe.CweTopTwentyfive2019URL
 | 
			
		||||
		}
 | 
			
		||||
		if dict, ok := c[cweNum]; ok && dict.SansTopTwentyfive != "" {
 | 
			
		||||
			sansTop25Rank = dict.SansTopTwentyfive
 | 
			
		||||
			sansTop25URL = cwe.SansTopTwentyfiveURL
 | 
			
		||||
		}
 | 
			
		||||
		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]
 | 
			
		||||
		}
 | 
			
		||||
		if dict, ok := c[cweNum]; ok && dict.CweTopTwentyfive2019 != "" {
 | 
			
		||||
			cweTop25Rank = dict.CweTopTwentyfive2019
 | 
			
		||||
			cweTop25URL = cwe.CweTopTwentyfive2019URL
 | 
			
		||||
		}
 | 
			
		||||
		if dict, ok := c[cweNum]; ok && dict.SansTopTwentyfive != "" {
 | 
			
		||||
			sansTop25Rank = dict.SansTopTwentyfive
 | 
			
		||||
			sansTop25URL = cwe.SansTopTwentyfiveURL
 | 
			
		||||
		}
 | 
			
		||||
		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"`
 | 
			
		||||
	CweTopTwentyfive2019 string   `json:"cweTopTwentyfive2019"`
 | 
			
		||||
	SansTopTwentyfive    string   `json:"sansTopTwentyfive"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Kernel has the Release, version and whether need restart
 | 
			
		||||
type Kernel struct {
 | 
			
		||||
	Release        string
 | 
			
		||||
	Version        string
 | 
			
		||||
	RebootRequired bool
 | 
			
		||||
	Release        string `json:"release"`
 | 
			
		||||
	Version        string `json:"version"`
 | 
			
		||||
	RebootRequired bool   `json:"rebootRequired"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FilterByCvssOver is filter function.
 | 
			
		||||
@@ -83,9 +143,27 @@ func (r ScanResult) FilterByCvssOver(over float64) ScanResult {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FilterIgnoreCves is filter function.
 | 
			
		||||
func (r ScanResult) FilterIgnoreCves(cveIDs []string) ScanResult {
 | 
			
		||||
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 cveIDs {
 | 
			
		||||
		for _, c := range ignoreCves {
 | 
			
		||||
			if v.CveID == c {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
@@ -102,6 +180,10 @@ func (r ScanResult) FilterUnfixed() ScanResult {
 | 
			
		||||
		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
 | 
			
		||||
@@ -112,7 +194,86 @@ func (r ScanResult) FilterUnfixed() ScanResult {
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReportFileName returns the filename on localhost without extention
 | 
			
		||||
// FilterIgnorePkgs is filter function.
 | 
			
		||||
func (r ScanResult) FilterIgnorePkgs() ScanResult {
 | 
			
		||||
	var 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("Failed 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 extension
 | 
			
		||||
func (r ScanResult) ReportFileName() (name string) {
 | 
			
		||||
	if len(r.Container.ContainerID) == 0 {
 | 
			
		||||
		return fmt.Sprintf("%s", r.ServerName)
 | 
			
		||||
@@ -120,7 +281,7 @@ func (r ScanResult) ReportFileName() (name string) {
 | 
			
		||||
	return fmt.Sprintf("%s@%s", r.Container.Name, r.ServerName)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReportKeyName returns the name of key on S3, Azure-Blob without extention
 | 
			
		||||
// ReportKeyName returns the name of key on S3, Azure-Blob without extension
 | 
			
		||||
func (r ScanResult) ReportKeyName() (name string) {
 | 
			
		||||
	timestr := r.ScannedAt.Format(time.RFC3339)
 | 
			
		||||
	if len(r.Container.ContainerID) == 0 {
 | 
			
		||||
@@ -144,11 +305,14 @@ func (r ScanResult) ServerInfo() string {
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ServerInfoTui returns server infromation for TUI sidebar
 | 
			
		||||
// 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
 | 
			
		||||
		}
 | 
			
		||||
@@ -176,31 +340,188 @@ func (r ScanResult) FormatServerName() (name string) {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FormatTextReportHeadedr returns header of text report
 | 
			
		||||
func (r ScanResult) FormatTextReportHeadedr() string {
 | 
			
		||||
	serverInfo := r.ServerInfo()
 | 
			
		||||
// FormatTextReportHeader returns header of text report
 | 
			
		||||
func (r ScanResult) FormatTextReportHeader() string {
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	for i := 0; i < len(serverInfo); i++ {
 | 
			
		||||
	for i := 0; i < len(r.ServerInfo()); i++ {
 | 
			
		||||
		buf.WriteString("=")
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%s\n%s\n%s\t%s\n",
 | 
			
		||||
 | 
			
		||||
	return fmt.Sprintf("%s\n%s\n%s, %s, %s, %s, %s, %s\n",
 | 
			
		||||
		r.ServerInfo(),
 | 
			
		||||
		buf.String(),
 | 
			
		||||
		r.ScannedCves.FormatCveSummary(),
 | 
			
		||||
		r.Packages.FormatUpdatablePacksSummary(),
 | 
			
		||||
		r.ScannedCves.FormatFixedStatus(r.Packages),
 | 
			
		||||
		r.FormatUpdatablePacksSummary(),
 | 
			
		||||
		r.FormatExploitCveSummary(),
 | 
			
		||||
		r.FormatMetasploitCveSummary(),
 | 
			
		||||
		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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FormatMetasploitCveSummary returns a summary of exploit cve
 | 
			
		||||
func (r ScanResult) FormatMetasploitCveSummary() string {
 | 
			
		||||
	nMetasploitCve := 0
 | 
			
		||||
	for _, vuln := range r.ScannedCves {
 | 
			
		||||
		if 0 < len(vuln.Metasploits) {
 | 
			
		||||
			nMetasploitCve++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%d modules", nMetasploitCve)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FormatAlertSummary returns a summary of CERT 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 {
 | 
			
		||||
	if r.Family == config.FreeBSD {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
	Name        string
 | 
			
		||||
	Image       string
 | 
			
		||||
	Type        string
 | 
			
		||||
	ContainerID string `json:"containerID"`
 | 
			
		||||
	Name        string `json:"name"`
 | 
			
		||||
	Image       string `json:"image"`
 | 
			
		||||
	Type        string `json:"type"`
 | 
			
		||||
	UUID        string `json:"uuid"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Platform has platform information
 | 
			
		||||
type Platform struct {
 | 
			
		||||
	Name       string // aws or azure or gcp or other...
 | 
			
		||||
	InstanceID string
 | 
			
		||||
	Name       string `json:"name"` // aws or azure or gcp or other...
 | 
			
		||||
	InstanceID string `json:"instanceID"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RemoveRaspbianPackFromResult is for Raspberry Pi and removes the Raspberry Pi dedicated package from ScanResult.
 | 
			
		||||
func (r ScanResult) RemoveRaspbianPackFromResult() ScanResult {
 | 
			
		||||
	if r.Family != config.Raspbian {
 | 
			
		||||
		return r
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result := r
 | 
			
		||||
	packs := make(Packages)
 | 
			
		||||
	for _, pack := range r.Packages {
 | 
			
		||||
		if !IsRaspbianPackage(pack.Name, pack.Version) {
 | 
			
		||||
			packs[pack.Name] = pack
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	srcPacks := make(SrcPackages)
 | 
			
		||||
	for _, pack := range r.SrcPackages {
 | 
			
		||||
		if !IsRaspbianPackage(pack.Name, pack.Version) {
 | 
			
		||||
			srcPacks[pack.Name] = pack
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result.Packages = packs
 | 
			
		||||
	result.SrcPackages = srcPacks
 | 
			
		||||
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClearFields clears a given fields of ScanResult
 | 
			
		||||
func (r ScanResult) ClearFields(targetTagNames []string) ScanResult {
 | 
			
		||||
	if len(targetTagNames) == 0 {
 | 
			
		||||
		return r
 | 
			
		||||
	}
 | 
			
		||||
	target := map[string]bool{}
 | 
			
		||||
	for _, n := range targetTagNames {
 | 
			
		||||
		target[strings.ToLower(n)] = true
 | 
			
		||||
	}
 | 
			
		||||
	t := reflect.ValueOf(r).Type()
 | 
			
		||||
	for i := 0; i < t.NumField(); i++ {
 | 
			
		||||
		f := t.Field(i)
 | 
			
		||||
		jsonValue := strings.Split(f.Tag.Get("json"), ",")[0]
 | 
			
		||||
		if ok := target[strings.ToLower(jsonValue)]; ok {
 | 
			
		||||
			vv := reflect.New(f.Type).Elem().Interface()
 | 
			
		||||
			reflect.ValueOf(&r).Elem().FieldByName(f.Name).Set(reflect.ValueOf(vv))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,3 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Architect, Inc. Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
@@ -43,7 +27,7 @@ func TestFilterByCvssOver(t *testing.T) {
 | 
			
		||||
							CveID: "CVE-2017-0001",
 | 
			
		||||
							CveContents: NewCveContents(
 | 
			
		||||
								CveContent{
 | 
			
		||||
									Type:         NVD,
 | 
			
		||||
									Type:         NvdXML,
 | 
			
		||||
									CveID:        "CVE-2017-0001",
 | 
			
		||||
									Cvss2Score:   7.1,
 | 
			
		||||
									LastModified: time.Time{},
 | 
			
		||||
@@ -54,7 +38,7 @@ func TestFilterByCvssOver(t *testing.T) {
 | 
			
		||||
							CveID: "CVE-2017-0002",
 | 
			
		||||
							CveContents: NewCveContents(
 | 
			
		||||
								CveContent{
 | 
			
		||||
									Type:         NVD,
 | 
			
		||||
									Type:         NvdXML,
 | 
			
		||||
									CveID:        "CVE-2017-0002",
 | 
			
		||||
									Cvss2Score:   6.9,
 | 
			
		||||
									LastModified: time.Time{},
 | 
			
		||||
@@ -65,13 +49,13 @@ func TestFilterByCvssOver(t *testing.T) {
 | 
			
		||||
							CveID: "CVE-2017-0003",
 | 
			
		||||
							CveContents: NewCveContents(
 | 
			
		||||
								CveContent{
 | 
			
		||||
									Type:         NVD,
 | 
			
		||||
									Type:         NvdXML,
 | 
			
		||||
									CveID:        "CVE-2017-0003",
 | 
			
		||||
									Cvss2Score:   6.9,
 | 
			
		||||
									LastModified: time.Time{},
 | 
			
		||||
								},
 | 
			
		||||
								CveContent{
 | 
			
		||||
									Type:         JVN,
 | 
			
		||||
									Type:         Jvn,
 | 
			
		||||
									CveID:        "CVE-2017-0003",
 | 
			
		||||
									Cvss2Score:   7.2,
 | 
			
		||||
									LastModified: time.Time{},
 | 
			
		||||
@@ -87,7 +71,7 @@ func TestFilterByCvssOver(t *testing.T) {
 | 
			
		||||
						CveID: "CVE-2017-0001",
 | 
			
		||||
						CveContents: NewCveContents(
 | 
			
		||||
							CveContent{
 | 
			
		||||
								Type:         NVD,
 | 
			
		||||
								Type:         NvdXML,
 | 
			
		||||
								CveID:        "CVE-2017-0001",
 | 
			
		||||
								Cvss2Score:   7.1,
 | 
			
		||||
								LastModified: time.Time{},
 | 
			
		||||
@@ -98,13 +82,13 @@ func TestFilterByCvssOver(t *testing.T) {
 | 
			
		||||
						CveID: "CVE-2017-0003",
 | 
			
		||||
						CveContents: NewCveContents(
 | 
			
		||||
							CveContent{
 | 
			
		||||
								Type:         NVD,
 | 
			
		||||
								Type:         NvdXML,
 | 
			
		||||
								CveID:        "CVE-2017-0003",
 | 
			
		||||
								Cvss2Score:   6.9,
 | 
			
		||||
								LastModified: time.Time{},
 | 
			
		||||
							},
 | 
			
		||||
							CveContent{
 | 
			
		||||
								Type:         JVN,
 | 
			
		||||
								Type:         Jvn,
 | 
			
		||||
								CveID:        "CVE-2017-0003",
 | 
			
		||||
								Cvss2Score:   7.2,
 | 
			
		||||
								LastModified: time.Time{},
 | 
			
		||||
@@ -124,10 +108,10 @@ func TestFilterByCvssOver(t *testing.T) {
 | 
			
		||||
							CveID: "CVE-2017-0001",
 | 
			
		||||
							CveContents: NewCveContents(
 | 
			
		||||
								CveContent{
 | 
			
		||||
									Type:         Ubuntu,
 | 
			
		||||
									CveID:        "CVE-2017-0001",
 | 
			
		||||
									Severity:     "HIGH",
 | 
			
		||||
									LastModified: time.Time{},
 | 
			
		||||
									Type:          Ubuntu,
 | 
			
		||||
									CveID:         "CVE-2017-0001",
 | 
			
		||||
									Cvss2Severity: "HIGH",
 | 
			
		||||
									LastModified:  time.Time{},
 | 
			
		||||
								},
 | 
			
		||||
							),
 | 
			
		||||
						},
 | 
			
		||||
@@ -135,10 +119,10 @@ func TestFilterByCvssOver(t *testing.T) {
 | 
			
		||||
							CveID: "CVE-2017-0002",
 | 
			
		||||
							CveContents: NewCveContents(
 | 
			
		||||
								CveContent{
 | 
			
		||||
									Type:         RedHat,
 | 
			
		||||
									CveID:        "CVE-2017-0002",
 | 
			
		||||
									Severity:     "CRITICAL",
 | 
			
		||||
									LastModified: time.Time{},
 | 
			
		||||
									Type:          RedHat,
 | 
			
		||||
									CveID:         "CVE-2017-0002",
 | 
			
		||||
									Cvss2Severity: "CRITICAL",
 | 
			
		||||
									LastModified:  time.Time{},
 | 
			
		||||
								},
 | 
			
		||||
							),
 | 
			
		||||
						},
 | 
			
		||||
@@ -146,10 +130,10 @@ func TestFilterByCvssOver(t *testing.T) {
 | 
			
		||||
							CveID: "CVE-2017-0003",
 | 
			
		||||
							CveContents: NewCveContents(
 | 
			
		||||
								CveContent{
 | 
			
		||||
									Type:         Oracle,
 | 
			
		||||
									CveID:        "CVE-2017-0003",
 | 
			
		||||
									Severity:     "IMPORTANT",
 | 
			
		||||
									LastModified: time.Time{},
 | 
			
		||||
									Type:          Oracle,
 | 
			
		||||
									CveID:         "CVE-2017-0003",
 | 
			
		||||
									Cvss2Severity: "IMPORTANT",
 | 
			
		||||
									LastModified:  time.Time{},
 | 
			
		||||
								},
 | 
			
		||||
							),
 | 
			
		||||
						},
 | 
			
		||||
@@ -162,10 +146,10 @@ func TestFilterByCvssOver(t *testing.T) {
 | 
			
		||||
						CveID: "CVE-2017-0001",
 | 
			
		||||
						CveContents: NewCveContents(
 | 
			
		||||
							CveContent{
 | 
			
		||||
								Type:         Ubuntu,
 | 
			
		||||
								CveID:        "CVE-2017-0001",
 | 
			
		||||
								Severity:     "HIGH",
 | 
			
		||||
								LastModified: time.Time{},
 | 
			
		||||
								Type:          Ubuntu,
 | 
			
		||||
								CveID:         "CVE-2017-0001",
 | 
			
		||||
								Cvss2Severity: "HIGH",
 | 
			
		||||
								LastModified:  time.Time{},
 | 
			
		||||
							},
 | 
			
		||||
						),
 | 
			
		||||
					},
 | 
			
		||||
@@ -173,10 +157,10 @@ func TestFilterByCvssOver(t *testing.T) {
 | 
			
		||||
						CveID: "CVE-2017-0002",
 | 
			
		||||
						CveContents: NewCveContents(
 | 
			
		||||
							CveContent{
 | 
			
		||||
								Type:         RedHat,
 | 
			
		||||
								CveID:        "CVE-2017-0002",
 | 
			
		||||
								Severity:     "CRITICAL",
 | 
			
		||||
								LastModified: time.Time{},
 | 
			
		||||
								Type:          RedHat,
 | 
			
		||||
								CveID:         "CVE-2017-0002",
 | 
			
		||||
								Cvss2Severity: "CRITICAL",
 | 
			
		||||
								LastModified:  time.Time{},
 | 
			
		||||
							},
 | 
			
		||||
						),
 | 
			
		||||
					},
 | 
			
		||||
@@ -184,10 +168,10 @@ func TestFilterByCvssOver(t *testing.T) {
 | 
			
		||||
						CveID: "CVE-2017-0003",
 | 
			
		||||
						CveContents: NewCveContents(
 | 
			
		||||
							CveContent{
 | 
			
		||||
								Type:         Oracle,
 | 
			
		||||
								CveID:        "CVE-2017-0003",
 | 
			
		||||
								Severity:     "IMPORTANT",
 | 
			
		||||
								LastModified: time.Time{},
 | 
			
		||||
								Type:          Oracle,
 | 
			
		||||
								CveID:         "CVE-2017-0003",
 | 
			
		||||
								Cvss2Severity: "IMPORTANT",
 | 
			
		||||
								LastModified:  time.Time{},
 | 
			
		||||
							},
 | 
			
		||||
						),
 | 
			
		||||
					},
 | 
			
		||||
@@ -206,7 +190,6 @@ func TestFilterByCvssOver(t *testing.T) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestFilterIgnoreCveIDs(t *testing.T) {
 | 
			
		||||
	type in struct {
 | 
			
		||||
		cves []string
 | 
			
		||||
@@ -220,6 +203,7 @@ func TestFilterIgnoreCveIDs(t *testing.T) {
 | 
			
		||||
			in: in{
 | 
			
		||||
				cves: []string{"CVE-2017-0002"},
 | 
			
		||||
				rs: ScanResult{
 | 
			
		||||
					ServerName: "name",
 | 
			
		||||
					ScannedCves: VulnInfos{
 | 
			
		||||
						"CVE-2017-0001": {
 | 
			
		||||
							CveID: "CVE-2017-0001",
 | 
			
		||||
@@ -234,6 +218,7 @@ func TestFilterIgnoreCveIDs(t *testing.T) {
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: ScanResult{
 | 
			
		||||
				ServerName: "name",
 | 
			
		||||
				ScannedCves: VulnInfos{
 | 
			
		||||
					"CVE-2017-0001": {
 | 
			
		||||
						CveID: "CVE-2017-0001",
 | 
			
		||||
@@ -246,7 +231,10 @@ func TestFilterIgnoreCveIDs(t *testing.T) {
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		actual := tt.in.rs.FilterIgnoreCves(tt.in.cves)
 | 
			
		||||
		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])
 | 
			
		||||
@@ -254,6 +242,83 @@ func TestFilterIgnoreCveIDs(t *testing.T) {
 | 
			
		||||
				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)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -267,7 +332,7 @@ func TestFilterUnfixed(t *testing.T) {
 | 
			
		||||
				ScannedCves: VulnInfos{
 | 
			
		||||
					"CVE-2017-0001": {
 | 
			
		||||
						CveID: "CVE-2017-0001",
 | 
			
		||||
						AffectedPackages: PackageStatuses{
 | 
			
		||||
						AffectedPackages: PackageFixStatuses{
 | 
			
		||||
							{
 | 
			
		||||
								Name:        "a",
 | 
			
		||||
								NotFixedYet: true,
 | 
			
		||||
@@ -276,7 +341,7 @@ func TestFilterUnfixed(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
					"CVE-2017-0002": {
 | 
			
		||||
						CveID: "CVE-2017-0002",
 | 
			
		||||
						AffectedPackages: PackageStatuses{
 | 
			
		||||
						AffectedPackages: PackageFixStatuses{
 | 
			
		||||
							{
 | 
			
		||||
								Name:        "b",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
@@ -285,7 +350,7 @@ func TestFilterUnfixed(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
					"CVE-2017-0003": {
 | 
			
		||||
						CveID: "CVE-2017-0003",
 | 
			
		||||
						AffectedPackages: PackageStatuses{
 | 
			
		||||
						AffectedPackages: PackageFixStatuses{
 | 
			
		||||
							{
 | 
			
		||||
								Name:        "c",
 | 
			
		||||
								NotFixedYet: true,
 | 
			
		||||
@@ -302,7 +367,7 @@ func TestFilterUnfixed(t *testing.T) {
 | 
			
		||||
				ScannedCves: VulnInfos{
 | 
			
		||||
					"CVE-2017-0002": {
 | 
			
		||||
						CveID: "CVE-2017-0002",
 | 
			
		||||
						AffectedPackages: PackageStatuses{
 | 
			
		||||
						AffectedPackages: PackageFixStatuses{
 | 
			
		||||
							{
 | 
			
		||||
								Name:        "b",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
@@ -311,7 +376,7 @@ func TestFilterUnfixed(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
					"CVE-2017-0003": {
 | 
			
		||||
						CveID: "CVE-2017-0003",
 | 
			
		||||
						AffectedPackages: PackageStatuses{
 | 
			
		||||
						AffectedPackages: PackageFixStatuses{
 | 
			
		||||
							{
 | 
			
		||||
								Name:        "c",
 | 
			
		||||
								NotFixedYet: true,
 | 
			
		||||
@@ -336,3 +401,322 @@ func TestFilterUnfixed(t *testing.T) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			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)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||