Add wsl linter and fix

Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
Émile Ré
2026-05-19 14:51:08 +04:00
parent eedfdcecc8
commit 9156d6a16a
882 changed files with 6068 additions and 574 deletions

View File

@@ -123,6 +123,7 @@ func (b *Browser) checkAlive() *agent.ToolResult {
IsError: true,
}
}
return nil
}

View File

@@ -72,6 +72,7 @@ func DownloadPDFTool() agent.Tool {
ErrorDetail: fmt.Sprintf("cannot download PDF: %s", err),
}), nil
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
@@ -95,6 +96,7 @@ func DownloadPDFTool() agent.Tool {
ErrorDetail: fmt.Sprintf("cannot create temp dir: %s", err),
}), nil
}
defer func() { _ = os.RemoveAll(tmpDir) }()
tmpFile := filepath.Join(tmpDir, "input.pdf")
@@ -106,6 +108,7 @@ func DownloadPDFTool() agent.Tool {
// Get page count.
conf := model.NewDefaultConfiguration()
pageCount, err := api.PageCountFile(tmpFile)
if err != nil {
return agent.ResultJSON(downloadPDFResult{
@@ -130,15 +133,18 @@ func DownloadPDFTool() agent.Tool {
// Read all extracted content files.
var sb strings.Builder
entries, _ := os.ReadDir(outDir)
for _, entry := range entries {
if entry.IsDir() {
continue
}
content, err := os.ReadFile(filepath.Join(outDir, entry.Name()))
if err != nil {
continue
}
sb.Write(content)
sb.WriteString("\n")
}

View File

@@ -69,6 +69,7 @@ func FetchRobotsTxtTool() agent.Tool {
ErrorDetail: fmt.Sprintf("cannot fetch robots.txt: %s", err),
}), nil
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
@@ -79,6 +80,7 @@ func FetchRobotsTxtTool() agent.Tool {
}
var result robotsResult
result.Found = true
scanner := bufio.NewScanner(resp.Body)

View File

@@ -73,6 +73,7 @@ func FetchSitemapTool() agent.Tool {
ErrorDetail: fmt.Sprintf("cannot fetch sitemap: %s", err),
}), nil
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
@@ -92,7 +93,9 @@ func FetchSitemapTool() agent.Tool {
ErrorDetail: fmt.Sprintf("cannot decompress gzipped sitemap: %s", err),
}), nil
}
defer func() { _ = gz.Close() }()
reader = gz
}
@@ -125,6 +128,7 @@ func FetchSitemapTool() agent.Tool {
func parseSitemapXML(r io.Reader) ([]string, error) {
var urls []string
decoder := xml.NewDecoder(r)
for {
@@ -132,6 +136,7 @@ func parseSitemapXML(r io.Reader) ([]string, error) {
if err == io.EOF {
break
}
if err != nil {
return urls, err
}

View File

@@ -119,7 +119,9 @@ func NewPinnedTransport() *http.Transport {
// Dial the first validated IP directly to prevent DNS rebinding.
pinnedAddr := net.JoinHostPort(ips[0].IP.String(), port)
var d net.Dialer
return d.DialContext(ctx, network, pinnedAddr)
},
}

View File

@@ -52,6 +52,7 @@ func DiffDocumentsTool() agent.Tool {
if labelA == "" {
labelA = "document_a"
}
labelB := p.LabelB
if labelB == "" {
labelB = "document_b"
@@ -80,6 +81,7 @@ func DiffDocumentsTool() agent.Tool {
if len(output) > maxDiffOutput {
output = output[:maxDiffOutput] + "\n[... diff truncated]"
}
result.UnifiedDiff = output
}
@@ -114,6 +116,7 @@ func computeDiff(linesA, linesB []string, labelA, labelB string) diffOutput {
for i := range dp {
dp[i] = make([]int, n+1)
}
for i := m - 1; i >= 0; i-- {
for j := n - 1; j >= 0; j-- {
if linesA[i] == linesB[j] {
@@ -131,6 +134,7 @@ func computeDiff(linesA, linesB []string, labelA, labelB string) diffOutput {
fmt.Fprintf(&sb, "--- %s\n+++ %s\n", labelA, labelB)
var added, removed int
i, j := 0, 0
for i < m || j < n {
if i < m && j < n && linesA[i] == linesB[j] {
@@ -139,10 +143,12 @@ func computeDiff(linesA, linesB []string, labelA, labelB string) diffOutput {
j++
} else if j < n && (i >= m || dp[i][j+1] >= dp[i+1][j]) {
sb.WriteString("+ " + linesB[j] + "\n")
added++
j++
} else if i < m {
sb.WriteString("- " + linesA[i] + "\n")
removed++
i++
}

View File

@@ -72,6 +72,7 @@ func FirecrawlSearchTool(apiKey string) agent.Tool {
if maxResults <= 0 {
maxResults = 5
}
if maxResults > 10 {
maxResults = 10
}
@@ -111,6 +112,7 @@ func firecrawlSearch(
if err != nil {
return nil, fmt.Errorf("cannot create request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+apiKey)
@@ -118,6 +120,7 @@ func firecrawlSearch(
if err != nil {
return nil, fmt.Errorf("cannot execute search request: %w", err)
}
defer func() { _ = resp.Body.Close() }()
respBody, err := io.ReadAll(resp.Body)

View File

@@ -91,6 +91,7 @@ func CheckGovernmentDBTool(apiKey string) agent.Tool {
if err != nil {
continue
}
for _, e := range entries {
*s.target = append(
*s.target,

View File

@@ -28,6 +28,7 @@ type userAgentTransport struct {
func (t *userAgentTransport) RoundTrip(r *http.Request) (*http.Response, error) {
r2 := r.Clone(r.Context())
r2.Header.Set("User-Agent", "Probo-Agent/1.0")
return t.next.RoundTrip(r2)
}
@@ -35,5 +36,6 @@ func newHTTPClient() *http.Client {
client := httpclient.DefaultPooledClient()
client.Timeout = 15 * time.Second
client.Transport = &userAgentTransport{next: client.Transport}
return client
}

View File

@@ -66,6 +66,7 @@ func CheckWaybackTool() agent.Tool {
// Check availability.
availURL := "https://archive.org/wayback/available?url=" + url.QueryEscape(p.URL)
body, err := httpGet(ctx, client, availURL)
if err != nil {
result.ErrorDetail = fmt.Sprintf("cannot check Wayback Machine availability: %s", err)
@@ -118,6 +119,7 @@ func httpGet(ctx context.Context, client *http.Client, rawURL string) ([]byte, e
if err != nil {
return nil, err
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {

View File

@@ -101,6 +101,7 @@ func CheckCORSTool() agent.Tool {
ErrorDetail: fmt.Sprintf("cannot fetch %s: %s", p.URL, err),
}), nil
}
defer func() { _ = resp.Body.Close() }()
allowOrigin := resp.Header.Get("Access-Control-Allow-Origin")

View File

@@ -92,6 +92,7 @@ func AnalyzeCSPTool() agent.Tool {
ErrorDetail: fmt.Sprintf("cannot fetch %s: %s", p.URL, err),
}), nil
}
defer func() { _ = resp.Body.Close() }()
raw := resp.Header.Get("Content-Security-Policy")
@@ -111,6 +112,7 @@ func AnalyzeCSPTool() agent.Tool {
directives := parseCSPDirectives(raw)
var hasUnsafeEval, hasUnsafeInline, hasWildcard bool
for _, d := range directives {
for _, v := range d.Values {
switch v {

View File

@@ -46,6 +46,7 @@ func parseDMARCTag(record, tag string) string {
return after
}
}
return ""
}
@@ -60,6 +61,7 @@ func CheckDMARCTool() agent.Tool {
}
client := dns.NewClient()
answers, err := queryDNS(
ctx,
client,

View File

@@ -53,10 +53,14 @@ func CheckDNSRecordsTool() agent.Tool {
hdr := dns.Header{Name: fqdn, Class: dns.ClassINET}
client := dns.NewClient()
var result dnsRecordsResult
var errs []string
var (
result dnsRecordsResult
errs []string
)
// A records.
if answers, err := queryDNS(ctx, client, &dns.A{Hdr: hdr}); err != nil {
errs = append(errs, fmt.Sprintf("A query failed: %s", err))
} else {
@@ -148,12 +152,14 @@ func queryDNS(ctx context.Context, client *dns.Client, question dns.RR, opts ...
for _, opt := range opts {
opt(&msg.MsgHeader)
}
msg.Question = []dns.RR{question}
resp, _, err := client.Exchange(ctx, msg, "udp", defaultResolverAddr)
if err == nil && resp.Truncated {
resp, _, err = client.Exchange(ctx, msg, "tcp", defaultResolverAddr)
}
if err != nil {
return nil, err
}

View File

@@ -48,6 +48,7 @@ func CheckDNSSECTool() agent.Tool {
}
client := dns.NewClient()
answers, err := queryDNS(
ctx,
client,
@@ -66,8 +67,11 @@ func CheckDNSSECTool() agent.Tool {
}), nil
}
var keyCount int
var keyDetails []string
var (
keyCount int
keyDetails []string
)
for _, answer := range answers {
if key, ok := answer.(*dns.DNSKEY); ok {
keyCount++
@@ -76,6 +80,7 @@ func CheckDNSSECTool() agent.Tool {
if key.Flags&0x0001 != 0 {
flags = "KSK"
}
keyDetails = append(
keyDetails,
fmt.Sprintf("%s (algorithm=%d, flags=%d)", flags, key.Algorithm, key.Flags),

View File

@@ -52,6 +52,7 @@ type (
func checkHeader(h http.Header, name string) headerCheck {
v := h.Get(name)
return headerCheck{
Present: v != "",
Value: v,
@@ -92,6 +93,7 @@ func CheckSecurityHeadersTool() agent.Tool {
// First check the HTTP version to detect HTTP→HTTPS redirect.
redirectsToHTTPS := false
httpURL := p.URL
if after, ok := strings.CutPrefix(httpURL, "https://"); ok {
httpURL = "http://" + after
@@ -118,18 +120,21 @@ func CheckSecurityHeadersTool() agent.Tool {
}
followClient := &http.Client{Timeout: 10 * time.Second}
httpsReq, err := http.NewRequestWithContext(ctx, http.MethodGet, httpsURL, nil)
if err != nil {
return agent.ResultJSON(headersResult{
ErrorDetail: fmt.Sprintf("cannot create request for %s: %s", httpsURL, err),
}), nil
}
resp, err := followClient.Do(httpsReq)
if err != nil {
return agent.ResultJSON(headersResult{
ErrorDetail: fmt.Sprintf("cannot fetch %s: %s", httpsURL, err),
}), nil
}
defer func() { _ = resp.Body.Close() }()
result := headersFromResponse(resp)

View File

@@ -81,6 +81,7 @@ func CheckBreachesTool() agent.Tool {
ErrorDetail: fmt.Sprintf("cannot fetch breaches: %s", err),
}), nil
}
defer func() { _ = resp.Body.Close() }()
body, err := io.ReadAll(resp.Body)

View File

@@ -26,6 +26,7 @@ func resolverAddr() string {
if addr := os.Getenv("DNS_RESOLVER_ADDR"); addr != "" {
return addr
}
return "8.8.8.8:53"
}

View File

@@ -65,6 +65,7 @@ func CheckSPFTool() agent.Tool {
}
client := dns.NewClient()
answers, err := queryDNS(
ctx,
client,
@@ -83,6 +84,7 @@ func CheckSPFTool() agent.Tool {
}
var spfRecords []string
for _, answer := range answers {
txt, ok := answer.(*dns.TXT)
if !ok {
@@ -106,6 +108,7 @@ func CheckSPFTool() agent.Tool {
if len(spfRecords) == 1 {
record := spfRecords[0]
return agent.ResultJSON(spfResult{
Found: true,
RawRecord: record,

View File

@@ -89,16 +89,19 @@ func CheckSSLCertificateTool() agent.Tool {
},
}
netConn, err := dialer.DialContext(ctx, "tcp", p.Domain+":443")
var conn *tls.Conn
if netConn != nil {
conn = netConn.(*tls.Conn)
}
if err != nil {
return agent.ResultJSON(sslResult{
Valid: false,
ErrorDetail: err.Error(),
}), nil
}
defer func() { _ = conn.Close() }()
state := conn.ConnectionState()
@@ -124,6 +127,7 @@ func CheckSSLCertificateTool() agent.Tool {
for _, ic := range state.PeerCertificates[1:] {
opts.Intermediates.AddCert(ic)
}
if _, err := cert.Verify(opts); err != nil {
valid = false
}

View File

@@ -67,6 +67,7 @@ func CheckWhoisTool() agent.Tool {
if whoisServer == "" {
whoisServer = parseWhoisField(referral, "whois")
}
if whoisServer == "" {
// Try common TLD WHOIS servers as fallback.
parts := strings.Split(p.Domain, ".")
@@ -84,6 +85,7 @@ func CheckWhoisTool() agent.Tool {
if whoisHost == "" {
whoisHost = whoisServer
}
if err := netcheck.ValidatePublicDomain(whoisHost); err != nil {
return agent.ResultJSON(whoisResult{
ErrorDetail: fmt.Sprintf("WHOIS referral server not allowed: %s", err),
@@ -114,6 +116,7 @@ func CheckWhoisTool() agent.Tool {
years := int(age.Hours() / 24 / 365)
months := int(age.Hours()/24/30) % 12
result.DomainAge = fmt.Sprintf("%d years, %d months", years, months)
break
}
}
@@ -126,10 +129,12 @@ func CheckWhoisTool() agent.Tool {
func queryWhois(ctx context.Context, server, domain string) (string, error) {
dialer := net.Dialer{Timeout: 10 * time.Second}
conn, err := dialer.DialContext(ctx, "tcp", server)
if err != nil {
return "", fmt.Errorf("cannot connect to %s: %w", server, err)
}
defer func() { _ = conn.Close() }()
_ = conn.SetDeadline(time.Now().Add(10 * time.Second))
@@ -140,11 +145,13 @@ func queryWhois(ctx context.Context, server, domain string) (string, error) {
}
var sb strings.Builder
scanner := bufio.NewScanner(conn)
for scanner.Scan() {
sb.WriteString(scanner.Text())
sb.WriteString("\n")
}
if err := scanner.Err(); err != nil {
return "", fmt.Errorf("cannot read from %s: %w", server, err)
}
@@ -154,19 +161,23 @@ func queryWhois(ctx context.Context, server, domain string) (string, error) {
func parseWhoisField(raw, field string) string {
field = strings.ToLower(field)
for line := range strings.SplitSeq(raw, "\n") {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "%") || strings.HasPrefix(line, "#") {
continue
}
k, v, ok := strings.Cut(line, ":")
if !ok {
continue
}
if strings.ToLower(strings.TrimSpace(k)) == field {
return strings.TrimSpace(v)
}
}
return ""
}
@@ -199,16 +210,20 @@ var (
func parseWhoisResponse(raw string) whoisResult {
var result whoisResult
for line := range strings.SplitSeq(raw, "\n") {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "%") || strings.HasPrefix(line, "#") {
continue
}
k, v, ok := strings.Cut(line, ":")
if !ok {
continue
}
key := strings.ToLower(strings.TrimSpace(k))
val := strings.TrimSpace(v)
if val == "" {
continue