Skip to content

[medium] fix(crowdstrike_falcon): guard missing body/errors in API response - #811

Open
elhoim wants to merge 1 commit into
MISP:mainfrom
elhoim:fix/046-crowdstrike_falcon-get-missing-body
Open

elhoim wants to merge 1 commit into
MISP:mainfrom
elhoim:fix/046-crowdstrike_falcon-get-missing-body

Conversation

@elhoim

@elhoim elhoim commented Aug 31, 2026 •

Copy link
Copy Markdown
Contributor

BLUF — crowdstrike_falcon crashed on 200 responses missing body or errors instead of returning the payload.

  • Problem — CSIntelAPI.search_indicator() in crowdstrike_falcon.py validated a 200 response with len(r.get("body").get("errors")), which raises AttributeError if body is absent and TypeError if errors is, even though the very next line already tolerates a missing body.
  • Fix — Resolves the error list defensively as (r.get("body") or {}).get("errors") or [] and reuses it when raising, with regression tests added.
  • Effect — The module degrades to returning whatever body the API sent rather than an unrelated traceback.

Problem

CSIntelAPI.search_indicator() in misp_modules/modules/expansion/crowdstrike_falcon.py (line 245 before this change) validated a successful response with:

if len(r.get("body").get("errors")):

That assumes every HTTP 200 payload from the Falcon Intel API contains a body dict which in turn contains an errors key. Neither is guaranteed:

  • A 200 response with no body key — e.g. {"status_code": 200} — makes r.get("body") return None, so .get("errors") raises AttributeError: 'NoneType' object has no attribute 'get'.
  • A 200 response whose body has no errors key — e.g. {"status_code": 200, "body": {"resources": []}} — makes len(None) raise TypeError: object of type 'NoneType' has no len().

In both cases the module fails with an unrelated traceback instead of returning the response the caller asked for, which masks the actual API result. The success path on the next line already tolerates a missing body (return r.get("body", {})), so the guard is stricter than the code immediately following it.

Fix

Resolve the error list defensively before testing it, and reuse it when raising:

errors = (r.get("body") or {}).get("errors") or []
if errors:
    raise Exception("API Error: " + " | ".join(errors))

This keeps the existing behaviour whenever the API does report errors (same exception type and message format), and otherwise falls through to the existing return r.get("body", {}) — matching the tolerance that success path already has. It also avoids walking the response dict twice to build the message.

Testing

  • Gate: poetry run pytest (the same invocation CI uses) — result: 107 passed, 5 subtests passed in 79.28s (0:01:19).
  • Regression coverage added in tests/test_crowdstrike_falcon.py:
    • test_search_indicator_without_body — 200 response with no body key returns {} instead of raising AttributeError.
    • test_search_indicator_without_errors_key — 200 response with a body but no errors key returns the body instead of raising TypeError.
    • test_search_indicator_raises_on_api_errors — a populated errors list still raises with the joined message, so the existing behaviour is preserved.

No live Falcon credentials are needed; the tests patch the Intel client.

🤖 Generated with Claude Code

CSIntelAPI.search_indicator called r.get("body").get("errors") on a
200 response. When the Falcon API returns a payload without a "body"
key this raises AttributeError on None, and when "body" is present but
carries no "errors" key len(None) raises TypeError -- both masking the
real response instead of returning it.

Resolve the errors list defensively before testing it, and reuse it in
the raised message instead of re-walking the response.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012VxctWivKrfQWn8tVD8qkT
@elhoim elhoim changed the title fix(crowdstrike_falcon): guard missing body/errors in API response [medium] fix(crowdstrike_falcon): guard missing body/errors in API response Sep 3, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant