Compare commits
10 Commits
e3e4e6250d
...
1cd61646ff
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1cd61646ff | ||
|
|
75b2c01f88 | ||
|
|
7dcecc3f85 | ||
|
|
9087fe7fd2 | ||
| 7cdf8d3d77 | |||
|
|
de9ac86677 | ||
|
|
0e024f4208 | ||
|
|
6487328cd8 | ||
|
|
7a2756a8f3 | ||
|
|
36124a1afe |
10
CHANGELOG.md
10
CHANGELOG.md
@ -1,16 +1,26 @@
|
||||
## Changelog
|
||||
- master
|
||||
- New
|
||||
- Changed
|
||||
- Fix a bug in autocalibration strategy merging, when two files have the same strategy key
|
||||
- Fix panic when setting rate to 0 in the interactive console
|
||||
|
||||
- v2.1.0
|
||||
- New
|
||||
- autocalibration-strategy refactored to support extensible strategy configuration
|
||||
- New cli flag `-raw` to omit urlencoding for URIs
|
||||
- New cli flags `-ck` and `-cc` to enable the use of client side certificate authentication
|
||||
- Integration with `github.com/ffuf/pencode` library, added `-enc` cli flag to do various in-fly encodings for input data
|
||||
- Changed
|
||||
- Fix multiline output
|
||||
- Explicitly allow TLS1.0
|
||||
- Fix markdown output file format
|
||||
- Fix csv output file format
|
||||
- Fixed divide by 0 error when setting rate limit to 0 manually.
|
||||
- Automatic brotli and deflate decompression
|
||||
- Report if request times out when a time based matcher or filter is active
|
||||
- All 2XX status codes are now matched
|
||||
- Allow adding "unused" wordlists in config file
|
||||
|
||||
- v2.0.0
|
||||
- New
|
||||
|
||||
10
README.md
10
README.md
@ -150,16 +150,19 @@ parameter.
|
||||
To define the test case for ffuf, use the keyword `FUZZ` anywhere in the URL (`-u`), headers (`-H`), or POST data (`-d`).
|
||||
|
||||
```
|
||||
Fuzz Faster U Fool - v2.0.0
|
||||
Fuzz Faster U Fool - v2.1.0
|
||||
|
||||
HTTP OPTIONS:
|
||||
-H Header `"Name: Value"`, separated by colon. Multiple -H flags are accepted.
|
||||
-X HTTP method to use
|
||||
-b Cookie data `"NAME1=VALUE1; NAME2=VALUE2"` for copy as curl functionality.
|
||||
-cc Client cert for authentication. Client key needs to be defined as well for this to work
|
||||
-ck Client key for authentication. Client certificate needs to be defined as well for this to work
|
||||
-d POST data
|
||||
-http2 Use HTTP2 protocol (default: false)
|
||||
-ignore-body Do not fetch the response content. (default: false)
|
||||
-r Follow redirects (default: false)
|
||||
-raw Do not encode URI (default: false)
|
||||
-recursion Scan recursively. Only FUZZ keyword is supported, and URL (-u) has to end in it. (default: false)
|
||||
-recursion-depth Maximum recursion depth. (default: 0)
|
||||
-recursion-strategy Recursion strategy: "default" for a redirect based, and "greedy" to recurse on all matches (default: default)
|
||||
@ -175,7 +178,7 @@ GENERAL OPTIONS:
|
||||
-acc Custom auto-calibration string. Can be used multiple times. Implies -ac
|
||||
-ach Per host autocalibration (default: false)
|
||||
-ack Autocalibration keyword (default: FUZZ)
|
||||
-acs Autocalibration strategy: "basic" or "advanced" (default: basic)
|
||||
-acs Custom auto-calibration strategies. Can be used multiple times. Implies -ac
|
||||
-c Colorize output. (default: false)
|
||||
-config Load configuration from a file
|
||||
-json JSON output, printing newline-delimited JSON records (default: false)
|
||||
@ -195,7 +198,7 @@ GENERAL OPTIONS:
|
||||
-v Verbose output, printing full URL and redirect location (if any) with the results. (default: false)
|
||||
|
||||
MATCHER OPTIONS:
|
||||
-mc Match HTTP status codes, or "all" for everything. (default: 200,204,301,302,307,401,403,405,500)
|
||||
-mc Match HTTP status codes, or "all" for everything. (default: 200-299,301,302,307,401,403,405,500)
|
||||
-ml Match amount of lines in response
|
||||
-mmode Matcher set operator. Either of: and, or (default: or)
|
||||
-mr Match regexp
|
||||
@ -215,6 +218,7 @@ FILTER OPTIONS:
|
||||
INPUT OPTIONS:
|
||||
-D DirSearch wordlist compatibility mode. Used in conjunction with -e flag. (default: false)
|
||||
-e Comma separated list of extensions. Extends FUZZ keyword.
|
||||
-enc Encoders for keywords, eg. 'FUZZ:urlencode b64encode'
|
||||
-ic Ignore wordlist comments (default: false)
|
||||
-input-cmd Command producing the input. --input-num is required when using this input method. Overrides -w.
|
||||
-input-num Number of inputs to test. Used in conjunction with --input-cmd. (default: 100)
|
||||
|
||||
22
default.nix
Normal file
22
default.nix
Normal file
@ -0,0 +1,22 @@
|
||||
{ pkgs ? (
|
||||
let
|
||||
inherit (builtins) fetchTree fromJSON readFile;
|
||||
inherit ((fromJSON (readFile ./flake.lock)).nodes) nixpkgs gomod2nix;
|
||||
in
|
||||
import (fetchTree nixpkgs.locked) {
|
||||
overlays = [
|
||||
(import "${fetchTree gomod2nix.locked}/overlay.nix")
|
||||
];
|
||||
}
|
||||
)
|
||||
, buildGoApplication ? pkgs.buildGoApplication
|
||||
}:
|
||||
|
||||
buildGoApplication {
|
||||
# TODO: Change name to ffuff to avoid conflicts
|
||||
pname = "ffuf";
|
||||
version = "0.1";
|
||||
pwd = ./.;
|
||||
src = ./.;
|
||||
modules = ./gomod2nix.toml;
|
||||
}
|
||||
85
flake.lock
Normal file
85
flake.lock
Normal file
@ -0,0 +1,85 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gomod2nix": {
|
||||
"inputs": {
|
||||
"flake-utils": [
|
||||
"flake-utils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1733668782,
|
||||
"narHash": "sha256-tPsqU00FhgdFr0JiQUiBMgPVbl1jbPCY5gbFiJycL3I=",
|
||||
"owner": "nix-community",
|
||||
"repo": "gomod2nix",
|
||||
"rev": "514283ec89c39ad0079ff2f3b1437404e4cba608",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "gomod2nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1738546358,
|
||||
"narHash": "sha256-nLivjIygCiqLp5QcL7l56Tca/elVqM9FG1hGd9ZSsrg=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "c6e957d81b96751a3d5967a0fd73694f303cc914",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"gomod2nix": "gomod2nix",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
29
flake.nix
Normal file
29
flake.nix
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
description = "A basic gomod2nix flake";
|
||||
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
inputs.flake-utils.url = "github:numtide/flake-utils";
|
||||
inputs.gomod2nix.url = "github:nix-community/gomod2nix";
|
||||
inputs.gomod2nix.inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.gomod2nix.inputs.flake-utils.follows = "flake-utils";
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils, gomod2nix }:
|
||||
(flake-utils.lib.eachDefaultSystem
|
||||
(system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
|
||||
# The current default sdk for macOS fails to compile go projects, so we use a newer one for now.
|
||||
# This has no effect on other platforms.
|
||||
callPackage = pkgs.darwin.apple_sdk_11_0.callPackage or pkgs.callPackage;
|
||||
in
|
||||
{
|
||||
packages.default = callPackage ./. {
|
||||
inherit (gomod2nix.legacyPackages.${system}) buildGoApplication;
|
||||
};
|
||||
devShells.default = callPackage ./shell.nix {
|
||||
inherit (gomod2nix.legacyPackages.${system}) mkGoEnv gomod2nix;
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
30
gomod2nix.toml
Normal file
30
gomod2nix.toml
Normal file
@ -0,0 +1,30 @@
|
||||
schema = 3
|
||||
|
||||
[mod]
|
||||
[mod."github.com/PuerkitoBio/goquery"]
|
||||
version = "v1.8.0"
|
||||
hash = "sha256-I3QaPWATvBOL/F26fIiYWKS13yBUYo+9o3tcsGIu8tY="
|
||||
[mod."github.com/adrg/xdg"]
|
||||
version = "v0.4.0"
|
||||
hash = "sha256-zGjkdUQmrVqD6rMO9oDY+TeJCpuqnHyvkPCaXDlac/U="
|
||||
[mod."github.com/andybalholm/brotli"]
|
||||
version = "v1.0.5"
|
||||
hash = "sha256-/qS8wU8yZQJ+uTOg66rEl9s7spxq9VIXF5L1BcaEClc="
|
||||
[mod."github.com/andybalholm/cascadia"]
|
||||
version = "v1.3.1"
|
||||
hash = "sha256-M0u22DXSeXUaYtl1KoW1qWL46niFpycFkraCEQ/luYA="
|
||||
[mod."github.com/davecgh/go-spew"]
|
||||
version = "v1.1.1"
|
||||
hash = "sha256-nhzSUrE1fCkN0+RL04N4h8jWmRFPPPWbCuDc7Ss0akI="
|
||||
[mod."github.com/ffuf/pencode"]
|
||||
version = "v0.0.0-20230421231718-2cea7e60a693"
|
||||
hash = "sha256-/ysCCAHXmBBQC8MojiRbmbZSWo5gUE3u9AuCV24ul18="
|
||||
[mod."github.com/pelletier/go-toml"]
|
||||
version = "v1.9.5"
|
||||
hash = "sha256-RJ9K1BTId0Mled7S66iGgxHkZ5JKEIsrrNaEfM8aImc="
|
||||
[mod."golang.org/x/net"]
|
||||
version = "v0.7.0"
|
||||
hash = "sha256-LgZYZRwtMqm+soNh+esxDSeRuIDxRGb9OEfYaFJHCDI="
|
||||
[mod."golang.org/x/sys"]
|
||||
version = "v0.5.0"
|
||||
hash = "sha256-0LTr3KeJ1OMQAwYUQo1513dXJtQAJn5Dq8sFkc8ps1U="
|
||||
4
main.go
4
main.go
@ -51,7 +51,7 @@ func (m *wordlistFlag) Set(value string) error {
|
||||
func ParseFlags(opts *ffuf.ConfigOptions) *ffuf.ConfigOptions {
|
||||
var ignored bool
|
||||
|
||||
var cookies, autocalibrationstrings, autocalibrationstrategies, headers, inputcommands multiStringFlag
|
||||
var cookies, autocalibrationstrings, autocalibrationstrategies, headers, inputcommands multiStringFlag
|
||||
var wordlists, encoders wordlistFlag
|
||||
|
||||
cookies = opts.HTTP.Cookies
|
||||
@ -144,7 +144,7 @@ func ParseFlags(opts *ffuf.ConfigOptions) *ffuf.ConfigOptions {
|
||||
|
||||
opts.General.AutoCalibrationStrings = autocalibrationstrings
|
||||
if len(autocalibrationstrategies) > 0 {
|
||||
opts.General.AutoCalibrationStrategies = []string {}
|
||||
opts.General.AutoCalibrationStrategies = []string{}
|
||||
for _, strategy := range autocalibrationstrategies {
|
||||
opts.General.AutoCalibrationStrategies = append(opts.General.AutoCalibrationStrategies, strings.Split(strategy, ",")...)
|
||||
}
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
package ffuf
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
"encoding/json"
|
||||
"path/filepath"
|
||||
"os"
|
||||
)
|
||||
|
||||
type AutocalibrationStrategy map[string][]string
|
||||
@ -20,9 +20,9 @@ func (j *Job) autoCalibrationStrings() map[string][]string {
|
||||
if len(j.Config.AutoCalibrationStrings) > 0 {
|
||||
cInputs["custom"] = append(cInputs["custom"], j.Config.AutoCalibrationStrings...)
|
||||
return cInputs
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
for _, strategy := range j.Config.AutoCalibrationStrategies {
|
||||
jsonStrategy, err := os.ReadFile(filepath.Join(AUTOCALIBDIR, strategy+".json"))
|
||||
if err != nil {
|
||||
@ -36,36 +36,36 @@ func (j *Job) autoCalibrationStrings() map[string][]string {
|
||||
j.Output.Warning(fmt.Sprintf("Skipping strategy \"%s\" because of error: %s\n", strategy, err))
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
cInputs = mergeMaps(cInputs, tmpStrategy)
|
||||
}
|
||||
|
||||
|
||||
return cInputs
|
||||
}
|
||||
|
||||
func setupDefaultAutocalibrationStrategies() error {
|
||||
basic_strategy := AutocalibrationStrategy {
|
||||
"basic_admin": []string{"admin"+RandomString(16), "admin"+RandomString(8)},
|
||||
"htaccess": []string{".htaccess"+RandomString(16), ".htaccess"+RandomString(8)},
|
||||
basic_strategy := AutocalibrationStrategy{
|
||||
"basic_admin": []string{"admin" + RandomString(16), "admin" + RandomString(8)},
|
||||
"htaccess": []string{".htaccess" + RandomString(16), ".htaccess" + RandomString(8)},
|
||||
"basic_random": []string{RandomString(16), RandomString(8)},
|
||||
}
|
||||
basic_strategy_json, err := json.Marshal(basic_strategy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
advanced_strategy := AutocalibrationStrategy {
|
||||
"basic_admin": []string{"admin"+RandomString(16), "admin"+RandomString(8)},
|
||||
"htaccess": []string{".htaccess"+RandomString(16), ".htaccess"+RandomString(8)},
|
||||
|
||||
advanced_strategy := AutocalibrationStrategy{
|
||||
"basic_admin": []string{"admin" + RandomString(16), "admin" + RandomString(8)},
|
||||
"htaccess": []string{".htaccess" + RandomString(16), ".htaccess" + RandomString(8)},
|
||||
"basic_random": []string{RandomString(16), RandomString(8)},
|
||||
"admin_dir": []string{"admin"+RandomString(16)+"/", "admin"+RandomString(8)+"/"},
|
||||
"random_dir": []string{RandomString(16)+"/", RandomString(8)+"/"},
|
||||
"admin_dir": []string{"admin" + RandomString(16) + "/", "admin" + RandomString(8) + "/"},
|
||||
"random_dir": []string{RandomString(16) + "/", RandomString(8) + "/"},
|
||||
}
|
||||
advanced_strategy_json, err := json.Marshal(advanced_strategy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
basic_strategy_file := filepath.Join(AUTOCALIBDIR, "basic.json")
|
||||
if !FileExists(basic_strategy_file) {
|
||||
err = os.WriteFile(filepath.Join(AUTOCALIBDIR, "basic.json"), basic_strategy_json, 0640)
|
||||
@ -76,7 +76,7 @@ func setupDefaultAutocalibrationStrategies() error {
|
||||
err = os.WriteFile(filepath.Join(AUTOCALIBDIR, "advanced.json"), advanced_strategy_json, 0640)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
108
pkg/ffuf/autocalibration_test.go
Normal file
108
pkg/ffuf/autocalibration_test.go
Normal file
@ -0,0 +1,108 @@
|
||||
package ffuf
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// NullOutput is a dummy output provider that does nothing
|
||||
type NullOutput struct {
|
||||
Results []Result
|
||||
}
|
||||
|
||||
func NewNullOutput() *NullOutput { return &NullOutput{} }
|
||||
func (o *NullOutput) Banner() {}
|
||||
func (o *NullOutput) Finalize() error { return nil }
|
||||
func (o *NullOutput) Progress(status Progress) {}
|
||||
func (o *NullOutput) Info(infostring string) {}
|
||||
func (o *NullOutput) Error(errstring string) {}
|
||||
func (o *NullOutput) Raw(output string) {}
|
||||
func (o *NullOutput) Warning(warnstring string) {}
|
||||
func (o *NullOutput) Result(resp Response) {}
|
||||
func (o *NullOutput) PrintResult(res Result) {}
|
||||
func (o *NullOutput) SaveFile(filename, format string) error { return nil }
|
||||
func (o *NullOutput) GetCurrentResults() []Result { return o.Results }
|
||||
func (o *NullOutput) SetCurrentResults(results []Result) { o.Results = results }
|
||||
func (o *NullOutput) Reset() {}
|
||||
func (o *NullOutput) Cycle() {}
|
||||
|
||||
func TestAutoCalibrationStrings(t *testing.T) {
|
||||
// Create a temporary directory for the test
|
||||
tmpDir, err := os.MkdirTemp("", "ffuf-test")
|
||||
AUTOCALIBDIR = tmpDir
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temporary directory: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
// Create a test strategy file
|
||||
strategy := AutocalibrationStrategy{
|
||||
"test": {"foo", "bar"},
|
||||
}
|
||||
strategyJSON, err := json.Marshal(strategy)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal strategy to JSON: %v", err)
|
||||
}
|
||||
strategyFile := filepath.Join(tmpDir, "test.json")
|
||||
err = os.WriteFile(strategyFile, strategyJSON, 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write strategy file: %v", err)
|
||||
}
|
||||
|
||||
// Create a test job with the strategy
|
||||
job := &Job{
|
||||
Config: &Config{
|
||||
AutoCalibrationStrategies: []string{"test"},
|
||||
},
|
||||
Output: NewNullOutput(),
|
||||
}
|
||||
cInputs := job.autoCalibrationStrings()
|
||||
|
||||
// Verify that the custom strategy was added
|
||||
if len(cInputs["custom"]) != 0 {
|
||||
t.Errorf("Expected custom strategy to be empty, but got %v", cInputs["custom"])
|
||||
}
|
||||
|
||||
// Verify that the test strategy was added
|
||||
expected := []string{"foo", "bar"}
|
||||
if len(cInputs["test"]) != len(expected) {
|
||||
t.Errorf("Expected test strategy to have %d inputs, but got %d", len(expected), len(cInputs["test"]))
|
||||
}
|
||||
for i, input := range cInputs["test"] {
|
||||
if input != expected[i] {
|
||||
t.Errorf("Expected test strategy input %d to be %q, but got %q", i, expected[i], input)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that a missing strategy is skipped
|
||||
job = &Job{
|
||||
Config: &Config{
|
||||
AutoCalibrationStrategies: []string{"missing"},
|
||||
},
|
||||
Output: NewNullOutput(),
|
||||
}
|
||||
cInputs = job.autoCalibrationStrings()
|
||||
if len(cInputs) != 0 {
|
||||
t.Errorf("Expected missing strategy to be skipped, but got %v", cInputs)
|
||||
}
|
||||
|
||||
// Verify that a malformed strategy is skipped
|
||||
malformedStrategy := []byte(`{"test": "foo"}`)
|
||||
malformedFile := filepath.Join(tmpDir, "malformed.json")
|
||||
err = os.WriteFile(malformedFile, malformedStrategy, 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write malformed strategy file: %v", err)
|
||||
}
|
||||
job = &Job{
|
||||
Config: &Config{
|
||||
AutoCalibrationStrategies: []string{"malformed"},
|
||||
},
|
||||
Output: NewNullOutput(),
|
||||
}
|
||||
cInputs = job.autoCalibrationStrings()
|
||||
if len(cInputs) != 0 {
|
||||
t.Errorf("Expected malformed strategy to be skipped, but got %v", cInputs)
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
var (
|
||||
//VERSION holds the current version number
|
||||
VERSION = "2.0.0"
|
||||
VERSION = "2.1.0"
|
||||
//VERSION_APPENDIX holds additional version definition
|
||||
VERSION_APPENDIX = "-dev"
|
||||
CONFIGDIR = filepath.Join(xdg.ConfigHome, "ffuf")
|
||||
|
||||
@ -347,6 +347,7 @@ func (j *Job) isMatch(resp Response) bool {
|
||||
}
|
||||
if match {
|
||||
matched = true
|
||||
fmt.Printf("%s\n", resp.Data)
|
||||
} else if j.Config.MatcherMode == "and" {
|
||||
// we already know this isn't "and" match
|
||||
return false
|
||||
|
||||
@ -170,7 +170,7 @@ func NewConfigOptions() *ConfigOptions {
|
||||
c.Matcher.Lines = ""
|
||||
c.Matcher.Regexp = ""
|
||||
c.Matcher.Size = ""
|
||||
c.Matcher.Status = "200,204,301,302,307,401,403,405,500"
|
||||
c.Matcher.Status = "200-299,301,302,307,401,403,405,500"
|
||||
c.Matcher.Time = ""
|
||||
c.Matcher.Words = ""
|
||||
c.Output.DebugLog = ""
|
||||
@ -373,7 +373,6 @@ func ConfigFromOptions(parseOpts *ConfigOptions, ctx context.Context, cancel con
|
||||
conf.ClientKey = parseOpts.HTTP.ClientKey
|
||||
}
|
||||
|
||||
|
||||
//Prepare headers and make canonical
|
||||
for _, v := range parseOpts.HTTP.Headers {
|
||||
hs := strings.SplitN(v, ":", 2)
|
||||
|
||||
@ -19,15 +19,13 @@ func NewRateThrottle(conf *Config) *RateThrottle {
|
||||
Config: conf,
|
||||
lastAdjustment: time.Now(),
|
||||
}
|
||||
|
||||
if conf.Rate > 0 {
|
||||
r.rateCounter = ring.New(int(conf.Rate * 5))
|
||||
} else {
|
||||
r.rateCounter = ring.New(conf.Threads * 5)
|
||||
}
|
||||
if conf.Rate > 0 {
|
||||
ratemicros := 1000000 / conf.Rate
|
||||
r.RateLimiter = time.NewTicker(time.Microsecond * time.Duration(ratemicros))
|
||||
} else {
|
||||
r.rateCounter = ring.New(conf.Threads * 5)
|
||||
//Million rps is probably a decent hardcoded upper speedlimit
|
||||
r.RateLimiter = time.NewTicker(time.Microsecond * 1)
|
||||
}
|
||||
@ -72,10 +70,17 @@ func (r *RateThrottle) ChangeRate(rate int) {
|
||||
}
|
||||
|
||||
r.RateLimiter.Stop()
|
||||
r.RateLimiter = time.NewTicker(time.Microsecond * time.Duration(ratemicros))
|
||||
if rate > 0 {
|
||||
r.RateLimiter = time.NewTicker(time.Microsecond * time.Duration(ratemicros))
|
||||
// reset the rate counter
|
||||
r.rateCounter = ring.New(rate * 5)
|
||||
} else {
|
||||
r.RateLimiter = time.NewTicker(time.Microsecond * 1)
|
||||
// reset the rate counter
|
||||
r.rateCounter = ring.New(r.Config.Threads * 5)
|
||||
}
|
||||
|
||||
r.Config.Rate = int64(rate)
|
||||
// reset the rate counter
|
||||
r.rateCounter = ring.New(rate * 5)
|
||||
}
|
||||
|
||||
// rateTick adds a new duration measurement tick to rate counter
|
||||
|
||||
@ -131,7 +131,16 @@ func mergeMaps(m1 map[string][]string, m2 map[string][]string) map[string][]stri
|
||||
merged[k] = v
|
||||
}
|
||||
for key, value := range m2 {
|
||||
merged[key] = value
|
||||
if _, ok := merged[key]; !ok {
|
||||
// Key not found, add it
|
||||
merged[key] = value
|
||||
continue
|
||||
}
|
||||
for _, entry := range value {
|
||||
if !StrInSlice(entry, merged[key]) {
|
||||
merged[key] = append(merged[key], entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
return merged
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ import (
|
||||
|
||||
func TestToCSV(t *testing.T) {
|
||||
result := ffuf.Result{
|
||||
Input: map[string][]byte{"x": {66}},
|
||||
Input: map[string][]byte{"x": {66}, "FFUFHASH": {65}},
|
||||
Position: 1,
|
||||
StatusCode: 200,
|
||||
ContentLength: 3,
|
||||
@ -37,8 +37,8 @@ func TestToCSV(t *testing.T) {
|
||||
"5",
|
||||
"application/json",
|
||||
"123ns",
|
||||
"resultfile"}) {
|
||||
|
||||
"resultfile",
|
||||
"A"}) {
|
||||
t.Errorf("CSV was not generated in expected format")
|
||||
}
|
||||
}
|
||||
|
||||
24
shell.nix
Normal file
24
shell.nix
Normal file
@ -0,0 +1,24 @@
|
||||
{ pkgs ? (
|
||||
let
|
||||
inherit (builtins) fetchTree fromJSON readFile;
|
||||
inherit ((fromJSON (readFile ./flake.lock)).nodes) nixpkgs gomod2nix;
|
||||
in
|
||||
import (fetchTree nixpkgs.locked) {
|
||||
overlays = [
|
||||
(import "${fetchTree gomod2nix.locked}/overlay.nix")
|
||||
];
|
||||
}
|
||||
)
|
||||
, mkGoEnv ? pkgs.mkGoEnv
|
||||
, gomod2nix ? pkgs.gomod2nix
|
||||
}:
|
||||
|
||||
let
|
||||
goEnv = mkGoEnv { pwd = ./.; };
|
||||
in
|
||||
pkgs.mkShell {
|
||||
packages = [
|
||||
goEnv
|
||||
gomod2nix
|
||||
];
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user