mirror of
https://github.com/larksuite/cli.git
synced 2026-07-06 00:06:28 +08:00
172 lines
4.6 KiB
Go
172 lines
4.6 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
// Package filecheck provides mail attachment file validation utilities shared
|
|
// by the emlbuilder and draft packages.
|
|
package filecheck
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// blockedExtensions is the set of file extensions that are not allowed as mail
|
|
// attachments. These are potentially harmful executable or script types that
|
|
// should be blocked for security reasons (htm/html are intentionally excluded).
|
|
var blockedExtensions = map[string]struct{}{
|
|
"action": {},
|
|
"apk": {},
|
|
"app": {},
|
|
"applescript": {},
|
|
"asp": {},
|
|
"awk": {},
|
|
"bash": {},
|
|
"bat": {},
|
|
"bin": {},
|
|
"cdxml": {},
|
|
"chm": {},
|
|
"cmd": {},
|
|
"coffee": {},
|
|
"com": {},
|
|
"command": {},
|
|
"cpl": {},
|
|
"csh": {},
|
|
"dart": {},
|
|
"dll": {},
|
|
"es": {},
|
|
"exe": {},
|
|
"fish": {},
|
|
"gadget": {},
|
|
"go": {},
|
|
"hta": {},
|
|
"inf1": {},
|
|
"ins": {},
|
|
"inx": {},
|
|
"ipa": {},
|
|
"isu": {},
|
|
"jar": {},
|
|
"job": {},
|
|
"js": {},
|
|
"jse": {},
|
|
"ksh": {},
|
|
"lnk": {},
|
|
"lua": {},
|
|
"msc": {},
|
|
"msh": {},
|
|
"msh1": {},
|
|
"msh1xml": {},
|
|
"msh2": {},
|
|
"msh2xml": {},
|
|
"mshxml": {},
|
|
"msi": {},
|
|
"msp": {},
|
|
"mst": {},
|
|
"msu": {},
|
|
"osx": {},
|
|
"out": {},
|
|
"paf": {},
|
|
"php": {},
|
|
"pif": {},
|
|
"pl": {},
|
|
"plist": {},
|
|
"pls": {},
|
|
"pm": {},
|
|
"prg": {},
|
|
"ps": {},
|
|
"ps1": {},
|
|
"ps1xml": {},
|
|
"ps2": {},
|
|
"ps2xml": {},
|
|
"psc1": {},
|
|
"psc2": {},
|
|
"psd1": {},
|
|
"psdm1": {},
|
|
"psm1": {},
|
|
"pssc": {},
|
|
"py": {},
|
|
"pyc": {},
|
|
"pyo": {},
|
|
"pyw": {},
|
|
"pyz": {},
|
|
"pyzw": {},
|
|
"rb": {},
|
|
"reg": {},
|
|
"rgs": {},
|
|
"run": {},
|
|
"scf": {},
|
|
"scr": {},
|
|
"sct": {},
|
|
"sh": {},
|
|
"shb": {},
|
|
"shs": {},
|
|
"tcsh": {},
|
|
"terminal": {},
|
|
"ts": {},
|
|
"tsx": {},
|
|
"u3p": {},
|
|
"vb": {},
|
|
"vbe": {},
|
|
"vbs": {},
|
|
"vbscript": {},
|
|
"ws": {},
|
|
"wsc": {},
|
|
"wsf": {},
|
|
"wsh": {},
|
|
"zsh": {},
|
|
}
|
|
|
|
// CheckBlockedExtension returns an error if the filename has a blocked extension.
|
|
func CheckBlockedExtension(filename string) error {
|
|
ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(filename), "."))
|
|
if ext == "" {
|
|
return nil
|
|
}
|
|
if _, ok := blockedExtensions[ext]; ok {
|
|
return fmt.Errorf("file extension %q is not allowed as a mail attachment", "."+ext)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// allowedInlineExtensions is the whitelist of file extensions allowed for
|
|
// inline images. Only well-supported image formats are included.
|
|
var allowedInlineExtensions = map[string]struct{}{
|
|
"jpg": {},
|
|
"jpeg": {},
|
|
"png": {},
|
|
"gif": {},
|
|
"webp": {},
|
|
}
|
|
|
|
// allowedInlineMIMETypes is the whitelist of MIME types allowed for inline
|
|
// images, checked via content sniffing (http.DetectContentType).
|
|
var allowedInlineMIMETypes = map[string]struct{}{
|
|
"image/jpeg": {},
|
|
"image/png": {},
|
|
"image/gif": {},
|
|
"image/webp": {},
|
|
}
|
|
|
|
// CheckInlineImageFormat validates that the file is an allowed inline image
|
|
// format by checking both extension and content-sniffed MIME type.
|
|
// Both must match the whitelist to prevent extension spoofing and MIME forgery.
|
|
// On success it returns the detected MIME type; callers MUST use this as the
|
|
// final Content-Type instead of trusting any user-supplied or inherited value.
|
|
func CheckInlineImageFormat(filename string, content []byte) (string, error) {
|
|
ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(filename), "."))
|
|
if _, ok := allowedInlineExtensions[ext]; !ok {
|
|
return "", fmt.Errorf("inline image extension %q is not allowed; supported formats: jpg, jpeg, png, gif, webp", ext)
|
|
}
|
|
detected := http.DetectContentType(content)
|
|
// DetectContentType may return params (e.g. "text/plain; charset=utf-8"),
|
|
// strip to the base media type.
|
|
if i := strings.IndexByte(detected, ';'); i != -1 {
|
|
detected = strings.TrimSpace(detected[:i])
|
|
}
|
|
if _, ok := allowedInlineMIMETypes[detected]; !ok {
|
|
return "", fmt.Errorf("inline image content type %q does not match an allowed image format; supported: image/jpeg, image/png, image/gif, image/webp", detected)
|
|
}
|
|
return detected, nil
|
|
}
|