Add Cortex XSOAR adapter#299
Draft
maximelb wants to merge 2 commits into
Draft
Conversation
Adds a USP adapter that polls the Palo Alto Networks Cortex XSOAR (formerly
Demisto) "search incidents by filter" API and ships each incident verbatim to
LimaCharlie. Works against both Cortex XSOAR 6 (on-prem) and 8 (cloud).
- Auth: standard and advanced (SHA-256 signed) API keys; x-xdr-auth-id for
XSOAR 8 and advanced keys.
- Incremental collection by incident "modified" time with a high-water cursor;
updated incidents are re-collected, deduplicated per id+modified so each
distinct version ships exactly once.
- 0-indexed page/size pagination over the {"data":[...],"total":N} envelope.
- Source-side errors are non-fatal warnings (retried next poll); 5xx/429 retried
with backoff.
Tested end-to-end against an in-memory mock reproducing the documented API
shapes (auth, modified filter, ascending sort, pagination, envelope). Not yet
live-verified against a real instance.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012BgvY1mfwhUa5FZzwHPctH
lcbill
previously approved these changes
Jun 25, 2026
From a thorough self-review: - Paginate on the raw page length, not the post-filter count. A persistently malformed record on an otherwise-full page would make the page look short and end the walk early, skipping later pages each poll. extractIncidents now also returns the raw record count; the short-page/total checks use it. - Cap the cursor at "now". A single incident with a future-dated "modified" (clock skew / a bad record) would have jumped the cursor ahead and starved collection of everything between now and that future time. - Stop the poll loop on parent-context cancellation, not only Close(); otherwise a cancelled ctx makes every poll fail fast and the loop spins once per interval. Added regression tests for all three (raw-length pagination end-to-end, malformed record on a full page, future-dated cursor cap) plus the extractIncidents pageLen unit assertions. All tests pass under -race. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012BgvY1mfwhUa5FZzwHPctH
lcbill
approved these changes
Jun 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a USP adapter that ingests incidents from Palo Alto Networks Cortex XSOAR (formerly Demisto) into LimaCharlie. Incidents are shipped verbatim (LimaCharlie parses/maps in the cloud). Closes the XSOAR connectivity gap — XSOAR had no existing adapter or extension.
Works against both Cortex XSOAR 6 (on-prem) and 8 (cloud) — the request/response shapes are identical; only the API path prefix (
/xsoar/public/v1on v8) and the auth headers differ, both selected from config.API
Built entirely from the official/SDK-sourced API shapes (no live instance available to me):
POST /incidents/search— demisto-py swagger (searchIncidents,SearchIncidentsData,IncidentFilter,Order); sample payload fromSearchIncidentsV2.Authorization: <key>), XSOAR 8 addsx-xdr-auth-id; advanced key is signed —Authorization = sha256hex(apiKey + nonce + timestamp)withx-xdr-nonce/x-xdr-timestamp/x-xdr-auth-id(plain SHA-256, not HMAC). Per the demisto-py auth client and XSOAR 8 API docs.How it works
modifiedtime with a high-water cursor (modified:>="<cursor>", ascending sort). Updated/reopened incidents are re-collected so the platform sees the incident's evolution.id+modified(re-fetched boundary records and unchanged re-polls suppressed; genuine updates re-ship). First poll bounded byinitial_lookback.page/sizeover{"data":[...],"total":N}, walked to a short page /total, capped bymax_pages.Testing
go test ./cortex_xsoar/...— passes (also-race). End-to-end against an in-memory mock that reproduces standard + advanced auth, themodifiedfilter, ascending sort, 0-indexed pagination and the{data,total}envelope, plus unit tests for validation, URL/version resolution, query/body building, parsing (precision, zero-time, offsets), id resolution, dedup-by-version, and the advanced signature.modified:>=Lucene query behaves as expected on a real instance.Follow-ups (not in scope here)
🤖 Generated with Claude Code