model.goraw
package session
import (
"errors"
"fmt"
"net/url"
"strings"
"time"
)
const (
maxTabs = 50
maxHistoryItems = 200
maxTitleLength = 200
maxURLLength = 4096
)
type PrivacySettings struct {
Trackers bool `json:"trackers"`
Cookies bool `json:"cookies"`
Fingerprint bool `json:"fingerprint"`
WebRTC bool `json:"webrtc"`
}
type Tab struct {
ID string `json:"id"`
Title string `json:"title"`
URL string `json:"url"`
History []string `json:"history"`
HistoryIndex int `json:"historyIndex"`
Private bool `json:"private"`
}
type State struct {
Tabs []Tab `json:"tabs"`
ActiveTabID string `json:"activeTabId"`
Settings PrivacySettings `json:"settings"`
}
type Session struct {
ID string `json:"-"`
State State `json:"state"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
ExpiresAt time.Time `json:"expiresAt"`
}
func DefaultState(tabID string) State {
return State{
Tabs: []Tab{{
ID: tabID,
Title: "Новая вкладка",
URL: "veil://start",
History: []string{"veil://start"},
HistoryIndex: 0,
}},
ActiveTabID: tabID,
Settings: PrivacySettings{
Trackers: true,
Cookies: true,
Fingerprint: true,
WebRTC: true,
},
}
}
func (state State) Validate() error {
if len(state.Tabs) == 0 || len(state.Tabs) > maxTabs {
return fmt.Errorf("tabs must contain between 1 and %d items", maxTabs)
}
seen := make(map[string]struct{}, len(state.Tabs))
activeTabFound := false
for _, tab := range state.Tabs {
if tab.ID == "" || len(tab.ID) > 128 {
return errors.New("tab id is invalid")
}
if _, exists := seen[tab.ID]; exists {
return errors.New("tab ids must be unique")
}
seen[tab.ID] = struct{}{}
activeTabFound = activeTabFound || tab.ID == state.ActiveTabID
if len(tab.Title) > maxTitleLength {
return errors.New("tab title is too long")
}
if len(tab.History) == 0 || len(tab.History) > maxHistoryItems {
return fmt.Errorf("tab history must contain between 1 and %d items", maxHistoryItems)
}
if tab.HistoryIndex < 0 || tab.HistoryIndex >= len(tab.History) {
return errors.New("history index is out of range")
}
if err := validateBrowserURL(tab.URL); err != nil {
return fmt.Errorf("tab url: %w", err)
}
for _, item := range tab.History {
if err := validateBrowserURL(item); err != nil {
return fmt.Errorf("history url: %w", err)
}
}
}
if !activeTabFound {
return errors.New("active tab does not exist")
}
return nil
}
func validateBrowserURL(value string) error {
if value == "veil://start" {
return nil
}
if value == "" || len(value) > maxURLLength || strings.ContainsAny(value, "\r\n") {
return errors.New("url is invalid")
}
parsed, err := url.ParseRequestURI(value)
if err != nil || (parsed.Scheme != "http" && parsed.Scheme != "https") || parsed.Host == "" {
return errors.New("only http and https urls are allowed")
}
return nil
}