go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/resources/updates/win_updates.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package updates 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "io" 10 11 "github.com/rs/zerolog/log" 12 "go.mondoo.com/cnquery/providers/os/connection/shared" 13 "go.mondoo.com/cnquery/providers/os/resources/powershell" 14 ) 15 16 const ( 17 WindowsUpdateFormat = "wsus" 18 ) 19 20 var WINDOWS_QUERY_WSUS_AVAILABLE = ` 21 $ProgressPreference='SilentlyContinue'; 22 $updateSession = new-object -com "Microsoft.Update.Session" 23 $searcher=$updateSession.CreateupdateSearcher().Search(("IsInstalled=0 and Type='Software'")) 24 $updates = $searcher.Updates | ForEach-Object { 25 $update = $_ 26 $value = New-Object psobject -Property @{ 27 "UpdateID" = $update.Identity.UpdateID; 28 "Title" = $update.Title 29 "MsrcSeverity" = $update.MsrcSeverity 30 "RevisionNumber" = $update.Identity.RevisionNumber; 31 "CategoryIDs" = @($update.Categories | % { $_.CategoryID }) 32 "SecurityBulletinIDs" = $update.SecurityBulletinIDs 33 "RebootRequired" = $update.RebootRequired 34 "KBArticleIDs" = $update.KBArticleIDs 35 "CveIDs" = @($update.CveIDs) 36 } 37 $value 38 } 39 @($updates) | ConvertTo-Json` 40 41 type powershellWinUpdate struct { 42 UpdateID string `json:"UpdateID"` 43 Title string `json:"Title"` 44 MsrcSeverity string `json:"MsrcSeverity"` 45 Revision string `json:"Revision"` 46 RebootRequired bool `json:"RebootRequired"` 47 CategoryIDs []string `json:"CategoryIDs"` 48 KBArticleIDs []string `json:"KBArticleIDs"` 49 } 50 51 type WindowsUpdateManager struct { 52 conn shared.Connection 53 } 54 55 func (um *WindowsUpdateManager) Name() string { 56 return "Windows Server Update Services Manager" 57 } 58 59 func (um *WindowsUpdateManager) Format() string { 60 return WindowsUpdateFormat 61 } 62 63 func (um *WindowsUpdateManager) List() ([]OperatingSystemUpdate, error) { 64 cmd := powershell.Encode(WINDOWS_QUERY_WSUS_AVAILABLE) 65 c, err := um.conn.RunCommand(cmd) 66 if err != nil { 67 return nil, fmt.Errorf("could not read package list") 68 } 69 return ParseWindowsUpdates(c.Stdout) 70 } 71 72 func ParseWindowsUpdates(input io.Reader) ([]OperatingSystemUpdate, error) { 73 data, err := io.ReadAll(input) 74 if err != nil { 75 return nil, err 76 } 77 78 // handle case where no packages are installed 79 if len(data) == 0 { 80 return []OperatingSystemUpdate{}, nil 81 } 82 83 var powerShellUpdates []powershellWinUpdate 84 err = json.Unmarshal(data, &powerShellUpdates) 85 if err != nil { 86 return nil, err 87 } 88 89 updates := make([]OperatingSystemUpdate, len(powerShellUpdates)) 90 for i := range powerShellUpdates { 91 if len(powerShellUpdates[i].KBArticleIDs) == 0 { 92 log.Warn().Str("update", powerShellUpdates[i].UpdateID).Msg("ms update has no kb assigned") 93 continue 94 } 95 96 // todo: we may want to make that decision server-side, since it does not require us to update the agent 97 // therefore we need additional information to be transmitted via the packages eg. labels 98 // important := false 99 // for ci := range powerShellUpdates[i].CategoryIDs { 100 // id := powerShellUpdates[i].CategoryIDs[ci] 101 // classification := wsusClassificationGUID[strings.ToLower(id)] 102 // if classification == CriticalUpdates || classification == SecurityUpdates || classification == UpdateRollups { 103 // important = true 104 // } 105 // } 106 107 updates[i] = OperatingSystemUpdate{ 108 ID: powerShellUpdates[i].UpdateID, 109 Name: powerShellUpdates[i].KBArticleIDs[0], 110 Description: powerShellUpdates[i].Title, 111 Version: powerShellUpdates[i].Revision, 112 Severity: powerShellUpdates[i].MsrcSeverity, 113 Format: "windows/updates", 114 Restart: powerShellUpdates[i].RebootRequired, 115 } 116 } 117 return updates, nil 118 }