@@ -150,70 +150,124 @@ type AuditServer struct {
150150 sideTaskSeq atomic.Uint64
151151}
152152
153- func (as * AuditServer ) handleFrame (frame []byte ) gnet.Action {
154- // Parse the audit log for rule evaluation
153+ // MatchResult describes the outcome of matching a single audit frame.
154+ // It mirrors the rule-group matching behavior used by the runtime event loop,
155+ // returning the decoded log and any rule groups that matched.
156+ type MatchResult struct {
157+ Matched bool
158+ Log AuditLog
159+ MatchedGroups []string
160+ }
161+
162+ type matchResult struct {
163+ Matched bool
164+ Log AuditLog
165+ matchedGroupIndexes []int
166+ }
167+
168+ // MatchFrame evaluates a raw audit log frame against configured rule groups.
169+ // It returns whether any group matched, the decoded audit log, and the names of
170+ // matching rule groups in configured order.
171+ func (as * AuditServer ) MatchFrame (frame []byte ) (MatchResult , error ) {
172+ result , err := as .matchFrame (frame )
173+ if err != nil {
174+ return MatchResult {}, err
175+ }
176+
177+ matchedGroups := make ([]string , 0 , len (result .matchedGroupIndexes ))
178+ for _ , idx := range result .matchedGroupIndexes {
179+ matchedGroups = append (matchedGroups , as .ruleGroups [idx ].Name )
180+ }
181+
182+ return MatchResult {
183+ Matched : result .Matched ,
184+ Log : result .Log ,
185+ MatchedGroups : matchedGroups ,
186+ }, nil
187+ }
188+
189+ func (as * AuditServer ) matchFrame (frame []byte ) (matchResult , error ) {
155190 auditLog := auditLogPool .Get ().(* AuditLog )
156- * auditLog = AuditLog {} // reset pooled object
191+ * auditLog = AuditLog {}
157192
158193 err := json .Unmarshal (frame , auditLog )
159194 if err != nil {
160- as .logger .Error ("Error parsing audit log" , "error" , err )
161195 auditLogPool .Put (auditLog )
162- return gnet .Close
196+ return matchResult {}, err
197+ }
198+
199+ var matchedIndexes []int
200+ for idx := range as .ruleGroups {
201+ if as .ruleGroups [idx ].shouldLog (auditLog ) {
202+ matchedIndexes = append (matchedIndexes , idx )
203+ }
204+ }
205+ result := matchResult {
206+ Matched : len (matchedIndexes ) > 0 ,
207+ Log : * auditLog ,
208+ matchedGroupIndexes : matchedIndexes ,
163209 }
164210
165- matched := false
211+ auditLogPool .Put (auditLog )
212+ return result , nil
213+ }
214+
215+ func (as * AuditServer ) handleFrameWithResult (frame []byte , result matchResult ) {
216+
166217 var payload []byte
167218 var payloadStr string
168219 payloadReady := false
169220 payloadStrReady := false
170221
171- // Check each rule group
172- for _ , rg := range as .ruleGroups {
173- if rg .shouldLog (auditLog ) {
174- matched = true
175- as .logger .Debug ("Matched rule group" , "group" , rg .Name )
222+ for _ , rgIdx := range result .matchedGroupIndexes {
223+ rg := as .ruleGroups [rgIdx ]
176224
177- if rg .Messenger != nil || rg .Forwarder != nil {
178- if ! payloadReady {
179- payload = append ([]byte (nil ), frame ... )
180- payloadReady = true
181- }
182- if rg .Messenger != nil && ! payloadStrReady {
183- payloadStr = string (payload )
184- payloadStrReady = true
185- }
186- _ = as .enqueueSide (sideTask {
187- groupName : rg .Name ,
188- payload : payload ,
189- payloadStr : payloadStr ,
190- messenger : rg .Messenger ,
191- forwarder : rg .Forwarder ,
192- })
225+ as .logger .Debug ("Matched rule group" , "group" , rg .Name )
226+
227+ if rg .Messenger != nil || rg .Forwarder != nil {
228+ if ! payloadReady {
229+ payload = append ([]byte (nil ), frame ... )
230+ payloadReady = true
231+ }
232+ if rg .Messenger != nil && ! payloadStrReady {
233+ payloadStr = string (payload )
234+ payloadStrReady = true
193235 }
236+ _ = as .enqueueSide (sideTask {
237+ groupName : rg .Name ,
238+ payload : payload ,
239+ payloadStr : payloadStr ,
240+ messenger : rg .Messenger ,
241+ forwarder : rg .Forwarder ,
242+ })
243+ }
194244
195- // zero‑copy write to log when possible
196- if rg .Writer != nil {
197- if _ , err := rg .Writer .Write (frame ); err != nil {
198- as .logger .Error ("Failed to write audit log" , "group" , rg .Name , "error" , err )
199- }
245+ if rg .Writer != nil {
246+ if _ , err := rg .Writer .Write (frame ); err != nil {
247+ as .logger .Error ("Failed to write audit log" , "group" , rg .Name , "error" , err )
248+ }
249+ } else {
250+ if payloadStrReady {
251+ rg .Logger .Print (payloadStr )
200252 } else {
201- if payloadStrReady {
202- rg .Logger .Print (payloadStr )
203- } else {
204- rg .Logger .Print (string (frame ))
205- }
253+ rg .Logger .Print (string (frame ))
206254 }
207- // TODO(JM):Add a flag to prevent logging to multiple groups
208- // break
209255 }
210256 }
257+ }
211258
212- auditLogPool .Put (auditLog )
213-
214- if ! matched {
259+ func (as * AuditServer ) handleFrame (frame []byte ) gnet.Action {
260+ result , err := as .matchFrame (frame )
261+ if err != nil {
262+ as .logger .Error ("Error parsing audit log" , "error" , err )
263+ return gnet .Close
264+ }
265+ if ! result .Matched {
215266 return gnet .Close
216267 }
268+
269+ as .handleFrameWithResult (frame , result )
270+
217271 return gnet .None
218272}
219273
0 commit comments