mockdr — Multi-EDR Mock Server
Version updated for https://github.com/mockdr/mockdr to version v2.0.0.
- This action is used across all versions by ? repositories.
Action Type
This is a Composite action.
Go to the GitHub Marketplace to find the latest changes.
Action Summary
The mockdr action is a self-contained multi-EDR mock server that provides realistic data and response formats for eight security platforms, including SentinelOne, CrowdStrike Falcon, Microsoft Defender for Endpoint, Elastic Security, Cortex XDR, Splunk SIEM, and Microsoft Sentinel. It supports various authentication methods and response formats to simulate real API interactions without the need for live environments or lab licenses. The action is designed to facilitate testing and development of EDR integration tools and automation scripts across multiple platforms.
What’s Changed
First tagged release since the project was opened up; v1.0.4 pointed at the
initial commit. The major bump reflects two behaviour changes rather than a
rewrite: an unparseable OData $filter is now refused instead of quietly
returning the wrong rows, and the UI moved to Tailwind CSS v4.
Security
- Dependency advisories published since the last refresh are cleared:
python-multipart0.0.22 → 0.0.32 (PYSEC-2026-3036/3037/3038/3039/3040) andpytest9.0.2 → 9.0.3 (PYSEC-2026-1845), plus transitive frontend bumps ofform-data,nanoid,picomatch,postcssandyaml.pip-auditandnpm auditboth report zero vulnerabilities. - Splunk index creation, HEC token management and KV Store collection
management now require an administrator role.
require_splunk_adminexisted but was never applied, so the seededviewercould create indexes and mint HEC tokens.sc_admincounts as an administrator, as it does in Splunk Cloud.
Added
- OData
$filtersupportsendswith(field,'value'), which real Graph serves and mockdr previously rejected. - Unquoted
true,false,null,Edm.Guidand ISO-8601 date/time literals are recognised in$filter, as OData v4 writes them. - EDR→SIEM bridging is live (ADR-009): the Splunk and Sentinel bridges are registered at startup and EDR mutations publish to the event bus, so a Defender alert or a triggered scenario now appears in the SIEMs. Both bridges and the bus were previously unreachable code.
- MDE list endpoints support
$count=true, returning@odata.count.
Changed
- An unparseable or unsupported OData
$filterreturns400instead of being partly ignored. Input the parser could not read used to be skipped, which widened the filter —$filter=@@@returned every record with a200, and a stray paren silently dropped the rest of the expression. Unsupported-but-valid syntax (not,in, nested functions) previously raised and surfaced as500. Both now answer400in the vendor’s error envelope, matching Defender and Graph. No filter that worked before stops working. - The UI is built with Tailwind CSS v4. Tailwind’s default palette moved to
OKLCH, so built-in colours (
text-green-400and friends) render slightly more vivid; a 13-page screenshot comparison found 12 pages byte-identical and the customs1-*palette unchanged. - The version is defined once per workspace —
config.APP_VERSION,pyproject.tomlandpackage.json— and the sidebar footer reads it at build time instead of hardcoding a string. The five sources previously disagreed. A unit test now fails if they drift apart. - Dependencies refreshed: FastAPI 0.141.1, faker 40.36.0, ruff 0.16.2,
pytest-cov 7.1.0, pre-commit 4.6.2, vue-router 5.2.0, lucide-vue-next 1.0.0,
jsdom 30.0.1 and
@types/node26.2.0. - The Graph, MDE and Sentinel token endpoints return OAuth 2.0 errors —
{"error": "...", "error_description": "AADSTS...", "error_codes": [...]}— instead of the OData envelope of the API they sit in front of. MSAL and other OAuth clients read those keys. GET /graph/v1.0/mereturns400 Request_BadRequestunder app-only authentication, as real Graph does, instead of returning the first seeded user.- Splunk endpoints now answer in Atom XML unless
output_mode=jsonis requested, as splunkd does. HEC still always answers JSON. - Sentinel management-plane requests now require
?api-version=, as Azure Resource Manager does. The Log Analytics query endpoint is unaffected. - Graph
$count=truenow requiresConsistencyLevel: eventualinstead of being answered regardless.
Fixed
contains()andstartswith()in an OData$filterreturned500on every call, on both MDE and Graph — the tokeniser consumed the opening paren as part of the function token and the parser then demanded it again. These are the forms the XSOAR MDE integration and Microsoft’s Graph documentation lead with. Graph imports the parser from MDE, so one defect reached both.andbound looser thanorin an OData$filter, inverting the precedence OData specifies:a and b or cevaluated asa and (b or c), so records matching only theorarm were dropped. The parser builds a tree rather than a flat clause list, and parentheses nest correctly.accountEnabled eq truematched nothing andne truematched everything — unquoted keywords were compared as strings againststr(True), i.e."True".Unquoted timestamps were truncated to their year, so
createdDateTime ge 2026-08-08T00:00:00Zcompared against2026and answered far more coarsely than asked. Unquoted GUIDs were shredded the same way.A doubled quote — OData’s escape for a literal one — ended the string, so
startswith(displayName,'O''Brien')matched every name starting withO.Deeply nested parentheses raised
RecursionErrorand surfaced as500, reachable from a short query string. Nesting is capped.Every in-repo caller of the Sentinel operations endpoint omitted the
api-versionparameter that ARM enforcement had just made mandatory, including the UI’s health check, which returned400at runtime. The test fixture supplied the parameter automatically, hiding it from the suite.apply_graph_filterleaked its synthetic_lambda_*keys into the caller’s records when a filter failed to parse; cleanup now runs unconditionally.Cortex XDR advanced authentication now uses the documented scheme —
SHA256(key + nonce + timestamp)over the plain concatenation — instead of an HMAC overnonce:timestamp, which rejected every client built to Palo Alto’s specification. Standard authentication (the API key inAuthorization) is supported as well.Request-validation failures are returned in the mocked vendor’s error envelope with the status that vendor uses, instead of FastAPI’s
422 {"detail": [...]}, which no mocked API emits.POST /mde/api/indicatorsnow rejects a body missingindicatorValue,indicatorType,actionortitlewith400 BadRequestinstead of creating an indicator with empty fields.Authorization scheme names are matched case-insensitively for Sentinel and Splunk, per RFC 7235.
Splunk accepts a session key in its own
Authorization: Splunk <key>scheme, not only asBearer.Graph reports a missing resource with the code its sub-API uses:
Request_ResourceNotFoundfor directory objects,ErrorItemNotFoundfor Outlook mail,itemNotFoundfor drive items andnotFoundfor the security API.A state snapshot now covers every seeded collection. Graph, Sentinel, Splunk and hash exceptions were absent from the registry, and because loading a snapshot skips seeding, restarting with
MOCKDR_PERSISTleft those vendors permanently empty. A coverage test now fails if a new collection is missed.Sentinel rejects a
$skipTokenit did not issue with400 InvalidSkipTokeninstead of raisingValueErrorand returning a 500, andnextLinkis now the absolute, followable URL ARM returns rather than a bare?$skipToken=.POST /iocs/bulkaccepts a single indicator sent as an object instead of silently discarding it and reporting success.Activity records evicted from the bounded order deque are deleted with it, so
count()andlist_activities()no longer diverge past 10,000 activities.The Webhooks UI reads
eventTypesandcreatedAt, the field names the API actually returns; it previously read snake_case and threw while rendering.frontend/.envis created from.env.examplebystart.shand the Docker build. Vite inlinesVITE_*at build time, so without it every vendor client in the UI authenticated asundefined.xdr_api_key_repo.get_by_key_idis a single dict lookup instead of rebuilding an index on every call, matching what its docstring already claimed.Graph, MDE and Sentinel token endpoints now accept the tenant-scoped URL real Entra ID uses (
/{tenant}/oauth2/v2.0/token) in addition to the bare path, so clients that mirror the Microsoft authority shape no longer fall through to the SPA catch-all and get a misleading405 Method Not Allowed(#22). Like Entra, the segment accepts the tenant GUID or a verified domain name; a tenant that matches neither is rejected with400 invalid_request(AADSTS90002). SetMOCKDR_STRICT_TENANT=falseto accept any tenant.