Files
wehub-resource-sync ead81af521
Deploy to GitHub Pages / deploy (push) Failing after 0s
CI / go (push) Has been cancelled
CI / build (darwin, macos-14) (push) Has been cancelled
CI / build (windows, windows-2025) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:31:13 +08:00

100 lines
2.1 KiB
Go

// Package plugintrust persists approvals for Lua plugin content.
package plugintrust
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"github.com/bjarneo/cliamp/internal/fileutil"
)
const manifestName = ".trust.json"
var (
ErrUntrusted = errors.New("plugin is not trusted")
ErrHashMismatch = errors.New("plugin content changed since approval")
)
type Manifest struct {
Version int `json:"version"`
Plugins map[string]string `json:"plugins"`
}
func HashFile(path string) (string, error) {
f, err := os.Open(path)
if err != nil {
return "", fmt.Errorf("open plugin: %w", err)
}
defer f.Close()
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
return "", fmt.Errorf("hash plugin: %w", err)
}
return hex.EncodeToString(h.Sum(nil)), nil
}
func Load(dir string) (Manifest, error) {
m := Manifest{Version: 1, Plugins: make(map[string]string)}
data, err := os.ReadFile(filepath.Join(dir, manifestName))
if errors.Is(err, os.ErrNotExist) {
return m, nil
}
if err != nil {
return m, fmt.Errorf("read plugin trust manifest: %w", err)
}
if err := json.Unmarshal(data, &m); err != nil {
return m, fmt.Errorf("parse plugin trust manifest: %w", err)
}
if m.Version != 1 || m.Plugins == nil {
return m, errors.New("unsupported plugin trust manifest")
}
return m, nil
}
func Save(dir string, m Manifest) error {
m.Version = 1
data, err := json.MarshalIndent(m, "", " ")
if err != nil {
return fmt.Errorf("encode plugin trust manifest: %w", err)
}
data = append(data, '\n')
return fileutil.WriteFileAtomic(filepath.Join(dir, manifestName), data, 0o600)
}
func Approve(dir, name, path string) (string, error) {
hash, err := HashFile(path)
if err != nil {
return "", err
}
m, err := Load(dir)
if err != nil {
return "", err
}
m.Plugins[name] = hash
if err := Save(dir, m); err != nil {
return "", err
}
return hash, nil
}
func Verify(m Manifest, name, path string) error {
want, ok := m.Plugins[name]
if !ok {
return ErrUntrusted
}
got, err := HashFile(path)
if err != nil {
return err
}
if got != want {
return ErrHashMismatch
}
return nil
}