feat: complete unified plugin admin v1.1.0
Business Plugins CI / check (plugin-admin) (push) Successful in 1m42s
Business Plugins CI / check (subscription-admin) (push) Successful in 1m30s

This commit is contained in:
Qiufeng
2026-08-30 12:10:04 +08:00
parent 3c1a17f4d7
commit ada4ab3c21
69 changed files with 6681 additions and 901 deletions
+77
View File
@@ -16,6 +16,7 @@ import (
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
"time"
)
@@ -31,6 +32,75 @@ var (
marketplaceSHA256Pattern = regexp.MustCompile(`^[a-f0-9]{64}$`)
)
// compareMarketplaceVersions implements the semver subset accepted by the
// manifest validator. It is used only for marketplace installs, where a
// catalog entry must never silently downgrade an already registered plugin.
func compareMarketplaceVersions(left, right string) int {
parse := func(value string) ([3]int, []string) {
value = strings.TrimPrefix(strings.TrimSpace(value), "v")
parts := strings.SplitN(value, "-", 2)
core := strings.Split(parts[0], ".")
var numbers [3]int
for i := 0; i < len(numbers) && i < len(core); i++ {
numbers[i], _ = strconv.Atoi(core[i])
}
if len(parts) == 1 {
return numbers, nil
}
return numbers, strings.Split(parts[1], ".")
}
leftCore, leftPre := parse(left)
rightCore, rightPre := parse(right)
for i := range leftCore {
if leftCore[i] < rightCore[i] {
return -1
}
if leftCore[i] > rightCore[i] {
return 1
}
}
if len(leftPre) == 0 && len(rightPre) == 0 {
return 0
}
if len(leftPre) == 0 {
return 1
}
if len(rightPre) == 0 {
return -1
}
for i := 0; i < len(leftPre) && i < len(rightPre); i++ {
l, r := leftPre[i], rightPre[i]
if l == r {
continue
}
ln, le := strconv.Atoi(l)
rn, re := strconv.Atoi(r)
if le == nil && re == nil {
if ln < rn {
return -1
}
return 1
}
if le == nil {
return -1
}
if re == nil {
return 1
}
if l < r {
return -1
}
return 1
}
if len(leftPre) < len(rightPre) {
return -1
}
if len(leftPre) > len(rightPre) {
return 1
}
return 0
}
// marketplaceEntry is deliberately metadata-only. The browser never receives
// the archive URL; downloads are performed by this server after catalog and
// transport policy validation.
@@ -63,6 +133,7 @@ type marketplaceService struct {
remoteURL *url.URL
allowedHosts map[string]struct{}
allowLoopback bool
indexSHA256 string
client *http.Client
}
@@ -238,6 +309,12 @@ func (m marketplaceService) loadIndex(ctx context.Context) (marketplaceIndex, ma
return marketplaceIndex{}, origin, errors.New("marketplace index exceeds size limit")
}
}
if expected := strings.TrimSpace(m.indexSHA256); expected != "" {
sum := sha256.Sum256(raw)
if !strings.EqualFold(hex.EncodeToString(sum[:]), expected) {
return marketplaceIndex{}, origin, errors.New("marketplace index sha256 does not match the configured pin")
}
}
var index marketplaceIndex
decoder := json.NewDecoder(bytes.NewReader(raw))
decoder.DisallowUnknownFields()