larksuite--cli
bf9395e022
CI / license-header (push) Has been skipped
CI / e2e-dry-run (push) Has been skipped
CI / fast-gate (push) Failing after 0s
Test PR Label Logic / test-pr-labels (push) Failing after 1s
Skill Format Check / check-format (push) Failing after 2s
CI / security (push) Failing after 5s
CI / unit-test (push) Has been skipped
CI / lint (push) Has been skipped
CI / script-test (push) Has been skipped
CI / deterministic-gate (push) Has been skipped
CI / coverage (push) Has been skipped
CI / results (push) Has been cancelled
CI / deadcode (push) Has been cancelled
CI / e2e-live (push) Has been cancelled
101 行
3.2 KiB
Go
101 行
3.2 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package apps
|
|
|
|
import (
|
|
"io/fs"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/larksuite/cli/errs"
|
|
"github.com/larksuite/cli/extension/fileio"
|
|
)
|
|
|
|
type htmlPublishCandidate struct {
|
|
RelPath string
|
|
AbsPath string
|
|
Size int64
|
|
}
|
|
|
|
// isUnsafeRelPath reports whether a forward-slash relative path contains
|
|
// anything that should never be written into a tar header or treated as
|
|
// inside-root: leading slash (absolute), .. as a path component (start /
|
|
// middle / end / whole), or an embedded null byte. Component-aware so it
|
|
// does not false-positive on legitimate filenames that contain ".." as a
|
|
// substring (e.g. "archive.tar..bak").
|
|
func isUnsafeRelPath(rel string) bool {
|
|
return strings.HasPrefix(rel, "/") ||
|
|
rel == ".." ||
|
|
strings.HasPrefix(rel, "../") ||
|
|
strings.Contains(rel, "/../") ||
|
|
strings.HasSuffix(rel, "/..") ||
|
|
strings.ContainsRune(rel, 0)
|
|
}
|
|
|
|
// walkHTMLPublishCandidates walks rootPath and returns each regular file as a
|
|
// candidate. Stat goes through fileio so SafeInputPath validation runs on the
|
|
// root; the directory walk itself uses filepath.WalkDir because runtime.FileIO
|
|
// has no WalkDir equivalent today.
|
|
func walkHTMLPublishCandidates(fio fileio.FileIO, rootPath string) ([]htmlPublishCandidate, error) {
|
|
stat, err := fio.Stat(rootPath)
|
|
if err != nil {
|
|
return nil, appsInputPathError(err)
|
|
}
|
|
if !stat.IsDir() {
|
|
return []htmlPublishCandidate{{
|
|
RelPath: filepath.Base(rootPath),
|
|
AbsPath: rootPath,
|
|
Size: stat.Size(),
|
|
}}, nil
|
|
}
|
|
|
|
var out []htmlPublishCandidate
|
|
//nolint:forbidigo // fileio has no WalkDir; rootPath is already validated above via fio.Stat -> SafeInputPath.
|
|
err = filepath.WalkDir(rootPath, func(path string, d fs.DirEntry, walkErr error) error {
|
|
if walkErr != nil {
|
|
return appsInputPathEntryError(path, walkErr)
|
|
}
|
|
// Skip a stray git repo: a directory named .git skips the whole subtree,
|
|
// and a .git file (the gitdir pointer used by submodules/worktrees) is
|
|
// skipped too.
|
|
if d.Name() == ".git" {
|
|
if d.IsDir() {
|
|
return filepath.SkipDir
|
|
}
|
|
return nil
|
|
}
|
|
if d.IsDir() {
|
|
return nil
|
|
}
|
|
info, err := d.Info()
|
|
if err != nil {
|
|
return appsInputPathEntryError(path, err)
|
|
}
|
|
// 只接受 regular file —— symlink / device / pipe / socket 都跳过。
|
|
// symlink 不跟随是设计决策(避免 loop + out-of-root 引用),且 fio.Open 也会拒非 regular。
|
|
if !info.Mode().IsRegular() {
|
|
return nil
|
|
}
|
|
rel, err := filepath.Rel(rootPath, path)
|
|
if err != nil {
|
|
return appsFileIOError(err, "resolve relative path for %s: %v", path, err)
|
|
}
|
|
relSlash := filepath.ToSlash(rel)
|
|
// Defense in depth: WalkDir + Rel inside rootPath should never yield a
|
|
// path with .. components, but a future logic change or unusual
|
|
// filesystem layout shouldn't be able to inject one into RelPath.
|
|
// Mirrors the same guard at tar entry write time.
|
|
if isUnsafeRelPath(relSlash) {
|
|
return errs.NewInternalError(errs.SubtypeUnknown, "walker produced unsafe relative path %q for %s", relSlash, path)
|
|
}
|
|
out = append(out, htmlPublishCandidate{
|
|
RelPath: relSlash,
|
|
AbsPath: path,
|
|
Size: info.Size(),
|
|
})
|
|
return nil
|
|
})
|
|
return out, err
|
|
}
|