Documentation
¶
Overview ¶
Package wpprobe provides a public API for scanning WordPress sites for plugins and vulnerabilities.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DatabaseExists ¶
func DatabaseExists() bool
DatabaseExists checks if the Wordfence vulnerability database file exists.
func UpdateDatabases ¶
func UpdateDatabases() error
UpdateDatabases updates both Wordfence and WPScan vulnerability databases. WPScan update requires WPSCAN_API_TOKEN environment variable to be set. Returns an error only if Wordfence update fails. WPScan update failures are ignored (WPScan is optional and requires Enterprise plan). Logging is disabled during database updates when called from the API.
Types ¶
type Config ¶
type Config struct {
// Target URL to scan
Target string
// Scan mode: "stealthy", "bruteforce", or "hybrid"
ScanMode string
// Number of concurrent threads
Threads int
// Requests per second (0 = unlimited)
RateLimit int
// Custom HTTP headers (format: "Header: Value")
Headers []string
// Proxy URL (e.g., "http://proxy:8080")
Proxy string
// Maximum number of redirects to follow (0 = disable redirects, -1 = use default: 10)
MaxRedirects int
// Path to plugin list file (for bruteforce/hybrid modes)
PluginList string
// Skip version checking
NoCheckVersion bool
// Context for cancellation
Context context.Context
// Progress callback (optional)
ProgressCallback func(message string, current, total int)
// Enable verbose logging (default: false for API)
Verbose bool
// HTTPClient allows injecting an external HTTP client (e.g., from a connection pool).
// If provided, wpprobe will use this client instead of creating its own.
// The client should handle timeouts, TLS, and redirects as needed.
HTTPClient *http.Client
}
Config holds configuration for a WordPress scan.
type PluginResult ¶
type PluginResult struct {
// Plugin slug/name
Name string
// Detected version
Version string
// Confidence score (0-100)
Confidence float64
// Whether the detection is ambiguous
Ambiguous bool
// Vulnerabilities grouped by severity
Vulnerabilities VulnerabilitiesBySeverity
}
PluginResult represents a detected plugin with its vulnerabilities.
type ScanResult ¶
type ScanResult struct {
// Target URL that was scanned
Target string
// Detected plugins
Plugins []PluginResult
// Detected themes
Themes []ThemeResult
// Total number of vulnerabilities found
TotalVulnerabilities int
// Summary by severity
Summary VulnerabilitySummary
}
ScanResult contains the complete scan results.
type Scanner ¶
type Scanner struct{}
Scanner is the main scanner instance. It uses the global vulnerability cache to avoid memory duplication.
func New ¶
New creates a new Scanner instance. The scanner uses a global vulnerability cache that is loaded once and shared across all Scanner instances. This prevents memory bloat when creating many scanners. Logging is disabled during vulnerability loading when called from the API.
func (*Scanner) Reload ¶
Reload reloads vulnerabilities from the database files. Call this after UpdateDatabases() to use the newly downloaded data. Since all scanners share the global cache, this affects all Scanner instances.
func (*Scanner) Scan ¶
func (s *Scanner) Scan(cfg Config) (*ScanResult, error)
Scan performs a WordPress scan with the given configuration.
Example ¶
ExampleScanner_Scan demonstrates a basic WordPress scan.
package main
import (
"context"
"fmt"
"log"
wpprobe "github.com/Chocapikk/wpprobe/pkg"
)
func main() {
// Initialize the scanner
scanner, err := wpprobe.New()
if err != nil {
log.Fatalf("Failed to initialize scanner: %v", err)
}
// Configure the scan
cfg := wpprobe.Config{
Target: "https://example.com",
ScanMode: "stealthy", // Options: "stealthy", "bruteforce", "hybrid"
Threads: 10, // Number of concurrent threads
RateLimit: 5, // Requests per second (0 = unlimited)
MaxRedirects: 10, // Maximum redirects to follow (0 = disable, -1 = default: 10)
Context: context.Background(),
}
// Perform the scan
result, err := scanner.Scan(cfg)
if err != nil {
log.Fatalf("Scan failed: %v", err)
}
// Display results
fmt.Printf("Scan completed for: %s\n", result.Target)
fmt.Printf("Plugins detected: %d\n", len(result.Plugins))
fmt.Printf("Total vulnerabilities: %d\n", result.TotalVulnerabilities)
fmt.Printf("Severity breakdown: Critical=%d, High=%d, Medium=%d, Low=%d\n",
result.Summary.Critical,
result.Summary.High,
result.Summary.Medium,
result.Summary.Low,
)
// Iterate over detected plugins
for _, plugin := range result.Plugins {
fmt.Printf("\nPlugin: %s (v%s)\n", plugin.Name, plugin.Version)
if len(plugin.Vulnerabilities.Critical) > 0 {
fmt.Printf(" Critical: %d\n", len(plugin.Vulnerabilities.Critical))
}
if len(plugin.Vulnerabilities.High) > 0 {
fmt.Printf(" High: %d\n", len(plugin.Vulnerabilities.High))
}
if len(plugin.Vulnerabilities.Medium) > 0 {
fmt.Printf(" Medium: %d\n", len(plugin.Vulnerabilities.Medium))
}
if len(plugin.Vulnerabilities.Low) > 0 {
fmt.Printf(" Low: %d\n", len(plugin.Vulnerabilities.Low))
}
}
}
Output:
Example (BruteforceMode) ¶
ExampleScanner_Scan_bruteforceMode demonstrates using bruteforce scan mode.
package main
import (
"context"
"fmt"
"log"
wpprobe "github.com/Chocapikk/wpprobe/pkg"
)
func main() {
scanner, err := wpprobe.New()
if err != nil {
log.Fatalf("Failed to initialize scanner: %v", err)
}
cfg := wpprobe.Config{
Target: "https://example.com",
ScanMode: "bruteforce", // Brute-force mode checks thousands of plugins
Threads: 20, // Higher thread count for faster bruteforce
RateLimit: 10, // Limit rate to avoid overwhelming the server
PluginList: "plugins.txt", // Path to plugin list file
Context: context.Background(),
}
result, err := scanner.Scan(cfg)
if err != nil {
log.Fatalf("Scan failed: %v", err)
}
fmt.Printf("Bruteforce scan completed: %d plugins found\n", len(result.Plugins))
}
Output:
Example (FilterResults) ¶
ExampleScanner_Scan_filterResults demonstrates how to filter and process scan results.
package main
import (
"context"
"fmt"
"log"
wpprobe "github.com/Chocapikk/wpprobe/pkg"
)
func main() {
scanner, err := wpprobe.New()
if err != nil {
log.Fatalf("Failed to initialize scanner: %v", err)
}
cfg := wpprobe.Config{
Target: "https://example.com",
ScanMode: "stealthy",
Context: context.Background(),
}
result, err := scanner.Scan(cfg)
if err != nil {
log.Fatalf("Scan failed: %v", err)
}
// Filter plugins with critical vulnerabilities
var criticalPlugins []wpprobe.PluginResult
for _, plugin := range result.Plugins {
if len(plugin.Vulnerabilities.Critical) > 0 {
criticalPlugins = append(criticalPlugins, plugin)
}
}
fmt.Printf("Total plugins: %d\n", len(result.Plugins))
fmt.Printf("Plugins with critical vulnerabilities: %d\n", len(criticalPlugins))
// Display critical CVEs
for _, plugin := range criticalPlugins {
fmt.Printf("\n%s (v%s) - Critical CVEs:\n", plugin.Name, plugin.Version)
for _, vuln := range plugin.Vulnerabilities.Critical {
fmt.Printf(" - %s: %s\n", vuln.CVE, vuln.Title)
}
}
}
Output:
Example (HybridMode) ¶
ExampleScanner_Scan_hybridMode demonstrates using hybrid scan mode.
package main
import (
"context"
"fmt"
"log"
wpprobe "github.com/Chocapikk/wpprobe/pkg"
)
func main() {
scanner, err := wpprobe.New()
if err != nil {
log.Fatalf("Failed to initialize scanner: %v", err)
}
cfg := wpprobe.Config{
Target: "https://example.com",
ScanMode: "hybrid", // Hybrid mode: stealthy first, then bruteforce
Threads: 15,
RateLimit: 5,
PluginList: "plugins.txt",
Context: context.Background(),
}
result, err := scanner.Scan(cfg)
if err != nil {
log.Fatalf("Scan failed: %v", err)
}
fmt.Printf("Hybrid scan completed: %d plugins found\n", len(result.Plugins))
}
Output:
Example (WithCustomHeaders) ¶
ExampleScanner_Scan_withCustomHeaders demonstrates scanning with custom HTTP headers.
package main
import (
"context"
"fmt"
"log"
wpprobe "github.com/Chocapikk/wpprobe/pkg"
)
func main() {
scanner, err := wpprobe.New()
if err != nil {
log.Fatalf("Failed to initialize scanner: %v", err)
}
cfg := wpprobe.Config{
Target: "https://example.com",
ScanMode: "stealthy",
Threads: 10,
Headers: []string{
"User-Agent: CustomScanner/1.0",
"X-Custom-Header: value",
},
Context: context.Background(),
}
result, err := scanner.Scan(cfg)
if err != nil {
log.Fatalf("Scan failed: %v", err)
}
fmt.Printf("Scan completed: %d plugins found\n", len(result.Plugins))
}
Output:
Example (WithProgress) ¶
ExampleScanner_Scan_withProgress demonstrates scanning with progress tracking.
package main
import (
"fmt"
"log"
wpprobe "github.com/Chocapikk/wpprobe/pkg"
)
func main() {
scanner, err := wpprobe.New()
if err != nil {
log.Fatalf("Failed to initialize scanner: %v", err)
}
cfg := wpprobe.Config{
Target: "https://example.com",
ScanMode: "stealthy",
Threads: 10,
ProgressCallback: func(message string, current, total int) {
// This callback is invoked during the scan to report progress
fmt.Printf("[%d/%d] %s\n", current, total, message)
},
}
result, err := scanner.Scan(cfg)
if err != nil {
log.Fatalf("Scan failed: %v", err)
}
fmt.Printf("Scan completed: %d plugins found\n", len(result.Plugins))
}
Output:
Example (WithProxy) ¶
ExampleScanner_Scan_withProxy demonstrates scanning through a proxy.
package main
import (
"context"
"fmt"
"log"
wpprobe "github.com/Chocapikk/wpprobe/pkg"
)
func main() {
scanner, err := wpprobe.New()
if err != nil {
log.Fatalf("Failed to initialize scanner: %v", err)
}
cfg := wpprobe.Config{
Target: "https://example.com",
ScanMode: "stealthy",
Threads: 10,
Proxy: "http://proxy.example.com:8080", // Proxy URL
Context: context.Background(),
}
result, err := scanner.Scan(cfg)
if err != nil {
log.Fatalf("Scan failed: %v", err)
}
fmt.Printf("Scan completed: %d plugins found\n", len(result.Plugins))
}
Output:
Example (WithTimeout) ¶
ExampleScanner_Scan_withTimeout demonstrates scanning with a timeout.
package main
import (
"context"
"fmt"
"log"
"time"
wpprobe "github.com/Chocapikk/wpprobe/pkg"
)
func main() {
scanner, err := wpprobe.New()
if err != nil {
log.Fatalf("Failed to initialize scanner: %v", err)
}
// Create a context with 30 second timeout
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
cfg := wpprobe.Config{
Target: "https://example.com",
ScanMode: "stealthy",
Threads: 5,
Context: ctx,
}
result, err := scanner.Scan(cfg)
if err != nil {
if err == context.DeadlineExceeded {
log.Println("Scan timed out")
} else {
log.Fatalf("Scan failed: %v", err)
}
return
}
fmt.Printf("Scan completed: %d plugins found\n", len(result.Plugins))
}
Output:
type ThemeResult ¶ added in v0.11.0
type ThemeResult struct {
// Theme slug/name
Name string
// Detected version
Version string
// Vulnerabilities grouped by severity
Vulnerabilities VulnerabilitiesBySeverity
}
ThemeResult represents a detected theme with its vulnerabilities.
type VulnerabilitiesBySeverity ¶
type VulnerabilitiesBySeverity struct {
Critical []Vulnerability
High []Vulnerability
Medium []Vulnerability
Low []Vulnerability
}
VulnerabilitiesBySeverity groups vulnerabilities by severity level.
type Vulnerability ¶
type Vulnerability struct {
// CVE identifier (e.g., "CVE-2024-1234")
CVE string
// Title/description
Title string
// Severity: "critical", "high", "medium", "low"
Severity string
// Authentication type: "unauth", "privileged", "none"
AuthType string
// Affected version range
AffectedVersion string
// CVSS score (0-10)
CVSSScore float64
// CVSS vector string
CVSSVector string
}
Vulnerability represents a single vulnerability.