caption-download ToolThe caption-download MCP tool in yutu passes the caller-supplied file parameter directly to os.Create() at pkg/caption/caption.go:272 without any path validation, canonicalization, or confinement to the pkg.Root boundary (YUTU_ROOT). A local attacker — or any process able to reach the HTTP MCP server — can write arbitrary content to any path writable by the yutu process, entirely outside the intended working directory. This is a High severity vulnerability (CVSS 7.7) with high integrity and availability impact.
yutu uses pkg.Root (backed by Go 1.24's os.OpenRoot) to restrict all file I/O to the YUTU_ROOT directory. Every other caption file-write path honours this boundary:
| Method | Sink | Confined? |
|--------|------|-----------|
| Caption.Insert() | pkg.Root.Open(c.File) (caption.go:109) | Yes |
| Caption.Update() | pkg.Root.Open(c.File) (caption.go:193) | Yes |
| Caption.Download() | os.Create(c.File) (caption.go:272) | No |
Caption.Download() is the sole outlier. The attacker-controlled file field flows without restriction from the MCP tool input schema to a raw os.Create() call:
cmd/caption/download.go:32–41: downloadInSchema declares file as a required string field in the MCP JSON input schema.cmd/caption/download.go:61–64: cobramcp.GenToolHandler maps MCP input to input.Download(writer).pkg/caption/caption.go:272: os.Create(c.File) creates or truncates the file at the attacker-supplied path.pkg/caption/caption.go:280: file.Write(body) writes the downloaded caption bytes to that path.// cmd/caption/download.go
var downloadInSchema = &jsonschema.Schema{
Required: []string{"ids", "file"}, // line 34
// ...
"file": {Type: "string", Description: fileUsage}, // line 40
}
// cobramcp.GenToolHandler binds MCP →...
0.10.9-dev1Exploitability
AV:LAC:LPR:NUI:NScope
S:UImpact
C:NI:HA:H7.7/CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:HInput Validation