Skip to content

Commit e04a464

Browse files
feat: add temporal comparison features for scan results
- Added new results commands for comparing scans over time: - `diff` to compare two specific scans - `history` to show scan progression for a target - `changes` to analyze differences in a time window - Added context timeout support for discovery operations - Modified quick mode configuration to enable minimal web crawl for auth endpoint discovery - Implemented detailed comparison logic to identify new and fixed vulnerabilities - Added formatte
1 parent 25ff12c commit e04a464

10 files changed

Lines changed: 1297 additions & 51 deletions

File tree

cmd/discover.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cmd
22

33
import (
4+
"context"
45
"encoding/json"
56
"fmt"
67
"strings"
@@ -64,8 +65,12 @@ func runDiscoveryOnly(target string) error {
6465
// Create discovery engine
6566
engine := discovery.NewEngine(config, log.WithComponent("discovery"))
6667

67-
// Start discovery
68-
session, err := engine.StartDiscovery(target)
68+
// Create context with timeout for discovery
69+
ctx, cancel := context.WithTimeout(context.Background(), config.Timeout)
70+
defer cancel()
71+
72+
// Start discovery (passing context for timeout)
73+
session, err := engine.StartDiscovery(ctx, target)
6974
if err != nil {
7075
return fmt.Errorf("failed to start discovery: %w", err)
7176
}

cmd/monitoring.go

Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"time"
6+
7+
"github.com/fatih/color"
8+
"github.com/spf13/cobra"
9+
)
10+
11+
// TASK 12: Monitoring Query Commands
12+
13+
var monitoringCmd = &cobra.Command{
14+
Use: "monitoring",
15+
Short: "Query monitoring data (alerts, DNS changes, certificates)",
16+
Long: `Access monitoring data stored in the database.
17+
18+
Available subcommands:
19+
alerts - List monitoring alerts
20+
dns-changes - Show DNS record changes for a target
21+
certificates - Show certificate expiry information
22+
git-changes - Show Git repository changes
23+
web-changes - Show website change detection
24+
25+
Examples:
26+
shells monitoring alerts
27+
shells monitoring alerts --target example.com --severity critical
28+
shells monitoring dns-changes example.com
29+
shells monitoring certificates expiring --days 30`,
30+
}
31+
32+
func init() {
33+
rootCmd.AddCommand(monitoringCmd)
34+
35+
// Add subcommands
36+
monitoringCmd.AddCommand(monitoringAlertsCmd)
37+
monitoringCmd.AddCommand(monitoringDNSChangesCmd)
38+
monitoringCmd.AddCommand(monitoringCertificatesCmd)
39+
monitoringCmd.AddCommand(monitoringGitChangesCmd)
40+
monitoringCmd.AddCommand(monitoringWebChangesCmd)
41+
}
42+
43+
// monitoringAlertsCmd lists monitoring alerts
44+
var monitoringAlertsCmd = &cobra.Command{
45+
Use: "alerts",
46+
Short: "List monitoring alerts",
47+
Long: `List all monitoring alerts or filter by target/severity.
48+
49+
Examples:
50+
shells monitoring alerts
51+
shells monitoring alerts --target example.com
52+
shells monitoring alerts --severity critical
53+
shells monitoring alerts --since 7d`,
54+
RunE: func(cmd *cobra.Command, args []string) error {
55+
target, _ := cmd.Flags().GetString("target")
56+
severity, _ := cmd.Flags().GetString("severity")
57+
sinceDuration, _ := cmd.Flags().GetString("since")
58+
limit, _ := cmd.Flags().GetInt("limit")
59+
output, _ := cmd.Flags().GetString("output")
60+
61+
store := GetStore()
62+
if store == nil {
63+
return fmt.Errorf("database not initialized")
64+
}
65+
66+
ctx := GetContext()
67+
68+
// Calculate time window
69+
var since time.Time
70+
if sinceDuration != "" {
71+
duration, err := parseDuration(sinceDuration)
72+
if err != nil {
73+
return fmt.Errorf("invalid duration: %w", err)
74+
}
75+
since = time.Now().Add(-duration)
76+
}
77+
78+
// Query alerts from database
79+
// Note: This requires implementing GetMonitoringAlerts in ResultStore interface
80+
// For now, show a placeholder message
81+
fmt.Println()
82+
color.Cyan("═══ Monitoring Alerts ═══")
83+
84+
if target != "" {
85+
fmt.Printf(" Target: %s\n", target)
86+
}
87+
if severity != "" {
88+
fmt.Printf(" Severity: %s\n", severity)
89+
}
90+
if sinceDuration != "" {
91+
fmt.Printf(" Since: %s (from %s)\n", sinceDuration, since.Format("2006-01-02"))
92+
}
93+
fmt.Println()
94+
95+
// TODO: Implement actual database query when monitoring_alerts table is populated
96+
color.Yellow(" Note: Monitoring alerts table exists but no query method implemented yet.\n")
97+
color.Yellow(" This feature will be available once monitoring is actively running.\n")
98+
fmt.Println()
99+
100+
_ = ctx
101+
_ = limit
102+
_ = output
103+
104+
return nil
105+
},
106+
}
107+
108+
// monitoringDNSChangesCmd shows DNS changes for a target
109+
var monitoringDNSChangesCmd = &cobra.Command{
110+
Use: "dns-changes <target>",
111+
Short: "Show DNS record changes for a target",
112+
Long: `Display DNS record changes detected for a target.
113+
114+
Examples:
115+
shells monitoring dns-changes example.com
116+
shells monitoring dns-changes example.com --since 30d
117+
shells monitoring dns-changes example.com --record-type A`,
118+
Args: cobra.ExactArgs(1),
119+
RunE: func(cmd *cobra.Command, args []string) error {
120+
target := args[0]
121+
sinceDuration, _ := cmd.Flags().GetString("since")
122+
recordType, _ := cmd.Flags().GetString("record-type")
123+
output, _ := cmd.Flags().GetString("output")
124+
125+
store := GetStore()
126+
if store == nil {
127+
return fmt.Errorf("database not initialized")
128+
}
129+
130+
ctx := GetContext()
131+
132+
var since time.Time
133+
if sinceDuration != "" {
134+
duration, err := parseDuration(sinceDuration)
135+
if err != nil {
136+
return fmt.Errorf("invalid duration: %w", err)
137+
}
138+
since = time.Now().Add(-duration)
139+
}
140+
141+
fmt.Println()
142+
color.Cyan("═══ DNS Changes: %s ═══", target)
143+
144+
if sinceDuration != "" {
145+
fmt.Printf(" Since: %s (from %s)\n", sinceDuration, since.Format("2006-01-02"))
146+
}
147+
if recordType != "" {
148+
fmt.Printf(" Record Type: %s\n", recordType)
149+
}
150+
fmt.Println()
151+
152+
// TODO: Implement actual database query
153+
color.Yellow(" Note: DNS monitoring table exists but no query method implemented yet.\n")
154+
color.Yellow(" This feature will be available once monitoring is actively running.\n")
155+
fmt.Println()
156+
157+
_ = ctx
158+
_ = output
159+
160+
return nil
161+
},
162+
}
163+
164+
// monitoringCertificatesCmd shows certificate expiry information
165+
var monitoringCertificatesCmd = &cobra.Command{
166+
Use: "certificates",
167+
Short: "Show certificate expiry information",
168+
Long: `Display SSL/TLS certificates and their expiry status.
169+
170+
Examples:
171+
shells monitoring certificates expiring --days 30
172+
shells monitoring certificates --domain example.com
173+
shells monitoring certificates --output json`,
174+
RunE: func(cmd *cobra.Command, args []string) error {
175+
domain, _ := cmd.Flags().GetString("domain")
176+
days, _ := cmd.Flags().GetInt("days")
177+
expiring, _ := cmd.Flags().GetBool("expiring")
178+
output, _ := cmd.Flags().GetString("output")
179+
180+
store := GetStore()
181+
if store == nil {
182+
return fmt.Errorf("database not initialized")
183+
}
184+
185+
ctx := GetContext()
186+
187+
fmt.Println()
188+
color.Cyan("═══ SSL/TLS Certificates ═══")
189+
190+
if domain != "" {
191+
fmt.Printf(" Domain: %s\n", domain)
192+
}
193+
if expiring {
194+
fmt.Printf(" Expiring within: %d days\n", days)
195+
}
196+
fmt.Println()
197+
198+
// TODO: Implement actual database query
199+
color.Yellow(" Note: Certificate monitoring table exists but no query method implemented yet.\n")
200+
color.Yellow(" This feature will be available once monitoring is actively running.\n")
201+
fmt.Println()
202+
203+
_ = ctx
204+
_ = output
205+
206+
return nil
207+
},
208+
}
209+
210+
// monitoringGitChangesCmd shows Git repository changes
211+
var monitoringGitChangesCmd = &cobra.Command{
212+
Use: "git-changes <repo-url>",
213+
Short: "Show Git repository changes",
214+
Long: `Display changes detected in Git repositories.
215+
216+
Examples:
217+
shells monitoring git-changes https://github.com/example/repo
218+
shells monitoring git-changes https://github.com/example/repo --since 7d`,
219+
Args: cobra.ExactArgs(1),
220+
RunE: func(cmd *cobra.Command, args []string) error {
221+
repoURL := args[0]
222+
sinceDuration, _ := cmd.Flags().GetString("since")
223+
output, _ := cmd.Flags().GetString("output")
224+
225+
store := GetStore()
226+
if store == nil {
227+
return fmt.Errorf("database not initialized")
228+
}
229+
230+
ctx := GetContext()
231+
232+
var since time.Time
233+
if sinceDuration != "" {
234+
duration, err := parseDuration(sinceDuration)
235+
if err != nil {
236+
return fmt.Errorf("invalid duration: %w", err)
237+
}
238+
since = time.Now().Add(-duration)
239+
}
240+
241+
fmt.Println()
242+
color.Cyan("═══ Git Changes: %s ═══", repoURL)
243+
244+
if sinceDuration != "" {
245+
fmt.Printf(" Since: %s (from %s)\n", sinceDuration, since.Format("2006-01-02"))
246+
}
247+
fmt.Println()
248+
249+
// TODO: Implement actual database query
250+
color.Yellow(" Note: Git monitoring table exists but no query method implemented yet.\n")
251+
color.Yellow(" This feature will be available once monitoring is actively running.\n")
252+
fmt.Println()
253+
254+
_ = ctx
255+
_ = output
256+
257+
return nil
258+
},
259+
}
260+
261+
// monitoringWebChangesCmd shows website change detection
262+
var monitoringWebChangesCmd = &cobra.Command{
263+
Use: "web-changes <url>",
264+
Short: "Show website change detection",
265+
Long: `Display changes detected on websites.
266+
267+
Examples:
268+
shells monitoring web-changes https://example.com
269+
shells monitoring web-changes https://example.com --since 7d`,
270+
Args: cobra.ExactArgs(1),
271+
RunE: func(cmd *cobra.Command, args []string) error {
272+
url := args[0]
273+
sinceDuration, _ := cmd.Flags().GetString("since")
274+
output, _ := cmd.Flags().GetString("output")
275+
276+
store := GetStore()
277+
if store == nil {
278+
return fmt.Errorf("database not initialized")
279+
}
280+
281+
ctx := GetContext()
282+
283+
var since time.Time
284+
if sinceDuration != "" {
285+
duration, err := parseDuration(sinceDuration)
286+
if err != nil {
287+
return fmt.Errorf("invalid duration: %w", err)
288+
}
289+
since = time.Now().Add(-duration)
290+
}
291+
292+
fmt.Println()
293+
color.Cyan("═══ Web Changes: %s ═══", url)
294+
295+
if sinceDuration != "" {
296+
fmt.Printf(" Since: %s (from %s)\n", sinceDuration, since.Format("2006-01-02"))
297+
}
298+
fmt.Println()
299+
300+
// TODO: Implement actual database query
301+
color.Yellow(" Note: Web monitoring table exists but no query method implemented yet.\n")
302+
color.Yellow(" This feature will be available once monitoring is actively running.\n")
303+
fmt.Println()
304+
305+
_ = ctx
306+
_ = output
307+
308+
return nil
309+
},
310+
}
311+
312+
func init() {
313+
// Alerts command flags
314+
monitoringAlertsCmd.Flags().String("target", "", "Filter by target")
315+
monitoringAlertsCmd.Flags().String("severity", "", "Filter by severity (critical, high, medium, low)")
316+
monitoringAlertsCmd.Flags().String("since", "", "Show alerts since duration (e.g., 7d, 24h)")
317+
monitoringAlertsCmd.Flags().Int("limit", 100, "Maximum number of alerts to show")
318+
monitoringAlertsCmd.Flags().StringP("output", "o", "text", "Output format (text, json)")
319+
320+
// DNS changes command flags
321+
monitoringDNSChangesCmd.Flags().String("since", "", "Show changes since duration")
322+
monitoringDNSChangesCmd.Flags().String("record-type", "", "Filter by DNS record type (A, AAAA, MX, TXT, etc.)")
323+
monitoringDNSChangesCmd.Flags().StringP("output", "o", "text", "Output format (text, json)")
324+
325+
// Certificates command flags
326+
monitoringCertificatesCmd.Flags().String("domain", "", "Filter by domain")
327+
monitoringCertificatesCmd.Flags().Bool("expiring", false, "Show only expiring certificates")
328+
monitoringCertificatesCmd.Flags().Int("days", 30, "Days until expiry (with --expiring)")
329+
monitoringCertificatesCmd.Flags().StringP("output", "o", "text", "Output format (text, json)")
330+
331+
// Git changes command flags
332+
monitoringGitChangesCmd.Flags().String("since", "", "Show changes since duration")
333+
monitoringGitChangesCmd.Flags().StringP("output", "o", "text", "Output format (text, json)")
334+
335+
// Web changes command flags
336+
monitoringWebChangesCmd.Flags().String("since", "", "Show changes since duration")
337+
monitoringWebChangesCmd.Flags().StringP("output", "o", "text", "Output format (text, json)")
338+
}

cmd/orchestrator/orchestrator.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ func (o *Orchestrator) RunIntelligentDiscovery(ctx context.Context, target strin
5959

6060
discoveryEngine := discovery.NewEngineWithConfig(discoveryConfig, o.log.WithComponent("discovery"), o.cfg)
6161

62-
// Start discovery
63-
session, err := discoveryEngine.StartDiscovery(target)
62+
// Start discovery (passing context for timeout propagation)
63+
session, err := discoveryEngine.StartDiscovery(ctx, target)
6464
if err != nil {
6565
return fmt.Errorf("failed to start discovery: %w", err)
6666
}

0 commit comments

Comments
 (0)