github.com/Uptycs/basequery-go@v0.8.0/plugin/logger/logger.go (about)

     1  // Package logger creates an osquery logging plugin.
     2  //
     3  // See https://osquery.readthedocs.io/en/latest/development/logger-plugins/ for more.
     4  package logger
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"encoding/json"
    10  
    11  	"github.com/Uptycs/basequery-go/gen/osquery"
    12  )
    13  
    14  // LogFunc is the logger function used by an osquery Logger plugin.
    15  //
    16  // The LogFunc should log the provided result string. The LogType
    17  // argument can be optionally used to log differently depending on the
    18  // type of log received. The context argument can optionally be used
    19  // for cancellation in long-running operations.
    20  type LogFunc func(ctx context.Context, typ LogType, log string) error
    21  
    22  // Plugin is an osquery logger plugin.
    23  // The Plugin struct implements the OsqueryPlugin interface.
    24  type Plugin struct {
    25  	name  string
    26  	logFn LogFunc
    27  }
    28  
    29  // NewPlugin takes a value that implements LoggerPlugin and wraps it with
    30  // the appropriate methods to satisfy the OsqueryPlugin interface. Use this to
    31  // easily create plugins implementing osquery loggers.
    32  func NewPlugin(name string, fn LogFunc) *Plugin {
    33  	return &Plugin{name: name, logFn: fn}
    34  }
    35  
    36  // Name returns the logger plugin name.
    37  func (t *Plugin) Name() string {
    38  	return t.name
    39  }
    40  
    41  // RegistryName always returns static string "logger" for logger plugins.
    42  func (t *Plugin) RegistryName() string {
    43  	return "logger"
    44  }
    45  
    46  // Routes returns empty plugin response for logger plugins.
    47  func (t *Plugin) Routes() osquery.ExtensionPluginResponse {
    48  	return []map[string]string{}
    49  }
    50  
    51  // Ping returns static "OK" response.
    52  func (t *Plugin) Ping() osquery.ExtensionStatus {
    53  	return osquery.ExtensionStatus{Code: 0, Message: "OK"}
    54  }
    55  
    56  // Call is invoked to log the specified request details. Depending on the type of logger implementation,
    57  // contents of the requests can be saved to a file, sent to remote destination etc after necessary formatting.
    58  func (t *Plugin) Call(ctx context.Context, request osquery.ExtensionPluginRequest) osquery.ExtensionResponse {
    59  	var err error
    60  	if log, ok := request["string"]; ok {
    61  		err = t.logFn(ctx, LogTypeString, log)
    62  	} else if log, ok := request["snapshot"]; ok {
    63  		err = t.logFn(ctx, LogTypeSnapshot, log)
    64  	} else if log, ok := request["health"]; ok {
    65  		err = t.logFn(ctx, LogTypeHealth, log)
    66  	} else if log, ok := request["init"]; ok {
    67  		err = t.logFn(ctx, LogTypeInit, log)
    68  	} else if _, ok := request["status"]; ok {
    69  		statusJSON := []byte(request["log"])
    70  		if len(statusJSON) == 0 {
    71  			return osquery.ExtensionResponse{
    72  				Status: &osquery.ExtensionStatus{
    73  					Code:    1,
    74  					Message: "got empty status",
    75  				},
    76  			}
    77  		}
    78  
    79  		// Dirty hack because osquery gives us malformed JSON.
    80  		statusJSON = bytes.Replace(statusJSON, []byte(`"":`), []byte(``), -1)
    81  		statusJSON[0] = '['
    82  		statusJSON[len(statusJSON)-1] = ']'
    83  
    84  		var parsedStatuses []json.RawMessage
    85  		if err := json.Unmarshal(statusJSON, &parsedStatuses); err != nil {
    86  			return osquery.ExtensionResponse{
    87  				Status: &osquery.ExtensionStatus{
    88  					Code:    1,
    89  					Message: "error parsing status logs: " + err.Error(),
    90  				},
    91  			}
    92  		}
    93  
    94  		for _, s := range parsedStatuses {
    95  			err = t.logFn(ctx, LogTypeStatus, string(s))
    96  		}
    97  	} else {
    98  		return osquery.ExtensionResponse{
    99  			Status: &osquery.ExtensionStatus{
   100  				Code:    1,
   101  				Message: "unknown log request",
   102  			},
   103  		}
   104  	}
   105  
   106  	if err != nil {
   107  		return osquery.ExtensionResponse{
   108  			Status: &osquery.ExtensionStatus{
   109  				Code:    1,
   110  				Message: "error logging: " + err.Error(),
   111  			},
   112  		}
   113  	}
   114  
   115  	return osquery.ExtensionResponse{
   116  		Status:   &osquery.ExtensionStatus{Code: 0, Message: "OK"},
   117  		Response: osquery.ExtensionPluginResponse{},
   118  	}
   119  }
   120  
   121  // Shutdown is a no-op function for logger plugins.
   122  func (t *Plugin) Shutdown() {}
   123  
   124  //LogType encodes the type of log osquery is outputting.
   125  type LogType int
   126  
   127  const (
   128  	// LogTypeString to log a string
   129  	LogTypeString LogType = iota
   130  	// LogTypeSnapshot to log snapshot results
   131  	LogTypeSnapshot
   132  	// LogTypeHealth for health details logging
   133  	LogTypeHealth
   134  	// LogTypeInit for init details logging
   135  	LogTypeInit
   136  	// LogTypeStatus for differential results status logging
   137  	LogTypeStatus
   138  )
   139  
   140  // String implements the fmt.Stringer interface for LogType.
   141  func (l LogType) String() string {
   142  	var typeString string
   143  	switch l {
   144  	case LogTypeString:
   145  		typeString = "string"
   146  	case LogTypeSnapshot:
   147  		typeString = "snapshot"
   148  	case LogTypeHealth:
   149  		typeString = "health"
   150  	case LogTypeInit:
   151  		typeString = "init"
   152  	case LogTypeStatus:
   153  		typeString = "status"
   154  	default:
   155  		typeString = "unknown"
   156  	}
   157  	return typeString
   158  }