Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/gosec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
# gosec emits null entries in rules[].relationships which violates the SARIF schema.
# Remove null entries so that upload-sarif accepts the file.
run: |
jq '(.runs[].tool.driver.rules // [])[] |= (if .relationships then .relationships |= map(select(. != null)) else . end)' results.sarif > results-fixed.sarif
jq '(.runs[]?.tool.driver.rules[]?) |= (if .relationships then .relationships |= map(select(. != null)) else . end)' results.sarif > results-fixed.sarif
mv results-fixed.sarif results.sarif

- name: Upload SARIF file
Expand Down
21 changes: 15 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/secmon-lab/warren

go 1.26.0
go 1.26.4

require (
cloud.google.com/go/bigquery v1.74.0
Expand All @@ -13,26 +13,35 @@ require (
github.com/getsentry/sentry-go v0.44.1
github.com/go-chi/chi/v5 v5.2.5
github.com/gollem-dev/gollem v0.26.0
github.com/gollem-dev/tools/abusech v0.1.0
github.com/gollem-dev/tools/bigquery v0.1.0
github.com/gollem-dev/tools/github v0.1.0
github.com/gollem-dev/tools/intune v0.1.0
github.com/gollem-dev/tools/ipdb v0.1.0
github.com/gollem-dev/tools/otx v0.1.0
github.com/gollem-dev/tools/shodan v0.1.0
github.com/gollem-dev/tools/slack v0.1.0
github.com/gollem-dev/tools/urlscan v0.1.0
github.com/gollem-dev/tools/vt v0.1.0
github.com/gollem-dev/tools/webfetch v0.1.0
github.com/gollem-dev/tools/whois v0.1.0
github.com/google/go-github/v74 v74.0.0
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.3
github.com/graph-gophers/dataloader/v7 v7.1.3
github.com/lestrrat-go/jwx/v2 v2.1.6
github.com/likexian/whois v1.15.7
github.com/m-mizutani/clog v0.2.1
github.com/m-mizutani/fireconf v0.3.0
github.com/m-mizutani/goerr/v2 v2.0.1
github.com/m-mizutani/gt v0.2.1
github.com/m-mizutani/harlog v0.0.3
github.com/m-mizutani/masq v0.2.1
github.com/m-mizutani/opaq v0.3.0
github.com/migueleliasweb/go-github-mock v1.4.0
github.com/newmo-oss/go-caller v0.1.0
github.com/slack-go/slack v0.23.1
github.com/stretchr/testify v1.11.1
github.com/urfave/cli/v3 v3.8.0
github.com/vektah/gqlparser/v2 v2.5.32
golang.org/x/net v0.54.0
google.golang.org/api v0.275.0
google.golang.org/genai v1.53.0
google.golang.org/grpc v1.80.0
Expand Down Expand Up @@ -114,14 +123,12 @@ require (
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/google/flatbuffers v25.12.19+incompatible // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-github/v73 v73.0.0 // indirect
github.com/google/go-github/v84 v84.0.0 // indirect
github.com/google/go-querystring v1.2.0 // indirect
github.com/google/jsonschema-go v0.4.2 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect
github.com/googleapis/gax-go/v2 v2.21.0 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.72 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-getter v1.8.6 // indirect
Expand All @@ -142,6 +149,7 @@ require (
github.com/lestrrat-go/jwx/v3 v3.0.13 // indirect
github.com/lestrrat-go/option v1.0.1 // indirect
github.com/lestrrat-go/option/v2 v2.0.0 // indirect
github.com/likexian/whois v1.15.7 // indirect
github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
github.com/m-mizutani/jsonex v0.0.1 // indirect
github.com/matryer/moq v0.6.0 // indirect
Expand Down Expand Up @@ -200,6 +208,7 @@ require (
golang.org/x/crypto v0.51.0 // indirect
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect
golang.org/x/mod v0.35.0 // indirect
golang.org/x/net v0.54.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.44.0 // indirect
Expand Down
30 changes: 24 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,35 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/gollem-dev/gollem v0.26.0 h1:FIGZq1un1AWCXIU00VXdtJ3SPo5C2pwWj8jOc5z/yyY=
github.com/gollem-dev/gollem v0.26.0/go.mod h1:qEZEgRi5g/tGoNQyLgq8uWfM7mhzCMkjdP2IZLSGSxM=
github.com/gollem-dev/tools/abusech v0.1.0 h1:0EvwiABq6585q7sarWYkpNRyDvMIgIAYxAAT6wIQZt0=
github.com/gollem-dev/tools/abusech v0.1.0/go.mod h1:4xn+UrOECXLWiBTPSgZoOOVrsqo4P2wZ0u/umtbsRFc=
github.com/gollem-dev/tools/bigquery v0.1.0 h1:TL3v6IVrozstX/LbFsID4NFiP/I/OUDXojYzv+H2by8=
github.com/gollem-dev/tools/bigquery v0.1.0/go.mod h1:H+sKbHbwEpCP1+FCaToZ5ftiOpMMO1Wbi2DaL3E7AqE=
github.com/gollem-dev/tools/github v0.1.0 h1:HK3CKjYrC62gqhw7v+Oi39ai3yxWbkEqjHlN7hD42X4=
github.com/gollem-dev/tools/github v0.1.0/go.mod h1:LHt/fbIzFTU+eU2U5uXSgTTu6Yoco2Lkzc9DRMaT/zI=
github.com/gollem-dev/tools/intune v0.1.0 h1:Qe41jsuRDvQrXsSmk91q15hBCT68bhNacupKIeU2vhg=
github.com/gollem-dev/tools/intune v0.1.0/go.mod h1:WdgquLJMs6tPFhzISArygZ+AGr2KvpOPulhtp1gp+oY=
github.com/gollem-dev/tools/ipdb v0.1.0 h1:Df1j1PwcIQvVFvqI6LP09EtRqHAj10pLzPnQk8EKfoU=
github.com/gollem-dev/tools/ipdb v0.1.0/go.mod h1:Zz0w9yXKpBcimB+ut4I3jlRpXWV+Qs6QQqb2G6MnpuE=
github.com/gollem-dev/tools/otx v0.1.0 h1:+mKhHd+gikhh3dvNSmyvbSaiACIIMaZe1UoXwzlk2+8=
github.com/gollem-dev/tools/otx v0.1.0/go.mod h1:8cuXN15Bq/ILXN4CPACpCFIabe52pz3O76Z1iePHQEk=
github.com/gollem-dev/tools/shodan v0.1.0 h1:eWmb426Kg1koYlHCI11sJ6BnZUxcUKbdd0lEoWdKr4k=
github.com/gollem-dev/tools/shodan v0.1.0/go.mod h1:AvuHaiYDPmJMJiQOK7fClkJ00pMH+RscS6VjU38/zos=
github.com/gollem-dev/tools/slack v0.1.0 h1:8dmzH5c2jM1WZvwXeLGdvWE/TBQNkp6sWfM31byWwhQ=
github.com/gollem-dev/tools/slack v0.1.0/go.mod h1:g415ENkXWQRDToVCviuSHQ2BMiz0omrIQsjbmQwOahM=
github.com/gollem-dev/tools/urlscan v0.1.0 h1:AxMMuy9bsXFUSCWXaIWBesgKAIVYEGl2/Cjt3BvnTgg=
github.com/gollem-dev/tools/urlscan v0.1.0/go.mod h1:cZ7bUqclkpFCL2Z5pFKLWd6KWTWzf+iy69NvnFoewIo=
github.com/gollem-dev/tools/vt v0.1.0 h1:X2T0E1HT5aoU4BK6B/ZITwRvOLMiq6ttLAgHdVfPeEc=
github.com/gollem-dev/tools/vt v0.1.0/go.mod h1:iV1dpMTWMMQSZp7+dbUVzgb4Rd6eIJHLSxPEa8W8mKo=
github.com/gollem-dev/tools/webfetch v0.1.0 h1:jDcY+oQGH6zoKL/tBSzzgin0R9t60UY6xn3jwFMj/9A=
github.com/gollem-dev/tools/webfetch v0.1.0/go.mod h1:zTCZBmDrF3gFJhzES/gaL+pnYHa6BIAglc8E0GR8rrY=
github.com/gollem-dev/tools/whois v0.1.0 h1:YePfmuLx44mHFaGi7IOGKATKme81AE+dHqisdALGrfU=
github.com/gollem-dev/tools/whois v0.1.0/go.mod h1:U/iXnDoKtJmWZA7ma9bcrOVV4W2oaLwyuRP+vd+6AKY=
github.com/google/flatbuffers v25.12.19+incompatible h1:haMV2JRRJCe1998HeW/p0X9UaMTK6SDo0ffLn2+DbLs=
github.com/google/flatbuffers v25.12.19+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-github/v73 v73.0.0 h1:aR+Utnh+Y4mMkS+2qLQwcQ/cF9mOTpdwnzlaw//rG24=
github.com/google/go-github/v73 v73.0.0/go.mod h1:fa6w8+/V+edSU0muqdhCVY7Beh1M8F1IlQPZIANKIYw=
github.com/google/go-github/v74 v74.0.0 h1:yZcddTUn8DPbj11GxnMrNiAnXH14gNs559AsUpNpPgM=
github.com/google/go-github/v74 v74.0.0/go.mod h1:ubn/YdyftV80VPSI26nSJvaEsTOnsjrxG3o9kJhcyak=
github.com/google/go-github/v84 v84.0.0 h1:I/0Xn5IuChMe8TdmI2bbim5nyhaRFJ7DEdzmD2w+yVA=
Expand All @@ -245,8 +267,6 @@ github.com/googleapis/enterprise-certificate-proxy v0.3.14 h1:yh8ncqsbUY4shRD5dA
github.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg=
github.com/googleapis/gax-go/v2 v2.21.0 h1:h45NjjzEO3faG9Lg/cFrBh2PgegVVgzqKzuZl/wMbiI=
github.com/googleapis/gax-go/v2 v2.21.0/go.mod h1:But/NJU6TnZsrLai/xBAQLLz+Hc7fHZJt/hsCz3Fih4=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/graph-gophers/dataloader/v7 v7.1.3 h1:mXCI1E3dBG0aG1Tzg1tXaz+nN140opFIgEfYhxHR0XA=
Expand Down Expand Up @@ -331,8 +351,6 @@ github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3Ry
github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
github.com/migueleliasweb/go-github-mock v1.4.0 h1:pQ6K8r348m2q79A8Khb0PbEeNQV7t3h1xgECV+jNpXk=
github.com/migueleliasweb/go-github-mock v1.4.0/go.mod h1:/DUmhXkxrgVlDOVBqGoUXkV4w0ms5n1jDQHotYm135o=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
Expand Down
150 changes: 28 additions & 122 deletions pkg/tool/abusech/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,25 @@ package abusech

import (
"context"
"encoding/json"
"io"
"log/slog"
"net/http"
"net/url"
"strings"

"github.com/gollem-dev/gollem"
extabusech "github.com/gollem-dev/tools/abusech"
"github.com/m-mizutani/goerr/v2"
"github.com/secmon-lab/warren/pkg/domain/interfaces"
"github.com/secmon-lab/warren/pkg/utils/errutil"
"github.com/secmon-lab/warren/pkg/utils/safe"
"github.com/urfave/cli/v3"
)

// Action is the warren-side wrapper around github.com/gollem-dev/tools/abusech.
// It implements interfaces.Tool, binding CLI flags and warren-specific planner
// metadata onto the external gollem.ToolSet that carries the Specs/Run logic.
type Action struct {
apiKey string
baseURL string

opts []extabusech.Option
inner gollem.ToolSet
}

var _ interfaces.Tool = &Action{}
Expand Down Expand Up @@ -52,128 +53,39 @@ func (x *Action) Flags() []cli.Flag {
Category: "Tool",
Value: "https://mb-api.abuse.ch/api/v1",
Sources: cli.EnvVars("WARREN_ABUSECH_BASE_URL"),
},
}
}

func (x *Action) Specs(ctx context.Context) ([]gollem.ToolSpec, error) {
return []gollem.ToolSpec{
{
Name: "abusech.bazaar.query",
Description: "Query malware information from MalwareBazaar by file hash value.",
Parameters: map[string]*gollem.Parameter{
"hash": {
Type: gollem.TypeString,
Description: "The hash value (MD5, SHA1, or SHA256) to query",
},
Action: func(_ context.Context, _ *cli.Command, v string) error {
x.opts = append(x.opts, extabusech.WithBaseURL(v))
return nil
},
},
}, nil
}

func (x *Action) Run(ctx context.Context, name string, args map[string]any) (map[string]any, error) {
if x.apiKey == "" {
return nil, goerr.New("abuse.ch API key is required")
}

client := &http.Client{}
var hash string

switch name {
case "abusech.bazaar.query":
var ok bool
hash, ok = args["hash"].(string)
if !ok {
return nil, goerr.New("hash parameter is required")
}
default:
return nil, goerr.New("invalid function name", goerr.V("name", name))
}

// Build form data
formData := url.Values{}
formData.Set("query", "get_info")
formData.Set("hash", hash)

// Create request with form data
req, err := http.NewRequestWithContext(ctx, http.MethodPost, x.baseURL+"/", strings.NewReader(formData.Encode()))
if err != nil {
return nil, goerr.Wrap(err, "failed to create request")
}

// Set headers
req.Header.Set("Auth-Key", x.apiKey)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Accept", "*/*")
req.Header.Set("Accept-Encoding", "identity")
req.Header.Set("Connection", "Keep-Alive")

// Send request
eb := goerr.NewBuilder(
goerr.V("request_url", x.baseURL),
goerr.V("request_method", req.Method),
goerr.V("request_body", formData.Encode()),
)

resp, err := client.Do(req)
if err != nil {
return nil, eb.Wrap(err, "failed to send request")
}
defer safe.Close(ctx, resp.Body)

body, err := io.ReadAll(resp.Body)
eb = eb.With(
goerr.V("response_status_code", resp.StatusCode),
goerr.V("response_body", string(body)),
)
if err != nil {
return nil, eb.Wrap(err, "failed to read response body")
}

if resp.StatusCode != http.StatusOK {
return nil, eb.Wrap(err, "unexpected response code from MalwareBazaar")
}

var result map[string]any
if err := json.Unmarshal(body, &result); err != nil {
return nil, eb.Wrap(err, "failed to unmarshal response")
}

// Check for API error response
if status, ok := result["query_status"].(string); ok && status == "error" {
errMsg, _ := result["error_message"].(string)
if errMsg == "" {
errMsg, _ = result["error"].(string)
}
return nil, eb.Wrap(err, "MalwareBazaar API returned error",
goerr.V("error", errMsg))
}

// Validate response format
if _, ok := result["data"].([]interface{}); !ok {
return nil, eb.Wrap(err, "invalid response format: missing data array")
}

return result, nil
}

func (x *Action) Configure(ctx context.Context) error {
func (x *Action) Configure(_ context.Context) error {
if x.apiKey == "" {
return errutil.ErrActionUnavailable
}

parsedURL, err := url.Parse(x.baseURL)
ts, err := extabusech.New(x.apiKey, x.opts...)
if err != nil {
return goerr.Wrap(err, "invalid base URL", goerr.V("base_url", x.baseURL))
return goerr.Wrap(err, "failed to configure abusech tool")
}
x.inner = ts
return nil
}

if !strings.HasPrefix(parsedURL.Scheme, "http") {
return goerr.New("invalid base URL scheme",
goerr.V("base_url", x.baseURL),
goerr.V("scheme", parsedURL.Scheme))
func (x *Action) Specs(ctx context.Context) ([]gollem.ToolSpec, error) {
if x.inner == nil {
return nil, goerr.New("abusech tool is not configured")
}
return x.inner.Specs(ctx)
}

return nil
func (x *Action) Run(ctx context.Context, name string, args map[string]any) (map[string]any, error) {
if x.inner == nil {
return nil, goerr.New("abusech tool is not configured")
}
return x.inner.Run(ctx, name, args)
}

func (x *Action) LogValue() slog.Value {
Expand All @@ -183,13 +95,7 @@ func (x *Action) LogValue() slog.Value {
)
}

// Prompt returns additional instructions for the system prompt
func (x *Action) Prompt(ctx context.Context) (string, error) {
// Prompt returns additional instructions for the system prompt.
func (x *Action) Prompt(_ context.Context) (string, error) {
return "", nil
}

func New() *Action {
return &Action{
baseURL: "https://mb-api.abuse.ch/api/v1",
}
}
Loading
Loading