Files
attesto-go/connectorkit/manifest.go

121 lines
3.6 KiB
Go

package connectorkit
import "regexp"
type Manifest struct {
SchemaVersion string `json:"schemaVersion"`
Slug string `json:"slug"`
Name string `json:"name"`
Version string `json:"version"`
AssetType string `json:"assetType"`
Category string `json:"category"`
Summary string `json:"summary,omitempty"`
Description string `json:"description,omitempty"`
Publisher map[string]string `json:"publisher"`
Repository map[string]string `json:"repository"`
Documentation map[string]string `json:"documentation"`
Capabilities []string `json:"capabilities"`
Evidence map[string]bool `json:"evidence"`
Security map[string]bool `json:"security"`
SupportedLanguages []string `json:"supportedLanguages,omitempty"`
}
type Finding struct {
Code string
Severity string
Message string
}
type ValidationResult struct {
OK bool
EvidenceScore int
Tier string
Findings []Finding
}
var slugPattern = regexp.MustCompile(`^[a-z0-9][a-z0-9-]{2,95}$`)
var versionPattern = regexp.MustCompile(`^[0-9]+\.[0-9]+\.[0-9]+(?:[-+][a-zA-Z0-9.-]+)?$`)
var categories = map[string]bool{
"ai-governance": true,
"compliance": true,
"crm": true,
"devops": true,
"erp": true,
"iam": true,
"security": true,
"storage": true,
}
func ValidateManifest(manifest Manifest) ValidationResult {
findings := make([]Finding, 0)
if !slugPattern.MatchString(manifest.Slug) {
findings = append(findings, Finding{"manifest.invalid_slug", "error", "slug must be lower-kebab-case"})
}
if !versionPattern.MatchString(manifest.Version) {
findings = append(findings, Finding{"manifest.invalid_version", "error", "version must be semantic versioning"})
}
if !categories[manifest.Category] {
findings = append(findings, Finding{"manifest.invalid_category", "error", "unsupported category"})
}
if len(manifest.Capabilities) == 0 {
findings = append(findings, Finding{"capabilities.empty", "error", "at least one capability is required"})
}
if manifest.SchemaVersion == "" || manifest.Name == "" || manifest.AssetType == "" {
findings = append(findings, Finding{"manifest.missing_field", "error", "schemaVersion, name, and assetType are required"})
}
score := 0
if len(findings) == 0 {
score = 0
if manifest.Evidence["receipts"] && manifest.Evidence["offlineVerification"] {
score += 25
} else {
score += 18
}
if manifest.Security["secretScan"] && manifest.Security["dependencyScan"] {
score += 20
} else {
score += 12
}
if manifest.Evidence["witnessCompatible"] {
score += 15
} else {
score += 8
}
if manifest.Repository["url"] != "" && manifest.Documentation["url"] != "" {
score += 15
} else {
score += 10
}
if hasCapability(manifest.Capabilities, "proofstream") {
score += 15
} else {
score += 10
}
score += 5
}
tier := "hidden"
switch {
case score >= 95:
tier = "platinum"
case score >= 85:
tier = "gold"
case score >= 70:
tier = "silver"
case score >= 50:
tier = "community"
}
if score < 50 && len(findings) == 0 {
findings = append(findings, Finding{"score.hidden", "error", "Evidence Score below 50 cannot be publicly listed"})
}
return ValidationResult{OK: len(findings) == 0 && score >= 50, EvidenceScore: score, Tier: tier, Findings: findings}
}
func hasCapability(values []string, target string) bool {
for _, value := range values {
if value == target {
return true
}
}
return false
}