package tui import ( "net/http" "strings" "github.com/charmbracelet/lipgloss" "github.com/r3g1tpr0cs/burterm/internal/project" "github.com/r3g1tpr0cs/burterm/internal/scanner" ) // Цвета подобраны по классам HTTP-статуса — тот же принцип, что и // цветовая маркировка в Burp: 2xx зелёный, 3xx голубой, 4xx жёлтый, // 5xx красный. Сетевые ошибки (таймаут, обрыв соединения — не HTTP- // статус вовсе) получают отдельный цвет, чтобы не путать с 5xx. var ( style2xx = lipgloss.NewStyle().Foreground(lipgloss.Color("42")) style3xx = lipgloss.NewStyle().Foreground(lipgloss.Color("51")) style4xx = lipgloss.NewStyle().Foreground(lipgloss.Color("220")) style5xx = lipgloss.NewStyle().Foreground(lipgloss.Color("196")) styleErrStatus = lipgloss.NewStyle().Foreground(lipgloss.Color("201")) styleDim = lipgloss.NewStyle().Foreground(lipgloss.Color("245")) // styleHighlight — единый стиль для строк, подпавших под пользовательское // regex-правило подсветки. Намеренно НЕ комбинируется с statusStyle на // одной строке: lipgloss.Style.Render оборачивает текст кодами // ESC[...]...ESC[0m, а ESC[0m сбрасывает вообще все атрибуты, включая // внешние — если вложить один Render внутрь другого, первый же // внутренний сброс "съест" стиль снаружи для остатка строки. Поэтому // для подсвеченных строк весь рендер идёт через один-единственный // styleHighlight, без отдельной раскраски статуса внутри. styleHighlight = lipgloss.NewStyle().Bold(true).Background(lipgloss.Color("58")).Foreground(lipgloss.Color("230")) ) // statusStyle подбирает стиль по классу статус-кода. func statusStyle(code int) lipgloss.Style { switch { case code >= 200 && code < 300: return style2xx case code >= 300 && code < 400: return style3xx case code >= 400 && code < 500: return style4xx case code >= 500 && code < 600: return style5xx default: return styleDim } } // colorLabelStyle раскрашивает пользовательскую цветовую метку проекта // (project.ColorLabel) в её собственный цвет — не путать с statusStyle, // которая красит по автоматическому классу HTTP-статуса: это разные // вещи, метка выставляется руками (клавиша "c" на вкладке Project). func colorLabelStyle(c project.ColorLabel) lipgloss.Style { switch c { case project.ColorRed: return lipgloss.NewStyle().Foreground(lipgloss.Color("196")) case project.ColorOrange: return lipgloss.NewStyle().Foreground(lipgloss.Color("208")) case project.ColorYellow: return lipgloss.NewStyle().Foreground(lipgloss.Color("220")) case project.ColorGreen: return lipgloss.NewStyle().Foreground(lipgloss.Color("42")) case project.ColorCyan: return lipgloss.NewStyle().Foreground(lipgloss.Color("51")) case project.ColorBlue: return lipgloss.NewStyle().Foreground(lipgloss.Color("33")) case project.ColorPurple: return lipgloss.NewStyle().Foreground(lipgloss.Color("129")) default: return styleDim } } // severityStyle подбирает стиль для находок Scanner — переиспользует // ту же цветовую шкалу, что и statusStyle, просто с другим входом. func severityStyle(sev scanner.Severity) lipgloss.Style { switch sev { case scanner.SeverityHigh: return style5xx case scanner.SeverityMedium: return style4xx case scanner.SeverityLow: return style3xx default: return styleDim } } // contentTypeLabel вытаскивает короткую метку типа контента из заголовка // Content-Type: "application/json; charset=utf-8" → "json". func contentTypeLabel(h http.Header) string { if h == nil { return "" } ct := h.Get("Content-Type") if ct == "" { return "" } if idx := strings.Index(ct, ";"); idx != -1 { ct = ct[:idx] } ct = strings.TrimSpace(ct) if idx := strings.LastIndex(ct, "/"); idx != -1 { return ct[idx+1:] } return ct }