V0.38.5 git board rewrite (#238)

Co-authored-by: gobha <jasafpro@gmail.com>
Co-committed-by: gobha <jasafpro@gmail.com>
This commit is contained in:
2026-03-25 22:24:59 +00:00
committed by xcaliber
parent 495bcc94f4
commit c03ece4230
24 changed files with 1354 additions and 413 deletions

View File

@@ -63,13 +63,28 @@ func (r *ConnectionResolverAdapter) decryptInPlace(ctx context.Context, conn *mo
if len(enc) == 0 {
continue
}
plaintext, err := r.vault.Decrypt(enc, nonce, conn.Scope, conn.OwnerID)
if err == nil {
plaintext := r.safeDecrypt(enc, nonce, conn.Scope, conn.OwnerID)
if plaintext != "" {
conn.Config[k] = plaintext
}
}
}
// safeDecrypt wraps vault.Decrypt to recover from GCM panics caused by
// invalid nonce lengths (e.g. stale vault keys after container restart).
func (r *ConnectionResolverAdapter) safeDecrypt(enc, nonce []byte, scope, ownerID string) (plaintext string) {
defer func() {
if rv := recover(); rv != nil {
plaintext = ""
}
}()
result, err := r.vault.Decrypt(enc, nonce, scope, ownerID)
if err != nil {
return ""
}
return result
}
// lookupSecretFields reads the package manifest to identify secret fields.
func (r *ConnectionResolverAdapter) lookupSecretFields(ctx context.Context, packageID, connType string) map[string]bool {
result := map[string]bool{}

View File

@@ -4,6 +4,7 @@ package handlers
// Pattern follows apiconfigs.go (ProviderConfigHandler).
import (
"encoding/base64"
"encoding/json"
"net/http"
@@ -27,7 +28,9 @@ func NewConnectionHandler(s store.Stores, vault *crypto.KeyResolver) *Connection
// ListConnections returns the user's personal connections.
func (h *ConnectionHandler) ListConnections(c *gin.Context) {
userID := getUserID(c)
conns, err := h.stores.Connections.ListForUser(c.Request.Context(), userID)
// v0.38.5: Show all accessible connections (personal + team + global)
// so users can see which connections are available to them.
conns, err := h.stores.Connections.ListAccessible(c.Request.Context(), userID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list connections"})
return
@@ -252,13 +255,23 @@ func (h *ConnectionHandler) decryptSecrets(config models.JSONMap, secretFields m
}
// toBytes coerces an interface{} to []byte. Handles both []byte (from Go)
// and string (from JSON unmarshal of base64).
// and string (from JSON unmarshal base64 encoded by json.Marshal for []byte).
func toBytes(v interface{}) []byte {
switch t := v.(type) {
case []byte:
return t
case string:
return []byte(t)
if t == "" {
return nil
}
// JSON round-trip: json.Marshal encodes []byte as base64 strings.
// Decode back to the original bytes.
decoded, err := base64.StdEncoding.DecodeString(t)
if err != nil {
// Fallback: might be raw string (shouldn't happen, but be safe)
return []byte(t)
}
return decoded
}
return nil
}

View File

@@ -100,7 +100,12 @@ func (h *ExtAPIHandler) Handle(c *gin.Context) {
return
}
// ── 3. Check api.http permission ───────────
// ── 3. Check permissions ──────────────────
// The package needs at least one granted permission to be active.
// api.http is NOT required here — a consumer package may delegate
// HTTP to a library via lib.require(). The sandbox wires the http
// module only when api.http is granted; without it the script simply
// cannot call http.get() directly, which is correct for consumers.
if h.stores.ExtPermissions == nil {
c.JSON(http.StatusForbidden, gin.H{"error": "permission system unavailable"})
return
@@ -110,8 +115,8 @@ func (h *ExtAPIHandler) Handle(c *gin.Context) {
c.JSON(http.StatusInternalServerError, gin.H{"error": "permission check failed"})
return
}
if !hasPermission(granted, models.ExtPermAPIHTTP) {
c.JSON(http.StatusForbidden, gin.H{"error": "extension does not have api.http permission"})
if len(granted) == 0 {
c.JSON(http.StatusForbidden, gin.H{"error": "extension has no granted permissions"})
return
}

View File

@@ -469,6 +469,11 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
}
}
// v0.38.5: Declare manifest permissions (same as AdminInstallExtension).
// This creates the permission rows and sets status to pending_review
// if the package declares permissions.
SyncManifestPermissions(c, h.stores, pkgID, manifest)
// v0.30.0: Run schema migrations if declared.
newSchemaVersion := ParseSchemaVersion(manifest)
if newSchemaVersion > 0 && h.sandbox != nil {