m5c detections

Detections are source-controlled rules that produce findings or other detection results from contract-shaped data. They can be authored directly in Mach5 Detection IR or imported from foreign formats for review and lowering.

Where detection files live

m5c discovers detections from *.yaml files under a directory named detections inside packages.

A typical layout is:

packages/security-analytics/
  detection_families/
    identity-access.yaml
  detections/
    okta-password-spray.yaml
    github-repository-activity.yaml
  tests/
    expected_findings/
      okta-password-spray.yaml

Build a detection

Build a detection in this order:

  1. Choose a detection family and detector.
  2. Require the contracts and capabilities the rule needs.
  3. Write matchers against contract fields, not raw source fields.
  4. Set detector parameters.
  5. Define dedupe fields.
  6. Define finding output and entity fields.
  7. Add ATT&CK, lifecycle, promotion, and changelog metadata.
  8. Add expected-finding tests.
  9. Run m5c validate, m5c test, and m5c lower.

Detection document

kind: Detection
apiVersion: semantic-catalog.mach5.io/v1alpha1
metadata:
  name: okta-password-spray
  labels:
    source: native
  version: "0.3.0"
spec:
  id: M5-SA-OKTA-001
  title: Multiple failed Okta authentications from one source
  description: A single client IP produced repeated failed Okta authentication events in a short window.
  family: identity_access
  detector: aggregation_threshold
  level: medium
  confidence: 0.65
  enabled: true
  feature_gate: okta_identity_detections
  requires:
    contracts:
      identity.authentication_event.v1: ">=1.0.0 <2.0.0"
    capabilities:
      - identity.authentication_event.v1.principal_available
      - identity.authentication_event.v1.source_ip_available
      - identity.authentication_event.v1.status_available
  matchers:
    - field: status_id
      operator: eq
      value: 2
  parameters:
    group_by:
      - src_endpoint.ip
    min_count: 10
    window: 15m
  dedupe:
    strategy: windowed_entity
    fields:
      - detection_id
      - src_endpoint.ip
      - window_start
  output:
    finding_type: identity.password_spray
    entity:
      kind: ip
      field: src_endpoint.ip
  mitre_attack:
    - technique_id: T1110
      tactic: credential-access
  tags:
    - okta
    - identity
    - brute_force
  lifecycle:
    owner: security-detections@mach5.io
    updated_at: "2026-05-22"
    review_after: "2026-08-22"
    source: native
  promotion:
    stage: staging
    validation_status: runtime_smoke
    reviewer: secops-lead@mach5.io
    promoted_at: "2026-05-22T00:00:00Z"
  changelog:
    - version: "0.3.0"
      date: "2026-05-22"
      summary: Added aggregation threshold metadata and smoke-test evidence.

Top-level fields

FieldRequiredMeaning
kindYesMust be Detection.
apiVersionRecommendedCurrent examples use semantic-catalog.mach5.io/v1alpha1.
metadata.nameYesFile-friendly rule name.
metadata.versionRecommendedSemver version of this detection document.
metadata.labelsOptionalLabels for ownership, source, or grouping.
metadata.annotationsOptionalImport diagnostics and other non-schema metadata.
spec.idYesStable detection ID. Must be unique in the workspace.
spec.titleYesHuman-readable title.
spec.familyYesDetection family name.
spec.detectorYesDetector name within the family.
spec.requiresRecommendedContracts and capabilities needed by the rule.
spec.matchersRecommendedField/operator/value criteria.
spec.parametersDetector-dependentValues declared by the selected detector.
spec.dedupeOften requiredRequired when the selected detector has dedupe.required: true.
spec.outputRecommendedFinding type and primary entity.

Identity and state

Use metadata.name for source control and spec.id for stable rule identity.

metadata:
  name: okta-password-spray
spec:
  id: M5-SA-OKTA-001
  title: Multiple failed Okta authentications from one source

spec.enabled defaults to true when omitted. Set it explicitly for review workflows:

enabled: false
status: experimental

Common level values are informational, low, medium, high, and critical. confidence is a number between 0 and 1 by convention.

Family and detector

Every detection must point to a declared detector shape.

family: identity_access
detector: aggregation_threshold

m5c validate checks that the family exists and that the detector exists inside that family.

Contract and capability requirements

Detections require contracts by name and semver range.

requires:
  contracts:
    identity.authentication_event.v1: ">=1.0.0 <2.0.0"
  capabilities:
    - identity.authentication_event.v1.source_ip_available

The contract name must match a local DataContract.metadata.name. Capabilities must be defined by the target contract under spec.capabilities and referenced as contract.capability.

Write detections against contract fields and capabilities, never vendor raw fields.

Matchers

Matchers compare contract fields to values.

matchers:
  - field: status_id
    operator: eq
    value: 2

The field must exist in one of the required contracts. If operator is omitted or empty, it defaults to eq.

Common matcher shapes:

# equality
- field: status_id
  operator: eq
  value: 2

# list membership
- field: event_type
  operator: in
  value: [PushEvent, DeleteEvent]

# substring match
- field: repository.full_name
  operator: contains
  value: mach5-io/

Allowed operators come from the selected DetectionFamily. m5c validate rejects operators that are not listed by the detector.

Parameters

Parameters are detector-specific.

parameters:
  group_by:
    - src_endpoint.ip
  min_count: 10
  window: 15m

m5c validate checks that every provided parameter is declared by the detector and that values match supported parameter types such as int, duration, bool, or list<string>.

Dedupe

Dedupe controls stable finding or alert grouping.

dedupe:
  strategy: windowed_entity
  fields:
    - detection_id
    - src_endpoint.ip
    - window_start

If the detector requires dedupe, both strategy and non-empty fields are mandatory. Use fields that produce stable grouping without hiding distinct incidents.

Output

Output describes the generated finding type and primary entity.

output:
  finding_type: identity.password_spray
  entity:
    kind: ip
    field: src_endpoint.ip

The entity field should be a contract field. It is used by expected-finding tests and by downstream finding presentation.

ATT&CK, tags, and lifecycle

Use ATT&CK labels only when the rule has a defensible technique mapping.

mitre_attack:
  - technique_id: T1110
    tactic: credential-access
tags: [okta, identity, brute_force]
lifecycle:
  owner: security-detections@mach5.io
  updated_at: "2026-05-22"
  review_after: "2026-08-22"
  source: native

Promotion metadata records release state:

promotion:
  stage: staging
  validation_status: runtime_smoke
  reviewer: secops-lead@mach5.io
  promoted_at: "2026-05-22T00:00:00Z"

Expected-finding tests

Expected-finding tests are YAML files under a directory named expected_findings. They are separate from detection files.

kind: DetectionExpectedFindingsTest
spec:
  cases:
    - name: okta_password_spray_matches
      detection_id: M5-SA-OKTA-001
      input_ref: fixtures/okta-password-spray-events.json
      should_match: true
      expect:
        detection_id: M5-SA-OKTA-001
        entity_kind: ip
        entity_id: 203.0.113.10
        matched_event_count: 10

input_ref is resolved relative to the expected-finding test file. For event-match detectors, the input is usually one contract-shaped JSON object. For aggregation-style tests, the input is usually an array of contract-shaped JSON objects.

Run tests with:

m5c test apps/security-analytics --workspace

The human report includes:

expected findings: 12 case(s), 0 failed

Source-level expected-finding tests currently evaluate eq, in, contains, startswith, and endswith. Cover other operators and lowering-specific behavior with runtime smoke tests.

Lower detection output

m5c lower apps/security-analytics --dev --bindings bindings/dev.yaml --out lowered

Lowering plans enabled detections, resolves contract bindings and feature gates, writes generated bundles, and emits reports such as detection plan, coverage, lineage, freshness, promotion, and ATT&CK coverage where available.

Validate and test workflow

m5c validate apps/security-analytics --workspace --offline
m5c test apps/security-analytics --workspace
m5c lower apps/security-analytics --dev --out /tmp/m5c-lowered

If validation fails, check:

  • duplicate spec.id values;
  • unknown family or detector names;
  • contract names and semver ranges;
  • capability references;
  • matcher fields against required contracts;
  • matcher operators against the detector;
  • parameter names and types;
  • required dedupe fields.

Common mistakes

MistakeFix
Matching raw vendor fields such as outcome.resultMatch normalized contract fields such as status_id.
Using an alias under requires.contractsUse the contract name as the key and a semver range as the value.
Referencing a capability without the contract prefixUse identity.authentication_event.v1.status_available.
Using an operator not allowed by the familyAdd the operator to the family only if the lowering template supports it.
Omitting required detector parametersAdd values under spec.parameters.
Using unstable dedupe fieldsUse stable entity, event, or window fields.
Enabling imported or partial detections without reviewKeep them disabled until validated and tested.

Best practices

  • Require contracts and capabilities, not raw source names.
  • Keep matchers simple and reviewable.
  • Use stable dedupe fields.
  • Keep ATT&CK labels evidence-based.
  • Add lifecycle and promotion metadata before production use.
  • Add expected-finding tests for match and non-match cases.
  • Keep imported rules reviewable; do not treat unsupported imports as production-ready.

Analytics Cookies

Help us understand website usage.

Necessary storage remembers your choice. With your consent, Mach5 also uses PostHog analytics to measure website traffic and interactions.

Change this anytime from Cookie Settings in the footer. Privacy Notice.