This document summarizes the implementation of all requested features and bug fixes for the SubScraper reconnaissance tool.
Implemented:
- Added comprehensive filter controls to the Reports page
- Filters include:
- Domain Search: Case-insensitive text search across all domains
- Status Filter: All / Pending / Complete
- Min Severity Filter: All / Info+ / Low+ / Medium+ / High+ / Critical
- Has Findings: Checkbox to show only domains with Nuclei or Nikto findings
- Has Screenshots: Checkbox to show only domains with captured screenshots
- Filter preferences persist in browser localStorage
- Live count showing "X of Y reports" after filtering
- All filters work together (AND logic)
Location in Code:
getReportFilters(): Loads filters from localStoragesaveReportFiltersToStorage(): Persists filter stateattachReportFilterListeners(): Binds event handlersrenderReports(): Main function with filtering logic (lines ~8094-8300)
Usage:
- Navigate to Reports page
- Use filter controls at the top of the page
- Filters apply instantly as you type/select
- Filter preferences saved automatically
Implemented:
- Modified JavaScript polling to be context-aware
- Auto-refresh only enabled on: Overview, Jobs, Queue, Workers, System Resources, Monitors, Logs
- Auto-refresh disabled on: Reports, Gallery, Settings, and all detail pages
- Refresh occurs on view change for fresh data
Location in Code:
- Lines ~9952-9980 in main.py (JavaScript section)
VIEWS_WITH_AUTO_REFRESHarray defines which pages get auto-refreshstartPolling()function checks current view before fetchinghashchangeevent handler refreshes when switching to monitored views
Technical Details:
- Poll interval: 8 seconds (configurable via
POLL_INTERVAL) - Uses
setIntervalbut only executes fetch on allowed views - Prevents unnecessary API calls on static pages
- Improves performance and reduces server load
Implemented:
- Added interactive canvas-based network visualization
- Shows domain as central node with subdomains in orbit
- Nodes are clickable:
- Click subdomain → Navigate to subdomain detail page
- Click domain → Navigate to Reports page for that domain
- Toggle button to show/hide map
- Responsive design with high-DPI support
Location in Code:
toggleNodeMap(): Shows/hides the visualizationinitNodeMap(): Initializes canvas and datadrawNodeMap(): Renders the network graph (lines ~7390-7530)handleNodeMapClick(): Handles click interactions
Visual Features:
- Domain: Blue circle in center (larger)
- Subdomains: Blue circles orbiting the domain
- Connecting lines from domain to each subdomain
- Labels for nodes (abbreviated if many subdomains)
- Legend showing subdomain count
- Smooth canvas rendering with devicePixelRatio support
Usage:
- Navigate to Overview page
- Each target card has a "Toggle Map" button
- Click to reveal the network visualization
- Click on any subdomain node to view its details
- Click on the central domain node to view the full report
Problem Identified:
The tool could start more jobs than MAX_RUNNING_JOBS due to a race condition:
schedule_jobs()checked available slots- Collected jobs to start
- Started threads OUTSIDE the lock
- Multiple concurrent
schedule_jobs()calls could start too many threads
Solution Implemented:
- Fix 1: Modified
_start_job_thread()to start thread WITHINJOB_LOCK(line ~5463) - Fix 2: Modified
schedule_jobs()to start jobs one at a time (lines ~5467-5493) - Fix 3: Each job start checks slot availability while holding lock
Location in Code:
_start_job_thread(): Lines ~5413-5464- Thread started while holding JOB_LOCK (line 5463)
- Ensures
count_active_jobs_locked()sees thread immediately
schedule_jobs(): Lines ~5467-5493- Changed from batch processing to sequential with lock checks
- Loop acquires lock, checks slots, starts one job, releases lock, repeats
Thread Safety Guarantees:
count_active_jobs_locked()must be called with JOB_LOCK held- Thread start happens atomically with slot check
- No window for race conditions
- Works correctly even during connection failures
Testing:
test_schedule_jobs_thread_safety(): Validates no race conditions under concurrent accesstest_schedule_jobs_respects_max_limit(): Ensures slot limit never exceeded
Test Coverage:
Created test_main.py with 18 comprehensive tests across 6 test classes:
test_count_active_jobs_locked_empty: Validates counting with no jobstest_count_active_jobs_locked_with_jobs: Tests counting with mixed alive/dead threadstest_count_active_jobs_locked_no_thread: Tests jobs without threadstest_schedule_jobs_respects_max_limit: Ensures MAX_RUNNING_JOBS respectedtest_schedule_jobs_starts_multiple_if_slots_available: Tests batch startingtest_schedule_jobs_thread_safety: Critical test for race conditions
test_severity_comparison: Validates severity level orderingtest_domain_search_filter: Tests case-insensitive search
test_build_state_payload_structure: Validates API response formattest_build_state_payload_includes_completed_jobs: Tests job merging
test_insert_target: Tests target creationtest_insert_subdomain: Tests subdomain creationtest_cascade_delete: Validates foreign key constraints
test_job_lock_prevents_race_conditions: Critical lock validationtest_tool_gate_limits_concurrent_access: Tests ToolGate mechanism
test_is_subdomain_input: Tests subdomain detectiontest_sanitize_domain_input: Tests input cleaningtest_is_rate_limit_error: Tests error classification
Running Tests:
# Install dependencies
pip3 install -r requirements.txt
# Run all tests
python3 -m pytest test_main.py -v
# Run with coverage
python3 -m pytest test_main.py -v --cov=main --cov-report=htmlDocumentation:
TESTING.md: Comprehensive guide for running and understanding testsrequirements.txt: Lists testing dependencies (pytest, pytest-cov, pytest-timeout)
- Lines 5413-5464: Fixed
_start_job_thread()race condition - Lines 5467-5493: Fixed
schedule_jobs()to be thread-safe - Lines 8090-8160: Added report filter functions
- Lines 8094-8300: Updated
renderReports()with filtering - Lines 7348-7368: Added node map to target cards
- Lines 7390-7530: Implemented node map drawing functions
- Lines 9952-9980: Made polling context-aware
- test_main.py: Comprehensive test suite (18 tests)
- requirements.txt: Testing dependencies
- TESTING.md: Testing documentation and guide
- IMPLEMENTATION.md: This file
All changes maintain existing security measures:
- Input sanitization preserved
- SQL injection protection via parameterized queries
- Path traversal protection in file serving
- Thread safety improved (reduces potential for data corruption)
- No new external dependencies (except testing tools)
Improvements:
- Reduced unnecessary API calls (context-aware polling)
- Filter operations done in-memory (fast)
- Node map uses canvas (GPU-accelerated)
- Thread safety fixes prevent lock contention
No Negative Impact:
- Filter adds minimal overhead (O(n) where n = number of domains)
- Node map only renders when visible
- Job scheduling fixes add negligible overhead (proper locking)
All changes are backward compatible:
- Existing data structures unchanged
- Database schema unchanged
- API endpoints unchanged
- Configuration options unchanged
- Filter preferences optional (defaults provided)
Potential improvements not in scope:
-
Advanced Filters:
- Filter by specific tool results
- Date range filtering
- Export filtered results
-
Node Map Enhancements:
- 3D visualization option
- Clustering for large datasets
- Export as image
-
Performance:
- Pagination for very large reports
- Virtual scrolling for subdomain lists
- Worker threads for heavy processing
-
Testing:
- End-to-end browser tests with Selenium
- Load testing for concurrent jobs
- Performance benchmarks
# Set MAX_RUNNING_JOBS to 2 in settings
# Start 5 jobs quickly
# Observe: Never more than 2 running simultaneously
# Check logs: No "exceeded slot limit" warnings# Navigate to Reports page
# Open browser DevTools → Network tab
# Observe: No /api/state calls every 8 seconds
# Navigate to Overview page
# Observe: /api/state calls resume every 8 seconds# Navigate to Reports page
# Set "Min Severity" to "High+"
# Observe: Only domains with HIGH or CRITICAL findings shown
# Set "Has Findings" checkbox
# Observe: Only domains with Nuclei/Nikto findings shown
# Refresh page
# Observe: Filters still applied (localStorage persistence)# Navigate to Overview page
# Click "Toggle Map" on any target
# Observe: Network visualization appears
# Click on any subdomain node
# Observe: Navigates to subdomain detail pagecd /home/runner/work/subScraper/subScraper
pip3 install -r requirements.txt
python3 -m pytest test_main.py -v
# Expected: 18 passedAll requirements from the problem statement have been successfully implemented:
✅ Filter reports by severity, domain, status, findings, and screenshots
✅ Fixed refresh interval to only work on overview/monitoring pages
✅ Added node map visualization with clickable nodes
✅ Fixed job slot overflow race condition
✅ Added comprehensive tests with 18 test cases
The implementation is minimal, focused, and follows the existing code patterns. All changes are well-documented and tested.