package db import ( "database/sql" "time" "crypto-miner-server/internal/recon" ) func (d *Database) ensureReconSSRfCanaryTable() error { _, err := d.Exec(`CREATE TABLE IF NOT EXISTS recon_ssrf_canary ( scan_id TEXT PRIMARY KEY, host TEXT NOT NULL, canary_url TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'pending', paste_field_name TEXT NOT NULL DEFAULT '', paste_field_id TEXT NOT NULL DEFAULT '', hit_at DATETIME, created_at DATETIME NOT NULL)`) return err } func (d *Database) InsertReconSSRfCanary(scanID, host, canaryURL, pasteName, pasteID string) error { if err := d.ensureReconSSRfCanaryTable(); err != nil { return err } _, err := d.Exec(`INSERT INTO recon_ssrf_canary (scan_id, host, canary_url, status, paste_field_name, paste_field_id, created_at) VALUES (?, ?, ?, 'pending', ?, ?, ?)`, scanID, host, canaryURL, pasteName, pasteID, time.Now().UTC().Format(time.RFC3339Nano)) return err } func (d *Database) GetReconSSRfCanary(scanID string) (*recon.SSRFCanaryInfo, error) { if err := d.ensureReconSSRfCanaryTable(); err != nil { return nil, err } var host, canaryURL, status, pasteName, pasteID string var hitAt sql.NullString err := d.QueryRow(`SELECT host, canary_url, status, paste_field_name, paste_field_id, hit_at FROM recon_ssrf_canary WHERE scan_id = ?`, scanID).Scan(&host, &canaryURL, &status, &pasteName, &pasteID, &hitAt) if err != nil { return nil, err } info := &recon.SSRFCanaryInfo{ScanID: scanID, URL: canaryURL, Status: status, PasteTarget: canaryURL, PasteFieldName: pasteName, PasteFieldID: pasteID} if hitAt.Valid { if ts, err := time.Parse(time.RFC3339Nano, hitAt.String); err == nil { info.HitAt = &ts } } return info, nil } func (d *Database) UpdateReconSSRfCanaryPasteField(scanID, pasteName, pasteID string) error { if err := d.ensureReconSSRfCanaryTable(); err != nil { return err } _, err := d.Exec(`UPDATE recon_ssrf_canary SET paste_field_name = ?, paste_field_id = ? WHERE scan_id = ?`, pasteName, pasteID, scanID) return err } func (d *Database) MarkReconSSRfCanaryHit(scanID string) (bool, error) { if err := d.ensureReconSSRfCanaryTable(); err != nil { return false, err } res, err := d.Exec(`UPDATE recon_ssrf_canary SET status = 'confirmed', hit_at = ? WHERE scan_id = ? AND status != 'confirmed'`, time.Now().UTC().Format(time.RFC3339Nano), scanID) if err != nil { return false, err } if n, _ := res.RowsAffected(); n > 0 { return true, nil } var status string err = d.QueryRow(`SELECT status FROM recon_ssrf_canary WHERE scan_id = ?`, scanID).Scan(&status) return status == "confirmed", err }