Add webpage screenshot capture with Playwright

Implements automated screenshot capture for all discovered HTTP/HTTPS services using Playwright with headless Chromium. Screenshots are saved as PNG files and referenced in JSON reports.

Features:
- Separate ScreenshotCapture module for code organization
- Viewport screenshots (1280x720) with 15-second timeout
- Graceful handling of self-signed certificates
- Browser reuse for optimal performance
- Screenshots stored in timestamped directories
- Comprehensive documentation in README.md and new CLAUDE.md

Technical changes:
- Added src/screenshot_capture.py: Screenshot capture module with context manager pattern
- Updated src/scanner.py: Integrated screenshot capture into HTTP/HTTPS analysis phase
- Updated Dockerfile: Added Chromium and Playwright browser installation
- Updated requirements.txt: Added playwright==1.40.0
- Added CLAUDE.md: Developer documentation and implementation guide
- Updated README.md: Enhanced features section, added screenshot details and troubleshooting
- Updated .gitignore: Ignore entire output/ directory including screenshots

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-14 00:57:36 +00:00
parent 48755a8539
commit 61cc24f8d2
7 changed files with 822 additions and 25 deletions

View File

@@ -5,6 +5,7 @@ SneakyScanner - Masscan-based network scanner with YAML configuration
import argparse
import json
import logging
import subprocess
import sys
import tempfile
@@ -18,6 +19,8 @@ import yaml
from libnmap.process import NmapProcess
from libnmap.parser import NmapParser
from screenshot_capture import ScreenshotCapture
# Force unbuffered output for Docker
sys.stdout.reconfigure(line_buffering=True)
sys.stderr.reconfigure(line_buffering=True)
@@ -31,6 +34,7 @@ class SneakyScanner:
self.output_dir = Path(output_dir)
self.output_dir.mkdir(parents=True, exist_ok=True)
self.config = self._load_config()
self.screenshot_capture = None
def _load_config(self) -> Dict[str, Any]:
"""Load and validate YAML configuration"""
@@ -511,6 +515,16 @@ class SneakyScanner:
result = {'protocol': protocol}
# Capture screenshot if screenshot capture is enabled
if self.screenshot_capture:
try:
screenshot_path = self.screenshot_capture.capture(ip, port, protocol)
if screenshot_path:
result['screenshot'] = screenshot_path
except Exception as e:
print(f" Screenshot capture error for {ip}:{port}: {e}",
file=sys.stderr, flush=True)
# If HTTPS, analyze SSL/TLS
if protocol == 'https':
try:
@@ -545,6 +559,14 @@ class SneakyScanner:
# Record start time
start_time = time.time()
scan_timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
# Initialize screenshot capture
self.screenshot_capture = ScreenshotCapture(
output_dir=str(self.output_dir),
scan_timestamp=scan_timestamp,
timeout=15
)
# Collect all unique IPs
all_ips = set()
@@ -658,6 +680,10 @@ class SneakyScanner:
report['sites'].append(site_result)
# Clean up screenshot capture browser
if self.screenshot_capture:
self.screenshot_capture._close_browser()
return report
def save_report(self, report: Dict[str, Any]) -> Path:
@@ -673,6 +699,13 @@ class SneakyScanner:
def main():
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[logging.StreamHandler(sys.stderr)]
)
parser = argparse.ArgumentParser(
description='SneakyScanner - Masscan-based network scanner'
)