name: keytos-ezca
description: A package for Keytos EZCA (cloud certificate authority) SIEM logs.
testCases:
  - event:
      rawString: '{"LogType":"EZCA_Certificates","EventID":4887,"CAID":"3f2c1b7a-1234-4a5b-9c8d-0e1f2a3b4c5d","DateRequested":"2026-07-22T15:04:05.123Z","SubjectName":"device01.contoso.com","Thumbprint":"A1B2C3D4E5F60718293A4B5C6D7E8F9012345678","TemplateID":"SSL Template","CertificatePEM":"-----BEGIN CERTIFICATE-----\nMIIBExampleBase64Data\n-----END CERTIFICATE-----","Requester":"user@contoso.com","CAFriendlyName":"Contoso Issuing CA","CertLocationType":"Azure Key Vault","CertLocation":"https://myvault.vault.azure.net/","CertNameAtLocation":"device01-cert","Action":"Create","EventSummary":"Certificate Services approved a certificate request and issued a certificate. Requester: user@contoso.com Subject: device01.contoso.com Thumbprint: A1B2C3D4E5F60718293A4B5C6D7E8F9012345678"}'
    assertions:
      - fieldsHaveValues:
          "#Vendor": "keytos"
          "#event.module": "ezca"
          "#event.kind": "event"
          "#event.dataset": "ezca_certificates"
          "event.category[0]": "configuration"
          "event.type[0]": "info"
          "event.id": "4887"
          "event.action": "Create"
          "user.name": "user@contoso.com"
          "user.email": "user@contoso.com"
          "keytos.ca.name": "Contoso Issuing CA"
          "x509.subject.common_name": "device01.contoso.com"
          "tls.server.hash.sha1": "A1B2C3D4E5F60718293A4B5C6D7E8F9012345678"
  - event:
      rawString: '{"LogType":"EZCA_CAs","EventID":4872,"CAID":"3f2c1b7a-1234-4a5b-9c8d-0e1f2a3b4c5d","DateRequested":"2026-07-22T18:00:00Z","Requester":"EZCA Automation","CAFriendlyName":"Contoso Issuing CA","Action":"Publish CRL","EventSummary":"A CRL for CA 3f2c1b7a-1234-4a5b-9c8d-0e1f2a3b4c5d was issued","RootCACertificate":"-----BEGIN CERTIFICATE-----\nMIICExampleRootData\n-----END CERTIFICATE-----","ObjectChanged":"{\"CAID\":\"3f2c1b7a-1234-4a5b-9c8d-0e1f2a3b4c5d\"}"}'
    assertions:
      - fieldsHaveValues:
          "#event.module": "ezca"
          "#event.dataset": "ezca_cas"
          "event.category[0]": "configuration"
          "event.type[0]": "info"
          "event.id": "4872"
          "event.action": "Publish CRL"
          "user.name": "EZCA Automation"
          "keytos.ca.name": "Contoso Issuing CA"
        fieldsNotPresent:
          - "user.email"
          - "x509.subject.common_name"
  - event:
      rawString: '{"EventID":0,"CAID":"NA","DateRequested":"2026-07-22T22:49:51.9422785Z","SubjectName":"NA","Thumbprint":"NA","TemplateID":"NA","CertificatePEM":"NA","Requester":"NA","CAFriendlyName":"NA","CertLocationType":"NA","CertLocation":"NA","CertNameAtLocation":"NA","Action":"Test Connection","EventSummary":"Testing SIEM Connection"}'
    assertions:
      - fieldsHaveValues:
          "#event.module": "ezca"
          "#event.dataset": "ezca"
          "event.category[0]": "configuration"
          "event.type[0]": "info"
          "event.id": "0"
          "event.action": "Test Connection"
          "event.reason": "Testing SIEM Connection"
        fieldsNotPresent:
          - "tls.server.hash.sha1"
fieldsToBeRemovedBeforeParsing: []
$schema: https://schemas.humio.com/parser/v0.3.0
script: |-
  // #region PREPARSE
  /************************************************************
  ****** Parse timestamp and log headers
  ****** Extract message field for parsing
  ****** Parse structured data
  ************************************************************/

  // parseJson captures EVERY field EZCA emits into Vendor.* automatically.
  parseJson(prefix="Vendor.", excludeEmpty=true, handleNull=discard)
  | case {
      Vendor.DateRequested = *
        | parseTimestamp(field=Vendor.DateRequested);
      *;
  }

  // #endregion

  // #region METADATA
  /************************************************************
  ****** Static Metadata Definitions
  ************************************************************/

  | Parser.version := "2.2.0"
  | Cps.version := "1.2.0"
  | ecs.version := "9.3.0"
  | Vendor := "keytos"
  | event.module := "ezca"
  | event.kind := "event"

  // #endregion

  // #region NORMALIZATION
  /************************************************************
  ****** Parse unstructured data (i.e. message field)
  ****** Normalize fields to data model
  ************************************************************/

  // Write EZCA values straight through.
  | event.id := Vendor.EventID
  | event.action := Vendor.Action
  | event.reason := Vendor.EventSummary
  | message := Vendor.EventSummary

  // event.dataset is driven by the record's own LogType value (lower-cased),
  // so a new EZCA log type becomes a new dataset on its own. When the transport
  // strips LogType, everything lands under a single "ezca" dataset.
  | case {
      Vendor.LogType = *
        | event.dataset := lower(Vendor.LogType);
      *
        | event.dataset := "ezca";
  }

  // CPS requires at least one event.category and event.type. This is a
  // passthrough, so use a single static category and a neutral "info" type.
  | array:append(array="event.category[]", values=["configuration"])
  | array:append(array="event.type[]", values=["info"])

  // Direct identity/certificate field writes.
  | user.name := Vendor.Requester
  | case { user.name = /@/ | user.email := lower(user.name); *; }
  | keytos.ca.name := Vendor.CAFriendlyName
  | x509.subject.common_name := Vendor.SubjectName
  // Only a valid 40-char SHA-1 goes into the typed hash field (CPS validates its
  // length); EZCA's "NA" placeholder and any malformed value are skipped.
  | case {
      Vendor.Thumbprint = /^[0-9A-Fa-f]{40}$/
        | tls.server.hash.sha1 := Vendor.Thumbprint
        | array:append(array="related.hash[]", values=[Vendor.Thumbprint]);
      *;
  }

  // #endregion

  // #region POST-NORMALIZATION
  /************************************************************
  ****** Post Normalization
  ****** Custom parser logic needed after normalization
  ************************************************************/

  // #endregion
tagFields:
  - Vendor
  - Cps.version
  - ecs.version
  - event.dataset
  - event.kind
  - event.module
  - observer.type
