package templates
import (
"bytes"
"os"
"path/filepath"
"strings"
"sync"
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/disk"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolinit"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
"github.com/projectdiscovery/nuclei/v3/pkg/templates/extensions"
"github.com/projectdiscovery/nuclei/v3/pkg/templates/signer"
"github.com/projectdiscovery/nuclei/v3/pkg/types"
"github.com/projectdiscovery/utils/errkit"
)
var (
defaultOpts *types.Options = types.DefaultOptions()
initOnce = sync.OnceFunc(func() {
_ = protocolstate.Init(defaultOpts)
_ = protocolinit.Init(defaultOpts)
})
ErrNotATemplate = errkit.New("given filePath is not a template", "tag", "signer")
)
func UseOptionsForSigner(opts *types.Options) {
defaultOpts = opts
}
func TemplateSignerLFA() {
defaultOpts.AllowLocalFileAccess = true
}
func VerifyTemplateSignature(templatePath string) (bool, error) {
template, _, err := getTemplate(templatePath)
if err != nil {
return false, err
}
return template.Verified, nil
}
func SignTemplate(templateSigner *signer.TemplateSigner, templatePath string) error {
initOnce()
if !strings.HasSuffix(templatePath, extensions.YAML) {
return ErrNotATemplate
}
template, bin, err := getTemplate(templatePath)
if err != nil {
return errkit.Wrap(err, "failed to get template from disk")
}
if len(template.Workflows) > 0 {
return ErrNotATemplate
}
if !template.Verified {
_, content := signer.ExtractSignatureAndContent(bin)
signatureData, err := templateSigner.Sign(bin, template)
if err != nil {
return err
}
buff := bytes.NewBuffer(content)
buff.WriteString("\n" + signatureData)
return os.WriteFile(templatePath, buff.Bytes(), 0644)
}
return nil
}
func getTemplate(templatePath string) (*Template, []byte, error) {
catalog := disk.NewCatalog(filepath.Dir(templatePath))
executerOpts := &protocols.ExecutorOptions{
Catalog: catalog,
Options: defaultOpts,
TemplatePath: templatePath,
}
bin, err := os.ReadFile(templatePath)
if err != nil {
return nil, bin, err
}
template, err := ParseTemplateFromReader(bytes.NewReader(bin), nil, executerOpts)
if err != nil {
return nil, bin, errkit.Wrap(err, "failed to parse template")
}
return template, bin, nil
}