Skip to content

Migrate CrowdStrikeFalcon integration and fix On-Demand Scan empty JSON result - #1420

Open
ranjanprabhat-cyber wants to merge 3 commits into
chronicle:mainfrom
ranjanprabhat-cyber:migration_crowdstrikefalcon
Open

ranjanprabhat-cyber wants to merge 3 commits into
chronicle:mainfrom
ranjanprabhat-cyber:migration_crowdstrikefalcon

Conversation

@ranjanprabhat-cyber

Copy link
Copy Markdown

Summary

  • Deconstruct and migrate the CrowdStrikeFalcon integration (v84.0) to content/response_integrations/google/crowd_strike_falcon.
  • Fix OnDemandScan returning is_success=True with an empty JSON result ([]) when:
    • GET /ods/entities/scans/v1 returns unmapped transitional scan statuses (queued, initiated, started) or an empty resources list right after scan creation.
    • No valid entities/hosts are in scope or zero scans are processed (now explicitly fails with result_value=False).
  • Add unit tests in test_on_demand_scan.py covering empty entity scope and unmapped transitional scan statuses.

@ranjanprabhat-cyber
ranjanprabhat-cyber requested review from a team and adarshtwy as code owners October 28, 2026 08:17
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request migrates the CrowdStrikeFalcon integration to a new directory structure and addresses a bug in the On-Demand Scan functionality. The fix ensures that the scan correctly handles transitional statuses and empty resource lists, preventing false success reports and improving reliability when no valid entities are in scope.

Highlights

  • Integration Migration: Deconstructed and migrated the CrowdStrikeFalcon integration (v84.0) to the new structure at content/response_integrations/google/crowd_strike_falcon.
  • On-Demand Scan Fix: Resolved an issue where OnDemandScan incorrectly returned is_success=True with an empty JSON result when encountering unmapped transitional scan statuses or empty resource lists.
  • Testing: Added comprehensive unit tests in test_on_demand_scan.py to validate empty entity scopes and unmapped transitional scan statuses.
New Features

🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request introduces the CrowdStrike Falcon integration, adding various actions, connectors, and core utilities. Key feedback includes addressing a stale timestamp issue in the OnDemandScan action by dynamically calculating since_time, correcting the return state of _finalize_action_on_failure to handle partial successes properly, and fixing a return type mismatch in _perform_action. Additionally, several style guide violations regarding missing type annotations and absolute imports in OnDemandScan.py must be resolved, and a duplicate requests dependency in pyproject.toml should be removed.

HostIDsTuple = collections.namedtuple("HostIDsTuple", ["valid_hosts_map", "invalid_hosts"])
ScanMappingsTuple = collections.namedtuple("ScanMappingsTuple", ["host_group_scan_mapping", "host_scan_mapping"])
ScanDetailsTuple = collections.namedtuple("ScanDetailsTuple", ["host_group_scan_details", "host_scan_details"])
since_time: str = (datetime.now(timezone.utc) - timedelta(hours=1)).strftime("%Y-%m-%dT%H:%M:%SZ")

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.

high

The since_time variable is defined as a module-level global and evaluated at import time. In long-running environments, this timestamp will become stale and won't reflect the actual execution time of the action. It should be calculated dynamically inside the method where it is used.

Comment on lines +825 to +853
def _finalize_action_on_failure(self) -> ActionResult:
if self.results.processed_host_scans or self.results.processed_host_group_scans:
self._finalize_action_on_success()
# case: some success and some invalid + failed hosts
if self.results.invalid_hosts or self.results.failed_host_scans:
self.output_messages.append(self._some_invalid_hosts_err_msg)

# case: some success and some invalid + failed host groups
if self.results.invalid_host_groups or self.results.failed_host_group_scans:
self.output_messages.append(self._some_invalid_host_groups_err_msg)

else:
# case: no success and all invalid + failed hosts
if self.results.invalid_hosts or self.results.failed_host_scans:
self.output_messages.append(self._all_invalid_hosts_err_msg)

# case: no success and all invalid + failed host groups
if self.results.invalid_host_groups or self.results.failed_host_group_scans:
self.output_messages.append(self._all_invalid_host_groups_err_msg)

if not (
self.results.invalid_hosts
or self.results.failed_host_scans
or self.results.invalid_host_groups
or self.results.failed_host_group_scans
):
self.output_messages.append(self._all_invalid_hosts_err_msg)

return ActionResult(ExecutionState.COMPLETED, False)

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.

high

In _finalize_action_on_failure, if there are processed scans (i.e., partial success), self._finalize_action_on_success() is called, but its return value is ignored, and the method ultimately returns ActionResult(ExecutionState.COMPLETED, False). This causes the entire action to be marked as failed even if some hosts were successfully scanned. If there is partial success, the method should return ActionResult(ExecutionState.COMPLETED, True) with the appropriate warning messages appended to self.output_messages.

    def _finalize_action_on_failure(self) -> ActionResult:\n        if self.results.processed_host_scans or self.results.processed_host_group_scans:\n            self._finalize_action_on_success()\n            # case: some success and some invalid + failed hosts\n            if self.results.invalid_hosts or self.results.failed_host_scans:\n                self.output_messages.append(self._some_invalid_hosts_err_msg)\n\n            # case: some success and some invalid + failed host groups\n            if self.results.invalid_host_groups or self.results.failed_host_group_scans:\n                self.output_messages.append(self._some_invalid_host_groups_err_msg)\n\n            return ActionResult(ExecutionState.COMPLETED, True)\n\n        else:\n            # case: no success and all invalid + failed hosts\n            if self.results.invalid_hosts or self.results.failed_host_scans:\n                self.output_messages.append(self._all_invalid_hosts_err_msg)\n\n            # case: no success and all invalid + failed host groups\n            if self.results.invalid_host_groups or self.results.failed_host_group_scans:\n                self.output_messages.append(self._all_invalid_host_groups_err_msg)\n\n            if not (\n                self.results.invalid_hosts\n                or self.results.failed_host_scans\n                or self.results.invalid_host_groups\n                or self.results.failed_host_group_scans\n            ):\n                self.output_messages.append(self._all_invalid_hosts_err_msg)\n\n        return ActionResult(ExecutionState.COMPLETED, False)

if self.params.max_scan_duration and self.params.max_scan_duration < constants.DEFAULT_MAX_SCAN_DURATION:
raise ValueError('Please provide positive number in parameter "Max Scan Duration".')

def _perform_action(self, manager: CrowdStrikeManager) -> tuple[int, bool]:

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.

medium

The return type annotation of _perform_action is specified as tuple[int, bool], but the method actually returns ActionResult (via self._finalize_action()). This mismatch violates static type checking and should be updated to ActionResult.

Suggested change
def _perform_action(self, manager: CrowdStrikeManager) -> tuple[int, bool]:
def _perform_action(self, manager: CrowdStrikeManager) -> ActionResult:
References
  1. Static Type Checking: All function parameters and return types must be annotated. (link)

version = "84.0"
description = "CrowdStrike Falcon is the leader in next-generation endpoint protection, threat intelligence and incident response through cloud-based endpoint protection."
requires-python = ">=3.11,<3.12"
dependencies = [ "environmentcommon", "requests==2.32.5", "requests==2.32.5", "tipcommon", "tldextract==5.1.2",]

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.

medium

The dependency requests==2.32.5 is duplicated in the dependencies list. Please remove the duplicate entry.

Suggested change
dependencies = [ "environmentcommon", "requests==2.32.5", "requests==2.32.5", "tipcommon", "tldextract==5.1.2",]
dependencies = [ "environmentcommon", "requests==2.32.5", "tipcommon", "tldextract==5.1.2",]

Comment on lines +33 to +44
from crowd_strike_falcon.core.AsyncActionBaseClass import ActionResult, AsyncActionBaseClass
from crowd_strike_falcon.core import constants
from crowd_strike_falcon.core.CrowdStrikeManager import CrowdStrikeManager
from crowd_strike_falcon.core.CrowdStrikeParser import CrowdStrikeParser
from crowd_strike_falcon.core.datamodels import OnDemandScanData
from crowd_strike_falcon.core.exceptions import (
ODSDetectionLevelError,
ODSInvalidFilePathError,
CrowdStrikeBadRequestError,
InvalidCidError,
)
from crowd_strike_falcon.core.utils import get_entity_original_identifier, validate_device_id

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.

medium

Internal imports must be relative per the repository style guide (e.g., from ..core.AsyncActionBaseClass import ... instead of from crowd_strike_falcon.core.AsyncActionBaseClass import ...). Please update the absolute imports on lines 33-44 to be relative.

from ..core.AsyncActionBaseClass import ActionResult, AsyncActionBaseClass\nfrom ..core import constants\nfrom ..core.CrowdStrikeManager import CrowdStrikeManager\nfrom ..core.CrowdStrikeParser import CrowdStrikeParser\nfrom ..core.datamodels import OnDemandScanData\nfrom ..core.exceptions import (\n    ODSDetectionLevelError,\n    ODSInvalidFilePathError,\n    CrowdStrikeBadRequestError,\n    InvalidCidError,\n)\nfrom ..core.utils import get_entity_original_identifier, validate_device_id
References
  1. Internal imports must be relative: from ..core.Manager import ... not from Manager import ... (link)



class OnDemandScanAction(AsyncActionBaseClass):
def __init__(self, siemplify):

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.

medium

The __init__ method is missing type annotations for its parameter siemplify and its return type. According to the repository style guide, all function parameters and return types must be annotated. Please update it to def __init__(self, siemplify: SiemplifyAction) -> None:.

Suggested change
def __init__(self, siemplify):
def __init__(self, siemplify: SiemplifyAction) -> None:
References
  1. Static Type Checking: All function parameters and return types must be annotated. (link)

invalid_hosts=[],
)

def _extract_action_configuration(self):

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.

medium

The _extract_action_configuration method is missing a return type annotation. Please update it to def _extract_action_configuration(self) -> None:.

Suggested change
def _extract_action_configuration(self):
def _extract_action_configuration(self) -> None:
References
  1. Static Type Checking: All function parameters and return types must be annotated. (link)

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