Merge branch 'dev' of https://github.com/projectdiscovery/nuclei into more-protocols

This commit is contained in:
Ice3man543 2021-10-29 17:54:02 +05:30
commit 15d467f9ff
19 changed files with 370 additions and 82 deletions

View File

@ -90,30 +90,29 @@ TARGET:
-l, -list string path to file containing a list of target URLs/hosts to scan (one per line) -l, -list string path to file containing a list of target URLs/hosts to scan (one per line)
TEMPLATES: TEMPLATES:
-tl list all available templates
-t, -templates string[] template or template directory paths to include in the scan -t, -templates string[] template or template directory paths to include in the scan
-w, -workflows string[] list of workflows to run -nt, -new-templates run only new templates added in latest nuclei-templates release
-nt, -new-templates run newly added templates only -w, -workflows string[] workflow or workflow directory paths to include in the scan
-validate validate the passed templates to nuclei -validate validate the passed templates to nuclei
-tl list all available templates
FILTERING: FILTERING:
-tags string[] execute a subset of templates that contain the provided tags -tags string[] execute a subset of templates that contain the provided tags
-include-tags string[] tags from the default deny list that permit executing more intrusive templates -etags, -exclude-tags string[] exclude templates with the provided tags
-etags, -exclude-tags string[] exclude templates with the provided tags -itags, -include-tags string[] tags from the default deny list that permit executing more intrusive templates
-include-templates string[] templates to be executed even if they are excluded either by default or configuration -et, -exclude-templates string[] template or template directory paths to exclude
-exclude-templates, -exclude string[] template or template directory paths to exclude -it, -include-templates string[] templates to be executed even if they are excluded either by default or configuration
-severity, -impact value[] Templates to run based on severity. Possible values: info, low, medium, high, critical -s, -severity value[] Templates to run based on severity. Possible values - info,low,medium,high,critical
-author string[] execute templates that are (co-)created by the specified authors -es, -exclude-severity value[] Templates to exclude based on severity. Possible values - info,low,medium,high,critical
-a, -author string[] execute templates that are (co-)created by the specified authors
OUTPUT: OUTPUT:
-o, -output string output file to write found issues/vulnerabilities -o, -output string output file to write found issues/vulnerabilities
-silent display findings only -silent display findings only
-v, -verbose show verbose output
-vv display extra verbose information
-nc, -no-color disable output content coloring (ANSI escape codes) -nc, -no-color disable output content coloring (ANSI escape codes)
-json write output in JSONL(ines) format -json write output in JSONL(ines) format
-irr, -include-rr include request/response pairs in the JSONL output (for findings only) -irr, -include-rr include request/response pairs in the JSONL output (for findings only)
-nm, -no-meta don't display match metadata in CLI output -nm, -no-meta don't display match metadata
-nts, -no-timestamp don't display timestamp metadata in CLI output -nts, -no-timestamp don't display timestamp metadata in CLI output
-rdb, -report-db string local nuclei reporting database (always use this to persist report data) -rdb, -report-db string local nuclei reporting database (always use this to persist report data)
-me, -markdown-export string directory to export results in markdown format -me, -markdown-export string directory to export results in markdown format
@ -125,37 +124,39 @@ CONFIGURATIONS:
-H, -header string[] custom headers in header:value format -H, -header string[] custom headers in header:value format
-V, -var value custom vars in var=value format -V, -var value custom vars in var=value format
-r, -resolvers string file containing resolver list for nuclei -r, -resolvers string file containing resolver list for nuclei
-system-resolvers use system DNS resolving as error fallback -sr, -system-resolvers use system DNS resolving as error fallback
-passive enable passive HTTP response processing mode -passive enable passive HTTP response processing mode
-env-vars enable environment variables support -ev, -env-vars enable environment variables to be used in template
INTERACTSH: INTERACTSH:
-no-interactsh disable interactsh server for OOB testing -iserver, -interactsh-server string interactsh server url for self-hosted instance (default "https://interactsh.com")
-interactsh-url string interactsh server url for self-hosted instance (default "https://interactsh.com") -itoken, -interactsh-token string authentication token for self-hosted interactsh server
-interactsh-token string authentication token for self-hosted interactsh server -interactions-cache-size int number of requests to keep in the interactions cache (default 5000)
-interactions-cache-size int number of requests to keep in the interactions cache (default 5000) -interactions-eviction int number of seconds to wait before evicting requests from cache (default 60)
-interactions-eviction int number of seconds to wait before evicting requests from cache (default 60) -interactions-poll-duration int number of seconds to wait before each interaction poll request (default 5)
-interactions-poll-duration int number of seconds to wait before each interaction poll request (default 5) -interactions-cooldown-period int extra time for interaction polling before exiting (default 5)
-interactions-cooldown-period int extra time for interaction polling before exiting (default 5) -ni, -no-interactsh disable interactsh server for OAST testing, exclude OAST based templates
RATE-LIMIT: RATE-LIMIT:
-rl, -rate-limit int maximum number of requests to send per second (default 150) -rl, -rate-limit int maximum number of requests to send per second (default 150)
-rlm, -rate-limit-minute int maximum number of requests to send per minute -rlm, -rate-limit-minute int maximum number of requests to send per minute
-bs, -bulk-size int maximum number of hosts to be analyzed in parallel per template (default 25) -bs, -bulk-size int maximum number of hosts to be analyzed in parallel per template (default 25)
-c, -concurrency int maximum number of templates to be executed in parallel (default 10) -c, -concurrency int maximum number of templates to be executed in parallel (default 25)
OPTIMIZATIONS: OPTIMIZATIONS:
-timeout int time to wait in seconds before timeout (default 5) -timeout int time to wait in seconds before timeout (default 5)
-retries int number of times to retry a failed request (default 1) -retries int number of times to retry a failed request (default 1)
-max-host-error int max errors for a host before skipping from scan (default 30) -mhe, -max-host-error int max errors for a host before skipping from scan (default 30)
-project use a project folder to avoid sending same request multiple times -project use a project folder to avoid sending same request multiple times
-project-path string set a specific project path (default "$TMPDIR/") -project-path string set a specific project path
-spm, -stop-at-first-path stop processing HTTP requests after the first match (may break template/workflow logic) -spm, -stop-at-first-path stop processing HTTP requests after the first match (may break template/workflow logic)
-stream Stream mode - start elaborating without sorting the input
HEADLESS: HEADLESS:
-headless enable templates that require headless browser support -headless enable templates that require headless browser support
-page-timeout int seconds to wait for each page in headless mode (default 20) -page-timeout int seconds to wait for each page in headless mode (default 20)
-show-browser show the browser on the screen when running templates with headless mode -sb, -show-browser show the browser on the screen when running templates with headless mode
-sc, -system-chrome Use local installed chrome browser instead of nuclei installed
DEBUG: DEBUG:
-debug show all requests and responses -debug show all requests and responses
@ -163,22 +164,24 @@ DEBUG:
-debug-resp show all received responses -debug-resp show all received responses
-proxy, -proxy-url string URL of the HTTP proxy server -proxy, -proxy-url string URL of the HTTP proxy server
-proxy-socks-url string URL of the SOCKS proxy server -proxy-socks-url string URL of the SOCKS proxy server
-trace-log string file to write sent requests trace log -tlog, -trace-log string file to write sent requests trace log
-version show nuclei version -version show nuclei version
-v, -verbose show verbose output
-vv display extra verbose information
-tv, -templates-version shows the version of the installed nuclei-templates -tv, -templates-version shows the version of the installed nuclei-templates
UPDATE: UPDATE:
-update update nuclei to the latest released version -update update nuclei engine to the latest released version
-ut, -update-templates update the community templates to latest released version -ut, -update-templates update nuclei-templates to latest released version
-nut, -no-update-templates do not check for nuclei-templates updates -ud, -update-directory string overwrite the default directory to install nuclei-templates
-ud, -update-directory string overwrite the default nuclei-templates directory (default "$HOME/nuclei-templates") -duc, -disable-update-check disable automatic nuclei/templates update check
STATISTICS: STATISTICS:
-stats display statistics about the running scan -stats display statistics about the running scan
-stats-json write statistics data to an output file in JSONL(ines) format -sj, -stats-json write statistics data to an output file in JSONL(ines) format
-si, -stats-interval int number of seconds to wait between showing a statistics update (default 5) -si, -stats-interval int number of seconds to wait between showing a statistics update (default 5)
-metrics expose nuclei metrics on a port -m, -metrics expose nuclei metrics on a port
-metrics-port int port to expose nuclei metrics on (default 9092) -mp, -metrics-port int port to expose nuclei metrics on (default 9092)
``` ```
### Running Nuclei ### Running Nuclei

View File

@ -2392,6 +2392,31 @@ read-size: 2048
``` ```
</div>
<hr />
<div class="dd">
<code>read-all</code> <i>bool</i>
</div>
<div class="dt">
ReadAll determines if the data stream should be read till the end regardless of the size
Default value for read-all is false.
Examples:
```yaml
read-all: false
```
</div> </div>
<hr /> <hr />

View File

@ -0,0 +1,19 @@
id: interactsh-integration-test
info:
name: Interactsh Integration Test
author: pdteam
severity: info
requests:
- method: GET
path:
- "{{BaseURL}}"
headers:
url: 'http://{{interactsh-url}}'
matchers:
- type: word
part: interactsh_protocol # Confirms the HTTP Interaction
words:
- "http"

View File

@ -809,6 +809,11 @@
"title": "size of network response to read", "title": "size of network response to read",
"description": "Size of response to read at the end. Default is 1024 bytes" "description": "Size of response to read at the end. Default is 1024 bytes"
}, },
"read-all": {
"type": "boolean",
"title": "read all response stream",
"description": "Read all response stream till the server stops sending"
},
"matchers": { "matchers": {
"items": { "items": {
"$ref": "#/definitions/matchers.Matcher" "$ref": "#/definitions/matchers.Matcher"
@ -845,6 +850,7 @@
], ],
"properties": { "properties": {
"id": { "id": {
"pattern": "^([a-zA-Z0-9]+[-_])*[a-zA-Z0-9]+$",
"type": "string", "type": "string",
"title": "id of the template", "title": "id of the template",
"description": "The Unique ID for the template", "description": "The Unique ID for the template",

View File

@ -31,9 +31,36 @@ var httpTestcases = map[string]testutils.TestCase{
"http/raw-unsafe-request.yaml": &httpRawUnsafeRequest{}, "http/raw-unsafe-request.yaml": &httpRawUnsafeRequest{},
"http/request-condition.yaml": &httpRequestCondition{}, "http/request-condition.yaml": &httpRequestCondition{},
"http/request-condition-new.yaml": &httpRequestCondition{}, "http/request-condition-new.yaml": &httpRequestCondition{},
"http/interactsh.yaml": &httpInteractshRequest{},
"http/self-contained.yaml": &httpRequestSelContained{}, "http/self-contained.yaml": &httpRequestSelContained{},
} }
type httpInteractshRequest struct{}
// Executes executes a test case and returns an error if occurred
func (h *httpInteractshRequest) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", httprouter.Handle(func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
value := r.Header.Get("url")
if value != "" {
if resp, _ := http.DefaultClient.Get(value); resp != nil {
resp.Body.Close()
}
}
}))
ts := httptest.NewServer(router)
defer ts.Close()
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
if err != nil {
return err
}
if len(results) != 1 {
return errIncorrectResultsCount(results)
}
return nil
}
type httpGetHeaders struct{} type httpGetHeaders struct{}
// Execute executes a test case and returns an error if occurred // Execute executes a test case and returns an error if occurred

View File

@ -141,7 +141,7 @@ on extensive configurability, massive extensibility and ease of use.`)
flagSet.StringVarP(&options.TraceLogFile, "trace-log", "tlog", "", "file to write sent requests trace log"), flagSet.StringVarP(&options.TraceLogFile, "trace-log", "tlog", "", "file to write sent requests trace log"),
flagSet.BoolVar(&options.Version, "version", false, "show nuclei version"), flagSet.BoolVar(&options.Version, "version", false, "show nuclei version"),
flagSet.BoolVarP(&options.Verbose, "verbose", "v", false, "show verbose output"), flagSet.BoolVarP(&options.Verbose, "verbose", "v", false, "show verbose output"),
flagSet.BoolVar(&options.VerboseVerbose, "vv", false, "display extra verbose information"), flagSet.BoolVar(&options.VerboseVerbose, "vv", false, "display templates loaded for scan"),
flagSet.BoolVarP(&options.TemplatesVersion, "templates-version", "tv", false, "shows the version of the installed nuclei-templates"), flagSet.BoolVarP(&options.TemplatesVersion, "templates-version", "tv", false, "shows the version of the installed nuclei-templates"),
) )

View File

@ -20,6 +20,6 @@ func showBanner() {
gologger.Print().Msgf("%s\n", banner) gologger.Print().Msgf("%s\n", banner)
gologger.Print().Msgf("\t\tprojectdiscovery.io\n\n") gologger.Print().Msgf("\t\tprojectdiscovery.io\n\n")
gologger.Error().Label("WRN").Msgf("Use with caution. You are responsible for your actions.\n") gologger.Print().Label("WRN").Msgf("Use with caution. You are responsible for your actions.\n")
gologger.Error().Label("WRN").Msgf("Developers assume no liability and are not responsible for any misuse or damage.\n") gologger.Print().Label("WRN").Msgf("Developers assume no liability and are not responsible for any misuse or damage.\n")
} }

View File

@ -22,11 +22,6 @@ func ParseOptions(options *types.Options) {
// Check if stdin pipe was given // Check if stdin pipe was given
options.Stdin = hasStdin() options.Stdin = hasStdin()
// if VerboseVerbose is set, it implicitly enables the Verbose option as well
if options.VerboseVerbose {
options.Verbose = true
}
// Read the inputs and configure the logging // Read the inputs and configure the logging
configureOutput(options) configureOutput(options)
@ -127,7 +122,7 @@ func isValidURL(urlString string) bool {
// configureOutput configures the output logging levels to be displayed on the screen // configureOutput configures the output logging levels to be displayed on the screen
func configureOutput(options *types.Options) { func configureOutput(options *types.Options) {
// If the user desires verbose output, show verbose output // If the user desires verbose output, show verbose output
if options.Verbose || options.VerboseVerbose { if options.Verbose {
gologger.DefaultLogger.SetMaxLevel(levels.LevelVerbose) gologger.DefaultLogger.SetMaxLevel(levels.LevelVerbose)
} }
if options.Debug { if options.Debug {

View File

@ -26,7 +26,7 @@ type Config struct {
const nucleiConfigFilename = ".templates-config.json" const nucleiConfigFilename = ".templates-config.json"
// Version is the current version of nuclei // Version is the current version of nuclei
const Version = `2.5.3-dev` const Version = `2.5.4-dev`
func getConfigDetails() (string, error) { func getConfigDetails() (string, error) {
homeDir, err := os.UserHomeDir() homeDir, err := os.UserHomeDir()

View File

@ -5,6 +5,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"regexp" "regexp"
"strings"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
@ -17,7 +18,10 @@ import (
"github.com/projectdiscovery/nuclei/v2/pkg/utils/stats" "github.com/projectdiscovery/nuclei/v2/pkg/utils/stats"
) )
const mandatoryFieldMissingTemplate = "mandatory '%s' field is missing" const (
mandatoryFieldMissingTemplate = "mandatory '%s' field is missing"
invalidFieldFormatTemplate = "invalid field format for '%s' (allowed format is %s)"
)
// LoadTemplate returns true if the template is valid and matches the filtering criteria. // LoadTemplate returns true if the template is valid and matches the filtering criteria.
func LoadTemplate(templatePath string, tagFilter *filter.TagFilter, extraTags []string) (bool, error) { func LoadTemplate(templatePath string, tagFilter *filter.TagFilter, extraTags []string) (bool, error) {
@ -30,12 +34,12 @@ func LoadTemplate(templatePath string, tagFilter *filter.TagFilter, extraTags []
return false, nil return false, nil
} }
templateInfo := template.Info if validationError := validateTemplateFields(template); validationError != nil {
if validationError := validateMandatoryInfoFields(&templateInfo); validationError != nil { stats.Increment(SyntaxErrorStats)
return false, validationError return false, validationError
} }
return isTemplateInfoMetadataMatch(tagFilter, &templateInfo, extraTags) return isTemplateInfoMetadataMatch(tagFilter, &template.Info, extraTags)
} }
// LoadWorkflow returns true if the workflow is valid and matches the filtering criteria. // LoadWorkflow returns true if the workflow is valid and matches the filtering criteria.
@ -45,10 +49,8 @@ func LoadWorkflow(templatePath string) (bool, error) {
return false, templateParseError return false, templateParseError
} }
templateInfo := template.Info
if len(template.Workflows) > 0 { if len(template.Workflows) > 0 {
if validationError := validateMandatoryInfoFields(&templateInfo); validationError != nil { if validationError := validateTemplateFields(template); validationError != nil {
return false, validationError return false, validationError
} }
return true, nil return true, nil
@ -71,18 +73,29 @@ func isTemplateInfoMetadataMatch(tagFilter *filter.TagFilter, templateInfo *mode
return match, err return match, err
} }
func validateMandatoryInfoFields(info *model.Info) error { func validateTemplateFields(template *templates.Template) error {
if info == nil { info := template.Info
return fmt.Errorf(mandatoryFieldMissingTemplate, "info")
} var errors []string
if utils.IsBlank(info.Name) { if utils.IsBlank(info.Name) {
return fmt.Errorf(mandatoryFieldMissingTemplate, "name") errors = append(errors, fmt.Sprintf(mandatoryFieldMissingTemplate, "name"))
} }
if info.Authors.IsEmpty() { if info.Authors.IsEmpty() {
return fmt.Errorf(mandatoryFieldMissingTemplate, "author") errors = append(errors, fmt.Sprintf(mandatoryFieldMissingTemplate, "author"))
} }
if template.ID == "" {
errors = append(errors, fmt.Sprintf(mandatoryFieldMissingTemplate, "id"))
} else if !templateIDRegexp.MatchString(template.ID) {
errors = append(errors, fmt.Sprintf(invalidFieldFormatTemplate, "id", templateIDRegexp.String()))
}
if len(errors) > 0 {
return fmt.Errorf(strings.Join(errors, ", "))
}
return nil return nil
} }
@ -90,6 +103,7 @@ var (
parsedTemplatesCache *cache.Templates parsedTemplatesCache *cache.Templates
ShouldValidate bool ShouldValidate bool
fieldErrorRegexp = regexp.MustCompile(`not found in`) fieldErrorRegexp = regexp.MustCompile(`not found in`)
templateIDRegexp = regexp.MustCompile(`^([a-zA-Z0-9]+[-_])*[a-zA-Z0-9]+$`)
) )
const ( const (

View File

@ -0,0 +1,110 @@
package parsers
import (
"errors"
"fmt"
"testing"
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/loader/filter"
"github.com/projectdiscovery/nuclei/v2/pkg/model"
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/stringslice"
"github.com/projectdiscovery/nuclei/v2/pkg/templates"
"github.com/stretchr/testify/require"
)
func TestLoadTemplate(t *testing.T) {
origTemplatesCache := parsedTemplatesCache
defer func() { parsedTemplatesCache = origTemplatesCache }()
tt := []struct {
name string
template *templates.Template
templateErr error
expectedErr error
}{
{
name: "valid",
template: &templates.Template{
ID: "CVE-2021-27330",
Info: model.Info{
Name: "Valid template",
Authors: stringslice.StringSlice{Value: "Author"},
},
},
},
{
name: "emptyTemplate",
template: &templates.Template{},
expectedErr: errors.New("mandatory 'name' field is missing, mandatory 'author' field is missing, mandatory 'id' field is missing"),
},
{
name: "emptyNameWithInvalidID",
template: &templates.Template{
ID: "invalid id",
Info: model.Info{
Authors: stringslice.StringSlice{Value: "Author"},
},
},
expectedErr: errors.New("mandatory 'name' field is missing, invalid field format for 'id' (allowed format is ^([a-zA-Z0-9]+[-_])*[a-zA-Z0-9]+$)"),
},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
parsedTemplatesCache.Store(tc.name, tc.template, tc.templateErr)
tagFilter := filter.New(&filter.Config{})
success, err := LoadTemplate(tc.name, tagFilter, nil)
if tc.expectedErr == nil {
require.NoError(t, err)
require.True(t, success)
} else {
require.Equal(t, tc.expectedErr, err)
require.False(t, success)
}
})
}
t.Run("invalidTemplateID", func(t *testing.T) {
tt := []struct {
id string
success bool
}{
{id: "A-B-C", success: true},
{id: "A-B-C-1", success: true},
{id: "CVE_2021_27330", success: true},
{id: "ABC DEF", success: false},
{id: "_-__AAA_", success: false},
{id: " CVE-2021-27330", success: false},
{id: "CVE-2021-27330 ", success: false},
{id: "CVE-2021-27330-", success: false},
{id: "-CVE-2021-27330-", success: false},
{id: "CVE-2021--27330", success: false},
{id: "CVE-2021+27330", success: false},
}
for i, tc := range tt {
name := fmt.Sprintf("regexp%d", i)
t.Run(name, func(t *testing.T) {
template := &templates.Template{
ID: tc.id,
Info: model.Info{
Name: "Valid template",
Authors: stringslice.StringSlice{Value: "Author"},
},
}
parsedTemplatesCache.Store(name, template, nil)
tagFilter := filter.New(&filter.Config{})
success, err := LoadTemplate(name, tagFilter, nil)
if tc.success {
require.NoError(t, err)
require.True(t, success)
} else {
require.Equal(t, errors.New("invalid field format for 'id' (allowed format is ^([a-zA-Z0-9]+[-_])*[a-zA-Z0-9]+$)"), err)
require.False(t, success)
}
})
}
})
}

View File

@ -1,13 +1,18 @@
package engine package engine
import ( import (
"context"
"crypto/tls" "crypto/tls"
"fmt"
"net"
"net/http" "net/http"
"net/http/cookiejar"
"net/url" "net/url"
"time" "time"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/protocolstate" "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/protocolstate"
"github.com/projectdiscovery/nuclei/v2/pkg/types" "github.com/projectdiscovery/nuclei/v2/pkg/types"
"golang.org/x/net/proxy"
) )
// newhttpClient creates a new http client for headless communication with a timeout // newhttpClient creates a new http client for headless communication with a timeout
@ -28,7 +33,35 @@ func newhttpClient(options *types.Options) *http.Client {
if proxyURL, err := url.Parse(options.ProxyURL); err == nil { if proxyURL, err := url.Parse(options.ProxyURL); err == nil {
transport.Proxy = http.ProxyURL(proxyURL) transport.Proxy = http.ProxyURL(proxyURL)
} }
} else if options.ProxySocksURL != "" {
var proxyAuth *proxy.Auth
socksURL, proxyErr := url.Parse(options.ProxySocksURL)
if proxyErr == nil {
proxyAuth = &proxy.Auth{}
proxyAuth.User = socksURL.User.Username()
proxyAuth.Password, _ = socksURL.User.Password()
}
dialer, proxyErr := proxy.SOCKS5("tcp", fmt.Sprintf("%s:%s", socksURL.Hostname(), socksURL.Port()), proxyAuth, proxy.Direct)
dc := dialer.(interface {
DialContext(ctx context.Context, network, addr string) (net.Conn, error)
})
if proxyErr == nil {
transport.DialContext = dc.DialContext
}
} }
return &http.Client{Transport: transport, Timeout: time.Duration(options.Timeout*3) * time.Second} jar, _ := cookiejar.New(nil)
httpclient := &http.Client{
Transport: transport,
Timeout: time.Duration(options.Timeout*3) * time.Second,
Jar: jar,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
// the browser should follow redirects not us
return http.ErrUseLastResponse
},
}
return httpclient
} }

View File

@ -8,6 +8,9 @@ import (
// routingRuleHandler handles proxy rule for actions related to request/response modification // routingRuleHandler handles proxy rule for actions related to request/response modification
func (p *Page) routingRuleHandler(ctx *rod.Hijack) { func (p *Page) routingRuleHandler(ctx *rod.Hijack) {
// usually browsers don't use chunked transfer encoding so we set the content-length nevertheless
ctx.Request.Req().ContentLength = int64(len(ctx.Request.Body()))
for _, rule := range p.rules { for _, rule := range p.rules {
if rule.Part != "request" { if rule.Part != "request" {
continue continue

View File

@ -384,7 +384,7 @@ func (request *Request) executeRequest(reqURL string, generatedRequest *generate
}() }()
var curlCommand string var curlCommand string
if !request.Unsafe && resp != nil && generatedRequest.request != nil { if !request.Unsafe && resp != nil && generatedRequest.request != nil && resp.Request != nil {
bodyBytes, _ := generatedRequest.request.BodyBytes() bodyBytes, _ := generatedRequest.request.BodyBytes()
resp.Request.Body = ioutil.NopCloser(bytes.NewReader(bodyBytes)) resp.Request.Body = ioutil.NopCloser(bytes.NewReader(bodyBytes))
command, _ := http2curl.GetCurlCommand(resp.Request) command, _ := http2curl.GetCurlCommand(resp.Request)

View File

@ -60,6 +60,13 @@ type Request struct {
// examples: // examples:
// - value: "2048" // - value: "2048"
ReadSize int `yaml:"read-size,omitempty" jsonschema:"title=size of network response to read,description=Size of response to read at the end. Default is 1024 bytes"` ReadSize int `yaml:"read-size,omitempty" jsonschema:"title=size of network response to read,description=Size of response to read at the end. Default is 1024 bytes"`
// description: |
// ReadAll determines if the data stream should be read till the end regardless of the size
//
// Default value for read-all is false.
// examples:
// - value: false
ReadAll bool `yaml:"read-all,omitempty" jsonschema:"title=read all response stream,description=Read all response stream till the server stops sending"`
// description: | // description: |
// SelfContained specifies if the request is self contained. // SelfContained specifies if the request is self contained.

View File

@ -6,6 +6,7 @@ import (
"io" "io"
"net" "net"
"net/url" "net/url"
"os"
"strings" "strings"
"time" "time"
@ -200,13 +201,48 @@ func (request *Request) executeRequestWithPayloads(actualAddress, address, input
if request.ReadSize != 0 { if request.ReadSize != 0 {
bufferSize = request.ReadSize bufferSize = request.ReadSize
} }
final := make([]byte, bufferSize)
n, err := conn.Read(final) var (
if err != nil && err != io.EOF { final []byte
request.options.Output.Request(request.options.TemplateID, address, "network", err) n int
return errors.Wrap(err, "could not read from server") )
if request.ReadAll {
readInterval := time.NewTimer(time.Second * 1)
// stop the timer and drain the channel
closeTimer := func(t *time.Timer) {
if !t.Stop() {
<-t.C
}
}
read_socket:
for {
select {
case <-readInterval.C:
closeTimer(readInterval)
break read_socket
default:
buf := make([]byte, bufferSize)
nBuf, err := conn.Read(buf)
if err != nil && !os.IsTimeout(err) {
request.options.Output.Request(request.options.TemplateID, address, "network", err)
closeTimer(readInterval)
return errors.Wrap(err, "could not read from server")
}
responseBuilder.Write(buf[:nBuf])
final = append(final, buf...)
n += nBuf
}
}
} else {
final = make([]byte, bufferSize)
n, err = conn.Read(final)
if err != nil && err != io.EOF {
request.options.Output.Request(request.options.TemplateID, address, "network", err)
return errors.Wrap(err, "could not read from server")
}
responseBuilder.Write(final[:n])
} }
responseBuilder.Write(final[:n])
response := responseBuilder.String() response := responseBuilder.String()
outputEvent := request.responseToDSLMap(reqBuilder.String(), string(final[:n]), response, input, actualAddress) outputEvent := request.responseToDSLMap(reqBuilder.String(), string(final[:n]), response, input, actualAddress)

View File

@ -58,6 +58,9 @@ func New(options *Options) (*Integration, error) {
if err != nil { if err != nil {
return nil, errors.Wrap(err, "could not parse custom baseurl") return nil, errors.Wrap(err, "could not parse custom baseurl")
} }
if !strings.HasSuffix(parsed.Path, "/") {
parsed.Path += "/"
}
client.BaseURL = parsed client.BaseURL = parsed
} }
return &Integration{client: client, options: options}, nil return &Integration{client: client, options: options}, nil

View File

@ -29,7 +29,7 @@ type Template struct {
// examples: // examples:
// - name: ID Example // - name: ID Example
// value: "\"CVE-2021-19520\"" // value: "\"CVE-2021-19520\""
ID string `yaml:"id" jsonschema:"title=id of the template,description=The Unique ID for the template,example=cve-2021-19520"` ID string `yaml:"id" jsonschema:"title=id of the template,description=The Unique ID for the template,example=cve-2021-19520,pattern=^([a-zA-Z0-9]+[-_])*[a-zA-Z0-9]+$"`
// description: | // description: |
// Info contains metadata information about the template. // Info contains metadata information about the template.
// examples: // examples:

View File

@ -843,7 +843,7 @@ func init() {
FieldName: "network", FieldName: "network",
}, },
} }
NETWORKRequestDoc.Fields = make([]encoder.Doc, 9) NETWORKRequestDoc.Fields = make([]encoder.Doc, 10)
NETWORKRequestDoc.Fields[0].Name = "id" NETWORKRequestDoc.Fields[0].Name = "id"
NETWORKRequestDoc.Fields[0].Type = "string" NETWORKRequestDoc.Fields[0].Type = "string"
NETWORKRequestDoc.Fields[0].Note = "" NETWORKRequestDoc.Fields[0].Note = ""
@ -883,22 +883,29 @@ func init() {
NETWORKRequestDoc.Fields[5].Comments[encoder.LineComment] = "ReadSize is the size of response to read at the end" NETWORKRequestDoc.Fields[5].Comments[encoder.LineComment] = "ReadSize is the size of response to read at the end"
NETWORKRequestDoc.Fields[5].AddExample("", 2048) NETWORKRequestDoc.Fields[5].AddExample("", 2048)
NETWORKRequestDoc.Fields[6].Name = "matchers" NETWORKRequestDoc.Fields[6].Name = "read-all"
NETWORKRequestDoc.Fields[6].Type = "[]matchers.Matcher" NETWORKRequestDoc.Fields[6].Type = "bool"
NETWORKRequestDoc.Fields[6].Note = "" NETWORKRequestDoc.Fields[6].Note = ""
NETWORKRequestDoc.Fields[6].Description = "Matchers contains the detection mechanism for the request to identify\nwhether the request was successful by doing pattern matching\non request/responses.\n\nMultiple matchers can be combined with `matcher-condition` flag\nwhich accepts either `and` or `or` as argument." NETWORKRequestDoc.Fields[6].Description = "ReadAll determines if the data stream should be read till the end regardless of the size\n\nDefault value for read-all is false."
NETWORKRequestDoc.Fields[6].Comments[encoder.LineComment] = "Matchers contains the detection mechanism for the request to identify" NETWORKRequestDoc.Fields[6].Comments[encoder.LineComment] = "ReadAll determines if the data stream should be read till the end regardless of the size"
NETWORKRequestDoc.Fields[7].Name = "extractors"
NETWORKRequestDoc.Fields[7].Type = "[]extractors.Extractor" NETWORKRequestDoc.Fields[6].AddExample("", false)
NETWORKRequestDoc.Fields[7].Name = "matchers"
NETWORKRequestDoc.Fields[7].Type = "[]matchers.Matcher"
NETWORKRequestDoc.Fields[7].Note = "" NETWORKRequestDoc.Fields[7].Note = ""
NETWORKRequestDoc.Fields[7].Description = "Extractors contains the extraction mechanism for the request to identify\nand extract parts of the response." NETWORKRequestDoc.Fields[7].Description = "Matchers contains the detection mechanism for the request to identify\nwhether the request was successful by doing pattern matching\non request/responses.\n\nMultiple matchers can be combined with `matcher-condition` flag\nwhich accepts either `and` or `or` as argument."
NETWORKRequestDoc.Fields[7].Comments[encoder.LineComment] = "Extractors contains the extraction mechanism for the request to identify" NETWORKRequestDoc.Fields[7].Comments[encoder.LineComment] = "Matchers contains the detection mechanism for the request to identify"
NETWORKRequestDoc.Fields[8].Name = "matchers-condition" NETWORKRequestDoc.Fields[8].Name = "extractors"
NETWORKRequestDoc.Fields[8].Type = "string" NETWORKRequestDoc.Fields[8].Type = "[]extractors.Extractor"
NETWORKRequestDoc.Fields[8].Note = "" NETWORKRequestDoc.Fields[8].Note = ""
NETWORKRequestDoc.Fields[8].Description = "MatchersCondition is the condition between the matchers. Default is OR." NETWORKRequestDoc.Fields[8].Description = "Extractors contains the extraction mechanism for the request to identify\nand extract parts of the response."
NETWORKRequestDoc.Fields[8].Comments[encoder.LineComment] = "MatchersCondition is the condition between the matchers. Default is OR." NETWORKRequestDoc.Fields[8].Comments[encoder.LineComment] = "Extractors contains the extraction mechanism for the request to identify"
NETWORKRequestDoc.Fields[8].Values = []string{ NETWORKRequestDoc.Fields[9].Name = "matchers-condition"
NETWORKRequestDoc.Fields[9].Type = "string"
NETWORKRequestDoc.Fields[9].Note = ""
NETWORKRequestDoc.Fields[9].Description = "MatchersCondition is the condition between the matchers. Default is OR."
NETWORKRequestDoc.Fields[9].Comments[encoder.LineComment] = "MatchersCondition is the condition between the matchers. Default is OR."
NETWORKRequestDoc.Fields[9].Values = []string{
"and", "and",
"or", "or",
} }