feat: initial commit

This commit is contained in:
eric
2026-03-12 22:16:34 +01:00
parent 8555b02752
commit f13f4a9a69
155 changed files with 11988 additions and 0 deletions

View File

@@ -0,0 +1,188 @@
package platform
import (
"archive/tar"
"archive/zip"
"compress/gzip"
"context"
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/Eriyc/rules_wails/pkg/wails3kit/updates"
)
func extractBundle(_ context.Context, archivePath string, format updates.ArtifactFormat) (string, updates.BundleManifest, error) {
stageDir, err := os.MkdirTemp("", "wails3kit-stage-*")
if err != nil {
return "", updates.BundleManifest{}, err
}
switch format {
case updates.ArtifactFormatZip:
err = extractZip(archivePath, stageDir)
case updates.ArtifactFormatTarGz:
err = extractTarGz(archivePath, stageDir)
default:
err = fmt.Errorf("%w: unsupported format %s", updates.ErrInvalidArtifact, format)
}
if err != nil {
_ = os.RemoveAll(stageDir)
return "", updates.BundleManifest{}, err
}
manifestPath := filepath.Join(stageDir, "bundle.json")
bytes, err := os.ReadFile(manifestPath)
if err != nil {
_ = os.RemoveAll(stageDir)
return "", updates.BundleManifest{}, err
}
var manifest updates.BundleManifest
if err := json.Unmarshal(bytes, &manifest); err != nil {
_ = os.RemoveAll(stageDir)
return "", updates.BundleManifest{}, err
}
if err := validateBundle(stageDir, manifest); err != nil {
_ = os.RemoveAll(stageDir)
return "", updates.BundleManifest{}, err
}
return stageDir, manifest, nil
}
func extractZip(archivePath string, targetDir string) error {
reader, err := zip.OpenReader(archivePath)
if err != nil {
return err
}
defer reader.Close()
for _, file := range reader.File {
path, err := safeArchivePath(targetDir, file.Name)
if err != nil {
return err
}
if file.FileInfo().IsDir() {
if err := os.MkdirAll(path, 0o755); err != nil {
return err
}
continue
}
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
return err
}
source, err := file.Open()
if err != nil {
return err
}
if err := writeFile(path, source, file.Mode()); err != nil {
_ = source.Close()
return err
}
_ = source.Close()
}
return nil
}
func extractTarGz(archivePath string, targetDir string) error {
file, err := os.Open(archivePath)
if err != nil {
return err
}
defer file.Close()
gzipReader, err := gzip.NewReader(file)
if err != nil {
return err
}
defer gzipReader.Close()
reader := tar.NewReader(gzipReader)
for {
header, err := reader.Next()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
path, err := safeArchivePath(targetDir, header.Name)
if err != nil {
return err
}
switch header.Typeflag {
case tar.TypeDir:
if err := os.MkdirAll(path, 0o755); err != nil {
return err
}
case tar.TypeReg:
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
return err
}
if err := writeFile(path, reader, os.FileMode(header.Mode)); err != nil {
return err
}
default:
return fmt.Errorf("%w: unsupported tar entry %s", updates.ErrInvalidArtifact, header.Name)
}
}
}
func validateBundle(stageDir string, manifest updates.BundleManifest) error {
if manifest.SchemaVersion != 1 {
return fmt.Errorf("%w: unsupported bundle schema %d", updates.ErrInvalidArtifact, manifest.SchemaVersion)
}
if !isSafeRelative(manifest.EntryPoint) {
return fmt.Errorf("%w: invalid entrypoint", updates.ErrInvalidArtifact)
}
if len(manifest.Files) == 0 {
return fmt.Errorf("%w: bundle contains no files", updates.ErrInvalidArtifact)
}
for _, file := range manifest.Files {
if !isSafeRelative(file.Path) {
return fmt.Errorf("%w: invalid bundle path %s", updates.ErrInvalidArtifact, file.Path)
}
if _, err := strconv.ParseUint(file.Mode, 8, 32); err != nil {
return fmt.Errorf("%w: invalid mode %s", updates.ErrInvalidArtifact, file.Mode)
}
info, err := os.Stat(filepath.Join(stageDir, filepath.FromSlash(file.Path)))
if err != nil {
return err
}
if info.IsDir() {
return fmt.Errorf("%w: file entry %s is a directory", updates.ErrInvalidArtifact, file.Path)
}
}
return nil
}
func safeArchivePath(root string, name string) (string, error) {
if !isSafeRelative(name) && strings.TrimSpace(name) != "bundle.json" {
return "", fmt.Errorf("%w: unsafe archive path %s", updates.ErrInvalidArtifact, name)
}
return filepath.Join(root, filepath.FromSlash(name)), nil
}
func isSafeRelative(path string) bool {
cleaned := filepath.Clean(filepath.FromSlash(path))
if cleaned == "." || cleaned == "" {
return false
}
if filepath.IsAbs(cleaned) {
return false
}
return !strings.HasPrefix(cleaned, "..")
}