Execute an action on a target with timeout protection
(self, action, ip, ports, row, action_key, current_data)
| 490 | |
| 491 | |
| 492 | def execute_action(self, action, ip, ports, row, action_key, current_data): |
| 493 | """Execute an action on a target with timeout protection""" |
| 494 | # NOTE: Port checking is now done BEFORE calling this method (performance optimization) |
| 495 | # The caller pre-filters by port to avoid unnecessary semaphore acquisitions |
| 496 | |
| 497 | # Check if attacks are enabled (skip attack actions if disabled, but allow scanning) |
| 498 | enable_attacks = getattr(self.shared_data, 'enable_attacks', True) |
| 499 | attack_action_names = [ |
| 500 | 'SSHBruteforce', 'FTPBruteforce', 'TelnetBruteforce', |
| 501 | 'RDPBruteforce', 'SMBBruteforce', 'SQLBruteforce', |
| 502 | 'SSHConnector', 'FTPConnector', 'TelnetConnector', |
| 503 | 'RDPConnector', 'SMBConnector', 'SQLConnector', |
| 504 | 'StealDataSQL', 'StealFilesFTP', 'StealFilesRDP', |
| 505 | 'StealFilesSMB', 'StealFilesSSH', 'StealFilesTelnet' |
| 506 | ] |
| 507 | if not enable_attacks and action.action_name in attack_action_names: |
| 508 | logger.debug(f"Skipping attack action {action.action_name} for {ip}:{action.port} - attacks are disabled") |
| 509 | return False |
| 510 | |
| 511 | # Check parent action status |
| 512 | if action.b_parent_action: |
| 513 | parent_status = row.get(action.b_parent_action, "") |
| 514 | if 'success' not in parent_status: |
| 515 | return False # Skip child action if parent action has not succeeded |
| 516 | |
| 517 | # Check success retry logic using helper |
| 518 | should_retry, reason = self._should_retry(action_key, row, 'success') |
| 519 | if not should_retry: |
| 520 | logger.warning(f"Skipping action {action.action_name} for {ip}:{action.port} due to {reason}") |
| 521 | return False |
| 522 | |
| 523 | # Check failed retry logic using helper |
| 524 | should_retry, reason = self._should_retry(action_key, row, 'failed') |
| 525 | if not should_retry: |
| 526 | logger.warning(f"Skipping action {action.action_name} for {ip}:{action.port} due to {reason}") |
| 527 | return False |
| 528 | |
| 529 | # CRITICAL: Check system resources before executing action (Pi Zero W2 protection) |
| 530 | if not resource_monitor.can_start_operation( |
| 531 | operation_name=f"action_{action.action_name}", |
| 532 | min_memory_mb=30 # Require at least 30MB free memory |
| 533 | ): |
| 534 | logger.warning( |
| 535 | f"Skipping action {action.action_name} for {ip}:{action.port} - " |
| 536 | f"Insufficient system resources (preventing hang)" |
| 537 | ) |
| 538 | return False |
| 539 | |
| 540 | try: |
| 541 | logger.info(f"Executing action {action.action_name} for {ip}:{action.port}") |
| 542 | self.shared_data.ragnarstatustext2 = ip |
| 543 | |
| 544 | # Execute action with timeout protection |
| 545 | action_callable = lambda: action.execute(ip, str(action.port), row, action_key) |
| 546 | result = self._execute_with_timeout( |
| 547 | action_callable, |
| 548 | timeout=self.action_timeout, |
| 549 | action_name=f"{action.action_name}@{ip}:{action.port}" |
no test coverage detected