Files
kubeshark/cmd/createNoFollow_unix.go
T
Volodymyr StoikoandGitHub a76a1920dc mcp: confine download_file destination and harden start_kubeshark argument handling (#1957)
* mcp: confine download_file dest and fix start_kubeshark arg injection

download_file wrote fetched bytes to a caller-supplied dest with no
validation, and appended the caller-supplied pod_regex to 'kubeshark tap'
with no end-of-options separator. Both are reachable via induced-agent
(prompt-injection) tool arguments.

- download_file: resolve dest through secureDownloadDest, confined to a base
  directory (CWD by default, relocatable via KUBESHARK_MCP_DOWNLOAD_DIR);
  reject dest that escapes the base (../ or absolute) and '..' in the Hub
  path (CWE-22).
- start_kubeshark: append pod_regex last, after a '--' separator, so it is
  always the [POD REGEX] positional and never parsed as a --set flag (CWE-88).
- tests: set downloadDir in the download tests; add a traversal-rejection test.

Reported by novice-22 via coordinated disclosure.

* mcp: reject download destinations that escape the base via symlink

Containment in secureDownloadDest was lexical only, so a symlinked
subdirectory inside the download dir (or a dest that is itself a symlink)
could still redirect the write outside it. Resolve symlinks on the
deepest existing ancestor and re-check containment, and open the file
with O_NOFOLLOW where available to narrow the TOCTOU window.
2026-08-12 13:00:04 -07:00

16 lines
435 B
Go

//go:build !windows
package cmd
import (
"os"
"syscall"
)
// createNoFollow creates or truncates name for writing and fails if the final
// path component is a symlink. Used for MCP download destinations so a symlink
// cannot redirect the write outside the confined download directory.
func createNoFollow(name string) (*os.File, error) {
return os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC|syscall.O_NOFOLLOW, 0o644)
}