Skip to content

Commit dc815f6

Browse files
committed
feat(server): support server mode
1 parent 6729def commit dc815f6

File tree

9 files changed

+463
-37
lines changed

9 files changed

+463
-37
lines changed

commands/server.go

+308
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
/* Vuls - Vulnerability Scanner
2+
Copyright (C) 2016 Future Architect, Inc. Japan.
3+
4+
This program is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package commands
19+
20+
import (
21+
"bytes"
22+
"context"
23+
"encoding/json"
24+
"flag"
25+
"fmt"
26+
"io"
27+
"net/http"
28+
"os"
29+
"path/filepath"
30+
31+
// "github.com/future-architect/vuls/Server"
32+
33+
c "github.com/future-architect/vuls/config"
34+
"github.com/future-architect/vuls/models"
35+
"github.com/future-architect/vuls/oval"
36+
"github.com/future-architect/vuls/report"
37+
"github.com/future-architect/vuls/scan"
38+
"github.com/future-architect/vuls/util"
39+
"github.com/google/subcommands"
40+
)
41+
42+
// ServerCmd is subcommand for server
43+
type ServerCmd struct {
44+
lang string
45+
debug bool
46+
debugSQL bool
47+
configPath string
48+
logDir string
49+
50+
cvssScoreOver float64
51+
ignoreUnscoredCves bool
52+
ignoreUnfixed bool
53+
54+
httpProxy string
55+
listen string
56+
57+
cveDBType string
58+
cveDBPath string
59+
cveDBURL string
60+
61+
ovalDBType string
62+
ovalDBPath string
63+
ovalDBURL string
64+
}
65+
66+
// Name return subcommand name
67+
func (*ServerCmd) Name() string { return "server" }
68+
69+
// Synopsis return synopsis
70+
func (*ServerCmd) Synopsis() string { return "Server" }
71+
72+
// Usage return usage
73+
func (*ServerCmd) Usage() string {
74+
return `Server:
75+
Server
76+
[-lang=en|ja]
77+
[-config=/path/to/config.toml]
78+
[-log-dir=/path/to/log]
79+
[-cvedb-type=sqlite3|mysql|postgres]
80+
[-cvedb-path=/path/to/cve.sqlite3]
81+
[-cvedb-url=http://127.0.0.1:1323 or DB connection string]
82+
[-ovaldb-type=sqlite3|mysql]
83+
[-ovaldb-path=/path/to/oval.sqlite3]
84+
[-ovaldb-url=http://127.0.0.1:1324 or DB connection string]
85+
[-cvss-over=7]
86+
[-diff]
87+
[-ignore-unscored-cves]
88+
[-ignore-unfixed]
89+
[-to-email]
90+
[-to-slack]
91+
[-to-stride]
92+
[-to-hipchat]
93+
[-to-chatwork]
94+
[-to-localfile]
95+
[-to-s3]
96+
[-to-azure-blob]
97+
[-format-json]
98+
[-format-xml]
99+
[-format-one-email]
100+
[-format-one-line-text]
101+
[-format-short-text]
102+
[-format-full-text]
103+
[-http-proxy=http://192.168.0.1:8080]
104+
[-debug]
105+
[-debug-sql]
106+
[-listen=localhost:5515]
107+
108+
[RFC3339 datetime format under results dir]
109+
`
110+
}
111+
112+
// SetFlags set flag
113+
func (p *ServerCmd) SetFlags(f *flag.FlagSet) {
114+
f.StringVar(&p.lang, "lang", "en", "[en|ja]")
115+
f.BoolVar(&p.debug, "debug", false, "debug mode")
116+
f.BoolVar(&p.debugSQL, "debug-sql", false, "SQL debug mode")
117+
118+
wd, _ := os.Getwd()
119+
120+
defaultConfPath := filepath.Join(wd, "config.toml")
121+
f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
122+
123+
defaultLogDir := util.GetDefaultLogDir()
124+
f.StringVar(&p.logDir, "log-dir", defaultLogDir, "/path/to/log")
125+
126+
f.StringVar(
127+
&p.cveDBType,
128+
"cvedb-type",
129+
"sqlite3",
130+
"DB type for fetching CVE dictionary (sqlite3, mysql or postgres)")
131+
132+
defaultCveDBPath := filepath.Join(wd, "cve.sqlite3")
133+
f.StringVar(
134+
&p.cveDBPath,
135+
"cvedb-path",
136+
defaultCveDBPath,
137+
"/path/to/sqlite3 (For get cve detail from cve.sqlite3)")
138+
139+
f.StringVar(
140+
&p.cveDBURL,
141+
"cvedb-url",
142+
"",
143+
"http://cve-dictionary.com:1323 or mysql connection string")
144+
145+
f.StringVar(
146+
&p.ovalDBType,
147+
"ovaldb-type",
148+
"sqlite3",
149+
"DB type for fetching OVAL dictionary (sqlite3 or mysql)")
150+
151+
defaultOvalDBPath := filepath.Join(wd, "oval.sqlite3")
152+
f.StringVar(
153+
&p.ovalDBPath,
154+
"ovaldb-path",
155+
defaultOvalDBPath,
156+
"/path/to/sqlite3 (For get oval detail from oval.sqlite3)")
157+
158+
f.StringVar(
159+
&p.ovalDBURL,
160+
"ovaldb-url",
161+
"",
162+
"http://goval-dictionary.com:1324 or mysql connection string")
163+
164+
f.Float64Var(
165+
&p.cvssScoreOver,
166+
"cvss-over",
167+
0,
168+
"-cvss-over=6.5 means Servering CVSS Score 6.5 and over (default: 0 (means Server all))")
169+
170+
f.BoolVar(
171+
&p.ignoreUnscoredCves,
172+
"ignore-unscored-cves",
173+
false,
174+
"Don't Server the unscored CVEs")
175+
176+
f.BoolVar(
177+
&p.ignoreUnfixed,
178+
"ignore-unfixed",
179+
false,
180+
"Don't Server the unfixed CVEs")
181+
182+
f.StringVar(
183+
&p.httpProxy,
184+
"http-proxy",
185+
"",
186+
"http://proxy-url:port (default: empty)")
187+
f.StringVar(
188+
&p.listen,
189+
"listen",
190+
"localhost:5515",
191+
"host:port (default: localhost:5515)")
192+
}
193+
194+
// Execute execute
195+
func (p *ServerCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
196+
c.Conf.Debug = p.debug
197+
c.Conf.DebugSQL = p.debugSQL
198+
c.Conf.LogDir = p.logDir
199+
util.Log = util.NewCustomLogger(c.ServerInfo{})
200+
201+
c.Conf.Lang = p.lang
202+
c.Conf.CveDBType = p.cveDBType
203+
c.Conf.CveDBPath = p.cveDBPath
204+
c.Conf.CveDBURL = p.cveDBURL
205+
c.Conf.OvalDBType = p.ovalDBType
206+
c.Conf.OvalDBPath = p.ovalDBPath
207+
c.Conf.OvalDBURL = p.ovalDBURL
208+
c.Conf.CvssScoreOver = p.cvssScoreOver
209+
c.Conf.IgnoreUnscoredCves = p.ignoreUnscoredCves
210+
c.Conf.IgnoreUnfixed = p.ignoreUnfixed
211+
c.Conf.HTTPProxy = p.httpProxy
212+
213+
util.Log.Info("Validating config...")
214+
if !c.Conf.ValidateOnReport() {
215+
return subcommands.ExitUsageError
216+
}
217+
if err := report.CveClient.CheckHealth(); err != nil {
218+
util.Log.Errorf("CVE HTTP server is not running. err: %s", err)
219+
util.Log.Errorf("Run go-cve-dictionary as server mode before Servering or run with -cvedb-path option")
220+
return subcommands.ExitFailure
221+
}
222+
if c.Conf.CveDBURL != "" {
223+
util.Log.Infof("cve-dictionary: %s", c.Conf.CveDBURL)
224+
} else {
225+
if c.Conf.CveDBType == "sqlite3" {
226+
util.Log.Infof("cve-dictionary: %s", c.Conf.CveDBPath)
227+
}
228+
}
229+
230+
if c.Conf.OvalDBURL != "" {
231+
util.Log.Infof("oval-dictionary: %s", c.Conf.OvalDBURL)
232+
err := oval.Base{}.CheckHTTPHealth()
233+
if err != nil {
234+
util.Log.Errorf("OVAL HTTP server is not running. err: %s", err)
235+
util.Log.Errorf("Run goval-dictionary as server mode before Servering or run with -ovaldb-path option")
236+
return subcommands.ExitFailure
237+
}
238+
} else {
239+
if c.Conf.OvalDBType == "sqlite3" {
240+
util.Log.Infof("oval-dictionary: %s", c.Conf.OvalDBPath)
241+
}
242+
}
243+
244+
var dbclient report.DBClient
245+
var err error
246+
if dbclient, err = report.NewDBClient(
247+
c.Conf.CveDBType,
248+
c.Conf.CveDBURL,
249+
c.Conf.CveDBPath,
250+
c.Conf.OvalDBType,
251+
c.Conf.OvalDBURL,
252+
c.Conf.OvalDBPath,
253+
c.Conf.DebugSQL,
254+
); err != nil {
255+
util.Log.Errorf("Failed to New DB Clients: %s", err)
256+
return subcommands.ExitFailure
257+
}
258+
defer dbclient.CloseDB()
259+
260+
http.Handle("/", vulsHandler{dbclient: dbclient})
261+
util.Log.Infof("Listening on %s", p.listen)
262+
if err := http.ListenAndServe(p.listen, nil); err != nil {
263+
util.Log.Errorf("Failed to start server: %s", err)
264+
return subcommands.ExitFailure
265+
}
266+
return subcommands.ExitSuccess
267+
}
268+
269+
type vulsHandler struct {
270+
dbclient report.DBClient
271+
}
272+
273+
func (h vulsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
274+
var err error
275+
result := models.ScanResult{ScannedCves: models.VulnInfos{}}
276+
277+
contentType := r.Header.Get("Content-Type")
278+
if contentType == "application/json" {
279+
if err = json.NewDecoder(r.Body).Decode(&result); err != nil {
280+
http.Error(w, "Invalid JSON", http.StatusBadRequest)
281+
return
282+
}
283+
} else if contentType == "text/plain" {
284+
buf := new(bytes.Buffer)
285+
io.Copy(buf, r.Body)
286+
if result, err = scan.ViaHTTP(r.Header, buf.String()); err != nil {
287+
http.Error(w, err.Error(), http.StatusBadRequest)
288+
return
289+
}
290+
} else {
291+
http.Error(w, fmt.Sprintf("Invalid Content-Type: %s", contentType), http.StatusUnsupportedMediaType)
292+
return
293+
}
294+
295+
if err := report.FillCveInfo(h.dbclient, &result, []string{}); err != nil {
296+
util.Log.Error(err)
297+
return
298+
}
299+
300+
res, err := json.Marshal(result)
301+
if err != nil {
302+
util.Log.Error(err)
303+
return
304+
}
305+
306+
w.Header().Set("Content-Type", "application/json")
307+
w.Write(res)
308+
}

main.go

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ func main() {
3939
subcommands.Register(&commands.HistoryCmd{}, "history")
4040
subcommands.Register(&commands.ReportCmd{}, "report")
4141
subcommands.Register(&commands.ConfigtestCmd{}, "configtest")
42+
subcommands.Register(&commands.ServerCmd{}, "server")
4243

4344
var v = flag.Bool("v", false, "Show version")
4445

scan/alpine.go

+5
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,11 @@ func (o *alpine) scanInstalledPackages() (models.Packages, error) {
141141
return o.parseApkInfo(r.Stdout)
142142
}
143143

144+
func (o *alpine) parseInstalledPackages(stdout string) (models.Packages, models.SrcPackages, error) {
145+
installedPackages, err := o.parseApkInfo(stdout)
146+
return installedPackages, nil, err
147+
}
148+
144149
func (o *alpine) parseApkInfo(stdout string) (models.Packages, error) {
145150
packs := models.Packages{}
146151
scanner := bufio.NewScanner(strings.NewReader(stdout))

0 commit comments

Comments
 (0)