2021-10-14 23:30:37 +02:00
|
|
|
package loader
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bufio"
|
|
|
|
|
"fmt"
|
|
|
|
|
"net/http"
|
2022-01-12 18:33:17 +05:30
|
|
|
"net/url"
|
2021-10-14 23:30:37 +02:00
|
|
|
"strings"
|
2021-11-05 17:24:23 +05:30
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
2022-02-07 16:41:55 +02:00
|
|
|
|
2022-01-24 16:48:12 +05:30
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/utils"
|
2021-10-14 23:30:37 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type ContentType string
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
Template ContentType = "Template"
|
|
|
|
|
Workflow ContentType = "Workflow"
|
|
|
|
|
)
|
|
|
|
|
|
2022-01-24 16:48:12 +05:30
|
|
|
type RemoteContent struct {
|
2021-10-14 23:30:37 +02:00
|
|
|
Content []string
|
|
|
|
|
Type ContentType
|
|
|
|
|
Error error
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-21 20:14:50 +05:30
|
|
|
func getRemoteTemplatesAndWorkflows(templateURLs, workflowURLs, remoteTemplateDomainList []string) ([]string, []string, error) {
|
2022-01-24 16:48:12 +05:30
|
|
|
remoteContentChannel := make(chan RemoteContent)
|
2021-10-14 23:30:37 +02:00
|
|
|
|
|
|
|
|
for _, templateURL := range templateURLs {
|
2022-01-24 16:48:12 +05:30
|
|
|
go getRemoteContent(templateURL, remoteTemplateDomainList, remoteContentChannel, Template)
|
2021-10-14 23:30:37 +02:00
|
|
|
}
|
|
|
|
|
for _, workflowURL := range workflowURLs {
|
2022-01-24 16:48:12 +05:30
|
|
|
go getRemoteContent(workflowURL, remoteTemplateDomainList, remoteContentChannel, Workflow)
|
2021-10-14 23:30:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var remoteTemplateList []string
|
|
|
|
|
var remoteWorkFlowList []string
|
|
|
|
|
var err error
|
|
|
|
|
for i := 0; i < (len(templateURLs) + len(workflowURLs)); i++ {
|
2022-01-24 16:48:12 +05:30
|
|
|
remoteContent := <-remoteContentChannel
|
|
|
|
|
if remoteContent.Error != nil {
|
2021-10-14 23:30:37 +02:00
|
|
|
if err != nil {
|
2022-01-24 16:48:12 +05:30
|
|
|
err = errors.New(remoteContent.Error.Error() + ": " + err.Error())
|
2021-10-14 23:30:37 +02:00
|
|
|
} else {
|
2022-01-24 16:48:12 +05:30
|
|
|
err = remoteContent.Error
|
2021-10-14 23:30:37 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
2022-01-24 16:48:12 +05:30
|
|
|
if remoteContent.Type == Template {
|
|
|
|
|
remoteTemplateList = append(remoteTemplateList, remoteContent.Content...)
|
|
|
|
|
} else if remoteContent.Type == Workflow {
|
|
|
|
|
remoteWorkFlowList = append(remoteWorkFlowList, remoteContent.Content...)
|
2021-10-14 23:30:37 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return remoteTemplateList, remoteWorkFlowList, err
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-24 16:48:12 +05:30
|
|
|
func getRemoteContent(URL string, remoteTemplateDomainList []string, remoteContentChannel chan<- RemoteContent, contentType ContentType) {
|
2022-02-07 16:41:55 +02:00
|
|
|
if err := validateRemoteTemplateURL(URL, remoteTemplateDomainList); err != nil {
|
2022-01-27 13:45:03 +05:30
|
|
|
remoteContentChannel <- RemoteContent{
|
|
|
|
|
Error: err,
|
2022-01-12 18:33:17 +05:30
|
|
|
}
|
2022-01-27 13:45:03 +05:30
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if strings.HasPrefix(URL, "http") && (strings.HasSuffix(URL, ".yaml") || strings.HasSuffix(URL, ".yml")) {
|
2022-01-24 16:48:12 +05:30
|
|
|
remoteContentChannel <- RemoteContent{
|
2022-01-12 18:33:17 +05:30
|
|
|
Content: []string{URL},
|
|
|
|
|
Type: contentType,
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
2021-10-14 23:30:37 +02:00
|
|
|
response, err := http.Get(URL)
|
|
|
|
|
if err != nil {
|
2022-01-24 16:48:12 +05:30
|
|
|
remoteContentChannel <- RemoteContent{
|
2021-10-14 23:30:37 +02:00
|
|
|
Error: err,
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
defer response.Body.Close()
|
|
|
|
|
if response.StatusCode < 200 || response.StatusCode > 299 {
|
2022-01-24 16:48:12 +05:30
|
|
|
remoteContentChannel <- RemoteContent{
|
2021-10-14 23:30:37 +02:00
|
|
|
Error: fmt.Errorf("get \"%s\": unexpect status %d", URL, response.StatusCode),
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scanner := bufio.NewScanner(response.Body)
|
|
|
|
|
var templateList []string
|
|
|
|
|
for scanner.Scan() {
|
|
|
|
|
text := strings.TrimSpace(scanner.Text())
|
|
|
|
|
if text == "" {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2022-01-27 13:45:03 +05:30
|
|
|
if utils.IsURL(text) {
|
2022-02-07 16:41:55 +02:00
|
|
|
if err := validateRemoteTemplateURL(text, remoteTemplateDomainList); err != nil {
|
2022-01-27 13:45:03 +05:30
|
|
|
remoteContentChannel <- RemoteContent{
|
|
|
|
|
Error: err,
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-10-14 23:30:37 +02:00
|
|
|
templateList = append(templateList, text)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := scanner.Err(); err != nil {
|
2022-01-24 16:48:12 +05:30
|
|
|
remoteContentChannel <- RemoteContent{
|
2021-10-14 23:30:37 +02:00
|
|
|
Error: errors.Wrap(err, "get \"%s\""),
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-24 16:48:12 +05:30
|
|
|
remoteContentChannel <- RemoteContent{
|
2021-10-14 23:30:37 +02:00
|
|
|
Content: templateList,
|
|
|
|
|
Type: contentType,
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-01-27 13:45:03 +05:30
|
|
|
|
2022-02-07 16:41:55 +02:00
|
|
|
func validateRemoteTemplateURL(inputURL string, remoteTemplateDomainList []string) error {
|
2022-01-27 13:45:03 +05:30
|
|
|
parsedURL, err := url.Parse(inputURL)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if !utils.StringSliceContains(remoteTemplateDomainList, parsedURL.Host) {
|
|
|
|
|
return errors.Errorf("Remote template URL host (%s) is not present in the `remote-template-domain` list in nuclei config", parsedURL.Host)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|