nuclei/v2/pkg/templates/template_sign.go
Tarun Koyalwar c35162c8ef
nuclei v3 bug fixes (#4176)
* store and generate signer keys

* fix trailing newline in code_response

* fix formatting and update error string

* fix integration test

* fix rsaSigned code integration test

* bug fixes , docs and more

* bump go -> 1.21

* use 'response' as default part in code templates

* disable sourcemaps for all js runtimes

* disable eval function

* rewrite file validation in sandbox mode

* sandbox file read improvements + minor refactor

* refactor sign and verify logic

* fix panic and missing id in code protocol

* disable re-signing code protocol templates

* fix code resigning in tests

* allow -lfa in test for signing templates

* start index from 1 in flow and multiproto

* remove testfiles

* add python in integration test

* update code protocol docs

* add python engine in template

* rework template signer

* fix integration test and more

* reworked template signer

* fix lint error

* display signature stats

* update docs

* add user fragment to signature

* use md5 to generate fragment

* update docs with code re-sign

* misc updates

* public crt update

* remove workflow info statement

* fix printing issues

* refactor preprocessor logic

* remove debug statement

* fix failing example test

* go mod tidy

---------

Co-authored-by: sandeep <8293321+ehsandeep@users.noreply.github.com>
Co-authored-by: Sandeep Singh <sandeep@projectdiscovery.io>
2023-10-13 13:17:27 +05:30

100 lines
3.3 KiB
Go

package templates
import (
"bytes"
"os"
"path/filepath"
"strings"
"sync"
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/disk"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/protocolinit"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/protocolstate"
"github.com/projectdiscovery/nuclei/v2/pkg/templates/extensions"
"github.com/projectdiscovery/nuclei/v2/pkg/templates/signer"
"github.com/projectdiscovery/nuclei/v2/pkg/types"
errorutil "github.com/projectdiscovery/utils/errors"
)
// Due to file references in sensitive fields of template
// ex: javascript code in flow or bash command in code.Source etc
// signing / verifying template is only possible after loading the template
// with these fields resolved
var (
defaultOpts *types.Options = types.DefaultOptions()
initOnce = sync.OnceFunc(func() {
_ = protocolstate.Init(defaultOpts)
_ = protocolinit.Init(defaultOpts)
})
ErrNotATemplate = errorutil.NewWithTag("signer", "given filePath is not a template")
)
// New Signer/Verification logic requires it to load content of file references
// and this is done respecting sandbox restrictions to avoid any security issues
// AllowLocalFileAccess is a function that allows local file access by disabling sandbox restrictions
// and **MUST** be called before signing / verifying any templates for intialization
func TemplateSignerLFA() {
defaultOpts.AllowLocalFileAccess = true
}
// VerifyTemplateSignature verifies the signature of the template
// using default signers
func VerifyTemplateSignature(templatePath string) (bool, error) {
template, _, err := getTemplate(templatePath)
if err != nil {
return false, err
}
return template.Verified, nil
}
// SignTemplate signs the tempalate using custom signer
func SignTemplate(templateSigner *signer.TemplateSigner, templatePath string) error {
// sign templates requires code files such as javsacript bash command to be included
// in template hence we first load template and append such resolved file references to content
initOnce()
// signing is only supported on yaml nuclei templates
if !strings.HasSuffix(templatePath, extensions.YAML) {
return ErrNotATemplate
}
template, bin, err := getTemplate(templatePath)
if err != nil {
return errorutil.NewWithErr(err).Msgf("failed to get template from disk")
}
if len(template.Workflows) > 0 {
// signing workflows is not supported at least yet
return ErrNotATemplate
}
if !template.Verified {
signatureData, err := templateSigner.Sign(bin, template)
if err != nil {
return err
}
buff := bytes.NewBuffer(signer.RemoveSignatureFromData(bin))
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, errorutil.NewWithErr(err).Msgf("failed to parse template")
}
return template, bin, nil
}