Files
karti 35a0b2b715 feat: initial release with 48 Mattermost MCP tools
- 27 base tools: channels, messaging, users, reactions, files, DMs
- Team management: invite/remove users, get stats, list members
- Slash commands: execute /remind, /poll, etc.
- Webhook management: incoming and outgoing webhooks
- System tools: server config, logs, bulk status updates
- Channel admin: create, invite, leave, delete channels
- Read-only mode for safe exploration
- Dual token support (bot + PAT) for enhanced security
- Apache 2.0 licensed
2026-04-15 21:26:19 -07:00

151 lines
3.7 KiB
Go

package file
import (
"fmt"
"net/http"
"os"
"path/filepath"
"strings"
)
const MaxFileSize = 50 * 1024 * 1024
var AllowedMimeTypes = []string{
"image/jpeg",
"image/png",
"image/gif",
"image/webp",
"application/pdf",
"text/plain",
"text/markdown",
"application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
}
var DangerousExtensions = []string{
".exe", ".sh", ".bat", ".cmd", ".com", ".scr", ".pif", ".vbs",
".js", ".jse", ".wsf", ".wsh", ".ps1", ".ps2", ".msc",
".dll", ".so", ".dylib", ".bin",
}
func ValidatePath(base, target string) (string, error) {
cleanTarget := filepath.Clean(target)
if filepath.IsAbs(cleanTarget) {
return "", fmt.Errorf("absolute paths not allowed: %s", target)
}
fullPath := filepath.Join(base, cleanTarget)
realBase, err := filepath.Abs(base)
if err != nil {
return "", fmt.Errorf("failed to get absolute path for base: %w", err)
}
realTarget, err := filepath.Abs(fullPath)
if err != nil {
return "", fmt.Errorf("failed to get absolute path for target: %w", err)
}
if !filepath.HasPrefix(realTarget, realBase) {
return "", fmt.Errorf("path traversal detected: %s", target)
}
return realTarget, nil
}
func Exists(path string) bool {
_, err := os.Stat(path)
return !os.IsNotExist(err)
}
func IsValidPath(path string) bool {
cleanPath := filepath.Clean(path)
if strings.Contains(cleanPath, "..") {
return false
}
return true
}
func SanitizePath(path string) (string, error) {
if !IsValidPath(path) {
return "", fmt.Errorf("invalid path: path traversal detected: %s", path)
}
return filepath.Clean(path), nil
}
func ValidateFileSize(size int64) error {
if size > MaxFileSize {
return fmt.Errorf("file size %d bytes exceeds maximum allowed size of %d bytes (50MB)", size, MaxFileSize)
}
return nil
}
func IsAllowedMimeType(mimeType string) bool {
baseMimeType := strings.Split(mimeType, ";")[0]
baseMimeType = strings.TrimSpace(baseMimeType)
for _, allowed := range AllowedMimeTypes {
if strings.EqualFold(baseMimeType, allowed) {
return true
}
}
if strings.HasPrefix(baseMimeType, "application/vnd.openxmlformats-officedocument") {
return true
}
return false
}
func DetectMimeType(data []byte) string {
return http.DetectContentType(data)
}
func ValidateMimeType(data []byte) (string, error) {
mimeType := DetectMimeType(data)
if !IsAllowedMimeType(mimeType) {
return "", fmt.Errorf("MIME type %q is not in the allowed whitelist", mimeType)
}
return mimeType, nil
}
func IsDangerousExtension(filename string) bool {
ext := strings.ToLower(filepath.Ext(filename))
for _, dangerous := range DangerousExtensions {
if ext == dangerous {
return true
}
}
return false
}
func ValidateFilename(filename string) error {
if filename == "" {
return fmt.Errorf("filename cannot be empty")
}
if IsDangerousExtension(filename) {
return fmt.Errorf("file type %q is not allowed for security reasons", filepath.Ext(filename))
}
if strings.Contains(filename, "\x00") {
return fmt.Errorf("filename contains invalid characters")
}
return nil
}
func GetFileInfo(path string) (os.FileInfo, error) {
return os.Stat(path)
}
func CheckDiskSpace(path string, requiredBytes int64) error {
dir := filepath.Dir(path)
if err := os.MkdirAll(dir, 0755); err != nil {
return fmt.Errorf("failed to create directory: %w", err)
}
testFile := filepath.Join(dir, ".write_test_"+fmt.Sprintf("%d", os.Getpid()))
f, err := os.Create(testFile)
if err != nil {
return fmt.Errorf("insufficient permissions or disk space at %s: %w", dir, err)
}
f.Close()
os.Remove(testFile)
return nil
}