Skip to content

[Threat Intel 5/11] Index templates, catalog seeding, and inference features - #287201

Merged
stephmilovic merged 26 commits into
elastic:mainfrom
stephmilovic:threat-intel-4b-setup
Sep 2, 2026
Merged

[Threat Intel 5/11] Index templates, catalog seeding, and inference features#287201
stephmilovic merged 26 commits into
elastic:mainfrom
stephmilovic:threat-intel-4b-setup

Conversation

@stephmilovic

@stephmilovic stephmilovic commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Installs Threat Intelligence index templates, semantic-text readiness checks, and fixed-catalog source seeding. The catalog is code-authoritative: missing entries are created, code-owned fields are reconciled, and operator enabled choices are preserved.

Where this sits

# PR Depends on Status
1 #287197 workflow gate correction + fixtures nothing ✅ merged
2 #287198 contracts, constants, shared libs nothing ✅ merged
3 #287199 content parsing #287198 closed
4 #287200 SSRF-guarded HTTP client #287198 ✅ merged
5 #287201 index templates, seeding, inference features ← this PR #287198, #287200 ✅ merged
6 #287202 indicator alias #287198, #287201 ✅ merged
7 #287203 IOC extraction #287198, #287200 ✅ merged
8 #287204 LLM services, routes, source catalog API #287198, #287200, #287202, #287203 ✅ merged
9 #287205 RSS adapter #287198, #287200, #287203, #287204 ✅ merged
10 #287206 remaining adapters, dispatcher, fetch_source step #287198, #287200, #287203, #287205 ✅ merged
11 #287207 promote/scrub tasks and plugin wiring #287198#287206 ✅ merged
12 #287479 generator fixture article URLs nothing ✅ merged

#287479 came first in the merge train and is already on main. The numbered series (#287197#287207) stacks on top in dependency order. #287199 (content parsing) was closed; IOC extraction ships its own section-header classifier.

Scope review follow-up

  • Disables legacy catalog rows outside the approved ID set on each boot.
  • Exports approved catalog IDs in common constants for downstream routes.
  • Treats bulk item failures and incomplete concurrency metadata as bootstrap failures.
  • Keeps vendor_api:elastic-security-labs as the stable document ID while reconciling adapter_type to rss.

Product boundary

Operators can only toggle enabled on approved sources. threatIntelSupplyEnabled stays off until direct-index cross-space isolation is hardened.

To test

node scripts/jest --config x-pack/solutions/security/plugins/security_solution/server/threat_intel/jest.config.js setup/seed_default_sources.test.ts setup/bootstrap_threat_intel.test.ts

PR developed with Cursor + Auto

@stephmilovic stephmilovic added release_note:skip Skip the PR/issue when compiling release notes backport:skip This PR does not require backporting Team:Threat Hunting Security Solution Threat Hunting Team Team: SecuritySolution Security Solutions Team working on SIEM, Endpoint, Timeline, Resolver, etc. v9.6.0 labels Aug 25, 2026
@stephmilovic
stephmilovic force-pushed the threat-intel-4b-setup branch 3 times, most recently from 7d4ad78 to 203704e Compare August 26, 2026 16:22
@stephmilovic
stephmilovic marked this pull request as ready for review August 26, 2026 16:22
@stephmilovic
stephmilovic requested a review from a team as a code owner August 26, 2026 16:22
@infra-vault-gh-plugin-prod

Copy link
Copy Markdown

Pinging @elastic/security-threat-hunting (Team:Threat Hunting)

@infra-vault-gh-plugin-prod

Copy link
Copy Markdown

Pinging @elastic/security-solution (Team: SecuritySolution)

@stephmilovic stephmilovic added the reviewer:libra PR review with Libra. This disables Claude and Scout reviewers label Aug 26, 2026

@kibanamachine kibanamachine left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Libra found 2 issues.

Generated by Libra

@stephmilovic

Copy link
Copy Markdown
Contributor Author

Both addressed, and I took a different approach on the migration one than suggested, so worth a look.

Propagating each migration error would mean changing all 13 signatures and would still miss the failure mode this file worries about most: a migration that runs cleanly but takes a wrong branch and skips its field reports success while leaving the schema just as broken. So the install verifies the outcome instead, re-reading the mappings after every migration and throwing if a migration-owned field is still missing. That gets the retry and unresolved-readiness behaviour asked for, and covers the silent no-op too.

Also carries content.body_is_title_fallback for the title-fallback flag in #287199, so those two want to land together.

stephmilovic and others added 3 commits September 2, 2026 09:13
Use Object.hasOwn for catalog URL lookups, drop config from the
fetch_source hit schema, paginate legacy-source disable with
search_after, and surface disable failures in the seed result so
bootstrap retries.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@kibanamachine

Copy link
Copy Markdown
Contributor

💛 Build succeeded, but was flaky

Failed CI Steps

Metrics [docs]

✅ unchanged

Test Failures

  • [job] [logs] FTR Configs #72 / Fleet tasks Agent status change task should not duplicate status-change docs on the next run when last_known_status matches
  • [job] [logs] Defend Workflows Cypress Tests #5 / Response console Execute operations: "before all" hook for ""execute --command" - should execute a command" "before all" hook for ""execute --command" - should execute a command"

History

@stephmilovic
stephmilovic merged commit fd6a14e into elastic:main Sep 2, 2026
41 checks passed
stephmilovic added a commit to stephmilovic/kibana that referenced this pull request Sep 2, 2026
After elastic#287201, feed URLs are no longer stored on source documents.

Co-authored-by: Cursor <cursoragent@cursor.com>
stephmilovic added a commit to stephmilovic/kibana that referenced this pull request Sep 2, 2026
After elastic#287201, feed URLs live in catalog_source_urls rather than source config.

Co-authored-by: Cursor <cursoragent@cursor.com>
stephmilovic added a commit to stephmilovic/kibana that referenced this pull request Sep 2, 2026
…de catalog

After elastic#287201, feed URLs live in catalog_source_urls rather than source config.

Co-authored-by: Cursor <cursoragent@cursor.com>
stephmilovic added a commit to stephmilovic/kibana that referenced this pull request Sep 3, 2026
After elastic#287201, feed URLs live in catalog_source_urls rather than source config.

Co-authored-by: Cursor <cursoragent@cursor.com>
stephmilovic added a commit to stephmilovic/kibana that referenced this pull request Sep 3, 2026
…de catalog

After elastic#287201, feed URLs live in catalog_source_urls rather than source config.

Co-authored-by: Cursor <cursoragent@cursor.com>
stephmilovic added a commit to stephmilovic/kibana that referenced this pull request Sep 3, 2026
After elastic#287201, feed URLs are no longer stored on source documents.

Co-authored-by: Cursor <cursoragent@cursor.com>
stephmilovic added a commit to stephmilovic/kibana that referenced this pull request Sep 4, 2026
After elastic#287201, feed URLs live in catalog_source_urls rather than source config.

Co-authored-by: Cursor <cursoragent@cursor.com>
stephmilovic added a commit to stephmilovic/kibana that referenced this pull request Sep 4, 2026
…de catalog

After elastic#287201, feed URLs live in catalog_source_urls rather than source config.

Co-authored-by: Cursor <cursoragent@cursor.com>
stephmilovic added a commit to stephmilovic/kibana that referenced this pull request Sep 4, 2026
After elastic#287201, feed URLs are no longer stored on source documents.

Co-authored-by: Cursor <cursoragent@cursor.com>
stephmilovic added a commit to stephmilovic/kibana that referenced this pull request Sep 4, 2026
After elastic#287201, feed URLs live in catalog_source_urls rather than source config.

Co-authored-by: Cursor <cursoragent@cursor.com>
stephmilovic added a commit to stephmilovic/kibana that referenced this pull request Sep 4, 2026
…de catalog

After elastic#287201, feed URLs live in catalog_source_urls rather than source config.

Co-authored-by: Cursor <cursoragent@cursor.com>
stephmilovic added a commit to stephmilovic/kibana that referenced this pull request Sep 4, 2026
After elastic#287201, feed URLs live in catalog_source_urls rather than source config.

Co-authored-by: Cursor <cursoragent@cursor.com>
stephmilovic added a commit to stephmilovic/kibana that referenced this pull request Sep 4, 2026
…de catalog

After elastic#287201, feed URLs live in catalog_source_urls rather than source config.

Co-authored-by: Cursor <cursoragent@cursor.com>
stephmilovic added a commit to stephmilovic/kibana that referenced this pull request Sep 4, 2026
After elastic#287201, feed URLs live in catalog_source_urls rather than source config.

Co-authored-by: Cursor <cursoragent@cursor.com>
stephmilovic added a commit to stephmilovic/kibana that referenced this pull request Sep 4, 2026
…de catalog

After elastic#287201, feed URLs live in catalog_source_urls rather than source config.

Co-authored-by: Cursor <cursoragent@cursor.com>
stephmilovic added a commit to stephmilovic/kibana that referenced this pull request Sep 4, 2026
After elastic#287201, feed URLs live in catalog_source_urls rather than source config.

Co-authored-by: Cursor <cursoragent@cursor.com>
stephmilovic added a commit to stephmilovic/kibana that referenced this pull request Sep 4, 2026
…de catalog

After elastic#287201, feed URLs live in catalog_source_urls rather than source config.

Co-authored-by: Cursor <cursoragent@cursor.com>
stephmilovic added a commit to stephmilovic/kibana that referenced this pull request Sep 4, 2026
After elastic#287201, feed URLs live in catalog_source_urls rather than source config.

Co-authored-by: Cursor <cursoragent@cursor.com>
stephmilovic added a commit to stephmilovic/kibana that referenced this pull request Sep 4, 2026
…de catalog

After elastic#287201, feed URLs live in catalog_source_urls rather than source config.

Co-authored-by: Cursor <cursoragent@cursor.com>
stephmilovic added a commit that referenced this pull request Sep 4, 2026
…og API (#287204)

## Summary

Adds LLM enrichment services and Threat Intelligence HTTP routes,
including the fixed-catalog source list and enable/disable API. Manual
report creation and provenance URLs go through the shared HTTP/HTTPS
normalizer.

Also wires the routes, inference features, and one-time bootstrap into
`plugin.ts`, gated by `threatIntelSupplyEnabled`. This wiring is
interim: it exists so the enrichment eval suite (see Evaluation below)
has routes to call ahead of the full pipeline wiring. `#287207` replaces
it with a proper `wiring.ts` module; that PR's description has a rebase
note covering the `plugin.ts` conflict resolution.

## Where this sits

| # | PR | Depends on | Status |
|---|---|---|---|
| 1 | #287197 workflow gate correction + fixtures | nothing | ✅ merged |
| 2 | #287198 contracts, constants, shared libs | nothing | ✅ merged |
| 3 | #287199 content parsing | #287198 | closed |
| 4 | #287200 SSRF-guarded HTTP client | #287198 | ✅ merged |
| 5 | #287201 index templates, seeding, inference features | #287198,
#287200 | ✅ merged |
| 6 | #287202 indicator alias | #287198, #287201 | ✅ merged |
| 7 | #287203 IOC extraction | #287198, #287200 | ✅ merged |
| 8 | #287204 LLM services, routes, source catalog API **← this PR** |
#287198, #287200, #287202, #287203 | 👀 ready |
| 9 | #287205 RSS adapter | #287198, #287200, #287203, #287204 | draft |
| 10 | #287206 remaining adapters, dispatcher, fetch_source step |
#287198, #287200, #287203, #287205 | draft |
| 11 | #287207 promote/scrub tasks and plugin wiring | #287198#287206 |
draft |
| 12 | #287479 generator fixture article URLs | nothing | ✅ merged |
| 13 | #289343 Scout API tests for the deterministic routes | #287204 |
draft |
| 14 | #289345 enrichment eval suite | #287204 | draft |


#287479 came first in the merge train and is already on `main`. The
numbered series (#287197#287207) stacks on top in dependency order.
#287199 (content parsing) was closed; IOC extraction ships its own
section-header classifier.

## Scope review follow-up

- Shared `normalizeProvenanceUrl` helper (HTTP/HTTPS only, strips
credentials, bounded length).
- Source create/delete routes removed. Updates may change only
`enabled`.
- Lists and mutations reject catalog IDs outside the approved set.
- Ensures the per-space indicator alias on `list_sources` (idempotent).

## Evaluation

A `@kbn/evals` suite exercises all four enrichment stages against the
BlackHat demo pack article text, posting each input directly to the
internal route and scoring the structured response. It runs as a tracked
baseline (scores recorded, no build-failing thresholds yet). Latest full
run, EIS Claude Sonnet 4.6 as model and judge, 1 repetition, 20
examples:

| Stage | Evaluator | Score |
|---|---|---|
| assess_relevance | IsIntelligenceMatch | 1.00 |
| assess_relevance | RelevanceShapeValid | 1.00 |
| classify_severity | SeverityExactMatch | 0.83 |
| classify_severity | SeverityWithinOneLevel | 1.00 |
| enrich_taxonomy | CategoryRecall | 0.80 |
| enrich_taxonomy | RegionRecall | 1.00 |
| extract_diamond | DiamondNoIocLeak | 1.00 |
| extract_diamond | DiamondSignalCount | 1.00 |
| extract_diamond | criteria (LLM judge, majority vote) | 1.00 |

The suite ships in #289345, a separate eval-only PR that stacks on this
one (it needs the routes and the flag-gated wiring added here). The four
deterministic routes are covered separately by Scout API tests in
#289343. Any prompt or calibration fix the evals surface lands by
amending this PR, not the eval PR.

## To test

```
node scripts/jest --config x-pack/solutions/security/plugins/security_solution/server/threat_intel/jest.config.js routes/list_sources.test.ts services/provenance_url.test.ts
```

_PR developed with Cursor + Auto_

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Jon Wålstedt <jon.walstedt@elastic.co>
Co-authored-by: Cursor <cursoragent@cursor.com>
stephmilovic added a commit to stephmilovic/kibana that referenced this pull request Sep 4, 2026
After elastic#287201, feed URLs live in catalog_source_urls rather than source config.

Co-authored-by: Cursor <cursoragent@cursor.com>
stephmilovic added a commit to stephmilovic/kibana that referenced this pull request Sep 4, 2026
…de catalog

After elastic#287201, feed URLs live in catalog_source_urls rather than source config.

Co-authored-by: Cursor <cursoragent@cursor.com>
jonwalstedt added a commit that referenced this pull request Sep 11, 2026
…d the flag (#287207)

## Summary

Wires Threat Intelligence behind `threatIntelSupplyEnabled`: bootstrap,
managed workflow installation, promote/scrub tasks, and route
registration. Promote mirrors extracted IOCs into
`.threat-intel-indicators` for Indicator Match rules.

## Where this sits

| # | PR | Depends on | Status |
|---|---|---|---|
| 1 | #287197 workflow gate correction + fixtures | nothing | ✅ merged |
| 2 | #287198 contracts, constants, shared libs | nothing | ✅ merged |
| 3 | #287199 content parsing | #287198 | closed |
| 4 | #287200 SSRF-guarded HTTP client | #287198 | ✅ merged |
| 5 | #287201 index templates, seeding, inference features | #287198,
#287200 | ✅ merged |
| 6 | #287202 indicator alias | #287198, #287201 | ✅ merged |
| 7 | #287203 IOC extraction | #287198, #287200 | ✅ merged |
| 8 | #287204 LLM services, routes, source catalog API | #287198,
#287200, #287202, #287203 | ✅ merged |
| 9 | #287205 RSS adapter | #287198, #287200, #287203, #287204 | ✅
merged |
| 10 | #287206 remaining adapters, dispatcher, fetch_source step |
#287198, #287200, #287203, #287205 | ✅ merged |
| 11 | #287207 promote/scrub tasks and plugin wiring **← this PR** |
#287198#287206 | ready for review |
| 12 | #287479 generator fixture article URLs | nothing | ✅ merged |
| 13 | #289343 Scout API tests for the deterministic routes | #287204 |
ready for review |
| 14 | #289345 enrichment eval suite | #287204 | ready for review |
| 15 | #290144 read APIs, readiness, space-keyed attribution | #287207 |
draft |


#287479 came first in the merge train and is already on `main`. The
numbered series (#287197#287205) is merged; #287206 and #287207 now sit
directly on `main`. #287199 (content parsing) was closed; IOC extraction
ships its own section-header classifier.

## Scope review follow-up

- Installs the managed workflows with the right space topology:
`attribute_alerts_to_reports` installs per space (it queries
`.alerts-security.alerts-*` and writes per-space hit totals, so a global
install would clobber every space's totals onto one shared doc), while
`ingest_threat_feeds` and `enrich_threat_report` install once globally.
Alert analysis and threat intel share a single `ready()` call so neither
install set is dropped, and spaces created after boot are reconciled on
the promote task. All three ship `enabled: false`, and enrich routes its
HTTP calls through a fixed space (`default`) instead of the install-time
`workflow.spaceId` (which is `'*'` globally).
- Ensures the default-space indicator alias on start.
- Chunks promotion bulk writes and treats HTTP 408/500 as retryable bulk
failures.
- Sanitizes provenance and extracted IOC reference URLs during
promotion.
- Documents direct-index cross-space isolation as the blocker to
enabling the feature.

## Bootstrap fix folded in from #289343

`seed_default_sources.ts` sorted the legacy-source disable scan on
`_id`, which Elasticsearch rejects with `illegal_argument_exception:
Fielddata access on the _id field is disallowed` unless
`indices.id_field_data.enabled` is set, so it failed bootstrap on a
stock cluster. Found this while getting #289343's Scout suite green and
moved the fix here since this PR is the one wiring up bootstrap and
already needs `security-threat-hunting` review, so it isn't adding a
reviewer #289343 wouldn't otherwise need.

## Product boundary

`threatIntelSupplyEnabled` defaults to false. Do not enable until
direct-index cross-space isolation is hardened or the administrator
trust model is explicitly accepted.

## To test

```
node scripts/jest --config x-pack/solutions/security/plugins/security_solution/server/threat_intel/jest.config.js wiring.test.ts tasks/promote_threat_indicators.test.ts
node scripts/jest x-pack/solutions/security/plugins/security_solution/server/workflows/
node scripts/jest src/platform/packages/shared/kbn-workflows/managed/definitions/threat_intel/
```

_PR developed with Cursor + Auto + Sonnet 5 + Opus 4.8_

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Jon Wålstedt <jon.walstedt@elastic.co>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport:skip This PR does not require backporting release_note:skip Skip the PR/issue when compiling release notes reviewer:libra PR review with Libra. This disables Claude and Scout reviewers Team: SecuritySolution Security Solutions Team working on SIEM, Endpoint, Timeline, Resolver, etc. Team:Threat Hunting Security Solution Threat Hunting Team v9.6.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants