Files
larksuite-cli/internal/validate/resource.go
tuxedomm 900c12ce8d feat: add FileIO extension for file transfer abstraction (#314)
* feat: add FileIO extension for file transfer abstraction

Introduce extension/fileio package with Provider/FileIO/File interfaces
and a global registry, following the same pattern as extension/credential.

- Add LocalFileIO default implementation with path validation and atomic writes
- Wire FileIOProvider into Factory and resolve at runtime via RuntimeContext.FileIO()
- Factory holds Provider (not resolved instance), deferring resolution to execution time
2026-04-08 14:13:59 +08:00

63 lines
2.3 KiB
Go

// Copyright (c) 2026 Lark Technologies Pte. Ltd.
// SPDX-License-Identifier: MIT
package validate
import (
"fmt"
"net/url"
"regexp"
"strings"
"github.com/larksuite/cli/internal/charcheck"
)
// unsafeResourceChars matches URL-special characters, control characters,
// and percent signs (to prevent %2e%2e encoding bypass).
var unsafeResourceChars = regexp.MustCompile(`[?#%\x00-\x1f\x7f]`)
// ResourceName validates an API resource identifier (messageId, fileToken, etc.)
// before it is interpolated into a URL path via fmt.Sprintf. It rejects path
// traversal (..), URL metacharacters (?#%), percent-encoded bypasses (%2e%2e),
// control characters, and dangerous Unicode.
//
// Without this check, an input like "../admin" or "?evil=true" in a message ID
// would alter the API endpoint the request is sent to. Works alongside
// EncodePathSegment for defense-in-depth.
func ResourceName(name, flagName string) error {
if name == "" {
return fmt.Errorf("%s must not be empty", flagName)
}
for _, seg := range strings.Split(name, "/") {
if seg == ".." {
return fmt.Errorf("%s must not contain '..' path traversal", flagName)
}
}
if unsafeResourceChars.MatchString(name) {
return fmt.Errorf("%s contains invalid characters", flagName)
}
for _, r := range name {
if charcheck.IsDangerousUnicode(r) {
return fmt.Errorf("%s contains dangerous Unicode characters", flagName)
}
}
return nil
}
// EncodePathSegment percent-encodes user input for safe use as a single URL path
// segment (e.g. / → %2F, ? → %3F, # → %23), ensuring the value cannot alter the
// URL routing structure when interpolated into an API path.
//
// This provides defense-in-depth alongside ResourceName: ResourceName rejects known
// dangerous patterns at the input layer, while EncodePathSegment acts as a fallback
// at the concatenation layer — if ResourceName rules are relaxed in the future, or
// if an API path bypasses ResourceName validation (e.g. cmd/service/ generic calls),
// encoding still prevents special characters from being interpreted as path separators
// or query parameters.
//
// Convention: all user-provided variables in fmt.Sprintf API paths within shortcuts/
// MUST be wrapped with this function.
func EncodePathSegment(s string) string {
return url.PathEscape(s)
}