IBM Security Verify Modeling Rule

Modeling Rule

IBM Security Verify

Details

IDIBM_Security_Verify_ModelingRule
From Version8.6.0

Rules (XIF)

//Created a generic mapping rule for fields relevant to most event types in IBM Security Verify.
[RULE: generic_fields_mapping]
alter
    src_ipv6 = arrayindex(regextract(data -> origin, "(?:[a-fA-F\d]{0,4}\:){7}[\wa-fA-F]{0,4}"),0),
    src_ipv4 = arrayindex(regextract(data -> origin, "(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})"),0)
| alter
    xdm.event.type = if(event_type in ("sso", "authentication"), "authentication", event_type),
    xdm.event.id = id,
    xdm.source.ipv4 = if(src_ipv4 != null, src_ipv4, ""),
    xdm.source.ipv6 = if(src_ipv6 != null, src_ipv6, ""),
    xdm.source.user_agent = data -> devicetype,
    xdm.observer.name = if(tenantname != null AND servicename != null, concat(tenantname, " - ", servicename), tenantname),
    xdm.observer.unique_identifier = tenantid;

[MODEL: dataset=ibm_security_verify_raw]
/*
Mapping of 'management' event types.
*/
filter event_type = "management"
| alter
    src_user1 = arrayindex(regextract(data -> performedby_username, "(.+)\@") ,0), // The username can sometimes appear as a username and other times as an email.
    src_user2 = arrayindex(regextract(data -> username, "(.+)\@") ,0),
    mod = if(data -> modified = "[]", null, data -> modified),
    res = lowercase(data -> result)
| alter
    xdm.event.outcome = if(res ~= "[Ss]uccess" OR res = "sent", XDM_CONST.OUTCOME_SUCCESS, res ~= "fail", XDM_CONST.OUTCOME_FAILED, res IN("pending", "sent"), XDM_CONST.OUTCOME_UNKNOWN, res = "skipped", XDM_CONST.OUTCOME_PARTIAL, res),
    xdm.event.operation_sub_type = coalesce(data -> dict_op, data -> action),
    xdm.target.application.name = data -> applicationname,
    xdm.event.description = if(data -> cause != null AND data -> modified != null, concat(rtrim(data -> cause, "."), ". modified properties: ", mod), coalesce(data -> cause, data -> modified)), // Checks if 'cause' and 'modified' are both populated. If so, appends the cause with a list of modified properties. If not, returns whichever field is available.
    xdm.event.original_event_type = data -> messageid,
    xdm.auth.mfa.method = data -> mfamethod,
    xdm.source.user.identifier = if(data -> performedby IN("System", "shared_apikey"), null, data -> performedby),
    xdm.source.application.name = coalesce(data -> performedby_clientname, if(data -> performedby IN("System", "shared_apikey"), data -> performedby)),
    xdm.source.user.username = coalesce(src_user2, data -> username, src_user1, data -> performedby_username), //2 possible username fields, performedby_username and username.
    xdm.source.user.upn = if(src_user2 != null, data -> username ,src_user1 != null, data -> performedby_username),
    xdm.target.resource.id = data -> entitlementid,
    xdm.target.resource.name = data -> entitlementname,
    xdm.target.resource.sub_type = data -> entitlementsubtype,
    xdm.target.resource.type = if(data -> entitlementtype != null AND data -> entitlementcategory != null, concat(data -> entitlementtype, " - ", data -> entitlementcategory), coalesce(data -> entitlementtype, data -> entitlementcategory, data -> resource )), // The "type" field represents the role, while the "category" field specifies the role type, e.g., "role - admin."
    xdm.source.user.groups = to_json_string(data -> entitlements) -> []
| call generic_fields_mapping;

/*
Mapping of 'sso' event types.
*/
filter event_type = "sso"
| alter
    res = lowercase(data -> result),
    src_user = arrayindex(regextract(data -> username, "(.+)\@") ,0) // The username can sometimes appear as a username and other times as an email.
| alter
    xdm.event.description = data -> cause,
    xdm.event.outcome = if(res ~= "[Ss]uccess" OR res = "sent", XDM_CONST.OUTCOME_SUCCESS, res ~= "fail", XDM_CONST.OUTCOME_FAILED, res),
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_AUTH_LOGIN,
    xdm.auth.service = "SP",
    xdm.auth.auth_method = data -> subtype,
    xdm.source.user.identifier = data -> userid,
    xdm.source.user.username = coalesce(src_user, data -> username),
    xdm.source.user.upn = if(src_user != null, data -> username),
    xdm.target.resource.id = data -> applicationid,
    xdm.target.resource.name = data -> applicationname,
    xdm.target.application.name = data -> applicationname,
    xdm.source.host.device_id = data -> deviceid,
    xdm.event.tags = arraycreate(XDM_CONST.EVENT_TAG_AUTHENTICATION),
    xdm.network.ip_protocol = XDM_CONST.IP_PROTOCOL_TCP,
    xdm.target.ipv4 = "", //For story 102, we hardcode the necessary fields since the data isn't available in the logs.
    xdm.source.port = 0, //For story 102, we hardcode the necessary fields since the data isn't available in the logs.
    xdm.target.port = 0, //For story 102, we hardcode the necessary fields since the data isn't available in the logs.
    xdm.event.original_event_type = event_type
| call generic_fields_mapping;

/*
Mapping of 'authentication' event types
*/
filter event_type = "authentication"
| alter
    res = lowercase(data -> result),
    src_user = arrayindex(regextract(data -> username, "(.+)\@") ,0), // The username can sometimes appear as a username and other times as an email.
    subtype = lowercase(data -> subtype),
    mfamethod = lowercase(data -> mfamethod),
    cause_lower = lowercase(data -> cause),
    mfaresult_lower = lowercase(data -> mfaresult)
| alter
    xdm.event.outcome = if(res ~= "[Ss]uccess" OR res = "sent", XDM_CONST.OUTCOME_SUCCESS, res ~= "fail", XDM_CONST.OUTCOME_FAILED, res),
    xdm.event.outcome_reason = if(mfaresult_lower = "user_denied" or cause_lower ~= "one-time password .*? was invalid", "mfa_failure", cause_lower ~= "the account for the user [^\s]+ is locked" or cause_lower contains "locked_pwd_failures", "account_locked", cause_lower contains "invalid_creds", "bad_credentials", cause_lower ~= "user [^\s]+ is not authorized to access this resource", "auth_login_restrictions", cause_lower contains "user_fraudulent", "risky_signin" ,cause_lower contains "user_denied","user_reject", cause_lower ~= "user [^\s]+ is not an active user", "account_expired_or_disabled", cause_lower ~= "unexpected error - unable to fetch the tenant's state from cache." or cause_lower ~= "unexpected error - [\S]+ an error occurred while mapping the user", "failed_login", cause_lower ~= "unexpected error", "OTHER", to_string(data -> cause)),
    xdm.event.operation = if(subtype = "mfa",  XDM_CONST.OPERATION_TYPE_AUTH_MFA, XDM_CONST.OPERATION_TYPE_AUTH_LOGIN),
    xdm.event.operation_sub_type = if(mfamethod = "fido2", "hardware_token", mfamethod = "email otp", "email", mfamethod = "ibm verify push", "application", mfamethod = "sms otp", "sms", subtype in ("certificate", "social", "socialjwt"), "application", subtype = "user_password", "password", subtype = "federation", "Generic SSO", subtype = "device trust", "trusted_login", subtype),
    xdm.event.description = data -> cause,
    xdm.auth.service = "IDP",
    xdm.auth.auth_method = data -> sourcetype,
    xdm.source.user.identifier = if(data -> subject = "UNKNOWN", null, coalesce(data -> subject, data -> performedby)),
    xdm.target.url = data -> ["target"],
    xdm.source.user.username = coalesce(src_user, data -> username),
    xdm.source.user.upn = if(src_user != null, data -> username),
    xdm.source.host.device_id = data -> deviceid,
    xdm.source.host.device_category = if(lowercase(data -> mfadevice) ~= "iphone" or lowercase(data -> devicetype) ~= "verify/3" or lowercase(data -> devicetype) ~= "com.ibm.security.verifyapp", "Mobile", null),
    xdm.auth.mfa.method = data -> mfamethod,
    xdm.auth.mfa.client_details = data -> mfadevice,
    xdm.event.original_event_type = event_type,
    xdm.session_context_id = data -> usersessionid,
    xdm.event.tags = arraycreate(XDM_CONST.EVENT_TAG_AUTHENTICATION),
    xdm.network.ip_protocol= XDM_CONST.IP_PROTOCOL_TCP,
    xdm.target.ipv4 = "", //For story 102, we hardcode the necessary fields since the data isn't available in the logs.
    xdm.source.port = 0, //For story 102, we hardcode the necessary fields since the data isn't available in the logs.
    xdm.target.port = 0 //For story 102, we hardcode the necessary fields since the data isn't available in the logs.
| call generic_fields_mapping;

/*
Mapping of 'slo' event types.
*/
filter event_type = "slo"
| alter
    res = lowercase(data -> result),
    src_user = arrayindex(regextract(data -> username, "(.+)\@") ,0) // The username can sometimes appear as a username and other times as an email.
| alter
    xdm.event.outcome = if(res ~= "[Ss]uccess" OR res = "sent", XDM_CONST.OUTCOME_SUCCESS, res ~= "fail", XDM_CONST.OUTCOME_FAILED, res IN("pending", "sent"), XDM_CONST.OUTCOME_UNKNOWN, res = "skipped", XDM_CONST.OUTCOME_PARTIAL, res),
    xdm.event.operation_sub_type = data -> action,
    xdm.target.application.name = data -> applicationname,
    xdm.event.description = data -> cause,
    xdm.event.outcome_reason = data -> logoutresultdetail,
    xdm.auth.service = data -> sourcetype,
    xdm.source.user.identifier = coalesce(data -> userid, data -> subject),
    xdm.source.user.username = coalesce(src_user, data -> username),
    xdm.source.user.upn = if(src_user != null, data -> username),
    xdm.target.url = data -> ["target"],
    xdm.network.session_id = data -> usersessionid
| call generic_fields_mapping;

/*
Mapping of 'access_request' event types.
*/
filter event_type = "access_request"
| alter
    inter_user1 = arrayindex(regextract(data -> approver_username, "(.+)\@") ,0), // The username can sometimes appear as a username and other times as an email.
    inter_user2 = arrayindex(regextract(data -> performedby_username, "(.+)\@") ,0),
    src_user1 = arrayindex(regextract(data -> requestor_username, "(.+)\@") ,0),
    src_user2 = arrayindex(regextract(data -> beneficiary_username, "(.+)\@") ,0),
    src_ipv4 =  arrayindex(regextract(data -> origin, "(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})"),0),
    src_ipv6 = arrayindex(regextract(data -> origin, "(?:[a-fA-F\d]{0,4}\:){7}[\wa-fA-F]{0,4}"),0),
    action = lowercase(data -> action),
    stat = data -> status
| alter
    xdm.target.application.name = data -> applicationname,
    xdm.intermediate.user.identifier = coalesce(data -> approver, data -> performedby),
    xdm.intermediate.user.username = coalesce(inter_user1, inter_user2, data -> approver_username ,data -> performedby_username),
    xdm.intermediate.user.upn = if(inter_user1 != null, data -> approver_username, inter_user2 != null, data -> performedby_username),
    xdm.source.user.identifier = coalesce(data -> requestor, data -> beneficiary),
    xdm.source.user.username = coalesce(src_user1, src_user2, data -> username, data -> beneficiary_username),
    xdm.source.user.upn = if(src_user1 != null, data -> requestor_username, src_user2 != null, data -> beneficiary_username),
    xdm.event.description = data -> cause,
    xdm.source.user_agent = data -> devicetype,
    xdm.target.resource.name = data -> entitlement_name,
    xdm.target.resource.value = if(data -> entitlement_rights = "", null, data -> entitlement_rights),
    xdm.target.resource.type = data -> entitlement_type,
    xdm.source.ipv4 = src_ipv4,
    xdm.source.ipv6 = src_ipv6,
    xdm.session_context_id = data -> request,
    xdm.event.operation_sub_type = data -> resource,
    xdm.event.type = event_type,
    xdm.event.id = id,
    xdm.event.is_completed = if(stat = "pending", False, stat IN ("invalidated", "completed"), True),
    xdm.event.outcome = if(action IN("complete", "approve"), XDM_CONST.OUTCOME_SUCCESS, action = "invalid", XDM_CONST.OUTCOME_FAILED, action = "submit", XDM_CONST.OUTCOME_PARTIAL, action),
    xdm.observer.name = if(tenantname != null AND servicename != null, concat(tenantname, " - ", servicename), tenantname),
    xdm.observer.unique_identifier = tenantid;

Schema

ibm_security_verify_raw

Field Type Array?
data string
event_type string
geoip string
id string
servicename string
tenantid string
tenantname string
Raw JSON
{
    "ibm_security_verify_raw": {
        "data": {
            "type": "string",
            "is_array": false
        },
        "geoip": {
            "type": "string",
            "is_array": false
        },
        "event_type": {
            "type": "string",
            "is_array": false
        },
        "id": {
            "type": "string",
            "is_array": false
        },
        "tenantname": {
            "type": "string",
            "is_array": false
        },
        "servicename": {
            "type": "string",
            "is_array": false
        },
        "tenantid": {
            "type": "string",
            "is_array": false
        }
    }
}