161ef94b4f
Check engine pin consistency / Dockerfile / CI pin consistency (push) Successful in 8s
Sirius CI/CD Pipeline / Detect Changes (push) Successful in 23s
Validate Docker Configuration / Validate Docker Compose Configuration (push) Successful in 47s
Sirius CI/CD Pipeline / Build API (${{ matrix.platform }}) (push) Has been cancelled
Sirius CI/CD Pipeline / Build UI (${{ matrix.platform }}) (push) Has been cancelled
Sirius CI/CD Pipeline / Merge Engine Manifest (push) Has been cancelled
Sirius CI/CD Pipeline / Merge API Manifest (push) Has been cancelled
Sirius CI/CD Pipeline / Merge UI Manifest (push) Has been cancelled
Sirius CI/CD Pipeline / Build Engine (${{ matrix.platform }}) (push) Has been cancelled
Sirius CI/CD Pipeline / Build Infra (${{ matrix.service }}, ${{ matrix.platform }}) (push) Has been cancelled
Sirius CI/CD Pipeline / Merge Infra Manifest (sirius-postgres) (push) Has been cancelled
Sirius CI/CD Pipeline / Merge Infra Manifest (sirius-rabbitmq) (push) Has been cancelled
Sirius CI/CD Pipeline / Merge Infra Manifest (sirius-valkey) (push) Has been cancelled
Sirius CI/CD Pipeline / Integration Test (push) Has been cancelled
Sirius CI/CD Pipeline / Public Stack Contract (push) Has been cancelled
Sirius CI/CD Pipeline / Dispatch Demo Deployment (sirius-demo branch) (push) Has been cancelled
Sirius CI/CD Pipeline / Dispatch Demo Canary (main branch) (push) Has been cancelled
Sirius CI/CD Pipeline / Guard Registry Namespace (push) Has been cancelled
2116 lines
53 KiB
Markdown
2116 lines
53 KiB
Markdown
---
|
||
title: "Sirius Scanner - Vulnerability Scanning Engine"
|
||
description: "Comprehensive documentation for the Sirius vulnerability scanning engine"
|
||
template: "TEMPLATE.documentation-standard"
|
||
version: "1.0.0"
|
||
last_updated: "2025-10-25"
|
||
author: "Sirius Team"
|
||
tags: ["scanner", "vulnerability", "nmap", "nse", "go", "rabbitmq"]
|
||
categories: ["backend", "security", "scanning"]
|
||
difficulty: "advanced"
|
||
prerequisites: ["Go 1.23+", "Docker", "RabbitMQ", "Nmap"]
|
||
related_docs:
|
||
- "documentation/dev/architecture/README.architecture.md"
|
||
- "documentation/dev/README.development.md"
|
||
dependencies:
|
||
- "../minor-projects/app-scanner"
|
||
- "../minor-projects/go-api"
|
||
- "../minor-projects/sirius-nse"
|
||
llm_context: "high"
|
||
search_keywords:
|
||
[
|
||
"scanner",
|
||
"vulnerability",
|
||
"nmap",
|
||
"nse",
|
||
"rabbitmq",
|
||
"valkey",
|
||
"scanning-engine",
|
||
]
|
||
---
|
||
|
||
# Sirius Scanner - Vulnerability Scanning Engine
|
||
|
||
## Overview
|
||
|
||
Sirius Scanner is a sophisticated, modular vulnerability scanning engine that orchestrates multiple security scanning tools (Nmap, RustScan, Naabu) through a message-driven architecture. Built in Go, it processes scan requests from RabbitMQ, executes multi-phase security assessments, and enriches vulnerability data with NVD information.
|
||
|
||
**Key Features:**
|
||
|
||
- **Message-Driven Architecture**: RabbitMQ-based scan request processing
|
||
- **Multi-Phase Scanning**: Enumeration → Discovery → Vulnerability assessment
|
||
- **Concurrent Processing**: Worker pool pattern for parallel target scanning
|
||
- **NSE Script Management**: Git-based synchronization with curated script repository
|
||
- **Template System**: Pre-configured and custom scan templates
|
||
- **Source Attribution**: Comprehensive tracking of scan origins and configurations
|
||
- **Real-Time Updates**: ValKey integration for live scan progress monitoring
|
||
|
||
**Repository Location:** `../minor-projects/app-scanner`
|
||
|
||
---
|
||
|
||
## Table of Contents
|
||
|
||
1. [Core Architecture](#core-architecture)
|
||
2. [Scanning Strategies](#scanning-strategies)
|
||
3. [NSE Script Management](#nse-script-management)
|
||
4. [Template System](#template-system)
|
||
5. [Scan Message Format](#scan-message-format)
|
||
6. [Target Processing](#target-processing)
|
||
7. [Source Attribution System](#source-attribution-system)
|
||
8. [State Management](#state-management)
|
||
9. [Docker Integration](#docker-integration)
|
||
10. [Configuration Files](#configuration-files)
|
||
11. [Development Workflow](#development-workflow)
|
||
12. [Key Files Reference](#key-files-reference)
|
||
13. [ValKey Schema Reference](#valkey-schema-reference)
|
||
14. [Profile vs Template System](#profile-vs-template-system)
|
||
15. [RabbitMQ Message Schema](#rabbitmq-message-schema)
|
||
|
||
---
|
||
|
||
## Core Architecture
|
||
|
||
### Message-Driven Design
|
||
|
||
The scanner operates as a **RabbitMQ consumer**, listening on the `scan` queue for incoming scan requests. This design enables:
|
||
|
||
- **Asynchronous Processing**: Scans don't block the API
|
||
- **Load Distribution**: Multiple scanner instances can consume from the same queue
|
||
- **Fault Tolerance**: Failed scans can be retried without data loss
|
||
- **Priority Handling**: Scan requests include priority levels (1-5)
|
||
|
||
**Entry Point:**
|
||
|
||
```go
|
||
// main.go
|
||
func main() {
|
||
scanManager := scan.NewScanManager(kvStore, toolFactory, scanUpdater)
|
||
scanManager.ListenForScans() // Blocks, listening for RabbitMQ messages
|
||
select {} // Keep service running
|
||
}
|
||
```
|
||
|
||
### Multi-Phase Scanning Workflow
|
||
|
||
Scans proceed through up to three phases based on `scan_types` configuration:
|
||
|
||
```
|
||
┌─────────────────┐
|
||
│ Enumeration │ Naabu: Fast port enumeration (SYN scan)
|
||
│ (Optional) │ Output: List of open ports
|
||
└────────┬────────┘
|
||
│
|
||
▼
|
||
┌─────────────────┐
|
||
│ Discovery │ RustScan: Rapid host/port discovery
|
||
│ (Optional) │ Output: Live hosts with open ports
|
||
└────────┬────────┘
|
||
│
|
||
▼
|
||
┌─────────────────┐
|
||
│ Vulnerability │ Nmap + NSE: Deep vulnerability scanning
|
||
│ (Required) │ Output: CVEs, service versions, OS detection
|
||
└─────────────────┘
|
||
```
|
||
|
||
**Phase Selection:**
|
||
|
||
```json
|
||
{
|
||
"options": {
|
||
"scan_types": ["enumeration", "discovery", "vulnerability"]
|
||
}
|
||
}
|
||
```
|
||
|
||
### Worker Pool Pattern
|
||
|
||
The scanner uses a **concurrent worker pool** (default: 10 workers) to process multiple targets simultaneously:
|
||
|
||
```go
|
||
// internal/scan/worker_pool.go
|
||
type WorkerPool struct {
|
||
workerCount int
|
||
taskQueue chan ScanTask
|
||
manager *ScanManager
|
||
}
|
||
|
||
// Each worker runs in its own goroutine
|
||
func (wp *WorkerPool) worker(ctx context.Context, id int) {
|
||
for {
|
||
select {
|
||
case task := <-wp.taskQueue:
|
||
wp.manager.scanIP(task.IP) // Execute scan
|
||
case <-ctx.Done():
|
||
return
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**Benefits:**
|
||
|
||
- **Parallel Execution**: Scan multiple IPs concurrently
|
||
- **Resource Control**: Limit concurrent scans to prevent system overload
|
||
- **Graceful Shutdown**: Context-based cancellation
|
||
|
||
### Strategy Pattern
|
||
|
||
The scanner implements the **Strategy Pattern** for pluggable scan tools:
|
||
|
||
```go
|
||
// internal/scan/strategies.go
|
||
type ScanStrategy interface {
|
||
Execute(target string) (sirius.Host, error)
|
||
}
|
||
|
||
// Implementations:
|
||
type NaabuStrategy struct { ... } // Port enumeration
|
||
type RustScanStrategy struct { ... } // Discovery
|
||
type NmapStrategy struct { ... } // Vulnerability scanning
|
||
```
|
||
|
||
**Advantages:**
|
||
|
||
- **Extensibility**: Add new scanning tools without modifying core logic
|
||
- **Testability**: Mock strategies for unit testing
|
||
- **Flexibility**: Swap implementations based on requirements
|
||
|
||
### Factory Pattern
|
||
|
||
The `ScanToolFactory` dynamically creates appropriate strategies:
|
||
|
||
```go
|
||
// internal/scan/factory.go
|
||
func (f *ScanToolFactory) CreateTool(toolType string) ScanStrategy {
|
||
switch toolType {
|
||
case "enumeration":
|
||
return &NaabuStrategy{...}
|
||
case "discovery":
|
||
return &RustScanStrategy{}
|
||
case "vulnerability":
|
||
return &NmapStrategy{...}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Scanning Strategies
|
||
|
||
### NaabuStrategy (Port Enumeration)
|
||
|
||
**Purpose:** Fast, accurate port enumeration using ProjectDiscovery's Naabu.
|
||
|
||
**Technology:** SYN-based port scanning (requires root/CAP_NET_RAW)
|
||
|
||
**Configuration:**
|
||
|
||
```go
|
||
type NaabuStrategy struct {
|
||
Ports string // Port range (e.g., "1-65535", "80,443,8080")
|
||
Retries int // Number of retry attempts
|
||
}
|
||
```
|
||
|
||
**Implementation Details:**
|
||
|
||
- Uses `github.com/projectdiscovery/naabu/v2` library
|
||
- Default timeout: 5 seconds per host
|
||
- Returns error `ErrHostDown` if no open ports found
|
||
- Outputs `sirius.Host` with populated `Ports` array
|
||
|
||
**Example Usage:**
|
||
|
||
```go
|
||
strategy := &NaabuStrategy{
|
||
Ports: "1-10000",
|
||
Retries: 3,
|
||
}
|
||
host, err := strategy.Execute("192.168.1.100")
|
||
// host.Ports: [{ID: 80, Protocol: "tcp", State: "open"}, ...]
|
||
```
|
||
|
||
**When to Use:**
|
||
|
||
- Need comprehensive port enumeration
|
||
- Scanning large port ranges (1-65535)
|
||
- Require retry logic for unstable networks
|
||
- Want detailed port state information
|
||
|
||
### RustScanStrategy (Discovery)
|
||
|
||
**Purpose:** Ultra-fast port discovery for identifying live hosts.
|
||
|
||
**Technology:** Adaptive multi-threaded port scanning (RustScan binary)
|
||
|
||
**Configuration:**
|
||
|
||
```go
|
||
type RustScanStrategy struct {
|
||
// Uses default RustScan settings
|
||
// Automatically adjusts thread count based on available resources
|
||
}
|
||
```
|
||
|
||
**Implementation Details:**
|
||
|
||
- Executes `rustscan` binary via `exec.Command`
|
||
- Parses output to extract open ports
|
||
- Filters results: hosts with no open ports are skipped
|
||
- Much faster than Nmap for initial discovery (5-20x speedup)
|
||
|
||
**Example Usage:**
|
||
|
||
```go
|
||
strategy := &RustScanStrategy{}
|
||
host, err := strategy.Execute("192.168.1.100")
|
||
// host.Ports: [{ID: 22, ...}, {ID: 80, ...}, {ID: 443, ...}]
|
||
```
|
||
|
||
**When to Use:**
|
||
|
||
- Scanning large IP ranges (entire subnets)
|
||
- Need rapid feedback on live hosts
|
||
- Initial reconnaissance phase
|
||
- Time-sensitive assessments
|
||
|
||
### NmapStrategy (Vulnerability Scanning)
|
||
|
||
**Purpose:** Deep vulnerability assessment with NSE script execution.
|
||
|
||
**Technology:** Nmap with NSE (Nmap Scripting Engine) for CVE detection
|
||
|
||
**Configuration:**
|
||
|
||
```go
|
||
type NmapStrategy struct {
|
||
Protocols []string // Legacy: protocol-based selection (e.g., ["smb", "http"])
|
||
ScriptList []string // Explicit script names (overrides Protocols)
|
||
}
|
||
```
|
||
|
||
**Implementation Details:**
|
||
|
||
- **Script Selection:**
|
||
|
||
- **Template-based**: Uses `EnabledScripts` from template
|
||
- **Protocol-based**: Selects scripts matching protocols (smb, http, ssh, ftp, rdp)
|
||
- **Wildcard**: `["*"]` runs all available NSE scripts
|
||
|
||
- **Nmap Command Construction:**
|
||
|
||
```bash
|
||
nmap -T4 -sV -Pn -p <ports> --script <scripts> --script-args-file <args.txt> <target> -oX -
|
||
```
|
||
|
||
- **CVE Extraction:** Parses Nmap XML output for CVE patterns
|
||
- **Vulnerability Enrichment:** Fetches CVE details from NVD API
|
||
- **Fallback Mechanism:** If script errors occur, retries with minimal safe scripts
|
||
|
||
**Example Usage:**
|
||
|
||
```go
|
||
// Template-based (recommended)
|
||
strategy := &NmapStrategy{
|
||
ScriptList: []string{"vulners", "smb-vuln-ms17-010", "http-shellshock"},
|
||
}
|
||
|
||
// Protocol-based
|
||
strategy := &NmapStrategy{
|
||
Protocols: []string{"smb", "http"},
|
||
}
|
||
|
||
host, err := strategy.Execute("192.168.1.100")
|
||
// host.Vulnerabilities: [{VID: "CVE-2017-0143", RiskScore: 9.3, ...}, ...]
|
||
```
|
||
|
||
**Script Processing:**
|
||
|
||
1. **Execution**: Nmap runs selected NSE scripts against target
|
||
2. **Parsing**: Extract structured data from script output
|
||
3. **CVE Detection**: Regex matching for `CVE-YYYY-NNNNN` patterns
|
||
4. **Enrichment**: Query NVD API for descriptions, CVSS scores
|
||
5. **Deduplication**: Remove duplicate CVEs from different scripts
|
||
|
||
**Fallback Scan:**
|
||
|
||
If NSE scripts fail (e.g., script syntax errors), scanner automatically retries with minimal safe scripts:
|
||
|
||
```go
|
||
safeScripts := []string{"banner", "http-title", "ssl-cert"}
|
||
```
|
||
|
||
**When to Use:**
|
||
|
||
- Deep security assessment required
|
||
- Need CVE identification
|
||
- Service version detection
|
||
- OS fingerprinting
|
||
- Protocol-specific vulnerability checks
|
||
|
||
---
|
||
|
||
## NSE Script Management
|
||
|
||
The scanner maintains a curated collection of NSE scripts via the `sirius-nse` repository.
|
||
|
||
### Repository Management (`internal/nse/repo.go`)
|
||
|
||
**Purpose:** Git-based synchronization of NSE scripts.
|
||
|
||
**Repository URL:** `https://github.com/SiriusScan/sirius-nse.git`
|
||
|
||
**Local Path:** `/opt/sirius/nse/sirius-nse` (in Docker container)
|
||
|
||
**Operations:**
|
||
|
||
```go
|
||
type RepoManager struct {
|
||
BasePath string // /opt/sirius/nse/sirius-nse
|
||
RepoURL string // GitHub repository
|
||
gitOps GitOperations
|
||
}
|
||
|
||
// Ensure repository exists and is up-to-date
|
||
func (rm *RepoManager) EnsureRepo() error {
|
||
if !rm.isGitRepo() {
|
||
// Clone on first run
|
||
return rm.gitOps.Clone(rm.RepoURL, rm.BasePath)
|
||
}
|
||
// Update existing repository
|
||
return rm.updateRepo()
|
||
}
|
||
|
||
// Update: fetch + reset to origin/main
|
||
func (rm *RepoManager) updateRepo() error {
|
||
rm.gitOps.Fetch(rm.BasePath)
|
||
rm.gitOps.Reset(rm.BasePath) // Hard reset to origin/main
|
||
return nil
|
||
}
|
||
```
|
||
|
||
**Sync Timing:**
|
||
|
||
- **Startup**: Syncs when `ScanManager` initializes
|
||
- **Before Scans**: Automatic sync before first scan (via `ListenForScans`)
|
||
- **Manual**: Can be triggered via management API (future)
|
||
|
||
**Symlink Strategy:**
|
||
|
||
Nmap's script directory is symlinked to sirius-nse:
|
||
|
||
```dockerfile
|
||
# Dockerfile
|
||
RUN ln -sf /opt/sirius/nse/sirius-nse/scripts /usr/local/share/nmap/scripts
|
||
```
|
||
|
||
This ensures **only curated scripts** are available, preventing accidental execution of default Nmap scripts.
|
||
|
||
### Script Selection (`internal/nse/script_selector.go`)
|
||
|
||
**Purpose:** Select appropriate NSE scripts based on protocols or explicit list.
|
||
|
||
**Manifest Structure:**
|
||
|
||
```json
|
||
{
|
||
"version": "1.0",
|
||
"scripts": [
|
||
{
|
||
"name": "vulners",
|
||
"category": "vuln",
|
||
"protocols": ["*"]
|
||
},
|
||
{
|
||
"name": "smb-vuln-ms17-010",
|
||
"category": "vuln",
|
||
"protocols": ["smb"]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**Script Selection Logic:**
|
||
|
||
```go
|
||
type ScriptSelector struct {
|
||
manifest *Manifest
|
||
blacklist map[string]bool
|
||
}
|
||
|
||
// Build Nmap --script flag
|
||
func (ss *ScriptSelector) BuildNmapScriptFlag(protocols ...string) (string, error) {
|
||
if len(protocols) == 1 && protocols[0] == "*" {
|
||
// Return all non-blacklisted scripts
|
||
return ss.getAllScripts(), nil
|
||
}
|
||
|
||
// Filter by protocols
|
||
scripts := []string{}
|
||
for _, script := range ss.manifest.Scripts {
|
||
if ss.matchesProtocol(script, protocols) && !ss.isBlacklisted(script.Name) {
|
||
scripts = append(scripts, script.Name)
|
||
}
|
||
}
|
||
|
||
return strings.Join(scripts, ","), nil
|
||
}
|
||
```
|
||
|
||
**Protocol Matching:**
|
||
|
||
- `*` (wildcard): Script applies to all protocols
|
||
- Exact match: `["smb"]` matches scripts with `protocols: ["smb"]`
|
||
- Multiple protocols: `["smb", "http"]` matches scripts with either protocol
|
||
|
||
**Example:**
|
||
|
||
```go
|
||
selector := nse.NewScriptSelector(manifest)
|
||
scriptFlag, _ := selector.BuildNmapScriptFlag("smb", "http")
|
||
// Result: "vulners,smb-vuln-ms17-010,smb-os-discovery,http-title,http-enum,..."
|
||
```
|
||
|
||
### Script Blacklist (`internal/nse/script_blacklist.go`)
|
||
|
||
**Purpose:** Exclude problematic or slow scripts.
|
||
|
||
**Blacklist Criteria:**
|
||
|
||
- **False Positives**: Scripts with high FP rates
|
||
- **Performance**: Extremely slow scripts (>5 minutes per host)
|
||
- **Stability**: Scripts that crash or hang frequently
|
||
- **Compatibility**: Scripts incompatible with our environment
|
||
|
||
**Example Blacklist:**
|
||
|
||
```go
|
||
var DefaultBlacklist = map[string]bool{
|
||
"broadcast-dhcp-discover": true, // Sends broadcasts
|
||
"firewalk": true, // Very slow
|
||
"http-slowloris-check": true, // DoS risk
|
||
}
|
||
```
|
||
|
||
**Blacklist Management:**
|
||
|
||
- Centrally managed in `script_blacklist.go`
|
||
- Can be overridden via environment variables (future)
|
||
- Logged when scripts are excluded
|
||
|
||
### Sync Manager (`internal/nse/sync.go`)
|
||
|
||
**Purpose:** Coordinate NSE repository updates with scan operations.
|
||
|
||
**Responsibilities:**
|
||
|
||
1. **Pre-Scan Sync**: Ensure scripts are up-to-date before scanning
|
||
2. **Concurrency Control**: Prevent multiple simultaneous syncs
|
||
3. **Error Handling**: Log sync failures but don't block scans
|
||
4. **Context Awareness**: Respect context cancellation
|
||
|
||
**Implementation:**
|
||
|
||
```go
|
||
type SyncManager struct {
|
||
repoManager *RepoManager
|
||
kvStore store.KVStore
|
||
lastSync time.Time
|
||
syncMutex sync.Mutex
|
||
}
|
||
|
||
func (sm *SyncManager) Sync(ctx context.Context) error {
|
||
sm.syncMutex.Lock()
|
||
defer sm.syncMutex.Unlock()
|
||
|
||
// Skip if recently synced (within 1 hour)
|
||
if time.Since(sm.lastSync) < time.Hour {
|
||
return nil
|
||
}
|
||
|
||
// Perform sync
|
||
if err := sm.repoManager.EnsureRepo(); err != nil {
|
||
return fmt.Errorf("failed to sync NSE repo: %w", err)
|
||
}
|
||
|
||
sm.lastSync = time.Now()
|
||
return nil
|
||
}
|
||
```
|
||
|
||
**Sync Strategy:**
|
||
|
||
- **Cooldown Period**: 1 hour between syncs
|
||
- **Non-Blocking**: Sync failures logged but don't prevent scans
|
||
- **Startup Sync**: Always sync on scanner startup
|
||
|
||
---
|
||
|
||
## Template System
|
||
|
||
Templates provide **pre-configured scan profiles** for common use cases.
|
||
|
||
### Template Structure
|
||
|
||
```go
|
||
type Template struct {
|
||
ID string // Unique identifier (e.g., "high-risk")
|
||
Name string // Human-readable name
|
||
Description string // Usage description
|
||
Type TemplateType // SystemTemplate or CustomTemplate
|
||
EnabledScripts []string // NSE scripts to run
|
||
ScanOptions TemplateOptions // Default scan options
|
||
CreatedAt time.Time
|
||
UpdatedAt time.Time
|
||
}
|
||
|
||
type TemplateOptions struct {
|
||
ScanTypes []string // ["enumeration", "discovery", "vulnerability"]
|
||
PortRange string // "1-10000"
|
||
Aggressive bool // Enable aggressive scanning
|
||
MaxRetries int // Retry attempts
|
||
Parallel bool // Parallel target scanning
|
||
ExcludePorts []string // Ports to skip
|
||
}
|
||
```
|
||
|
||
### System Templates
|
||
|
||
**Pre-defined templates initialized on startup:**
|
||
|
||
#### 1. `high-risk` - Focused Critical Vulnerability Scan
|
||
|
||
**Purpose:** Balanced scan focusing on high-impact vulnerabilities.
|
||
|
||
**Scripts:** 10 carefully selected scripts
|
||
|
||
```go
|
||
EnabledScripts: []string{
|
||
"vulners", // CVE detection (highest value)
|
||
"smb-vuln-ms17-010", // EternalBlue
|
||
"http-shellshock", // Shellshock vulnerability
|
||
"http-vuln-cve2017-5638", // Apache Struts RCE
|
||
"banner", // Service identification
|
||
"http-title", // HTTP identification
|
||
"ssl-cert", // SSL certificate info
|
||
"http-enum", // HTTP path enumeration
|
||
"smb-os-discovery", // SMB OS detection
|
||
"ftp-anon", // Anonymous FTP access
|
||
}
|
||
|
||
ScanOptions: {
|
||
ScanTypes: []string{"enumeration", "discovery", "vulnerability"},
|
||
PortRange: "1-10000",
|
||
Aggressive: true,
|
||
MaxRetries: 3,
|
||
Parallel: true,
|
||
}
|
||
```
|
||
|
||
**Use Case:** Default scan for most security assessments.
|
||
|
||
#### 2. `all` - Comprehensive Scan
|
||
|
||
**Purpose:** Exhaustive scanning with all available NSE scripts.
|
||
|
||
**Scripts:** All non-blacklisted scripts (wildcard)
|
||
|
||
```go
|
||
EnabledScripts: []string{"*"} // Special marker
|
||
|
||
ScanOptions: {
|
||
ScanTypes: []string{"enumeration", "discovery", "vulnerability"},
|
||
PortRange: "1-65535",
|
||
Aggressive: true,
|
||
MaxRetries: 3,
|
||
Parallel: false, // Sequential for thoroughness
|
||
}
|
||
```
|
||
|
||
**Use Case:** Deep penetration testing, compliance audits.
|
||
|
||
**Warning:** Can take hours per host. Use sparingly.
|
||
|
||
#### 3. `quick` - Fast Scan
|
||
|
||
**Purpose:** Rapid assessment with essential scripts.
|
||
|
||
**Scripts:** 3 lightweight scripts
|
||
|
||
```go
|
||
EnabledScripts: []string{
|
||
"vulners", // CVE detection
|
||
"banner", // Service identification
|
||
"http-title", // HTTP identification
|
||
}
|
||
|
||
ScanOptions: {
|
||
ScanTypes: []string{"enumeration", "vulnerability"},
|
||
PortRange: "top500Ports", // Most common 500 ports
|
||
Aggressive: false,
|
||
MaxRetries: 2,
|
||
Parallel: true,
|
||
}
|
||
```
|
||
|
||
**Use Case:** Initial reconnaissance, time-sensitive scans.
|
||
|
||
### Custom Templates
|
||
|
||
**Users can create custom templates via UI (future) or API.**
|
||
|
||
**Creation Example:**
|
||
|
||
```go
|
||
template := &Template{
|
||
ID: "web-app-scan",
|
||
Name: "Web Application Scan",
|
||
Description: "Focused scan for web application vulnerabilities",
|
||
Type: CustomTemplate,
|
||
EnabledScripts: []string{
|
||
"vulners",
|
||
"http-enum",
|
||
"http-shellshock",
|
||
"http-sql-injection",
|
||
"ssl-cert",
|
||
},
|
||
ScanOptions: TemplateOptions{
|
||
ScanTypes: []string{"discovery", "vulnerability"},
|
||
PortRange: "80,443,8080,8443",
|
||
Aggressive: false,
|
||
MaxRetries: 2,
|
||
Parallel: true,
|
||
},
|
||
}
|
||
|
||
templateManager.CreateTemplate(ctx, template)
|
||
```
|
||
|
||
**Template Operations:**
|
||
|
||
```go
|
||
// Get template
|
||
template, err := templateManager.GetTemplate(ctx, "high-risk")
|
||
|
||
// List all templates
|
||
templates, err := templateManager.ListTemplates(ctx)
|
||
|
||
// Update custom template (system templates are immutable)
|
||
templateManager.UpdateTemplate(ctx, template)
|
||
|
||
// Delete custom template
|
||
templateManager.DeleteTemplate(ctx, "my-template")
|
||
```
|
||
|
||
### Template Resolution
|
||
|
||
**When a scan message includes `template_id`, the scanner:**
|
||
|
||
1. **Fetches Template**: Retrieve from ValKey
|
||
2. **Applies Defaults**: Use template's `EnabledScripts` and `ScanOptions`
|
||
3. **Merges User Options**: User-provided options override template defaults
|
||
4. **Resolves Scripts**: Convert template script list to Nmap `--script` flag
|
||
|
||
**Resolution Logic:**
|
||
|
||
```go
|
||
func (sm *ScanManager) handleMessage(msg string) {
|
||
var scanMsg ScanMessage
|
||
json.Unmarshal([]byte(msg), &scanMsg)
|
||
|
||
if scanMsg.Options.TemplateID != "" {
|
||
template, _ := sm.templateManager.GetTemplate(ctx, scanMsg.Options.TemplateID)
|
||
|
||
// User options override template defaults
|
||
if scanMsg.Options.PortRange == "" {
|
||
scanMsg.Options.PortRange = template.ScanOptions.PortRange
|
||
}
|
||
if len(scanMsg.Options.ScanTypes) == 0 {
|
||
scanMsg.Options.ScanTypes = template.ScanOptions.ScanTypes
|
||
}
|
||
// ... merge other options
|
||
}
|
||
|
||
sm.processTarget(scanMsg.Targets[0])
|
||
}
|
||
```
|
||
|
||
**Priority:** User Options > Template Defaults > System Defaults
|
||
|
||
---
|
||
|
||
## Scan Message Format
|
||
|
||
Scan requests are **JSON messages** sent to the `scan` RabbitMQ queue.
|
||
|
||
### Message Structure
|
||
|
||
```go
|
||
type ScanMessage struct {
|
||
ID string // Unique scan identifier
|
||
Targets []Target // Targets to scan
|
||
Options ScanOptions // Scan configuration
|
||
Priority int // 1 (low) to 5 (high)
|
||
CallbackURL string // Optional webhook on completion
|
||
}
|
||
|
||
type Target struct {
|
||
Value string // IP, range, CIDR, or hostname
|
||
Type TargetType // Target type identifier
|
||
Timeout int // Per-target timeout (seconds, optional)
|
||
}
|
||
|
||
type TargetType string
|
||
const (
|
||
SingleIP TargetType = "single_ip" // 192.168.1.1
|
||
IPRange TargetType = "ip_range" // 192.168.1.1-192.168.1.254
|
||
CIDR TargetType = "cidr" // 192.168.1.0/24
|
||
DNSName TargetType = "dns_name" // example.com
|
||
DNSWildcard TargetType = "dns_wildcard" // *.example.com (TODO)
|
||
)
|
||
|
||
type ScanOptions struct {
|
||
TemplateID string // Template to use (optional)
|
||
PortRange string // "1-65535", "80,443,8080"
|
||
Aggressive bool // Aggressive scanning mode
|
||
ExcludePorts []string // Ports to skip
|
||
ScanTypes []string // ["enumeration", "discovery", "vulnerability"]
|
||
MaxRetries int // Retry attempts
|
||
Parallel bool // Parallel target scanning
|
||
}
|
||
```
|
||
|
||
### Example Messages
|
||
|
||
#### Simple Single-IP Scan
|
||
|
||
```json
|
||
{
|
||
"id": "scan-001",
|
||
"targets": [
|
||
{
|
||
"value": "192.168.1.100",
|
||
"type": "single_ip"
|
||
}
|
||
],
|
||
"options": {
|
||
"template_id": "high-risk",
|
||
"scan_types": ["vulnerability"]
|
||
},
|
||
"priority": 3
|
||
}
|
||
```
|
||
|
||
#### CIDR Range with Custom Options
|
||
|
||
```json
|
||
{
|
||
"id": "scan-002",
|
||
"targets": [
|
||
{
|
||
"value": "192.168.1.0/24",
|
||
"type": "cidr"
|
||
}
|
||
],
|
||
"options": {
|
||
"port_range": "1-10000",
|
||
"aggressive": true,
|
||
"scan_types": ["discovery", "vulnerability"],
|
||
"max_retries": 3,
|
||
"parallel": true
|
||
},
|
||
"priority": 4
|
||
}
|
||
```
|
||
|
||
#### Multi-Target Scan with Template
|
||
|
||
```json
|
||
{
|
||
"id": "scan-003",
|
||
"targets": [
|
||
{
|
||
"value": "192.168.1.1",
|
||
"type": "single_ip"
|
||
},
|
||
{
|
||
"value": "192.168.1.100-192.168.1.110",
|
||
"type": "ip_range"
|
||
},
|
||
{
|
||
"value": "example.com",
|
||
"type": "dns_name"
|
||
}
|
||
],
|
||
"options": {
|
||
"template_id": "quick",
|
||
"scan_types": ["enumeration", "vulnerability"]
|
||
},
|
||
"priority": 2,
|
||
"callback_url": "https://api.example.com/scan-complete"
|
||
}
|
||
```
|
||
|
||
### Validation Rules
|
||
|
||
**Required Fields:**
|
||
|
||
- `id`: Must be unique (recommended: UUID)
|
||
- `targets`: At least one target
|
||
- `options.scan_types`: At least one scan type
|
||
- `priority`: 1-5 (inclusive)
|
||
|
||
**Optional Fields:**
|
||
|
||
- `template_id`: If omitted, uses system defaults
|
||
- `callback_url`: If provided, webhook POST on completion
|
||
|
||
**Validation Errors:**
|
||
|
||
```go
|
||
func (sm *ScanManager) validateScanMessage(msg *ScanMessage) error {
|
||
if len(msg.Targets) == 0 {
|
||
return fmt.Errorf("no targets specified")
|
||
}
|
||
if msg.Priority < 1 || msg.Priority > 5 {
|
||
return fmt.Errorf("invalid priority: must be between 1 and 5")
|
||
}
|
||
return nil
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Target Processing
|
||
|
||
### Target Type Expansion
|
||
|
||
The scanner **expands** targets into individual IPs for worker pool processing.
|
||
|
||
#### Single IP
|
||
|
||
**Format:** `192.168.1.100`
|
||
|
||
**Processing:** Direct pass-through
|
||
|
||
```go
|
||
case SingleIP:
|
||
if !validateIP(target.Value) {
|
||
return nil, fmt.Errorf("invalid IP address: %s", target.Value)
|
||
}
|
||
return []string{target.Value}, nil
|
||
```
|
||
|
||
#### IP Range
|
||
|
||
**Format:** `192.168.1.1-192.168.1.254`
|
||
|
||
**Processing:** Expand to all IPs in range
|
||
|
||
```go
|
||
case IPRange:
|
||
return expandIPRange(target.Value)
|
||
// Returns: ["192.168.1.1", "192.168.1.2", ..., "192.168.1.254"]
|
||
```
|
||
|
||
**Implementation:**
|
||
|
||
```go
|
||
func expandIPRange(rangeStr string) ([]string, error) {
|
||
parts := strings.Split(rangeStr, "-")
|
||
startIP := net.ParseIP(parts[0])
|
||
endIP := net.ParseIP(parts[1])
|
||
|
||
// Iterate from startIP to endIP
|
||
ips := []string{}
|
||
for ip := startIP; !ip.Equal(endIP); ip = incrementIP(ip) {
|
||
ips = append(ips, ip.String())
|
||
}
|
||
return ips, nil
|
||
}
|
||
```
|
||
|
||
#### CIDR Block
|
||
|
||
**Format:** `192.168.1.0/24`
|
||
|
||
**Processing:** Expand to all IPs in subnet (excluding network/broadcast)
|
||
|
||
```go
|
||
case CIDR:
|
||
if !validateCIDR(target.Value) {
|
||
return nil, fmt.Errorf("invalid CIDR notation: %s", target.Value)
|
||
}
|
||
return expandCIDR(target.Value)
|
||
// Returns: ["192.168.1.1", "192.168.1.2", ..., "192.168.1.254"]
|
||
```
|
||
|
||
**Note:** Skips network address (`.0`) and broadcast address (`.255`)
|
||
|
||
#### DNS Name
|
||
|
||
**Format:** `example.com`
|
||
|
||
**Processing:** DNS resolution to IP(s)
|
||
|
||
```go
|
||
case DNSName:
|
||
ips, err := net.LookupIP(target.Value)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("DNS lookup failed: %v", err)
|
||
}
|
||
result := make([]string, len(ips))
|
||
for i, ip := range ips {
|
||
result[i] = ip.String()
|
||
}
|
||
return result, nil
|
||
```
|
||
|
||
**Handles:**
|
||
|
||
- Multiple A/AAAA records (returns all IPs)
|
||
- IPv4 and IPv6 addresses
|
||
- DNS resolution failures (returns error)
|
||
|
||
#### DNS Wildcard (TODO)
|
||
|
||
**Format:** `*.example.com`
|
||
|
||
**Status:** Not yet implemented
|
||
|
||
**Planned Behavior:**
|
||
|
||
1. Subdomain enumeration (via DNS brute-force or Certificate Transparency logs)
|
||
2. Resolution of discovered subdomains to IPs
|
||
3. Deduplication of IPs
|
||
|
||
### Worker Pool Processing
|
||
|
||
After expansion, each IP is **enqueued as a task**:
|
||
|
||
```go
|
||
func (sm *ScanManager) processTarget(target Target) {
|
||
targetIPs, err := sm.prepareTarget(target)
|
||
if err != nil {
|
||
log.Printf("Failed to prepare target: %v", err)
|
||
return
|
||
}
|
||
|
||
// Add each IP to worker pool queue
|
||
for _, ip := range targetIPs {
|
||
task := ScanTask{
|
||
IP: ip,
|
||
Options: sm.currentScanOptions,
|
||
}
|
||
sm.workerPool.AddTask(task)
|
||
}
|
||
}
|
||
```
|
||
|
||
**Worker Pool Flow:**
|
||
|
||
```
|
||
Target Expansion
|
||
│
|
||
▼
|
||
┌─────────────────┐
|
||
│ Task Queue │
|
||
│ (Channel) │
|
||
└────────┬────────┘
|
||
│
|
||
├─────► Worker 1 ──► scanIP(192.168.1.1)
|
||
├─────► Worker 2 ──► scanIP(192.168.1.2)
|
||
├─────► Worker 3 ──► scanIP(192.168.1.3)
|
||
│ ...
|
||
└─────► Worker 10 ─► scanIP(192.168.1.10)
|
||
```
|
||
|
||
---
|
||
|
||
## Source Attribution System
|
||
|
||
The scanner implements **comprehensive source attribution** to track scan origins and configurations.
|
||
|
||
### Purpose
|
||
|
||
- **Audit Trail**: Who/what/when/how for each scan
|
||
- **Debugging**: Troubleshoot scanning issues with full context
|
||
- **Compliance**: Demonstrate due diligence in security assessments
|
||
- **Analytics**: Understand scanning patterns and effectiveness
|
||
|
||
### Source Metadata
|
||
|
||
```go
|
||
type ScanSource struct {
|
||
Name string // Tool name (e.g., "nmap", "rustscan", "naabu")
|
||
Version string // Tool version (e.g., "7.94")
|
||
Config string // Scan configuration (semicolon-separated key:value pairs)
|
||
}
|
||
```
|
||
|
||
### Configuration Tracking
|
||
|
||
The scanner captures:
|
||
|
||
**Scan Configuration:**
|
||
|
||
- `ports`: Port range scanned
|
||
- `aggressive`: Aggressive mode enabled
|
||
- `types`: Scan types executed
|
||
- `exclude`: Excluded ports
|
||
- `template`: Template ID used
|
||
|
||
**System Information:**
|
||
|
||
- `host`: Hostname of scanning system
|
||
- `user`: User running scanner
|
||
- `scanner_id`: Unique scan identifier
|
||
- `go_version`: Go runtime version
|
||
|
||
**Example Config String:**
|
||
|
||
```
|
||
ports:1-10000;aggressive:true;types:enumeration,discovery,vulnerability;template:high-risk;host:scanner-prod-01;user:siriususer;scanner_id:scan-abc123;go_version:go1.23.0
|
||
```
|
||
|
||
### Version Detection
|
||
|
||
Automatically detects scanning tool versions:
|
||
|
||
```go
|
||
func (sm *ScanManager) detectScannerVersion(toolName string) string {
|
||
switch toolName {
|
||
case "nmap":
|
||
output, _ := exec.Command("nmap", "--version").Output()
|
||
// Parse: "Nmap version 7.94 ( https://nmap.org )"
|
||
return extractVersion(output)
|
||
case "rustscan":
|
||
output, _ := exec.Command("rustscan", "--version").Output()
|
||
// Parse: "rustscan 2.1.1"
|
||
return extractVersion(output)
|
||
case "naabu":
|
||
output, _ := exec.Command("naabu", "-version").Output()
|
||
return extractVersion(output)
|
||
}
|
||
}
|
||
```
|
||
|
||
### API Submission
|
||
|
||
Results are submitted via **source-aware API endpoint**:
|
||
|
||
```go
|
||
func (sm *ScanManager) submitHostWithSource(host sirius.Host, toolName string) error {
|
||
source := sm.createScanSource(toolName)
|
||
|
||
request := SourcedHostRequest{
|
||
Host: host,
|
||
Source: source,
|
||
}
|
||
|
||
jsonData, _ := json.Marshal(request)
|
||
url := fmt.Sprintf("%s/host/with-source", sm.apiBaseURL)
|
||
resp, _ := http.Post(url, "application/json", bytes.NewBuffer(jsonData))
|
||
|
||
return nil
|
||
}
|
||
```
|
||
|
||
**API Endpoint:** `POST /host/with-source`
|
||
|
||
**Request Body:**
|
||
|
||
```json
|
||
{
|
||
"host": {
|
||
"ip": "192.168.1.100",
|
||
"ports": [...],
|
||
"vulnerabilities": [...]
|
||
},
|
||
"source": {
|
||
"name": "nmap",
|
||
"version": "7.94",
|
||
"config": "ports:1-10000;aggressive:true;template:high-risk;host:scanner-01;..."
|
||
}
|
||
}
|
||
```
|
||
|
||
### Database Storage
|
||
|
||
The API stores source attribution in PostgreSQL:
|
||
|
||
**Schema:**
|
||
|
||
```sql
|
||
CREATE TABLE scan_sources (
|
||
id SERIAL PRIMARY KEY,
|
||
name VARCHAR(255),
|
||
version VARCHAR(50),
|
||
config TEXT,
|
||
created_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
|
||
CREATE TABLE hosts (
|
||
id SERIAL PRIMARY KEY,
|
||
ip VARCHAR(45),
|
||
scan_source_id INTEGER REFERENCES scan_sources(id),
|
||
...
|
||
);
|
||
```
|
||
|
||
This enables queries like:
|
||
|
||
- "Show all vulnerabilities found by Nmap 7.94"
|
||
- "List scans using the 'high-risk' template"
|
||
- "Find hosts scanned from scanner-prod-01"
|
||
|
||
---
|
||
|
||
## State Management
|
||
|
||
The scanner maintains **real-time scan state** using two systems:
|
||
|
||
### ValKey Integration
|
||
|
||
**Purpose:** Live scan progress tracking for UI display.
|
||
|
||
**Key Structure:**
|
||
|
||
```
|
||
scan:<scan_id> = {
|
||
"status": "running",
|
||
"start_time": "2025-10-25T10:30:00Z",
|
||
"end_time": null,
|
||
"hosts": ["192.168.1.1", "192.168.1.2", ...],
|
||
"hosts_completed": 45,
|
||
"vulnerabilities": [
|
||
{
|
||
"id": "CVE-2017-0143",
|
||
"severity": "critical",
|
||
"title": "EternalBlue SMB RCE",
|
||
"description": "Remote code execution..."
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**Update Pattern:**
|
||
|
||
```go
|
||
type ScanUpdater struct {
|
||
kvStore store.KVStore
|
||
}
|
||
|
||
func (su *ScanUpdater) Update(ctx context.Context, updateFn func(*ScanResult) error) error {
|
||
// 1. Get current scan state from ValKey
|
||
current, _ := su.kvStore.GetValue(ctx, "scan:"+scanID)
|
||
|
||
// 2. Apply update function
|
||
var scan ScanResult
|
||
json.Unmarshal([]byte(current.Message.Value), &scan)
|
||
updateFn(&scan)
|
||
|
||
// 3. Write updated state back to ValKey
|
||
updated, _ := json.Marshal(scan)
|
||
su.kvStore.SetValue(ctx, "scan:"+scanID, string(updated))
|
||
|
||
return nil
|
||
}
|
||
```
|
||
|
||
**Update Events:**
|
||
|
||
- **Discovery Complete:** Add host to `hosts` array
|
||
- **Host Complete:** Increment `hosts_completed`
|
||
- **Vulnerability Found:** Add to `vulnerabilities` array
|
||
- **Scan Complete:** Set `status = "completed"`, set `end_time`
|
||
|
||
**Example Update:**
|
||
|
||
```go
|
||
sm.scanUpdater.Update(ctx, func(scan *ScanResult) error {
|
||
scan.HostsCompleted++
|
||
if scan.HostsCompleted >= len(scan.Hosts) {
|
||
scan.Status = "completed"
|
||
scan.EndTime = time.Now().Format(time.RFC3339)
|
||
}
|
||
return nil
|
||
})
|
||
```
|
||
|
||
### Logging System
|
||
|
||
**Purpose:** Structured audit trail for compliance and debugging.
|
||
|
||
**Implementation:** RabbitMQ-based logging to `scanner_logs` queue.
|
||
|
||
**Log Client:**
|
||
|
||
```go
|
||
type LoggingClient struct {
|
||
queueName string // "scanner_logs"
|
||
}
|
||
|
||
func (lc *LoggingClient) LogScanEvent(scanID, eventType, message string, metadata map[string]interface{}) {
|
||
logEntry := map[string]interface{}{
|
||
"timestamp": time.Now().Format(time.RFC3339),
|
||
"scan_id": scanID,
|
||
"event_type": eventType,
|
||
"message": message,
|
||
"metadata": metadata,
|
||
}
|
||
|
||
jsonData, _ := json.Marshal(logEntry)
|
||
queue.Publish("scanner_logs", string(jsonData))
|
||
}
|
||
```
|
||
|
||
**Event Types:**
|
||
|
||
| Event Type | Description | Metadata |
|
||
| --------------------- | ------------------------ | -------------------------------------------- |
|
||
| `scan_initiated` | Scan request received | targets_count, priority, template_id |
|
||
| `target_prepared` | Target expanded to IPs | target_value, target_type, ips_generated |
|
||
| `host_discovered` | Live host found | host_ip, ports, tool |
|
||
| `tool_execution` | Scanning tool completed | tool, duration, success, ports_found |
|
||
| `vulnerability_found` | CVE detected | host_ip, cve_id, severity, tool |
|
||
| `host_completed` | All phases done for host | host_ip, vulnerabilities_found |
|
||
| `scan_completed` | Entire scan finished | total_hosts, vulnerabilities_found, duration |
|
||
| `scan_error` | Error occurred | host_ip, error_type, error_message |
|
||
|
||
**Example Log Entries:**
|
||
|
||
```json
|
||
// scan_initiated
|
||
{
|
||
"timestamp": "2025-10-25T10:30:00Z",
|
||
"scan_id": "scan-abc123",
|
||
"event_type": "scan_initiated",
|
||
"message": "Scan request received",
|
||
"metadata": {
|
||
"targets_count": 3,
|
||
"priority": 4,
|
||
"template_id": "high-risk"
|
||
}
|
||
}
|
||
|
||
// vulnerability_found
|
||
{
|
||
"timestamp": "2025-10-25T10:35:22Z",
|
||
"scan_id": "scan-abc123",
|
||
"event_type": "vulnerability_found",
|
||
"message": "CVE detected",
|
||
"metadata": {
|
||
"host_ip": "192.168.1.100",
|
||
"cve_id": "CVE-2017-0143",
|
||
"severity": "critical",
|
||
"tool": "nmap"
|
||
}
|
||
}
|
||
```
|
||
|
||
**Log Consumption:**
|
||
|
||
- **Elasticsearch**: Indexing for search/analytics
|
||
- **File**: JSON lines for long-term storage
|
||
- **Monitoring**: Real-time alerting on errors
|
||
|
||
---
|
||
|
||
## Docker Integration
|
||
|
||
The scanner runs inside the `sirius-engine` container, which bundles multiple applications.
|
||
|
||
### Dockerfile Architecture
|
||
|
||
**Multi-Stage Build:**
|
||
|
||
```dockerfile
|
||
# Stage 1: Builder
|
||
FROM golang:1.23-bullseye AS builder
|
||
|
||
# Install build dependencies
|
||
RUN apt-get update && apt-get install -y \
|
||
git ca-certificates build-essential libpcap-dev
|
||
|
||
# Clone and build app-scanner
|
||
RUN git clone https://github.com/SiriusScan/app-scanner.git && \
|
||
cd app-scanner && \
|
||
CGO_ENABLED=1 GOOS=linux go build -ldflags="-w -s" -o scanner main.go
|
||
|
||
# Stage 2: Runtime
|
||
FROM debian:bullseye-slim
|
||
|
||
# Install runtime dependencies
|
||
RUN apt-get update && apt-get install -y \
|
||
nmap \ # Nmap binary
|
||
rustscan \ # RustScan binary
|
||
libpcap0.8 \ # Packet capture library
|
||
git # For NSE repo cloning
|
||
|
||
# Copy built scanner binary
|
||
COPY --from=builder /repos/app-scanner/scanner /app-scanner-src/scanner
|
||
|
||
# Create NSE directory
|
||
RUN mkdir -p /opt/sirius/nse/sirius-nse
|
||
|
||
# Symlink Nmap scripts to sirius-nse
|
||
RUN ln -sf /opt/sirius/nse/sirius-nse/scripts /usr/local/share/nmap/scripts
|
||
```
|
||
|
||
### Volume Mounts
|
||
|
||
**Development Mode:**
|
||
|
||
```yaml
|
||
# docker-compose.dev.yaml
|
||
services:
|
||
sirius-engine:
|
||
volumes:
|
||
- ../minor-projects/app-scanner:/app-scanner # Live code reload
|
||
- ../minor-projects/go-api:/go-api
|
||
- ../minor-projects/sirius-nse:/sirius-nse
|
||
```
|
||
|
||
**Production Mode:**
|
||
|
||
No volume mounts. Uses pre-compiled binary at `/app-scanner-src/scanner`.
|
||
|
||
### Startup Script
|
||
|
||
The scanner is started by `start-enhanced.sh`:
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
|
||
# Development mode: run from source with live reload
|
||
if [ "$GO_ENV" = "development" ] && [ -d "/app-scanner" ]; then
|
||
echo "Starting scanner in development mode..."
|
||
cd /app-scanner
|
||
air -c .air.toml & # Live reload with air
|
||
fi
|
||
|
||
# Production mode: run pre-compiled binary
|
||
if [ ! "$GO_ENV" = "development" ]; then
|
||
echo "Starting scanner in production mode..."
|
||
/app-scanner-src/scanner &
|
||
fi
|
||
|
||
# Keep container running
|
||
wait
|
||
```
|
||
|
||
### NSE Script Installation
|
||
|
||
**Automatic on Startup:**
|
||
|
||
The `SyncManager` automatically clones/updates the `sirius-nse` repository when `ListenForScans()` is called:
|
||
|
||
```go
|
||
func (sm *ScanManager) ListenForScans() {
|
||
// Sync NSE scripts before listening
|
||
if err := sm.nseSync.Sync(sm.ctx); err != nil {
|
||
log.Printf("Warning: failed to sync NSE scripts: %v", err)
|
||
// Continue anyway - may use cached scripts
|
||
}
|
||
|
||
queue.Listen("scan", sm.handleMessage)
|
||
}
|
||
```
|
||
|
||
**Fallback:** If sync fails, uses existing scripts from `/opt/sirius/nse/sirius-nse`.
|
||
|
||
### Environment Variables
|
||
|
||
```bash
|
||
# Scanner configuration
|
||
SIRIUS_API_URL=http://sirius-api:9001 # API endpoint for results
|
||
RABBITMQ_URL=amqp://guest:guest@sirius-rabbitmq:5672/
|
||
VALKEY_HOST=sirius-valkey
|
||
VALKEY_PORT=6379
|
||
|
||
# Development settings
|
||
GO_ENV=development # Enable dev mode
|
||
```
|
||
|
||
---
|
||
|
||
## Configuration Files
|
||
|
||
### manifest.json
|
||
|
||
**Location:** `/app-scanner/manifest.json`
|
||
|
||
**Purpose:** Reference to external repositories (sirius-nse).
|
||
|
||
**Structure:**
|
||
|
||
```json
|
||
{
|
||
"repositories": [
|
||
{
|
||
"name": "sirius-nse",
|
||
"url": "https://github.com/SiriusScan/sirius-nse.git"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**Usage:** Read by installer scripts or future auto-update mechanisms.
|
||
|
||
### nmap-args/args.txt
|
||
|
||
**Location:** `/app-scanner/nmap-args/args.txt` (or `/opt/sirius/nse/sirius-nse/scripts/args.txt`)
|
||
|
||
**Purpose:** Default arguments for NSE scripts.
|
||
|
||
**Example Content:**
|
||
|
||
```
|
||
# Vulners script configuration
|
||
vulners.showall=false
|
||
|
||
# HTTP scripts
|
||
http.useragent=Sirius Scanner/1.0
|
||
|
||
# SMB scripts
|
||
smbdomain=WORKGROUP
|
||
smbusername=guest
|
||
smbpassword=
|
||
|
||
# Timing
|
||
timeout=30000
|
||
```
|
||
|
||
**Usage:** Passed to Nmap via `--script-args-file` flag.
|
||
|
||
**Path Resolution:**
|
||
|
||
```go
|
||
argsFilePaths := []string{
|
||
"/opt/sirius/nse/sirius-nse/scripts/args.txt",
|
||
"/app-scanner/nmap-args/args.txt",
|
||
"/app-scanner-src/nmap-args/args.txt",
|
||
"nmap-args/args.txt",
|
||
}
|
||
|
||
// Use first existing file
|
||
for _, path := range argsFilePaths {
|
||
if _, err := os.Stat(path); err == nil {
|
||
argsFilePath = path
|
||
break
|
||
}
|
||
}
|
||
```
|
||
|
||
### .air.toml
|
||
|
||
**Location:** `/app-scanner/.air.toml`
|
||
|
||
**Purpose:** Live reload configuration for development.
|
||
|
||
**Key Settings:**
|
||
|
||
```toml
|
||
[build]
|
||
cmd = "go build -o ./tmp/scanner main.go"
|
||
bin = "./tmp/scanner"
|
||
include_ext = ["go", "tpl", "tmpl", "html"]
|
||
exclude_dir = ["tmp", "vendor", "testdata"]
|
||
delay = 1000 # 1 second delay before rebuild
|
||
|
||
[log]
|
||
time = true
|
||
```
|
||
|
||
**Usage:** Automatically watches for file changes and rebuilds/restarts scanner.
|
||
|
||
---
|
||
|
||
## Development Workflow
|
||
|
||
### Running Tests
|
||
|
||
**Unit Tests:**
|
||
|
||
```bash
|
||
cd /app-scanner
|
||
go test ./internal/scan/... -v
|
||
|
||
# Specific test
|
||
go test ./internal/scan/ -run TestScanToolFactory -v
|
||
|
||
# With coverage
|
||
go test ./internal/scan/... -cover -coverprofile=coverage.out
|
||
go tool cover -html=coverage.out
|
||
```
|
||
|
||
**Integration Tests:**
|
||
|
||
```bash
|
||
# Full scan test (requires running infrastructure)
|
||
go run cmd/scan-full-test/main.go
|
||
```
|
||
|
||
### Manual Scan Testing
|
||
|
||
**Test Commands:**
|
||
|
||
```bash
|
||
# Direct Nmap test
|
||
cd cmd/direct-nmap-test
|
||
go run main.go
|
||
|
||
# NSE script test
|
||
cd cmd/nse-test
|
||
go run main.go
|
||
|
||
# Validate NSE script fixes
|
||
cd cmd/validate-nse-fix
|
||
go run main.go
|
||
```
|
||
|
||
### Debugging NSE Scripts
|
||
|
||
**Check Script Availability:**
|
||
|
||
```bash
|
||
docker exec sirius-engine nmap --script-help vulners
|
||
```
|
||
|
||
**Test Script Manually:**
|
||
|
||
```bash
|
||
docker exec sirius-engine nmap -sV --script vulners 192.168.1.100
|
||
```
|
||
|
||
**View NSE Repository:**
|
||
|
||
```bash
|
||
docker exec sirius-engine ls -la /opt/sirius/nse/sirius-nse/scripts/
|
||
```
|
||
|
||
**Check Symlink:**
|
||
|
||
```bash
|
||
docker exec sirius-engine ls -la /usr/local/share/nmap/scripts
|
||
# Should point to: /opt/sirius/nse/sirius-nse/scripts
|
||
```
|
||
|
||
### Live Reload Development
|
||
|
||
**Development Mode:**
|
||
|
||
1. Edit code in `../minor-projects/app-scanner`
|
||
2. Air detects changes and rebuilds
|
||
3. Scanner restarts automatically
|
||
4. Logs visible in `docker logs -f sirius-engine`
|
||
|
||
**Watch Logs:**
|
||
|
||
```bash
|
||
# Scanner logs
|
||
docker logs -f sirius-engine | grep scanner
|
||
|
||
# RabbitMQ messages
|
||
docker exec sirius-rabbitmq rabbitmqctl list_queues name messages
|
||
|
||
# ValKey scan state
|
||
docker exec sirius-valkey valkey-cli GET scan:scan-abc123
|
||
```
|
||
|
||
### Adding New Scan Strategies
|
||
|
||
**Step 1: Implement Strategy Interface**
|
||
|
||
```go
|
||
// internal/scan/strategies.go
|
||
type MyNewStrategy struct {
|
||
CustomOption string
|
||
}
|
||
|
||
func (s *MyNewStrategy) Execute(target string) (sirius.Host, error) {
|
||
// Your scanning logic here
|
||
return host, nil
|
||
}
|
||
```
|
||
|
||
**Step 2: Update Factory**
|
||
|
||
```go
|
||
// internal/scan/factory.go
|
||
func (f *ScanToolFactory) CreateTool(toolType string) ScanStrategy {
|
||
switch toolType {
|
||
case "my-new-tool":
|
||
return &MyNewStrategy{
|
||
CustomOption: f.currentOptions.SomeOption,
|
||
}
|
||
// ... existing cases
|
||
}
|
||
}
|
||
```
|
||
|
||
**Step 3: Add to Scan Types**
|
||
|
||
Update documentation and UI to include new scan type in `scan_types` options.
|
||
|
||
**Step 4: Write Tests**
|
||
|
||
```go
|
||
// internal/scan/strategies_test.go
|
||
func TestMyNewStrategy(t *testing.T) {
|
||
strategy := &MyNewStrategy{CustomOption: "test"}
|
||
host, err := strategy.Execute("192.168.1.100")
|
||
assert.NoError(t, err)
|
||
assert.NotEmpty(t, host.IP)
|
||
}
|
||
```
|
||
|
||
### Creating System Templates
|
||
|
||
**Step 1: Define Template**
|
||
|
||
```go
|
||
// internal/scan/template_manager.go
|
||
func (tm *TemplateManager) InitializeSystemTemplates(ctx context.Context) error {
|
||
templates := []Template{
|
||
// ... existing templates
|
||
{
|
||
ID: "my-template",
|
||
Name: "My Custom Template",
|
||
Description: "Description of what this template does",
|
||
Type: SystemTemplate,
|
||
EnabledScripts: []string{
|
||
"script1",
|
||
"script2",
|
||
},
|
||
ScanOptions: TemplateOptions{
|
||
ScanTypes: []string{"vulnerability"},
|
||
PortRange: "1-1000",
|
||
Aggressive: false,
|
||
},
|
||
},
|
||
}
|
||
// ... create templates
|
||
}
|
||
```
|
||
|
||
**Step 2: Rebuild Scanner**
|
||
|
||
```bash
|
||
cd /app-scanner
|
||
go build -o scanner main.go
|
||
```
|
||
|
||
**Step 3: Test Template**
|
||
|
||
```json
|
||
{
|
||
"id": "test-scan",
|
||
"targets": [{ "value": "192.168.1.100", "type": "single_ip" }],
|
||
"options": { "template_id": "my-template" },
|
||
"priority": 3
|
||
}
|
||
```
|
||
|
||
### Optimizing Scan Performance
|
||
|
||
**Strategies:**
|
||
|
||
1. **Adjust Worker Pool Size:**
|
||
|
||
```go
|
||
// internal/scan/manager.go
|
||
const DEFAULT_WORKERS = 20 // Increase for more parallelism
|
||
```
|
||
|
||
2. **Reduce Port Range:**
|
||
|
||
```json
|
||
{ "port_range": "1-1000" } // Instead of 1-65535
|
||
```
|
||
|
||
3. **Limit NSE Scripts:**
|
||
|
||
```json
|
||
{ "template_id": "quick" } // Use quick template
|
||
```
|
||
|
||
4. **Enable Parallel Processing:**
|
||
|
||
```json
|
||
{ "parallel": true }
|
||
```
|
||
|
||
5. **Adjust Nmap Timing:**
|
||
|
||
```go
|
||
// modules/nmap/nmap.go
|
||
args := []string{
|
||
"-T5", // Insane timing (faster but less accurate)
|
||
// ...
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Key Files Reference
|
||
|
||
| File | Lines | Purpose |
|
||
| ----------------------------------- | ----- | ------------------------------------------------------------------ |
|
||
| `main.go` | 37 | Application entry point, initializes ScanManager |
|
||
| `internal/scan/manager.go` | 783 | Core scan orchestration, RabbitMQ listener, worker pool |
|
||
| `internal/scan/strategies.go` | 164 | ScanStrategy interface and implementations (Naabu, RustScan, Nmap) |
|
||
| `internal/scan/factory.go` | 50 | Strategy factory for creating scan tools |
|
||
| `internal/scan/worker_pool.go` | ~150 | Concurrent worker pool for parallel scanning |
|
||
| `internal/scan/template_manager.go` | 413 | Template CRUD operations, system template initialization |
|
||
| `internal/scan/template_types.go` | ~100 | Template data structures |
|
||
| `internal/scan/updater.go` | ~150 | ValKey state update management |
|
||
| `internal/scan/logging.go` | ~200 | Structured logging to RabbitMQ |
|
||
| `internal/scan/helpers.go` | ~100 | Utility functions (severity calculation, etc.) |
|
||
| `internal/scan/network_helpers.go` | ~150 | IP/CIDR expansion, validation |
|
||
| `internal/nse/repo.go` | 138 | Git repository management for NSE scripts |
|
||
| `internal/nse/sync.go` | ~150 | NSE sync coordination |
|
||
| `internal/nse/script_selector.go` | ~200 | Protocol-based NSE script selection |
|
||
| `internal/nse/script_blacklist.go` | ~50 | Problematic script exclusion |
|
||
| `internal/nse/types.go` | ~100 | NSE data structures (Manifest, Script) |
|
||
| `modules/nmap/nmap.go` | 533 | Nmap integration, XML parsing, CVE extraction |
|
||
| `modules/rustscan/rustscan.go` | ~150 | RustScan integration |
|
||
| `modules/naabu/naabu.go` | ~150 | Naabu integration |
|
||
|
||
**Total:** ~3,800 lines of application code
|
||
|
||
---
|
||
|
||
## Code Examples
|
||
|
||
### Sending a Scan Request
|
||
|
||
**From Go:**
|
||
|
||
```go
|
||
scanMsg := ScanMessage{
|
||
ID: uuid.New().String(),
|
||
Targets: []Target{
|
||
{Value: "192.168.1.0/24", Type: CIDR},
|
||
},
|
||
Options: ScanOptions{
|
||
TemplateID: "high-risk",
|
||
ScanTypes: []string{"discovery", "vulnerability"},
|
||
},
|
||
Priority: 4,
|
||
}
|
||
|
||
msgBytes, _ := json.Marshal(scanMsg)
|
||
queue.Publish("scan", string(msgBytes))
|
||
```
|
||
|
||
**From CLI (RabbitMQ):**
|
||
|
||
```bash
|
||
rabbitmqadmin publish exchange=amq.default \
|
||
routing_key=scan \
|
||
payload='{"id":"scan-123","targets":[{"value":"192.168.1.100","type":"single_ip"}],"options":{"template_id":"quick"},"priority":3}'
|
||
```
|
||
|
||
### Monitoring Scan Progress
|
||
|
||
**ValKey Query:**
|
||
|
||
```bash
|
||
docker exec sirius-valkey valkey-cli GET scan:scan-123
|
||
```
|
||
|
||
**Output:**
|
||
|
||
```json
|
||
{
|
||
"status": "running",
|
||
"start_time": "2025-10-25T10:30:00Z",
|
||
"hosts": ["192.168.1.1", "192.168.1.2", "192.168.1.100"],
|
||
"hosts_completed": 2,
|
||
"vulnerabilities": [
|
||
{
|
||
"id": "CVE-2017-0143",
|
||
"severity": "critical",
|
||
"title": "EternalBlue SMB RCE"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Troubleshooting
|
||
|
||
### NSE Script Errors
|
||
|
||
**Symptom:** "NSE: failed to initialize the script engine"
|
||
|
||
**Causes:**
|
||
|
||
- Script syntax errors
|
||
- Missing script dependencies
|
||
- Blacklisted scripts referenced
|
||
|
||
**Solutions:**
|
||
|
||
1. **Check script exists:**
|
||
|
||
```bash
|
||
docker exec sirius-engine ls /opt/sirius/nse/sirius-nse/scripts/ | grep vulners
|
||
```
|
||
|
||
2. **Test script manually:**
|
||
|
||
```bash
|
||
docker exec sirius-engine nmap --script-help vulners
|
||
```
|
||
|
||
3. **Check blacklist:**
|
||
|
||
Review `internal/nse/script_blacklist.go`
|
||
|
||
4. **Force repository sync:**
|
||
|
||
```bash
|
||
docker exec sirius-engine rm -rf /opt/sirius/nse/sirius-nse
|
||
docker restart sirius-engine # Will re-clone on startup
|
||
```
|
||
|
||
### Scan Timeouts
|
||
|
||
**Symptom:** Scans never complete, hosts stuck in "running" status
|
||
|
||
**Causes:**
|
||
|
||
- Network unreachability
|
||
- Firewall blocking scans
|
||
- Extremely large port ranges
|
||
|
||
**Solutions:**
|
||
|
||
1. **Reduce port range:**
|
||
|
||
```json
|
||
{ "port_range": "1-1000" } // Instead of 1-65535
|
||
```
|
||
|
||
2. **Enable aggressive mode:**
|
||
|
||
```json
|
||
{ "aggressive": true } // Faster but more detectable
|
||
```
|
||
|
||
3. **Check network connectivity:**
|
||
|
||
```bash
|
||
docker exec sirius-engine ping 192.168.1.100
|
||
```
|
||
|
||
4. **View scanner logs:**
|
||
|
||
```bash
|
||
docker logs -f sirius-engine | grep "ERROR\|Failed"
|
||
```
|
||
|
||
### Memory Issues
|
||
|
||
**Symptom:** Scanner crashes with OOM (Out of Memory) errors
|
||
|
||
**Causes:**
|
||
|
||
- Too many concurrent workers
|
||
- Large target ranges without pagination
|
||
- Memory leaks in scanning tools
|
||
|
||
**Solutions:**
|
||
|
||
1. **Reduce worker pool size:**
|
||
|
||
```go
|
||
const DEFAULT_WORKERS = 5 // Down from 10
|
||
```
|
||
|
||
2. **Scan smaller ranges:**
|
||
|
||
Split large CIDR blocks into smaller chunks.
|
||
|
||
3. **Increase Docker memory:**
|
||
|
||
```yaml
|
||
# docker-compose.yaml
|
||
services:
|
||
sirius-engine:
|
||
deploy:
|
||
resources:
|
||
limits:
|
||
memory: 4G # Increase from 2G
|
||
```
|
||
|
||
4. **Monitor memory usage:**
|
||
|
||
```bash
|
||
docker stats sirius-engine
|
||
```
|
||
|
||
### RabbitMQ Disconnections
|
||
|
||
**Symptom:** "Failed to consume from queue: connection closed"
|
||
|
||
**Causes:**
|
||
|
||
- RabbitMQ container crashed
|
||
- Network partition
|
||
- Connection timeout
|
||
|
||
**Solutions:**
|
||
|
||
1. **Check RabbitMQ status:**
|
||
|
||
```bash
|
||
docker exec sirius-rabbitmq rabbitmqctl status
|
||
```
|
||
|
||
2. **Restart RabbitMQ:**
|
||
|
||
```bash
|
||
docker restart sirius-rabbitmq
|
||
```
|
||
|
||
3. **Check connection string:**
|
||
|
||
```bash
|
||
echo $RABBITMQ_URL
|
||
# Should be: amqp://guest:guest@sirius-rabbitmq:5672/
|
||
```
|
||
|
||
4. **Implement reconnection logic (future):**
|
||
|
||
Add automatic reconnection with exponential backoff.
|
||
|
||
---
|
||
|
||
## Performance Benchmarks
|
||
|
||
**Environment:** 4-core CPU, 8GB RAM, 1Gbps network
|
||
|
||
| Scan Type | Target Range | Duration | Hosts/sec |
|
||
| --------------------- | ---------------------- | --------- | --------- |
|
||
| Quick (top 500 ports) | /24 subnet (254 hosts) | 3-5 min | ~50-80 |
|
||
| High-Risk | /24 subnet | 15-25 min | ~10-15 |
|
||
| All Scripts | /24 subnet | 2-4 hours | ~1-2 |
|
||
| Quick | Single host | 10-20 sec | N/A |
|
||
| High-Risk | Single host | 2-5 min | N/A |
|
||
|
||
**Optimization Tips:**
|
||
|
||
- **Parallelism:** Increase worker count for faster subnet scans
|
||
- **Port Range:** Limit to relevant ports (80, 443, 22, 3389)
|
||
- **Script Selection:** Use templates instead of wildcard `*`
|
||
- **Network:** Faster networks = faster scans (avoid VPNs)
|
||
|
||
---
|
||
|
||
## Security Considerations
|
||
|
||
### Responsible Scanning
|
||
|
||
**Always:**
|
||
|
||
- ✅ Get written authorization before scanning
|
||
- ✅ Use reasonable rate limiting (avoid DoS)
|
||
- ✅ Scan only approved IP ranges
|
||
- ✅ Document all scanning activities
|
||
|
||
**Never:**
|
||
|
||
- ❌ Scan production systems without approval
|
||
- ❌ Use aggressive mode on fragile systems
|
||
- ❌ Store vulnerability data in insecure locations
|
||
- ❌ Share scan results with unauthorized parties
|
||
|
||
### Rate Limiting
|
||
|
||
The scanner implements implicit rate limiting through:
|
||
|
||
- **Worker Pool Size:** Limits concurrent scans
|
||
- **Nmap Timing:** `-T4` (balanced) by default, not `-T5` (insane)
|
||
- **Connection Limits:** Naabu respects system limits
|
||
|
||
**Future Enhancements:**
|
||
|
||
- Configurable rate limits (scans per second)
|
||
- Per-target rate limiting
|
||
- Adaptive throttling based on network conditions
|
||
|
||
---
|
||
|
||
## Future Enhancements
|
||
|
||
**Planned Features:**
|
||
|
||
1. **DNS Wildcard Support:** Subdomain enumeration via CT logs
|
||
2. **Distributed Scanning:** Multiple scanner instances coordinating via ValKey
|
||
3. **Scan Scheduling:** Cron-based recurring scans
|
||
4. **Advanced Reporting:** PDF/HTML reports with executive summaries
|
||
5. **Machine Learning:** Anomaly detection for unusual scan results
|
||
6. **API Gateway Integration:** Direct scan triggering via REST API
|
||
7. **WebSocket Updates:** Real-time scan progress in UI
|
||
8. **Scan Comparison:** Diff scans to identify new vulnerabilities
|
||
|
||
---
|
||
|
||
## ValKey Schema Reference
|
||
|
||
ValKey (Redis-compatible) is used for live scan state. Key structure:
|
||
|
||
- **`currentScan`** – Main key storing the live scan result as JSON. Contains the full `ScanResult` object with hosts, vulnerabilities, sub-scans, and progress.
|
||
- **Encoding:** JSON may be stored as a base64-encoded string in some contexts; decode with `b64Decode` from `~/utils/std.ts` when reading from the frontend.
|
||
- **Polling:** The frontend polls this key at regular intervals (5-second refetch) to display live progress.
|
||
- **TTL:** No TTL is set; the key persists until overwritten by the next scan.
|
||
|
||
---
|
||
|
||
## Profile vs Template System
|
||
|
||
- **Scan Profiles:** High-level presets that control overall scan behavior (e.g., "quick-scan", "full-scan", "agent-only"). Stored as `ScanProfile` type. They determine which scan methods are enabled and how they are configured.
|
||
- **Agent Templates:** Specific detection templates used by the agent scanner (e.g., checks for a specific CVE). Stored in the template system. They define detection steps such as `file_content`, `file_hash`, `file_search`, and `version_cmd`. The authoritative set is `KnownDetectionTypes` in `app-agent/internal/modules/detection_types.go` (drift-checked against the runtime registry by `detection_types_registry_test.go`).
|
||
- Profiles may reference templates but are not the same thing: profiles define *what* to run and *how*; templates define the concrete detection logic.
|
||
|
||
---
|
||
|
||
## RabbitMQ Message Schema
|
||
|
||
- **Queue:** Scan queue (consumed by app-scanner).
|
||
- **Message format:** JSON with target list and scan options.
|
||
- **Scan request contents:** Includes `targets`, `scan_types` configuration, and `agent_scan` configuration.
|
||
- **Results:** app-scanner writes results back to the ValKey `currentScan` key.
|
||
|
||
---
|
||
|
||
## LLM Context
|
||
|
||
This documentation is optimized for AI assistant context. Key information:
|
||
|
||
**Architecture:** Message-driven (RabbitMQ), worker pool concurrency, strategy pattern for extensibility
|
||
|
||
**Core Flow:** Receive scan message → Expand targets → Distribute to workers → Execute strategies → Enrich results → Submit to API
|
||
|
||
**Key Components:** ScanManager (orchestration), ScanStrategies (tool integrations), TemplateManager (scan configs), NSE system (script management)
|
||
|
||
**Integration Points:** RabbitMQ (input), ValKey (state), PostgreSQL (results), NVD API (CVE enrichment)
|
||
|
||
**Development:** Go 1.23+, runs in sirius-engine container, live reload with Air, unit/integration tests
|
||
|
||
**Common Tasks:** Add strategies (implement interface + factory), create templates (InitializeSystemTemplates), debug NSE (check symlinks/manifest)
|
||
|
||
---
|
||
|
||
**Last Updated:** 2025-10-25
|
||
**Version:** 1.0.0
|
||
**Maintainer:** Sirius Team
|
||
**Repository:** https://github.com/SiriusScan/app-scanner
|