feat: human-readable scan_id from UTC time + request CIDRs + short random suffix

Made-with: Cursor
This commit is contained in:
PTah
2026-04-13 14:55:40 +10:00
parent 635876bbe3
commit 5b3eeaed45
3 changed files with 68 additions and 6 deletions
+2 -1
View File
@@ -24,12 +24,13 @@ func NewPostgresStore(db *sql.DB) *PostgresStore {
} }
func (s *PostgresStore) CreateScan(req CreateScanRequest) (ScanJob, error) { func (s *PostgresStore) CreateScan(req CreateScanRequest) (ScanJob, error) {
cidrSnapshot := append([]string(nil), req.CIDRs...)
if err := validateCreateScanRequest(&req); err != nil { if err := validateCreateScanRequest(&req); err != nil {
return ScanJob{}, err return ScanJob{}, err
} }
applyDefaultOptions(&req.Options) applyDefaultOptions(&req.Options)
id, err := newID() id, err := newScanID(cidrSnapshot)
if err != nil { if err != nil {
return ScanJob{}, err return ScanJob{}, err
} }
+29
View File
@@ -0,0 +1,29 @@
package scans
import (
"strings"
"testing"
)
func TestNewScanIDShape(t *testing.T) {
id, err := newScanID([]string{"192.168.160.0/24"})
if err != nil {
t.Fatal(err)
}
parts := strings.Split(id, "_")
if len(parts) < 3 {
t.Fatalf("expected at least time_slug_suffix, got %q", id)
}
if len(parts[len(parts)-1]) != 4 {
t.Fatalf("expected 4-hex suffix, got %q", id)
}
if !strings.Contains(id, "192.168.160.0_24") {
t.Fatalf("expected sanitized cidr in id: %q", id)
}
}
func TestSanitizeScanSlug(t *testing.T) {
if g := sanitizeScanSlug("10.0.0.0/8"); g != "10.0.0.0_8" {
t.Fatalf("got %q", g)
}
}
+37 -5
View File
@@ -6,6 +6,7 @@ import (
"errors" "errors"
"net" "net"
"sort" "sort"
"strings"
"sync" "sync"
"time" "time"
) )
@@ -161,13 +162,14 @@ func NewMemoryStore() *MemoryStore {
} }
func (s *MemoryStore) CreateScan(req CreateScanRequest) (ScanJob, error) { func (s *MemoryStore) CreateScan(req CreateScanRequest) (ScanJob, error) {
cidrSnapshot := append([]string(nil), req.CIDRs...)
if err := validateCreateScanRequest(&req); err != nil { if err := validateCreateScanRequest(&req); err != nil {
return ScanJob{}, err return ScanJob{}, err
} }
applyDefaultOptions(&req.Options) applyDefaultOptions(&req.Options)
id, err := newID() id, err := newScanID(cidrSnapshot)
if err != nil { if err != nil {
return ScanJob{}, err return ScanJob{}, err
} }
@@ -546,12 +548,42 @@ func validateCreateScanRequest(req *CreateScanRequest) error {
return nil return nil
} }
func newID() (string, error) { // newScanID — человекочитаемый scan_id: UTC-время с наносекундами + CIDR из запроса (как ввёл пользователь, до expand) + 4 hex (уникальность при коллизии по времени/CIDR).
var b [16]byte func newScanID(cidrLabels []string) (string, error) {
if _, err := rand.Read(b[:]); err != nil { var tail [2]byte
if _, err := rand.Read(tail[:]); err != nil {
return "", err return "", err
} }
return hex.EncodeToString(b[:]), nil sfx := hex.EncodeToString(tail[:])
ts := time.Now().UTC().Format("20060102T150405.000000000")
raw := strings.TrimSpace(strings.Join(cidrLabels, "+"))
if raw == "" {
raw = "scan"
}
slug := sanitizeScanSlug(raw)
const maxSlug = 180
if len(slug) > maxSlug {
slug = slug[:maxSlug]
}
return ts + "_" + slug + "_" + sfx, nil
}
func sanitizeScanSlug(s string) string {
var b strings.Builder
b.Grow(len(s))
for _, r := range s {
switch {
case r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r >= '0' && r <= '9':
b.WriteRune(r)
case r == '.', r == '-', r == '_':
b.WriteRune(r)
case r == '/', r == ':', r == ',', r == '+', r == ' ', r == '\\':
b.WriteByte('_')
default:
b.WriteByte('_')
}
}
return strings.Trim(strings.TrimSpace(b.String()), "_")
} }
func dedupeHostsLatest(items []HostResult, limit int) []HostResult { func dedupeHostsLatest(items []HostResult, limit int) []HostResult {