(appDir string, fileName string)
| 233 | } |
| 234 | |
| 235 | func validateAndResolveFilePath(appDir string, fileName string) (string, error) { |
| 236 | if filepath.IsAbs(fileName) { |
| 237 | return "", fmt.Errorf("fileName must be relative, got absolute path: %s", fileName) |
| 238 | } |
| 239 | |
| 240 | cleanPath := filepath.Clean(fileName) |
| 241 | if strings.HasPrefix(cleanPath, "..") || strings.Contains(cleanPath, string(filepath.Separator)+"..") { |
| 242 | return "", fmt.Errorf("path traversal not allowed: %s", fileName) |
| 243 | } |
| 244 | |
| 245 | fullPath := filepath.Join(appDir, cleanPath) |
| 246 | resolvedPath, err := filepath.Abs(fullPath) |
| 247 | if err != nil { |
| 248 | return "", fmt.Errorf("failed to resolve path: %w", err) |
| 249 | } |
| 250 | |
| 251 | resolvedAppDir, err := filepath.Abs(appDir) |
| 252 | if err != nil { |
| 253 | return "", fmt.Errorf("failed to resolve app directory: %w", err) |
| 254 | } |
| 255 | |
| 256 | if !strings.HasPrefix(resolvedPath, resolvedAppDir+string(filepath.Separator)) && resolvedPath != resolvedAppDir { |
| 257 | return "", fmt.Errorf("path escapes app directory: %s", fileName) |
| 258 | } |
| 259 | |
| 260 | return resolvedPath, nil |
| 261 | } |
| 262 | |
| 263 | func WriteAppFile(appId string, fileName string, contents []byte) error { |
| 264 | if err := ValidateAppId(appId); err != nil { |
no test coverage detected