Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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: 0 additions & 2 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ name: Docker
# documentation.

on:
schedule:
- cron: "27 0 * * *"
push:
branches: ["main"]
# Publish semver tags as releases.
Expand Down
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ func toolDomainResourceAction(client *v1client.V1ApiClient) mcpserver.ServerTool
- Server validates annotations match toolset registration (panics on mismatch)
- Add read tools to `ToolsetsReadOnly{Domain}`, write tools to `ToolsetsWrite{Domain}` in respective `tools/*.go` files
- Register toolsets in `server.go`
- **REQUIRED:** Update `README.md` Tools section with new tools (include tool name, description, and mode)

### API Paths
Paths must NOT start with `/`. The client's `Parse` method handles URL joining:
Expand Down Expand Up @@ -102,6 +103,7 @@ Used by domains requiring custom headers (e.g., AI Security uses `TMV1-Applicati
| Email | `v1client/email.go` | `tools/email.go` | `v3.0/email/` |
| Container | `v1client/container.go` | `tools/container.go` | `v3.0/containerSecurity/` |
| Endpoint | `v1client/endpoint.go` | `tools/endpoint.go` | `v3.0/endpointSecurity/` |
| Threat Intel | `v1client/threatintel.go` | `tools/threatintel.go` | `v3.0/threatintel/` |

**Note:** OAT (Observed Attack Techniques) has its own client file but tools are registered under the Workbench toolset.

Expand Down
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,26 @@ Alternatively, copy the following into your `settings.json`.
| ---- | ----------- | ---- |
| `aisecurity_guardrails_apply` | Evaluates prompts against AI guard policies and returns the recommended action (Allow/Block) with reasons for any policy violations detected | `read` |

### Threat Intelligence

| Tool | Description | Mode |
| ---- | ----------- | ---- |
| `threatintel_suspicious_objects_list` | Retrieves information about domains, file SHA-1, file SHA-256, IP addresses, email addresses, or URLs in the Suspicious Object List | `read` |
| `threatintel_suspicious_objects_add` | Adds information about domains, file SHA-1, file SHA-256, IP addresses, email addresses, or URLs to the Suspicious Object List | `write` |
| `threatintel_suspicious_objects_delete` | Deletes information about domains, file SHA-1, file SHA-256, IP addresses, email addresses, or URLs from the Suspicious Object List | `write` |
| `threatintel_exceptions_list` | Retrieves information about domains, file SHA-1, file SHA-256, IP addresses, sender addresses, or URLs in the Exception List | `read` |
| `threatintel_exceptions_add` | Adds domains, file SHA-1, file SHA-256, IP addresses, sender addresses, or URLs to the Exception List | `write` |
| `threatintel_exceptions_delete` | Deletes the specified objects from the Exception List | `write` |
| `threatintel_intelligence_reports_list` | Retrieves a list of custom intelligence reports created from imported or retrieved data | `read` |
| `threatintel_intelligence_report_get` | Downloads a custom intelligence report as a STIX Bundle | `read` |
| `threatintel_intelligence_reports_delete` | Deletes the specified custom intelligence reports | `write` |
| `threatintel_sweep_trigger` | Searches your environment for threat indicators specified in a custom intelligence report | `write` |
| `threatintel_tasks_list` | Displays information about threat intelligence tasks and asynchronous jobs | `read` |
| `threatintel_task_results_get` | Retrieves the results of a threat intelligence task | `read` |
| `threatintel_feed_indicators_list` | Retrieves a list of IoCs from Trend Threat Intelligence Feed | `read` |
| `threatintel_feeds_list` | Retrieves a list of intelligence reports from the Trend Threat Intelligence Feed with associated objects and relationships | `read` |
| `threatintel_feed_filter_definition_get` | Retrieves supported filter keys and values for Trend Threat Intelligence Feed queries | `read` |

## Architecture

![high-level architecture](./doc/images/trend-vision-one-mcp.png)
Expand Down
254 changes: 254 additions & 0 deletions internal/v1client/threatintel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
package v1client

import (
"bytes"
"encoding/json"
"fmt"
"net/http"

"github.com/google/go-querystring/query"
)

// ThreatIntelQueryParameters contains query parameters specific to threat intel APIs
type ThreatIntelQueryParameters struct {
OrderBy string `url:"orderBy,omitempty"`
Top int `url:"top,omitempty"`
SkipToken string `url:"skipToken,omitempty"`
StartDateTime string `url:"startDateTime,omitempty"`
EndDateTime string `url:"endDateTime,omitempty"`
Filter string `url:"filter,omitempty"`
}

// ThreatIntelFeedParameters contains query parameters for threat intelligence feed APIs
type ThreatIntelFeedParameters struct {
StartDateTime string `url:"startDateTime,omitempty"`
EndDateTime string `url:"endDateTime,omitempty"`
Top int `url:"top,omitempty"`
TopReport int `url:"topReport,omitempty"`
IndicatorObjectFormat string `url:"indicatorObjectFormat,omitempty"`
ResponseObjectFormat string `url:"responseObjectFormat,omitempty"`
}

// SuspiciousObject represents an object to add to the suspicious object list
type SuspiciousObject struct {
URL string `json:"url,omitempty"`
Domain string `json:"domain,omitempty"`
IP string `json:"ip,omitempty"`
SenderMailAddress string `json:"senderMailAddress,omitempty"`
FileSha1 string `json:"fileSha1,omitempty"`
FileSha256 string `json:"fileSha256,omitempty"`
Description string `json:"description,omitempty"`
ScanAction string `json:"scanAction,omitempty"`
RiskLevel string `json:"riskLevel,omitempty"`
DaysToExpiration int `json:"daysToExpiration,omitempty"`
}

// SuspiciousObjectException represents an object to add to the exception list
type SuspiciousObjectException struct {
URL string `json:"url,omitempty"`
Domain string `json:"domain,omitempty"`
IP string `json:"ip,omitempty"`
SenderMailAddress string `json:"senderMailAddress,omitempty"`
FileSha1 string `json:"fileSha1,omitempty"`
FileSha256 string `json:"fileSha256,omitempty"`
Description string `json:"description,omitempty"`
}

// SuspiciousObjectDelete represents an object to delete from the suspicious object list
type SuspiciousObjectDelete struct {
URL string `json:"url,omitempty"`
Domain string `json:"domain,omitempty"`
IP string `json:"ip,omitempty"`
SenderMailAddress string `json:"senderMailAddress,omitempty"`
FileSha1 string `json:"fileSha1,omitempty"`
FileSha256 string `json:"fileSha256,omitempty"`
}

// IntelligenceReportDelete represents a report to delete
type IntelligenceReportDelete struct {
ID string `json:"id"`
}

// IntelligenceReportSweep represents a sweep request
type IntelligenceReportSweep struct {
ID string `json:"id"`
SweepType string `json:"sweepType"`
Description string `json:"description,omitempty"`
}

// ThreatIntelListSuspiciousObjects retrieves suspicious objects from the list
func (c *V1ApiClient) ThreatIntelListSuspiciousObjects(filter string, queryParams ThreatIntelQueryParameters) (*http.Response, error) {
return c.searchAndFilter("v3.0/threatintel/suspiciousObjects", filter, queryParams)
}

// ThreatIntelAddSuspiciousObjects adds objects to the suspicious object list
func (c *V1ApiClient) ThreatIntelAddSuspiciousObjects(objects []SuspiciousObject) (*http.Response, error) {
b, err := json.Marshal(&objects)
if err != nil {
return nil, err
}

r, err := c.newRequest(
http.MethodPost,
"v3.0/threatintel/suspiciousObjects",
bytes.NewReader(b),
withContentTypeJSON(),
)
if err != nil {
return nil, err
}
return c.client.Do(r)
}

// ThreatIntelDeleteSuspiciousObjects removes objects from the suspicious object list
func (c *V1ApiClient) ThreatIntelDeleteSuspiciousObjects(objects []SuspiciousObjectDelete) (*http.Response, error) {
b, err := json.Marshal(&objects)
if err != nil {
return nil, err
}

r, err := c.newRequest(
http.MethodPost,
"v3.0/threatintel/suspiciousObjects/delete",
bytes.NewReader(b),
withContentTypeJSON(),
)
if err != nil {
return nil, err
}
return c.client.Do(r)
}

// ThreatIntelListExceptions retrieves the exception list
func (c *V1ApiClient) ThreatIntelListExceptions(filter string, queryParams ThreatIntelQueryParameters) (*http.Response, error) {
return c.searchAndFilter("v3.0/threatintel/suspiciousObjectExceptions", filter, queryParams)
}

// ThreatIntelAddExceptions adds objects to the exception list
func (c *V1ApiClient) ThreatIntelAddExceptions(objects []SuspiciousObjectException) (*http.Response, error) {
b, err := json.Marshal(&objects)
if err != nil {
return nil, err
}

r, err := c.newRequest(
http.MethodPost,
"v3.0/threatintel/suspiciousObjectExceptions",
bytes.NewReader(b),
withContentTypeJSON(),
)
if err != nil {
return nil, err
}
return c.client.Do(r)
}

// ThreatIntelDeleteExceptions removes objects from the exception list
func (c *V1ApiClient) ThreatIntelDeleteExceptions(objects []SuspiciousObjectDelete) (*http.Response, error) {
b, err := json.Marshal(&objects)
if err != nil {
return nil, err
}

r, err := c.newRequest(
http.MethodPost,
"v3.0/threatintel/suspiciousObjectExceptions/delete",
bytes.NewReader(b),
withContentTypeJSON(),
)
if err != nil {
return nil, err
}
return c.client.Do(r)
}

// ThreatIntelListIntelligenceReports retrieves custom intelligence reports
func (c *V1ApiClient) ThreatIntelListIntelligenceReports(queryParams ThreatIntelQueryParameters) (*http.Response, error) {
return c.searchAndFilter("v3.0/threatintel/intelligenceReports", "", queryParams)
}

// ThreatIntelGetIntelligenceReport downloads a custom intelligence report as a STIX Bundle
func (c *V1ApiClient) ThreatIntelGetIntelligenceReport(reportId string) (*http.Response, error) {
return c.genericGet(fmt.Sprintf("v3.0/threatintel/intelligenceReports/%s", reportId))
}

// ThreatIntelDeleteIntelligenceReports deletes custom intelligence reports
func (c *V1ApiClient) ThreatIntelDeleteIntelligenceReports(reportIds []string) (*http.Response, error) {
deleteBody := []IntelligenceReportDelete{}
for _, id := range reportIds {
deleteBody = append(deleteBody, IntelligenceReportDelete{ID: id})
}
b, err := json.Marshal(&deleteBody)
if err != nil {
return nil, err
}

r, err := c.newRequest(
http.MethodPost,
"v3.0/threatintel/intelligenceReports/delete",
bytes.NewReader(b),
withContentTypeJSON(),
)
if err != nil {
return nil, err
}
return c.client.Do(r)
}

// ThreatIntelTriggerSweep triggers a sweeping task for intelligence reports
func (c *V1ApiClient) ThreatIntelTriggerSweep(sweeps []IntelligenceReportSweep) (*http.Response, error) {
b, err := json.Marshal(&sweeps)
if err != nil {
return nil, err
}

r, err := c.newRequest(
http.MethodPost,
"v3.0/threatintel/intelligenceReports/sweep",
bytes.NewReader(b),
withContentTypeJSON(),
)
if err != nil {
return nil, err
}
return c.client.Do(r)
}

// ThreatIntelListTasks retrieves sweeping tasks
func (c *V1ApiClient) ThreatIntelListTasks(queryParams ThreatIntelQueryParameters) (*http.Response, error) {
return c.searchAndFilter("v3.0/threatintel/tasks", "", queryParams)
}

// ThreatIntelGetTaskResults retrieves the results of a task
func (c *V1ApiClient) ThreatIntelGetTaskResults(taskId string) (*http.Response, error) {
return c.genericGet(fmt.Sprintf("v3.0/threatintel/tasks/%s", taskId))
}

// ThreatIntelListFeedIndicators retrieves IoCs from Trend Threat Intelligence Feed
func (c *V1ApiClient) ThreatIntelListFeedIndicators(queryParams ThreatIntelFeedParameters) (*http.Response, error) {
return c.searchAndFilter("v3.0/threatintel/feedIndicators", "", queryParams)
}

// ThreatIntelListFeeds retrieves intelligence objects from Trend Threat Intelligence Feed
func (c *V1ApiClient) ThreatIntelListFeeds(contextualFilter string, queryParams ThreatIntelFeedParameters) (*http.Response, error) {
p, err := query.Values(queryParams)
if err != nil {
return nil, err
}
r, err := c.newRequest(
http.MethodGet,
"v3.0/threatintel/feeds",
http.NoBody,
withHeader("TMV1-Contextual-Filter", contextualFilter),
withUrlParameters(p),
)
if err != nil {
return nil, err
}
return c.client.Do(r)
}

// ThreatIntelGetFeedFilterDefinition retrieves supported filter keys and values for feed queries
func (c *V1ApiClient) ThreatIntelGetFeedFilterDefinition() (*http.Response, error) {
return c.genericGet("v3.0/threatintel/feeds/filterDefinition")
}
2 changes: 2 additions & 0 deletions internal/v1mcp/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,12 @@ func NewMcpServer(cfg ServerConfig) (*mcpserver.MCPServer, error) {
addReadOnlyToolset(s, client, tools.ToolsetsReadOnlyContainer)
addReadOnlyToolset(s, client, tools.ToolsetsReadOnlyEndpoint)
addReadOnlyToolset(s, client, tools.ToolsetsReadOnlyAISecurity)
addReadOnlyToolset(s, client, tools.ToolsetsReadOnlyThreatIntel)

if !cfg.ReadOnly {
addWriteToolset(s, client, tools.ToolsetsWriteCloudPosture)
addWriteToolset(s, client, tools.ToolsetsWriteIAM)
addWriteToolset(s, client, tools.ToolsetsWriteThreatIntel)
}

return s, nil
Expand Down
Loading
Loading