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  }