Skip to content

Conversation

@talltechy
Copy link
Owner

Description

Implements comprehensive Site Management module for InsightVM v2.0 API.

Changes

New Files

  • src/rapid7/api/sites.py (600+ lines)

    • Complete SiteAPI class inheriting from BaseAPI
    • Smart filtering capabilities (name patterns, site IDs, empty sites)
    • Mass operations with safety features (dry-run mode, validation)
    • Full CRUD operations for sites and related resources
  • docs/SITE_MANAGEMENT.md

    • Comprehensive usage guide
    • Smart filtering examples
    • Mass operations documentation
    • Best practices and error handling

Modified Files

  • src/rapid7/client.py
    • Added SiteAPI integration to InsightVMClient
    • Updated docstring to reflect new sites attribute

Key Features

CRUD Operations

  • list(), get_all(), get_site() - Retrieve sites
  • create(), update(), delete_site() - Modify sites

Smart Filtering

  • filter_by_name_pattern() - Filter by starts_with/ends_with/contains
  • filter_by_ids() - Filter by list of site IDs
  • filter_empty_sites() - Find sites with no assets

Mass Operations

  • mass_delete() - Bulk delete with dry-run mode (safe by default)
  • delete_by_pattern() - Delete sites matching name pattern

Asset & Scan Operations

  • get_assets(), get_asset_count() - Asset management
  • get_scan_template(), get_scan_engine() - Scan configuration

Safety Features

  • Dry-run mode for preview before execution
  • Validation for required fields
  • Continue-on-error support for bulk operations
  • Comprehensive error handling

Architecture

  • Follows v2.0 BaseAPI inheritance pattern
  • Uses HTTPBasicAuth for authentication
  • Consistent with existing API modules (assets, asset_groups)
  • Integrates seamlessly with InsightVMClient

Testing Performed

  • ✅ Linting passed (flake8 compatible)
  • ✅ Module imports correctly
  • ✅ Integration with InsightVMClient verified
  • ✅ Documentation reviewed for accuracy

Breaking Changes

None - This is a new module addition.

Related Issues

Fixes #49

Checklist

  • Code follows project style guidelines
  • Documentation added
  • No breaking changes
  • Commits follow conventional commit format
  • Issue reference included in commits

Next Steps

After this PR is merged:

Implements comprehensive site management operations including:
- Full CRUD operations (list, get, create, update, delete)
- Smart filtering by name patterns (starts_with, ends_with, contains)
- Filter by site IDs
- Filter empty sites (no assets)
- Mass delete operations with dry-run mode and validation
- Site asset and scan engine/template operations
- Integration with InsightVMClient as client.sites

Relates to #49
Added detailed documentation for Site Management module including:
- Quick start and basic operations
- Smart filtering examples
- Mass operations with safety features
- Common use cases and best practices
- Error handling patterns

Relates to #49
Copilot AI review requested due to automatic review settings October 7, 2025 21:00
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements a comprehensive Site Management API module for InsightVM v2.0, adding full CRUD operations and advanced site management capabilities with safety features.

  • Implements a new SiteAPI class with comprehensive site management operations including filtering, mass operations, and asset management
  • Adds smart filtering capabilities for sites by name patterns, IDs, and empty sites detection
  • Integrates the SiteAPI module into the main InsightVMClient for seamless access

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/rapid7/client.py Integrates SiteAPI module into InsightVMClient with import and initialization
src/rapid7/api/sites.py New comprehensive SiteAPI class with CRUD operations, smart filtering, mass operations, and safety features
docs/SITE_MANAGEMENT.md Complete usage guide and documentation for the Site Management module with examples and best practices

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines 316 to 343

for site in all_sites:
name = site.get('name', '')

if not case_sensitive:
name_lower = name.lower()
starts_lower = starts_with.lower() if starts_with else None
ends_lower = ends_with.lower() if ends_with else None
contains_lower = contains.lower() if contains else None
else:
name_lower = name
starts_lower = starts_with
ends_lower = ends_with
contains_lower = contains

# Check all conditions
matches = True

if starts_with and not name_lower.startswith(starts_lower):
matches = False
if ends_with and not name_lower.endswith(ends_lower):
matches = False
if contains and contains_lower not in name_lower:
matches = False

if matches:
filtered.append(site)

Copy link

Copilot AI Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic for case-insensitive conversion is duplicated. Consider extracting it into a helper function or using a more concise approach.

Suggested change
for site in all_sites:
name = site.get('name', '')
if not case_sensitive:
name_lower = name.lower()
starts_lower = starts_with.lower() if starts_with else None
ends_lower = ends_with.lower() if ends_with else None
contains_lower = contains.lower() if contains else None
else:
name_lower = name
starts_lower = starts_with
ends_lower = ends_with
contains_lower = contains
# Check all conditions
matches = True
if starts_with and not name_lower.startswith(starts_lower):
matches = False
if ends_with and not name_lower.endswith(ends_lower):
matches = False
if contains and contains_lower not in name_lower:
matches = False
if matches:
filtered.append(site)
def _case(val):
return val if case_sensitive or val is None else val.lower()
for site in all_sites:
name = site.get('name', '')
name_cmp = _case(name)
starts_cmp = _case(starts_with)
ends_cmp = _case(ends_with)
contains_cmp = _case(contains)
# Check all conditions
matches = True
if starts_with and not name_cmp.startswith(starts_cmp):
matches = False
if ends_with and not name_cmp.endswith(ends_cmp):
matches = False
if contains and (contains_cmp not in name_cmp):
matches = False
if matches:
filtered.append(site)

Copilot uses AI. Check for mistakes.
Comment on lines 331 to 341
# Check all conditions
matches = True

if starts_with and not name_lower.startswith(starts_lower):
matches = False
if ends_with and not name_lower.endswith(ends_lower):
matches = False
if contains and contains_lower not in name_lower:
matches = False

if matches:
Copy link

Copilot AI Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The multiple if statements can be simplified using early return or a single boolean expression to improve readability.

Suggested change
# Check all conditions
matches = True
if starts_with and not name_lower.startswith(starts_lower):
matches = False
if ends_with and not name_lower.endswith(ends_lower):
matches = False
if contains and contains_lower not in name_lower:
matches = False
if matches:
# Check all conditions in a single boolean expression
if (
(not starts_with or name_lower.startswith(starts_lower)) and
(not ends_with or name_lower.endswith(ends_lower)) and
(not contains or contains_lower in name_lower)
):

Copilot uses AI. Check for mistakes.
Comment on lines 429 to 432
filtered_sites = [
s for s in filtered_sites
if name_pattern.lower() in s.get('name', '').lower()
]
Copy link

Copilot AI Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This filtering logic is inconsistent with the more comprehensive pattern matching in filter_by_name_pattern(). Consider using the same method for consistency.

Suggested change
filtered_sites = [
s for s in filtered_sites
if name_pattern.lower() in s.get('name', '').lower()
]
# Use the same comprehensive pattern matching as filter_by_name_pattern
name_pattern_sites = self.filter_by_name_pattern(name_pattern=name_pattern)
name_pattern_site_ids = {s['id'] for s in name_pattern_sites}
filtered_sites = [s for s in filtered_sites if s['id'] in name_pattern_site_ids]

Copilot uses AI. Check for mistakes.
- Extract case conversion logic into helper function
- Simplify boolean logic with compound expressions
- Use filter_by_name_pattern for consistency in filter_sites

Relates to #49
@talltechy talltechy merged commit 706f995 into main Oct 7, 2025
9 of 13 checks passed
@talltechy talltechy deleted the feature/issue-49-site-management branch October 7, 2025 21:12
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.

[Feature] Implement Site Management API module

2 participants