Insert or update a host record. DUPLICATE PREVENTION: - If adding a real MAC for an IP that has a pseudo-MAC, migrates data and deletes pseudo-MAC - If adding a pseudo-MAC for an IP that has a real MAC, uses the real MAC instead - Prevents duplicate
(self, mac: str, ip: str = None, hostname: str = None,
vendor: str = None, ports: str = None, services: str = None,
vulnerabilities: str = None, **kwargs)
| 609 | "UPDATE hosts SET hostname = ?, updated_at = ? WHERE mac = ?", |
| 610 | (sanitized, now, mac) |
| 611 | ) |
| 612 | updated += 1 |
| 613 | |
| 614 | if updated: |
| 615 | logger.info(f"Sanitized {updated} hostnames with invalid formatting") |
| 616 | return updated |
| 617 | except Exception as e: |
| 618 | logger.error(f"Failed to sanitize existing hostnames: {e}") |
| 619 | return 0 |
| 620 | |
| 621 | def upsert_host(self, mac: str, ip: str = None, hostname: str = None, |
| 622 | vendor: str = None, ports: str = None, services: str = None, |
| 623 | vulnerabilities: str = None, **kwargs): |
| 624 | """ |
| 625 | Insert or update a host record. |
| 626 | |
| 627 | DUPLICATE PREVENTION: |
| 628 | - If adding a real MAC for an IP that has a pseudo-MAC, migrates data and deletes pseudo-MAC |
| 629 | - If adding a pseudo-MAC for an IP that has a real MAC, uses the real MAC instead |
| 630 | - Prevents duplicate entries for the same IP with different MACs |
| 631 | |
| 632 | Args: |
| 633 | mac: MAC address (primary key) |
| 634 | ip: IP address |
| 635 | hostname: Hostname |
| 636 | vendor: Vendor/manufacturer |
| 637 | ports: Comma-separated list of open ports |
| 638 | services: JSON string or dict of port->service mappings |
| 639 | vulnerabilities: JSON string or dict of vulnerabilities |
| 640 | **kwargs: Additional columns (action statuses, notes, etc.) |
| 641 | |
| 642 | Returns: |
| 643 | bool: True if successful |
| 644 | """ |
| 645 | if not mac: |
| 646 | logger.warning("Cannot upsert host without MAC address") |
| 647 | return False |
| 648 | |
| 649 | # Normalize MAC address |
| 650 | mac = mac.lower().strip() |
| 651 | |
| 652 | # DUPLICATE PREVENTION: Check for existing entry with same IP but different MAC |
| 653 | if ip: |
| 654 | existing_host = self.get_host_by_ip(ip) |
| 655 | if existing_host and existing_host['mac'] != mac: |
| 656 | existing_mac = existing_host['mac'] |
| 657 | is_new_mac_pseudo = self._is_pseudo_mac(mac) |
| 658 | is_existing_mac_pseudo = self._is_pseudo_mac(existing_mac) |
| 659 | |
| 660 | if is_new_mac_pseudo and not is_existing_mac_pseudo: |
| 661 | # Trying to add pseudo-MAC when real MAC exists - use real MAC instead |
| 662 | logger.info(f"🔄 Real MAC {existing_mac} already exists for IP {ip}, ignoring pseudo-MAC {mac}") |
| 663 | mac = existing_mac |
| 664 | elif not is_new_mac_pseudo and is_existing_mac_pseudo: |
| 665 | # Upgrading from pseudo-MAC to real MAC - migrate data |
| 666 | logger.info(f"🔄 Upgrading IP {ip} from pseudo-MAC {existing_mac} to real MAC {mac}") |
| 667 | |
| 668 | # Merge data from old entry with new data, preserving valuable info |
no test coverage detected