package signature

import (
 "context"
 "os"
 "path/filepath"
 "testing"

 "github.com/grafana/grafana/pkg/plugins"
 "github.com/grafana/grafana/pkg/plugins/config"
 "github.com/grafana/grafana/pkg/plugins/manager/sources"
)
func distributorPlugin(t *testing.T,dir string) (plugins.PluginSource,plugins.FoundPlugin) {
 t.Helper()
 src:=sources.NewLocalSource(plugins.ClassExternal,[]string{dir})
 found,err:=src.Discover(context.Background())
 if err!=nil||len(found)!=1 {t.Fatalf("discover: %v count=%d",err,len(found))}
 return src,found[0].Primary
}
func distributorStatus(t *testing.T,src plugins.PluginSource,p plugins.FoundPlugin,want plugins.SignatureStatus) {
 t.Helper()
 s,err:=DefaultCalculator(&config.PluginManagementCfg{}).Calculate(context.Background(),src,p)
 if err!=nil||s.Status!=want {t.Fatalf("%s: got %+v / %v; want %s",p.JSONData.ID,s,err,want)}
 if want==plugins.SignatureStatusDistributorVerified {
  if s.Type!=""||s.SigningOrg=="" {t.Fatalf("incorrect upstream signing attribution: %+v",s)}
  if err:=NewValidator(nil).ValidateSignature(&plugins.Plugin{Signature:s.Status});err!=nil {t.Fatal(err)}
 } else if want==plugins.SignatureStatusModified||want==plugins.SignatureStatusInvalid {
  if err:=NewValidator(nil).ValidateSignature(&plugins.Plugin{Signature:s.Status});err==nil {t.Fatal("tampered plugin accepted by validator")}
 }
}
func TestDistributorAllPackagedBackends(t *testing.T) {
 for id,entry:=range distributorBackends {
  t.Run(id,func(t *testing.T){src,p:=distributorPlugin(t,"/usr/share/grafana/data/plugins-bundled/"+entry.Directory);distributorStatus(t,src,p,plugins.SignatureStatusDistributorVerified)})
 }
}
func TestDistributorRejectsTampering(t *testing.T) {
 dir:="/usr/share/grafana/data/plugins-bundled/prometheus"
 src,p:=distributorPlugin(t,dir)
 entry:=distributorBackends[p.JSONData.ID]
 for _,name:=range []string{entry.Binary,"module.js","plugin.json","MANIFEST.txt"} {
  t.Run(name,func(t *testing.T){
   path:=filepath.Join(dir,name);old,err:=os.ReadFile(path);if err!=nil{t.Fatal(err)}
   changed:=append([]byte(nil),old...);changed[len(changed)/2]^=1
   if err=os.WriteFile(path,changed,0755);err!=nil{t.Fatal(err)}
   t.Cleanup(func(){if err:=os.WriteFile(path,old,0755);err!=nil{t.Fatal(err)}})
   want:=plugins.SignatureStatusModified;if name=="MANIFEST.txt"{want=plugins.SignatureStatusInvalid}
   distributorStatus(t,src,p,want)
  })
 }
 t.Run("missing manifest",func(t *testing.T){path:=filepath.Join(dir,"MANIFEST.txt");if err:=os.Rename(path,path+".saved");err!=nil{t.Fatal(err)};t.Cleanup(func(){os.Rename(path+".saved",path)});distributorStatus(t,src,p,plugins.SignatureStatusUnsigned)})
 t.Run("additional unsigned file",func(t *testing.T){path:=filepath.Join(dir,"injected.js");if err:=os.WriteFile(path,[]byte("tampered"),0644);err!=nil{t.Fatal(err)};t.Cleanup(func(){os.Remove(path)});src,p:=distributorPlugin(t,dir);distributorStatus(t,src,p,plugins.SignatureStatusModified)})
 t.Run("unknown identity",func(t *testing.T){p:=p;p.JSONData.ID="unknown-plugin";distributorStatus(t,src,p,plugins.SignatureStatusModified)})
 t.Run("different version",func(t *testing.T){p:=p;p.JSONData.Info.Version="0.0.0";distributorStatus(t,src,p,plugins.SignatureStatusModified)})
 t.Run("external directory",func(t *testing.T){moved:=dir+"-external";if err:=os.Rename(dir,moved);err!=nil{t.Fatal(err)};t.Cleanup(func(){os.Rename(moved,dir)});src,p:=distributorPlugin(t,moved);distributorStatus(t,src,p,plugins.SignatureStatusModified)})
 t.Run("symlink directory",func(t *testing.T){moved:=dir+"-target";if err:=os.Rename(dir,moved);err!=nil{t.Fatal(err)};if err:=os.Symlink(moved,dir);err!=nil{t.Fatal(err)};t.Cleanup(func(){os.Remove(dir);os.Rename(moved,dir)});distributorStatus(t,src,p,plugins.SignatureStatusModified)})
}
