// Package policy 对外门面:Guard + DomainTrie + RobotsChecker 组合与审计落点。 package policy import ( "context" "net/http" "time" "onesvm.com/onesvm/browser-server/internal/store" ) // Engine 出站策略引擎(gateway 入队前 / httpx 每跳重验共用一个实例)。 type Engine struct { Guard *Guard Trie *DomainTrie Robots *RobotsChecker db *store.DB robotsBypass map[string]bool // 特权 key 覆盖 robots 时打审计标记(首版预留) } // NewEngine 组装策略引擎(db 可为 nil:单测场景禁用审计落库)。 func NewEngine(db *store.DB, httpClient *http.Client) *Engine { e := &Engine{ Guard: &Guard{}, Trie: NewDomainTrie(), Robots: NewRobotsChecker(db, httpClient), db: db, } // ITER-3 FIX-3:robots 拉取的每跳重验挂接(3xx 目标过 SSRF 门闩)。 e.Robots.SetRedirectCheck(e.RedirectCheck) return e } // Reload 热载域名规则(rules 表 → trie)。 func (e *Engine) Reload() (int, error) { return e.Trie.LoadFromStore(e.db) } // Authorize 综合放行判定:SSRF(含 DNS)→ 域名 trie。 // 任一拒绝即 DeniedError(含 rule_id,调用方落审计)。 func (e *Engine) Authorize(ctx context.Context, rawURL string) *DeniedError { if d := e.Guard.CheckURL(rawURL); d != nil { return d } host := parseHost(rawURL) if a, ok := e.Trie.Lookup(host); ok && a == ActionDeny { return &DeniedError{RuleID: "domain_deny", Reason: "域名黑名单命中"} } return nil } // Audit 合规拦截审计(denied 记录,design-arch §5.4 审计行)。 func (e *Engine) Audit(consumerID int64, url, ruleID string) { if e.db == nil { return } _ = e.db.AuditAppend(consumerID, url, ruleID) } // RedirectCheck 重定向每跳重验(ITER-3 FIX-3 接线:design-arch §5.4)。 // 供 httpx.Client CheckRedirect hook / robots 拉取 client 调用——新 URL 先 CheckURL // 再 ResolveCheck(DNS 重解析比对私网)。出站面:robots 拉取 + 引擎内网调用; // CDP 导航(lightpanda/headless-shell)不经 Go client,其重定向防护属引擎容器职责。 func (e *Engine) RedirectCheck(ctx context.Context, rawURL string) *DeniedError { return e.Guard.CheckRedirect(ctx, rawURL) } // RobotsAllowed robots 判定(普通 key)。特权覆盖由 W2 调用方显式传 bypass 并打审计。 func (e *Engine) RobotsAllowed(ctx context.Context, rawURL string, bypass bool) (bool, error) { if bypass { return true, nil } cctx, cancel := context.WithTimeout(ctx, 12*time.Second) defer cancel() return e.Robots.Allowed(cctx, rawURL, "onesvm-browser-server") }