# Version 10.0.2 # main query used in audit trail for users activities app [audit_trail_users_query] disabled=0 dispatch.earliest_time = -24h@h dispatch.latest_time = now search = ```filter out capability checks, internal users and quota checks``` \ index=_audit NOT info IN (denied, granted, "denied *", "granted *") NOT cap=1 NOT action=quota user IN ($ss_user_names$) host IN ($ss_host_names$) $ss_text$ \ ```Filter out audit v2 logs```\ | search sourcetype="audittrail"\ | rex field=_raw "user=(?[a-zA-Z0-9/\s_\.-]*)," \ | rex field=_raw "action=(?[a-zA-Z0-9_\s]*)," \ ```calculate user_action to get rid of spaces``` \ | eval user_action=lower(replace(auditrep_action, "\s", "_")) \ ```filter out internal or unknown users, preserve specific actions``` \ | eval auditrep_user=IF(auditrep_user == "n/a" AND user_action IN ("splunkstarting", "splunkshuttingdown"), "unknown", auditrep_user) \ | search NOT auditrep_user IN ("n/a", internal_observability, splunk-system-user, internal_automation, soar_proxy_user, soar_automation_user) \ ```filter out SCS events``` \ | spath input=info path=generated_by output=scs_generated_by | where isnull(scs_generated_by) \ ```filter out internal automated non-human actions``` \ | search NOT user_action IN (validate_token, get_password, alert_fired, list_scs_token) \ ```calculate useragent_prefix for login_attempt actions``` \ | eval normalized_useragent=IF(user_action=="login_attempt" AND (isnull(useragent) OR useragent=""), "unknown", useragent) \ | eval useragent_prefix=IF(user_action=="login_attempt", mvindex(split(mvindex(split(normalized_useragent,"/"),0), " "), 0), "n/a") \ ```filter out cancelled searches, searches ran by scheduler and subsearches``` \ | eval skip="0" \ | eval skip=IF(user_action=="search" AND (info=="cancel" OR info=="canceled" OR provenance="scheduler" OR search_id like "'subsearch_%"), "1", skip) \ ```| eval skip=IF(user_action=="login_attempt" AND useragent_prefix != "Mozilla", "1", skip) ``` \ | search skip="0" \ ```calculate extended user_action - ex_user_action``` \ | rex field=user_action "(?[a-zA-Z]+)_(?[a-zA-Z_]*)" \ | eval ex_user_action=user_action \ | eval object_name=name \ | eval object_name=IF(object_type=="saved_search", savedsearch_name, object_name) \ | eval object_name=IF(object_type=="token", tokenId, object_name) \ | eval object_name=IF(object_type=="user", IF(isnull(username), user, username), object_name) \ | eval object_name=IF(object_type=="app", IF( NOT isnull(app_name), app_name, IF(NOT isnull(app), app, object_name)), object_name) \ | eval object_name=IF(object_type=="saml_group_map", group, object_name) \ | eval object_name=IF(object_type=="datamodel", IF(isnull(object_name), displayName, object_name), object_name) \ | eval object_name=IF(object_type=="password", password_id, object_name) \ | eval ex_user_action=IF(object_action IN ("edit", "create", "new", "remove", "delete", "update", "updated", "disable", "enable", "move", "install") AND NOT isnull(object_name) AND NOT lower(provenance)=="n/a", ex_user_action."@".object_name, ex_user_action) \ | eval ex_user_action=IF(user_action like "login_attempt%" OR user_action like "logout%" , ex_user_action."@".user, ex_user_action) \ ```search is treated a little differently``` \ | eval object_name=IF(user_action=="search", IF(NOT isnull(provenance) AND NOT provenance=="N/A", provenance, \ IF(NOT isnull(savedsearch_name) AND savedsearch_name != "", savedsearch_name, "noname_search")), object_name) \ | eval ex_user_action=IF(user_action=="search", "search@".object_name, ex_user_action) \ ```Preserve original event body``` \ | eval orig_body=_raw \ | eval object_name=IF(isnull(object_name), "n/a", object_name) \ | eval host=IF(isnull(host), "n/a", host) \ | eval app=IF(isnull(app), "n/a", app) \ | eval useragent=IF(isnull(useragent_prefix), "n/a", useragent_prefix) \ | eval user=auditrep_user \ | stats count by _time, user, user_action, ex_user_action, app, object_name, host, useragent, info, orig_body \ # main query used in audit trail knowledge objects app [audit_trail_knowledge_objects_query] action.email.useNSSubject = 1 action.webhook.enable_allowlist = 0 dispatch.earliest_time = -7d@h dispatch.latest_time = now display.general.type = statistics display.page.search.tab = statistics display.visualizations.show = 0 request.ui_dispatch_app = audit_trail request.ui_dispatch_view = search search = ```Firstly filter out capability checks and non CRUD, non knowledge objects audit logs```\ index=_audit | where isnull(cap) AND NOT info IN ("granted", "denied") | search action IN ("create_*", "update_*", "updated_*", "edit_*", "remove_*", "delete_*", "disable_*", "new_*", "move_*", "enable_*", "acl_*") | search action IN ("*_user", "*_saved_search", "*_data_model_file", "*_event_type", "*_tag", "*_fields_*", "*_lookup_table", "*_ui_view*", "*_ui_panel", "*_macro", "*_command")\ ```Filter out audit v2 logs```\ | search sourcetype="audittrail"\ ```Extract object type and action```\ | rex action="(?[a-zA-Z]+)_(?[a-zA-Z_]*),"\ ```Extract application name```\ | rex field=info "app=\"(?.*)\"" \ | eval appnametmp=IF(!isnull(apptmp),apptmp,IF(isnull(app),"-missing-",app) )\ | rex field=appnametmp "'(?.*)'" \ | eval appname=IF(!isnull(apptmp2),apptmp2,appnametmp) \ ```Unify object name```\ | eval name=IF(object_type="user",username,name) \ | eval name=IF(object_type="saved_search",savedsearch_name,name)\ | eval name=IF(!isnull(name),name,"-missing-")\ ```Tags may have mnulti values: {[0]='tag_1' [1]='tag_2'}- split it to separate events```\ | eval tagnames=split(replace(name, "\\[[0-9]*\\]=",""), "' '") | mvexpand tagnames | eval name=ltrim(rtrim(tagnames,"'}"),"{'")\ ```Extract host name from fqdn```\ | eval host=mvindex(split(host,"."),0)\ ```Finally rename object type and actions to user friendly ones```\ | lookup object_types_normalization ORIG_OBJECT_TYPE AS object_type OUTPUT DEST_OBJECT_TYPE AS object_type\ | lookup action_types_normalization ORIG_ACTION AS object_action OUTPUT DEST_ACTION AS object_action\ ```Preserve original event body```\ | eval orig_body=_raw\ ```Apply filtering in the base search to avoid 'unknown sid' issue```\ ```| where "text_token$"="-" OR orig_body like "%text_token$%"``` \ | stats count by _time, user, action, name, appname, host, info, object_type, object_action, orig_body