Skip to content

Commit cc47870

Browse files
Kbayeromjabascal10ylladaJocLRojasosmontero
authored
Release/v10.8.1 (#1225)
* fix(compliance-schedule): fix standard and section selection issue in report creation * chore: Update CHANGELOG.md * chore: update version.yml * fix(ui): display array fields as a single field without numeric suffixes * chore: update CHANGELOG.md * fix(alert-field-render): resolve persistent loading spinner when displaying "tags" column * chore: Update CHANGELOG.md * fix: Resolve false positive checkbox selection when editing tagging rules * feat: implement alert correlation and context building for enhanced alert analysis * add debug logging for GPT request * feat: add debug logging for alert processing and related alerts retrieval * fix: update to return schema.Alert and adjust related logic * refactor: simplify body creation in ElasticSearch function and remove unnecessary debug logs * fix: optimize alert correlation logic and improve classification handling * fix: update of the logic of correlation of alerts and construction of the historical context based on counts * fix: improve log handling in GPT request and ensure last log entry is used * feat: update macOS install steps with `utmstack-macos-agent.pkg` * fix: add pipeline for aws, sophos-central and o365 integrations * fix: remove logging of debug * Migrate from correlation service to direct Logstash connection in aws integration. * Migrate from correlation service to direct Logstash connection in office365 integration. * Migrate from correlation service to direct Logstash connection in sophos integration. * fix: add pipeline for aws, sophos-central and o365 integrations * chore: resolve merge conflicts * chore: integrate recent UI improvements * add datasource in macos agent logs * include logstash ports in installer for aws, o365 and sophos * fix: update TagRulesApplied field type to slice and join in conversion * fix: change TagRulesApplied field type from string to slice of int * Refactoring the event sending format to Logstash in the AWS plugin. * Refactoring the event sending format to Logstash in the Sophos plugin. * Refactoring the event sending format to Logstash in the office365 plugin. * fix: add pipeline for aws, sophos-central and o365 integrations * "Update blocklist processing to support severity levels and enhance IP threat intelligence integration." * "Add IP validation using net.ParseIP to ensure proper processing of source and destination IPs." * "Fix path in Dockerfile COPY command for the correlation binary." * fix: add pipeline for aws, sophos-central and o365 integrations * Update correlation Dockerfile * fix: add pipeline for aws, sophos-central and o365 integrations * fix: add pipeline for aws, sophos-central and o365 integrations * fix: filter only valid IPs when parsing coordinate map chart data * fix: update display name for Sophos integration * Implement Sophos Central filter (v1.0.0). * Refactor AWS filter (v2.0.0) to use JSON instead of Grok. * Refactor Office 365 filter (v2.0.0) by simplifying the structure. * fix: corrected typo in compliance status label from "Complaint" to "Compliant" * send logs from new windows channels in arm agent * fix: hide sorting action for assets filters * fix: improve CSV export limit parameters * fix: correct uninstalling command for macOs agent * feat: add Windows ARM64 support to agent installation platforms * set correct api url environment * fix: update filter for winevent log agent * fix: update wineventlog filter * fix: update wineventlog filter * update version and changelog --------- Co-authored-by: Manuel Abascal <[email protected]> Co-authored-by: Yadian Llada Lopez <[email protected]> Co-authored-by: JocLRojas <[email protected]> Co-authored-by: Osmany Montero <[email protected]>
1 parent 368869d commit cc47870

File tree

64 files changed

+3966
-262
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+3966
-262
lines changed

CHANGELOG.md

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
# UTMStack 10.8.0 Release Notes
2-
- Updated Soc-AI models and released the code as open source.
3-
- Added the ability for users to choose which model to use with Soc-AI.
4-
- Enhanced the prompt sent to OpenAI by including additional contextual details.
5-
- Added support for RedHat; UTMStack can now be installed on both Ubuntu and RedHat.
6-
- Improved log delivery from ARM-based agents on Windows, now sending native system logs.
7-
- Added support for macOS ARM64; agents can now be installed on that platform.
8-
- Improved agent information displayed in the Sources panel, providing more accurate OS details and agent versions.
1+
# UTMStack 10.8.1 Release Notes
2+
3+
- Improved log parsing and processing for AWS, O365, and Sophos Central integrations.
4+
- Updated Sophos XG integration from legacy mode to support newer versions
5+
- Enhanced log processing and parsing for Windows Agent on ARM architectures.
6+
- Added support for new log channels using the Windows API to retrieve additional logs.
7+
- Compliance Report Scheduling: Improved the stability of the selection process when creating new report schedules.
8+
- Improved field rendering in Log Explorer by consolidating list-based fields into a single entry for better readability and consistency.
9+
- Improved field rendering for tags and note fields in Alerts.
10+
- Improved export functionality to better handle large data sets and avoid performance issues during report generation.

agent/collectors/macos.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package collectors
55

66
import (
77
"bufio"
8+
"os"
89
"os/exec"
910
"path/filepath"
1011

@@ -29,6 +30,11 @@ func getCollectorsInstances() []Collector {
2930
func (d Darwin) SendLogs() {
3031
path := utils.GetMyPath()
3132
collectorPath := filepath.Join(path, "utmstack-collector-mac")
33+
host, err := os.Hostname()
34+
if err != nil {
35+
utils.Logger.ErrorF("error getting hostname: %v", err)
36+
host = "unknown"
37+
}
3238

3339
cmd := exec.Command(collectorPath)
3440

@@ -62,9 +68,11 @@ func (d Darwin) SendLogs() {
6268
continue
6369
}
6470

71+
messageWithHost := config.GetMessageFormated(host, validatedLog)
72+
6573
logservice.LogQueue <- logservice.LogPipe{
6674
Src: string(config.DataTypeMacOs),
67-
Logs: []string{validatedLog},
75+
Logs: []string{messageWithHost},
6876
}
6977
}
7078

agent/collectors/windows_arm64.go

Lines changed: 108 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ import (
77
"encoding/json"
88
"encoding/xml"
99
"fmt"
10-
"log"
1110
"os"
1211
"os/signal"
1312
"strconv"
1413
"strings"
14+
"sync"
1515
"syscall"
16+
"time"
1617
"unsafe"
1718

1819
"github.com/threatwinds/validations"
@@ -74,8 +75,10 @@ type EventSubscription struct {
7475
Channel string
7576
Query string
7677
Errors chan error
77-
Callback func(event *Event)
7878
winAPIHandle windows.Handle
79+
80+
mu sync.Mutex
81+
running bool
7982
}
8083

8184
const (
@@ -90,9 +93,13 @@ var (
9093
procEvtSubscribe = modwevtapi.NewProc("EvtSubscribe")
9194
procEvtRender = modwevtapi.NewProc("EvtRender")
9295
procEvtClose = modwevtapi.NewProc("EvtClose")
96+
incomingEvents = make(chan string, 1024)
9397
)
9498

9599
func (evtSub *EventSubscription) Create() error {
100+
evtSub.mu.Lock()
101+
defer evtSub.mu.Unlock()
102+
96103
if evtSub.winAPIHandle != 0 {
97104
return fmt.Errorf("windows_events: subscription has already been created")
98105
}
@@ -109,8 +116,6 @@ func (evtSub *EventSubscription) Create() error {
109116

110117
callback := syscall.NewCallback(evtSub.winAPICallback)
111118

112-
log.Printf("Debug - Subscribing to channel: %s", evtSub.Channel)
113-
114119
handle, _, err := procEvtSubscribe.Call(
115120
0,
116121
0,
@@ -131,8 +136,11 @@ func (evtSub *EventSubscription) Create() error {
131136
}
132137

133138
func (evtSub *EventSubscription) Close() error {
139+
evtSub.mu.Lock()
140+
defer evtSub.mu.Unlock()
141+
134142
if evtSub.winAPIHandle == 0 {
135-
return fmt.Errorf("windows_events: no active subscription to close")
143+
return nil
136144
}
137145
ret, _, err := procEvtClose.Call(uintptr(evtSub.winAPIHandle))
138146
if ret == 0 {
@@ -145,47 +153,101 @@ func (evtSub *EventSubscription) Close() error {
145153
func (evtSub *EventSubscription) winAPICallback(action, userContext, event uintptr) uintptr {
146154
switch action {
147155
case evtSubscribeActionError:
148-
evtSub.Errors <- fmt.Errorf("windows_events: error in callback, code: %x", uint16(event))
149-
case evtSubscribeActionDeliver:
150-
bufferSize := uint32(4096)
151-
for {
152-
renderSpace := make([]uint16, bufferSize/2)
153-
bufferUsed := uint32(0)
154-
propertyCount := uint32(0)
155-
ret, _, err := procEvtRender.Call(
156-
0,
157-
event,
158-
evtRenderEventXML,
159-
uintptr(bufferSize),
160-
uintptr(unsafe.Pointer(&renderSpace[0])),
161-
uintptr(unsafe.Pointer(&bufferUsed)),
162-
uintptr(unsafe.Pointer(&propertyCount)),
163-
)
164-
if ret == 0 {
165-
if err == windows.ERROR_INSUFFICIENT_BUFFER {
166-
bufferSize *= 2
167-
continue
156+
err := fmt.Errorf("windows_events: error in callback, code: %x", uint16(event))
157+
evtSub.Errors <- err
158+
159+
go func(channel string) {
160+
utils.Logger.LogF(100, "Attempting to resubscribe to channel: %s after error: %v", channel, err)
161+
evtSub.mu.Lock()
162+
defer evtSub.mu.Unlock()
163+
164+
_ = evtSub.Close()
165+
166+
for {
167+
time.Sleep(5 * time.Second)
168+
if err := evtSub.Create(); err != nil {
169+
utils.Logger.ErrorF("Retry failed for channel %s: %s", channel, err)
170+
} else {
171+
utils.Logger.LogF(100, "Resubscribed to channel: %s", channel)
172+
break
168173
}
169-
evtSub.Errors <- fmt.Errorf("windows_events: failed to render event: %w", err)
170-
return 0
171-
}
172-
xmlStr := windows.UTF16ToString(renderSpace)
173-
xmlStr = cleanXML(xmlStr)
174-
175-
dataParsed := new(Event)
176-
if err := xml.Unmarshal([]byte(xmlStr), dataParsed); err != nil {
177-
evtSub.Errors <- fmt.Errorf("windows_events: failed to parse XML: %s", err)
178-
} else {
179-
evtSub.Callback(dataParsed)
180174
}
175+
}(evtSub.Channel)
176+
177+
case evtSubscribeActionDeliver:
178+
utils.Logger.LogF(100, "Received event from channel: %s", evtSub.Channel)
179+
xmlStr, err := quickRenderXML(event)
180+
if err != nil {
181+
evtSub.Errors <- fmt.Errorf("render in callback: %v", err)
181182
break
182183
}
184+
select {
185+
case incomingEvents <- xmlStr:
186+
default:
187+
utils.Logger.ErrorF("incomingEvents lleno: evento descartado")
188+
}
183189
default:
184190
evtSub.Errors <- fmt.Errorf("windows_events: unsupported action in callback: %x", uint16(action))
185191
}
186192
return 0
187193
}
188194

195+
func eventWorker() {
196+
for xmlStr := range incomingEvents {
197+
ev := new(Event)
198+
if err := xml.Unmarshal([]byte(xmlStr), ev); err != nil {
199+
utils.Logger.ErrorF("unmarshal error: %v", err)
200+
continue
201+
}
202+
203+
eventJSON, err := convertEventToJSON(ev)
204+
if err != nil {
205+
utils.Logger.ErrorF("toJSON error: %v", err)
206+
continue
207+
}
208+
209+
validatedLog, _, err := validations.ValidateString(eventJSON, false)
210+
if err != nil {
211+
utils.Logger.LogF(100, "validation error: %s: %v", eventJSON, err)
212+
continue
213+
}
214+
215+
select {
216+
case logservice.LogQueue <- logservice.LogPipe{
217+
Src: string(config.DataTypeWindowsAgent),
218+
Logs: []string{validatedLog},
219+
}:
220+
default:
221+
utils.Logger.LogF(100, "LogQueue full: event discarded")
222+
}
223+
}
224+
}
225+
226+
func quickRenderXML(h uintptr) (string, error) {
227+
bufSize := uint32(4096)
228+
for {
229+
space := make([]uint16, bufSize/2)
230+
used := uint32(0)
231+
prop := uint32(0)
232+
233+
ret, _, err := procEvtRender.Call(
234+
0, h, evtRenderEventXML,
235+
uintptr(bufSize),
236+
uintptr(unsafe.Pointer(&space[0])),
237+
uintptr(unsafe.Pointer(&used)),
238+
uintptr(unsafe.Pointer(&prop)),
239+
)
240+
if ret == 0 {
241+
if err == windows.ERROR_INSUFFICIENT_BUFFER {
242+
bufSize *= 2
243+
continue
244+
}
245+
return "", err
246+
}
247+
return cleanXML(windows.UTF16ToString(space)), nil
248+
}
249+
}
250+
189251
func cleanXML(xmlStr string) string {
190252
xmlStr = strings.TrimSpace(xmlStr)
191253
if idx := strings.Index(xmlStr, "<?xml"); idx > 0 {
@@ -210,36 +272,23 @@ func getCollectorsInstances() []Collector {
210272

211273
func (w Windows) SendLogs() {
212274
errorsChan := make(chan error, 10)
275+
go eventWorker()
213276

214-
callback := func(event *Event) {
215-
eventJSON, err := convertEventToJSON(event)
216-
if err != nil {
217-
utils.Logger.ErrorF("error converting event to JSON: %v", err)
218-
return
219-
}
220-
validatedLog, _, err := validations.ValidateString(eventJSON, false)
221-
if err != nil {
222-
utils.Logger.LogF(100, "error validating log: %s: %v", eventJSON, err)
223-
return
224-
}
225-
logservice.LogQueue <- logservice.LogPipe{
226-
Src: string(config.DataTypeWindowsAgent),
227-
Logs: []string{validatedLog},
228-
}
277+
channels := []string{
278+
"Security", "Application", "System", "Microsoft-Windows-Sysmon/Operational", "Windows Powershell",
279+
"Microsoft-Windows-Powershell/Operational", "ForwardedEvents", "Microsoft-Windows-WinLogon/Operational",
280+
"Microsoft-Windows-Windows Firewall With Advanced Security/Firewall", "Microsoft-Windows-Windows Defender/Operational",
229281
}
230-
231-
channels := []string{"Security", "Application", "System"}
232282
var subscriptions []*EventSubscription
233283

234284
for _, channel := range channels {
235285
sub := &EventSubscription{
236-
Channel: channel,
237-
Query: "*",
238-
Errors: errorsChan,
239-
Callback: callback,
286+
Channel: channel,
287+
Query: "*",
288+
Errors: errorsChan,
240289
}
241290
if err := sub.Create(); err != nil {
242-
utils.Logger.ErrorF("Error subscribing to channel %s: %s", channel, err)
291+
utils.Logger.LogF(100, "Error subscribing to channel %s: %s", channel, err)
243292
continue
244293
}
245294
subscriptions = append(subscriptions, sub)
@@ -255,6 +304,7 @@ func (w Windows) SendLogs() {
255304
exitChan := make(chan os.Signal, 1)
256305
signal.Notify(exitChan, os.Interrupt)
257306
<-exitChan
307+
close(incomingEvents)
258308
utils.Logger.LogF(100, "Interrupt received, closing subscriptions...")
259309
for _, sub := range subscriptions {
260310
if err := sub.Close(); err != nil {

aws/configuration/const.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ package configuration
33
import "github.com/utmstack/UTMStack/aws/utils"
44

55
const (
6-
CORRELATIONURL = "http://correlation:8080/v1/newlog"
76
URL_CHECK_CONNECTION = "https://sts.amazonaws.com"
7+
LogstashEndpoint = "http://%s:%s"
8+
UTMLogSeparator = "<utm-log-separator>"
89
)
910

1011
func GetInternalKey() string {
@@ -14,3 +15,11 @@ func GetInternalKey() string {
1415
func GetPanelServiceName() string {
1516
return utils.Getenv("PANEL_SERV_NAME")
1617
}
18+
19+
func GetLogstashHost() string {
20+
return utils.Getenv("UTM_LOGSTASH_HOST")
21+
}
22+
23+
func GetLogstashPort() string {
24+
return utils.Getenv("UTM_LOGSTASH_PORT")
25+
}

aws/processor/pull.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ func PullLogs(startTime time.Time, endTime time.Time, group types.ModuleGroup) *
1818
return err
1919
}
2020

21-
err = SendToCorrelation(logs)
21+
err = SendToLogstash(logs)
2222
if err != nil {
2323
return err
2424
}

0 commit comments

Comments
 (0)