
Review Integration
- 211 installs
- 15 repo stars
- Updated August 5, 2026
- elastic/integration-skills
Elastic skill reviewing existing integration package quality across pipelines mappings and manifests.
About
Elastic integration review skill for existing packages. Covers quality reviews of ingest pipelines, ecs.yml field mappings, CEL programs, package manifests, changelog entries, and test coverage. Identifies guideline violations, missing tests, ECS alignment issues, and documentation gaps before merge. Complements dashboard-review and maintain-integration skills with holistic package-level review criteria for PR approval of integration changes.
- Holistic quality review of existing integration packages
- Checks pipelines ecs.yml CEL manifests and changelog
- ECS alignment and test coverage assessment
- PR review criteria for integration package changes
- Identifies guideline violations before merge approval
Review Integration by the numbers
- 211 all-time installs (skills.sh)
- Ranked #334 of 1,352 Code Review & Quality skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
review-integration capabilities & compatibility
- Capabilities
- review pipelines · review field mappings · review cel programs · check manifest compliance
- Works with
- elasticsearch
- Use cases
- code review · security audit
npx skills add https://github.com/elastic/integration-skills --skill review-integrationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 211 |
|---|---|
| repo stars | ★ 15 |
| Last updated | August 5, 2026 |
| Repository | elastic/integration-skills ↗ |
Is this Elastic integration package ready for merge?
Review an existing Elastic integration package for quality across pipelines, field mappings, CEL programs, manifests, and changelog compliance.
Who is it for?
Reviewers evaluating Elastic integration PRs for package quality.
Skip if: Creating new integrations from scratch (use create-integration).
When should I use this skill?
User reviews an existing Elastic integration package or PR changes.
What you get
Review findings with pipeline, mapping, CEL, manifest, and test compliance assessment.
Files
review-integration
You are a skeptical, thorough quality reviewer for Elastic integrations. Your job is to find actionable issues only -- never praise code or confirm compliance. If a domain has no issues, say so in one line and move on.
Skill authority
The rules and patterns defined in the domain skills and their reference files are the authoritative source of truth. Existing integrations in elastic/integrations may contain legacy patterns that predate current standards. Always judge the integration under review against the skills, not against patterns found in other integrations.
When to use
- Reviewing an integration package for quality (any scope: full package, specific streams, specific domains)
- Invoked directly by a user in any agent environment (Cursor, Claude Code, Codex, etc.)
- Referenced by
maintain-integration-> review-workflow for delegated reviews
When NOT to use
- Building integrations (use
create-integration,cel-programs,ingest-pipelines, etc.) - Making fixes or improvements (use
maintain-integration) - Researching vendors (use
research-integration)
This skill is read-only. It produces findings. It does not edit files.
---
Reviewing new vs existing integrations
The domain skills state current standards as absolute rules (e.g., ecs.version: 9.3.0, format_version: "3.4.2", conditions.kibana.version: "^8.19.0 || ^9.1.0"). These are correct for building new integrations. When reviewing existing integrations, apply these severity adjustments:
Version-related rules
| Rule | New package | Existing package |
|---|---|---|
format_version | Must be "3.4.2" -- HIGH if different | Any version supporting all features used is acceptable. Only HIGH if features require a higher version than declared. |
conditions.kibana.version | Must be `"^8.19.0 \ | \ |
ecs.version in pipeline | Must be 9.3.0 -- HIGH if older | Any version is acceptable as long as it matches the build.yml ECS pin. Only HIGH if pipeline and build.yml are inconsistent with each other. |
build.yml ECS pin | Must be git@v9.3.0 -- HIGH if different | Must match pipeline ecs.version. Only HIGH if mismatch between the two, not because the version is older. |
Pattern-related rules
| Rule | New package | Existing package |
|---|---|---|
| Processor tags on all processors | MEDIUM if missing | LOW (improvement suggestion). Tags are only enforced by elastic-package check at format_version >= 3.6.0. |
| on_failure exact 3-step structure | HIGH if missing/wrong | Missing on_failure entirely: HIGH. Wrong structure/order: LOW (improvement). Full structure enforced from format_version >= 3.6.0. |
| CEL-only opening processors (agentless remove + terminate) | MEDIUM if missing for CEL streams | LOW (modernization suggestion). These are Agentless-era additions; pre-Agentless CEL integrations don't have them. |
| JSE00001 exact 2-processor pattern | HIGH if missing | Verify event.original is preserved (the concept). If the implementation differs from the exact current pattern but achieves the same result: MEDIUM, not HIGH. |
| ASN enrichment alongside geo enrichment | HIGH if geo present but ASN missing | MEDIUM (improvement suggestion). Geo+ASN pairing is a newer standard. |
preserve_duplicate_custom_fields pattern | HIGH (prohibited) | MEDIUM (technical debt). This was an officially recommended pattern before deprecation. Flag as HIGH only if the pipeline is being refactored in this change. |
base-fields.yml exactly 6 entries | HIGH if wrong | Verify minimum entries present (data_stream.type, data_stream.dataset, data_stream.namespace, @timestamp). Missing event.module or event.dataset: MEDIUM. |
beats.yml must exist | HIGH if absent | Not required for CEL or HTTPJSON input types (they don't emit log.offset). For file-based inputs: MEDIUM if absent. |
source.geo.* in dynamic_fields | MEDIUM | For existing integrations where updating format_version/conditions is not in scope, source.geo in dynamic_fields may be an acceptable workaround. Note as technical debt. |
How to determine new vs existing
Read the package's changelog.yml:
- One entry (version
0.0.1or1.0.0): this is a new package. Apply new-package standards. - Multiple entries: this is an existing package. Apply existing-package adjustments above.
If reviewing a PR that adds a new data stream to an existing package, apply new-package standards to the new data stream's files but existing-package standards to unchanged files.
---
Step 1: Determine scope
Identify what is being reviewed:
- Local package: user provides a package directory path. Read the root
manifest.yml, list all data streams and input types. - Changed files: user provides a list of changed files (e.g., from a PR or branch comparison). Classify each file by domain.
- User description: user describes what to review. Identify the relevant package and files.
If the user provides initial requirements, a research brief, or a task description, note what was requested for the "Requirements match" check.
Determine whether this is a new package or an existing package (see "Reviewing new vs existing integrations" above) to calibrate severity correctly.
Step 2: Classify files by domain
For every file in scope, classify into a domain:
| File pattern | Domain |
|---|---|
elasticsearch/ingest_pipeline/*.yml | pipeline |
fields/*.yml | fields |
agent/stream/*.yml.hbs | input |
manifest.yml (root or data stream) | manifest |
_dev/build/build.yml | build |
changelog.yml | changelog |
_dev/test/pipeline/* | tests |
_dev/test/system/* | tests |
kibana/**/*.json | dashboard |
_dev/build/docs/README.md | docs |
elasticsearch/transform/** | transform |
*-expected.json, sample_event.json | generated (skip review) |
Print which domains are present and how many files each has.
Step 3: Load domain skills and review checklists
Only load what the detected domains require. Do not load all skills for every review.
| Domain | Skill to load | Review checklist to load |
|---|---|---|
| pipeline | ingest-pipelines SKILL.md | checklists/pipeline-review-checklist.md |
| fields | ecs-field-mappings SKILL.md | checklists/field-review-checklist.md |
| input (CEL) | cel-programs SKILL.md | checklists/cel-review-checklist.md |
| input (HTTPJSON) | input-configurations SKILL.md -> references/httpjson-guide.md | checklists/httpjson-review-checklist.md |
| input (other types) | input-configurations SKILL.md -> matching type guide | input-configurations/references/common-input-patterns.md |
| manifest + changelog | package-spec SKILL.md | package-spec/references/manifest-rules.md |
| tests | integration-testing SKILL.md -> relevant testing reference | -- |
| dashboard | dashboard-review SKILL.md + dashboard-guidelines SKILL.md | dashboard-review/references/review-procedure.md |
| build | ecs-field-mappings SKILL.md | (ECS version pinning rules) |
| transform | this skill's references/transform-guide.md | (includes review checklist) |
| docs | (inline checklist below) | -- |
Step 3b: Always-load skills
Load these for every review regardless of which domains are present:
| Skill | Why |
|---|---|
elastic-package-cli SKILL.md | Validation commands (format, lint, check, test) and troubleshooting |
create-integration -> references/package-layout.md | Package topology, required files, directory structure, naming constraints |
anonymize-logs SKILL.md | Placeholder conventions (RFC 5737 IPs, example.com domains, synthetic UUIDs) for data anonymization checks |
Step 4: Load review-specific references
These references live in this skill's references/ directory and provide review-only procedures.
| Condition | Reference to load |
|---|---|
| Always | references/severity-rubric.md -- severity calibration across all domains |
| Always | references/conflict-resolutions.md -- known rule conflicts and resolution decisions |
| 2+ domains touched | references/consistency-rules.md -- cross-domain consistency (pipeline-fields-manifest-tests alignment) |
| CEL input files in scope | references/version-check-procedure.md + references/beats-mito-version-matrix.md + references/config-options-by-version.md + references/extensions-per-version.md |
| CEL input files in scope | references/cel-validator-procedure.md -- celfmt authority, type conversion audit, error shape validation |
| CEL or HTTPJSON with API docs available | references/api-conformance-methodology.md -- cross-reference implementation vs vendor docs |
| Any input templates in scope | references/input-review-orchestration.md -- review depth routing by input type |
| Cloud security / CDR integration | ecs-field-mappings/references/cdr-field-requirements.md + ingest-pipelines/references/cdr-pipeline-requirements.md + references/cdr-transform-requirements.md |
CDR detection: Check the root manifest.yml categories. If cloudsecurity_cdr is listed, the integration is CDR and all three CDR references must be loaded. Do NOT apply CDR rules to EDR/XDR integrations (crowdstrike, sentinel_one, trend_micro) unless they explicitly have cloudsecurity_cdr in their categories.
---
Step 5: Run automated validation
If you have access to the package on disk, run:
cd packages/<package_name>
elastic-package format --fail-fast
elastic-package lint
elastic-package checkIf pipeline or system tests are appropriate and a stack is available:
elastic-package test pipeline
elastic-package test systemRecord every failure with its full error message.
Step 6: Inspect and produce findings
For each file in scope (excluding generated files):
1. Read the full file for complete context 2. If reviewing a diff, read the diff hunks to understand what changed 3. Apply the relevant checklist items from the domain skills and review checklists 4. For every issue found, record:
- severity: critical, high, medium, or low
- domain: one of the domain tags below
- title: short description (10 words or fewer)
- path: file path relative to repo root
- line: line number in the file (use line 1 if unknown)
- description: what is wrong and why it matters
- recommendation: how to fix -- include a code block showing the corrected YAML/CEL/JSON
Cross-file checks
After individual file inspection, check cross-domain consistency (load references/consistency-rules.md if not already loaded):
- Fields set in pipeline processors must be declared in
fields/ecs.ymlunless the field is a standard ECS keyword/date type that works via dynamic mapping build.ymlECS version must matchecs.versionset in pipeline- Manifest variables must be referenced in stream templates
- Data stream manifest must not duplicate root manifest fields (
format_version,conditions) - Pipeline test fixtures must cover every branch
sample_event.jsonmust be system-test-generated or absent with{{ event }}commented out
Read unchanged files from the workspace if needed for cross-referencing.
---
Output format
Write the review to `tmp/integration-review.md` in the current working directory. Create the tmp/ directory if it does not exist. Also present the full review in your response so the user sees the findings directly without needing to open the file.
Read references/review-output-template.md for the exact output format and rendering rules. The template defines: per-domain sections, per-issue format (title, severity, location, problem, recommendation with code block), suggestions, summary table, and verdict. Use the same format for both the file and the response.
Verdict rules
- Any critical or high finding ->
NEEDS_CHANGES - Only medium/low findings ->
APPROVED_WITH_SUGGESTIONS - No findings ->
APPROVED
Domain tags
Every issue must include exactly one domain tag:
| Tag | Covers |
|---|---|
domain:manifest | Root or data stream manifest fields, format_version, conditions, categories, owner, policy templates |
domain:changelog | changelog.yml schema, version mismatch, missing entries, invalid links |
domain:build | _dev/build/build.yml missing or outdated, doc template issues |
domain:pipeline | Ingest pipeline correctness, JSE00001, on_failure, tags, ECS categorization in pipeline |
domain:input | Agent stream template issues -- all input types including CEL, HTTPJSON, AWS S3, TCP, etc. |
domain:fields | Field definitions, types, duplicates, geo nesting, ECS mapping strategy |
domain:tests | Pipeline test fixtures, system test configs, test-common-config.yml, sample_event.json |
domain:dashboard | Kibana dashboard JSON at package root (kibana/), TSVB, dataset filters, by-reference panels |
domain:transform | Transform configuration at package root (elasticsearch/transform/), sync, field definitions, CDR |
domain:docs | README content, placeholder text, title/description quality |
domain:anonymization | Real data in committed files, non-synthetic IPs/hostnames/credentials |
domain:consistency | Cross-domain issues: pipeline-fields mismatch, build.yml-pipeline ECS mismatch, unused manifest vars |
Severity levels
- CRITICAL: broken functionality, security vulnerabilities, missing required files, build/lint failures, infinite loops
- HIGH: quality standard violations -- must fix before merge
- MEDIUM: suboptimal patterns, missing edge cases, documentation gaps -- fix when possible
- LOW: style issues, minor improvements -- nice to have
Load references/severity-rubric.md for domain-specific calibration and references/conflict-resolutions.md for known inter-rule conflicts.
Important rules
- Never include positive observations in findings
- Every issue must have a file path and line number
- Every recommendation must include a code block showing the corrected code
- Consolidate duplicates: merge same issue found in multiple files
- If a domain was reviewed and has no issues, write one line: "✅ Reviewed — No actionable issues found."
- If a domain is not in scope, omit it entirely
Review discipline
- Every finding must cite a concrete, present-tense bug with evidence in the code under review — not a hypothetical. If the description relies on "what if the API changes" or "in a future scenario," the finding lacks evidence and should be dropped.
- Do NOT flag
validation.ymlexclusions (managed by package author, not a review concern) - Do NOT suggest adding processors for vendor-handled fields (e.g., suggesting
redactfor passwords the vendor already masks) - Do NOT flag hypothetical security risks without evidence of actual exposure in the code
---
Reference files
| File | Load condition | Content |
|---|---|---|
references/reviewer-subagent-guidance.md | Read by the reviewer subagent itself (the orchestrator passes only its path, never embeds the content) | Scope, skill-load sequence, read-only operating rules, per-issue format checklist, verdict rules, reporting contract for the orchestrator-dispatched reviewer |
references/review-output-template.md | Always | Output format template, rendering rules, severity mapping |
references/severity-rubric.md | Always | CRITICAL/HIGH/MEDIUM/LOW definitions with domain-specific calibration |
references/conflict-resolutions.md | Always | Known rule conflicts and resolution decisions |
references/consistency-rules.md | 2+ domains | Cross-domain consistency rules (pipeline-fields-manifest-tests) |
references/version-check-procedure.md | CEL in scope | 5-step systematic version verification procedure |
references/beats-mito-version-matrix.md | CEL in scope | Full beats-to-mito version mapping (160+ entries) |
references/config-options-by-version.md | CEL in scope | CEL config option introduction by beats version |
references/extensions-per-version.md | CEL in scope | Registered mito extensions per beats version |
references/cel-validator-procedure.md | CEL in scope | celfmt authority, type conversion audit, error shape validation |
references/api-conformance-methodology.md | CEL/HTTPJSON + API docs | Cross-referencing implementation vs vendor API documentation |
references/input-review-orchestration.md | Any input templates | Review depth routing by input type |
references/transform-guide.md | Transform in scope | Transform types, config, fields, sync, review checklist |
references/cdr-transform-requirements.md | CDR transforms | CDR latest transform requirements, destination naming, keys, retention |
checklists/pipeline-review-checklist.md | Pipeline in scope | Severity-tagged pipeline review checklist |
checklists/field-review-checklist.md | Fields in scope | Severity-tagged field mapping review checklist |
checklists/cel-review-checklist.md | CEL in scope | Severity-tagged CEL review checklist |
checklists/httpjson-review-checklist.md | HTTPJSON in scope | Severity-tagged HTTPJSON review checklist |
CEL review checklist
Severity-tagged checklist for reviewing CEL input programs. Use for self-review before submitting a PR, or for formal review. Items are grouped by domain. For systematic version compatibility verification, load the review-integration skill's version check references.
Structure
- [ ]
interval: {{interval}}uses Handlebars variable, not hardcoded -- HIGH if hardcoded - [ ] Request tracer path matches input type:
../../logs/cel/http-request-trace-*.ndjson. Two formats: conditional{{#if enable_request_tracer}}(any version) or blockresource.tracer:withenabled:field (v8.15.0+). Tracer should be at data stream level, not input level -- MEDIUM if wrong path or wrong level - [ ]
stateinitializes all fields the program reads -- HIGH if program reads uninitialized state - [ ]
redact.fieldslists every secret key in state. Useredact.fields: ~if none. Cross-check against anysecret_fieldsin manifest -- HIGH if secrets not redacted - [ ]
max_executionsdefaults to 1000 if not specified. Only flag if the default is inappropriate for the specific API (e.g., API with known infinite pagination bugs). Do NOT flag absence as a routine finding -- LOW if missing with no evidence of need
State and cursor
- [ ]
want_moreis explicitly set on ALL execution paths (success, error, empty response, pagination complete) -- HIGH if any path omits want_more - [ ]
want_morederives from API response (next cursor present, has_more field, etc.), NOT fromsize(events) > 0or event count -- HIGH if gated on event count (causes missed pages) - [ ] Cursor values only persist when at least one event is published. If empty pages with new cursors are expected, emit a placeholder event and drop it in the pipeline -- MEDIUM if empty-page cursor scenario not handled
- [ ] Optional access patterns: use
.?field,.orValue(default),has(obj.field)for fields that may be absent -- MEDIUM if direct access on optional fields - [ ] On error paths, cursor handling must match the error shape:
- Single-object
{"events": {"error": {...}}}— cursor is intentionally NOT preserved (agent retries from last known good state). Do NOT flag cursor absence here. - Array
{"events": [{"error": {...}}]}— cursor MUST be preserved (event indexed, processing continues). HIGH if cursor lost with array error shape.
Error handling
- [ ] HTTP status code checked after every request (
resp.StatusCode != 200or similar) -- HIGH if no status check - [ ] Error shape: single-object
{"events": {"error": {"code": "...", "id": "...", "message": "..."}}, "want_more": false}= retry/halt semantics (cursor deleted, agent retries). Array{"events": [{"error": {"code": "...", "id": "...", "message": "..."}}], "want_more": false}= advance semantics (cursor preserved, event indexed). Choose shape based on whether the error is recoverable -- HIGH if error events use wrong shape for the scenario - [ ]
want_moreis false on all error paths -- CRITICAL if want_more true on error (causes infinite error loop)
Pagination
- [ ] Pagination terminates: there must be a condition that sets
want_more: false(empty response, no next cursor, max page reached) -- CRITICAL if no termination condition - [ ] Pagination continuation uses API response fields (next_cursor, has_more, etc.), not event count -- HIGH if
want_more: size(events) > 0 - [ ] For timestamp-based cursors, cursor defaults use
nowglobal for upper bound, notnow()function -- MEDIUM if usingnow()(unstable within evaluation)
Authentication transport
- [ ] Config-level auth (
auth.oauth2,auth.digest,auth.aws,auth.file,auth.custom) applies to direct HTTP calls (get(),post()) ANDdo_request()calls - [ ] If using
do_request()withauth.basicorauth.tokenconfig: these are NOT applied todo_request()calls (only to direct calls). Must set auth headers manually in the request map fordo_request()-- HIGH if relying on config auth that is not applied - [ ] For new integrations targeting v8.19.0+: prefer
auth.customfor static Bearer/Token auth over manual header construction -- LOW informational
Type safety
- [ ] All CEL numbers are float64 (IEEE 754 double). Integers >= 10^7 will appear in scientific notation. Integers >= 2^53 lose precision -- HIGH if large integer IDs not handled
- [ ] Pipeline must use
convertprocessor for fields that need specific types (long, double). CEL delivers everything as JSON numbers which Elasticsearch may interpret differently -- MEDIUM if numeric fields lack convert processor - [ ] String IDs that look numeric (e.g., Snowflake IDs, large event IDs) should be kept as strings in CEL using
.string()or extracted from the string representation -- HIGH if precision-sensitive IDs treated as numbers
Code quality
- [ ]
.as()nesting depth does not exceed 5 levels fromstate.with()inward. HTTP core should be 2 levels:do_request().as(resp, decode_json().as(body, {...}))insidestate.with()-- HIGH if exceeds 5 - [ ] Cursor defaults and window/time-range bounds extracted as pre-bindings BEFORE
state.with(), not stacked inside the HTTP chain -- MEDIUM if pre-bindings inside HTTP chain - [ ] Single-use values (e.g.,
int(state.batch_size)used once) are inlined at the call site, not wrapped in their own.as()binding -- LOW
Configuration
- [ ]
redact.fieldsandsecret_fields(if present in manifest) are consistent. Every secret in manifest should be redacted in CEL config -- HIGH if mismatch - [ ] No Handlebars
{{syntax inside theprogram:block. Usestateto pass manifest variables into the CEL program -- CRITICAL if Handlebars in program (breaks CEL compilation) - [ ] Request tracer: prefer data stream level
enable_request_tracervariable over input-level. Default value should befalse-- MEDIUM if at wrong level
Formatting
- [ ] celfmt output is the canonical formatting authority. Do not flag or "fix" formatting that celfmt produces. If unsure about syntax, run
celfmt -agent -i cel.yml.hbs -o cel.yml.hbs-- informational
Rate limiting
- [ ] It is valid to call
rate_limit()and ignore the return value (for logging/diagnostics). Only flag rate limiting issues when: API docs show rate limit headers AND the integration does not handle them AND incorrect arguments are passed torate_limit()-- MEDIUM if genuinely wrong, do not over-flag - [ ] Before v9.3.0:
rate_limit()return must be placed instate.rate_limitfor the limit to take effect (next cycle only). From v9.3.0: immediate apply, state placement optional -- HIGH if targeting pre-v9.3.0 and return not in state
Version awareness
If the program uses a function or config option introduced after v8.6.0, verify that conditions.kibana.version in the root manifest allows a high enough beats version. See cel-polymorphic-patterns.md for version-tagged patterns. For systematic version verification during formal reviews, use the review-integration skill's version check references.
Field mapping review checklist
Build configuration
- [ ]
_dev/build/build.ymlexists when any field files are present -- HIGH if missing - [ ] ECS reference pinned to
git@v9.3.0-- HIGH if wrong version - [ ] ECS pin consistent with
ecs.versionset in pipeline -- HIGH if mismatch
base-fields.yml
- [ ] Exists in every data stream's
fields/directory -- HIGH if missing - [ ] Exactly 6 entries:
data_stream.type,data_stream.dataset,data_stream.namespace,event.module,event.dataset,@timestamp-- HIGH if wrong entries - [ ] All 6 entries use
external: ecs(all are ECS fields; descriptions inherited from ECS) -- HIGH if missing - [ ]
event.moduleandevent.datasetoverridetype: constant_keywordwithvalue-- MEDIUM - [ ]
event.modulevalue matches package name -- MEDIUM - [ ]
event.datasetvalue matches<package>.<stream>-- MEDIUM - [ ]
@timestampuses single-quoted key'@timestamp'withexternal: ecs-- MEDIUM
ECS fields (ecs.yml)
- [ ] Every ECS field set in the pipeline is listed in
fields/ecs.ymlusingname+external: ecs-- HIGH if missing - [ ]
external: ecsappears on every field whose name exists in ECS, regardless of which field file it is in -- HIGH if missing - [ ] No extra metadata on ECS fields beyond
nameandexternal: ecs(except where type/value overrides are explicitly needed) -- LOW
Custom fields (fields.yml)
- [ ] Correct types:
keywordfor identifiers,long/doublefor numbers,datefor timestamps,ipfor IP addresses -- HIGH if wrong type - [ ] Vendor-namespaced under appropriate group (not at root level) -- MEDIUM
- [ ] No
geo_pointfields at root level -- geo fields nested under parent entities (source.geo,destination.geo, etc.) -- HIGH if at root - [ ] No duplicate field definitions across field files -- MEDIUM
- [ ] Every declared field should be written by the pipeline -- MEDIUM if orphan declaration
Field descriptions
- [ ] Every field has a
descriptionset -- LOW if empty - [ ] Descriptions are meaningful, not just the field name restated -- LOW
ECS categorization values
- [ ]
event.kind: onlyalert,asset,enrichment,event,metric,state,pipeline_error,signal-- HIGH if invalid - [ ]
event.category: only allowed values (api, authentication, configuration, database, driver, email, file, host, iam, intrusion_detection, library, malware, network, package, process, registry, session, threat, vulnerability, web) -- HIGH if invalid - [ ]
event.type: only allowed values (access, admin, allowed, change, connection, creation, deletion, denied, device, end, error, group, indicator, info, installation, protocol, start, user) -- HIGH if invalid - [ ]
event.outcome: onlyfailure,success,unknown-- HIGH if invalid - [ ] Values are semantically appropriate for the data source -- MEDIUM
When reviewing a diff
Check: are new fields from changed pipeline processors declared? Are removed pipeline fields cleaned up from declarations? Do type changes in the pipeline match field type declarations?
HTTPJSON review checklist
Severity-tagged checklist for reviewing httpjson.yml.hbs templates. Items are grouped by template section. Severity levels: CRITICAL, HIGH, MEDIUM, LOW.
Structure
- [ ]
intervaluses Handlebars variable ({{interval}}) -- HIGH - [ ] Request tracer path matches the input type name -- MEDIUM
- [ ] Request tracer uses correct format for target stack version (conditional vs block) -- LOW
- [ ] SSL/proxy/timeout blocks present when needed -- LOW
Request
- [ ] URL uses Handlebars variable for base URL -- MEDIUM
- [ ] Headers configured correctly for the target API -- MEDIUM
- [ ] Query parameters match API documentation -- HIGH
- [ ] Date formats use Go reference time correctly (
2006-01-02T15:04:05Z) -- MEDIUM - [ ]
request.methodmatches API requirements (GET vs POST) -- HIGH
Response
- [ ]
response.split.targetpoints to the correct array field -- HIGH - [ ]
ignore_empty_value: trueon split -- MEDIUM - [ ]
keep_parent: truepresent when nested splits need parent fields -- MEDIUM - [ ] Map-type split used when response is an object, not an array -- MEDIUM
Pagination
- [ ] Has termination condition (conditional evaluating to empty string when done) -- CRITICAL
- [ ]
fail_on_template_error: trueon all pagination set transforms with conditionals -- HIGH - [ ] Time parameters preserved during pagination (not overwritten by pagination transforms) -- HIGH
- [ ] POST requests include
response.request_body_on_pagination: true-- HIGH - [ ] Offset/page math is correct (zero-index vs one-index) -- MEDIUM
- [ ] Pagination pattern matches API documentation -- HIGH
Cursor
- [ ] Tracks the correct field from the API response -- HIGH
- [ ]
ignore_empty_value: trueon cursor entries -- MEDIUM - [ ] Empty page scenario handled if API returns cursors with empty data arrays -- MEDIUM
- [ ] Initial value set via
defaulton request transforms for first poll cycle -- MEDIUM - [ ] First-page-only update used when appropriate (to avoid cursor advancing past data) -- LOW
Authentication
- [ ] Auth method matches API documentation -- HIGH
- [ ] No hardcoded credentials in the template -- CRITICAL
- [ ] Mutual exclusion correct when multiple auth methods are supported -- MEDIUM
- [ ] OAuth2 scopes and endpoint params match API requirements -- MEDIUM
- [ ] Credential variables use
type: passwordin manifest -- HIGH
Common patterns
- [ ]
preserve_original_eventis conditional ({{#if}}) -- MEDIUM - [ ]
forwardedtag andpublisher_pipeline.disable_hostare coupled -- MEDIUM - [ ] Custom processors passthrough at top level -- LOW
- [ ] All user-configurable values use Handlebars variables -- HIGH
Ingest pipeline review checklist
Severity-tagged checklist. Each item: what to check, violation criteria, severity.
Prohibited patterns (quick reject)
- [ ] Pipeline does NOT set
event.ingested-- managed by Elasticsearch -- HIGH - [ ] Pipeline does NOT use
preserve_duplicate_custom_fieldstag pattern -- HIGH - [ ] Pipeline does NOT have a trailing
removeprocessor deletingevent.originalbased on absence ofpreserve_original_eventtag -- deprecated pattern -- HIGH
JSE00001 (event.original preservation)
- [ ] Rename
messagetoevent.original(if absent) with description and tag -- HIGH if missing - [ ] Remove
messagewhenevent.originalis set, with description and tag -- HIGH if missing - [ ] No additional processors modifying
event.originalafter the initial rename -- MEDIUM
Pipeline-level on_failure
- [ ] on_failure block present at pipeline level -- HIGH if missing
- [ ] First action: append full
error.messageusing_ingest.on_failure_*template variables -- HIGH if wrong template - [ ] Second: set
event.kind: pipeline_error-- HIGH if missing - [ ] Third: append
preserve_original_eventtotags-- MEDIUM if missing - [ ] Order matters: error.message THEN event.kind THEN tags -- MEDIUM if wrong order
ECS version
- [ ] Pipeline sets
ecs.version: 9.3.0-- HIGH if older version
Processor-level checks
- [ ] Every processor has a
tagfield -- MEDIUM if missing - [ ] ECS field mapping uses
renamewhere possible, notset+copy_fromduplication -- MEDIUM - [ ] Mustache template syntax uses triple braces with single quotes:
'{{{field.name}}}'not double braces"{{field.name}}"(double braces HTML-escape). Exception:{{ IngestPipeline "..." }}is Go template, not Mustache -- MEDIUM - [ ] Grok patterns anchored with
^...$for full-line matching -- MEDIUM - [ ]
@timestampset from a parsed date field, not left as default ingest time -- HIGH if no date parsing - [ ] Type conversions: numeric fields use
convertprocessor for long/double -- MEDIUM
Enrichment
- [ ] geoip and user_agent processors have an
ifcondition checking field existence (e.g.,if: ctx.source?.ip != null), not justignore_missing: true. Theignore_missingoption checks too late and still incurs lookup cost -- MEDIUM - [ ] When geoip is used for geolocation (e.g.,
source.geo), there must be a companion ASN lookup usingdatabase_file: GeoLite2-ASN.mmdbtargeting*.as, followed by renames*.as.asnto*.as.numberand*.as.organization_nameto*.as.organization.name-- HIGH if geo enrichment present but ASN missing - [ ]
related.ippopulated with every IP field the pipeline sets, one append per field,allow_duplicates: false, guarded byifcondition -- HIGH if IP fields not in related.ip
ECS categorization
- [ ]
event.kind,event.category,event.type,event.outcomeuse only allowed ECS values -- HIGH if invalid values - [ ]
event.categoryandevent.typeare arrays: must useappendprocessor, notset-- HIGH if using set - [ ] Categorization values are semantically appropriate for the data (not just valid but correct) -- MEDIUM
CEL-only opening processors
- [ ] If stream uses CEL input:
removeprocessor for agentless metadata (organization,division,team) when all are strings, followed byterminateprocessor on collector error placeholder shape -- MEDIUM if missing for CEL streams - [ ] These processors must NOT be present for non-CEL streams -- MEDIUM if present for wrong input type
Performance
- [ ] Cheap checks (conditionals, renames) before expensive operations (grok, geoip, user_agent) -- LOW
- [ ] Dissect preferred over grok when the format is fixed/delimited -- LOW
Security
- [ ] No hardcoded sensitive values in processors -- CRITICAL
When reviewing a diff
Prioritize: the changed processors, their error handling (do they have processor-level on_failure or at least correct pipeline-level handling?), and their field declarations (are the new fields declared in fields/ files?).
API conformance methodology
Procedure for cross-referencing input implementation against vendor API documentation.
When to run
When the PR modifies input templates (cel.yml.hbs, httpjson.yml.hbs) AND API documentation is available (linked in PR description, commit messages, manifest variable descriptions, or data stream README).
What to cross-reference
For each API endpoint the integration calls:
| Aspect | What to verify |
|---|---|
| Endpoint URL | Path matches API docs (correct version, correct resource) |
| HTTP method | GET/POST/PATCH matches what the API expects |
| Query parameters | Required params included, names match docs |
| Request headers | Content-Type, Accept, custom headers match docs |
| Authentication | Method matches API requirements |
| Pagination style | cursor/offset/page/link matches API's documented pagination |
| Response schema | split target matches documented response body structure |
| Rate limit headers | Header names match vendor's rate limit implementation |
Conformance table output
For each endpoint, produce:
| Endpoint | Aspect | Expected (from docs) | Found (in implementation) | Status |
|---|---|---|---|---|
| /api/v2/events | Pagination | cursor-based, nextToken field | cursor-based, body.nextToken | PASS |
| /api/v2/events | Rate limit | X-RateLimit-Remaining header | Not handled | FAIL |
Limitations
- Not applicable when no API docs are available
- Do not speculate about undocumented behavior
- If the API docs are ambiguous, note the ambiguity rather than flagging a finding
Beats to mito version matrix
Generated from the beats repo go.mod at each release tag. Use this table to find the first beats release shipping a given mito version.
| beats | mito |
|---|---|
| v8.6.0 | v0.0.0-20221025013803-2ab0322317ce |
| v8.6.1 | v0.0.0-20221025013803-2ab0322317ce |
| v8.6.2 | v0.0.0-20221025013803-2ab0322317ce |
| v8.7.0 | v0.0.0-20230302005114-1dda06e81678 |
| v8.7.1 | v0.0.0-20230302005114-1dda06e81678 |
| v8.8.0 | v0.0.0-20230302005114-1dda06e81678 |
| v8.8.1 | v0.0.0-20230302005114-1dda06e81678 |
| v8.8.2 | v0.0.0-20230302005114-1dda06e81678 |
| v8.9.0 | v1.4.0 |
| v8.9.1 | v1.4.0 |
| v8.9.2 | v1.5.0 |
| v8.10.0 | v1.5.0 |
| v8.10.1 | v1.5.0 |
| v8.10.2 | v1.5.0 |
| v8.10.3 | v1.6.0 |
| v8.10.4 | v1.6.0 |
| v8.11.0 | v1.6.0 |
| v8.11.1 | v1.6.0 |
| v8.11.2 | v1.6.0 |
| v8.11.3 | v1.6.0 |
| v8.11.4 | v1.6.0 |
| v8.12.0 | v1.7.0 |
| v8.12.1 | v1.7.0 |
| v8.12.2 | v1.7.0 |
| v8.13.0 | v1.9.0 |
| v8.13.1 | v1.9.0 |
| v8.13.2 | v1.9.0 |
| v8.13.3 | v1.9.0 |
| v8.13.4 | v1.9.0 |
| v8.14.0 | v1.10.0 |
| v8.14.1 | v1.10.0 |
| v8.14.2 | v1.10.0 |
| v8.14.3 | v1.10.0 |
| v8.15.0 | v1.13.1 |
| v8.15.1 | v1.13.1 |
| v8.15.2 | v1.13.1 |
| v8.15.3 | v1.13.1 |
| v8.15.4 | v1.13.1 |
| v8.15.5 | v1.13.1 |
| v8.16.0 | v1.15.0 |
| v8.16.1 | v1.15.0 |
| v8.16.2 | v1.15.0 |
| v8.16.3 | v1.15.0 |
| v8.16.4 | v1.15.0 |
| v8.16.5 | v1.15.0 |
| v8.16.6 | v1.15.0 |
| v8.17.0 | v1.15.0 |
| v8.17.1 | v1.15.0 |
| v8.17.2 | v1.15.0 |
| v8.17.3 | v1.15.0 |
| v8.17.4 | v1.15.0 |
| v8.17.5 | v1.15.0 |
| v8.17.6 | v1.15.0 |
| v8.17.7 | v1.15.0 |
| v8.17.8 | v1.15.0 |
| v8.17.9 | v1.15.0 |
| v8.17.10 | v1.19.0 |
| v8.18.0 | v1.17.0 |
| v8.18.1 | v1.19.0 |
| v8.18.2 | v1.19.0 |
| v8.18.3 | v1.19.0 |
| v8.18.4 | v1.19.0 |
| v8.18.5 | v1.19.0 |
| v8.18.6 | v1.19.0 |
| v8.18.7 | v1.19.0 |
| v8.18.8 | v1.19.0 |
| v8.19.0 | v1.22.0 |
| v8.19.1 | v1.22.0 |
| v8.19.2 | v1.22.0 |
| v8.19.3 | v1.22.0 |
| v8.19.4 | v1.22.0 |
| v8.19.5 | v1.22.0 |
| v8.19.6 | v1.22.0 |
| v8.19.7 | v1.22.0 |
| v8.19.8 | v1.23.0 |
| v8.19.9 | v1.23.0 |
| v8.19.10 | v1.23.0 |
| v8.19.11 | v1.23.1 |
| v8.19.12 | v1.23.1 |
| v8.19.13 | v1.23.1 |
| v8.19.14 | v1.23.1 |
| v9.0.0 | v1.17.0 |
| v9.0.1 | v1.19.0 |
| v9.0.2 | v1.19.0 |
| v9.0.3 | v1.19.0 |
| v9.0.4 | v1.19.0 |
| v9.0.5 | v1.19.0 |
| v9.0.6 | v1.19.0 |
| v9.0.7 | v1.19.0 |
| v9.0.8 | v1.19.0 |
| v9.1.0 | v1.22.0 |
| v9.1.1 | v1.22.0 |
| v9.1.2 | v1.22.0 |
| v9.1.3 | v1.22.0 |
| v9.1.4 | v1.22.0 |
| v9.1.5 | v1.22.0 |
| v9.1.6 | v1.22.0 |
| v9.1.7 | v1.22.0 |
| v9.1.8 | v1.22.0 |
| v9.1.9 | v1.22.0 |
| v9.1.10 | v1.22.0 |
| v9.2.0 | v1.23.0 |
| v9.2.1 | v1.23.0 |
| v9.2.2 | v1.23.0 |
| v9.2.3 | v1.23.0 |
| v9.2.4 | v1.23.1 |
| v9.2.5 | v1.23.1 |
| v9.2.6 | v1.23.1 |
| v9.3.0 | v1.24.0 |
| v9.3.1 | v1.24.0 |
| v9.3.2 | v1.24.0 |
| v9.3.3 | v1.25.0 |
| v9.3.4 | v1.25.0 |
| v9.3.5 | v1.25.1 |
9.4 branch (unreleased): v1.25.1
CDR transform requirements
Cloud security integrations must implement "latest" transforms that maintain a current-state view of findings. Each CDR data stream needs a latest transform for its finding type.
Aligned with: Elastic CDR 3P Developer Guide v1.0
Misconfiguration latest transform
Destination index
dest:
index: "security_solution-<integration>.misconfiguration_latest-<version>"
aliases:
- alias: "security_solution-<integration>.misconfiguration_latest"
move_on_creation: trueThe alias with move_on_creation is required so Kibana always reads from the latest version when the transform is recreated during upgrades.
Unique key and sort
latest:
unique_key:
- resource.id
- rule.uuid
- data_stream.namespace
sort: "@timestamp"The combination of resource.id + rule.uuid + data_stream.namespace aligns with the native CSP integration. rule.uuid is chosen over rule.id because the identifier must be unique across all benchmarks.
The fields above are the baseline. Some integrations vary: google_scc and microsoft_defender_cloud add event.id as a fourth key. Evaluate unique key composition case by case based on the source data's natural identity.
Frequency
5m
Sync
sync:
time:
field: event.ingestedCDR transforms sync on event.ingested (not @timestamp) because the fleet final pipeline guarantees this field is set on ingestion. The delay field is optional; when omitted, ES defaults to 60s. Most real CDR transforms omit it.
Retention policy
retention_policy:
time:
field: "@timestamp"
max_age: "2160h"CDR sources always have a meaningful @timestamp (the finding's event time), so retention uses @timestamp to expire findings whose event time is stale. If @timestamp were absent, event.ingested would be the fallback.
The retention value depends on the data source:
- Full-evaluation APIs: use the evaluation interval
- Incremental APIs: use 90d (2160h) as an interim solution
- Custom interval: must not exceed the
retention_policymax_age
Settings
settings:
unattended: true
_meta:
fleet_transform_version: "0.1.0"
managed: trueunattended: true is required for auto-recovery. Without it, a failed transform stays stopped and findings go stale without operator intervention.
Bump fleet_transform_version when any transform code changes to trigger delete + reinstall + restart during package upgrade.
Source query patterns
CDR transforms should filter the source to exclude error documents and cold/frozen tiers:
source:
index:
- "logs-<integration>.<datastream>-*"
query:
bool:
filter:
- term:
event.kind: state
must_not:
- exists:
field: error.message
- terms:
_tier:
- data_frozen
- data_colderror.messageexclusion prevents ingestion-error documents from appearing in the latest view_tierexclusion prevents scanning cold/frozen storage (performance)- For full-evaluation APIs, an optional
@timestamprange filter (e.g.,gte: "now-26h") limits the scan window to match the evaluation interval
Vulnerability latest transform
Destination index
dest:
index: "security_solution-<integration>.vulnerability_latest-<version>"
aliases:
- alias: "security_solution-<integration>.vulnerability_latest"
move_on_creation: trueUnique key, sort, and remaining config
latest:
unique_key:
- vulnerability.id
- resource.id
- package.name
- package.version
- data_stream.namespace
sort: "@timestamp"The combination of vulnerability.id + resource.id + package.name + package.version + data_stream.namespace aligns with native CSP. Some integrations vary: Wiz uses vulnerability.package.name and vulnerability.package.version instead of package.name/package.version. AWS Inspector uses a custom aws.inspector.transform_unique_id. Evaluate case by case based on the source data.
Frequency, sync, retention, and settings are the same as misconfiguration transforms (see above).
Field mapping for latest index
The latest index needs explicit field definitions. Key type overrides:
- name: data_stream.namespace
type: keyword # NOT constant_keyword -- accommodates multiple namespaces
external: ecs
- name: observer.vendor
type: constant_keyword # Performance optimisation for constant values
external: ecs
- name: vulnerability.scanner.vendor
type: constant_keyword
external: ecs
- name: vulnerability.published_date
type: date # Not covered by ecs@mappingsFrom stack version 8.19/9.1, the ecs@mappings component template applies to both source and destination index templates. Before 8.19, ECS fields must be explicitly mapped with external: ecs in the destination index fields.
Known CDR integrations
Packages with cloudsecurity_cdr manifest category: aws_securityhub, cloud_asset_inventory, cloud_security_posture, google_scc, m365_defender, microsoft_defender_cloud, microsoft_defender_endpoint, prisma_cloud, qualys_vmdr, rapid7_insightvm, snyk, tenable_io, tetragon, wiz
CDR transform review checklist
- [ ] Destination index follows
security_solution-{integration}.{type}_latest-{version}pattern -- HIGH if wrong - [ ] Alias defined with
move_on_creation: true-- HIGH if missing - [ ] Unique key matches finding type (
resource.id+rule.uuid+namespacefor misconfiguration;vulnerability.id+resource.id+package.name+package.version+namespacefor vulnerability) -- HIGH if wrong - [ ] Sort on
@timestamp-- MEDIUM - [ ] Source query excludes
error.messagedocuments and cold/frozen tiers -- MEDIUM - [ ] Frequency is 5m -- LOW
- [ ] Sync on
event.ingested-- HIGH if wrong field. Delay is optional (ES defaults to 60s) -- LOW if omitted - [ ]
retention_policypresent on@timestampwith appropriatemax_age-- HIGH if missing - [ ]
unattended: truein settings -- HIGH if missing - [ ]
fleet_transform_versionin_meta-- HIGH if missing - [ ]
data_stream.namespacemapped askeyword(notconstant_keyword) -- HIGH if wrong type - [ ]
observer.vendormapped asconstant_keyword-- MEDIUM
CEL validator procedure
Review-time validation checks beyond the public checklist. Apply these during every formal CEL PR review.
celfmt formatting authority
celfmt output is canonical. Never flag formatting that celfmt produces as incorrect. If a reviewer is unsure about CEL syntax or formatting, the correct action is to run celfmt, not to manually reformat. The formatted output IS the standard.
Consequences:
- If the PR's CEL program matches celfmt output, formatting is correct regardless of how it looks.
- If the PR's CEL program differs from celfmt output, request the author run celfmt.
- Do not suggest manual formatting changes that contradict celfmt.
Type conversion audit
All CEL numbers are IEEE 754 float64:
- Integers >= 10^7 appear in scientific notation in JSON output (e.g.,
10000000becomes1e+07) - Integers with magnitude >= 2^53 (9,007,199,254,740,992) lose integer round-trip precision (both positive and negative)
- This affects any numeric value that passes through
encode_jsonor appears in the output event
Check for:
- Large integer IDs (Snowflake IDs, Discord IDs, Twitter/X IDs, Box IDs)
- Timestamps in milliseconds (13-digit epoch values)
- Large counters, sequence numbers, or offsets
- Fields destined for ES
longorkeywordmappings
Pattern table for suspicious type usage
| Pattern | Risk | Action |
|---|---|---|
int(value) used as ID | Precision loss if > 2^53 | Flag HIGH if ID can exceed 2^53 |
| Timestamp as int (ms since epoch) | Scientific notation | Flag MEDIUM, recommend string format |
| Counter > 10^7 | Scientific notation in JSON | Flag MEDIUM, ensure pipeline handles it |
| String ID converted to int | Unnecessary precision risk | Flag MEDIUM, keep as string |
| Float arithmetic for currency | Rounding errors | Flag HIGH, recommend integer cents or string |
Recommendation: String IDs should stay strings. Numeric fields that may exceed safe integer range need either string representation in CEL or a convert processor in the ingest pipeline.
Error shape validation
CEL programs return events to the agent via the events field. The shape of the error event determines recovery behavior:
Object shape (retry semantics):
{"events": {"error": {"code": "...", "id": "...", "message": "error text"}}, "want_more": false}- Cursor is deleted. The agent retries from the last known good cursor on the next evaluation.
- Use for transient errors: HTTP 429, 500, 502, 503, network timeouts.
Array shape (advance semantics):
{"events": [{"error": {"code": "...", "id": "...", "message": "error text"}}], "want_more": false}- Cursor is preserved. The error event is indexed as a document and the cursor advances.
- Use when the pipeline has a terminate processor that drops error events, or when error events should be visible in the index.
Validation checks:
- Verify the shape matches the intended recovery behavior.
- If the integration has a terminate processor in the ingest pipeline for error events, the CEL program should use array shape so the event reaches the pipeline.
- If the integration wants the agent to retry on transient errors (429, 500), use object shape.
- A program that uses object shape for 4xx client errors (other than 429) is likely a bug -- client errors are not transient and will retry forever.
- Check that
want_moreis set tofalsein error paths.
secret_fields vs redact cross-check
If the data stream manifest declares secret: true for any variable, that variable's value in state must be listed in redact.fields in the CEL config.
Procedure: 1. Read the data stream manifest.yml. Find all vars entries with secret: true. 2. Read the CEL config (cel.yml.hbs). Find the redact.fields list. 3. For each secret variable, verify its state path appears in redact.fields. 4. If redact.fields is ~ (null/empty), verify that no secret variables exist in state. 5. If a secret variable is in state but not in redact.fields, flag it.
Common state paths for secrets:
state.header.Authorization(for auth tokens passed via state)state.api_key,state.secret_key,state.password- Any state field populated from a
secret: truemanifest variable
Handlebars-in-program detection
The program: | block in cel.yml.hbs must NOT contain {{ or }} Handlebars template syntax.
Manifest variables should be passed into the initial state map via Handlebars in the state block, then accessed as state.* fields within the CEL program. Handlebars inside the program block causes:
- CEL parse errors when the template renders unexpected types
- Difficulty testing the program outside the integration context
- Security issues if user-controlled values are interpolated into the program text
Detection:
- If
{{appears inside theprogram: |block: flag as CRITICAL. - The
state:block and other config fields may legitimately use{{-- only theprogramblock is restricted. - False positive:
{{inside a CEL string literal that is not Handlebars (extremely rare). Verify by checking if the surrounding context is a Handlebars expression ({{variable_name}}).
CEL config options by beats version
Lookup table for determining when each CEL input config option was introduced. Use this during review to verify that the integration's declared conditions.kibana.version is compatible with all config options it uses.
Top-level config options
| Option | First beats version | Description |
|---|---|---|
interval | v8.6.0 | Polling interval |
program | v8.6.0 | CEL program text |
state | v8.6.0 | Initial state map |
regexp | v8.6.0 | Named regexp patterns for Regexp extension |
auth | v8.6.0 | Authentication configuration |
resource | v8.6.0 | Resource (HTTP client) configuration |
redact | v8.6.0 | Field redaction configuration |
max_executions | v8.9.0 | Maximum evaluation cycles per interval |
limits | v8.16.0 | Rate limit policies |
Resource sub-config options
| Option | First beats version | Description |
|---|---|---|
resource.url | v8.6.0 | Target URL |
resource.ssl | v8.6.0 | TLS/SSL settings |
resource.timeout | v8.6.0 | HTTP request timeout |
resource.keep_alive | v8.6.0 | HTTP keep-alive settings |
resource.retry | v8.6.0 | Retry configuration |
resource.redirect | v8.6.0 | Redirect policy |
resource.rate_limit | v8.6.0 | Per-resource rate limit |
resource.tracer | v8.9.0 | Request/response debug tracing |
resource.transport_security | v8.16.0 | Transport security mode |
Auth sub-config options
| Option | First beats version | Description |
|---|---|---|
auth.basic | v8.6.0 | Basic auth (username/password) |
auth.oauth2 | v8.6.0 | OAuth2 client credentials / token |
auth.digest | v8.16.0 | HTTP digest auth |
auth.custom | v8.16.0 | Custom auth headers via template |
Cumulative config set as of v9.3.0
All options above are available as of v9.3.0. The minimum version floor for each config combination is determined by the latest "First beats version" among all options used:
- Uses only v8.6.0 options: minimum is v8.6.0
- Uses
max_executions: minimum is v8.9.0 - Uses
resource.tracer: minimum is v8.9.0 - Uses
limits,auth.digest,auth.custom, orresource.transport_security: minimum is v8.16.0
How to use
1. List every config option the integration uses (top-level, resource, auth). 2. Look up the "First beats version" for each option in the tables above. 3. Take the maximum (latest) version across all options used. 4. Verify that conditions.kibana.version in the root manifest.yml allows that beats version or later. 5. If the manifest declares a lower version than required, flag it.
Example: An integration using auth.digest and resource.tracer requires v8.16.0 (digest is the binding constraint). If the manifest says ^8.9.0, that is incorrect -- must be ^8.16.0 or later.
Conflict resolutions
Build skills (loaded in Step 3) are prescriptive — they teach the current recommended way to build integrations. The review skill must accept a broader range of valid patterns, including older approaches that predate current standards. This file documents where the review interpretation diverges from the build prescription and why.
state.with() absence
Conflict: The cel-programs skill teaches state.with() as the standard pattern for state construction. Review guidance historically rated its absence as HIGH.
Resolution: state.with() is the recommended pattern for new code, but if a program constructs a complete state map without it, this is valid. Only flag as HIGH if state construction is incomplete (missing cursor, missing want_more, missing events).
ECS field declarations vs dynamic mapping
Conflict: The ecs-field-mappings skill says pipeline fields must be declared in fields/ecs.yml. But standard ECS keyword/date fields work via dynamic mapping and don't require explicit external: ecs declarations.
Resolution: Only flag when the field type genuinely requires explicit declaration (geo_point, geo_shape, nested, flattened) or when elastic-package would fail validation. Standard keyword/date ECS fields are not findings.
rate_limit() in CEL programs
Conflict: The cel-programs skill says "Do NOT implement rate limiting in the CEL program" and directs authors to use YAML-level resource.rate_limit.* instead. But many existing integrations call rate_limit() directly in the program, and this is a valid, functioning pattern.
Resolution: Do not flag rate_limit() usage in existing integrations. Only flag when ALL of: (1) API docs show rate limit headers, (2) the integration does not handle rate limiting at all, (3) rate_limit() is called with incorrect arguments. Ignoring the return value is valid. From v9.3.0, the return no longer needs to be placed in state for the limit to be applied.
Build-skill authoring process vs product correctness
Conflict: Build skills include both runtime requirements and authoring-process guidance. Runtime requirements (e.g., mito compatibility — mito is the library Elastic CEL programs execute on, not generic CEL) are product correctness concerns. Authoring-process rules (e.g., "write no more than 10-15 lines before testing," reference loading order, incremental development methodology) guide how to produce code, not what correct code looks like.
Resolution: Runtime requirements from build skills are valid review concerns — a CEL program that doesn't work on mito is defective. Authoring methodology and workflow sequencing are not review findings. Review evaluates the product artifact, not how it was produced.
Cross-domain consistency rules
Rules that span multiple skills. Each rule specifies which files to compare.
Pipeline to fields consistency
- Every field SET by a pipeline processor (rename target, set target, append target, convert target) must have a declaration in
fields/ecs.yml(if ECS) orfields/fields.yml(if custom). - Every field DECLARED in field files should be written by the pipeline. Declared-but-never-written fields indicate stale declarations or missing pipeline logic.
- Field types must match: a field extracted as a number must not be declared as
keywordunless specifically needed for range queries.
Build config to pipeline consistency
_dev/build/build.ymlmust exist when field files are present.- ECS reference pin in build.yml must match the
ecs.versionvalue set in the pipeline. For new packages the current standard isgit@v9.3.0/ecs.version: 9.3.0. For existing packages, any ECS version is acceptable as long as the pipeline and build.yml are consistent with each other. Only flag HIGH if there is a mismatch between the two, not because the version is older than the current standard.
Manifest to template consistency
- Every variable declared in data stream
manifest.ymlmust be referenced in at least one stream template (agent/stream/*.yml.hbs). - No unused variables (declared but never
{{variable_name}}in any template). - Variable names in manifest must match exactly what the template references.
Root manifest to data stream manifest
- Data stream
manifest.ymlmust NOT set its ownformat_versionorconditions-- these belong only in the root manifest. - Root manifest
format_versionshould be"3.4.2"for new packages. For existing packages, the minimum version that supports all features used is acceptable. Flag as HIGH if the version is too low for features used or if a new package uses anything other than the current standard. - Root manifest
conditions.kibana.version-- for new packages should be"^8.19.0 || ^9.1.0". For existing packages, verify the constraint supports all agent features the package uses (CEL functions, config options, input types). Only flag HIGH if features require a higher version than declared, not merely because the constraint is older than the current standard.
Test coverage
- Pipeline test fixtures must cover every branch: if a router pipeline sends to sub-pipelines, each sub-pipeline needs test input.
- Input fixtures follow naming:
test-<package>-<datastream>-<type>-sample.log(or.json). test-common-config.ymlmust includefields.tags: [preserve_original_event].source.geo.*fields should NOT be indynamic_fields. For new packages, fix by ensuringformat_versionandconditions.kibana.versionare current. For existing packages where updating those versions is not in scope,source.geoindynamic_fieldsmay be an acceptable workaround -- note as technical debt.- Expected output files should be generated, not hand-written.
Sample event
sample_event.jsonmust be system-test-generated, not hand-crafted.- If absent,
{{ event "stream" }}must be commented out in_dev/build/docs/README.md.
Registered mito extensions per beats version
Which CEL extensions are registered in each beats release. A function is only available if its extension is registered AND the shipped mito version includes that function.
Extension registration table
| First beats version | Extensions added | Cumulative set |
|---|---|---|
| v8.6.0 | Collections, Crypto, File, Globals, HTTP, JSON, Limit, MIME, Regexp\*, Time, Try | Collections, Crypto, File, Globals, HTTP, JSON, Limit, MIME, Regexp\*, Time, Try |
| v8.7.0 | Strings | + Strings |
| v8.9.0 | XML | + XML |
| v8.11.0 | Debug | + Debug |
| v8.18.0 / v9.0.0 | Printf | + Printf |
| v8.19.0 / v9.1.0 | AWS | + AWS |
Full set as of v9.3.0: AWS, Collections, Crypto, Debug, File, Globals, HTTP, JSON, Limit, MIME, Printf, Regexp\*, Strings, Time, Try, XML.
Notes
- Regexp is conditional: only registered when the
regexpconfig block is present in the integration config. If a CEL program usesre_match,re_find, etc., the integration must have aregexpconfig section. - Globals provides variables (
now,useragent,env,remaining_executions), not functions. It is always registered. - Coverage and Dump appear in mito's extension list but are diagnostic/testing aids, not registered in the beats CEL input. Do not count them as available extensions.
- Limit registration changed at v9.3.0: registered via
LimitWithApplyinstead ofLimit, enabling in-evaluation rate limit updates. This is a behavioral change, not a new extension.
Mito lib extensions by version
What exists in the mito library at each tagged version. This is distinct from what beats registers -- mito may define extensions that beats never registers.
| mito version | Extensions in lib |
|---|---|
| v1.0.0 | Collections, Coverage, Crypto, Debug, Dump, File, Globals, HTTP, JSON, Limit, MIME, Regexp, Strings, Time, Try, XML |
| v1.4.0 | (no new extensions) |
| v1.5.0 | (no new extensions) |
| v1.6.0 | (no new extensions) |
| v1.7.0 | (no new extensions) |
| v1.9.0 | (no new extensions) |
| v1.10.0 | (no new extensions) |
| v1.13.1 | (no new extensions) |
| v1.15.0 | (no new extensions) |
| v1.16.0 | + Printf |
| v1.17.0 | (no new extensions) |
| v1.19.0 | (no new extensions) |
| v1.21.0 | + AWS |
| v1.22.0 | (no new extensions) |
| v1.23.0 | (no new extensions) |
| v1.24.0 | (no new extensions) |
| v1.25.0 | (no new extensions) |
| v1.25.1 | (no new extensions) |
The key distinction: mito v1.16.0 added Printf to the library, but beats did not register it until v8.18.0 / v9.0.0. Similarly, mito v1.21.0 added AWS, but beats did not register it until v8.19.0 / v9.1.0. Always check both the mito version (for function existence) and the beats version (for extension registration).
Input review orchestration
How to route different input types through appropriate review depths.
Review depth by input type
| Input type | Public skill to load | Private skill to load | Review depth |
|---|---|---|---|
| CEL | cel-programs + checklists/cel-review-checklist.md | review-integration references (version matrices, validator procedure) | Deep: version matrices, validator procedure, API conformance |
| HTTPJSON | input-configurations -> httpjson-guide.md + checklists/httpjson-review-checklist.md | API conformance (if docs available) | Medium: 10 validation rules, pagination, cursor persistence |
| AWS S3 | input-configurations -> aws-s3-guide.md | -- | Standard: common patterns + type-specific guide |
| HTTP Endpoint | input-configurations -> http-endpoint-guide.md | -- | Standard |
| WebSocket | input-configurations -> websocket-guide.md | -- | Standard (check for CEL program inside WebSocket) |
| TCP/UDP | input-configurations -> tcp-udp-guide.md | -- | Standard |
| Other types | input-configurations -> matching guide | -- | Standard: common patterns + type-specific guide |
CEL-capable types
WebSocket and HTTP Endpoint inputs can contain embedded CEL programs (detected by program: key in the YAML). When detected, also load the CEL version matrices and validator procedure from review-integration references and apply CEL review depth.
Common patterns (always)
Always load input-configurations/references/common-input-patterns.md regardless of type. Check: tags, forwarded/disable_host coupling, processors passthrough, no hardcoded values.
Review output template
Use this format when writing tmp/integration-review.md.
Template
# Integration Review: {package_name}
## Automated Validation
- format: PASS/FAIL
- lint: PASS/FAIL (list errors)
- check: PASS/FAIL (list errors)
- test pipeline: PASS/FAIL/SKIPPED (N passed, M failed)
- test system: PASS/FAIL/SKIPPED
---
## Package Root
Covers root `manifest.yml`, `changelog.yml`, `_dev/build/build.yml`, and
package-level structure. Only include if root-level files are in scope.
### Manifest
[If no actionable issues: "✅ *Reviewed — No actionable issues found.*"]
[Otherwise:]
**Issue 1: {title}**
**Severity:** {severity_emoji} {severity}
**Location:** `{file_path}` line {line_number}
**Problem:** {description}
**Recommendation:**{corrected code}
### Changelog
[Same per-issue format. Only include if changelog.yml is in scope.]
### Build Configuration
[Same per-issue format. Only include if _dev/build/build.yml is in scope.]
---
## Data Stream: `{data_stream_name}`
Repeat this section for each data stream in scope. Each data stream may have
findings in multiple sub-domains.
### Manifest
[Data stream manifest.yml issues. Same per-issue format.]
### Input
[Agent stream template issues -- covers CEL, HTTPJSON, AWS S3, TCP/UDP, and
all other input types under agent/stream/*.yml.hbs. Same per-issue format.]
### Pipeline
[Ingest pipeline issues under elasticsearch/ingest_pipeline/. Same per-issue
format.]
### Field Mapping
[Field files under fields/. Same per-issue format.]
### Tests
[Pipeline and system test issues under _dev/test/. Same per-issue format.]
**Suggestions**
1. {non-critical suggestion}
2. ...
---
[Repeat the "Data Stream" section above for each data stream in scope.]
---
## Dashboards
Covers kibana/ assets at the package root. Only include if kibana/**/*.json
files are in scope.
[Same per-issue format.]
---
## Transforms
Covers elasticsearch/transform/ at the package root. Only include if
transform files are in scope.
[Same per-issue format.]
---
## Documentation
Covers _dev/build/docs/README.md and other doc files. Only include if
documentation files are in scope.
[Same per-issue format.]
---
## Data Anonymization
Cross-cutting: covers real data found in any committed file (test fixtures,
mock responses, sample events, default manifest values). Only include if
anonymization issues were found.
[Same per-issue format.]
---
## Cross-Domain Consistency
Issues spanning multiple files or domains (pipeline fields not declared in
ecs.yml, build.yml ECS pin mismatch, unused manifest variables, etc.). Only
include if cross-domain issues were found.
[Same per-issue format.]
---
## Summary
| Severity | Count |
|----------|-------|
| 🔴 Critical | {count} |
| 🟠 High | {count} |
| 🟡 Medium | {count} |
| 🔵 Low | {count} |
**Total Actionable Items:** {total}
**Verdict:** {APPROVED / APPROVED_WITH_SUGGESTIONS / NEEDS_CHANGES}Per-issue format
Every issue must follow this structure:
**Issue N: {title}**
**Severity:** {severity_emoji} {severity}
**Location:** `{file_path}` line {line_number}
**Problem:** {clear description of what is wrong and why it matters}
**Recommendation:**{corrected code showing the full processor/block/section -- must be copy-pasteable}
Rendering rules
1. FILTER OUT positive comments. Never include "no issues found", "compliant", "done well", "excellent", "correct" as findings. 2. KEEP ONLY actionable items: issues, warnings, errors, suggestions, recommendations. 3. CONSOLIDATE duplicates: merge the same issue found in multiple files into one finding. 4. PRESERVE code snippets: keep all YAML/CEL/JSON examples in recommendations. Show the full processor, field definition, or config block -- not just the changed line. 5. NUMBER issues sequentially within each section: "Issue 1:", "Issue 2:". 6. GROUP findings by location in the package hierarchy: package root sections first, then per-data-stream, then dashboards/transforms/docs/anonymization/cross-domain. 7. OMIT sections not in scope entirely. Do not create empty sections for domains not reviewed. 8. REQUIRE **Location:** for every issue with exact file path and line number. If line number is unknown, use line 1. 9. EVERY recommendation must include a code block showing the corrected code. 10. If a section was reviewed and has no issues, write one line: "✅ Reviewed — No actionable issues found." 11. OMIT confidence scores, uncertainty areas, and internal review metadata from the output.
Severity values
- 🔴 Critical
- 🟠 High
- 🟡 Medium
- 🔵 Low
Verdict rules
- Any critical or high finding -> NEEDS_CHANGES
- Only medium/low findings -> APPROVED_WITH_SUGGESTIONS
- No findings -> APPROVED
Integration reviewer subagent guidance
Operating manual for a subagent running a read-only quality review of an Elastic integration on behalf of the create-integration or maintain-integration orchestrator.
The orchestrator dispatches you with a brief task prompt that points you at this file by path. Read this entire file end-to-end before doing any other work, then read the skills and reference files listed in the "First steps" section below — they are mandatory. The orchestrator does not paste this file's content into your task prompt (to avoid burning context twice); you load it here in your own fresh context.
The orchestrator's task prompt tells you which package to review, any user-provided requirements or research brief, which automated validation results are already known (so you do not re-run them unnecessarily), and any focus areas or specific concerns. This file tells you how to operate as the integration reviewer. Follow both.
Scope
Your responsibility is strictly limited to:
- Running automated validation if it has not already been run (or if you
need to re-verify a specific failure)
- Loading the
review-integrationskill and following its phases end to
end against the package, set of changed files, or scope the orchestrator hands you
- Reading the full content of every file in scope (not just diffs or
hunks) so you can find issues the orchestrator's incremental view cannot
- Producing a severity-ranked, domain-tagged findings report in the
exact output format defined by review-integration/references/review-output-template.md
- Writing the findings to
tmp/integration-review.mdin the current
working directory and returning the same content in your task reply so the orchestrator sees it directly
You do NOT:
- Edit, create, or delete any files in the package under review — this
workflow is read-only. If a fix is obvious, describe it in the recommendation block; do not apply it.
- Modify
sample_event.json,*-expected.json, ingest pipelines,
field files, CEL programs, or any other artifact. Fixes are the orchestrator's responsibility (handled directly or routed to the pipeline / CEL program / setup / system-test subagents).
- Re-run pipeline tests or system tests when the orchestrator has
already confirmed which tests passed — accept the orchestrator's statement and only re-run when you find concrete evidence that the reported result is wrong.
- Praise the integration, summarise what works correctly, or add
"no issues — done well" notes. The output contains actionable findings only. If a domain has no actionable issues, write one line ("Reviewed -- no actionable issues found.") and move on.
- Truncate, summarise, or skip the reviewer's findings in the response.
Present them in full both in tmp/integration-review.md and in the task reply.
If the orchestrator's prompt asks you to fix issues rather than identify them, stop and report that the wrong subagent or guidance file was invoked — fixes belong to the builder/orchestrator, not the reviewer.
Skill authority
The rules and patterns defined in the review-integration skill and all the domain skills it routes to are the authoritative source of truth. When examining existing integrations in the elastic/integrations repository for patterns, many contain legacy patterns that predate current standards — always judge the integration under review against the skills, not against patterns observed in other integrations. If a reference integration uses a deprecated or prohibited pattern, flag any reproduction of it.
First steps — load the review skill and what it routes to
Before inspecting any file, load these skills in order. Do not shortcut this step — the review-integration skill is the dispatcher that tells you which domain skills, checklists, and review-specific references to load for the scope you are reviewing.
1. `review-integration` skill (SKILL.md) — read fully. This is your operating skill: it defines the phases, the file→domain classification table, the always-load skills, the conditional references, the output format, the severity calibration rubric for new vs existing packages, and the domain tag list every finding must carry.
2. Apply the skill's Step 2 file classification to the scope the orchestrator gave you, then load:
- Every domain skill the classification surfaces (see
review-integration/SKILL.md → "Step 3: Load domain skills and review checklists")
- Every domain review checklist under
review-integration/checklists/
- The always-load skills under "Step 3b: Always-load skills"
(elastic-package-cli, create-integration → references/package-layout.md, anonymize-logs)
- Every review-specific reference under
review-integration/references/ whose load condition is met (severity-rubric.md and conflict-resolutions.md always; consistency-rules.md whenever 2+ domains are touched; CEL references when CEL input is in scope; CDR references when cloudsecurity_cdr appears in root manifest categories; etc.)
Do not assume the skills will load automatically. Read each file you identify before you begin inspection — the SKILL.md summaries alone are not sufficient. The reference files contain the working code examples, severity tables, and version matrices you need to make correct findings.
How to operate
The phases below are operational rules for running as a subagent. The substantive review steps live in review-integration/SKILL.md — follow that skill's Step 1 → Step 6 sequence, with the following subagent-specific operating rules layered on top.
Determine new vs existing first
Before inspecting any file, read changelog.yml and decide whether this is a new package (single entry at 0.0.1 / 1.0.0) or an existing package (multiple entries). The review-integration skill's "Reviewing new vs existing integrations" table and the references/severity-rubric.md "new-vs-existing" adjustments must be applied to every version, manifest, and pattern-related finding. Calibrating these wrong is the most common review error.
For a PR that adds a new data stream to an existing package, apply new-package standards to the new stream's files and existing-package standards to unchanged files.
Trust the orchestrator's validation results, verify only when needed
If the orchestrator told you which elastic-package format / lint / check / test pipeline / test system runs already passed, do not re-run them by default. Re-run only when your manual inspection surfaces concrete evidence that a previously-reported result is wrong, or when no result was reported at all. When you do run a command, record the full error message — never paraphrase.
Read full files, not just diffs
For every file in scope, read it end to end before recording findings. Reviews based on diffs alone miss prohibited patterns and ECS violations elsewhere in the same file. When the orchestrator gives you a diff, also read the unchanged surrounding context — the recommendation in each finding has to fit the actual file shape.
Follow the per-issue format exactly
Every finding must include the seven required components defined in review-integration/references/review-output-template.md:
- title (≤ 10 words)
- severity (Critical / High / Medium / Low — calibrated per
new-vs-existing)
- location (file path + line number; use line 1 if truly
unknown)
- problem (what is wrong and why it matters)
- recommendation (with a copy-pasteable code block showing the
corrected YAML / CEL / JSON / Painless — never just prose)
- domain tag (exactly one from the table in
review-integration/SKILL.md → "Domain tags")
- issue number within its section ("Issue 1:", "Issue 2:", …)
Findings without a code-block recommendation are not acceptable. "Add error handling" is not a finding — show the processor or branch that needs to be added.
Consolidate duplicates, omit empty domains
When the same issue appears in multiple files (e.g. missing tag on multiple processors across multiple pipelines), merge into a single finding that lists every affected file. Do not repeat the same finding once per file.
If a domain was reviewed and has no actionable findings, write the single line Reviewed -- no actionable issues found. under that section. If a domain is not in scope at all, omit the section entirely rather than creating an empty one.
Apply first-version leniency where the rule says so
review-integration/references/conflict-resolutions.md resolves the first-version-leniency conflict: for first-version packages (0.0.1 / 1.0.0 with a single changelog entry), placeholder changelog links (pull/0) and placeholder logos/icons are informational notes only, not findings. Do not flag them at MEDIUM or HIGH. For subsequent versions, the same placeholders are real findings (MEDIUM or HIGH as appropriate).
CEL-specific operating rules
When CEL input is in scope, the review-integration skill requires loading several CEL-specific references (cel-validator-procedure.md, version-check-procedure.md, beats-mito-version-matrix.md, config-options-by-version.md, extensions-per-version.md, and the cel-review-checklist.md). Follow cel-validator-procedure.md for celfmt authority — never flag formatting that celfmt produces as a finding. Use the version references to verify every CEL function and config option against the conditions.kibana.version in the root manifest before flagging a "wrong version" issue.
Verdict rules
Apply these strictly — the orchestrator routes follow-up work based on the verdict:
- Any Critical or High finding →
NEEDS_CHANGES - Only Medium or Low findings →
APPROVED_WITH_SUGGESTIONS - No findings →
APPROVED
Do not soften the verdict because the package "is close" or "mostly works". The orchestrator will accept APPROVED_WITH_SUGGESTIONS and move on; downgrading a real Critical/High finding to keep the verdict green hides issues the user is paying you to surface.
Data anonymization findings
Treat any real production data, customer data, or identifiable information in committed files as a finding under domain:anonymization:
- IP addresses outside RFC 5737 (
198.51.100.x,203.0.113.x,
192.0.2.x) / RFC 3849 (2001:db8::/32)
- Hostnames outside
example.com/example.org/example.local - Real email addresses, person names, organisation names, tenant or
account IDs, API keys, tokens, credentials
- Real vendor URLs with customer-specific subdomains in default
manifest var values
Flag at Critical when found. Placeholder values must preserve the format/structure of the data they replace (a synthetic UUID for a UUID, not REDACTED). Refer to the anonymize-logs skill for the full placeholder convention list before deciding whether a value is synthetic enough.
What to return
When you finish:
1. Write the findings to tmp/integration-review.md in the current working directory (create tmp/ if needed). Use the exact format from review-integration/references/review-output-template.md. 2. Return the same content in full as your task reply, plus a short header summarising:
- Package name and scope (full package / specific streams /
specific domains)
- New vs existing package determination and the basis (changelog
entry count)
- Which automated validation commands you re-ran and their
results (or "trusted orchestrator's reported results" if you did not re-run)
- Total findings by severity (`X Critical, Y High, Z Medium,
W Low`)
- Verdict (
APPROVED/APPROVED_WITH_SUGGESTIONS/
NEEDS_CHANGES) 3. Do not include positive observations, "things done well" sections, confidence scores, or internal review metadata in either the file or the task reply. Findings only. 4. Do not truncate or summarise the findings list. Present every issue with its full per-issue block (title, severity, location, problem, recommendation code block, domain tag).
The orchestrator uses your domain-tagged findings to route fixes — mis-tagged or missing-tagged findings cause the wrong subagent to be re-dispatched. Re-check the domain tag on every finding before submitting.
Severity rubric
Severity definitions
CRITICAL: Broken functionality, security vulnerabilities (hardcoded secrets, leaked credentials), missing required files that cause elastic-package build/lint/check failures, infinite loops (pagination without termination, want_more true on error paths).
HIGH: Quality standard violations that should be fixed before merge -- missing error handling, wrong ECS categorization values, no test coverage, prohibited patterns (event.ingested in pipeline, preserve_duplicate_custom_fields, trailing event.original remove), missing ASN enrichment alongside geo enrichment, secrets not redacted, version compatibility violations.
MEDIUM: Suboptimal patterns that should be fixed when possible -- .as() nesting depth 6-7, set instead of rename for ECS mapping, missing grok anchoring, wrong Mustache syntax (double braces instead of triple), missing edge case coverage, documentation gaps, tracer at wrong level.
LOW: Style issues and minor improvements -- variable naming, field description wording, sprintf vs concatenation preference, informational notes about first-version leniency.
Domain-specific calibration
These severities apply to new packages. For existing packages, see the "Reviewing new vs existing integrations" section in review-integration/SKILL.md for adjustments.
Note: "Could be newer" or "below current standard" is never a finding by itself. Only flag version fields when a feature in the package requires a higher version than declared.
Universal rules (same severity regardless of package age)
| Domain | Finding | Severity |
|---|---|---|
| Pipeline | event.ingested set in pipeline | HIGH |
| Pipeline | event.original removal at end of pipeline | HIGH |
| Pipeline | Double-brace Mustache instead of triple | MEDIUM |
| Pipeline | Unanchored grok pattern | MEDIUM |
| CEL | want_more true on error path | CRITICAL |
| CEL | No pagination termination | CRITICAL |
| CEL | Handlebars in program block | CRITICAL |
| CEL | Secrets not in redact.fields | HIGH |
| CEL | Verify error shape matches intended recovery behavior | MEDIUM |
| CEL | .as() depth exceeds 5 (hard cap) | HIGH |
| CEL | Single-use .as() binding | LOW |
| Fields | Pipeline field not in ecs.yml (non-dynamic-mapped type) | HIGH |
| Fields | Wrong field type | HIGH |
| Fields | Missing field description | LOW |
| Fields | build.yml ECS pin mismatches pipeline ecs.version | HIGH |
| Manifest | format_version too low for features used | HIGH |
| Manifest | conditions.kibana.version too low for agent features used | HIGH |
| Manifest | Data stream duplicates root manifest fields | MEDIUM |
| Tests | No pipeline test fixtures | HIGH |
| Tests | Missing test-common-config.yml | HIGH |
| Input | Hardcoded credentials | CRITICAL |
| Input | Hardcoded URL | MEDIUM |
| Input | Missing forwarded/disable_host coupling | MEDIUM |
Rules with new-vs-existing severity adjustment
| Domain | Finding | New package | Existing package |
|---|---|---|---|
| Pipeline | Missing pipeline-level on_failure | HIGH | Missing entirely: HIGH. Wrong structure/order: LOW |
| Pipeline | preserve_duplicate_custom_fields tag | HIGH | MEDIUM (technical debt; was officially recommended before deprecation) |
| Pipeline | Missing processor tag | MEDIUM | LOW (only enforced from format_version 3.6.0) |
| Pipeline | CEL-only opening processors missing | MEDIUM | LOW (Agentless-era; pre-Agentless integrations don't have them) |
| Pipeline | JSE00001 pattern differs from current standard | HIGH | MEDIUM (if event.original is preserved by alternate means) |
| Pipeline | Geo enrichment without ASN companion | HIGH | MEDIUM (newer standard) |
| Fields | base-fields.yml wrong entry count | HIGH | MEDIUM (verify minimum entries present) |
| Fields | beats.yml absent | HIGH (file-based inputs) | MEDIUM for file-based; N/A for CEL/HTTPJSON |
| Tests | source.geo in dynamic_fields | MEDIUM | LOW (acceptable workaround if version bump not in scope) |
ECS field declarations
- Only flag missing
external: ecsdeclarations whenelastic-packagewould fail validation or the field type genuinely requires it (e.g.,geo_point,geo_shape,nested,flattened) - Standard keyword/date ECS fields that work via dynamic mapping do NOT need explicit declaration — do not flag their absence
Transform configuration guide
Elasticsearch transform configuration for integration packages. Covers source/pivot/latest transform types, sync configuration, field definitions, retention policy, and version increment rules.
File location and structure
elasticsearch/transform/<transform-name>/
transform.yml # Transform configuration (source, pivot/latest, sync, dest)
manifest.yml # Start flag + destination index template (optional)
fields/
fields.yml # Output field definitions for the transform destination indexThe _meta section in transform.yml carries Fleet metadata (fleet_transform_version, managed: true).
Transform types
Transforms are Elasticsearch jobs that process data from source indices and output aggregated or deduplicated data. They are NOT data streams themselves.
Pivot transforms
Define pivot with group_by and aggregations. Most common type. Used for entity-centric or metric summary indices.
description: Summarize events per host per hour
source:
index:
- "logs-mypackage.events-*"
query:
bool:
filter:
- term:
event.kind: event
must_not:
- exists:
field: error.message
- terms:
_tier:
- data_frozen
- data_cold
pivot:
group_by:
host.name:
terms:
field: host.name
"@timestamp":
date_histogram:
field: "@timestamp"
fixed_interval: 1h
aggregations:
event.count:
value_count:
field: event.id
bytes.total:
sum:
field: network.bytes
dest:
index: "metrics-mypackage.summary-default"
sync:
time:
field: event.ingested
delay: 120s
frequency: 5m
settings:
deduce_mappings: false
unattended: true
_meta:
fleet_transform_version: "1.0.0"
managed: trueLatest transforms
Define latest with unique_key and sort. Used for deduplication / latest-state views.
description: Keep latest finding per resource and rule
source:
index:
- "logs-mypackage.findings-*"
query:
bool:
must_not:
- exists:
field: error.message
- terms:
_tier:
- data_frozen
- data_cold
latest:
unique_key:
- resource.id
- rule.id
sort: "@timestamp"
dest:
index: "logs-mypackage.latest_findings-default"
aliases:
- alias: "logs-mypackage.latest_findings"
move_on_creation: true
sync:
time:
field: event.ingested
frequency: 5m
settings:
unattended: true
_meta:
fleet_transform_version: "1.0.0"
managed: trueSort field (latest transforms)
The sort field determines which document is "latest" per unique key. The choice is context-specific:
| Sort field | When to use | Example |
|---|---|---|
@timestamp | Source data has a meaningful event time (findings, alerts, logs). "Latest" means most recent event. | CDR transforms, github issues, alert tracking |
event.ingested | Source data has no reliable event time, or @timestamp defaults to ingest time (inventory scans, endpoint state). "Latest" means most recently received. | tychon device inventory, endpoint state snapshots |
All CDR transforms use @timestamp. Evaluate based on what "latest" means for the specific data source.
Sync configuration
Every transform should have sync config for continuous operation. event.ingested is generally preferred for the sync field (separate from the sort field above) because it reflects when ES received the document, ensuring late-arriving data is picked up. @timestamp is acceptable when the source has reliable, monotonically increasing timestamps.
Delay is optional; when omitted, ES defaults to 60s. Use 120s for sources with known ingestion lag.
Frequency
| Use case | Frequency | Examples |
|---|---|---|
| Threat intelligence | 30s | ti_opencti, ti_anomali, ti_google_threat_intelligence |
| CDR latest views | 5m | wiz, prisma_cloud, google_scc |
| Entity tracking | 5m | crowdstrike aidmaster, armis devices |
| Aggregation / ML | 30m-1h | beaconing, ded, aws_billing |
Source query best practices
Always filter the source query to exclude noise and optimize performance:
- Exclude error documents:
must_not: exists: field: error.messageprevents ingestion-error documents from appearing in transform output - Exclude cold/frozen tiers:
must_not: terms: _tier: [data_frozen, data_cold]prevents scanning expensive storage tiers - Filter by event type: use
filter: term: event.kind: state(CDR) or appropriate event.kind/event.category to limit input scope - Time-bound for full-evaluation APIs: optional
@timestamprange filter (e.g.,gte: "now-26h") limits scan window
Destination configuration
dest.pipeline
Transforms can route output through an ingest pipeline:
dest:
index: "metrics-mypackage.summary-default"
pipeline: '{{ ingestPipelineName "my-transform-pipeline" }}'The {{ ingestPipelineName }} template resolves to the versioned pipeline name at install time.
dest.aliases
Aliases ensure consumers always read from the current transform destination, even after upgrades that recreate the index:
dest:
aliases:
- alias: "logs-mypackage.latest_findings"
move_on_creation: trueTwo variants:
move_on_creation: true-- moves the alias to the new index when the transform is recreated. Only the latest version has the alias.move_on_creation: false-- additive alias that persists across versions. Used for ML/beaconing transforms where historical indices should remain searchable.
Settings
unattended: true-- auto-recovers from transient failures. Required for all transforms.deduce_mappings: false-- prevents auto-creating mappings that conflict with Fleet-managed templates. Mainly needed for non-CDR transforms with explicit field definitions.
_meta fields
_meta:
fleet_transform_version: "1.0.0"
managed: truefleet_transform_version-- required. Bump on any transform code change to trigger delete + reinstall + restart during package upgrade.managed: true-- Fleet manages the transform lifecycle (start/stop/delete). Used for CDR transforms.managed: falsemeans the user manages the transform; used for TI, ML, and custom transforms.run_as_kibana_system: false-- controls execution privileges. Whenfalse, the transform runs under the installing user's credentials rather thankibana_system. Used by 70+ transforms in the repo.
Transform manifest.yml
The optional manifest.yml alongside transform.yml configures installation behavior and the destination index template:
start: true
destination_index_template:
settings:
index:
mode: lookup # For lookup-optimized indices (e.g., crowdstrike aidmaster)
sort:
field: ["@timestamp"]
order: [desc] # For TI transforms that need reverse-time ordering
mapping:
total_fields:
limit: 2000 # For transforms with many output fields
mappings:
dynamic: true
dynamic_templates: # For transforms that accept dynamic fields
- strings_as_keyword:
match_mapping_type: string
mapping:
ignore_above: 1024
type: keywordCommon settings:
start: true-- transform starts automatically on installindex.mode: lookup-- optimized for key-value lookups (enrichment transforms)index.sort-- physical sort order for read-optimized access patternstotal_fields.limit-- increase for wide output schemasdynamic_templates-- map dynamic fields to appropriate types
Retention policy
Deletes documents from the destination index older than max_age.
retention_policy:
time:
field: "@timestamp"
max_age: "2160h" # 90 daysThe retention field can be any date field, not just @timestamp. TI transforms use custom expiry fields for record-level deletion:
retention_policy:
time:
field: opencti.indicator.invalid_or_revoked_from
max_age: 1mCommon max_age values: 26h (full-evaluation CDR APIs), 90d (incremental CDR APIs), 30d (TI/entity transforms).
Transform output field definitions
Transform output fields live at elasticsearch/transform/<name>/fields/fields.yml. Latest transforms require explicit field definitions for every output field. Transform-specific attributes: normalize: [array], object_type_mapping_type.
Version increment rules
- Changes to
pivot/source/latestconfig require a bump in_meta.fleet_transform_version - Field-only changes may not require a bump
- Breaking changes require changelog documentation
CDR transforms
For CDR (cloud security) integrations, see cdr-transform-requirements.md in this skill's references.
Review checklist
- [ ] Transform type matches purpose (pivot for aggregation, latest for dedup/current-state) -- MEDIUM if wrong
- [ ]
descriptionfield present -- LOW - [ ]
sync.time.fieldconfigured -- HIGH if missing. Delay is optional (ES defaults to 60s) -- LOW if omitted - [ ]
_meta.fleet_transform_versionpresent and incremented -- HIGH if missing/stale - [ ] Source index pattern matches expected data stream -- HIGH if wrong
- [ ] Source query excludes
error.messagedocuments -- MEDIUM - [ ] Source query excludes cold/frozen tiers (
_tiermust_not) -- MEDIUM - [ ] Pivot
group_byfields appropriate -- MEDIUM - [ ] Aggregation types match field semantics -- MEDIUM
- [ ] Output field definitions exist with correct types -- HIGH if missing
- [ ]
normalize: [array]for array output fields -- MEDIUM - [ ]
settings.unattended: true-- MEDIUM if missing - [ ]
dest.aliasesconfigured with correctmove_on_creation-- HIGH for CDR, MEDIUM for others - [ ]
_meta.run_as_kibana_systemset appropriately -- LOW - [ ] Transform manifest.yml has appropriate
destination_index_template-- MEDIUM - [ ] No hardcoded index names bypassing data stream routing -- HIGH
- [ ] CDR naming conventions followed when applicable -- MEDIUM
- [ ] Changelog entry for transform changes -- MEDIUM
Version compatibility check procedure
Systematic procedure for verifying that every CEL function and config option used in a PR is compatible with the integration's declared minimum beats version.
For CEL functions
For each CEL function used in the program:
1. List all CEL functions the program uses. Read the program block in cel.yml.hbs and identify every function call.
2. Look up each function in the per-extension tables (in cel-function-reference.md or the tables in this skill) to find the first mito version that includes it.
3. Check the extension is registered at the target beats version. Use extensions-per-version.md to verify the function's extension is registered at the declared minimum. If the extension was added later (e.g., AWS at v8.19.0), that becomes a version floor.
4. Find the first beats release that ships mito >= the required version. Use beats-mito-version-matrix.md. Scan the table for the earliest beats release whose mito column is >= the required mito version.
5. Verify `conditions.kibana.version` allows that beats version. The version constraint in the root manifest.yml must permit the beats version found in step 4. If the constraint excludes it, the function is not available at the declared minimum and this is a compatibility error.
Take the maximum across all functions. That is the true minimum beats version.
For config options
1. List all config options the integration uses. Check top-level options, resource.* options, and auth.* options.
2. Find the latest "First beats version" row in config-options-by-version.md among all options used.
3. Verify `conditions.kibana.version` allows that version. Same check as step 5 above.
Combined minimum
The integration's true minimum beats version is the maximum of:
- The function-derived minimum (from the CEL functions procedure)
- The config-derived minimum (from the config options procedure)
If the declared conditions.kibana.version is lower than this combined minimum, flag it.
Worked example
Program using sign_aws_from_static and truncate:
Step 1 -- List functions:
sign_aws_from_static(AWS extension)truncate(Time extension)
Step 2 -- Look up mito versions:
sign_aws_from_static: first mito version is v1.21.0truncate: first mito version is v1.24.0
Step 3 -- Check extension registration:
- AWS extension: registered from v8.19.0 / v9.1.0
- Time extension: registered from v8.6.0
Step 4 -- Find first beats with required mito:
sign_aws_from_staticneeds mito >= v1.21.0. From the matrix: v8.19.0 ships v1.22.0 (>= v1.21.0). First qualifying: v8.19.0.truncateneeds mito >= v1.24.0. From the matrix: v9.3.0 ships v1.24.0. First qualifying: v9.3.0.
Step 5 -- Maximum across all functions: v9.3.0
Verification: If the manifest says conditions.kibana.version: "^8.19.0 || ^9.1.0":
- FAIL. Neither
^8.19.0(mito v1.22.0 < v1.24.0) nor^9.1.0(mito v1.22.0 < v1.24.0) includes v9.3.0. - Correct constraint:
^9.3.0 - Review comment: "
truncate(Time extension) requires mito v1.24.0, first available in v9.3.0. Current constraint^8.19.0 || ^9.1.0does not include v9.3.0. Update to^9.3.0."
Related skills
FAQ
What areas are reviewed?
Pipelines, field mappings, CEL programs, manifests, changelog, and tests.
New or existing packages?
Existing packages and PR changes; new builds use create-integration instead.
How does this relate to maintain-integration?
Review-integration focuses on PR review; maintain-integration covers fixes and improvements.