Skip to content

Latest commit

 

History

History
783 lines (605 loc) · 21.3 KB

File metadata and controls

783 lines (605 loc) · 21.3 KB

Vulnerability Exceptions API Documentation

Overview

The Vulnerability Exceptions API module provides comprehensive functionality for managing vulnerability exceptions in InsightVM. Vulnerability exceptions allow security teams to document and track decisions about vulnerabilities that are false positives, have acceptable risk, have compensating controls, or require temporary exclusion from reporting.

Table of Contents

Getting Started

Basic Setup

from rapid7 import InsightVMClient

# Initialize client
client = InsightVMClient()

# Access vulnerability exceptions API
exceptions_api = client.vulnerability_exceptions

Quick Example

# List all vulnerability exceptions
exceptions = client.vulnerability_exceptions.list(page=0, size=50)

# Get details of a specific exception
exception = client.vulnerability_exceptions.get_exception(42)
print(f"Status: {exception['state']}")
print(f"Reason: {exception['submit']['reason']}")

Core Operations

Listing Exceptions

Retrieve paginated lists of vulnerability exceptions with optional sorting.

# Get first page with default size
exceptions = client.vulnerability_exceptions.list()

# Get larger page with custom sorting
exceptions = client.vulnerability_exceptions.list(
    page=0,
    size=100,
    sort=["submit.date,DESC", "scope.vulnerability,ASC"]
)

# Process results
for exc in exceptions['resources']:
    print(f"Exception {exc['id']}: {exc['state']}")
    print(f"  Vulnerability: {exc['scope']['vulnerability']}")
    print(f"  Submitted: {exc['submit']['date']}")

Getting Exception Details

Retrieve comprehensive information about a specific exception.

# Get exception by ID
exception = client.vulnerability_exceptions.get_exception(42)

# Access exception details
print(f"ID: {exception['id']}")
print(f"Status: {exception['state']}")
print(f"Vulnerability: {exception['scope']['vulnerability']}")
print(f"Scope Type: {exception['scope']['type']}")
print(f"Reason: {exception['submit']['reason']}")
print(f"Submitter: {exception['submit']['name']}")
print(f"Comment: {exception['submit']['comment']}")

# Check expiration
if 'expires' in exception:
    print(f"Expires: {exception['expires']}")

# Check review status
if 'review' in exception:
    print(f"Reviewed by: {exception['review']['name']}")
    print(f"Review date: {exception['review']['date']}")
    print(f"Review comment: {exception['review']['comment']}")

Creating Exceptions

Create new vulnerability exceptions with various scopes.

Global Exception (All Assets)

# Create global exception for false positive
new_exception = {
    "scope": {
        "type": "global",
        "vulnerability": "apache-httpd-cve-2002-0392"
    },
    "submit": {
        "reason": "false_positive",
        "comment": "This CVE does not apply to our Apache configuration"
    },
    "expires": "2025-12-31T23:59:59.000Z"
}

result = client.vulnerability_exceptions.create(new_exception)
print(f"Created exception ID: {result['id']}")

Site-Scoped Exception

# Exception for specific site
site_exception = {
    "scope": {
        "type": "site",
        "vulnerability": "ssh-weak-mac",
        "id": 5  # Site ID
    },
    "submit": {
        "reason": "acceptable_risk",
        "comment": "Internal dev environment, low risk"
    },
    "expires": "2026-01-31T23:59:59.000Z"
}

result = client.vulnerability_exceptions.create(site_exception)

Asset Group Exception

# Exception for asset group
group_exception = {
    "scope": {
        "type": "asset_group",
        "vulnerability": "tls-weak-cipher",
        "id": 12  # Asset Group ID
    },
    "submit": {
        "reason": "compensating_control",
        "comment": "Protected by network segmentation and IDS"
    }
}

result = client.vulnerability_exceptions.create(group_exception)

Asset-Specific Exception

# Exception for specific asset and port
asset_exception = {
    "scope": {
        "type": "asset",
        "vulnerability": "ssh-weak-cipher",
        "id": 123,  # Asset ID
        "port": 22
    },
    "submit": {
        "reason": "compensating_control",
        "comment": "SSH access restricted by firewall rules"
    }
}

result = client.vulnerability_exceptions.create(asset_exception)

Deleting Exceptions

Remove exceptions to restore normal vulnerability reporting.

# Delete an exception
client.vulnerability_exceptions.delete_exception(42)
print("Exception deleted - vulnerability will now appear in reports")

Status Management

Understanding Exception States

Vulnerability exceptions go through a workflow:

  • under_review: Initial state when exception is submitted
  • approved: Exception has been approved and suppresses findings
  • rejected: Exception was rejected and does not suppress findings
  • recall_requested: Approved exception is being recalled for re-review
  • deleted: Exception has been removed

State Transitions:

  • Submit → under_review
  • Approve action → approved state
  • Reject action → rejected state
  • Recall action → recall_requested state

Approving Exceptions

# Approve an exception with comment
result = client.vulnerability_exceptions.approve(
    exception_id=42,
    comment="Risk accepted by security team after review"
)
print("Exception approved")

# Or use update_status directly
result = client.vulnerability_exceptions.update_status(
    exception_id=42,
    status="approve",
    comment="Verified compensating controls are in place"
)

Rejecting Exceptions

# Reject an exception
result = client.vulnerability_exceptions.reject(
    exception_id=43,
    comment="Insufficient justification - requires additional controls"
)
print("Exception rejected")

# Alternative using update_status
result = client.vulnerability_exceptions.update_status(
    exception_id=43,
    status="reject",
    comment="Does not meet security policy requirements"
)

Recalling Exceptions

# Recall an approved exception for re-review
result = client.vulnerability_exceptions.recall(
    exception_id=42,
    comment="Compensating control no longer in place"
)
print("Exception recalled for review")

# Or use update_status
result = client.vulnerability_exceptions.update_status(
    exception_id=42,
    status="recall",
    comment="Infrastructure changes require re-evaluation"
)

Expiration Management

Getting Expiration Date

# Check when an exception expires
expiration = client.vulnerability_exceptions.get_expiration(42)

if expiration:
    print(f"Exception expires: {expiration}")
else:
    print("Exception has no expiration date")

Setting Expiration Date

# Set expiration to specific date
from datetime import datetime, timedelta

# Set to end of year
result = client.vulnerability_exceptions.set_expiration(
    exception_id=42,
    expiration_date="2025-12-31T23:59:59.000Z"
)
print("Expiration date set")

# Calculate dynamic expiration (90 days from now)
expiration = (datetime.now() + timedelta(days=90)).isoformat() + 'Z'
result = client.vulnerability_exceptions.set_expiration(
    exception_id=42,
    expiration_date=expiration
)
print(f"Exception will expire in 90 days: {expiration}")

# Set 1 year expiration
one_year = (datetime.now() + timedelta(days=365)).isoformat() + 'Z'
result = client.vulnerability_exceptions.set_expiration(
    exception_id=42,
    expiration_date=one_year
)

Advanced Features

Bulk Operations

# Get all exceptions (handles pagination automatically)
all_exceptions = client.vulnerability_exceptions.get_all(
    sort=["submit.date,DESC"]
)

print(f"Total exceptions: {len(all_exceptions)}")

# Process all exceptions
for exc in all_exceptions:
    print(f"{exc['id']}: {exc['state']} - {exc['scope']['vulnerability']}")

Filtering by Status

# Get all exceptions
all_exceptions = client.vulnerability_exceptions.get_all()

# Filter by status
under_review = [e for e in all_exceptions if e['state'] == 'under_review']
approved = [e for e in all_exceptions if e['state'] == 'approved']
rejected = [e for e in all_exceptions if e['state'] == 'rejected']

print(f"Under Review: {len(under_review)}")
print(f"Approved: {len(approved)}")
print(f"Rejected: {len(rejected)}")

Finding Expired Exceptions

from datetime import datetime

# Get all exceptions
all_exceptions = client.vulnerability_exceptions.get_all()

# Find expired exceptions
now = datetime.now()
expired = []

for exc in all_exceptions:
    if 'expires' in exc and exc['expires']:
        expiry_date = datetime.fromisoformat(
            exc['expires'].replace('Z', '+00:00')
        )
        if expiry_date < now:
            expired.append(exc)

print(f"Found {len(expired)} expired exceptions")
for exc in expired:
    print(f"  {exc['id']}: Expired {exc['expires']}")

Exception Reporting

# Generate exception summary report
all_exceptions = client.vulnerability_exceptions.get_all()

# Analyze by reason
reasons = {}
for exc in all_exceptions:
    reason = exc['submit']['reason']
    reasons[reason] = reasons.get(reason, 0) + 1

print("Exception Reasons Summary:")
for reason, count in sorted(reasons.items(), key=lambda x: x[1], reverse=True):
    print(f"  {reason}: {count}")

# Analyze by scope type
scopes = {}
for exc in all_exceptions:
    scope_type = exc['scope']['type']
    scopes[scope_type] = scopes.get(scope_type, 0) + 1

print("\nException Scope Types:")
for scope, count in sorted(scopes.items(), key=lambda x: x[1], reverse=True):
    print(f"  {scope}: {count}")

Use Cases

Use Case 1: Managing False Positives

Scenario: Security team needs to mark false positive vulnerabilities to reduce noise in reports.

from rapid7 import InsightVMClient

client = InsightVMClient()

# Create false positive exception for a global vulnerability
false_positive = {
    "scope": {
        "type": "global",
        "vulnerability": "apache-struts-cve-2017-5638"
    },
    "submit": {
        "reason": "false_positive",
        "comment": "Detection triggered by HTTP headers but Struts is not in use"
    },
    "expires": "2026-06-30T23:59:59.000Z"
}

result = client.vulnerability_exceptions.create(false_positive)
exception_id = result['id']
print(f"Created false positive exception: {exception_id}")

# Approve the exception
client.vulnerability_exceptions.approve(
    exception_id=exception_id,
    comment="Confirmed false positive through manual verification"
)
print("Exception approved - vulnerability will not appear in reports")

Use Case 2: Temporary Risk Acceptance

Scenario: Accept vulnerability risk temporarily while patch testing is in progress.

from datetime import datetime, timedelta

client = InsightVMClient()

# Create temporary exception for critical patch testing
temp_exception = {
    "scope": {
        "type": "site",
        "vulnerability": "windows-kb5001234-missing",
        "id": 8  # Production site ID
    },
    "submit": {
        "reason": "acceptable_risk",
        "comment": "Patch being tested in staging, will deploy in 30 days"
    }
}

result = client.vulnerability_exceptions.create(temp_exception)
exception_id = result['id']

# Set 30-day expiration
expiration = (datetime.now() + timedelta(days=30)).isoformat() + 'Z'
client.vulnerability_exceptions.set_expiration(
    exception_id=exception_id,
    expiration_date=expiration
)

# Approve for temporary risk acceptance
client.vulnerability_exceptions.approve(
    exception_id=exception_id,
    comment="Approved for 30-day testing window"
)

print(f"Temporary exception created, expires in 30 days")
print(f"Expiration date: {expiration}")

Use Case 3: Documenting Compensating Controls

Scenario: Document that compensating controls are in place for a known vulnerability.

client = InsightVMClient()

# Exception for asset with compensating controls
compensating_control = {
    "scope": {
        "type": "asset",
        "vulnerability": "ssl-weak-protocol-tlsv1",
        "id": 456,  # Legacy system asset ID
        "port": 443
    },
    "submit": {
        "reason": "compensating_control",
        "comment": (
            "Compensating controls in place:\n"
            "- Traffic restricted to trusted internal network only\n"
            "- Web application firewall inspects all traffic\n"
            "- Network IDS monitors for anomalies\n"
            "- Legacy system scheduled for replacement in Q2"
        )
    }
}

result = client.vulnerability_exceptions.create(compensating_control)
exception_id = result['id']

# Set expiration to Q2 replacement date
client.vulnerability_exceptions.set_expiration(
    exception_id=exception_id,
    expiration_date="2026-06-30T23:59:59.000Z"
)

# Approve with additional documentation
client.vulnerability_exceptions.approve(
    exception_id=exception_id,
    comment="Compensating controls reviewed and approved by CISO"
)

print("Compensating control exception documented and approved")

Use Case 4: Exception Review Workflow

Scenario: Implement a review workflow for vulnerability exceptions.

client = InsightVMClient()

# Get all exceptions pending review
all_exceptions = client.vulnerability_exceptions.get_all()
pending_review = [
    e for e in all_exceptions 
    if e['state'] == 'under_review'
]

print(f"Found {len(pending_review)} exceptions awaiting review")

# Review each exception
for exc in pending_review:
    exception_id = exc['id']
    vulnerability = exc['scope']['vulnerability']
    reason = exc['submit']['reason']
    comment = exc['submit']['comment']
    submitter = exc['submit']['name']
    
    print(f"\n=== Exception {exception_id} ===")
    print(f"Vulnerability: {vulnerability}")
    print(f"Reason: {reason}")
    print(f"Justification: {comment}")
    print(f"Submitted by: {submitter}")
    
    # Example: Auto-approve false positives, manual review for others
    if reason == 'false_positive':
        client.vulnerability_exceptions.approve(
            exception_id=exception_id,
            comment="Auto-approved: False positive exceptions are pre-approved"
        )
        print("Status: AUTO-APPROVED")
    else:
        print("Status: Requires manual security team review")

Use Case 5: Exception Lifecycle Management

Scenario: Monitor and manage exception lifecycle, including expirations and recalls.

from datetime import datetime, timedelta

client = InsightVMClient()

# Get all approved exceptions
all_exceptions = client.vulnerability_exceptions.get_all()
approved = [e for e in all_exceptions if e['state'] == 'approved']

print(f"Monitoring {len(approved)} approved exceptions\n")

now = datetime.now()
expiring_soon = []
no_expiration = []

for exc in approved:
    exception_id = exc['id']
    vulnerability = exc['scope']['vulnerability']
    
    # Check expiration
    expiration = client.vulnerability_exceptions.get_expiration(exception_id)
    
    if not expiration:
        no_expiration.append(exc)
        print(f"WARNING: Exception {exception_id} has no expiration")
        print(f"  Vulnerability: {vulnerability}")
        print(f"  Setting 1-year expiration")
        
        # Set expiration if missing
        one_year = (now + timedelta(days=365)).isoformat() + 'Z'
        client.vulnerability_exceptions.set_expiration(
            exception_id=exception_id,
            expiration_date=one_year
        )
    else:
        # Parse expiration date
        expiry_date = datetime.fromisoformat(expiration.replace('Z', '+00:00'))
        days_until_expiry = (expiry_date - now).days
        
        if days_until_expiry < 30:
            expiring_soon.append((exc, days_until_expiry))
            print(f"ALERT: Exception {exception_id} expires in {days_until_expiry} days")
            print(f"  Vulnerability: {vulnerability}")
            
            # Recall for review if expiring
            if days_until_expiry < 7:
                client.vulnerability_exceptions.recall(
                    exception_id=exception_id,
                    comment=f"Automatic recall: Exception expires in {days_until_expiry} days"
                )
                print(f"  Action: Recalled for review")

print(f"\nSummary:")
print(f"  Approved exceptions: {len(approved)}")
print(f"  Expiring within 30 days: {len(expiring_soon)}")
print(f"  Missing expiration: {len(no_expiration)}")

API Reference

VulnerabilityExceptionsAPI Class

Methods

list()
def list(page: int = 0, size: int = 10, sort: Optional[List[str]] = None) -> Dict

Retrieve paginated list of vulnerability exceptions.

Parameters:

  • page (int): Zero-based page index (default: 0)
  • size (int): Records per page, max 500 (default: 10)
  • sort (List[str], optional): Sorting criteria ["property,ASC|DESC"]

Returns: Dict with resources, page metadata, and links

get_exception()
def get_exception(exception_id: int) -> Dict

Get detailed information about a specific exception.

Parameters:

  • exception_id (int): Unique identifier of the exception

Returns: Dict with complete exception details

create()
def create(vulnerability_exception: Dict) -> Dict

Create a new vulnerability exception.

Parameters:

  • vulnerability_exception (Dict): Exception definition with scope, submit, and optional expires

Returns: Dict with ID and links to new exception

delete_exception()
def delete_exception(exception_id: int) -> None

Delete a vulnerability exception.

Parameters:

  • exception_id (int): Unique identifier of the exception

Returns: None

update_status()
def update_status(exception_id: int, status: str, comment: Optional[str] = None) -> Dict

Update exception status (approve/reject/recall).

Parameters:

  • exception_id (int): Unique identifier of the exception
  • status (str): New status ("approve", "reject", or "recall")
  • comment (str, optional): Comment explaining status change

Returns: Dict with links to updated exception

approve()
def approve(exception_id: int, comment: Optional[str] = None) -> Dict

Approve a vulnerability exception (convenience method).

Parameters:

  • exception_id (int): Unique identifier of the exception
  • comment (str, optional): Comment explaining approval

Returns: Dict with links to updated exception

reject()
def reject(exception_id: int, comment: Optional[str] = None) -> Dict

Reject a vulnerability exception (convenience method).

Parameters:

  • exception_id (int): Unique identifier of the exception
  • comment (str, optional): Comment explaining rejection

Returns: Dict with links to updated exception

recall()
def recall(exception_id: int, comment: Optional[str] = None) -> Dict

Recall an exception for re-review (convenience method).

Parameters:

  • exception_id (int): Unique identifier of the exception
  • comment (str, optional): Comment explaining recall

Returns: Dict with links to updated exception

get_expiration()
def get_expiration(exception_id: int) -> str

Get the expiration date of an exception.

Parameters:

  • exception_id (int): Unique identifier of the exception

Returns: ISO 8601 date string or empty string if no expiration

set_expiration()
def set_expiration(exception_id: int, expiration_date: str) -> Dict

Set or update exception expiration date.

Parameters:

  • exception_id (int): Unique identifier of the exception
  • expiration_date (str): ISO 8601 format date (e.g., "2025-12-31T23:59:59.000Z")

Returns: Dict with links to updated exception

get_all()
def get_all(sort: Optional[List[str]] = None) -> List[Dict]

Get all exceptions with automatic pagination.

Parameters:

  • sort (List[str], optional): Sorting criteria

Returns: List of all exception dictionaries

Exception Reasons

The following exception reasons are supported:

  • false_positive: Vulnerability detection is incorrect
  • compensating_control: Controls in place mitigate the risk
  • acceptable_risk: Risk is acceptable to the organization
  • other: Other justified reason (requires detailed comment)

Scope Types

Exceptions can be scoped to different levels:

  • global: All instances of vulnerability across all assets
  • site: Vulnerability instances in specific sites
  • asset_group: Vulnerability instances in specific asset groups
  • asset: Specific vulnerability instances on specific assets

Best Practices

  1. Always Provide Comments: Include detailed justification for exceptions
  2. Set Expiration Dates: Time-bound exceptions for periodic review
  3. Use Appropriate Scope: Apply narrowest scope necessary
  4. Document Compensating Controls: Detail security measures in place
  5. Regular Reviews: Periodically review approved exceptions
  6. Track Expiring Exceptions: Monitor and renew before expiration
  7. Workflow Consistency: Establish approval workflows
  8. Audit Trail: Use comment fields to maintain complete audit trail

Related Documentation