Skip to content

Commit 6ef6ba4

Browse files
MaineK00nCopilotclaude
authored
feat!(detector): detect windows with vuls2 (#2499)
* feat!(detector): detect windows with vuls2 * fix(microsoft): align vuls2 KB detection output with gost behavior - Do not populate AffectedPackages for KB-only detections (keep nil) - WindowsKBFixedIns now includes KB prefix (e.g. "KB5075899") - KB IDs flow through separate kbIDs path instead of packStatuses - Update walkCriteria to return kbIDs separately from pack statuses - Fix build error from AcceptQueriesKB -> KB type rename Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(detector/vuls2): surface MSRC exploitability under Optional["exploit"] vuls-data-update now extracts the advisory-level MSRC exploitability assessment ("Publicly Disclosed:...;Exploited:...;Latest Software Release:...;...") as the CVRF vulnerability's "exploitability" optional value. Surface it as CveContent.Optional["exploit"], mirroring the legacy gost Microsoft path (gost/microsoft.go: Optional["exploit"] = cve.ExploitStatus). The Optional map is built by the new ecosystem-dispatched cveContentOptional in vendor.go; only the Microsoft case adds "exploit", so the generic path and non-Microsoft sources are unaffected. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(detector): drop unused logOpts param from DetectPkgCves logOpts became unused once Windows detection moved off gost to vuls2 (1e24818); vuls2.Detect does not take it. golangci-lint (revive unused-parameter) flagged it. Remove the parameter and update the two callers (detector.go, server/server.go). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(dependabot): drop github.com/vulsio/gost and go-exploitdb from gomod groups gost is removed by this PR; go-exploitdb was removed earlier in #2517. Neither is in go.mod anymore, so the `vuls` group patterns and the `others` group exclude-patterns no longer match anything. Remove the stale entries. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(detector/vuls2): add covered-based case to postConvert microsoft kb detection The existing "microsoft kb detection" case only exercised the unapplied-based evaluation path of the KB criterion (Accepts.KB.Unapplied=true). The covered-based path (Accepts.KB.Covered=true) is the more common real-world detection pattern: the host has applied KBs that produce a non-empty CoveredKBs, and the criterion's fix-KB is not in that set so it is reported as vulnerable. Rename the existing case to "microsoft kb detection: unapplied" for parallel clarity, and add a new "microsoft kb detection: covered" case that mirrors the structure with Accepts.KB.Covered=true. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 259e694 commit 6ef6ba4

24 files changed

Lines changed: 509 additions & 1580 deletions

.github/dependabot.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ updates:
4343
- "github.com/MaineK00n/vuls2"
4444
- "github.com/vulsio/go-cti"
4545
- "github.com/vulsio/go-cve-dictionary"
46-
- "github.com/vulsio/go-exploitdb"
47-
- "github.com/vulsio/gost"
4846
trivy:
4947
patterns:
5048
- "github.com/aquasecurity/trivy"
@@ -58,8 +56,6 @@ updates:
5856
- "github.com/MaineK00n/vuls2"
5957
- "github.com/vulsio/go-cti"
6058
- "github.com/vulsio/go-cve-dictionary"
61-
- "github.com/vulsio/go-exploitdb"
62-
- "github.com/vulsio/gost"
6359
- "github.com/aquasecurity/trivy"
6460
- "github.com/aquasecurity/trivy-db"
6561
- "github.com/aquasecurity/trivy-java-db"

config/config.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ type Config struct {
3939

4040
// report
4141
CveDict GoCveDictConf `json:"cveDict,omitzero"`
42-
Gost GostConf `json:"gost,omitzero"`
4342
Cti CtiConf `json:"cti,omitzero"`
4443
Vuls2 Vuls2Conf `json:"vuls2,omitzero"`
4544

@@ -186,7 +185,6 @@ func (c *Config) ValidateOnReport() bool {
186185

187186
for _, cnf := range []VulnDictInterface{
188187
&Conf.CveDict,
189-
&Conf.Gost,
190188
&Conf.Cti,
191189
} {
192190
if err := cnf.Validate(); err != nil {

config/tomlloader.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ func (c TOMLLoader) Load(pathToToml string) error {
3939

4040
for _, cnf := range []VulnDictInterface{
4141
&Conf.CveDict,
42-
&Conf.Gost,
4342
&Conf.Cti,
4443
} {
4544
cnf.Init()

config/vulnDictConf.go

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -173,33 +173,6 @@ func (cnf *GoCveDictConf) Init() {
173173
cnf.DebugSQL = Conf.DebugSQL
174174
}
175175

176-
// GostConf is gost config
177-
type GostConf struct {
178-
VulnDict
179-
}
180-
181-
const gostDBType = "GOSTDB_TYPE"
182-
const gostDBURL = "GOSTDB_URL"
183-
const gostDBPATH = "GOSTDB_SQLITE3_PATH"
184-
185-
// Init set options with the following priority.
186-
// 1. Environment variable
187-
// 2. config.toml
188-
func (cnf *GostConf) Init() {
189-
cnf.Name = "gost"
190-
if os.Getenv(gostDBType) != "" {
191-
cnf.Type = os.Getenv(gostDBType)
192-
}
193-
if os.Getenv(gostDBURL) != "" {
194-
cnf.URL = os.Getenv(gostDBURL)
195-
}
196-
if os.Getenv(gostDBPATH) != "" {
197-
cnf.SQLite3Path = os.Getenv(gostDBPATH)
198-
}
199-
cnf.setDefault("gost.sqlite3")
200-
cnf.DebugSQL = Conf.DebugSQL
201-
}
202-
203176
// CtiConf is go-cti config
204177
type CtiConf struct {
205178
VulnDict

detector/detector.go

Lines changed: 9 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import (
1717
"github.com/future-architect/vuls/contrib/owasp-dependency-check/parser"
1818
"github.com/future-architect/vuls/cwe"
1919
"github.com/future-architect/vuls/detector/vuls2"
20-
"github.com/future-architect/vuls/gost"
2120
"github.com/future-architect/vuls/logging"
2221
"github.com/future-architect/vuls/models"
2322
"github.com/future-architect/vuls/reporter"
@@ -50,7 +49,7 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
5049
return nil, xerrors.Errorf("Failed to fill with Library dependency: %w", err)
5150
}
5251

53-
if err := DetectPkgCves(&r, config.Conf.Gost, config.Conf.Vuls2, config.Conf.LogOpts, config.Conf.NoProgress); err != nil {
52+
if err := DetectPkgCves(&r, config.Conf.Vuls2, config.Conf.NoProgress); err != nil {
5453
return nil, xerrors.Errorf("Failed to detect Pkg CVE: %w", err)
5554
}
5655

@@ -296,19 +295,16 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
296295
}
297296

298297
// DetectPkgCves detects OS pkg cves
299-
func DetectPkgCves(r *models.ScanResult, gostCnf config.GostConf, vuls2Conf config.Vuls2Conf, logOpts logging.LogOpts, noProgress bool) error {
298+
func DetectPkgCves(r *models.ScanResult, vuls2Conf config.Vuls2Conf, noProgress bool) error {
300299
if isPkgCvesDetactable(r) {
301300
switch r.Family {
302301
case constant.RedHat, constant.CentOS, constant.Fedora, constant.Alma, constant.Rocky, constant.Oracle, constant.Amazon,
303302
constant.OpenSUSE, constant.OpenSUSELeap, constant.SUSEEnterpriseServer, constant.SUSEEnterpriseDesktop,
304-
constant.Debian, constant.Raspbian, constant.Ubuntu, constant.Alpine:
303+
constant.Debian, constant.Raspbian, constant.Ubuntu, constant.Alpine,
304+
constant.Windows:
305305
if err := vuls2.Detect(r, vuls2Conf, noProgress); err != nil {
306306
return xerrors.Errorf("Failed to detect CVE with Vuls2: %w", err)
307307
}
308-
case constant.Windows:
309-
if err := detectPkgsCvesWithGost(gostCnf, r, logOpts); err != nil {
310-
return xerrors.Errorf("Failed to detect CVE with gost: %w", err)
311-
}
312308
default:
313309
return xerrors.Errorf("Unsupported detection methods for %s", r.Family)
314310
}
@@ -344,27 +340,27 @@ func DetectPkgCves(r *models.ScanResult, gostCnf config.GostConf, vuls2Conf conf
344340
return nil
345341
}
346342

347-
// isPkgCvesDetactable checks whether CVEs is detactable with gost and vuls2 from the result
343+
// isPkgCvesDetactable checks whether CVEs is detactable with vuls2 from the result
348344
func isPkgCvesDetactable(r *models.ScanResult) bool {
349345
switch r.Family {
350346
case constant.FreeBSD, constant.MacOSX, constant.MacOSXServer, constant.MacOS, constant.MacOSServer, constant.ServerTypePseudo:
351-
logging.Log.Infof("%s type. Skip gost and vuls2 detection", r.Family)
347+
logging.Log.Infof("%s type. Skip vuls2 detection", r.Family)
352348
return false
353349
case constant.Windows:
354350
return true
355351
default:
356352
if r.ScannedVia == "trivy" {
357-
logging.Log.Infof("r.ScannedVia is trivy. Skip gost and vuls2 detection")
353+
logging.Log.Infof("r.ScannedVia is trivy. Skip vuls2 detection")
358354
return false
359355
}
360356

361357
if r.Release == "" {
362-
logging.Log.Infof("r.Release is empty. Skip gost and vuls2 detection")
358+
logging.Log.Infof("r.Release is empty. Skip vuls2 detection")
363359
return false
364360
}
365361

366362
if len(r.Packages)+len(r.SrcPackages) == 0 {
367-
logging.Log.Infof("Number of packages is 0. Skip gost and vuls2 detection")
363+
logging.Log.Infof("Number of packages is 0. Skip vuls2 detection")
368364
return false
369365
}
370366
return true
@@ -480,27 +476,6 @@ func fillCertAlerts(cvedetail *cvemodels.CveDetail) (dict models.AlertDict) {
480476
return dict
481477
}
482478

483-
func detectPkgsCvesWithGost(cnf config.GostConf, r *models.ScanResult, logOpts logging.LogOpts) error {
484-
client, err := gost.NewGostClient(cnf, r.Family, logOpts)
485-
if err != nil {
486-
return xerrors.Errorf("Failed to new a gost client: %w", err)
487-
}
488-
defer func() {
489-
if err := client.CloseDB(); err != nil {
490-
logging.Log.Errorf("Failed to close the gost DB. err: %+v", err)
491-
}
492-
}()
493-
494-
nCVEs, err := client.DetectCVEs(r, true)
495-
if err != nil {
496-
return xerrors.Errorf("Failed to detect CVEs with gost: %w", err)
497-
}
498-
499-
logging.Log.Infof("%s: %d CVEs are detected with gost", r.FormatServerName(), nCVEs)
500-
501-
return nil
502-
}
503-
504479
// DetectCpeURIsCves detects CVEs of given CPE-URIs
505480
func DetectCpeURIsCves(r *models.ScanResult, cpes []Cpe, cnf config.GoCveDictConf, logOpts logging.LogOpts) error {
506481
client, err := newGoCveDictClient(&cnf, logOpts)

detector/util.go

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515

1616
"github.com/future-architect/vuls/config"
1717
"github.com/future-architect/vuls/constant"
18-
"github.com/future-architect/vuls/gost"
1918
"github.com/future-architect/vuls/logging"
2019
"github.com/future-architect/vuls/models"
2120
"golang.org/x/xerrors"
@@ -134,7 +133,6 @@ func getPlusDiffCves(previous, current models.ScanResult) models.VulnInfos {
134133

135134
// TODO commented out because a bug of diff logic when multiple oval defs found for a certain CVE-ID and same updated_at
136135
// if these OVAL defs have different affected packages, this logic detects as updated.
137-
// This logic will be uncommented after integration with gost https://github.com/vulsio/gost
138136
// } else if isCveFixed(v, previous) {
139137
// updated[v.CveID] = v
140138
// logging.Log.Debugf("fixed: %s", v.CveID)
@@ -266,7 +264,7 @@ func loadOneServerScanResult(jsonFile string) (*models.ScanResult, error) {
266264
}
267265

268266
// ValidateDBs checks if the databases are accessible and can be closed properly
269-
func ValidateDBs(cveConf config.GoCveDictConf, gostConf config.GostConf, ctiConf config.CtiConf, logOpts logging.LogOpts) error {
267+
func ValidateDBs(cveConf config.GoCveDictConf, ctiConf config.CtiConf, logOpts logging.LogOpts) error {
270268
cvec, err := newGoCveDictClient(&cveConf, logOpts)
271269
if err != nil {
272270
return xerrors.Errorf("Failed to new CVE client. err: %w", err)
@@ -275,14 +273,6 @@ func ValidateDBs(cveConf config.GoCveDictConf, gostConf config.GostConf, ctiConf
275273
return xerrors.Errorf("Failed to close CVE DB. err: %w", err)
276274
}
277275

278-
gostc, err := gost.NewGostClient(gostConf, constant.ServerTypePseudo, logOpts)
279-
if err != nil {
280-
return xerrors.Errorf("Failed to new gost client. err: %w", err)
281-
}
282-
if err := gostc.CloseDB(); err != nil {
283-
return xerrors.Errorf("Failed to close gost DB. err: %w", err)
284-
}
285-
286276
ctic, err := newGoCTIDBClient(&ctiConf, logOpts)
287277
if err != nil {
288278
return xerrors.Errorf("Failed to new CTI client. err: %w", err)

detector/vuls2/vendor.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ func preConvertBinaryVersion(family, version string) string {
5151

5252
func toVuls2Family(vuls0Family, vuls0Release string) string {
5353
switch vuls0Family {
54+
case constant.Windows:
55+
return ecosystemTypes.EcosystemTypeMicrosoft
5456
case constant.Raspbian:
5557
return ecosystemTypes.EcosystemTypeDebian
5658
case constant.SUSEEnterpriseServer, constant.SUSEEnterpriseDesktop:
@@ -598,11 +600,37 @@ func advisoryReference(e ecosystemTypes.Ecosystem, s sourceTypes.SourceID, da mo
598600
Source: "SUSE",
599601
RefID: da.AdvisoryID,
600602
}, nil
603+
case ecosystemTypes.EcosystemTypeMicrosoft:
604+
return models.Reference{
605+
Link: fmt.Sprintf("https://msrc.microsoft.com/update-guide/vulnerability/%s", da.AdvisoryID),
606+
Source: "MICROSOFT",
607+
RefID: da.AdvisoryID,
608+
}, nil
601609
default:
602610
return models.Reference{}, xerrors.Errorf("unsupported family: %s", et)
603611
}
604612
}
605613

614+
// cveContentOptional builds the per-source CveContent.Optional map. Every
615+
// ecosystem records the vuls2-sources trace; ecosystem-specific extras are
616+
// added per case. Microsoft surfaces the MSRC exploitability assessment
617+
// ("Publicly Disclosed:...;Exploited:...;...") under "exploit", mirroring the
618+
// legacy gost Microsoft path (gost/microsoft.go: Optional["exploit"] =
619+
// cve.ExploitStatus). vuls-data-update extracts it as the advisory-level
620+
// "exploitability" optional value on the CVRF vulnerability content.
621+
func cveContentOptional(e ecosystemTypes.Ecosystem, v vulnerabilityTypes.Vulnerability, sources string) map[string]string {
622+
m := map[string]string{"vuls2-sources": sources}
623+
624+
et, _, _ := strings.Cut(string(e), ":")
625+
switch et {
626+
case ecosystemTypes.EcosystemTypeMicrosoft:
627+
if ex, ok := v.Content.Optional["exploitability"].(string); ok && ex != "" {
628+
m["exploit"] = ex
629+
}
630+
}
631+
return m
632+
}
633+
606634
func cveContentSourceLink(ccType models.CveContentType, v vulnerabilityTypes.Vulnerability) string {
607635
switch ccType {
608636
case models.RedHat, models.RedHatAPI:
@@ -621,6 +649,8 @@ func cveContentSourceLink(ccType models.CveContentType, v vulnerabilityTypes.Vul
621649
return fmt.Sprintf("https://security.alpinelinux.org/vuln/%s", v.Content.ID)
622650
case models.Nvd:
623651
return fmt.Sprintf("https://nvd.nist.gov/vuln/detail/%s", v.Content.ID)
652+
case models.Microsoft:
653+
return fmt.Sprintf("https://msrc.microsoft.com/update-guide/vulnerability/%s", v.Content.ID)
624654
default:
625655
return ""
626656
}
@@ -752,6 +782,22 @@ func compareSourceID(e ecosystemTypes.Ecosystem, a, b sourceTypes.SourceID) int
752782
}
753783
}
754784
return cmp.Compare(preferenceFn(a), preferenceFn(b))
785+
case ecosystemTypes.EcosystemTypeMicrosoft:
786+
preferenceFn := func(sourceID sourceTypes.SourceID) int {
787+
switch sourceID {
788+
case sourceTypes.MicrosoftCVRF:
789+
return 5
790+
case sourceTypes.MicrosoftCSAF:
791+
return 4
792+
case sourceTypes.MicrosoftBulletin:
793+
return 3
794+
case sourceTypes.MicrosoftWSUSSCN2, sourceTypes.MicrosoftMSUC:
795+
return 2
796+
default:
797+
return 1
798+
}
799+
}
800+
return cmp.Compare(preferenceFn(a), preferenceFn(b))
755801
default:
756802
return 0
757803
}
@@ -849,6 +895,8 @@ func toCveContentType(e ecosystemTypes.Ecosystem, s sourceTypes.SourceID) models
849895
}
850896
case ecosystemTypes.EcosystemTypeSUSELinuxEnterprise, ecosystemTypes.EcosystemTypeOpenSUSE, ecosystemTypes.EcosystemTypeOpenSUSELeap, ecosystemTypes.EcosystemTypeOpenSUSETumbleweed:
851897
return models.SUSE
898+
case ecosystemTypes.EcosystemTypeMicrosoft:
899+
return models.Microsoft
852900
default:
853901
return models.NewCveContentType(et)
854902
}
@@ -999,6 +1047,8 @@ func toVuls0Confidence(e ecosystemTypes.Ecosystem, s sourceTypes.SourceID) model
9991047
default:
10001048
return models.OvalMatch
10011049
}
1050+
case ecosystemTypes.EcosystemTypeMicrosoft:
1051+
return models.WindowsUpdateSearch
10021052
default:
10031053
return models.Confidence{
10041054
Score: 0,

0 commit comments

Comments
 (0)