From fa56800fcca3c8c8fe8e8dec28584210ca1527de Mon Sep 17 00:00:00 2001 From: Ice3man Date: Thu, 14 Mar 2024 03:08:53 +0530 Subject: [PATCH] Fuzzing layer enhancements + input-types support (#4477) * feat: move fuzz package to root directory * feat: added support for input providers like openapi,postman,etc * feat: integration of new fuzzing logic in engine * bugfix: use and instead of or * fixed lint errors * go mod tidy * add new reqresp type + bump utils * custom http request parser * use new struct type RequestResponse * introduce unified input/target provider * abstract input formats via new inputprovider * completed input provider refactor * remove duplicated code * add sdk method to load targets * rename component url->path * add new yaml format + remove duplicated code * use gopkg.in/yaml.v3 for parsing * update .gitignore * refactor/move + docs fuzzing in http protocol * fuzz: header + query integration test using fuzzplayground * fix integration test runner in windows * feat add support for filter in http fuzz * rewrite header/query integration test with filter * add replace regex rule * support kv fuzzing + misc updates * add path fuzzing example + misc improvements * fix matchedURL + skip httpx on multi formats * cookie fuzz integration test * add json body + params body tests * feat add multipart/form-data fuzzing support * add all fuzz body integration test * misc bug fixes + minor refactor * add multipart form + body form unit tests * only run fuzzing templates if -fuzz flag is given * refactor/move fuzz playground server to pkg * fix integration test + refactor * add auth types and strategies * add file auth provider * start implementing auth logic in http * add logic in http protocol * static auth implemented for http * default :80,:443 normalization * feat: dynamic auth init * feat: dynamic auth using templates * validate targets count in openapi+swagger * inputformats: add support to accept variables * fix workflow integration test * update lazy cred fetch logic * fix unit test * drop postman support * domain related normalization * update secrets.yaml file format + misc updates * add auth prefetch option * remove old secret files * add fuzzing+auth related sdk options * fix/support multiple mode in kv header fuzzing * rename 'headers' -> 'header' in fuzzing rules * fix deadlock due to merge conflict resolution * misc update * add bool type in parsed value * add openapi validation+override+ new flags * misc updates * remove optional path parameters when unavailable * fix swagger.yaml file * misc updates * update print msg * multiple openapi validation enchancements + appMode * add optional params in required_openapi_vars.yaml file * improve warning/verbose msgs in format * fix skip-format-validation not working * use 'params/parameter' instead of 'variable' in openapi * add retry support for falky tests * fix nuclei loading ignored templates (#4849) * fix tag include logic * fix unit test * remove quoting in extractor output * remove quote in debug code command * feat: issue tracker URLs in JSON + misc fixes (#4855) * feat: issue tracker URLs in JSON + misc fixes * misc changes * feat: status update support for issues * feat: report metadata generation hook support * feat: added CLI summary of tickets created * misc changes * introduce `disable-unsigned-templates` flag (#4820) * introduce `disable-unsigned-templates` flag * minor * skip instead of exit * remove duplicate imports * use stats package + misc enhancements * force display warning + adjust skipped stats in unsigned count * include unsigned skipped templates without -dut flag --------- Co-authored-by: Tarun Koyalwar * Purge cache on global callback set (#4840) * purge cache on global callback set * lint * purging cache * purge cache in runner after loading templates * include internal cache from parsers + add global cache register/purge via config * remove disable cache purge option --------- Co-authored-by: Tarun Koyalwar * misc update * add application/octet-stream support * openapi: support path specific params * misc option + readme update --------- Co-authored-by: Sandeep Singh Co-authored-by: sandeep <8293321+ehsandeep@users.noreply.github.com> Co-authored-by: Tarun Koyalwar Co-authored-by: Tarun Koyalwar <45962551+tarunKoyalwar@users.noreply.github.com> Co-authored-by: Dogan Can Bakir <65292895+dogancanbakir@users.noreply.github.com> Co-authored-by: Mzack9999 --- .gitignore | 7 +- Makefile | 2 + README.md | 39 +- cmd/integration-test/fuzz.go | 39 +- cmd/integration-test/integration-test.go | 20 + cmd/integration-test/library.go | 6 +- cmd/integration-test/workflow.go | 2 +- cmd/nuclei/main.go | 28 +- cmd/tools/fuzzplayground/main.go | 101 +-- go.mod | 33 +- go.sum | 74 ++- .../fuzz/fuzz-body-generic-sqli.yaml | 39 ++ .../fuzz/fuzz-body-json-sqli.yaml | 40 ++ .../fuzz/fuzz-body-multipart-form-sqli.yaml | 41 ++ .../fuzz/fuzz-body-params-sqli.yaml | 41 ++ .../fuzz/fuzz-body-xml-sqli.yaml | 40 ++ .../fuzz/fuzz-cookie-error-sqli.yaml | 59 ++ integration_tests/fuzz/fuzz-header-basic.yaml | 2 +- .../fuzz/fuzz-header-multiple.yaml | 2 +- .../fuzz/fuzz-host-header-injection.yaml | 43 ++ integration_tests/fuzz/fuzz-mode.yaml | 2 +- integration_tests/fuzz/fuzz-path-sqli.yaml | 42 ++ .../fuzz/fuzz-query-num-replace.yaml | 39 ++ integration_tests/fuzz/fuzz-query.yaml | 2 +- integration_tests/fuzz/fuzz-type.yaml | 2 +- .../fuzz/testData/ginandjuice.proxify.yaml | 535 ++++++++++++++++ integration_tests/run.sh | 2 +- internal/runner/inputs.go | 8 +- internal/runner/lazy.go | 123 ++++ internal/runner/options.go | 9 +- internal/runner/runner.go | 102 ++- lib/config.go | 29 +- lib/multi.go | 12 +- lib/sdk.go | 30 +- lib/sdk_private.go | 41 +- pkg/authprovider/authx/basic_auth.go | 31 + pkg/authprovider/authx/bearer_auth.go | 31 + pkg/authprovider/authx/cookies_auth.go | 43 ++ pkg/authprovider/authx/dynamic.go | 167 +++++ pkg/authprovider/authx/file.go | 253 ++++++++ pkg/authprovider/authx/file_test.go | 20 + pkg/authprovider/authx/headers_auth.go | 35 ++ pkg/authprovider/authx/query_auth.go | 42 ++ pkg/authprovider/authx/strategy.go | 39 ++ .../authx/testData/example-auth.yaml | 70 +++ pkg/authprovider/file.go | 168 +++++ pkg/authprovider/interface.go | 59 ++ pkg/authprovider/multi.go | 67 ++ pkg/catalog/config/constants.go | 16 +- pkg/catalog/loader/loader.go | 66 +- pkg/core/engine.go | 16 - pkg/core/execute_options.go | 13 +- pkg/core/executors.go | 5 +- pkg/core/inputs/inputs.go | 39 -- pkg/fuzz/component/body.go | 140 +++++ pkg/fuzz/component/body_test.go | 175 ++++++ pkg/fuzz/component/component.go | 95 +++ pkg/fuzz/component/cookie.go | 138 +++++ pkg/fuzz/component/cookie_test.go | 57 ++ pkg/fuzz/component/headers.go | 186 ++++++ pkg/fuzz/component/headers_test.go | 51 ++ pkg/fuzz/component/path.go | 83 +++ pkg/fuzz/component/path_test.go | 46 ++ pkg/fuzz/component/query.go | 92 +++ pkg/fuzz/component/query_test.go | 46 ++ pkg/fuzz/component/value.go | 120 ++++ pkg/fuzz/component/value_test.go | 39 ++ pkg/fuzz/dataformat/dataformat.go | 92 +++ pkg/fuzz/dataformat/dataformat_test.go | 54 ++ pkg/fuzz/dataformat/form.go | 61 ++ pkg/fuzz/dataformat/json.go | 48 ++ pkg/fuzz/dataformat/multipart.go | 116 ++++ pkg/fuzz/dataformat/raw.go | 34 + pkg/fuzz/dataformat/xml.go | 62 ++ pkg/{protocols/common => }/fuzz/doc.go | 0 pkg/fuzz/execute.go | 275 +++++++++ pkg/{protocols/common => }/fuzz/fuzz.go | 42 +- pkg/{protocols/common => }/fuzz/fuzz_test.go | 0 pkg/fuzz/parts.go | 210 +++++++ pkg/fuzz/parts_test.go | 2 + pkg/fuzz/type.go | 80 +++ pkg/input/README.md | 29 + pkg/input/formats/README.md | 90 +++ pkg/input/formats/burp/burp.go | 67 ++ pkg/input/formats/burp/burp_test.go | 33 + pkg/input/formats/formats.go | 103 ++++ pkg/input/formats/json/json.go | 74 +++ pkg/input/formats/json/json_test.go | 57 ++ pkg/input/formats/openapi/examples.go | 290 +++++++++ pkg/input/formats/openapi/generator.go | 461 ++++++++++++++ pkg/input/formats/openapi/openapi.go | 39 ++ pkg/input/formats/openapi/openapi_test.go | 63 ++ pkg/input/formats/swagger/swagger.go | 69 +++ pkg/input/formats/swagger/swagger_test.go | 34 + pkg/input/formats/testdata/burp.xml | 58 ++ .../formats/testdata/ginandjuice.proxify.json | 26 + .../formats/testdata/ginandjuice.proxify.yaml | 269 ++++++++ pkg/input/formats/testdata/openapi.yaml | 582 ++++++++++++++++++ pkg/input/formats/testdata/postman.json | 159 +++++ pkg/input/formats/testdata/swagger.yaml | 37 ++ pkg/input/formats/yaml/multidoc.go | 78 +++ pkg/input/formats/yaml/multidoc_test.go | 28 + pkg/input/provider/chunked.go | 29 + pkg/input/provider/http/multiformat.go | 120 ++++ pkg/input/provider/interface.go | 126 ++++ .../hybrid => input/provider/list}/hmap.go | 295 ++++----- .../provider/list}/hmap_test.go | 8 +- .../provider/list}/tests/AS134029.txt | 0 .../provider/list}/tests/AS14421.txt | 0 .../provider/list/utils.go} | 2 +- pkg/input/provider/simple.go | 73 +++ pkg/input/{input.go => transform.go} | 0 .../{input_test.go => transform_test.go} | 0 pkg/input/types/http.go | 305 +++++++++ pkg/input/types/http_test.go | 67 ++ pkg/input/types/probe.go | 7 + pkg/operators/operators.go | 22 +- pkg/parsers/parser.go | 2 + .../common/automaticscan/automaticscan.go | 11 +- pkg/protocols/common/contextargs/metainput.go | 40 +- pkg/protocols/common/fuzz/execute.go | 157 ----- pkg/protocols/common/fuzz/execute_test.go | 26 - pkg/protocols/common/fuzz/parts.go | 211 ------- pkg/protocols/common/fuzz/parts_test.go | 144 ----- pkg/protocols/headless/headless.go | 2 +- pkg/protocols/headless/request.go | 2 +- pkg/protocols/http/build_request.go | 27 + pkg/protocols/http/http.go | 25 +- pkg/protocols/http/raw/raw.go | 44 ++ pkg/protocols/http/request.go | 99 +-- pkg/protocols/http/request_fuzz.go | 305 +++++++++ pkg/protocols/protocols.go | 3 + pkg/templates/templates.go | 23 + pkg/testutils/fuzzplayground/db.go | 150 +++++ pkg/testutils/fuzzplayground/server.go | 212 +++++++ pkg/types/types.go | 13 + pkg/utils/http_probe.go | 16 + 137 files changed, 9269 insertions(+), 1103 deletions(-) create mode 100644 integration_tests/fuzz/fuzz-body-generic-sqli.yaml create mode 100644 integration_tests/fuzz/fuzz-body-json-sqli.yaml create mode 100644 integration_tests/fuzz/fuzz-body-multipart-form-sqli.yaml create mode 100644 integration_tests/fuzz/fuzz-body-params-sqli.yaml create mode 100644 integration_tests/fuzz/fuzz-body-xml-sqli.yaml create mode 100644 integration_tests/fuzz/fuzz-cookie-error-sqli.yaml create mode 100644 integration_tests/fuzz/fuzz-host-header-injection.yaml create mode 100644 integration_tests/fuzz/fuzz-path-sqli.yaml create mode 100644 integration_tests/fuzz/fuzz-query-num-replace.yaml create mode 100644 integration_tests/fuzz/testData/ginandjuice.proxify.yaml create mode 100644 internal/runner/lazy.go create mode 100644 pkg/authprovider/authx/basic_auth.go create mode 100644 pkg/authprovider/authx/bearer_auth.go create mode 100644 pkg/authprovider/authx/cookies_auth.go create mode 100644 pkg/authprovider/authx/dynamic.go create mode 100644 pkg/authprovider/authx/file.go create mode 100644 pkg/authprovider/authx/file_test.go create mode 100644 pkg/authprovider/authx/headers_auth.go create mode 100644 pkg/authprovider/authx/query_auth.go create mode 100644 pkg/authprovider/authx/strategy.go create mode 100644 pkg/authprovider/authx/testData/example-auth.yaml create mode 100644 pkg/authprovider/file.go create mode 100644 pkg/authprovider/interface.go create mode 100644 pkg/authprovider/multi.go delete mode 100644 pkg/core/inputs/inputs.go create mode 100644 pkg/fuzz/component/body.go create mode 100644 pkg/fuzz/component/body_test.go create mode 100644 pkg/fuzz/component/component.go create mode 100644 pkg/fuzz/component/cookie.go create mode 100644 pkg/fuzz/component/cookie_test.go create mode 100644 pkg/fuzz/component/headers.go create mode 100644 pkg/fuzz/component/headers_test.go create mode 100644 pkg/fuzz/component/path.go create mode 100644 pkg/fuzz/component/path_test.go create mode 100644 pkg/fuzz/component/query.go create mode 100644 pkg/fuzz/component/query_test.go create mode 100644 pkg/fuzz/component/value.go create mode 100644 pkg/fuzz/component/value_test.go create mode 100644 pkg/fuzz/dataformat/dataformat.go create mode 100644 pkg/fuzz/dataformat/dataformat_test.go create mode 100644 pkg/fuzz/dataformat/form.go create mode 100644 pkg/fuzz/dataformat/json.go create mode 100644 pkg/fuzz/dataformat/multipart.go create mode 100644 pkg/fuzz/dataformat/raw.go create mode 100644 pkg/fuzz/dataformat/xml.go rename pkg/{protocols/common => }/fuzz/doc.go (100%) create mode 100644 pkg/fuzz/execute.go rename pkg/{protocols/common => }/fuzz/fuzz.go (75%) rename pkg/{protocols/common => }/fuzz/fuzz_test.go (100%) create mode 100644 pkg/fuzz/parts.go create mode 100644 pkg/fuzz/parts_test.go create mode 100644 pkg/fuzz/type.go create mode 100644 pkg/input/README.md create mode 100644 pkg/input/formats/README.md create mode 100644 pkg/input/formats/burp/burp.go create mode 100644 pkg/input/formats/burp/burp_test.go create mode 100644 pkg/input/formats/formats.go create mode 100644 pkg/input/formats/json/json.go create mode 100644 pkg/input/formats/json/json_test.go create mode 100644 pkg/input/formats/openapi/examples.go create mode 100644 pkg/input/formats/openapi/generator.go create mode 100644 pkg/input/formats/openapi/openapi.go create mode 100644 pkg/input/formats/openapi/openapi_test.go create mode 100644 pkg/input/formats/swagger/swagger.go create mode 100644 pkg/input/formats/swagger/swagger_test.go create mode 100644 pkg/input/formats/testdata/burp.xml create mode 100644 pkg/input/formats/testdata/ginandjuice.proxify.json create mode 100644 pkg/input/formats/testdata/ginandjuice.proxify.yaml create mode 100644 pkg/input/formats/testdata/openapi.yaml create mode 100644 pkg/input/formats/testdata/postman.json create mode 100644 pkg/input/formats/testdata/swagger.yaml create mode 100644 pkg/input/formats/yaml/multidoc.go create mode 100644 pkg/input/formats/yaml/multidoc_test.go create mode 100644 pkg/input/provider/chunked.go create mode 100644 pkg/input/provider/http/multiformat.go create mode 100644 pkg/input/provider/interface.go rename pkg/{core/inputs/hybrid => input/provider/list}/hmap.go (87%) rename pkg/{core/inputs/hybrid => input/provider/list}/hmap_test.go (97%) rename pkg/{core/inputs/hybrid => input/provider/list}/tests/AS134029.txt (100%) rename pkg/{core/inputs/hybrid => input/provider/list}/tests/AS14421.txt (100%) rename pkg/{core/inputs/hybrid/options.go => input/provider/list/utils.go} (83%) create mode 100644 pkg/input/provider/simple.go rename pkg/input/{input.go => transform.go} (100%) rename pkg/input/{input_test.go => transform_test.go} (100%) create mode 100644 pkg/input/types/http.go create mode 100644 pkg/input/types/http_test.go create mode 100644 pkg/input/types/probe.go delete mode 100644 pkg/protocols/common/fuzz/execute.go delete mode 100644 pkg/protocols/common/fuzz/execute_test.go delete mode 100644 pkg/protocols/common/fuzz/parts.go delete mode 100644 pkg/protocols/common/fuzz/parts_test.go create mode 100644 pkg/protocols/http/request_fuzz.go create mode 100644 pkg/testutils/fuzzplayground/db.go create mode 100644 pkg/testutils/fuzzplayground/server.go diff --git a/.gitignore b/.gitignore index 96896c85..57d2daa0 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,9 @@ pkg/protocols/headless/engine/.cache /tsgen /scrapefuncs /integration_tests/.cache/ -/integration_tests/.nuclei-config/ /*.yaml -/pkg/protocols/headless/engine/.nuclei-config/ \ No newline at end of file +**/*-config +**/*-cache +/fuzzplayground +integration_tests/fuzzplayground + diff --git a/Makefile b/Makefile index cd2c2157..f6950b31 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,8 @@ jsupdate: ts: $(GOBUILD) $(GOFLAGS) -ldflags '$(LDFLAGS)' -o "tsgen" pkg/js/devtools/tsgen/cmd/tsgen/main.go ./tsgen -dir pkg/js/libs -out pkg/js/generated/ts +fuzzplayground: + $(GOBUILD) $(GOFLAGS) -ldflags '$(LDFLAGS)' -o "fuzzplayground" cmd/tools/fuzzplayground/main.go memogen: $(GOBUILD) $(GOFLAGS) -ldflags '$(LDFLAGS)' -o "memogen" cmd/memogen/memogen.go ./memogen -src pkg/js/libs -tpl cmd/memogen/function.tpl diff --git a/README.md b/README.md index 69ee2624..e231221c 100644 --- a/README.md +++ b/README.md @@ -113,12 +113,17 @@ Usage: Flags: TARGET: - -u, -target string[] target URLs/hosts to scan - -l, -list string path to file containing a list of target URLs/hosts to scan (one per line) - -eh, -exclude-hosts string[] hosts to exclude to scan from the input list (ip, cidr, hostname) - -resume string resume scan using resume.cfg (clustering will be disabled) - -sa, -scan-all-ips scan all the IP's associated with dns record - -iv, -ip-version string[] IP version to scan of hostname (4,6) - (default 4) + -u, -target string[] target URLs/hosts to scan + -l, -list string path to file containing a list of target URLs/hosts to scan (one per line) + -eh, -exclude-hosts string[] hosts to exclude to scan from the input list (ip, cidr, hostname) + -resume string resume scan using resume.cfg (clustering will be disabled) + -sa, -scan-all-ips scan all the IP's associated with dns record + -iv, -ip-version string[] IP version to scan of hostname (4,6) - (default 4) + +TARGET-FORMAT: + -im, -input-mode string mode of input file (list, burp, jsonl, yaml, openapi, swagger) (default "list") + -ro, -required-only use only required fields in input format when generating requests + -sfv, -skip-format-validation skip format validation (like missing vars) when parsing input file TEMPLATES: -nt, -new-templates run only new templates added in latest nuclei-templates release @@ -134,6 +139,7 @@ TEMPLATES: -tl list all available templates -sign signs the templates with the private key defined in NUCLEI_SIGNATURE_PRIVATE_KEY env variable -code enable loading code protocol-based templates + -dut, -disable-unsigned-templates disable running unsigned templates or templates with mismatched signature FILTERING: -a, -author string[] templates to run based on authors (comma-separated, file) @@ -142,8 +148,8 @@ FILTERING: -itags, -include-tags string[] tags to be executed even if they are excluded either by default or configuration -id, -template-id string[] templates to run based on template ids (comma-separated, file, allow-wildcard) -eid, -exclude-id string[] templates to exclude based on template ids (comma-separated, file) - -it, -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 to exclude (comma-separated, file) + -it, -include-templates string[] path to template file or directory to be executed even if they are excluded either by default or configuration + -et, -exclude-templates string[] path to template file or directory to exclude (comma-separated, file) -em, -exclude-matchers string[] template matchers to exclude in result -s, -severity value[] templates to run based on severity. Possible values: info, low, medium, high, critical, unknown -es, -exclude-severity value[] templates to exclude based on severity. Possible values: info, low, medium, high, critical, unknown @@ -215,6 +221,7 @@ INTERACTSH: FUZZING: -ft, -fuzzing-type string overrides fuzzing type set in template (replace, prefix, postfix, infix) -fm, -fuzzing-mode string overrides fuzzing mode set in template (multiple, single) + -fuzz enable loading fuzzing templates UNCOVER: -uc, -uncover enable uncover engine @@ -231,6 +238,8 @@ RATE-LIMIT: -c, -concurrency int maximum number of templates to be executed in parallel (default 25) -hbs, -headless-bulk-size int maximum number of headless hosts to be analyzed in parallel per template (default 10) -headc, -headless-concurrency int maximum number of headless templates to be executed in parallel (default 10) + -jsc, -js-concurrency int maximum number of javascript runtimes to be executed in parallel (default 120) + -pc, -payload-concurrency int max payload concurrency for each template (default 25) OPTIMIZATIONS: -timeout int time to wait in seconds before timeout (default 10) @@ -292,22 +301,26 @@ CLOUD: -cup, -cloud-upload upload scan results to pdcp dashboard -sid, -scan-id string upload scan results to given scan id +AUTHENTICATION: + -sf, -secret-file string[] path to config file containing secrets for nuclei authenticated scan + -ps, -prefetch-secrets prefetch secrets from the secrets file + EXAMPLES: Run nuclei on single host: - $ nuclei -target example.com + $ nuclei -target example.com Run nuclei with specific template directories: - $ nuclei -target example.com -t http/cves/ -t ssl + $ nuclei -target example.com -t http/cves/ -t ssl Run nuclei against a list of hosts: - $ nuclei -list hosts.txt + $ nuclei -list hosts.txt Run nuclei with a JSON output: - $ nuclei -target example.com -json-export output.json + $ nuclei -target example.com -json-export output.json Run nuclei with sorted Markdown outputs (with environment variables): - $ MARKDOWN_EXPORT_SORT_MODE=template nuclei -target example.com -markdown-export nuclei_report/ + $ MARKDOWN_EXPORT_SORT_MODE=template nuclei -target example.com -markdown-export nuclei_report/ Additional documentation is available at: https://docs.nuclei.sh/getting-started/running ``` diff --git a/cmd/integration-test/fuzz.go b/cmd/integration-test/fuzz.go index 276f8550..be2ac161 100644 --- a/cmd/integration-test/fuzz.go +++ b/cmd/integration-test/fuzz.go @@ -12,6 +12,10 @@ import ( "github.com/projectdiscovery/nuclei/v3/pkg/testutils" ) +const ( + targetFile = "fuzz/testData/ginandjuice.proxify.yaml" +) + var fuzzingTestCases = []TestCaseInfo{ {Path: "fuzz/fuzz-mode.yaml", TestCase: &fuzzModeOverride{}}, {Path: "fuzz/fuzz-type.yaml", TestCase: &fuzzTypeOverride{}}, @@ -19,6 +23,29 @@ var fuzzingTestCases = []TestCaseInfo{ {Path: "fuzz/fuzz-headless.yaml", TestCase: &HeadlessFuzzingQuery{}}, {Path: "fuzz/fuzz-header-basic.yaml", TestCase: &FuzzHeaderBasic{}}, {Path: "fuzz/fuzz-header-multiple.yaml", TestCase: &FuzzHeaderMultiple{}}, + // for fuzzing we should prioritize adding test case related backend + // logic in fuzz playground server instead of adding them here + {Path: "fuzz/fuzz-query-num-replace.yaml", TestCase: &genericFuzzTestCase{expectedResults: 2}}, + {Path: "fuzz/fuzz-host-header-injection.yaml", TestCase: &genericFuzzTestCase{expectedResults: 1}}, + {Path: "fuzz/fuzz-path-sqli.yaml", TestCase: &genericFuzzTestCase{expectedResults: 1}}, + {Path: "fuzz/fuzz-cookie-error-sqli.yaml", TestCase: &genericFuzzTestCase{expectedResults: 1}}, + {Path: "fuzz/fuzz-body-json-sqli.yaml", TestCase: &genericFuzzTestCase{expectedResults: 1}}, + {Path: "fuzz/fuzz-body-multipart-form-sqli.yaml", TestCase: &genericFuzzTestCase{expectedResults: 1}}, + {Path: "fuzz/fuzz-body-params-sqli.yaml", TestCase: &genericFuzzTestCase{expectedResults: 1}}, + {Path: "fuzz/fuzz-body-xml-sqli.yaml", TestCase: &genericFuzzTestCase{expectedResults: 1}}, + {Path: "fuzz/fuzz-body-generic-sqli.yaml", TestCase: &genericFuzzTestCase{expectedResults: 4}}, +} + +type genericFuzzTestCase struct { + expectedResults int +} + +func (g *genericFuzzTestCase) Execute(filePath string) error { + results, err := testutils.RunNucleiWithArgsAndGetResults(debug, "-t", filePath, "-l", targetFile, "-im", "yaml") + if err != nil { + return err + } + return expectResultsCount(results, g.expectedResults) } type httpFuzzQuery struct{} @@ -34,7 +61,7 @@ func (h *httpFuzzQuery) Execute(filePath string) error { ts := httptest.NewTLSServer(router) defer ts.Close() - results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL+"/?id=example", debug) + results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL+"/?id=example", debug, "-fuzz") if err != nil { return err } @@ -53,7 +80,7 @@ func (h *fuzzModeOverride) Execute(filePath string) error { }) ts := httptest.NewTLSServer(router) defer ts.Close() - results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL+"/?id=example&name=nuclei", debug, "-fuzzing-mode", "single", "-jsonl") + results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL+"/?id=example&name=nuclei", debug, "-fuzzing-mode", "single", "-jsonl", "-fuzz") if err != nil { return err } @@ -98,7 +125,7 @@ func (h *fuzzTypeOverride) Execute(filePath string) error { }) ts := httptest.NewTLSServer(router) defer ts.Close() - results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL+"?id=example", debug, "-fuzzing-type", "replace", "-jsonl") + results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL+"?id=example", debug, "-fuzzing-type", "replace", "-jsonl", "-fuzz") if err != nil { return err } @@ -143,7 +170,7 @@ func (h *HeadlessFuzzingQuery) Execute(filePath string) error { ts := httptest.NewTLSServer(router) defer ts.Close() - got, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL+"?url=https://scanme.sh", debug, "-headless") + got, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL+"?url=https://scanme.sh", debug, "-headless", "-fuzz") if err != nil { return err } @@ -164,7 +191,7 @@ func (h *FuzzHeaderBasic) Execute(filePath string) error { ts := httptest.NewTLSServer(router) defer ts.Close() - got, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug) + got, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-fuzz") if err != nil { return err } @@ -192,7 +219,7 @@ func (h *FuzzHeaderMultiple) Execute(filePath string) error { ts := httptest.NewTLSServer(router) defer ts.Close() - got, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug) + got, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-fuzz") if err != nil { return err } diff --git a/cmd/integration-test/integration-test.go b/cmd/integration-test/integration-test.go index 672224e1..76851413 100644 --- a/cmd/integration-test/integration-test.go +++ b/cmd/integration-test/integration-test.go @@ -9,7 +9,9 @@ import ( "github.com/logrusorgru/aurora" + "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/nuclei/v3/pkg/testutils" + "github.com/projectdiscovery/nuclei/v3/pkg/testutils/fuzzplayground" sliceutil "github.com/projectdiscovery/utils/slice" ) @@ -53,6 +55,10 @@ var ( "flow": flowTestcases, "javascript": jsTestcases, } + // flakyTests are run with a retry count of 3 + flakyTests = map[string]bool{ + "protocols/http/self-contained-file-input.yaml": true, + } // For debug purposes runProtocol = "" @@ -78,6 +84,18 @@ func main() { os.Exit(1) } + // start fuzz playground server + defer fuzzplayground.Cleanup() + server := fuzzplayground.GetPlaygroundServer() + defer server.Close() + go func() { + if err := server.Start("localhost:8082"); err != nil { + if !strings.Contains(err.Error(), "Server closed") { + gologger.Fatal().Msgf("Could not start server: %s\n", err) + } + } + }() + customTestsList := normalizeSplit(customTests) failedTestTemplatePaths := runTests(customTestsList) @@ -150,6 +168,8 @@ func runTests(customTemplatePaths []string) []string { var err error if proto == "interactsh" || strings.Contains(testCaseInfo.Path, "interactsh") { failedTemplatePath, err = executeWithRetry(testCaseInfo.TestCase, testCaseInfo.Path, interactshRetryCount) + } else if flakyTests[testCaseInfo.Path] { + failedTemplatePath, err = executeWithRetry(testCaseInfo.TestCase, testCaseInfo.Path, interactshRetryCount) } else { failedTemplatePath, err = execute(testCaseInfo.TestCase, testCaseInfo.Path) } diff --git a/cmd/integration-test/library.go b/cmd/integration-test/library.go index 5fec4226..fd02f17e 100644 --- a/cmd/integration-test/library.go +++ b/cmd/integration-test/library.go @@ -19,11 +19,10 @@ import ( "github.com/projectdiscovery/nuclei/v3/pkg/catalog/disk" "github.com/projectdiscovery/nuclei/v3/pkg/catalog/loader" "github.com/projectdiscovery/nuclei/v3/pkg/core" - "github.com/projectdiscovery/nuclei/v3/pkg/core/inputs" + "github.com/projectdiscovery/nuclei/v3/pkg/input/provider" "github.com/projectdiscovery/nuclei/v3/pkg/output" "github.com/projectdiscovery/nuclei/v3/pkg/parsers" "github.com/projectdiscovery/nuclei/v3/pkg/protocols" - "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/hosterrorscache" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/interactsh" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolinit" @@ -126,8 +125,7 @@ func executeNucleiAsLibrary(templatePath, templateURL string) ([]string, error) } store.Load() - input := &inputs.SimpleInputProvider{Inputs: []*contextargs.MetaInput{{Input: templateURL}}} - _ = engine.Execute(store.Templates(), input) + _ = engine.Execute(store.Templates(), provider.NewSimpleInputProviderWithUrls(templateURL)) engine.WorkPool().Wait() // Wait for the scan to finish return results, nil diff --git a/cmd/integration-test/workflow.go b/cmd/integration-test/workflow.go index c0ccb8ee..3ffd2887 100644 --- a/cmd/integration-test/workflow.go +++ b/cmd/integration-test/workflow.go @@ -137,7 +137,7 @@ type workflowSharedCookies struct{} // Execute executes a test case and returns an error if occurred func (h *workflowSharedCookies) Execute(filePath string) error { - handleFunc := func(name string, w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + handleFunc := func(name string, w http.ResponseWriter, _ *http.Request, _ httprouter.Params) { cookie := &http.Cookie{Name: name, Value: name} http.SetCookie(w, cookie) } diff --git a/cmd/nuclei/main.go b/cmd/nuclei/main.go index 684ea618..731865d7 100644 --- a/cmd/nuclei/main.go +++ b/cmd/nuclei/main.go @@ -22,6 +22,7 @@ import ( "github.com/projectdiscovery/interactsh/pkg/client" "github.com/projectdiscovery/nuclei/v3/internal/runner" "github.com/projectdiscovery/nuclei/v3/pkg/catalog/config" + "github.com/projectdiscovery/nuclei/v3/pkg/input/provider" "github.com/projectdiscovery/nuclei/v3/pkg/installer" "github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity" "github.com/projectdiscovery/nuclei/v3/pkg/operators/common/dsl" @@ -46,6 +47,9 @@ var ( ) func main() { + // enables CLI specific configs mostly interactive behavior + config.CurrentAppMode = config.AppModeCLI + if err := runner.ConfigureOptions(); err != nil { gologger.Fatal().Msgf("Could not initialize options: %s\n", err) } @@ -201,6 +205,12 @@ on extensive configurability, massive extensibility and ease of use.`) flagSet.StringSliceVarP(&options.IPVersion, "ip-version", "iv", nil, "IP version to scan of hostname (4,6) - (default 4)", goflags.CommaSeparatedStringSliceOptions), ) + flagSet.CreateGroup("target-format", "Target-Format", + flagSet.StringVarP(&options.InputFileMode, "input-mode", "im", "list", fmt.Sprintf("mode of input file (%v)", provider.SupportedInputFormats())), + flagSet.BoolVarP(&options.FormatUseRequiredOnly, "required-only", "ro", false, "use only required fields in input format when generating requests"), + flagSet.BoolVarP(&options.SkipFormatValidation, "skip-format-validation", "sfv", false, "skip format validation (like missing vars) when parsing input file"), + ) + flagSet.CreateGroup("templates", "Templates", flagSet.BoolVarP(&options.NewTemplates, "new-templates", "nt", false, "run only new templates added in latest nuclei-templates release"), flagSet.StringSliceVarP(&options.NewTemplatesWithVersion, "new-templates-version", "ntv", nil, "run new templates added in specific version", goflags.CommaSeparatedStringSliceOptions), @@ -226,8 +236,8 @@ on extensive configurability, massive extensibility and ease of use.`) flagSet.StringSliceVarP(&options.IncludeTags, "include-tags", "itags", nil, "tags to be executed even if they are excluded either by default or configuration", goflags.FileNormalizedStringSliceOptions), // TODO show default deny list flagSet.StringSliceVarP(&options.IncludeIds, "template-id", "id", nil, "templates to run based on template ids (comma-separated, file, allow-wildcard)", goflags.FileNormalizedStringSliceOptions), flagSet.StringSliceVarP(&options.ExcludeIds, "exclude-id", "eid", nil, "templates to exclude based on template ids (comma-separated, file)", goflags.FileNormalizedStringSliceOptions), - flagSet.StringSliceVarP(&options.IncludeTemplates, "include-templates", "it", nil, "templates to be executed even if they are excluded either by default or configuration", goflags.FileCommaSeparatedStringSliceOptions), - flagSet.StringSliceVarP(&options.ExcludedTemplates, "exclude-templates", "et", nil, "template or template directory to exclude (comma-separated, file)", goflags.FileCommaSeparatedStringSliceOptions), + flagSet.StringSliceVarP(&options.IncludeTemplates, "include-templates", "it", nil, "path to template file or directory to be executed even if they are excluded either by default or configuration", goflags.FileCommaSeparatedStringSliceOptions), + flagSet.StringSliceVarP(&options.ExcludedTemplates, "exclude-templates", "et", nil, "path to template file or directory to exclude (comma-separated, file)", goflags.FileCommaSeparatedStringSliceOptions), flagSet.StringSliceVarP(&options.ExcludeMatchers, "exclude-matchers", "em", nil, "template matchers to exclude in result", goflags.FileCommaSeparatedStringSliceOptions), flagSet.VarP(&options.Severities, "severity", "s", fmt.Sprintf("templates to run based on severity. Possible values: %s", severity.GetSupportedSeverities().String())), flagSet.VarP(&options.ExcludeSeverities, "exclude-severity", "es", fmt.Sprintf("templates to exclude based on severity. Possible values: %s", severity.GetSupportedSeverities().String())), @@ -303,6 +313,7 @@ on extensive configurability, massive extensibility and ease of use.`) flagSet.CreateGroup("fuzzing", "Fuzzing", flagSet.StringVarP(&options.FuzzingType, "fuzzing-type", "ft", "", "overrides fuzzing type set in template (replace, prefix, postfix, infix)"), flagSet.StringVarP(&options.FuzzingMode, "fuzzing-mode", "fm", "", "overrides fuzzing mode set in template (multiple, single)"), + flagSet.BoolVar(&options.FuzzTemplates, "fuzz", false, "enable loading fuzzing templates"), ) flagSet.CreateGroup("uncover", "Uncover", @@ -394,6 +405,11 @@ on extensive configurability, massive extensibility and ease of use.`) flagSet.StringVarP(&options.ScanID, "scan-id", "sid", "", "upload scan results to given scan id"), ) + flagSet.CreateGroup("Authentication", "Authentication", + flagSet.StringSliceVarP(&options.SecretsFile, "secret-file", "sf", nil, "path to config file containing secrets for nuclei authenticated scan", goflags.CommaSeparatedStringSliceOptions), + flagSet.BoolVarP(&options.PreFetchSecrets, "prefetch-secrets", "ps", false, "prefetch secrets from the secrets file"), + ) + flagSet.SetCustomHelpText(`EXAMPLES: Run nuclei on single host: $ nuclei -target example.com @@ -468,6 +484,14 @@ Additional documentation is available at: https://docs.nuclei.sh/getting-started config.DefaultConfig.SetTemplatesDir(options.NewTemplatesDirectory) } + if len(options.SecretsFile) > 0 { + for _, secretFile := range options.SecretsFile { + if !fileutil.FileExists(secretFile) { + gologger.Fatal().Msgf("given secrets file '%s' does not exist", options.SecretsFile) + } + } + } + cleanupOldResumeFiles() return flagSet } diff --git a/cmd/tools/fuzzplayground/main.go b/cmd/tools/fuzzplayground/main.go index 4298b12a..0ab764e8 100644 --- a/cmd/tools/fuzzplayground/main.go +++ b/cmd/tools/fuzzplayground/main.go @@ -1,94 +1,27 @@ package main import ( - "fmt" - "io" - "os/exec" - "strconv" - "strings" + "flag" - "github.com/labstack/echo/v4" - "github.com/labstack/echo/v4/middleware" - "github.com/projectdiscovery/retryablehttp-go" + _ "github.com/mattn/go-sqlite3" + "github.com/projectdiscovery/gologger" + "github.com/projectdiscovery/nuclei/v3/pkg/testutils/fuzzplayground" +) + +var ( + addr string ) func main() { - e := echo.New() - e.Use(middleware.Recover()) - e.Use(middleware.Logger()) + flag.StringVar(&addr, "addr", "localhost:8082", "playground server address") + flag.Parse() - e.GET("/", indexHandler) - e.GET("/info", infoHandler) - e.GET("/redirect", redirectHandler) - e.GET("/request", requestHandler) - e.GET("/email", emailHandler) - e.GET("/permissions", permissionsHandler) - if err := e.Start("localhost:8082"); err != nil { - panic(err) + defer fuzzplayground.Cleanup() + server := fuzzplayground.GetPlaygroundServer() + defer server.Close() + + // Start the server + if err := server.Start(addr); err != nil { + gologger.Fatal().Msgf("Could not start server: %s\n", err) } } - -var bodyTemplate = ` - -Fuzz Playground - - -%s - -` - -func indexHandler(ctx echo.Context) error { - return ctx.HTML(200, fmt.Sprintf(bodyTemplate, `

Fuzzing Playground


- -`)) -} - -func infoHandler(ctx echo.Context) error { - return ctx.HTML(200, fmt.Sprintf(bodyTemplate, fmt.Sprintf("Name of user: %s%s%s", ctx.QueryParam("name"), ctx.QueryParam("another"), ctx.QueryParam("random")))) -} - -func redirectHandler(ctx echo.Context) error { - url := ctx.QueryParam("redirect_url") - return ctx.Redirect(302, url) -} - -func requestHandler(ctx echo.Context) error { - url := ctx.QueryParam("url") - data, err := retryablehttp.DefaultClient().Get(url) - if err != nil { - return ctx.HTML(500, err.Error()) - } - defer data.Body.Close() - - body, _ := io.ReadAll(data.Body) - return ctx.HTML(200, fmt.Sprintf(bodyTemplate, string(body))) -} - -func emailHandler(ctx echo.Context) error { - text := ctx.QueryParam("text") - if strings.Contains(text, "{{") { - trimmed := strings.SplitN(strings.Trim(text[strings.Index(text, "{"):], "{}"), "*", 2) - if len(trimmed) < 2 { - return ctx.HTML(500, "invalid template") - } - first, _ := strconv.Atoi(trimmed[0]) - second, _ := strconv.Atoi(trimmed[1]) - text = strconv.Itoa(first * second) - } - return ctx.HTML(200, fmt.Sprintf(bodyTemplate, fmt.Sprintf("Text: %s", text))) -} - -func permissionsHandler(ctx echo.Context) error { - command := ctx.QueryParam("cmd") - fields := strings.Fields(command) - cmd := exec.Command(fields[0], fields[1:]...) - data, _ := cmd.CombinedOutput() - - return ctx.HTML(200, fmt.Sprintf(bodyTemplate, string(data))) -} diff --git a/go.mod b/go.mod index 1d4d558d..e1edff2e 100644 --- a/go.mod +++ b/go.mod @@ -42,7 +42,6 @@ require ( golang.org/x/oauth2 v0.11.0 golang.org/x/text v0.14.0 gopkg.in/yaml.v2 v2.4.0 - moul.io/http2curl v1.0.0 ) require ( @@ -60,18 +59,22 @@ require ( github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.72 github.com/aws/aws-sdk-go-v2/service/s3 v1.37.0 github.com/charmbracelet/glamour v0.6.0 + github.com/clbanning/mxj/v2 v2.7.0 github.com/denisenkom/go-mssqldb v0.12.3 github.com/ditashi/jsbeautifier-go v0.0.0-20141206144643-2520a8026a9c github.com/docker/go-units v0.5.0 github.com/dop251/goja v0.0.0-20230828202809-3dbe69dd2b8e github.com/fatih/structs v1.1.0 + github.com/getkin/kin-openapi v0.123.0 github.com/go-git/go-git/v5 v5.11.0 github.com/go-ldap/ldap/v3 v3.4.5 github.com/go-pg/pg v8.0.7+incompatible github.com/go-sql-driver/mysql v1.7.1 github.com/h2non/filetype v1.1.3 github.com/labstack/echo/v4 v4.10.2 + github.com/leslie-qiwa/flat v0.0.0-20230424180412-f9d1cf014baa github.com/lib/pq v1.10.1 + github.com/mattn/go-sqlite3 v1.14.22 github.com/mholt/archiver v3.1.1+incompatible github.com/ory/dockertest/v3 v3.10.0 github.com/praetorian-inc/fingerprintx v1.1.9 @@ -95,11 +98,13 @@ require ( github.com/projectdiscovery/wappalyzergo v0.0.112 github.com/redis/go-redis/v9 v9.1.0 github.com/sashabaranov/go-openai v1.15.3 + github.com/seh-msft/burpxml v1.0.1 github.com/stretchr/testify v1.9.0 github.com/zmap/zgrab2 v0.1.8-0.20230806160807-97ba87c0e706 golang.org/x/term v0.17.0 gopkg.in/src-d/go-git.v4 v4.13.1 gopkg.in/yaml.v3 v3.0.1 + moul.io/http2curl v1.0.0 ) require ( @@ -145,6 +150,8 @@ require ( github.com/gin-gonic/gin v1.9.1 // indirect github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect github.com/go-fed/httpsig v1.1.0 // indirect + github.com/go-openapi/jsonpointer v0.20.2 // indirect + github.com/go-openapi/swag v0.22.9 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect @@ -158,30 +165,35 @@ require ( github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.6 // indirect github.com/hbakhtiyor/strsim v0.0.0-20190107154042-4d2bbb273edf // indirect + github.com/invopop/yaml v0.2.0 // indirect github.com/jcmturner/aescts/v2 v2.0.0 // indirect github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect github.com/jcmturner/gofork v1.7.6 // indirect github.com/jcmturner/rpc/v2 v2.0.3 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/josharian/intern v1.0.0 // indirect github.com/kataras/jwt v0.1.10 // indirect - github.com/klauspost/compress v1.17.4 // indirect - github.com/klauspost/pgzip v1.2.5 // indirect + github.com/klauspost/compress v1.17.6 // indirect + github.com/klauspost/pgzip v1.2.6 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mackerelio/go-osstat v0.2.4 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mholt/archiver/v3 v3.5.1 // indirect github.com/minio/selfupdate v0.6.1-0.20230907112617-f11e74f84ca7 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moby/term v0.5.0 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.15.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect github.com/opencontainers/runc v1.1.12 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect - github.com/pierrec/lz4/v4 v4.1.2 // indirect + github.com/perimeterx/marshmallow v1.1.5 // indirect + github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/projectdiscovery/asnmap v1.1.0 // indirect @@ -195,7 +207,6 @@ require ( github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/skeema/knownhosts v1.2.1 // indirect - github.com/smartystreets/assertions v1.0.0 // indirect github.com/tidwall/btree v1.7.0 // indirect github.com/tidwall/buntdb v1.3.0 // indirect github.com/tidwall/gjson v1.17.0 // indirect @@ -258,8 +269,8 @@ require ( github.com/libdns/libdns v0.2.1 // indirect github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mholt/acmez v1.2.0 // indirect github.com/microcosm-cc/bluemonday v1.0.26 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -268,7 +279,7 @@ require ( github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/projectdiscovery/blackrock v0.0.1 // indirect github.com/projectdiscovery/networkpolicy v0.0.8 - github.com/rivo/uniseg v0.4.4 // indirect + github.com/rivo/uniseg v0.4.6 // indirect github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect github.com/tklauser/go-sysconf v0.3.11 // indirect github.com/tklauser/numcpus v0.6.0 // indirect @@ -286,20 +297,20 @@ require ( go.uber.org/zap v1.25.0 // indirect goftp.io/server/v2 v2.0.1 // indirect golang.org/x/crypto v0.19.0 // indirect - golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 + golang.org/x/exp v0.0.0-20240119083558-1b970713d09a golang.org/x/mod v0.14.0 // indirect golang.org/x/sys v0.17.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.17.0 google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.31.0 // indirect + google.golang.org/protobuf v1.32.0 // indirect gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect gopkg.in/corvus-ch/zbase32.v1 v1.0.0 // indirect ) require ( github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect + github.com/ProtonMail/go-crypto v1.1.0-alpha.0-proton // indirect github.com/alecthomas/chroma v0.10.0 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.5 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.35 // indirect diff --git a/go.sum b/go.sum index 952f4f93..b69e157d 100644 --- a/go.sum +++ b/go.sum @@ -79,8 +79,8 @@ github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8 github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= -github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= -github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/ProtonMail/go-crypto v1.1.0-alpha.0-proton h1:P5Wd8eQ6zAzT4HpJI67FDKnTSf3xiJGQFqY1agDJPy4= +github.com/ProtonMail/go-crypto v1.1.0-alpha.0-proton/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= github.com/RumbleDiscovery/rumble-tools v0.0.0-20201105153123-f2adbb3244d2/go.mod h1:jD2+mU+E2SZUuAOHZvZj4xP4frlOo+N/YrXDvASFhkE= @@ -200,7 +200,6 @@ github.com/bsm/ginkgo/v2 v2.9.5/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbA github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= @@ -230,12 +229,13 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= +github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/cfssl v1.6.4 h1:NMOvfrEjFfC63K3SGXgAnFdsgkmiq4kATme5BfcqrO8= github.com/cloudflare/cfssl v1.6.4/go.mod h1:8b3CQMxfWPAeom3zBnGJ6sd+G1NkL5TXqmDXacb+1J0= github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= -github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= @@ -330,6 +330,8 @@ github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/geoffgarside/ber v1.1.0 h1:qTmFG4jJbwiSzSXoNJeHcOprVzZ8Ulde2Rrrifu5U9w= github.com/geoffgarside/ber v1.1.0/go.mod h1:jVPKeCbj6MvQZhwLYsGwaGI52oUorHoHKNecGT85ZCc= +github.com/getkin/kin-openapi v0.123.0 h1:zIik0mRwFNLyvtXK274Q6ut+dPh6nlxBp0x7mNrPhs8= +github.com/getkin/kin-openapi v0.123.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= @@ -369,6 +371,10 @@ github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= +github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= +github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE= +github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE= github.com/go-pg/pg v8.0.7+incompatible h1:ty/sXL1OZLo+47KK9N8llRcmbA9tZasqbQ/OO4ld53g= github.com/go-pg/pg v8.0.7+incompatible/go.mod h1:a2oXow+aFOrvwcKs3eIA0lNFmMilrxK2sOkB5NWe0vA= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -389,6 +395,8 @@ github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/goburrow/cache v0.1.4 h1:As4KzO3hgmzPlnaMniZU9+VmoNYseUhuELbxy9mRBfw= github.com/goburrow/cache v0.1.4/go.mod h1:cDFesZDnIlrHoNlMYqqMpCRawuXulgx+y7mXU8HZ+/c= github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= @@ -559,10 +567,13 @@ github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 h1:i462o439Z github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= +github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/itchyny/gojq v0.12.13 h1:IxyYlHYIlspQHHTE0f3cJF0NKDMfajxViuhBLnHd/QU= github.com/itchyny/gojq v0.12.13/go.mod h1:JzwzAqenfhrPUuwbmEz3nu3JQmFLlQTQMUcOdnu/Sf4= github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE= @@ -593,6 +604,8 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -618,15 +631,16 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= -github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= +github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= +github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= +github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -648,6 +662,8 @@ github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8 github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/leslie-qiwa/flat v0.0.0-20230424180412-f9d1cf014baa h1:KQKuQDgA3DZX6C396lt3WDYB9Um1gLITLbvficVbqXk= +github.com/leslie-qiwa/flat v0.0.0-20230424180412-f9d1cf014baa/go.mod h1:HbwNE4XGwjgtUELkvQaAOjWrpianHYZdQVNqSdYW3UM= github.com/lib/pq v1.10.1 h1:6VXZrLU0jHBYyAqrSPa+MgPfnSvTPuMgK+k0o5kVFWo= github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis= @@ -665,6 +681,8 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2 github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/mackerelio/go-osstat v0.2.4 h1:qxGbdPkFo65PXOb/F/nhDKpF2nGmGaCFDLXoZjJTtUs= github.com/mackerelio/go-osstat v0.2.4/go.mod h1:Zy+qzGdZs3A9cuIqmgbJvwbmLQH9dJvtio5ZjJTbdlQ= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -673,13 +691,16 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30= @@ -720,6 +741,8 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8= github.com/mreiferson/go-httpclient v0.0.0-20201222173833-5e475fde3a4d/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8= github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= @@ -779,12 +802,15 @@ github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtb github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM= github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= +github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= @@ -915,13 +941,13 @@ github.com/remeh/sizedwaitgroup v1.0.0 h1:VNGGFwNo/R5+MJBf6yrsr110p0m4/OX4S3DCy7 github.com/remeh/sizedwaitgroup v1.0.0/go.mod h1:3j2R4OIe/SeS6YDhICBy22RWjJC5eNCJ1V+9+NVNYlo= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= -github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.4.6 h1:Sovz9sDSwbOz9tgUy8JpT+KgCkPYJEN/oYzlJiYTNLg= +github.com/rivo/uniseg v0.4.6/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -935,6 +961,8 @@ github.com/sashabaranov/go-openai v1.15.3/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adO github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= +github.com/seh-msft/burpxml v1.0.1 h1:5G3QPSzvfA1WcX7LkxmKBmK2RnNyGviGWnJPumE0nwg= +github.com/seh-msft/burpxml v1.0.1/go.mod h1:lTViCHPtGGS0scK0B4krm6Ld1kVZLWzQccwUomRc58I= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= @@ -955,9 +983,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8= -github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= @@ -1162,7 +1189,6 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= @@ -1179,8 +1205,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= -golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= +golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1254,7 +1280,6 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= @@ -1371,7 +1396,6 @@ golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXR golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= @@ -1390,7 +1414,6 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= @@ -1550,8 +1573,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1591,6 +1614,7 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo= diff --git a/integration_tests/fuzz/fuzz-body-generic-sqli.yaml b/integration_tests/fuzz/fuzz-body-generic-sqli.yaml new file mode 100644 index 00000000..83ca4962 --- /dev/null +++ b/integration_tests/fuzz/fuzz-body-generic-sqli.yaml @@ -0,0 +1,39 @@ +id: fuzz-body-generic + +info: + name: fuzzing error sqli payloads in http req body + author: pdteam + severity: info + description: | + This template attempts to find SQL injection vulnerabilities by fuzzing http body + It automatically handles and parses json,xml,multipart form and x-www-form-urlencoded data + and performs fuzzing on the value of every key + +http: + - filters: + - type: dsl + dsl: + - method != "GET" + - method != "HEAD" + - contains(path, "/user") # for scope of integration test + condition: and + + payloads: + injection: + - "'" + - "\"" + - ";" + + fuzzing: + - part: body + type: postfix + mode: single + fuzz: + - '{{injection}}' + + stop-at-first-match: true + matchers: + - type: word + words: + - "unrecognized token:" + - "null" diff --git a/integration_tests/fuzz/fuzz-body-json-sqli.yaml b/integration_tests/fuzz/fuzz-body-json-sqli.yaml new file mode 100644 index 00000000..187ce1b4 --- /dev/null +++ b/integration_tests/fuzz/fuzz-body-json-sqli.yaml @@ -0,0 +1,40 @@ +id: json-body-error-sqli + +info: + name: fuzzing error sqli payloads in json body + author: pdteam + severity: info + description: | + This template attempts to find SQL injection vulnerabilities by fuzzing http body of json type. + This is achieved by performing [ruleType](example: postfix) on value of json key + Note: this is example template, and payloads/matchers need to be modified appropriately. + +http: + - filters: + - type: dsl + dsl: + - method != "GET" + - method != "HEAD" + - contains(content_type, "application/json") + - contains(path, "/user") # for scope of integration test + condition: and + + payloads: + injection: + - "'" + - "\"" + - ";" + + fuzzing: + - part: body + type: postfix + mode: single + fuzz: + - '{{injection}}' + + stop-at-first-match: true + matchers: + - type: word + words: + - "unrecognized token:" + - "null" diff --git a/integration_tests/fuzz/fuzz-body-multipart-form-sqli.yaml b/integration_tests/fuzz/fuzz-body-multipart-form-sqli.yaml new file mode 100644 index 00000000..dcca2e18 --- /dev/null +++ b/integration_tests/fuzz/fuzz-body-multipart-form-sqli.yaml @@ -0,0 +1,41 @@ +id: body-multipart-error-sqli + +info: + name: fuzzing error sqli payloads in body of multipart form data + author: pdteam + severity: info + description: | + This template attempts to find SQL injection vulnerabilities by fuzzing http body of multipart form data (file upload, etc.) + This is achieved by performing [ruleType](example: postfix) on value of body form key + Note: this is example template, and payloads/matchers need to be modified appropriately. + +http: + - filters: + - type: dsl + dsl: + - method != "GET" + - method != "HEAD" + - contains(content_type, "multipart/form-data") + - contains(path, "/user") # for scope of integration test + condition: and + + payloads: + injection: + - "'" + - "\"" + - ";" + + fuzzing: + - part: body + type: postfix + mode: single + fuzz: + - '{{injection}}' + + stop-at-first-match: true + matchers: + - type: word + words: + - "unrecognized token:" + - "null" + - "SELECTs to the left and right of UNION do not have the same number of result columns" diff --git a/integration_tests/fuzz/fuzz-body-params-sqli.yaml b/integration_tests/fuzz/fuzz-body-params-sqli.yaml new file mode 100644 index 00000000..2fdd1742 --- /dev/null +++ b/integration_tests/fuzz/fuzz-body-params-sqli.yaml @@ -0,0 +1,41 @@ +id: body-params-error-sqli + +info: + name: fuzzing error sqli payloads in body with params + author: pdteam + severity: info + description: | + This template attempts to find SQL injection vulnerabilities by fuzzing http body of x-www-form-urlencoded data + This is achieved by performing [ruleType](example: postfix) on value of body key + Note: this is example template, and payloads/matchers need to be modified appropriately. + +http: + - filters: + - type: dsl + dsl: + - method != "GET" + - method != "HEAD" + - contains(content_type, "application/x-www-form-urlencoded") + - contains(path, "/user") # for scope of integration test + condition: and + + payloads: + injection: + - "'" + - "\"" + - ";" + + fuzzing: + - part: body + type: postfix + mode: single + fuzz: + - '{{injection}}' + + stop-at-first-match: true + matchers: + - type: word + words: + - "unrecognized token:" + - "null" + - "SELECTs to the left and right of UNION do not have the same number of result columns" diff --git a/integration_tests/fuzz/fuzz-body-xml-sqli.yaml b/integration_tests/fuzz/fuzz-body-xml-sqli.yaml new file mode 100644 index 00000000..8ac62842 --- /dev/null +++ b/integration_tests/fuzz/fuzz-body-xml-sqli.yaml @@ -0,0 +1,40 @@ +id: xml-body-error-sqli + +info: + name: fuzzing error sqli payloads in xml body + author: pdteam + severity: info + description: | + This template attempts to find SQL injection vulnerabilities by fuzzing http body of xml type. + This is achieved by performing [ruleType](example: postfix) on value of xml key + Note: this is example template, and payloads/matchers need to be modified appropriately. + +http: + - filters: + - type: dsl + dsl: + - method != "GET" + - method != "HEAD" + - contains(content_type, "application/xml") + - contains(path, "/user") # for scope of integration test + condition: and + + payloads: + injection: + - "'" + - "\"" + - ";" + + fuzzing: + - part: body + type: postfix + mode: single + fuzz: + - '{{injection}}' + + stop-at-first-match: true + matchers: + - type: word + words: + - "unrecognized token:" + - "null" diff --git a/integration_tests/fuzz/fuzz-cookie-error-sqli.yaml b/integration_tests/fuzz/fuzz-cookie-error-sqli.yaml new file mode 100644 index 00000000..86bb2a16 --- /dev/null +++ b/integration_tests/fuzz/fuzz-cookie-error-sqli.yaml @@ -0,0 +1,59 @@ +id: cookie-fuzzing-error-sqli + +info: + name: fuzzing error sqli payloads in cookie + author: pdteam + severity: info + description: | + This template attempts to find SQL injection vulnerabilities by fuzzing http cookies with SQL injection payloads. + Note: this is example template, and payloads/matchers need to be modified appropriately. + +http: + - filters: + - type: dsl + dsl: + - 'method == "GET"' + - len(cookie) > 0 + condition: and + + payloads: + sqli: + - "'" + - '' + - '`' + - '``' + - ',' + - '"' + - "" + - / + - // + - \ + - \\ + - ; + - -- or # + - '" OR 1 = 1 -- -' + - ' OR '' = ' + - '=' + - 'LIKE' + - "'=0--+" + - OR 1=1 + - "' OR 'x'='x" + - "' AND id IS NULL; --" + - "'''''''''''''UNION SELECT '2" + - '%00' + + fuzzing: + - part: cookie + type: postfix + mode: single + fuzz: + - '{{sqli}}' + + stop-at-first-match: true + matchers: + - type: word + words: + - "unrecognized token:" + - "syntax error" + - "null" + - "SELECTs to the left and right of UNION do not have the same number of result columns" diff --git a/integration_tests/fuzz/fuzz-header-basic.yaml b/integration_tests/fuzz/fuzz-header-basic.yaml index 1441878a..10d2928c 100644 --- a/integration_tests/fuzz/fuzz-header-basic.yaml +++ b/integration_tests/fuzz/fuzz-header-basic.yaml @@ -28,7 +28,7 @@ http: - "'\"><{{first}}" fuzzing: - - part: headers + - part: header type: replace mode: single keys: ["Origin"] diff --git a/integration_tests/fuzz/fuzz-header-multiple.yaml b/integration_tests/fuzz/fuzz-header-multiple.yaml index 04b88b1f..0a535b57 100644 --- a/integration_tests/fuzz/fuzz-header-multiple.yaml +++ b/integration_tests/fuzz/fuzz-header-multiple.yaml @@ -25,7 +25,7 @@ http: - "secret.local" fuzzing: - - part: headers + - part: header type: replace mode: multiple keys: ["Origin", "X-Forwared-For"] diff --git a/integration_tests/fuzz/fuzz-host-header-injection.yaml b/integration_tests/fuzz/fuzz-host-header-injection.yaml new file mode 100644 index 00000000..cda22235 --- /dev/null +++ b/integration_tests/fuzz/fuzz-host-header-injection.yaml @@ -0,0 +1,43 @@ +id: host-header-injection + +info: + name: Host Header Injection + author: pdteam + severity: info + description: Host header injection + +variables: + domain: "oast.fun" + +http: + - filters: + - type: dsl + dsl: + - 'method == "GET"' + - 'contains(path,"/host-header-lab")' # for integration testing only + condition: and + + fuzzing: + - part: header + type: replace + mode: single + fuzz: + X-Forwarded-For: "{{domain}}" + X-Forwarded-Host: "{{domain}}" + Forwarded: "{{domain}}" + X-Real-IP: "{{domain}}" + X-Original-URL: "{{domain}}" + X-Rewrite-URL: "{{domain}}" + Host: "{{domain}}" + # " Host": "{{domain}}" # space before host (not supported yet due to lack of unsafe mode) + + matchers: + - type: status + status: + - 200 + + - type: word + part: body + words: + - "Interactsh" + matchers-condition: and \ No newline at end of file diff --git a/integration_tests/fuzz/fuzz-mode.yaml b/integration_tests/fuzz/fuzz-mode.yaml index d8664083..549eb54e 100644 --- a/integration_tests/fuzz/fuzz-mode.yaml +++ b/integration_tests/fuzz/fuzz-mode.yaml @@ -5,7 +5,7 @@ info: author: pdteam severity: info -requests: +http: - method: GET path: - "{{BaseURL}}" diff --git a/integration_tests/fuzz/fuzz-path-sqli.yaml b/integration_tests/fuzz/fuzz-path-sqli.yaml new file mode 100644 index 00000000..e034dec3 --- /dev/null +++ b/integration_tests/fuzz/fuzz-path-sqli.yaml @@ -0,0 +1,42 @@ +id: path-based-sqli + +info: + name: Path Based SQLi + author: pdteam + severity: info + description: | + This template attempts to find SQL injection vulnerabilities on path based sqli and replacing numerical values with fuzzing payloads. + ex: /admin/user/55/profile , /user/15/action/update, /posts/15, /blog/100/data, /page/51/ etc these types of paths are filtered and + replaced with sqli path payloads. + Note: this is example template, and payloads/matchers need to be modified appropriately. + +http: + - filters: + - type: dsl + dsl: + - 'method == "GET"' + - regex("/(.*?/)([0-9]+)(/.*)?",path) + condition: and + + payloads: + pathsqli: + - "'OR1=1" + - '%20OR%20True' + + fuzzing: + - part: path + type: replace-regex + mode: single + replace-regex: '/(.*?/)([0-9]+)(/.*)?' + fuzz: + - '/${1}${2}{{pathsqli}}${3}' + + matchers: + - type: status + status: + - 200 + + - type: word + words: + - "admin" + matchers-condition: and \ No newline at end of file diff --git a/integration_tests/fuzz/fuzz-query-num-replace.yaml b/integration_tests/fuzz/fuzz-query-num-replace.yaml new file mode 100644 index 00000000..90f3a393 --- /dev/null +++ b/integration_tests/fuzz/fuzz-query-num-replace.yaml @@ -0,0 +1,39 @@ +id: fuzz-query-num + +info: + name: Fuzz Query Param For IDOR + author: pdteam + severity: info + description: Query Value Fuzzing using Fuzzing Rules + +http: + - filters: + - type: dsl + dsl: + - 'len(query) > 0' + # below filter is related to integration testing + - type: word + part: path + words: + - /blog/post + filters-condition: and + + payloads: + nums: + - 200 + - 201 + + fuzzing: + - part: query + type: replace + mode: multiple + values: + - "^[0-9]+$" # only if value is number + fuzz: + - '{{nums}}' + + matchers: + - type: status + status: + - 200 + diff --git a/integration_tests/fuzz/fuzz-query.yaml b/integration_tests/fuzz/fuzz-query.yaml index 3a0b672e..658ee553 100644 --- a/integration_tests/fuzz/fuzz-query.yaml +++ b/integration_tests/fuzz/fuzz-query.yaml @@ -5,7 +5,7 @@ info: author: pdteam severity: info -requests: +http: - method: GET path: - "{{BaseURL}}" diff --git a/integration_tests/fuzz/fuzz-type.yaml b/integration_tests/fuzz/fuzz-type.yaml index 860432c3..e2c20556 100644 --- a/integration_tests/fuzz/fuzz-type.yaml +++ b/integration_tests/fuzz/fuzz-type.yaml @@ -5,7 +5,7 @@ info: author: pdteam severity: info -requests: +http: - method: GET path: - "{{BaseURL}}" diff --git a/integration_tests/fuzz/testData/ginandjuice.proxify.yaml b/integration_tests/fuzz/testData/ginandjuice.proxify.yaml new file mode 100644 index 00000000..734ea39c --- /dev/null +++ b/integration_tests/fuzz/testData/ginandjuice.proxify.yaml @@ -0,0 +1,535 @@ +timestamp: 2024-02-20T19:24:13+05:30 +url: http://127.0.0.1:8082/blog/post?postId=3&source=proxify +request: + header: + Accept-Encoding: gzip + Connection: close + User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 + host: 127.0.0.1:8082 + method: GET + path: /blog/post + scheme: https + raw: |+ + GET /blog/post?postId=3&source=proxify HTTP/1.1 + Host: 127.0.0.1:8082 + Accept-Encoding: gzip + Connection: close + User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 + +response: + header: + Content-Encoding: gzip + Content-Type: text/html; charset=utf-8 + Date: Tue, 20 Feb 2024 13:54:13 GMT + Set-Cookie: AWSALB=9l3X2bRs5JVQp5cO8o7xLckrdo3FZIQzbS0ga0n4ctfDApb/nn0K6AiSLLAMbG7CCDHqKOj9kQdyj6T2RzBDULszJ+2Oy8KcyuOKhFsCGVTVabnJTkVwhyXLciFt; Expires=Tue, 27 Feb 2024 13:54:13 GMT; Path=/, AWSALBCORS=9l3X2bRs5JVQp5cO8o7xLckrdo3FZIQzbS0ga0n4ctfDApb/nn0K6AiSLLAMbG7CCDHqKOj9kQdyj6T2RzBDULszJ+2Oy8KcyuOKhFsCGVTVabnJTkVwhyXLciFt; Expires=Tue, 27 Feb 2024 13:54:13 GMT; Path=/; SameSite=None; Secure, session=ymwLa8dKmWjLl43BBpQnrp5LkFZraYkp; Secure; HttpOnly; SameSite=None + X-Backend: ea6c1d8c-a8e5-4bef-b8db-879bbb13cf62 + X-Frame-Options: SAMEORIGIN + raw: |+ + HTTP/1.1 200 OK + Connection: close + Content-Encoding: gzip + Content-Type: text/html; charset=utf-8 + Date: Tue, 20 Feb 2024 13:54:13 GMT + Set-Cookie: AWSALB=9l3X2bRs5JVQp5cO8o7xLckrdo3FZIQzbS0ga0n4ctfDApb/nn0K6AiSLLAMbG7CCDHqKOj9kQdyj6T2RzBDULszJ+2Oy8KcyuOKhFsCGVTVabnJTkVwhyXLciFt; Expires=Tue, 27 Feb 2024 13:54:13 GMT; Path=/ + Set-Cookie: AWSALBCORS=9l3X2bRs5JVQp5cO8o7xLckrdo3FZIQzbS0ga0n4ctfDApb/nn0K6AiSLLAMbG7CCDHqKOj9kQdyj6T2RzBDULszJ+2Oy8KcyuOKhFsCGVTVabnJTkVwhyXLciFt; Expires=Tue, 27 Feb 2024 13:54:13 GMT; Path=/; SameSite=None; Secure + Set-Cookie: session=ymwLa8dKmWjLl43BBpQnrp5LkFZraYkp; Secure; HttpOnly; SameSite=None + X-Backend: ea6c1d8c-a8e5-4bef-b8db-879bbb13cf62 + X-Frame-Options: SAMEORIGIN + +--- +timestamp: 2024-02-20T19:24:13+05:30 +url: http://127.0.0.1:8082/reset-password +request: + header: + Accept-Encoding: gzip + Connection: close + User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 + host: 127.0.0.1:8082 + method: GET + path: /blog/post + scheme: https + raw: |+ + POST /reset-password HTTP/1.1 + Host: 127.0.0.1:8082 + X-Forwarded-For: 127.0.0.1:8082 + Accept-Encoding: gzip + Connection: close + Content-Type: application/json + Content-Length: 23 + User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 + + {"password":"12345678"} +response: + header: + Content-Encoding: gzip + Content-Type: text/html; charset=utf-8 + Date: Tue, 20 Feb 2024 13:54:13 GMT + Set-Cookie: AWSALB=9l3X2bRs5JVQp5cO8o7xLckrdo3FZIQzbS0ga0n4ctfDApb/nn0K6AiSLLAMbG7CCDHqKOj9kQdyj6T2RzBDULszJ+2Oy8KcyuOKhFsCGVTVabnJTkVwhyXLciFt; Expires=Tue, 27 Feb 2024 13:54:13 GMT; Path=/, AWSALBCORS=9l3X2bRs5JVQp5cO8o7xLckrdo3FZIQzbS0ga0n4ctfDApb/nn0K6AiSLLAMbG7CCDHqKOj9kQdyj6T2RzBDULszJ+2Oy8KcyuOKhFsCGVTVabnJTkVwhyXLciFt; Expires=Tue, 27 Feb 2024 13:54:13 GMT; Path=/; SameSite=None; Secure, session=ymwLa8dKmWjLl43BBpQnrp5LkFZraYkp; Secure; HttpOnly; SameSite=None + X-Backend: ea6c1d8c-a8e5-4bef-b8db-879bbb13cf62 + X-Frame-Options: SAMEORIGIN + raw: |+ + HTTP/1.1 200 OK + Connection: close + Content-Encoding: gzip + Content-Type: text/html; charset=utf-8 + Date: Tue, 20 Feb 2024 13:54:13 GMT + Set-Cookie: AWSALB=9l3X2bRs5JVQp5cO8o7xLckrdo3FZIQzbS0ga0n4ctfDApb/nn0K6AiSLLAMbG7CCDHqKOj9kQdyj6T2RzBDULszJ+2Oy8KcyuOKhFsCGVTVabnJTkVwhyXLciFt; Expires=Tue, 27 Feb 2024 13:54:13 GMT; Path=/ + Set-Cookie: AWSALBCORS=9l3X2bRs5JVQp5cO8o7xLckrdo3FZIQzbS0ga0n4ctfDApb/nn0K6AiSLLAMbG7CCDHqKOj9kQdyj6T2RzBDULszJ+2Oy8KcyuOKhFsCGVTVabnJTkVwhyXLciFt; Expires=Tue, 27 Feb 2024 13:54:13 GMT; Path=/; SameSite=None; Secure + Set-Cookie: session=ymwLa8dKmWjLl43BBpQnrp5LkFZraYkp; Secure; HttpOnly; SameSite=None + X-Backend: ea6c1d8c-a8e5-4bef-b8db-879bbb13cf62 + X-Frame-Options: SAMEORIGIN + +--- +timestamp: 2024-02-20T19:24:13+06:30 +url: http://127.0.0.1:8082/user/55/profile +request: + header: + Accept-Encoding: gzip + Connection: close + User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 + host: 127.0.0.1:8082 + method: GET + path: /blog/post + scheme: https + raw: |+ + GET /user/55/profile HTTP/1.1 + Host: 127.0.0.1:8082 + Accept-Encoding: gzip + Connection: close + Content-Type: application/json + User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 + +response: + header: + Content-Type: application/json; charset=UTF-8 + Date: Tue, 27 Feb 2024 18:46:44 GMT + raw: |+ + HTTP/1.1 200 OK + Content-Type: application/json; charset=UTF-8 + Date: Tue, 27 Feb 2024 18:46:44 GMT + Content-Length: 47 + + {"ID":75,"Name":"user","Age":30,"Role":"user"} + +--- +timestamp: 2024-02-20T23:25:13+06:30 +url: http://127.0.0.1:8082/user +request: + header: + Accept-Encoding: gzip + Connection: close + Content-Type: application/json + User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 + host: 127.0.0.1:8082 + method: POST + path: /user + scheme: http + raw: |+ + POST /user HTTP/1.1 + Host: localhost:8082 + User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 + Accept: */* + Content-Length: 32 + Connection: close + Content-Type: application/json + + {"id": 7 , "name": "pdteam"} + +response: + header: + Content-Type: text/plain; charset=UTF-8 + Date: Tue, 27 Feb 2024 18:46:44 GMT + raw: |+ + HTTP/1.1 200 OK + Content-Type: text/plain; charset=UTF-8 + Date: Wed, 28 Feb 2024 13:58:52 GMT + Content-Length: 25 + + User updated successfully + +--- +timestamp: 2024-02-20T23:26:13+06:30 +url: http://127.0.0.1:8082/user +request: + header: + Accept-Encoding: gzip + Connection: close + Content-Type: application/x-www-form-urlencoded + User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 + host: 127.0.0.1:8082 + method: POST + path: /user + scheme: http + raw: |+ + POST /user HTTP/1.1 + Host: localhost:8082 + User-Agent: curl/8.1.2 + Accept: */* + Content-Length: 20 + Connection: close + Content-Type: application/x-www-form-urlencoded + + id=7&name=pdteam + +response: + header: + Content-Type: text/plain; charset=UTF-8 + Date: Tue, 27 Feb 2024 18:46:44 GMT + raw: |+ + HTTP/1.1 200 OK + Content-Type: text/plain; charset=UTF-8 + Date: Wed, 28 Feb 2024 13:58:52 GMT + Content-Length: 25 + + User updated successfully + +--- +timestamp: 2024-02-20T23:26:13+06:30 +url: http://127.0.0.1:8082/user +request: + header: + Accept-Encoding: gzip + Connection: close + Content-Type: multipart/form-data + User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 + host: 127.0.0.1:8082 + method: POST + path: /user + scheme: http + raw: |+ + POST /user HTTP/1.1 + Host: localhost:8082 + User-Agent: curl/8.1.2 + Accept: */* + Content-Length: 226 + Connection: close + Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW + + ------WebKitFormBoundary7MA4YWxkTrZu0gW + Content-Disposition: form-data; name="id" + + 7 + ------WebKitFormBoundary7MA4YWxkTrZu0gW + Content-Disposition: form-data; name="name" + + pdteam + ------WebKitFormBoundary7MA4YWxkTrZu0gW-- + +response: + header: + Content-Type: text/plain; charset=UTF-8 + Date: Tue, 27 Feb 2024 18:46:44 GMT + raw: |+ + HTTP/1.1 200 OK + Content-Type: text/plain; charset=UTF-8 + Date: Wed, 28 Feb 2024 13:58:52 GMT + Content-Length: 25 + + User updated successfully + +--- +--- +timestamp: 2024-02-20T19:25:13+06:30 +url: http://127.0.0.1:8082/blog/posts +request: + header: + Accept-Encoding: gzip + Connection: close + User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 + host: 127.0.0.1:8082 + Cookie: session=ymwLa8dKmWjLl43BBpQnrp5LkFZraYkp; lang=en + method: GET + path: /blog/posts + scheme: http + raw: |+ + GET /blog/posts HTTP/1.1 + Host: 127.0.0.1:8082 + Accept-Encoding: gzip + Cookie: session=ymwLa8dKmWjLl43BBpQnrp5LkFZraYkp; lang=en + Connection: close + Content-Type: application/json + User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 + +response: + header: + Content-Type: application/json; charset=UTF-8 + Date: Tue, 27 Feb 2024 18:46:44 GMT + raw: |+ + HTTP/1.1 200 OK + Content-Type: application/json; charset=UTF-8 + Date: Wed, 28 Feb 2024 13:58:52 GMT + Content-Length: 218 + + [{"ID":1,"Title":"The Joy of Programming","Content":"Programming is like painting a canvas with logic.","Lang":"en"},{"ID":2,"Title":"A Journey Through Code","Content":"Every line of code tells a story.","Lang":"en"}] + +--- +timestamp: 2024-02-20T23:26:13+06:30 +url: http://127.0.0.1:8082/user +request: + header: + Accept-Encoding: gzip + Connection: close + Content-Type: application/xml + User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 + host: 127.0.0.1:8082 + method: POST + path: /user + scheme: http + raw: |+ + POST /user HTTP/1.1 + Host: localhost:8082 + User-Agent: curl/8.1.2 + Accept: */* + Content-Length: 76 + Connection: close + Content-Type: application/xml + + + + 7 + pdteam + + +response: + header: + Content-Type: text/plain; charset=UTF-8 + Date: Tue, 27 Feb 2024 18:46:44 GMT + raw: |+ + HTTP/1.1 200 OK + Content-Type: text/plain; charset=UTF-8 + Date: Wed, 28 Feb 2024 13:58:52 GMT + Content-Length: 25 + + User updated successfully + +--- +timestamp: 2024-02-20T19:24:13+05:32 +url: http://127.0.0.1:8082/host-header-lab +request: + header: + Accept-Encoding: gzip + Authorization: Bearer 3x4mpl3t0k3n + Connection: close + User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 + host: 127.0.0.1:8082 + method: POST + path: /catalog/product + scheme: https + raw: |+ + GET /host-header-lab HTTP/1.1 + Host: 127.0.0.1:8082 + Authorization: Bearer 3x4mpl3t0k3n + Accept-Encoding: gzip + Connection: close + User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 + +response: + header: + Content-Encoding: gzip + Content-Type: text/html; charset=utf-8 + Date: Tue, 20 Feb 2024 13:54:13 GMT + Set-Cookie: AWSALB=Rzm8ZSSL/yQq1mG1SdGmWAahqexWvOcjIhlNKm0wBnk4jvY3Hdy3mRc+NKoMRNJ2RW+FNbtRk7DX+itnfzjMvKNpwtLWWAafxeKybc+v351g0MsLycRNQF5fG78y; Expires=Tue, 27 Feb 2024 13:54:13 GMT; Path=/, AWSALBCORS=Rzm8ZSSL/yQq1mG1SdGmWAahqexWvOcjIhlNKm0wBnk4jvY3Hdy3mRc+NKoMRNJ2RW+FNbtRk7DX+itnfzjMvKNpwtLWWAafxeKybc+v351g0MsLycRNQF5fG78y; Expires=Tue, 27 Feb 2024 13:54:13 GMT; Path=/; SameSite=None; Secure, session=fFcCUjmQguQy820Y8xrnRypp3KBWSPk6; Secure; HttpOnly; SameSite=None + X-Backend: 2235790d-f089-4324-8ac0-f64cc96f2460 + X-Frame-Options: SAMEORIGIN + body: | + + + + + + + + + + Fruit Overlays - Product - Gin & Juice Shop + + +
+
+

+ This is a deliberately vulnerable web application designed for testing web vulnerability scanners. + Put your scanner to the test! +

+
+
+
+ + + +
+ +
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+

Fruit Overlays

+ + + + $92.79 + + + + + +

When it comes to hospitality presentation is key, and nothing looks better than a well-dressed drink. We know gin fans like plenty of fruit in their glasses, some a bit more than they really need, but hey ho, each to their own. But what about fruit not inside your glass, but classily arranged over your glass? In comes Fruitus overlayus, the best way to jazz up any party. The possible colour combinations are endless, just picture that! All you need is a nice selection of small fruits, or maybe even use our Fruit Curliwurlier to add a dash of even more drama, and we will do the rest. This one is a real winner at our Christmas and New year’s outings, give it a go and turn some heads.

+

CONTENTS: 12 cocktail sticks.

+

HOW TO USE: Let your creative juices flow (Pun intended), and spend some time working on your colour coordination, try not to think too much about it, just do it! Pick up one of the Fruitus overlayus sticks and carefully slide the fruit along until there is a small space on either end of the stick. Balance the stick across the rim of the glass. Ta-Da! Your first fruit overlay. Keep going until you have as many overlays as you need. You can always purchase more at any time with a discount on bulk buys.

+
+ +
+ + + +
+ + + +
+ +
+ + + + +
+
+ + View cart + +
+
+
+
+ +
+ + + + raw: |+ + HTTP/1.1 200 OK + Connection: close + Content-Encoding: gzip + Content-Type: text/html; charset=utf-8 + Date: Tue, 20 Feb 2024 13:54:13 GMT + Set-Cookie: AWSALB=Rzm8ZSSL/yQq1mG1SdGmWAahqexWvOcjIhlNKm0wBnk4jvY3Hdy3mRc+NKoMRNJ2RW+FNbtRk7DX+itnfzjMvKNpwtLWWAafxeKybc+v351g0MsLycRNQF5fG78y; Expires=Tue, 27 Feb 2024 13:54:13 GMT; Path=/ + Set-Cookie: AWSALBCORS=Rzm8ZSSL/yQq1mG1SdGmWAahqexWvOcjIhlNKm0wBnk4jvY3Hdy3mRc+NKoMRNJ2RW+FNbtRk7DX+itnfzjMvKNpwtLWWAafxeKybc+v351g0MsLycRNQF5fG78y; Expires=Tue, 27 Feb 2024 13:54:13 GMT; Path=/; SameSite=None; Secure + Set-Cookie: session=fFcCUjmQguQy820Y8xrnRypp3KBWSPk6; Secure; HttpOnly; SameSite=None + X-Backend: 2235790d-f089-4324-8ac0-f64cc96f2460 + X-Frame-Options: SAMEORIGIN \ No newline at end of file diff --git a/integration_tests/run.sh b/integration_tests/run.sh index 92b76ab1..f893e1b3 100755 --- a/integration_tests/run.sh +++ b/integration_tests/run.sh @@ -1,7 +1,7 @@ #!/bin/bash echo "::group::Build nuclei" -rm integration-test nuclei 2>/dev/null +rm integration-test fuzzplayground nuclei 2>/dev/null cd ../cmd/nuclei go build -race . mv nuclei ../../integration_tests/nuclei diff --git a/internal/runner/inputs.go b/internal/runner/inputs.go index 406986d9..8dc27a7a 100644 --- a/internal/runner/inputs.go +++ b/internal/runner/inputs.go @@ -8,6 +8,7 @@ import ( "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/hmap/store/hybrid" "github.com/projectdiscovery/httpx/common/httpx" + "github.com/projectdiscovery/nuclei/v3/pkg/input/provider" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" "github.com/projectdiscovery/nuclei/v3/pkg/utils" stringsutil "github.com/projectdiscovery/utils/strings" @@ -19,10 +20,15 @@ const probeBulkSize = 50 // initializeTemplatesHTTPInput initializes the http form of input // for any loaded http templates if input is in non-standard format. func (r *Runner) initializeTemplatesHTTPInput() (*hybrid.HybridMap, error) { + hm, err := hybrid.New(hybrid.DefaultDiskOptions) if err != nil { return nil, errors.Wrap(err, "could not create temporary input file") } + if r.inputProvider.InputType() == provider.MultiFormatInputProvider { + // currently http probing for input mode types is not supported + return hm, nil + } gologger.Info().Msgf("Running httpx on input host") var bulkSize = probeBulkSize @@ -41,7 +47,7 @@ func (r *Runner) initializeTemplatesHTTPInput() (*hybrid.HybridMap, error) { // Probe the non-standard URLs and store them in cache swg := sizedwaitgroup.New(bulkSize) count := int32(0) - r.hmapInputProvider.Scan(func(value *contextargs.MetaInput) bool { + r.inputProvider.Iterate(func(value *contextargs.MetaInput) bool { if stringsutil.HasPrefixAny(value.Input, "http://", "https://") { return true } diff --git a/internal/runner/lazy.go b/internal/runner/lazy.go new file mode 100644 index 00000000..193b22ff --- /dev/null +++ b/internal/runner/lazy.go @@ -0,0 +1,123 @@ +package runner + +import ( + "fmt" + + "github.com/projectdiscovery/nuclei/v3/pkg/authprovider/authx" + "github.com/projectdiscovery/nuclei/v3/pkg/catalog" + "github.com/projectdiscovery/nuclei/v3/pkg/catalog/loader" + "github.com/projectdiscovery/nuclei/v3/pkg/output" + "github.com/projectdiscovery/nuclei/v3/pkg/protocols" + "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" + "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/helpers/writer" + "github.com/projectdiscovery/nuclei/v3/pkg/scan" + "github.com/projectdiscovery/nuclei/v3/pkg/types" + errorutil "github.com/projectdiscovery/utils/errors" +) + +type AuthLazyFetchOptions struct { + TemplateStore *loader.Store + ExecOpts protocols.ExecutorOptions + OnError func(error) +} + +// GetAuthTemlStore create new loader for loading auth templates +func GetAuthTmplStore(opts types.Options, catalog catalog.Catalog, execOpts protocols.ExecutorOptions) (*loader.Store, error) { + tmpls := []string{} + for _, file := range opts.SecretsFile { + data, err := authx.GetTemplatePathsFromSecretFile(file) + if err != nil { + return nil, errorutil.NewWithErr(err).Msgf("failed to get template paths from secrets file") + } + tmpls = append(tmpls, data...) + } + opts.Templates = tmpls + opts.Workflows = nil + opts.RemoteTemplateDomainList = nil + opts.TemplateURLs = nil + opts.WorkflowURLs = nil + opts.ExcludedTemplates = nil + opts.Tags = nil + opts.ExcludeTags = nil + opts.IncludeTemplates = nil + opts.Authors = nil + opts.Severities = nil + opts.ExcludeSeverities = nil + opts.IncludeTags = nil + opts.IncludeIds = nil + opts.ExcludeIds = nil + opts.Protocols = nil + opts.ExcludeProtocols = nil + opts.IncludeConditions = nil + cfg := loader.NewConfig(&opts, catalog, execOpts) + store, err := loader.New(cfg) + if err != nil { + return nil, errorutil.NewWithErr(err).Msgf("failed to initialize dynamic auth templates store") + } + return store, nil +} + +// GetLazyAuthFetchCallback returns a lazy fetch callback for auth secrets +func GetLazyAuthFetchCallback(opts *AuthLazyFetchOptions) authx.LazyFetchSecret { + return func(d *authx.Dynamic) error { + tmpls := opts.TemplateStore.LoadTemplates([]string{d.TemplatePath}) + if len(tmpls) == 0 { + return fmt.Errorf("no templates found for path: %s", d.TemplatePath) + } + if len(tmpls) > 1 { + return fmt.Errorf("multiple templates found for path: %s", d.TemplatePath) + } + data := map[string]interface{}{} + tmpl := tmpls[0] + // add args to tmpl here + vars := map[string]interface{}{} + ctx := scan.NewScanContext(contextargs.NewWithInput(d.Input)) + for _, v := range d.Variables { + vars[v.Key] = v.Value + ctx.Input.Add(v.Key, v.Value) + } + + var finalErr error + ctx.OnResult = func(e *output.InternalWrappedEvent) { + if e == nil { + finalErr = fmt.Errorf("no result found for template: %s", d.TemplatePath) + return + } + if !e.HasOperatorResult() { + finalErr = fmt.Errorf("no result found for template: %s", d.TemplatePath) + return + } + // dynamic values + for k, v := range e.OperatorsResult.DynamicValues { + if len(v) > 0 { + data[k] = v[0] + } + } + // named extractors + for k, v := range e.OperatorsResult.Extracts { + if len(v) > 0 { + data[k] = v[0] + } + } + if len(data) == 0 { + if e.OperatorsResult.Matched { + finalErr = fmt.Errorf("match found but no (dynamic/extracted) values found for template: %s", d.TemplatePath) + } else { + finalErr = fmt.Errorf("no match or (dynamic/extracted) values found for template: %s", d.TemplatePath) + } + } + // log result of template in result file/screen + _ = writer.WriteResult(e, opts.ExecOpts.Output, opts.ExecOpts.Progress, opts.ExecOpts.IssuesClient) + } + _, err := tmpl.Executer.ExecuteWithResults(ctx) + if err != nil { + finalErr = err + } + // store extracted result in auth context + d.Extracted = data + if finalErr != nil && opts.OnError != nil { + opts.OnError(finalErr) + } + return finalErr + } +} diff --git a/internal/runner/options.go b/internal/runner/options.go index 21a2c213..bc89a45b 100644 --- a/internal/runner/options.go +++ b/internal/runner/options.go @@ -289,6 +289,12 @@ func createReportingOptions(options *types.Options) (*reporting.Options, error) // configureOutput configures the output logging levels to be displayed on the screen func configureOutput(options *types.Options) { + // disable standard logger (ref: https://github.com/golang/go/issues/19895) + defer logutil.DisableDefaultLogger() + + if options.NoColor { + gologger.DefaultLogger.SetFormatter(formatter.NewCLI(true)) + } // If the user desires verbose output, show verbose output if options.Debug || options.DebugRequests || options.DebugResponse { gologger.DefaultLogger.SetMaxLevel(levels.LevelDebug) @@ -304,9 +310,6 @@ func configureOutput(options *types.Options) { if options.Silent { gologger.DefaultLogger.SetMaxLevel(levels.LevelSilent) } - - // disable standard logger (ref: https://github.com/golang/go/issues/19895) - logutil.DisableDefaultLogger() } // loadResolvers loads resolvers from both user-provided flags and file diff --git a/internal/runner/runner.go b/internal/runner/runner.go index a8a29d81..0986760c 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -14,6 +14,8 @@ import ( "time" "github.com/projectdiscovery/nuclei/v3/internal/pdcp" + "github.com/projectdiscovery/nuclei/v3/pkg/authprovider" + "github.com/projectdiscovery/nuclei/v3/pkg/input/provider" "github.com/projectdiscovery/nuclei/v3/pkg/installer" uncoverlib "github.com/projectdiscovery/uncover" pdcpauth "github.com/projectdiscovery/utils/auth/pdcp" @@ -33,7 +35,6 @@ import ( "github.com/projectdiscovery/nuclei/v3/pkg/catalog/disk" "github.com/projectdiscovery/nuclei/v3/pkg/catalog/loader" "github.com/projectdiscovery/nuclei/v3/pkg/core" - "github.com/projectdiscovery/nuclei/v3/pkg/core/inputs/hybrid" "github.com/projectdiscovery/nuclei/v3/pkg/external/customtemplates" "github.com/projectdiscovery/nuclei/v3/pkg/input" "github.com/projectdiscovery/nuclei/v3/pkg/output" @@ -69,22 +70,21 @@ var ( // Runner is a client for running the enumeration process. type Runner struct { - output output.Writer - interactsh *interactsh.Client - options *types.Options - projectFile *projectfile.ProjectFile - catalog catalog.Catalog - progress progress.Progress - colorizer aurora.Aurora - issuesClient reporting.Client - hmapInputProvider *hybrid.Input - browser *engine.Browser - rateLimiter *ratelimit.Limiter - hostErrors hosterrorscache.CacheInterface - resumeCfg *types.ResumeCfg - pprofServer *http.Server - // pdcp auto-save options + output output.Writer + interactsh *interactsh.Client + options *types.Options + projectFile *projectfile.ProjectFile + catalog catalog.Catalog + progress progress.Progress + colorizer aurora.Aurora + issuesClient reporting.Client + browser *engine.Browser + rateLimiter *ratelimit.Limiter + hostErrors hosterrorscache.CacheInterface + resumeCfg *types.ResumeCfg + pprofServer *http.Server pdcpUploadErrMsg string + inputProvider provider.InputProvider //general purpose temporary directory tmpDir string } @@ -219,14 +219,12 @@ func New(options *types.Options) (*Runner, error) { os.Exit(0) } - // Initialize the input source - hmapInput, err := hybrid.New(&hybrid.Options{ - Options: options, - }) + // create the input provider and load the inputs + inputProvider, err := provider.NewInputProvider(provider.InputOptions{Options: options}) if err != nil { return nil, errors.Wrap(err, "could not create input provider") } - runner.hmapInputProvider = hmapInput + runner.inputProvider = inputProvider // Create the output file if asked outputWriter, err := output.NewStandardWriter(options) @@ -344,7 +342,9 @@ func (r *Runner) Close() { if r.projectFile != nil { r.projectFile.Close() } - r.hmapInputProvider.Close() + if r.inputProvider != nil { + r.inputProvider.Close() + } protocolinit.Close() if r.pprofServer != nil { _ = r.pprofServer.Shutdown(context.Background()) @@ -433,6 +433,24 @@ func (r *Runner) RunEnumeration() error { TemporaryDirectory: r.tmpDir, } + if len(r.options.SecretsFile) > 0 && !r.options.Validate { + authTmplStore, err := GetAuthTmplStore(*r.options, r.catalog, executorOpts) + if err != nil { + return errors.Wrap(err, "failed to load dynamic auth templates") + } + authOpts := &authprovider.AuthProviderOptions{SecretsFiles: r.options.SecretsFile} + authOpts.LazyFetchSecret = GetLazyAuthFetchCallback(&AuthLazyFetchOptions{ + TemplateStore: authTmplStore, + ExecOpts: executorOpts, + }) + // initialize auth provider + provider, err := authprovider.NewAuthProvider(authOpts) + if err != nil { + return errors.Wrap(err, "could not create auth provider") + } + executorOpts.AuthProvider = provider + } + if r.options.ShouldUseHostError() { cache := hosterrorscache.New(r.options.MaxHostError, hosterrorscache.DefaultMaxHostsCount, r.options.TrackError) cache.SetVerbose(r.options.Verbose) @@ -449,9 +467,16 @@ func (r *Runner) RunEnumeration() error { } executorOpts.WorkflowLoader = workflowLoader - store, err := loader.New(loader.NewConfig(r.options, r.catalog, executorOpts)) + // If using input-file flags, only load http fuzzing based templates. + loaderConfig := loader.NewConfig(r.options, r.catalog, executorOpts) + if !strings.EqualFold(r.options.InputFileMode, "list") || r.options.FuzzTemplates { + // if input type is not list (implicitly enable fuzzing) + r.options.FuzzTemplates = true + loaderConfig.OnlyLoadHTTPFuzzing = true + } + store, err := loader.New(loaderConfig) if err != nil { - return errors.Wrap(err, "could not load templates from config") + return errors.Wrap(err, "Could not create loader.") } if r.options.Validate { @@ -484,7 +509,7 @@ func (r *Runner) RunEnumeration() error { } ret := uncover.GetUncoverTargetsFromMetadata(context.TODO(), store.Templates(), r.options.UncoverField, uncoverOpts) for host := range ret { - r.hmapInputProvider.SetWithExclusions(host) + _ = r.inputProvider.SetWithExclusions(host) } } // list all templates @@ -496,6 +521,14 @@ func (r *Runner) RunEnumeration() error { // display execution info like version , templates used etc r.displayExecutionInfo(store) + // prefetch secrets if enabled + if executorOpts.AuthProvider != nil && r.options.PreFetchSecrets { + gologger.Info().Msgf("Pre-fetching secrets from authprovider[s]") + if err := executorOpts.AuthProvider.PreFetchSecrets(); err != nil { + return errors.Wrap(err, "could not pre-fetch secrets") + } + } + // If not explicitly disabled, check if http based protocols // are used, and if inputs are non-http to pre-perform probing // of urls and storing them for execution. @@ -541,7 +574,7 @@ func (r *Runner) RunEnumeration() error { func (r *Runner) isInputNonHTTP() bool { var nonURLInput bool - r.hmapInputProvider.Scan(func(value *contextargs.MetaInput) bool { + r.inputProvider.Iterate(func(value *contextargs.MetaInput) bool { if !strings.Contains(value.Input, "://") { nonURLInput = true return false @@ -552,13 +585,13 @@ func (r *Runner) isInputNonHTTP() bool { } func (r *Runner) executeSmartWorkflowInput(executorOpts protocols.ExecutorOptions, store *loader.Store, engine *core.Engine) (*atomic.Bool, error) { - r.progress.Init(r.hmapInputProvider.Count(), 0, 0) + r.progress.Init(r.inputProvider.Count(), 0, 0) service, err := automaticscan.New(automaticscan.Options{ ExecuterOpts: executorOpts, Store: store, Engine: engine, - Target: r.hmapInputProvider, + Target: r.inputProvider, }) if err != nil { return nil, errors.Wrap(err, "could not create automatic scan service") @@ -589,7 +622,12 @@ func (r *Runner) executeTemplatesInput(store *loader.Store, engine *core.Engine) return nil, errors.New("no templates provided for scan") } - results := engine.ExecuteScanWithOpts(finalTemplates, r.hmapInputProvider, r.options.DisableClustering) + // pass input provider to engine + // TODO: this should be not necessary after r.hmapInputProvider is removed + refactored + if r.inputProvider == nil { + return nil, errors.New("no input provider found") + } + results := engine.ExecuteScanWithOpts(finalTemplates, r.inputProvider, r.options.DisableClustering) return results, nil } @@ -603,6 +641,7 @@ func (r *Runner) displayExecutionInfo(store *loader.Store) { // only print these stats in verbose mode stats.DisplayAsWarning(parsers.HeadlessFlagWarningStats) stats.DisplayAsWarning(parsers.CodeFlagWarningStats) + stats.DisplayAsWarning(parsers.FuzzFlagWarningStats) stats.DisplayAsWarning(parsers.TemplatesExecutedStats) } @@ -643,8 +682,9 @@ func (r *Runner) displayExecutionInfo(store *loader.Store) { } } } - if r.hmapInputProvider.Count() > 0 { - gologger.Info().Msgf("Targets loaded for current scan: %d", r.hmapInputProvider.Count()) + + if r.inputProvider.Count() > 0 { + gologger.Info().Msgf("Targets loaded for current scan: %d", r.inputProvider.Count()) } } diff --git a/lib/config.go b/lib/config.go index 65f3fc8c..58fab00a 100644 --- a/lib/config.go +++ b/lib/config.go @@ -4,9 +4,11 @@ import ( "context" "time" + "github.com/projectdiscovery/goflags" "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/ratelimit" + "github.com/projectdiscovery/nuclei/v3/pkg/authprovider" "github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity" "github.com/projectdiscovery/nuclei/v3/pkg/output" "github.com/projectdiscovery/nuclei/v3/pkg/progress" @@ -163,7 +165,7 @@ func EnableHeadlessWithOpts(hopts *HeadlessOpts) NucleiSDKOptions { if err != nil { return err } - e.executerOpts.Browser = browser + e.browserInstance = browser return nil } } @@ -356,3 +358,28 @@ func EnablePassiveMode() NucleiSDKOptions { return nil } } + +// WithAuthOptions allows setting a custom authprovider implementation +func WithAuthProvider(provider authprovider.AuthProvider) NucleiSDKOptions { + return func(e *NucleiEngine) error { + e.authprovider = provider + return nil + } +} + +// LoadSecretsFromFile allows loading secrets from file +func LoadSecretsFromFile(files []string, prefetch bool) NucleiSDKOptions { + return func(e *NucleiEngine) error { + e.opts.SecretsFile = goflags.StringSlice(files) + e.opts.PreFetchSecrets = prefetch + return nil + } +} + +// EnableFuzzTemplates allows enabling template fuzzing +func EnableFuzzTemplates() NucleiSDKOptions { + return func(e *NucleiEngine) error { + e.opts.FuzzTemplates = true + return nil + } +} diff --git a/lib/multi.go b/lib/multi.go index e09ea40b..7a3dd7ce 100644 --- a/lib/multi.go +++ b/lib/multi.go @@ -8,11 +8,10 @@ import ( "github.com/projectdiscovery/nuclei/v3/pkg/catalog/config" "github.com/projectdiscovery/nuclei/v3/pkg/catalog/loader" "github.com/projectdiscovery/nuclei/v3/pkg/core" - "github.com/projectdiscovery/nuclei/v3/pkg/core/inputs" + "github.com/projectdiscovery/nuclei/v3/pkg/input/provider" "github.com/projectdiscovery/nuclei/v3/pkg/output" "github.com/projectdiscovery/nuclei/v3/pkg/parsers" "github.com/projectdiscovery/nuclei/v3/pkg/protocols" - "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" "github.com/projectdiscovery/nuclei/v3/pkg/types" "github.com/projectdiscovery/ratelimit" errorutil "github.com/projectdiscovery/utils/errors" @@ -123,14 +122,7 @@ func (e *ThreadSafeNucleiEngine) ExecuteNucleiWithOpts(targets []string, opts .. } store.Load() - inputProvider := &inputs.SimpleInputProvider{ - Inputs: []*contextargs.MetaInput{}, - } - - // load targets - for _, target := range targets { - inputProvider.Set(target) - } + inputProvider := provider.NewSimpleInputProviderWithUrls(targets...) if len(store.Templates()) == 0 && len(store.Workflows()) == 0 { return ErrNoTemplatesAvailable diff --git a/lib/sdk.go b/lib/sdk.go index 70a28299..69978409 100644 --- a/lib/sdk.go +++ b/lib/sdk.go @@ -5,11 +5,12 @@ import ( "bytes" "io" - "github.com/projectdiscovery/httpx/common/httpx" + "github.com/projectdiscovery/nuclei/v3/pkg/authprovider" "github.com/projectdiscovery/nuclei/v3/pkg/catalog/disk" "github.com/projectdiscovery/nuclei/v3/pkg/catalog/loader" "github.com/projectdiscovery/nuclei/v3/pkg/core" - "github.com/projectdiscovery/nuclei/v3/pkg/core/inputs" + "github.com/projectdiscovery/nuclei/v3/pkg/input/provider" + providerTypes "github.com/projectdiscovery/nuclei/v3/pkg/input/types" "github.com/projectdiscovery/nuclei/v3/pkg/output" "github.com/projectdiscovery/nuclei/v3/pkg/parsers" "github.com/projectdiscovery/nuclei/v3/pkg/progress" @@ -65,12 +66,13 @@ type NucleiEngine struct { catalog *disk.DiskCatalog rateLimiter *ratelimit.Limiter store *loader.Store - httpxClient *httpx.HTTPX - inputProvider *inputs.SimpleInputProvider + httpxClient providerTypes.InputLivenessProbe + inputProvider provider.InputProvider engine *core.Engine mode engineMode browserInstance *engine.Browser httpClient *retryablehttp.Client + authprovider authprovider.AuthProvider // unexported meta options opts *types.Options @@ -110,7 +112,7 @@ func (e *NucleiEngine) GetTemplates() []*templates.Template { func (e *NucleiEngine) LoadTargets(targets []string, probeNonHttp bool) { for _, target := range targets { if probeNonHttp { - e.inputProvider.SetWithProbe(target, e.httpxClient) + _ = e.inputProvider.SetWithProbe(target, e.httpxClient) } else { e.inputProvider.Set(target) } @@ -122,13 +124,29 @@ func (e *NucleiEngine) LoadTargetsFromReader(reader io.Reader, probeNonHttp bool buff := bufio.NewScanner(reader) for buff.Scan() { if probeNonHttp { - e.inputProvider.SetWithProbe(buff.Text(), e.httpxClient) + _ = e.inputProvider.SetWithProbe(buff.Text(), e.httpxClient) } else { e.inputProvider.Set(buff.Text()) } } } +// LoadTargetsWithHttpData loads targets that contain http data from file it currently supports +// multiple formats like burp xml,openapi,swagger,proxify json +// Note: this is mutually exclusive with LoadTargets and LoadTargetsFromReader +func (e *NucleiEngine) LoadTargetsWithHttpData(filePath string, filemode string) error { + e.opts.TargetsFilePath = filePath + e.opts.InputFileMode = filemode + httpProvider, err := provider.NewInputProvider(provider.InputOptions{Options: e.opts}) + if err != nil { + e.opts.TargetsFilePath = "" + e.opts.InputFileMode = "" + return err + } + e.inputProvider = httpProvider + return nil +} + // GetExecuterOptions returns the nuclei executor options func (e *NucleiEngine) GetExecuterOptions() *protocols.ExecutorOptions { return &e.executerOpts diff --git a/lib/sdk_private.go b/lib/sdk_private.go index a9a2b4be..2e3f1cb1 100644 --- a/lib/sdk_private.go +++ b/lib/sdk_private.go @@ -8,19 +8,20 @@ import ( "time" "github.com/logrusorgru/aurora" + "github.com/pkg/errors" "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/gologger/levels" "github.com/projectdiscovery/httpx/common/httpx" "github.com/projectdiscovery/nuclei/v3/internal/runner" + "github.com/projectdiscovery/nuclei/v3/pkg/authprovider" "github.com/projectdiscovery/nuclei/v3/pkg/catalog/config" "github.com/projectdiscovery/nuclei/v3/pkg/catalog/disk" "github.com/projectdiscovery/nuclei/v3/pkg/core" - "github.com/projectdiscovery/nuclei/v3/pkg/core/inputs" + "github.com/projectdiscovery/nuclei/v3/pkg/input/provider" "github.com/projectdiscovery/nuclei/v3/pkg/installer" "github.com/projectdiscovery/nuclei/v3/pkg/output" "github.com/projectdiscovery/nuclei/v3/pkg/progress" "github.com/projectdiscovery/nuclei/v3/pkg/protocols" - "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/hosterrorscache" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/interactsh" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolinit" @@ -29,6 +30,7 @@ import ( "github.com/projectdiscovery/nuclei/v3/pkg/reporting" "github.com/projectdiscovery/nuclei/v3/pkg/testutils" "github.com/projectdiscovery/nuclei/v3/pkg/types" + nucleiUtils "github.com/projectdiscovery/nuclei/v3/pkg/utils" "github.com/projectdiscovery/ratelimit" ) @@ -86,9 +88,7 @@ func (e *NucleiEngine) applyRequiredDefaults() { // and idea is to disable them to avoid false positives e.opts.ExcludeTags = append(e.opts.ExcludeTags, config.ReadIgnoreFile().Tags...) - e.inputProvider = &inputs.SimpleInputProvider{ - Inputs: []*contextargs.MetaInput{}, - } + e.inputProvider = provider.NewSimpleInputProvider() } // init @@ -158,6 +158,33 @@ func (e *NucleiEngine) init() error { ResumeCfg: types.NewResumeCfg(), Browser: e.browserInstance, } + if len(e.opts.SecretsFile) > 0 { + authTmplStore, err := runner.GetAuthTmplStore(*e.opts, e.catalog, e.executerOpts) + if err != nil { + return errors.Wrap(err, "failed to load dynamic auth templates") + } + authOpts := &authprovider.AuthProviderOptions{SecretsFiles: e.opts.SecretsFile} + authOpts.LazyFetchSecret = runner.GetLazyAuthFetchCallback(&runner.AuthLazyFetchOptions{ + TemplateStore: authTmplStore, + ExecOpts: e.executerOpts, + }) + // initialize auth provider + provider, err := authprovider.NewAuthProvider(authOpts) + if err != nil { + return errors.Wrap(err, "could not create auth provider") + } + e.executerOpts.AuthProvider = provider + } + if e.authprovider != nil { + e.executerOpts.AuthProvider = e.authprovider + } + + // prefetch secrets + if e.executerOpts.AuthProvider != nil && e.opts.PreFetchSecrets { + if err := e.executerOpts.AuthProvider.PreFetchSecrets(); err != nil { + return errors.Wrap(err, "could not prefetch secrets") + } + } if e.opts.RateLimitMinute > 0 { e.executerOpts.RateLimiter = ratelimit.New(context.Background(), uint(e.opts.RateLimitMinute), time.Minute) @@ -172,8 +199,10 @@ func (e *NucleiEngine) init() error { httpxOptions := httpx.DefaultOptions httpxOptions.Timeout = 5 * time.Second - if e.httpxClient, err = httpx.New(&httpxOptions); err != nil { + if client, err := httpx.New(&httpxOptions); err != nil { return err + } else { + e.httpxClient = nucleiUtils.GetInputLivenessChecker(client) } // Only Happens once regardless how many times this function is called diff --git a/pkg/authprovider/authx/basic_auth.go b/pkg/authprovider/authx/basic_auth.go new file mode 100644 index 00000000..b7579066 --- /dev/null +++ b/pkg/authprovider/authx/basic_auth.go @@ -0,0 +1,31 @@ +package authx + +import ( + "net/http" + + "github.com/projectdiscovery/retryablehttp-go" +) + +var ( + _ AuthStrategy = &BasicAuthStrategy{} +) + +// BasicAuthStrategy is a strategy for basic auth +type BasicAuthStrategy struct { + Data *Secret +} + +// NewBasicAuthStrategy creates a new basic auth strategy +func NewBasicAuthStrategy(data *Secret) *BasicAuthStrategy { + return &BasicAuthStrategy{Data: data} +} + +// Apply applies the basic auth strategy to the request +func (s *BasicAuthStrategy) Apply(req *http.Request) { + req.SetBasicAuth(s.Data.Username, s.Data.Password) +} + +// ApplyOnRR applies the basic auth strategy to the retryable request +func (s *BasicAuthStrategy) ApplyOnRR(req *retryablehttp.Request) { + req.SetBasicAuth(s.Data.Username, s.Data.Password) +} diff --git a/pkg/authprovider/authx/bearer_auth.go b/pkg/authprovider/authx/bearer_auth.go new file mode 100644 index 00000000..edf6f439 --- /dev/null +++ b/pkg/authprovider/authx/bearer_auth.go @@ -0,0 +1,31 @@ +package authx + +import ( + "net/http" + + "github.com/projectdiscovery/retryablehttp-go" +) + +var ( + _ AuthStrategy = &BearerTokenAuthStrategy{} +) + +// BearerTokenAuthStrategy is a strategy for bearer token auth +type BearerTokenAuthStrategy struct { + Data *Secret +} + +// NewBearerTokenAuthStrategy creates a new bearer token auth strategy +func NewBearerTokenAuthStrategy(data *Secret) *BearerTokenAuthStrategy { + return &BearerTokenAuthStrategy{Data: data} +} + +// Apply applies the bearer token auth strategy to the request +func (s *BearerTokenAuthStrategy) Apply(req *http.Request) { + req.Header.Set("Authorization", "Bearer "+s.Data.Token) +} + +// ApplyOnRR applies the bearer token auth strategy to the retryable request +func (s *BearerTokenAuthStrategy) ApplyOnRR(req *retryablehttp.Request) { + req.Header.Set("Authorization", "Bearer "+s.Data.Token) +} diff --git a/pkg/authprovider/authx/cookies_auth.go b/pkg/authprovider/authx/cookies_auth.go new file mode 100644 index 00000000..7f3e756a --- /dev/null +++ b/pkg/authprovider/authx/cookies_auth.go @@ -0,0 +1,43 @@ +package authx + +import ( + "net/http" + + "github.com/projectdiscovery/retryablehttp-go" +) + +var ( + _ AuthStrategy = &CookiesAuthStrategy{} +) + +// CookiesAuthStrategy is a strategy for cookies auth +type CookiesAuthStrategy struct { + Data *Secret +} + +// NewCookiesAuthStrategy creates a new cookies auth strategy +func NewCookiesAuthStrategy(data *Secret) *CookiesAuthStrategy { + return &CookiesAuthStrategy{Data: data} +} + +// Apply applies the cookies auth strategy to the request +func (s *CookiesAuthStrategy) Apply(req *http.Request) { + for _, cookie := range s.Data.Cookies { + c := &http.Cookie{ + Name: cookie.Key, + Value: cookie.Value, + } + req.AddCookie(c) + } +} + +// ApplyOnRR applies the cookies auth strategy to the retryable request +func (s *CookiesAuthStrategy) ApplyOnRR(req *retryablehttp.Request) { + for _, cookie := range s.Data.Cookies { + c := &http.Cookie{ + Name: cookie.Key, + Value: cookie.Value, + } + req.AddCookie(c) + } +} diff --git a/pkg/authprovider/authx/dynamic.go b/pkg/authprovider/authx/dynamic.go new file mode 100644 index 00000000..0e210cf5 --- /dev/null +++ b/pkg/authprovider/authx/dynamic.go @@ -0,0 +1,167 @@ +package authx + +import ( + "encoding/json" + "fmt" + "strings" + "sync" + + "github.com/projectdiscovery/gologger" + "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/replacer" + errorutil "github.com/projectdiscovery/utils/errors" +) + +type LazyFetchSecret func(d *Dynamic) error + +var ( + _ json.Unmarshaler = &Dynamic{} +) + +// Dynamic is a struct for dynamic secret or credential +// these are high level secrets that take action to generate the actual secret +// ex: username and password are dynamic secrets, the actual secret is the token obtained +// after authenticating with the username and password +type Dynamic struct { + Secret `yaml:",inline"` // this is a static secret that will be generated after the dynamic secret is resolved + TemplatePath string `json:"template" yaml:"template"` + Variables []KV `json:"variables" yaml:"variables"` + Input string `json:"input" yaml:"input"` // (optional) target for the dynamic secret + Extracted map[string]interface{} `json:"-" yaml:"-"` // extracted values from the dynamic secret + fetchCallback LazyFetchSecret `json:"-" yaml:"-"` + m *sync.Mutex `json:"-" yaml:"-"` // mutex for lazy fetch + fetched bool `json:"-" yaml:"-"` // flag to check if the secret has been fetched + error error `json:"-" yaml:"-"` // error if any +} + +func (d *Dynamic) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &d); err != nil { + return err + } + var s Secret + if err := json.Unmarshal(data, &s); err != nil { + return err + } + d.Secret = s + return nil +} + +// Validate validates the dynamic secret +func (d *Dynamic) Validate() error { + d.m = &sync.Mutex{} + if d.TemplatePath == "" { + return errorutil.New(" template-path is required for dynamic secret") + } + if len(d.Variables) == 0 { + return errorutil.New("variables are required for dynamic secret") + } + d.skipCookieParse = true // skip cookie parsing in dynamic secrets during validation + if err := d.Secret.Validate(); err != nil { + return err + } + return nil +} + +// SetLazyFetchCallback sets the lazy fetch callback for the dynamic secret +func (d *Dynamic) SetLazyFetchCallback(callback LazyFetchSecret) { + d.fetchCallback = func(d *Dynamic) error { + err := callback(d) + d.fetched = true + if err != nil { + d.error = err + return err + } + if len(d.Extracted) == 0 { + return fmt.Errorf("no extracted values found for dynamic secret") + } + + // evaluate headers + for i, header := range d.Headers { + if strings.Contains(header.Value, "{{") { + header.Value = replacer.Replace(header.Value, d.Extracted) + } + if strings.Contains(header.Key, "{{") { + header.Key = replacer.Replace(header.Key, d.Extracted) + } + d.Headers[i] = header + } + + // evaluate cookies + for i, cookie := range d.Cookies { + if strings.Contains(cookie.Value, "{{") { + cookie.Value = replacer.Replace(cookie.Value, d.Extracted) + } + if strings.Contains(cookie.Key, "{{") { + cookie.Key = replacer.Replace(cookie.Key, d.Extracted) + } + if strings.Contains(cookie.Raw, "{{") { + cookie.Raw = replacer.Replace(cookie.Raw, d.Extracted) + } + d.Cookies[i] = cookie + } + + // evaluate query params + for i, query := range d.Params { + if strings.Contains(query.Value, "{{") { + query.Value = replacer.Replace(query.Value, d.Extracted) + } + if strings.Contains(query.Key, "{{") { + query.Key = replacer.Replace(query.Key, d.Extracted) + } + d.Params[i] = query + } + + // check username, password and token + if strings.Contains(d.Username, "{{") { + d.Username = replacer.Replace(d.Username, d.Extracted) + } + if strings.Contains(d.Password, "{{") { + d.Password = replacer.Replace(d.Password, d.Extracted) + } + if strings.Contains(d.Token, "{{") { + d.Token = replacer.Replace(d.Token, d.Extracted) + } + + // now attempt to parse the cookies + d.skipCookieParse = false + for i, cookie := range d.Cookies { + if cookie.Raw != "" { + if err := cookie.Parse(); err != nil { + return fmt.Errorf("[%s] invalid raw cookie in cookiesAuth: %s", d.TemplatePath, err) + } + d.Cookies[i] = cookie + } + } + return nil + } +} + +// GetStrategy returns the auth strategy for the dynamic secret +func (d *Dynamic) GetStrategy() AuthStrategy { + if !d.fetched { + _ = d.Fetch(true) + } + if d.error != nil { + return nil + } + return d.Secret.GetStrategy() +} + +// Fetch fetches the dynamic secret +// if isFatal is true, it will stop the execution if the secret could not be fetched +func (d *Dynamic) Fetch(isFatal bool) error { + d.m.Lock() + defer d.m.Unlock() + if d.fetched { + return nil + } + d.error = d.fetchCallback(d) + if d.error != nil && isFatal { + gologger.Fatal().Msgf("Could not fetch dynamic secret: %s\n", d.error) + } + return d.error +} + +// Error returns the error if any +func (d *Dynamic) Error() error { + return d.error +} diff --git a/pkg/authprovider/authx/file.go b/pkg/authprovider/authx/file.go new file mode 100644 index 00000000..698aafe8 --- /dev/null +++ b/pkg/authprovider/authx/file.go @@ -0,0 +1,253 @@ +package authx + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "regexp" + "strings" + + errorutil "github.com/projectdiscovery/utils/errors" + "github.com/projectdiscovery/utils/generic" + stringsutil "github.com/projectdiscovery/utils/strings" + "gopkg.in/yaml.v3" +) + +type AuthType string + +const ( + BasicAuth AuthType = "BasicAuth" + BearerTokenAuth AuthType = "BearerToken" + HeadersAuth AuthType = "Header" + CookiesAuth AuthType = "Cookie" + QueryAuth AuthType = "Query" +) + +// SupportedAuthTypes returns the supported auth types +func SupportedAuthTypes() []string { + return []string{ + string(BasicAuth), + string(BearerTokenAuth), + string(HeadersAuth), + string(CookiesAuth), + string(QueryAuth), + } +} + +// Authx is a struct for secrets or credentials file +type Authx struct { + ID string `json:"id" yaml:"id"` + Info AuthFileInfo `json:"info" yaml:"info"` + Secrets []Secret `json:"static" yaml:"static"` + Dynamic []Dynamic `json:"dynamic" yaml:"dynamic"` +} + +type AuthFileInfo struct { + Name string `json:"name" yaml:"name"` + Author string `json:"author" yaml:"author"` + Severity string `json:"severity" yaml:"severity"` + Description string `json:"description" yaml:"description"` +} + +// Secret is a struct for secret or credential +type Secret struct { + Type string `json:"type" yaml:"type"` + Domains []string `json:"domains" yaml:"domains"` + DomainsRegex []string `json:"domains-regex" yaml:"domains-regex"` + Headers []KV `json:"headers" yaml:"headers"` + Cookies []Cookie `json:"cookies" yaml:"cookies"` + Params []KV `json:"params" yaml:"params"` + Username string `json:"username" yaml:"username"` // can be either email or username + Password string `json:"password" yaml:"password"` + Token string `json:"token" yaml:"token"` // Bearer Auth token + skipCookieParse bool `json:"-" yaml:"-"` // temporary flag to skip cookie parsing (used in dynamic secrets) +} + +// GetStrategy returns the auth strategy for the secret +func (s *Secret) GetStrategy() AuthStrategy { + switch { + case strings.EqualFold(s.Type, string(BasicAuth)): + return NewBasicAuthStrategy(s) + case strings.EqualFold(s.Type, string(BearerTokenAuth)): + return NewBearerTokenAuthStrategy(s) + case strings.EqualFold(s.Type, string(HeadersAuth)): + return NewHeadersAuthStrategy(s) + case strings.EqualFold(s.Type, string(CookiesAuth)): + return NewCookiesAuthStrategy(s) + case strings.EqualFold(s.Type, string(QueryAuth)): + return NewQueryAuthStrategy(s) + } + return nil +} + +func (s *Secret) Validate() error { + if !stringsutil.EqualFoldAny(s.Type, SupportedAuthTypes()...) { + return fmt.Errorf("invalid type: %s", s.Type) + } + if len(s.Domains) == 0 && len(s.DomainsRegex) == 0 { + return fmt.Errorf("domains or domains-regex cannot be empty") + } + if len(s.DomainsRegex) > 0 { + for _, domain := range s.DomainsRegex { + _, err := regexp.Compile(domain) + if err != nil { + return fmt.Errorf("invalid domain regex: %s", domain) + } + } + } + + switch { + case strings.EqualFold(s.Type, string(BasicAuth)): + if s.Username == "" { + return fmt.Errorf("username cannot be empty in basic auth") + } + if s.Password == "" { + return fmt.Errorf("password cannot be empty in basic auth") + } + case strings.EqualFold(s.Type, string(BearerTokenAuth)): + if s.Token == "" { + return fmt.Errorf("token cannot be empty in bearer token auth") + } + case strings.EqualFold(s.Type, string(HeadersAuth)): + if len(s.Headers) == 0 { + return fmt.Errorf("headers cannot be empty in headers auth") + } + for _, header := range s.Headers { + if err := header.Validate(); err != nil { + return fmt.Errorf("invalid header in headersAuth: %s", err) + } + } + case strings.EqualFold(s.Type, string(CookiesAuth)): + if len(s.Cookies) == 0 { + return fmt.Errorf("cookies cannot be empty in cookies auth") + } + for _, cookie := range s.Cookies { + if cookie.Raw != "" && !s.skipCookieParse { + if err := cookie.Parse(); err != nil { + return fmt.Errorf("invalid raw cookie in cookiesAuth: %s", err) + } + } + if err := cookie.Validate(); err != nil { + return fmt.Errorf("invalid cookie in cookiesAuth: %s", err) + } + } + case strings.EqualFold(s.Type, string(QueryAuth)): + if len(s.Params) == 0 { + return fmt.Errorf("query cannot be empty in query auth") + } + for _, query := range s.Params { + if err := query.Validate(); err != nil { + return fmt.Errorf("invalid query in queryAuth: %s", err) + } + } + default: + return fmt.Errorf("invalid type: %s", s.Type) + } + return nil +} + +type KV struct { + Key string `json:"key" yaml:"key"` + Value string `json:"value" yaml:"value"` +} + +func (k *KV) Validate() error { + if k.Key == "" { + return fmt.Errorf("key cannot be empty") + } + if k.Value == "" { + return fmt.Errorf("value cannot be empty") + } + return nil +} + +type Cookie struct { + Key string `json:"key" yaml:"key"` + Value string `json:"value" yaml:"value"` + Raw string `json:"raw" yaml:"raw"` +} + +func (c *Cookie) Validate() error { + if c.Raw != "" { + return nil + } + if c.Key == "" { + return fmt.Errorf("key cannot be empty") + } + if c.Value == "" { + return fmt.Errorf("value cannot be empty") + } + return nil +} + +// Parse parses the cookie +// in raw the cookie is in format of +// Set-Cookie: =; Expires=; Path=; Domain=; Secure; HttpOnly +func (c *Cookie) Parse() error { + if c.Raw == "" { + return fmt.Errorf("raw cookie cannot be empty") + } + tmp := strings.TrimPrefix(c.Raw, "Set-Cookie: ") + slice := strings.Split(tmp, ";") + if len(slice) == 0 { + return fmt.Errorf("invalid raw cookie no ; found") + } + // first element is the cookie name and value + cookie := strings.Split(slice[0], "=") + if len(cookie) == 2 { + c.Key = cookie[0] + c.Value = cookie[1] + return nil + } + return fmt.Errorf("invalid raw cookie: %s", c.Raw) +} + +// GetAuthDataFromFile reads the auth data from file +func GetAuthDataFromFile(file string) (*Authx, error) { + ext := filepath.Ext(file) + if !generic.EqualsAny(ext, ".yml", ".yaml", ".json") { + return nil, fmt.Errorf("invalid file extension: supported extensions are .yml,.yaml and .json got %s", ext) + } + bin, err := os.ReadFile(file) + if err != nil { + return nil, err + } + if ext == ".yml" || ext == ".yaml" { + return GetAuthDataFromYAML(bin) + } + return GetAuthDataFromJSON(bin) +} + +// GetTemplateIDsFromSecretFile reads the template IDs from the secret file +func GetTemplatePathsFromSecretFile(file string) ([]string, error) { + auth, err := GetAuthDataFromFile(file) + if err != nil { + return nil, err + } + var paths []string + for _, dynamic := range auth.Dynamic { + paths = append(paths, dynamic.TemplatePath) + } + return paths, nil +} + +// GetAuthDataFromYAML reads the auth data from yaml +func GetAuthDataFromYAML(data []byte) (*Authx, error) { + var auth Authx + err := yaml.Unmarshal(data, &auth) + if err != nil { + return nil, errorutil.NewWithErr(err).Msgf("could not unmarshal yaml") + } + return &auth, nil +} + +// GetAuthDataFromJSON reads the auth data from json +func GetAuthDataFromJSON(data []byte) (*Authx, error) { + var auth Authx + err := json.Unmarshal(data, &auth) + if err != nil { + return nil, errorutil.NewWithErr(err).Msgf("could not unmarshal json") + } + return &auth, nil +} diff --git a/pkg/authprovider/authx/file_test.go b/pkg/authprovider/authx/file_test.go new file mode 100644 index 00000000..0e7ada81 --- /dev/null +++ b/pkg/authprovider/authx/file_test.go @@ -0,0 +1,20 @@ +package authx + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestSecretsUnmarshal(t *testing.T) { + loc := "testData/example-auth.yaml" + data, err := GetAuthDataFromFile(loc) + require.Nil(t, err, "could not read secrets file") + require.NotNil(t, data, "could not read secrets file") + for _, s := range data.Secrets { + require.Nil(t, s.Validate(), "could not validate secret") + } + for _, d := range data.Dynamic { + require.Nil(t, d.Validate(), "could not validate dynamic") + } +} diff --git a/pkg/authprovider/authx/headers_auth.go b/pkg/authprovider/authx/headers_auth.go new file mode 100644 index 00000000..b3ede114 --- /dev/null +++ b/pkg/authprovider/authx/headers_auth.go @@ -0,0 +1,35 @@ +package authx + +import ( + "net/http" + + "github.com/projectdiscovery/retryablehttp-go" +) + +var ( + _ AuthStrategy = &HeadersAuthStrategy{} +) + +// HeadersAuthStrategy is a strategy for headers auth +type HeadersAuthStrategy struct { + Data *Secret +} + +// NewHeadersAuthStrategy creates a new headers auth strategy +func NewHeadersAuthStrategy(data *Secret) *HeadersAuthStrategy { + return &HeadersAuthStrategy{Data: data} +} + +// Apply applies the headers auth strategy to the request +func (s *HeadersAuthStrategy) Apply(req *http.Request) { + for _, header := range s.Data.Headers { + req.Header.Set(header.Key, header.Value) + } +} + +// ApplyOnRR applies the headers auth strategy to the retryable request +func (s *HeadersAuthStrategy) ApplyOnRR(req *retryablehttp.Request) { + for _, header := range s.Data.Headers { + req.Header.Set(header.Key, header.Value) + } +} diff --git a/pkg/authprovider/authx/query_auth.go b/pkg/authprovider/authx/query_auth.go new file mode 100644 index 00000000..796d8b1f --- /dev/null +++ b/pkg/authprovider/authx/query_auth.go @@ -0,0 +1,42 @@ +package authx + +import ( + "net/http" + + "github.com/projectdiscovery/retryablehttp-go" + urlutil "github.com/projectdiscovery/utils/url" +) + +var ( + _ AuthStrategy = &QueryAuthStrategy{} +) + +// QueryAuthStrategy is a strategy for query auth +type QueryAuthStrategy struct { + Data *Secret +} + +// NewQueryAuthStrategy creates a new query auth strategy +func NewQueryAuthStrategy(data *Secret) *QueryAuthStrategy { + return &QueryAuthStrategy{Data: data} +} + +// Apply applies the query auth strategy to the request +func (s *QueryAuthStrategy) Apply(req *http.Request) { + q := urlutil.NewOrderedParams() + q.Decode(req.URL.RawQuery) + for _, p := range s.Data.Params { + q.Add(p.Key, p.Value) + } + req.URL.RawQuery = q.Encode() +} + +// ApplyOnRR applies the query auth strategy to the retryable request +func (s *QueryAuthStrategy) ApplyOnRR(req *retryablehttp.Request) { + q := urlutil.NewOrderedParams() + q.Decode(req.Request.URL.RawQuery) + for _, p := range s.Data.Params { + q.Add(p.Key, p.Value) + } + req.Request.URL.RawQuery = q.Encode() +} diff --git a/pkg/authprovider/authx/strategy.go b/pkg/authprovider/authx/strategy.go new file mode 100644 index 00000000..82040839 --- /dev/null +++ b/pkg/authprovider/authx/strategy.go @@ -0,0 +1,39 @@ +package authx + +import ( + "net/http" + + "github.com/projectdiscovery/retryablehttp-go" +) + +// AuthStrategy is an interface for auth strategies +// basic auth , bearer token, headers, cookies, query +type AuthStrategy interface { + // Apply applies the strategy to the request + Apply(*http.Request) + // ApplyOnRR applies the strategy to the retryable request + ApplyOnRR(*retryablehttp.Request) +} + +// DynamicAuthStrategy is an auth strategy for dynamic secrets +// it implements the AuthStrategy interface +type DynamicAuthStrategy struct { + // Dynamic is the dynamic secret to use + Dynamic Dynamic +} + +// Apply applies the strategy to the request +func (d *DynamicAuthStrategy) Apply(req *http.Request) { + strategy := d.Dynamic.GetStrategy() + if strategy != nil { + strategy.Apply(req) + } +} + +// ApplyOnRR applies the strategy to the retryable request +func (d *DynamicAuthStrategy) ApplyOnRR(req *retryablehttp.Request) { + strategy := d.Dynamic.GetStrategy() + if strategy != nil { + strategy.ApplyOnRR(req) + } +} diff --git a/pkg/authprovider/authx/testData/example-auth.yaml b/pkg/authprovider/authx/testData/example-auth.yaml new file mode 100644 index 00000000..0c317509 --- /dev/null +++ b/pkg/authprovider/authx/testData/example-auth.yaml @@ -0,0 +1,70 @@ +id: pd-nuclei-auth-test + +info: + name: ProjectDiscovery Test Dev Servers + author: pdteam + description: | + This is a auth file for ProjectDiscovery dev servers. + It contains auth data of all projectdiscovery dev servers. + +# Note: this is a dummy example file. none of the secrets here are real. + +# static secrets +static: + # for header based auth session + - type: header + domains: + - api.projectdiscovery.io + - cve.projectdiscovery.io + - chaos.projectdiscovery.io + headers: + - key: x-pdcp-key + value: + + # for query based auth session + - type: Query + domains: + - scanme.sh + params: + - key: token + value: 1a2b3c4d5e6f7g8h9i0j + + # for cookie based auth session + - type: Cookie + domains: + - scanme.sh + cookies: + - key: PHPSESSID + value: 1a2b3c4d5e6f7g8h9i0j + + # for basic auth session + - type: BasicAuth + domains: + - scanme.sh + username: test + password: test + + # for authorization bearer token + - type: BearerToken + domains-regex: + - .*scanme.sh + - .*pdtm.sh + token: test + + +# dynamic secrets (powered by nuclei-templates) +dynamic: + - template: /path/to/wordpress-login.yaml + variables: + - name: username + value: pdteam + - name: password + value: nuclei-v3.2.0 + type: Cookie + domains: + - localhost:8080 + cookies: + - raw: "{{wp-global-cookie}}" + - raw: "{{wp-admin-cookie}}" + - raw: "{{wp-plugin-cookie}}" + diff --git a/pkg/authprovider/file.go b/pkg/authprovider/file.go new file mode 100644 index 00000000..77f45e40 --- /dev/null +++ b/pkg/authprovider/file.go @@ -0,0 +1,168 @@ +package authprovider + +import ( + "net" + "net/url" + "regexp" + "strings" + + "github.com/projectdiscovery/nuclei/v3/pkg/authprovider/authx" + errorutil "github.com/projectdiscovery/utils/errors" + urlutil "github.com/projectdiscovery/utils/url" +) + +// FileAuthProvider is an auth provider for file based auth +// it accepts a secrets file and returns its provider +type FileAuthProvider struct { + Path string + store *authx.Authx + compiled map[*regexp.Regexp]authx.AuthStrategy + domains map[string]authx.AuthStrategy +} + +// NewFileAuthProvider creates a new file based auth provider +func NewFileAuthProvider(path string, callback authx.LazyFetchSecret) (AuthProvider, error) { + store, err := authx.GetAuthDataFromFile(path) + if err != nil { + return nil, err + } + if len(store.Secrets) == 0 && len(store.Dynamic) == 0 { + return nil, ErrNoSecrets + } + if len(store.Dynamic) > 0 && callback == nil { + return nil, errorutil.New("lazy fetch callback is required for dynamic secrets") + } + for _, secret := range store.Secrets { + if err := secret.Validate(); err != nil { + return nil, errorutil.NewWithErr(err).Msgf("invalid secret in file: %s", path) + } + } + for i, dynamic := range store.Dynamic { + if err := dynamic.Validate(); err != nil { + return nil, errorutil.NewWithErr(err).Msgf("invalid dynamic in file: %s", path) + } + dynamic.SetLazyFetchCallback(callback) + store.Dynamic[i] = dynamic + } + f := &FileAuthProvider{Path: path, store: store} + f.init() + return f, nil +} + +// init initializes the file auth provider +func (f *FileAuthProvider) init() { + for _, secret := range f.store.Secrets { + if len(secret.DomainsRegex) > 0 { + for _, domain := range secret.DomainsRegex { + if f.compiled == nil { + f.compiled = make(map[*regexp.Regexp]authx.AuthStrategy) + } + compiled, err := regexp.Compile(domain) + if err != nil { + continue + } + f.compiled[compiled] = secret.GetStrategy() + } + } + for _, domain := range secret.Domains { + if f.domains == nil { + f.domains = make(map[string]authx.AuthStrategy) + } + f.domains[strings.TrimSpace(domain)] = secret.GetStrategy() + if strings.HasSuffix(domain, ":80") { + f.domains[strings.TrimSuffix(domain, ":80")] = secret.GetStrategy() + } + if strings.HasSuffix(domain, ":443") { + f.domains[strings.TrimSuffix(domain, ":443")] = secret.GetStrategy() + } + } + } + for _, dynamic := range f.store.Dynamic { + if len(dynamic.DomainsRegex) > 0 { + for _, domain := range dynamic.DomainsRegex { + if f.compiled == nil { + f.compiled = make(map[*regexp.Regexp]authx.AuthStrategy) + } + compiled, err := regexp.Compile(domain) + if err != nil { + continue + } + f.compiled[compiled] = &authx.DynamicAuthStrategy{Dynamic: dynamic} + } + } + for _, domain := range dynamic.Domains { + if f.domains == nil { + f.domains = make(map[string]authx.AuthStrategy) + } + f.domains[strings.TrimSpace(domain)] = &authx.DynamicAuthStrategy{Dynamic: dynamic} + if strings.HasSuffix(domain, ":80") { + f.domains[strings.TrimSuffix(domain, ":80")] = &authx.DynamicAuthStrategy{Dynamic: dynamic} + } + if strings.HasSuffix(domain, ":443") { + f.domains[strings.TrimSuffix(domain, ":443")] = &authx.DynamicAuthStrategy{Dynamic: dynamic} + } + } + } +} + +// LookupAddr looks up a given domain/address and returns appropriate auth strategy +func (f *FileAuthProvider) LookupAddr(addr string) authx.AuthStrategy { + if strings.Contains(addr, ":") { + // default normalization for host:port + host, port, err := net.SplitHostPort(addr) + if err == nil && (port == "80" || port == "443") { + addr = host + } + } + for domain, strategy := range f.domains { + if strings.EqualFold(domain, addr) { + return strategy + } + } + for compiled, strategy := range f.compiled { + if compiled.MatchString(addr) { + return strategy + } + } + return nil +} + +// LookupURL looks up a given URL and returns appropriate auth strategy +func (f *FileAuthProvider) LookupURL(u *url.URL) authx.AuthStrategy { + return f.LookupAddr(u.Host) +} + +// LookupURLX looks up a given URL and returns appropriate auth strategy +func (f *FileAuthProvider) LookupURLX(u *urlutil.URL) authx.AuthStrategy { + return f.LookupAddr(u.Host) +} + +// GetTemplatePaths returns the template path for the auth provider +func (f *FileAuthProvider) GetTemplatePaths() []string { + res := []string{} + for _, dynamic := range f.store.Dynamic { + if dynamic.TemplatePath != "" { + res = append(res, dynamic.TemplatePath) + } + } + return res +} + +// PreFetchSecrets pre-fetches the secrets from the auth provider +func (f *FileAuthProvider) PreFetchSecrets() error { + for _, s := range f.domains { + if val, ok := s.(*authx.DynamicAuthStrategy); ok { + if err := val.Dynamic.Fetch(false); err != nil { + return err + } + } + } + for _, s := range f.compiled { + if val, ok := s.(*authx.DynamicAuthStrategy); ok { + if err := val.Dynamic.Fetch(false); err != nil { + return err + } + } + } + return nil +} diff --git a/pkg/authprovider/interface.go b/pkg/authprovider/interface.go new file mode 100644 index 00000000..b21668fc --- /dev/null +++ b/pkg/authprovider/interface.go @@ -0,0 +1,59 @@ +package authprovider + +import ( + "fmt" + "net/url" + + "github.com/projectdiscovery/nuclei/v3/pkg/authprovider/authx" + urlutil "github.com/projectdiscovery/utils/url" +) + +var ( + ErrNoSecrets = fmt.Errorf("no secrets in given provider") +) + +var ( + _ AuthProvider = &FileAuthProvider{} +) + +// AuthProvider is an interface for auth providers +// It implements a data structure suitable for quick lookup and retrieval +// of auth strategies +type AuthProvider interface { + // LookupAddr looks up a given domain/address and returns appropriate auth strategy + // for it (accepted inputs are scanme.sh or scanme.sh:443) + LookupAddr(string) authx.AuthStrategy + // LookupURL looks up a given URL and returns appropriate auth strategy + // it accepts a valid url struct and returns the auth strategy + LookupURL(*url.URL) authx.AuthStrategy + // LookupURLX looks up a given URL and returns appropriate auth strategy + // it accepts pd url struct (i.e urlutil.URL) and returns the auth strategy + LookupURLX(*urlutil.URL) authx.AuthStrategy + // GetTemplatePaths returns the template path for the auth provider + // that will be used for dynamic secret fetching + GetTemplatePaths() []string + // PreFetchSecrets pre-fetches the secrets from the auth provider + // instead of lazy fetching + PreFetchSecrets() error +} + +// AuthProviderOptions contains options for the auth provider +type AuthProviderOptions struct { + // File based auth provider options + SecretsFiles []string + // LazyFetchSecret is a callback for lazy fetching of dynamic secrets + LazyFetchSecret authx.LazyFetchSecret +} + +// NewAuthProvider creates a new auth provider from the given options +func NewAuthProvider(options *AuthProviderOptions) (AuthProvider, error) { + var providers []AuthProvider + for _, file := range options.SecretsFiles { + provider, err := NewFileAuthProvider(file, options.LazyFetchSecret) + if err != nil { + return nil, err + } + providers = append(providers, provider) + } + return NewMultiAuthProvider(providers...), nil +} diff --git a/pkg/authprovider/multi.go b/pkg/authprovider/multi.go new file mode 100644 index 00000000..2e9b19df --- /dev/null +++ b/pkg/authprovider/multi.go @@ -0,0 +1,67 @@ +package authprovider + +import ( + "net/url" + + "github.com/projectdiscovery/nuclei/v3/pkg/authprovider/authx" + urlutil "github.com/projectdiscovery/utils/url" +) + +// MultiAuthProvider is a convenience wrapper for multiple auth providers +// it returns the first matching auth strategy for a given domain +// if there are multiple auth strategies for a given domain, it returns the first one +type MultiAuthProvider struct { + Providers []AuthProvider +} + +// NewMultiAuthProvider creates a new multi auth provider +func NewMultiAuthProvider(providers ...AuthProvider) AuthProvider { + return &MultiAuthProvider{Providers: providers} +} + +func (m *MultiAuthProvider) LookupAddr(host string) authx.AuthStrategy { + for _, provider := range m.Providers { + strategy := provider.LookupAddr(host) + if strategy != nil { + return strategy + } + } + return nil +} + +func (m *MultiAuthProvider) LookupURL(u *url.URL) authx.AuthStrategy { + for _, provider := range m.Providers { + strategy := provider.LookupURL(u) + if strategy != nil { + return strategy + } + } + return nil +} + +func (m *MultiAuthProvider) LookupURLX(u *urlutil.URL) authx.AuthStrategy { + for _, provider := range m.Providers { + strategy := provider.LookupURLX(u) + if strategy != nil { + return strategy + } + } + return nil +} + +func (m *MultiAuthProvider) GetTemplatePaths() []string { + var res []string + for _, provider := range m.Providers { + res = append(res, provider.GetTemplatePaths()...) + } + return res +} + +func (m *MultiAuthProvider) PreFetchSecrets() error { + for _, provider := range m.Providers { + if err := provider.PreFetchSecrets(); err != nil { + return err + } + } + return nil +} diff --git a/pkg/catalog/config/constants.go b/pkg/catalog/config/constants.go index 09f6f481..06d4a1cc 100644 --- a/pkg/catalog/config/constants.go +++ b/pkg/catalog/config/constants.go @@ -6,6 +6,20 @@ import ( "github.com/Masterminds/semver/v3" ) +type AppMode string + +const ( + AppModeLibrary AppMode = "library" + AppModeCLI AppMode = "cli" +) + +var ( + // Global Var to control behaviours specific to cli or library + // maybe this should be moved to utils ?? + // this is overwritten in cmd/nuclei/main.go + CurrentAppMode = AppModeLibrary +) + const ( TemplateConfigFileName = ".templates-config.json" NucleiTemplatesDirName = "nuclei-templates" @@ -17,7 +31,7 @@ const ( CLIConfigFileName = "config.yaml" ReportingConfigFilename = "reporting-config.yaml" // Version is the current version of nuclei - Version = `v3.2.0-dev` + Version = `v3.2.0` // Directory Names of custom templates CustomS3TemplatesDirName = "s3" CustomGitHubTemplatesDirName = "github" diff --git a/pkg/catalog/loader/loader.go b/pkg/catalog/loader/loader.go index ea737003..4c35d31f 100644 --- a/pkg/catalog/loader/loader.go +++ b/pkg/catalog/loader/loader.go @@ -13,7 +13,6 @@ import ( "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/nuclei/v3/pkg/catalog" "github.com/projectdiscovery/nuclei/v3/pkg/catalog/config" - cfg "github.com/projectdiscovery/nuclei/v3/pkg/catalog/config" "github.com/projectdiscovery/nuclei/v3/pkg/catalog/loader/filter" "github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity" "github.com/projectdiscovery/nuclei/v3/pkg/parsers" @@ -62,6 +61,8 @@ type Config struct { Catalog catalog.Catalog ExecutorOptions protocols.ExecutorOptions + + OnlyLoadHTTPFuzzing bool } // Store is a storage for loaded nuclei templates @@ -111,19 +112,19 @@ func NewConfig(options *types.Options, catalog catalog.Catalog, executerOpts pro } // New creates a new template store based on provided configuration -func New(config *Config) (*Store, error) { +func New(cfg *Config) (*Store, error) { tagFilter, err := filter.New(&filter.Config{ - Tags: config.Tags, - ExcludeTags: config.ExcludeTags, - Authors: config.Authors, - Severities: config.Severities, - ExcludeSeverities: config.ExcludeSeverities, - IncludeTags: config.IncludeTags, - IncludeIds: config.IncludeIds, - ExcludeIds: config.ExcludeIds, - Protocols: config.Protocols, - ExcludeProtocols: config.ExcludeProtocols, - IncludeConditions: config.IncludeConditions, + Tags: cfg.Tags, + ExcludeTags: cfg.ExcludeTags, + Authors: cfg.Authors, + Severities: cfg.Severities, + ExcludeSeverities: cfg.ExcludeSeverities, + IncludeTags: cfg.IncludeTags, + IncludeIds: cfg.IncludeIds, + ExcludeIds: cfg.ExcludeIds, + Protocols: cfg.Protocols, + ExcludeProtocols: cfg.ExcludeProtocols, + IncludeConditions: cfg.IncludeConditions, }) if err != nil { return nil, err @@ -131,23 +132,23 @@ func New(config *Config) (*Store, error) { // Create a tag filter based on provided configuration store := &Store{ - config: config, + config: cfg, tagFilter: tagFilter, pathFilter: filter.NewPathFilter(&filter.PathFilterConfig{ - IncludedTemplates: config.IncludeTemplates, - ExcludedTemplates: config.ExcludeTemplates, - }, config.Catalog), - finalTemplates: config.Templates, - finalWorkflows: config.Workflows, + IncludedTemplates: cfg.IncludeTemplates, + ExcludedTemplates: cfg.ExcludeTemplates, + }, cfg.Catalog), + finalTemplates: cfg.Templates, + finalWorkflows: cfg.Workflows, } // Do a check to see if we have URLs in templates flag, if so // we need to processs them separately and remove them from the initial list var templatesFinal []string - for _, template := range config.Templates { + for _, template := range cfg.Templates { // TODO: Add and replace this with urlutil.IsURL() helper if stringsutil.HasPrefixAny(template, httpPrefix, httpsPrefix) { - config.TemplateURLs = append(config.TemplateURLs, template) + cfg.TemplateURLs = append(cfg.TemplateURLs, template) } else { templatesFinal = append(templatesFinal, template) } @@ -155,7 +156,7 @@ func New(config *Config) (*Store, error) { // fix editor paths remoteTemplates := []string{} - for _, v := range config.TemplateURLs { + for _, v := range cfg.TemplateURLs { if _, err := urlutil.Parse(v); err == nil { remoteTemplates = append(remoteTemplates, handleTemplatesEditorURLs(v)) } else { @@ -163,12 +164,12 @@ func New(config *Config) (*Store, error) { templatesFinal = append(templatesFinal, v) // something went wrong, treat it as a file } } - config.TemplateURLs = remoteTemplates + cfg.TemplateURLs = remoteTemplates store.finalTemplates = templatesFinal - urlBasedTemplatesProvided := len(config.TemplateURLs) > 0 || len(config.WorkflowURLs) > 0 + urlBasedTemplatesProvided := len(cfg.TemplateURLs) > 0 || len(cfg.WorkflowURLs) > 0 if urlBasedTemplatesProvided { - remoteTemplates, remoteWorkflows, err := getRemoteTemplatesAndWorkflows(config.TemplateURLs, config.WorkflowURLs, config.RemoteTemplateDomainList) + remoteTemplates, remoteWorkflows, err := getRemoteTemplatesAndWorkflows(cfg.TemplateURLs, cfg.WorkflowURLs, cfg.RemoteTemplateDomainList) if err != nil { return store, err } @@ -186,7 +187,7 @@ func New(config *Config) (*Store, error) { } // Handle a case with no templates or workflows, where we use base directory if len(store.finalTemplates) == 0 && len(store.finalWorkflows) == 0 && !urlBasedTemplatesProvided { - store.finalTemplates = []string{cfg.DefaultConfig.TemplatesDirectory} + store.finalTemplates = []string{config.DefaultConfig.TemplatesDirectory} } return store, nil @@ -405,13 +406,13 @@ func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templ if len(parsed.RequestsHeadless) > 0 && !store.config.ExecutorOptions.Options.Headless { // donot include headless template in final list if headless flag is not set stats.Increment(parsers.HeadlessFlagWarningStats) - if cfg.DefaultConfig.LogAllEvents { + if config.DefaultConfig.LogAllEvents { gologger.Print().Msgf("[%v] Headless flag is required for headless template '%s'.\n", aurora.Yellow("WRN").String(), templatePath) } } else if len(parsed.RequestsCode) > 0 && !store.config.ExecutorOptions.Options.EnableCodeTemplates { // donot include 'Code' protocol custom template in final list if code flag is not set stats.Increment(parsers.CodeFlagWarningStats) - if cfg.DefaultConfig.LogAllEvents { + if config.DefaultConfig.LogAllEvents { gologger.Print().Msgf("[%v] Code flag is required for code protocol template '%s'.\n", aurora.Yellow("WRN").String(), templatePath) } } else if len(parsed.RequestsCode) > 0 && !parsed.Verified && len(parsed.Workflows) == 0 { @@ -419,9 +420,16 @@ func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templ stats.Increment(parsers.UnsignedCodeWarning) // these will be skipped so increment skip counter stats.Increment(parsers.SkippedUnsignedStats) - if cfg.DefaultConfig.LogAllEvents { + if config.DefaultConfig.LogAllEvents { gologger.Print().Msgf("[%v] Tampered/Unsigned template at %v.\n", aurora.Yellow("WRN").String(), templatePath) } + } else if parsed.IsFuzzing() && !store.config.ExecutorOptions.Options.FuzzTemplates { + stats.Increment(parsers.FuzzFlagWarningStats) + if config.DefaultConfig.LogAllEvents { + gologger.Print().Msgf("[%v] Fuzz flag is required for fuzzing template '%s'.\n", aurora.Yellow("WRN").String(), templatePath) + } + } else if store.config.OnlyLoadHTTPFuzzing && !parsed.IsFuzzing() { + gologger.Warning().Msgf("Non-Fuzzing template '%s' can only be run on list input mode targets\n", templatePath) } else { loadedTemplates = append(loadedTemplates, parsed) } diff --git a/pkg/core/engine.go b/pkg/core/engine.go index b9a8036a..93915bc2 100644 --- a/pkg/core/engine.go +++ b/pkg/core/engine.go @@ -3,7 +3,6 @@ package core import ( "github.com/projectdiscovery/nuclei/v3/pkg/output" "github.com/projectdiscovery/nuclei/v3/pkg/protocols" - "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" "github.com/projectdiscovery/nuclei/v3/pkg/types" ) @@ -22,21 +21,6 @@ type Engine struct { Callback func(*output.ResultEvent) // Executed on results } -// InputProvider is an input providing interface for the nuclei execution -// engine. -// -// An example InputProvider implementation is provided in form of hybrid -// input provider in pkg/core/inputs/hybrid/hmap.go -type InputProvider interface { - // Count returns the number of items for input provider - Count() int64 - // Scan iterates the input and each found item is passed to the - // callback consumer. - Scan(callback func(value *contextargs.MetaInput) bool) - // Set adds item to input provider - Set(value string) -} - // New returns a new Engine instance func New(options *types.Options) *Engine { engine := &Engine{ diff --git a/pkg/core/execute_options.go b/pkg/core/execute_options.go index 50c61de3..fd1fadae 100644 --- a/pkg/core/execute_options.go +++ b/pkg/core/execute_options.go @@ -7,6 +7,7 @@ import ( "github.com/remeh/sizedwaitgroup" "github.com/projectdiscovery/gologger" + "github.com/projectdiscovery/nuclei/v3/pkg/input/provider" "github.com/projectdiscovery/nuclei/v3/pkg/output" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" "github.com/projectdiscovery/nuclei/v3/pkg/templates" @@ -20,18 +21,18 @@ import ( // // All the execution logic for the templates/workflows happens in this part // of the engine. -func (e *Engine) Execute(templates []*templates.Template, target InputProvider) *atomic.Bool { +func (e *Engine) Execute(templates []*templates.Template, target provider.InputProvider) *atomic.Bool { return e.ExecuteScanWithOpts(templates, target, false) } // ExecuteWithResults a list of templates with results -func (e *Engine) ExecuteWithResults(templatesList []*templates.Template, target InputProvider, callback func(*output.ResultEvent)) *atomic.Bool { +func (e *Engine) ExecuteWithResults(templatesList []*templates.Template, target provider.InputProvider, callback func(*output.ResultEvent)) *atomic.Bool { e.Callback = callback return e.ExecuteScanWithOpts(templatesList, target, false) } // ExecuteScanWithOpts executes scan with given scanStrategy -func (e *Engine) ExecuteScanWithOpts(templatesList []*templates.Template, target InputProvider, noCluster bool) *atomic.Bool { +func (e *Engine) ExecuteScanWithOpts(templatesList []*templates.Template, target provider.InputProvider, noCluster bool) *atomic.Bool { results := &atomic.Bool{} selfcontainedWg := &sync.WaitGroup{} @@ -100,7 +101,7 @@ func (e *Engine) ExecuteScanWithOpts(templatesList []*templates.Template, target } // executeTemplateSpray executes scan using template spray strategy where targets are iterated over each template -func (e *Engine) executeTemplateSpray(templatesList []*templates.Template, target InputProvider) *atomic.Bool { +func (e *Engine) executeTemplateSpray(templatesList []*templates.Template, target provider.InputProvider) *atomic.Bool { results := &atomic.Bool{} // wp is workpool that contains different waitgroups for @@ -131,11 +132,11 @@ func (e *Engine) executeTemplateSpray(templatesList []*templates.Template, targe } // executeHostSpray executes scan using host spray strategy where templates are iterated over each target -func (e *Engine) executeHostSpray(templatesList []*templates.Template, target InputProvider) *atomic.Bool { +func (e *Engine) executeHostSpray(templatesList []*templates.Template, target provider.InputProvider) *atomic.Bool { results := &atomic.Bool{} wp := sizedwaitgroup.New(e.options.BulkSize + e.options.HeadlessBulkSize) - target.Scan(func(value *contextargs.MetaInput) bool { + target.Iterate(func(value *contextargs.MetaInput) bool { wp.Add() go func(targetval *contextargs.MetaInput) { defer wp.Done() diff --git a/pkg/core/executors.go b/pkg/core/executors.go index 81baa020..b491bd8e 100644 --- a/pkg/core/executors.go +++ b/pkg/core/executors.go @@ -5,6 +5,7 @@ import ( "sync/atomic" "github.com/projectdiscovery/gologger" + "github.com/projectdiscovery/nuclei/v3/pkg/input/provider" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" "github.com/projectdiscovery/nuclei/v3/pkg/scan" "github.com/projectdiscovery/nuclei/v3/pkg/templates" @@ -44,7 +45,7 @@ func (e *Engine) executeAllSelfContained(alltemplates []*templates.Template, res } // executeTemplateWithTarget executes a given template on x targets (with a internal targetpool(i.e concurrency)) -func (e *Engine) executeTemplateWithTargets(template *templates.Template, target InputProvider, results *atomic.Bool) { +func (e *Engine) executeTemplateWithTargets(template *templates.Template, target provider.InputProvider, results *atomic.Bool) { // this is target pool i.e max target to execute wg := e.workPool.InputPool(template.Type()) @@ -75,7 +76,7 @@ func (e *Engine) executeTemplateWithTargets(template *templates.Template, target currentInfo.Unlock() } - target.Scan(func(scannedValue *contextargs.MetaInput) bool { + target.Iterate(func(scannedValue *contextargs.MetaInput) bool { // Best effort to track the host progression // skips indexes lower than the minimum in-flight at interruption time var skip bool diff --git a/pkg/core/inputs/inputs.go b/pkg/core/inputs/inputs.go deleted file mode 100644 index 3e8e456b..00000000 --- a/pkg/core/inputs/inputs.go +++ /dev/null @@ -1,39 +0,0 @@ -package inputs - -import ( - "github.com/projectdiscovery/httpx/common/httpx" - "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" - "github.com/projectdiscovery/nuclei/v3/pkg/utils" -) - -type SimpleInputProvider struct { - Inputs []*contextargs.MetaInput -} - -// Count returns the number of items for input provider -func (s *SimpleInputProvider) Count() int64 { - return int64(len(s.Inputs)) -} - -// Scan calls a callback function till the input provider is exhausted -func (s *SimpleInputProvider) Scan(callback func(value *contextargs.MetaInput) bool) { - for _, v := range s.Inputs { - if !callback(v) { - return - } - } -} - -// Set adds item to input provider -func (s *SimpleInputProvider) Set(value string) { - s.Inputs = append(s.Inputs, &contextargs.MetaInput{Input: value}) -} - -// SetWithProbe adds item to input provider with http probing -func (s *SimpleInputProvider) SetWithProbe(value string, httpxClient *httpx.HTTPX) { - valueToAppend := value - if result := utils.ProbeURL(value, httpxClient); result != "" { - valueToAppend = result - } - s.Inputs = append(s.Inputs, &contextargs.MetaInput{Input: valueToAppend}) -} diff --git a/pkg/fuzz/component/body.go b/pkg/fuzz/component/body.go new file mode 100644 index 00000000..79d8d6a8 --- /dev/null +++ b/pkg/fuzz/component/body.go @@ -0,0 +1,140 @@ +package component + +import ( + "bytes" + "context" + "io" + "strconv" + "strings" + + "github.com/pkg/errors" + "github.com/projectdiscovery/gologger" + "github.com/projectdiscovery/nuclei/v3/pkg/fuzz/dataformat" + "github.com/projectdiscovery/retryablehttp-go" + readerutil "github.com/projectdiscovery/utils/reader" +) + +// Body is a component for a request body +type Body struct { + value *Value + + req *retryablehttp.Request +} + +var _ Component = &Body{} + +// NewBody creates a new body component +func NewBody() *Body { + return &Body{} +} + +// Name returns the name of the component +func (b *Body) Name() string { + return RequestBodyComponent +} + +// Parse parses the component and returns the +// parsed component +func (b *Body) Parse(req *retryablehttp.Request) (bool, error) { + if req.Body == nil { + return false, nil + } + b.req = req + + contentType := req.Header.Get("Content-Type") + + data, err := io.ReadAll(req.Body) + if err != nil { + return false, errors.Wrap(err, "could not read body") + } + req.Body = io.NopCloser(bytes.NewReader(data)) + dataStr := string(data) + + if dataStr == "" { + return false, nil + } + + b.value = NewValue(dataStr) + if b.value.Parsed() != nil { + return true, nil + } + + switch { + case strings.Contains(contentType, "application/json") && b.value.Parsed() == nil: + return b.parseBody(dataformat.JSONDataFormat, req) + case strings.Contains(contentType, "application/xml") && b.value.Parsed() == nil: + return b.parseBody(dataformat.XMLDataFormat, req) + case strings.Contains(contentType, "multipart/form-data") && b.value.Parsed() == nil: + return b.parseBody(dataformat.MultiPartFormDataFormat, req) + } + parsed, err := b.parseBody(dataformat.FormDataFormat, req) + if err != nil { + gologger.Warning().Msgf("Could not parse body as form data: %s\n", err) + return b.parseBody(dataformat.RawDataFormat, req) + } + return parsed, err +} + +// parseBody parses a body with a custom decoder +func (b *Body) parseBody(decoderName string, req *retryablehttp.Request) (bool, error) { + decoder := dataformat.Get(decoderName) + if decoderName == dataformat.MultiPartFormDataFormat { + // set content type to extract boundary + if err := decoder.(*dataformat.MultiPartForm).ParseBoundary(req.Header.Get("Content-Type")); err != nil { + return false, errors.Wrap(err, "could not parse boundary") + } + } + decoded, err := decoder.Decode(b.value.String()) + if err != nil { + return false, errors.Wrap(err, "could not decode raw") + } + b.value.SetParsed(decoded, decoder.Name()) + return true, nil +} + +// Iterate iterates through the component +func (b *Body) Iterate(callback func(key string, value interface{}) error) error { + for key, value := range b.value.Parsed() { + if strings.HasPrefix(key, "#_") { + continue + } + if err := callback(key, value); err != nil { + return err + } + } + return nil +} + +// SetValue sets a value in the component +func (b *Body) SetValue(key string, value string) error { + if !b.value.SetParsedValue(key, value) { + return ErrSetValue + } + return nil +} + +// Delete deletes a key from the component +func (b *Body) Delete(key string) error { + if !b.value.Delete(key) { + return ErrKeyNotFound + } + return nil +} + +// Rebuild returns a new request with the +// component rebuilt +func (b *Body) Rebuild() (*retryablehttp.Request, error) { + encoded, err := b.value.Encode() + if err != nil { + return nil, errors.Wrap(err, "could not encode body") + } + cloned := b.req.Clone(context.Background()) + reusableReader, err := readerutil.NewReusableReadCloser(encoded) + if err != nil { + return nil, errors.Wrap(err, "could not create reusable reader") + } + cloned.Body = reusableReader + cloned.ContentLength = int64(len(encoded)) + cloned.Header.Set("Content-Length", strconv.Itoa(len(encoded))) + return cloned, nil +} diff --git a/pkg/fuzz/component/body_test.go b/pkg/fuzz/component/body_test.go new file mode 100644 index 00000000..42e7e405 --- /dev/null +++ b/pkg/fuzz/component/body_test.go @@ -0,0 +1,175 @@ +package component + +import ( + "bytes" + "io" + "mime/multipart" + "net/url" + "strings" + "testing" + + "github.com/projectdiscovery/retryablehttp-go" + "github.com/stretchr/testify/require" +) + +func TestBodyComponent(t *testing.T) { + req, err := retryablehttp.NewRequest("POST", "https://example.com", strings.NewReader(`{"foo":"bar"}`)) + if err != nil { + t.Fatal(err) + } + req.Header.Set("Content-Type", "application/json") + + body := New(RequestBodyComponent) + _, err = body.Parse(req) + if err != nil { + t.Fatal(err) + } + + var keys []string + var values []string + _ = body.Iterate(func(key string, value interface{}) error { + keys = append(keys, key) + values = append(values, value.(string)) + return nil + }) + + require.Equal(t, []string{"foo"}, keys, "unexpected keys") + require.Equal(t, []string{"bar"}, values, "unexpected values") + + _ = body.SetValue("foo", "baz") + + rebuilt, err := body.Rebuild() + if err != nil { + t.Fatal(err) + } + + newBody, err := io.ReadAll(rebuilt.Body) + if err != nil { + t.Fatal(err) + } + require.Equal(t, `{"foo":"baz"}`, string(newBody), "unexpected body") +} + +func TestBodyXMLComponent(t *testing.T) { + var body = "11" + + req, err := retryablehttp.NewRequest("POST", "https://example.com", strings.NewReader(body)) + if err != nil { + t.Fatal(err) + } + req.Header.Set("Content-Type", "application/xml") + + bodyComponent := New(RequestBodyComponent) + parsed, err := bodyComponent.Parse(req) + if err != nil { + t.Fatal(err) + } + require.True(t, parsed, "could not parse body") + + _ = bodyComponent.SetValue("stockCheck~productId", "2'6842") + rebuilt, err := bodyComponent.Rebuild() + if err != nil { + t.Fatal(err) + } + + newBody, err := io.ReadAll(rebuilt.Body) + if err != nil { + t.Fatal(err) + } + require.Equal(t, "2'68421", string(newBody), "unexpected body") +} + +func TestBodyFormComponent(t *testing.T) { + formData := url.Values{} + formData.Set("key1", "value1") + formData.Set("key2", "value2") + + req, err := retryablehttp.NewRequest("POST", "https://example.com", strings.NewReader(formData.Encode())) + if err != nil { + t.Fatal(err) + } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + body := New(RequestBodyComponent) + _, err = body.Parse(req) + if err != nil { + t.Fatal(err) + } + + var keys []string + var values []string + _ = body.Iterate(func(key string, value interface{}) error { + keys = append(keys, key) + values = append(values, value.(string)) + return nil + }) + + require.ElementsMatch(t, []string{"key1", "key2"}, keys, "unexpected keys") + require.ElementsMatch(t, []string{"value1", "value2"}, values, "unexpected values") + + _ = body.SetValue("key1", "updatedValue1") + + rebuilt, err := body.Rebuild() + if err != nil { + t.Fatal(err) + } + + newBody, err := io.ReadAll(rebuilt.Body) + if err != nil { + t.Fatal(err) + } + require.Equal(t, "key1=updatedValue1&key2=value2", string(newBody), "unexpected body") +} + +func TestMultiPartFormComponent(t *testing.T) { + formData := &bytes.Buffer{} + writer := multipart.NewWriter(formData) + + // Hypothetical form fields + _ = writer.WriteField("username", "testuser") + _ = writer.WriteField("password", "testpass") + + contentType := writer.FormDataContentType() + _ = writer.Close() + + req, err := retryablehttp.NewRequest("POST", "https://example.com", formData) + if err != nil { + t.Fatal(err) + } + req.Header.Set("Content-Type", contentType) + + body := New(RequestBodyComponent) + _, err = body.Parse(req) + if err != nil { + t.Fatal(err) + } + + var keys []string + var values []string + _ = body.Iterate(func(key string, value interface{}) error { + keys = append(keys, key) + values = append(values, value.(string)) + return nil + }) + + require.ElementsMatch(t, []string{"username", "password"}, keys, "unexpected keys") + require.ElementsMatch(t, []string{"testuser", "testpass"}, values, "unexpected values") + + // Update a value in the form + _ = body.SetValue("password", "updatedTestPass") + + rebuilt, err := body.Rebuild() + if err != nil { + t.Fatal(err) + } + + newBody, err := io.ReadAll(rebuilt.Body) + if err != nil { + t.Fatal(err) + } + + // Check if the body contains the updated multipart form data + require.Contains(t, string(newBody), "updatedTestPass", "unexpected body content") + require.Contains(t, string(newBody), "username", "unexpected body content") + require.Contains(t, string(newBody), "testuser", "unexpected body content") +} diff --git a/pkg/fuzz/component/component.go b/pkg/fuzz/component/component.go new file mode 100644 index 00000000..5a8279c4 --- /dev/null +++ b/pkg/fuzz/component/component.go @@ -0,0 +1,95 @@ +package component + +import ( + "errors" + "strings" + + "github.com/leslie-qiwa/flat" + "github.com/projectdiscovery/retryablehttp-go" +) + +// ErrSetValue is a error raised when a value cannot be set +var ErrSetValue = errors.New("could not set value") + +func IsErrSetValue(err error) bool { + if err == nil { + return false + } + return strings.Contains(err.Error(), "could not set value") +} + +// ErrKeyNotFound is a error raised when a key is not found +var ErrKeyNotFound = errors.New("key not found") + +// Component is a component for a request +type Component interface { + // Name returns the name of the component + Name() string + // Parse parses the component and returns the + // parsed component + Parse(req *retryablehttp.Request) (bool, error) + // Iterate iterates over all values of a component + // ex in case of query component, it will iterate over each query parameter + // depending on the rule if mode is single + // request is rebuilt for each value in this callback + // and in case of multiple, request will be rebuilt after iteration of all values + Iterate(func(key string, value interface{}) error) error + // SetValue sets a value in the component + // for a key + // + // After calling setValue for mutation, the value must be + // called again so as to reset the body to its original state. + SetValue(key string, value string) error + // Delete deletes a key from the component + // If it is applicable + Delete(key string) error + // Rebuild returns a new request with the + // component rebuilt + Rebuild() (*retryablehttp.Request, error) +} + +const ( + // RequestBodyComponent is the name of the request body component + RequestBodyComponent = "body" + // RequestQueryComponent is the name of the request query component + RequestQueryComponent = "query" + // RequestPathComponent is the name of the request url component + RequestPathComponent = "path" + // RequestHeaderComponent is the name of the request header component + RequestHeaderComponent = "header" + // RequestCookieComponent is the name of the request cookie component + RequestCookieComponent = "cookie" +) + +// Components is a list of all available components +var Components = []string{ + RequestBodyComponent, + RequestQueryComponent, + RequestPathComponent, + RequestHeaderComponent, + RequestCookieComponent, +} + +// New creates a new component for a componentType +func New(componentType string) Component { + switch componentType { + case "body": + return NewBody() + case "query": + return NewQuery() + case "path": + return NewPath() + case "header": + return NewHeader() + case "cookie": + return NewCookie() + } + return nil +} + +var ( + flatOpts = &flat.Options{ + Safe: true, + Delimiter: "~", + } +) diff --git a/pkg/fuzz/component/cookie.go b/pkg/fuzz/component/cookie.go new file mode 100644 index 00000000..7269284c --- /dev/null +++ b/pkg/fuzz/component/cookie.go @@ -0,0 +1,138 @@ +package component + +import ( + "context" + "net/http" + + "github.com/projectdiscovery/retryablehttp-go" +) + +// Cookie is a component for a request cookie +type Cookie struct { + value *Value + + req *retryablehttp.Request +} + +var _ Component = &Cookie{} + +// NewCookie creates a new cookie component +func NewCookie() *Cookie { + return &Cookie{} +} + +// Name returns the name of the component +func (c *Cookie) Name() string { + return RequestCookieComponent +} + +// Parse parses the component and returns the +// parsed component +func (c *Cookie) Parse(req *retryablehttp.Request) (bool, error) { + if len(req.Cookies()) == 0 { + return false, nil + } + c.req = req + c.value = NewValue("") + + parsedCookies := make(map[string]interface{}) + for _, cookie := range req.Cookies() { + parsedCookies[cookie.Name] = cookie.Value + } + if len(parsedCookies) == 0 { + return false, nil + } + c.value.SetParsed(parsedCookies, "") + return true, nil +} + +// Iterate iterates through the component +func (c *Cookie) Iterate(callback func(key string, value interface{}) error) error { + for key, value := range c.value.Parsed() { + // Skip ignored cookies + if _, ok := defaultIgnoredCookieKeys[key]; ok { + continue + } + if err := callback(key, value); err != nil { + return err + } + } + return nil +} + +// SetValue sets a value in the component +// for a key +func (c *Cookie) SetValue(key string, value string) error { + if !c.value.SetParsedValue(key, value) { + return ErrSetValue + } + return nil +} + +// Delete deletes a key from the component +func (c *Cookie) Delete(key string) error { + if !c.value.Delete(key) { + return ErrKeyNotFound + } + return nil +} + +// Rebuild returns a new request with the +// component rebuilt +func (c *Cookie) Rebuild() (*retryablehttp.Request, error) { + cloned := c.req.Clone(context.Background()) + + cloned.Header.Del("Cookie") + for key, value := range c.value.Parsed() { + cookie := &http.Cookie{ + Name: key, + Value: value.(string), // Assume the value is always a string for cookies + } + cloned.AddCookie(cookie) + } + return cloned, nil +} + +// A list of cookies that are essential to the request and +// must not be fuzzed. +var defaultIgnoredCookieKeys = map[string]struct{}{ + "awsELB": {}, + "AWSALB": {}, + "AWSALBCORS": {}, + "__utma": {}, + "__utmb": {}, + "__utmc": {}, + "__utmt": {}, + "__utmz": {}, + "_ga": {}, + "_gat": {}, + "_gid": {}, + "_gcl_au": {}, + "_fbp": {}, + "fr": {}, + "__hstc": {}, + "hubspotutk": {}, + "__hssc": {}, + "__hssrc": {}, + "mp_mixpanel__c": {}, + "JSESSIONID": {}, + "NREUM": {}, + "_pk_id": {}, + "_pk_ref": {}, + "_pk_ses": {}, + "_pk_cvar": {}, + "_pk_hsr": {}, + "_hjIncludedInSample": {}, + "__cfduid": {}, + "cf_use_ob": {}, + "cf_ob_info": {}, + "intercom-session": {}, + "optimizelyEndUserId": {}, + "optimizelySegments": {}, + "optimizelyBuckets": {}, + "optimizelyPendingLogEvents": {}, + "YSC": {}, + "VISITOR_INFO1_LIVE": {}, + "PREF": {}, + "GPS": {}, +} diff --git a/pkg/fuzz/component/cookie_test.go b/pkg/fuzz/component/cookie_test.go new file mode 100644 index 00000000..2e4ea947 --- /dev/null +++ b/pkg/fuzz/component/cookie_test.go @@ -0,0 +1,57 @@ +package component + +import ( + "net/http" + "testing" + + "github.com/projectdiscovery/retryablehttp-go" + "github.com/stretchr/testify/require" +) + +func TestCookieComponent(t *testing.T) { + req, err := retryablehttp.NewRequest(http.MethodGet, "https://example.com", nil) + if err != nil { + t.Fatal(err) + } + cookie := &http.Cookie{ + Name: "session", + Value: "test-session", + } + req.AddCookie(cookie) + + cookieComponent := NewCookie() // Assuming you have a function like this for creating a new cookie component + _, err = cookieComponent.Parse(req) + if err != nil { + t.Fatal(err) + } + + var cookieNames []string + var cookieValues []string + _ = cookieComponent.Iterate(func(key string, value interface{}) error { + cookieNames = append(cookieNames, key) + switch v := value.(type) { + case string: + cookieValues = append(cookieValues, v) + case []string: + cookieValues = append(cookieValues, v...) + } + return nil + }) + + require.Equal(t, []string{"session"}, cookieNames, "unexpected cookie names") + require.Equal(t, []string{"test-session"}, cookieValues, "unexpected cookie values") + + err = cookieComponent.SetValue("session", "new-session") + if err != nil { + t.Fatal(err) + } + + rebuilt, err := cookieComponent.Rebuild() + if err != nil { + t.Fatal(err) + } + + // Assuming the Rebuild function will reconstruct the entire request and also set the modified cookies + newCookie, _ := rebuilt.Cookie("session") + require.Equal(t, "new-session", newCookie.Value, "unexpected cookie value") +} diff --git a/pkg/fuzz/component/headers.go b/pkg/fuzz/component/headers.go new file mode 100644 index 00000000..60ac9804 --- /dev/null +++ b/pkg/fuzz/component/headers.go @@ -0,0 +1,186 @@ +package component + +import ( + "context" + "strings" + + "github.com/projectdiscovery/retryablehttp-go" +) + +// Header is a component for a request header +type Header struct { + value *Value + + req *retryablehttp.Request +} + +var _ Component = &Header{} + +// NewHeader creates a new header component +func NewHeader() *Header { + return &Header{} +} + +// Name returns the name of the component +func (q *Header) Name() string { + return RequestHeaderComponent +} + +// Parse parses the component and returns the +// parsed component +func (q *Header) Parse(req *retryablehttp.Request) (bool, error) { + q.req = req + q.value = NewValue("") + + parsedHeaders := make(map[string]interface{}) + for key, value := range req.Header { + if len(value) == 1 { + parsedHeaders[key] = value[0] + continue + } + parsedHeaders[key] = value + } + q.value.SetParsed(parsedHeaders, "") + return true, nil +} + +// Iterate iterates through the component +func (q *Header) Iterate(callback func(key string, value interface{}) error) error { + for key, value := range q.value.Parsed() { + // Skip ignored headers + if _, ok := defaultIgnoredHeaderKeys[key]; ok { + continue + } + if err := callback(key, value); err != nil { + return err + } + } + return nil +} + +// SetValue sets a value in the component +// for a key +func (q *Header) SetValue(key string, value string) error { + if !q.value.SetParsedValue(key, value) { + return ErrSetValue + } + return nil +} + +// Delete deletes a key from the component +func (q *Header) Delete(key string) error { + if !q.value.Delete(key) { + return ErrKeyNotFound + } + return nil +} + +// Rebuild returns a new request with the +// component rebuilt +func (q *Header) Rebuild() (*retryablehttp.Request, error) { + cloned := q.req.Clone(context.Background()) + for key, value := range q.value.parsed { + if strings.EqualFold(key, "Host") { + cloned.Host = value.(string) + } + switch v := value.(type) { + case []interface{}: + for _, vv := range v { + if cloned.Header[key] == nil { + cloned.Header[key] = make([]string, 0) + } + cloned.Header[key] = append(cloned.Header[key], vv.(string)) + } + case string: + cloned.Header[key] = []string{v} + } + } + return cloned, nil +} + +// A list of headers that are essential to the request and +// must not be fuzzed. +var defaultIgnoredHeaderKeys = map[string]struct{}{ + "Accept-Charset": {}, + "Accept-Datetime": {}, + "Accept-Encoding": {}, + "Accept-Language": {}, + "Accept": {}, + "Access-Control-Request-Headers": {}, + "Access-Control-Request-Method": {}, + "Authorization": {}, + "Cache-Control": {}, + "Connection": {}, + "Cookie": {}, + "Content-Length": {}, + "Content-Type": {}, + "Date": {}, + "Dnt": {}, + "Expect": {}, + "Forwarded": {}, + "From": {}, + "Host": {}, + "If-Match": {}, + "If-Modified-Since": {}, + "If-None-Match": {}, + "If-Range": {}, + "If-Unmodified-Since": {}, + "Max-Forwards": {}, + "Pragma": {}, + "Priority": {}, + "Proxy-Authorization": {}, + "Range": {}, + "Sec-Ch-Ua": {}, + "Sec-Ch-Ua-Mobile": {}, + "Sec-Ch-Ua-Platform": {}, + "Sec-Fetch-Dest": {}, + "Sec-Fetch-Mode": {}, + "Sec-Fetch-Site": {}, + "Sec-Fetch-User": {}, + "TE": {}, + "Upgrade": {}, + "Via": {}, + "Warning": {}, + "Upgrade-Insecure-Requests": {}, + "X-CSRF-Token": {}, + "X-Requested-With": {}, + "Strict-Transport-Security": {}, + "Content-Security-Policy": {}, + "X-Content-Type-Options": {}, + "X-Frame-Options": {}, + "X-XSS-Protection": {}, + "Public-Key-Pins": {}, + "Referrer-Policy": {}, + "Access-Control-Allow-Origin": {}, + "Access-Control-Allow-Credentials": {}, + "Access-Control-Expose-Headers": {}, + "Access-Control-Max-Age": {}, + "Access-Control-Allow-Methods": {}, + "Access-Control-Allow-Headers": {}, + "Server": {}, + "X-Powered-By": {}, + "X-AspNet-Version": {}, + "X-AspNetMvc-Version": {}, + "ETag": {}, + "Vary": {}, + "Expires": {}, + "Last-Modified": {}, + "X-Cache": {}, + "X-Proxy-ID": {}, + "CF-Ray": {}, // Cloudflare + "X-Served-By": {}, // Varnish, etc. + "X-Cache-Hits": {}, + "Content-Encoding": {}, + "Transfer-Encoding": {}, + "Location": {}, + "WWW-Authenticate": {}, + "Proxy-Authenticate": {}, + "X-Access-Token": {}, + "X-Refresh-Token": {}, + "Link": {}, + "X-Content-Duration": {}, + "X-UA-Compatible": {}, + "X-RateLimit-Limit": {}, // Rate limiting header + "X-RateLimit-Remaining": {}, // Rate limiting header + "X-RateLimit-Reset": {}, // Rate limiting header +} diff --git a/pkg/fuzz/component/headers_test.go b/pkg/fuzz/component/headers_test.go new file mode 100644 index 00000000..db5574a3 --- /dev/null +++ b/pkg/fuzz/component/headers_test.go @@ -0,0 +1,51 @@ +package component + +import ( + "net/http" + "testing" + + "github.com/projectdiscovery/retryablehttp-go" + "github.com/stretchr/testify/require" +) + +func TestHeaderComponent(t *testing.T) { + req, err := retryablehttp.NewRequest(http.MethodGet, "https://example.com", nil) + if err != nil { + t.Fatal(err) + } + req.Header.Set("User-Agent", "test-agent") + + header := NewHeader() + _, err = header.Parse(req) + if err != nil { + t.Fatal(err) + } + + var keys []string + var values []string + _ = header.Iterate(func(key string, value interface{}) error { + keys = append(keys, key) + switch v := value.(type) { + case string: + values = append(values, v) + case []string: + values = append(values, v...) + } + return nil + }) + + require.Equal(t, []string{"User-Agent"}, keys, "unexpected keys") + require.Equal(t, []string{"test-agent"}, values, "unexpected values") + + err = header.SetValue("User-Agent", "new-agent") + if err != nil { + t.Fatal(err) + } + + rebuilt, err := header.Rebuild() + if err != nil { + t.Fatal(err) + } + + require.Equal(t, "new-agent", rebuilt.Header.Get("User-Agent"), "unexpected header value") +} diff --git a/pkg/fuzz/component/path.go b/pkg/fuzz/component/path.go new file mode 100644 index 00000000..c1d31cee --- /dev/null +++ b/pkg/fuzz/component/path.go @@ -0,0 +1,83 @@ +package component + +import ( + "context" + + "github.com/pkg/errors" + "github.com/projectdiscovery/nuclei/v3/pkg/fuzz/dataformat" + "github.com/projectdiscovery/retryablehttp-go" +) + +// Path is a component for a request Path +type Path struct { + value *Value + + req *retryablehttp.Request +} + +var _ Component = &Path{} + +// NewPath creates a new URL component +func NewPath() *Path { + return &Path{} +} + +// Name returns the name of the component +func (q *Path) Name() string { + return RequestPathComponent +} + +// Parse parses the component and returns the +// parsed component +func (q *Path) Parse(req *retryablehttp.Request) (bool, error) { + q.req = req + q.value = NewValue(req.URL.Path) + + parsed, err := dataformat.Get(dataformat.RawDataFormat).Decode(q.value.String()) + if err != nil { + return false, err + } + q.value.SetParsed(parsed, dataformat.RawDataFormat) + return true, nil +} + +// Iterate iterates through the component +func (q *Path) Iterate(callback func(key string, value interface{}) error) error { + for key, value := range q.value.Parsed() { + if err := callback(key, value); err != nil { + return err + } + } + return nil +} + +// SetValue sets a value in the component +// for a key +func (q *Path) SetValue(key string, value string) error { + if !q.value.SetParsedValue(key, value) { + return ErrSetValue + } + return nil +} + +// Delete deletes a key from the component +func (q *Path) Delete(key string) error { + if !q.value.Delete(key) { + return ErrKeyNotFound + } + return nil +} + +// Rebuild returns a new request with the +// component rebuilt +func (q *Path) Rebuild() (*retryablehttp.Request, error) { + encoded, err := q.value.Encode() + if err != nil { + return nil, errors.Wrap(err, "could not encode query") + } + cloned := q.req.Clone(context.Background()) + if err := cloned.UpdateRelPath(encoded, true); err != nil { + cloned.URL.RawPath = encoded + } + return cloned, nil +} diff --git a/pkg/fuzz/component/path_test.go b/pkg/fuzz/component/path_test.go new file mode 100644 index 00000000..859ffcde --- /dev/null +++ b/pkg/fuzz/component/path_test.go @@ -0,0 +1,46 @@ +package component + +import ( + "net/http" + "testing" + + "github.com/projectdiscovery/retryablehttp-go" + "github.com/stretchr/testify/require" +) + +func TestURLComponent(t *testing.T) { + req, err := retryablehttp.NewRequest(http.MethodGet, "https://example.com/testpath", nil) + if err != nil { + t.Fatal(err) + } + + urlComponent := NewPath() + _, err = urlComponent.Parse(req) + if err != nil { + t.Fatal(err) + } + + var keys []string + var values []string + _ = urlComponent.Iterate(func(key string, value interface{}) error { + keys = append(keys, key) + values = append(values, value.(string)) + return nil + }) + + require.Equal(t, []string{"value"}, keys, "unexpected keys") + require.Equal(t, []string{"/testpath"}, values, "unexpected values") + + err = urlComponent.SetValue("value", "/newpath") + if err != nil { + t.Fatal(err) + } + + rebuilt, err := urlComponent.Rebuild() + if err != nil { + t.Fatal(err) + } + + require.Equal(t, "/newpath", rebuilt.URL.Path, "unexpected URL path") + require.Equal(t, "https://example.com/newpath", rebuilt.URL.String(), "unexpected full URL") +} diff --git a/pkg/fuzz/component/query.go b/pkg/fuzz/component/query.go new file mode 100644 index 00000000..0952c979 --- /dev/null +++ b/pkg/fuzz/component/query.go @@ -0,0 +1,92 @@ +package component + +import ( + "context" + + "github.com/pkg/errors" + "github.com/projectdiscovery/nuclei/v3/pkg/fuzz/dataformat" + "github.com/projectdiscovery/retryablehttp-go" + urlutil "github.com/projectdiscovery/utils/url" +) + +// Query is a component for a request query +type Query struct { + value *Value + + req *retryablehttp.Request +} + +var _ Component = &Query{} + +// NewQuery creates a new query component +func NewQuery() *Query { + return &Query{} +} + +// Name returns the name of the component +func (q *Query) Name() string { + return RequestQueryComponent +} + +// Parse parses the component and returns the +// parsed component +func (q *Query) Parse(req *retryablehttp.Request) (bool, error) { + if req.URL.Query().IsEmpty() { + return false, nil + } + q.req = req + + q.value = NewValue(req.URL.Query().Encode()) + + parsed, err := dataformat.Get(dataformat.FormDataFormat).Decode(q.value.String()) + if err != nil { + return false, err + } + q.value.SetParsed(parsed, dataformat.FormDataFormat) + return true, nil +} + +// Iterate iterates through the component +func (q *Query) Iterate(callback func(key string, value interface{}) error) error { + for key, value := range q.value.Parsed() { + if err := callback(key, value); err != nil { + return err + } + } + return nil +} + +// SetValue sets a value in the component +// for a key +func (q *Query) SetValue(key string, value string) error { + if !q.value.SetParsedValue(key, value) { + return ErrSetValue + } + return nil +} + +// Delete deletes a key from the component +func (q *Query) Delete(key string) error { + if !q.value.Delete(key) { + return ErrKeyNotFound + } + return nil +} + +// Rebuild returns a new request with the +// component rebuilt +func (q *Query) Rebuild() (*retryablehttp.Request, error) { + encoded, err := q.value.Encode() + if err != nil { + return nil, errors.Wrap(err, "could not encode query") + } + cloned := q.req.Clone(context.Background()) + cloned.URL.RawQuery = encoded + + // Clear the query parameters and re-add them + cloned.Params = nil + cloned.Params = urlutil.NewOrderedParams() + cloned.Params.Decode(encoded) + cloned.Update() + return cloned, nil +} diff --git a/pkg/fuzz/component/query_test.go b/pkg/fuzz/component/query_test.go new file mode 100644 index 00000000..48fe5aa2 --- /dev/null +++ b/pkg/fuzz/component/query_test.go @@ -0,0 +1,46 @@ +package component + +import ( + "net/http" + "testing" + + "github.com/projectdiscovery/retryablehttp-go" + "github.com/stretchr/testify/require" +) + +func TestQueryComponent(t *testing.T) { + req, err := retryablehttp.NewRequest(http.MethodGet, "https://example.com?foo=bar", nil) + if err != nil { + t.Fatal(err) + } + + query := NewQuery() + _, err = query.Parse(req) + if err != nil { + t.Fatal(err) + } + + var keys []string + var values []string + _ = query.Iterate(func(key string, value interface{}) error { + keys = append(keys, key) + values = append(values, value.(string)) + return nil + }) + + require.Equal(t, []string{"foo"}, keys, "unexpected keys") + require.Equal(t, []string{"bar"}, values, "unexpected values") + + err = query.SetValue("foo", "baz") + if err != nil { + t.Fatal(err) + } + + rebuilt, err := query.Rebuild() + if err != nil { + t.Fatal(err) + } + + require.Equal(t, "foo=baz", rebuilt.URL.RawQuery, "unexpected query string") + require.Equal(t, "https://example.com?foo=baz", rebuilt.URL.String(), "unexpected url") +} diff --git a/pkg/fuzz/component/value.go b/pkg/fuzz/component/value.go new file mode 100644 index 00000000..c6d7a960 --- /dev/null +++ b/pkg/fuzz/component/value.go @@ -0,0 +1,120 @@ +package component + +import ( + "strconv" + + "github.com/leslie-qiwa/flat" + "github.com/projectdiscovery/gologger" + "github.com/projectdiscovery/nuclei/v3/pkg/fuzz/dataformat" +) + +// Value is a value component containing a single +// parameter for the component +// +// It is a type of container that is used to represent +// all the data values that are used in a request. +type Value struct { + data string + parsed map[string]interface{} + dataFormat string +} + +// NewValue returns a new value component +func NewValue(data string) *Value { + if data == "" { + return &Value{} + } + v := &Value{data: data} + + // Do any dataformat decoding on the data if needed + decodedDataformat, err := dataformat.Decode(data) + if err == nil && decodedDataformat != nil { + v.SetParsed(decodedDataformat.Data, decodedDataformat.DataFormat) + } + return v +} + +// String returns the string representation of the value +func (v *Value) String() string { + return v.data +} + +// Parsed returns the parsed value +func (v *Value) Parsed() map[string]interface{} { + return v.parsed +} + +// SetParsed sets the parsed value map +func (v *Value) SetParsed(parsed map[string]interface{}, dataFormat string) { + flattened, err := flat.Flatten(parsed, flatOpts) + if err == nil { + v.parsed = flattened + } else { + v.parsed = parsed + } + v.dataFormat = dataFormat +} + +// SetParsedValue sets the parsed value for a key +// in the parsed map +func (v *Value) SetParsedValue(key string, value string) bool { + origValue, ok := v.parsed[key] + if !ok { + v.parsed[key] = value + return true + } + // If the value is a list, append to it + // otherwise replace it + switch v := origValue.(type) { + case []interface{}: + origValue = append(v, value) + case string: + origValue = value + case int, int32, int64, float32, float64: + parsed, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return false + } + origValue = parsed + case bool: + parsed, err := strconv.ParseBool(value) + if err != nil { + return false + } + origValue = parsed + case nil: + origValue = value + default: + gologger.Error().Msgf("unknown type %T for value %s", v, v) + } + v.parsed[key] = origValue + return true +} + +// Delete removes a key from the parsed value +func (v *Value) Delete(key string) bool { + if _, ok := v.parsed[key]; !ok { + return false + } + delete(v.parsed, key) + return true +} + +// Encode encodes the value into a string +// using the dataformat and encoding +func (v *Value) Encode() (string, error) { + toEncodeStr := v.data + + nested, err := flat.Unflatten(v.parsed, flatOpts) + if err != nil { + return "", err + } + if v.dataFormat != "" { + dataformatStr, err := dataformat.Encode(nested, v.dataFormat) + if err != nil { + return "", err + } + toEncodeStr = dataformatStr + } + return toEncodeStr, nil +} diff --git a/pkg/fuzz/component/value_test.go b/pkg/fuzz/component/value_test.go new file mode 100644 index 00000000..bafd0277 --- /dev/null +++ b/pkg/fuzz/component/value_test.go @@ -0,0 +1,39 @@ +package component + +import ( + "testing" + + "github.com/leslie-qiwa/flat" + "github.com/stretchr/testify/require" +) + +func TestFlatMap_FlattenUnflatten(t *testing.T) { + data := map[string]interface{}{ + "foo": "bar", + "bar": map[string]interface{}{ + "baz": "foo", + }, + "slice": []interface{}{ + "foo", + "bar", + }, + "with.dot": map[string]interface{}{ + "foo": "bar", + }, + } + + opts := &flat.Options{ + Safe: true, + Delimiter: "~", + } + flattened, err := flat.Flatten(data, opts) + if err != nil { + t.Fatal(err) + } + + nested, err := flat.Unflatten(flattened, opts) + if err != nil { + t.Fatal(err) + } + require.Equal(t, data, nested, "unexpected data") +} diff --git a/pkg/fuzz/dataformat/dataformat.go b/pkg/fuzz/dataformat/dataformat.go new file mode 100644 index 00000000..a07abc28 --- /dev/null +++ b/pkg/fuzz/dataformat/dataformat.go @@ -0,0 +1,92 @@ +package dataformat + +import ( + "errors" + "fmt" +) + +// dataformats is a list of dataformats +var dataformats map[string]DataFormat + +func init() { + dataformats = make(map[string]DataFormat) + + // register the default data formats + RegisterDataFormat(NewJSON()) + RegisterDataFormat(NewXML()) + RegisterDataFormat(NewRaw()) + RegisterDataFormat(NewForm()) + RegisterDataFormat(NewMultiPartForm()) +} + +const ( + // JSONDataFormat is the name of the JSON data format + JSONDataFormat = "json" + // XMLDataFormat is the name of the XML data format + XMLDataFormat = "xml" + // RawDataFormat is the name of the Raw data format + RawDataFormat = "raw" + // FormDataFormat is the name of the Form data format + FormDataFormat = "form" + // MultiPartFormDataFormat is the name of the MultiPartForm data format + MultiPartFormDataFormat = "multipart/form-data" +) + +// Get returns the dataformat by name +func Get(name string) DataFormat { + return dataformats[name] +} + +// RegisterEncoder registers an encoder +func RegisterDataFormat(dataformat DataFormat) { + dataformats[dataformat.Name()] = dataformat +} + +// DataFormat is an interface for encoding and decoding +type DataFormat interface { + // IsType returns true if the data is of the type + IsType(data string) bool + // Name returns the name of the encoder + Name() string + // Encode encodes the data into a format + Encode(data map[string]interface{}) (string, error) + // Decode decodes the data from a format + Decode(input string) (map[string]interface{}, error) +} + +// Decoded is a decoded data format +type Decoded struct { + // DataFormat is the data format + DataFormat string + // Data is the decoded data + Data map[string]interface{} +} + +// Decode decodes the data from a format +func Decode(data string) (*Decoded, error) { + for _, dataformat := range dataformats { + if dataformat.IsType(data) { + decoded, err := dataformat.Decode(data) + if err != nil { + return nil, err + } + value := &Decoded{ + DataFormat: dataformat.Name(), + Data: decoded, + } + return value, nil + } + } + return nil, nil +} + +// Encode encodes the data into a format +func Encode(data map[string]interface{}, dataformat string) (string, error) { + if dataformat == "" { + return "", errors.New("dataformat is required") + } + if encoder, ok := dataformats[dataformat]; ok { + return encoder.Encode(data) + } + return "", fmt.Errorf("dataformat %s is not supported", dataformat) +} diff --git a/pkg/fuzz/dataformat/dataformat_test.go b/pkg/fuzz/dataformat/dataformat_test.go new file mode 100644 index 00000000..7595f9b9 --- /dev/null +++ b/pkg/fuzz/dataformat/dataformat_test.go @@ -0,0 +1,54 @@ +package dataformat + +import ( + "testing" +) + +func TestDataformatDecodeEncode_JSON(t *testing.T) { + obj := `{"foo":"bar"}` + + decoded, err := Decode(obj) + if err != nil { + t.Fatal(err) + } + if decoded.DataFormat != "json" { + t.Fatal("unexpected data format") + } + if decoded.Data["foo"] != "bar" { + t.Fatal("unexpected data") + } + + encoded, err := Encode(decoded.Data, decoded.DataFormat) + if err != nil { + t.Fatal(err) + } + if encoded != obj { + t.Fatal("unexpected data") + } +} + +func TestDataformatDecodeEncode_XML(t *testing.T) { + obj := `bar` + + decoded, err := Decode(obj) + if err != nil { + t.Fatal(err) + } + if decoded.DataFormat != "xml" { + t.Fatal("unexpected data format") + } + if decoded.Data["foo"].(map[string]interface{})["#text"] != "bar" { + t.Fatal("unexpected data") + } + if decoded.Data["foo"].(map[string]interface{})["-attr"] != "baz" { + t.Fatal("unexpected data") + } + + encoded, err := Encode(decoded.Data, decoded.DataFormat) + if err != nil { + t.Fatal(err) + } + if encoded != obj { + t.Fatal("unexpected data") + } +} diff --git a/pkg/fuzz/dataformat/form.go b/pkg/fuzz/dataformat/form.go new file mode 100644 index 00000000..088a62fe --- /dev/null +++ b/pkg/fuzz/dataformat/form.go @@ -0,0 +1,61 @@ +package dataformat + +import ( + "net/url" +) + +type Form struct{} + +var ( + _ DataFormat = &Form{} +) + +// NewForm returns a new Form encoder +func NewForm() *Form { + return &Form{} +} + +// IsType returns true if the data is Form encoded +func (f *Form) IsType(data string) bool { + return false +} + +// Encode encodes the data into Form format +func (f *Form) Encode(data map[string]interface{}) (string, error) { + query := url.Values{} + for key, value := range data { + switch v := value.(type) { + case []interface{}: + for _, val := range v { + query.Add(key, val.(string)) + } + case string: + query.Set(key, v) + } + } + encoded := query.Encode() + return encoded, nil +} + +// Decode decodes the data from Form format +func (f *Form) Decode(data string) (map[string]interface{}, error) { + parsed, err := url.ParseQuery(data) + if err != nil { + return nil, err + } + + values := make(map[string]interface{}) + for key, value := range parsed { + if len(value) == 1 { + values[key] = value[0] + } else { + values[key] = value + } + } + return values, nil +} + +// Name returns the name of the encoder +func (f *Form) Name() string { + return FormDataFormat +} diff --git a/pkg/fuzz/dataformat/json.go b/pkg/fuzz/dataformat/json.go new file mode 100644 index 00000000..3979ed5e --- /dev/null +++ b/pkg/fuzz/dataformat/json.go @@ -0,0 +1,48 @@ +package dataformat + +import ( + "strings" + + jsoniter "github.com/json-iterator/go" +) + +// JSON is a JSON encoder +// +// For now JSON only supports objects as the root data type +// and not arrays +// +// TODO: Support arrays + other JSON oddities by +// adding more attirbutes to the map[string]interface{} +type JSON struct{} + +var ( + _ DataFormat = &JSON{} +) + +// NewJSON returns a new JSON encoder +func NewJSON() *JSON { + return &JSON{} +} + +// IsType returns true if the data is JSON encoded +func (j *JSON) IsType(data string) bool { + return strings.HasPrefix(data, "{") && strings.HasSuffix(data, "}") +} + +// Encode encodes the data into JSON format +func (j *JSON) Encode(data map[string]interface{}) (string, error) { + encoded, err := jsoniter.Marshal(data) + return string(encoded), err +} + +// Decode decodes the data from JSON format +func (j *JSON) Decode(data string) (map[string]interface{}, error) { + var decoded map[string]interface{} + err := jsoniter.Unmarshal([]byte(data), &decoded) + return decoded, err +} + +// Name returns the name of the encoder +func (j *JSON) Name() string { + return JSONDataFormat +} diff --git a/pkg/fuzz/dataformat/multipart.go b/pkg/fuzz/dataformat/multipart.go new file mode 100644 index 00000000..658f77b3 --- /dev/null +++ b/pkg/fuzz/dataformat/multipart.go @@ -0,0 +1,116 @@ +package dataformat + +import ( + "bytes" + "fmt" + "io" + "mime" + "mime/multipart" +) + +type MultiPartForm struct { + boundary string +} + +var ( + _ DataFormat = &MultiPartForm{} +) + +// NewMultiPartForm returns a new MultiPartForm encoder +func NewMultiPartForm() *MultiPartForm { + return &MultiPartForm{} +} + +// IsType returns true if the data is MultiPartForm encoded +func (m *MultiPartForm) IsType(data string) bool { + // This method should be implemented to detect if the data is multipart form encoded + return false +} + +// Encode encodes the data into MultiPartForm format +func (m *MultiPartForm) Encode(data map[string]interface{}) (string, error) { + var b bytes.Buffer + w := multipart.NewWriter(&b) + if err := w.SetBoundary(m.boundary); err != nil { + return "", err + } + + for key, value := range data { + var fw io.Writer + var err error + // Add field + if fw, err = w.CreateFormField(key); err != nil { + return "", err + } + if _, err = fw.Write([]byte(value.(string))); err != nil { + return "", err + } + } + + w.Close() + return b.String(), nil +} + +// ParseBoundary parses the boundary from the content type +func (m *MultiPartForm) ParseBoundary(contentType string) error { + _, params, err := mime.ParseMediaType(contentType) + if err != nil { + return err + } + m.boundary = params["boundary"] + if m.boundary == "" { + return fmt.Errorf("no boundary found in the content type") + } + return nil +} + +// Decode decodes the data from MultiPartForm format +func (m *MultiPartForm) Decode(data string) (map[string]interface{}, error) { + // Create a buffer from the string data + b := bytes.NewBufferString(data) + // The boundary parameter should be extracted from the Content-Type header of the HTTP request + // which is not available in this context, so this is a placeholder for demonstration. + // You will need to pass the actual boundary value to this function. + r := multipart.NewReader(b, m.boundary) + + form, err := r.ReadForm(32 << 20) // 32MB is the max memory used to parse the form + if err != nil { + return nil, err + } + defer func() { + _ = form.RemoveAll() + }() + + result := make(map[string]interface{}) + for key, values := range form.Value { + if len(values) > 1 { + result[key] = values + } else { + result[key] = values[0] + } + } + for key, files := range form.File { + fileContents := []interface{}{} + for _, fileHeader := range files { + file, err := fileHeader.Open() + if err != nil { + return nil, err + } + defer file.Close() + + buffer := new(bytes.Buffer) + if _, err := buffer.ReadFrom(file); err != nil { + return nil, err + } + fileContents = append(fileContents, buffer.String()) + } + result[key] = fileContents + } + + return result, nil +} + +// Name returns the name of the encoder +func (m *MultiPartForm) Name() string { + return "multipart/form-data" +} diff --git a/pkg/fuzz/dataformat/raw.go b/pkg/fuzz/dataformat/raw.go new file mode 100644 index 00000000..70431528 --- /dev/null +++ b/pkg/fuzz/dataformat/raw.go @@ -0,0 +1,34 @@ +package dataformat + +type Raw struct{} + +var ( + _ DataFormat = &Raw{} +) + +// NewRaw returns a new Raw encoder +func NewRaw() *Raw { + return &Raw{} +} + +// IsType returns true if the data is Raw encoded +func (r *Raw) IsType(data string) bool { + return false +} + +// Encode encodes the data into Raw format +func (r *Raw) Encode(data map[string]interface{}) (string, error) { + return data["value"].(string), nil +} + +// Decode decodes the data from Raw format +func (r *Raw) Decode(data string) (map[string]interface{}, error) { + return map[string]interface{}{ + "value": data, + }, nil +} + +// Name returns the name of the encoder +func (r *Raw) Name() string { + return RawDataFormat +} diff --git a/pkg/fuzz/dataformat/xml.go b/pkg/fuzz/dataformat/xml.go new file mode 100644 index 00000000..0609031b --- /dev/null +++ b/pkg/fuzz/dataformat/xml.go @@ -0,0 +1,62 @@ +package dataformat + +import ( + "fmt" + "regexp" + "strings" + + "github.com/clbanning/mxj/v2" +) + +// XML is an XML encoder +type XML struct{} + +// NewXML returns a new XML encoder +func NewXML() *XML { + return &XML{} +} + +// IsType returns true if the data is XML encoded +func (x *XML) IsType(data string) bool { + return strings.HasPrefix(data, "<") && strings.HasSuffix(data, ">") +} + +// Encode encodes the data into XML format +func (x *XML) Encode(data map[string]interface{}) (string, error) { + var header string + if value, ok := data["#_xml_header"]; ok && value != nil { + header = value.(string) + delete(data, "#_xml_header") + } + marshalled, err := mxj.Map(data).Xml() + if err != nil { + return "", err + } + if header != "" { + return fmt.Sprintf("%s", header, string(marshalled)), nil + } + return string(marshalled), err +} + +var xmlHeader = regexp.MustCompile(`\<\?(.*)\?\>`) + +// Decode decodes the data from XML format +func (x *XML) Decode(data string) (map[string]interface{}, error) { + var prefixStr string + prefix := xmlHeader.FindAllStringSubmatch(data, -1) + if len(prefix) > 0 { + prefixStr = prefix[0][1] + } + + decoded, err := mxj.NewMapXml([]byte(data)) + if err != nil { + return nil, err + } + decoded["#_xml_header"] = prefixStr + return decoded, nil +} + +// Name returns the name of the encoder +func (x *XML) Name() string { + return XMLDataFormat +} diff --git a/pkg/protocols/common/fuzz/doc.go b/pkg/fuzz/doc.go similarity index 100% rename from pkg/protocols/common/fuzz/doc.go rename to pkg/fuzz/doc.go diff --git a/pkg/fuzz/execute.go b/pkg/fuzz/execute.go new file mode 100644 index 00000000..3a86ef7e --- /dev/null +++ b/pkg/fuzz/execute.go @@ -0,0 +1,275 @@ +package fuzz + +import ( + "fmt" + "io" + "regexp" + "strings" + + "github.com/pkg/errors" + "github.com/projectdiscovery/gologger" + "github.com/projectdiscovery/nuclei/v3/pkg/fuzz/component" + "github.com/projectdiscovery/nuclei/v3/pkg/protocols" + "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" + "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators" + "github.com/projectdiscovery/retryablehttp-go" + errorutil "github.com/projectdiscovery/utils/errors" + urlutil "github.com/projectdiscovery/utils/url" +) + +var ( + ErrRuleNotApplicable = errorutil.NewWithFmt("rule not applicable : %v") +) + +// IsErrRuleNotApplicable checks if an error is due to rule not applicable +func IsErrRuleNotApplicable(err error) bool { + if err == nil { + return false + } + if strings.Contains(err.Error(), "rule not applicable") { + return true + } + return false +} + +// ExecuteRuleInput is the input for rule Execute function +type ExecuteRuleInput struct { + // Input is the context args input + Input *contextargs.Context + // Callback is the callback for generated rule requests + Callback func(GeneratedRequest) bool + // InteractURLs contains interact urls for execute call + InteractURLs []string + // Values contains dynamic values for the rule + Values map[string]interface{} + // BaseRequest is the base http request for fuzzing rule + BaseRequest *retryablehttp.Request +} + +// GeneratedRequest is a single generated request for rule +type GeneratedRequest struct { + // Request is the http request for rule + Request *retryablehttp.Request + // InteractURLs is the list of interactsh urls + InteractURLs []string + // DynamicValues contains dynamic values map + DynamicValues map[string]interface{} + // Component is the component for the request + Component component.Component +} + +// Execute executes a fuzzing rule accepting a callback on which +// generated requests are returned. +// +// Input is not thread safe and should not be shared between concurrent +// goroutines. +func (rule *Rule) Execute(input *ExecuteRuleInput) (err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("got panic while executing rule: %v", r) + } + }() + if !rule.isInputURLValid(input.Input) { + return ErrRuleNotApplicable.Msgf("invalid input url: %v", input.Input.MetaInput.Input) + } + if input.BaseRequest == nil && input.Input.MetaInput.ReqResp == nil { + return ErrRuleNotApplicable.Msgf("both base request and reqresp are nil for %v", input.Input.MetaInput.Input) + } + + var finalComponentList []component.Component + // match rule part with component name + for _, componentName := range component.Components { + if rule.partType != requestPartType && rule.Part != componentName { + continue + } + component := component.New(componentName) + discovered, err := component.Parse(input.BaseRequest) + if err != nil { + gologger.Verbose().Msgf("Could not parse component %s: %s\n", componentName, err) + continue + } + if !discovered { + continue + } + // check rule applicable on this component + if !rule.checkRuleApplicableOnComponent(component) { + continue + } + finalComponentList = append(finalComponentList, component) + } + + if len(finalComponentList) == 0 { + return ErrRuleNotApplicable.Msgf("no component matched on this rule") + } + + baseValues := input.Values + if rule.generator == nil { + for _, component := range finalComponentList { + evaluatedValues, interactURLs := rule.options.Variables.EvaluateWithInteractsh(baseValues, rule.options.Interactsh) + input.Values = generators.MergeMaps(evaluatedValues, baseValues, rule.options.Constants) + input.InteractURLs = interactURLs + err := rule.executeRuleValues(input, component) + if err != nil { + return err + } + } + return nil + } +mainLoop: + for _, component := range finalComponentList { + iterator := rule.generator.NewIterator() + for { + values, next := iterator.Value() + if !next { + continue mainLoop + } + evaluatedValues, interactURLs := rule.options.Variables.EvaluateWithInteractsh(generators.MergeMaps(values, baseValues), rule.options.Interactsh) + input.InteractURLs = interactURLs + input.Values = generators.MergeMaps(values, evaluatedValues, baseValues, rule.options.Constants) + + if err := rule.executeRuleValues(input, component); err != nil { + if err == io.EOF { + return nil + } + gologger.Warning().Msgf("[%s] Could not execute rule: %s\n", rule.options.TemplateID, err) + return err + } + } + } + return nil +} + +// isInputURLValid returns true if url is valid after parsing it +func (rule *Rule) isInputURLValid(input *contextargs.Context) bool { + if input == nil || input.MetaInput == nil || input.MetaInput.Input == "" { + return false + } + _, err := urlutil.Parse(input.MetaInput.Input) + return err == nil +} + +// executeRuleValues executes a rule with a set of values +func (rule *Rule) executeRuleValues(input *ExecuteRuleInput, ruleComponent component.Component) error { + // if we are only fuzzing values + if len(rule.Fuzz.Value) > 0 { + for _, value := range rule.Fuzz.Value { + if err := rule.executePartRule(input, ValueOrKeyValue{Value: value}, ruleComponent); err != nil { + if component.IsErrSetValue(err) { + // this are errors due to format restrictions + // ex: fuzzing string value in a json int field + continue + } + return err + } + } + return nil + } + + // if we are fuzzing both keys and values + if rule.Fuzz.KV != nil { + var gotErr error + rule.Fuzz.KV.Iterate(func(key, value string) bool { + if err := rule.executePartRule(input, ValueOrKeyValue{Key: key, Value: value}, ruleComponent); err != nil { + if component.IsErrSetValue(err) { + // this are errors due to format restrictions + // ex: fuzzing string value in a json int field + return true + } + gotErr = err + return false + } + return true + }) + // if mode is multiple now build and execute it + if rule.modeType == multipleModeType { + req, err := ruleComponent.Rebuild() + if err != nil { + return err + } + if gotErr := rule.execWithInput(input, req, input.InteractURLs, ruleComponent); gotErr != nil { + return gotErr + } + } + return gotErr + } + + // something else is wrong + return fmt.Errorf("no fuzz values specified") +} + +// Compile compiles a fuzzing rule and initializes it for operation +func (rule *Rule) Compile(generator *generators.PayloadGenerator, options *protocols.ExecutorOptions) error { + // If a payload generator is specified from base request, use it + // for payload values. + if generator != nil { + rule.generator = generator + } + rule.options = options + + // Resolve the default enums + if rule.Mode != "" { + if valueType, ok := stringToModeType[rule.Mode]; !ok { + return errors.Errorf("invalid mode value specified: %s", rule.Mode) + } else { + rule.modeType = valueType + } + } else { + rule.modeType = multipleModeType + } + if rule.Part != "" { + if valueType, ok := stringToPartType[rule.Part]; !ok { + return errors.Errorf("invalid part value specified: %s", rule.Part) + } else { + rule.partType = valueType + } + } else { + rule.partType = queryPartType + } + + if rule.Type != "" { + if valueType, ok := stringToRuleType[rule.Type]; !ok { + return errors.Errorf("invalid type value specified: %s", rule.Type) + } else { + rule.ruleType = valueType + } + } else { + rule.ruleType = replaceRuleType + } + + // Initialize other required regexes and maps + if len(rule.Keys) > 0 { + rule.keysMap = make(map[string]struct{}) + } + for _, key := range rule.Keys { + rule.keysMap[strings.ToLower(key)] = struct{}{} + } + for _, value := range rule.ValuesRegex { + compiled, err := regexp.Compile(value) + if err != nil { + return errors.Wrap(err, "could not compile value regex") + } + rule.valuesRegex = append(rule.valuesRegex, compiled) + } + for _, value := range rule.KeysRegex { + compiled, err := regexp.Compile(value) + if err != nil { + return errors.Wrap(err, "could not compile key regex") + } + rule.keysRegex = append(rule.keysRegex, compiled) + } + if rule.ruleType != replaceRegexRuleType { + if rule.ReplaceRegex != "" { + return errors.Errorf("replace-regex is only applicable for replace and replace-regex rule types") + } + } else { + if rule.ReplaceRegex == "" { + return errors.Errorf("replace-regex is required for replace-regex rule type") + } + compiled, err := regexp.Compile(rule.ReplaceRegex) + if err != nil { + return errors.Wrap(err, "could not compile replace regex") + } + rule.replaceRegex = compiled + } + return nil +} diff --git a/pkg/protocols/common/fuzz/fuzz.go b/pkg/fuzz/fuzz.go similarity index 75% rename from pkg/protocols/common/fuzz/fuzz.go rename to pkg/fuzz/fuzz.go index 0cf37a23..c70bc3b2 100644 --- a/pkg/protocols/common/fuzz/fuzz.go +++ b/pkg/fuzz/fuzz.go @@ -20,7 +20,7 @@ type Rule struct { // - "prefix" // - "postfix" // - "infix" - Type string `yaml:"type,omitempty" json:"type,omitempty" jsonschema:"title=type of rule,description=Type of fuzzing rule to perform,enum=replace,enum=prefix,enum=postfix,enum=infix"` + Type string `yaml:"type,omitempty" json:"type,omitempty" jsonschema:"title=type of rule,description=Type of fuzzing rule to perform,enum=replace,enum=prefix,enum=postfix,enum=infix,enum=replace-regex"` ruleType ruleType // description: | // Part is the part of request to fuzz. @@ -28,7 +28,7 @@ type Rule struct { // query fuzzes the query part of url. More parts will be added later. // values: // - "query" - Part string `yaml:"part,omitempty" json:"part,omitempty" jsonschema:"title=part of rule,description=Part of request rule to fuzz,enum=query"` + Part string `yaml:"part,omitempty" json:"part,omitempty" jsonschema:"title=part of rule,description=Part of request rule to fuzz,enum=query,enum=header,enum=path,enum=body,enum=cookie,enum=request"` partType partType // description: | // Mode is the mode of fuzzing to perform. @@ -71,10 +71,20 @@ type Rule struct { // - name: Examples of fuzz // value: > // []string{"{{ssrf}}", "{{interactsh-url}}", "example-value"} - Fuzz []string `yaml:"fuzz,omitempty" json:"fuzz,omitempty" jsonschema:"title=payloads of fuzz rule,description=Payloads to perform fuzzing substitutions with"` - - options *protocols.ExecutorOptions - generator *generators.PayloadGenerator + // or + // x-header: 1 + // x-header: 2 + Fuzz SliceOrMapSlice `yaml:"fuzz,omitempty" json:"fuzz,omitempty" jsonschema:"title=payloads of fuzz rule,description=Payloads to perform fuzzing substitutions with"` + // description: | + // replace-regex is regex for regex-replace rule type + // it is only required for replace-regex rule type + // examples: + // - type: replace-regex + // replace-regex: "https?://.*" + ReplaceRegex string `yaml:"replace-regex,omitempty" json:"replace-regex,omitempty" jsonschema:"title=replace regex of rule,description=Regex for regex-replace rule type"` + replaceRegex *regexp.Regexp `yaml:"-" json:"-"` + options *protocols.ExecutorOptions + generator *generators.PayloadGenerator } // ruleType is the type of rule enum declaration @@ -85,13 +95,15 @@ const ( prefixRuleType postfixRuleType infixRuleType + replaceRegexRuleType ) var stringToRuleType = map[string]ruleType{ - "replace": replaceRuleType, - "prefix": prefixRuleType, - "postfix": postfixRuleType, - "infix": infixRuleType, + "replace": replaceRuleType, + "prefix": prefixRuleType, + "postfix": postfixRuleType, + "infix": infixRuleType, + "replace-regex": replaceRegexRuleType, } // partType is the part of rule enum declaration @@ -100,11 +112,19 @@ type partType int const ( queryPartType partType = iota + 1 headersPartType + pathPartType + bodyPartType + cookiePartType + requestPartType ) var stringToPartType = map[string]partType{ "query": queryPartType, - "headers": headersPartType, + "header": headersPartType, + "path": pathPartType, + "body": bodyPartType, + "cookie": cookiePartType, + "request": requestPartType, // request means all request parts } // modeType is the mode of rule enum declaration diff --git a/pkg/protocols/common/fuzz/fuzz_test.go b/pkg/fuzz/fuzz_test.go similarity index 100% rename from pkg/protocols/common/fuzz/fuzz_test.go rename to pkg/fuzz/fuzz_test.go diff --git a/pkg/fuzz/parts.go b/pkg/fuzz/parts.go new file mode 100644 index 00000000..9b24446b --- /dev/null +++ b/pkg/fuzz/parts.go @@ -0,0 +1,210 @@ +package fuzz + +import ( + "io" + "strings" + + "github.com/projectdiscovery/nuclei/v3/pkg/fuzz/component" + "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/expressions" + "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators" + "github.com/projectdiscovery/nuclei/v3/pkg/types" + "github.com/projectdiscovery/retryablehttp-go" +) + +// executePartRule executes part rules based on type +func (rule *Rule) executePartRule(input *ExecuteRuleInput, payload ValueOrKeyValue, component component.Component) error { + return rule.executePartComponent(input, payload, component) +} + +// checkRuleApplicableOnComponent checks if a rule is applicable on given component +func (rule *Rule) checkRuleApplicableOnComponent(component component.Component) bool { + if rule.Part != component.Name() { + return false + } + foundAny := false + _ = component.Iterate(func(key string, value interface{}) error { + if rule.matchKeyOrValue(key, types.ToString(value)) { + foundAny = true + return io.EOF + } + return nil + }) + return foundAny +} + +// executePartComponent executes this rule on a given component and payload +func (rule *Rule) executePartComponent(input *ExecuteRuleInput, payload ValueOrKeyValue, ruleComponent component.Component) error { + if payload.IsKV() { + // for kv fuzzing + return rule.executePartComponentOnKV(input, payload, ruleComponent) + } else { + // for value only fuzzing + return rule.executePartComponentOnValues(input, payload.Value, ruleComponent) + } +} + +// executePartComponentOnValues executes this rule on a given component and payload +// this supports both single and multiple [ruleType] modes +// i.e if component has multiple values, they can be replaced once or all depending on mode +func (rule *Rule) executePartComponentOnValues(input *ExecuteRuleInput, payloadStr string, ruleComponent component.Component) error { + finalErr := ruleComponent.Iterate(func(key string, value interface{}) error { + valueStr := types.ToString(value) + if !rule.matchKeyOrValue(key, valueStr) { + // ignore non-matching keys + return nil + } + + var evaluated string + evaluated, input.InteractURLs = rule.executeEvaluate(input, key, valueStr, payloadStr, input.InteractURLs) + if err := ruleComponent.SetValue(key, evaluated); err != nil { + // gologger.Warning().Msgf("could not set value due to format restriction original(%s, %s[%T]) , new(%s,%s[%T])", key, valueStr, value, key, evaluated, evaluated) + return nil + } + + if rule.modeType == singleModeType { + req, err := ruleComponent.Rebuild() + if err != nil { + return err + } + + if qerr := rule.execWithInput(input, req, input.InteractURLs, ruleComponent); qerr != nil { + return qerr + } + // fmt.Printf("executed with value: %s\n", evaluated) + err = ruleComponent.SetValue(key, valueStr) // change back to previous value for temp + if err != nil { + return err + } + } + return nil + }) + if finalErr != nil { + return finalErr + } + + // We do not support analyzers with + // multiple payload mode. + if rule.modeType == multipleModeType { + req, err := ruleComponent.Rebuild() + if err != nil { + return err + } + if qerr := rule.execWithInput(input, req, input.InteractURLs, ruleComponent); qerr != nil { + err = qerr + return err + } + } + return nil +} + +// executePartComponentOnKV executes this rule on a given component and payload +// currently only supports single mode +func (rule *Rule) executePartComponentOnKV(input *ExecuteRuleInput, payload ValueOrKeyValue, ruleComponent component.Component) error { + var origKey string + var origValue interface{} + // when we have a key-value pair, iterate over only 1 value of the component + // multiple values (aka multiple mode) not supported for this yet + _ = ruleComponent.Iterate(func(key string, value interface{}) error { + if key == payload.Key { + origKey = key + origValue = value + } + return nil + }) + // iterate over given kv instead of component ones + return func(key, value string) error { + var evaluated string + evaluated, input.InteractURLs = rule.executeEvaluate(input, key, "", value, input.InteractURLs) + if err := ruleComponent.SetValue(key, evaluated); err != nil { + return err + } + if rule.modeType == singleModeType { + req, err := ruleComponent.Rebuild() + if err != nil { + return err + } + + if qerr := rule.execWithInput(input, req, input.InteractURLs, ruleComponent); qerr != nil { + return err + } + + // after building change back to original value to avoid repeating it in furthur requests + if origKey != "" { + err = ruleComponent.SetValue(origKey, types.ToString(origValue)) // change back to previous value for temp + if err != nil { + return err + } + } else { + _ = ruleComponent.Delete(key) // change back to previous value for temp + } + } + return nil + }(payload.Key, payload.Value) +} + +// execWithInput executes a rule with input via callback +func (rule *Rule) execWithInput(input *ExecuteRuleInput, httpReq *retryablehttp.Request, interactURLs []string, component component.Component) error { + request := GeneratedRequest{ + Request: httpReq, + InteractURLs: interactURLs, + DynamicValues: input.Values, + Component: component, + } + if !input.Callback(request) { + return types.ErrNoMoreRequests + } + return nil +} + +// executeEvaluate executes evaluation of payload on a key and value and +// returns completed values to be replaced and processed +// for fuzzing. +func (rule *Rule) executeEvaluate(input *ExecuteRuleInput, _, value, payload string, interactshURLs []string) (string, []string) { + // TODO: Handle errors + values := generators.MergeMaps(input.Values, map[string]interface{}{ + "value": value, + }, rule.options.Options.Vars.AsMap(), rule.options.Variables.GetAll()) + firstpass, _ := expressions.Evaluate(payload, values) + interactData, interactshURLs := rule.options.Interactsh.Replace(firstpass, interactshURLs) + evaluated, _ := expressions.Evaluate(interactData, values) + replaced := rule.executeRuleTypes(input, value, evaluated) + return replaced, interactshURLs +} + +// executeRuleTypes executes replacement for a key and value +// ex: prefix, postfix, infix, replace , replace-regex +func (rule *Rule) executeRuleTypes(_ *ExecuteRuleInput, value, replacement string) string { + var builder strings.Builder + if rule.ruleType == prefixRuleType || rule.ruleType == postfixRuleType { + builder.Grow(len(value) + len(replacement)) + } + var returnValue string + + switch rule.ruleType { + case prefixRuleType: + builder.WriteString(replacement) + builder.WriteString(value) + returnValue = builder.String() + case postfixRuleType: + builder.WriteString(value) + builder.WriteString(replacement) + returnValue = builder.String() + case infixRuleType: + if len(value) <= 1 { + builder.WriteString(value) + builder.WriteString(replacement) + returnValue = builder.String() + } else { + middleIndex := len(value) / 2 + builder.WriteString(value[:middleIndex]) + builder.WriteString(replacement) + builder.WriteString(value[middleIndex:]) + returnValue = builder.String() + } + case replaceRuleType: + returnValue = replacement + case replaceRegexRuleType: + returnValue = rule.replaceRegex.ReplaceAllString(value, replacement) + } + return returnValue +} diff --git a/pkg/fuzz/parts_test.go b/pkg/fuzz/parts_test.go new file mode 100644 index 00000000..d31f0ea0 --- /dev/null +++ b/pkg/fuzz/parts_test.go @@ -0,0 +1,2 @@ +// TODO: Write tests +package fuzz diff --git a/pkg/fuzz/type.go b/pkg/fuzz/type.go new file mode 100644 index 00000000..6110b2cd --- /dev/null +++ b/pkg/fuzz/type.go @@ -0,0 +1,80 @@ +package fuzz + +import ( + "encoding/json" + "fmt" + + mapsutil "github.com/projectdiscovery/utils/maps" + "gopkg.in/yaml.v2" +) + +var ( + _ json.Marshaler = &SliceOrMapSlice{} + _ json.Unmarshaler = &SliceOrMapSlice{} + _ yaml.Marshaler = &SliceOrMapSlice{} + _ yaml.Unmarshaler = &SliceOrMapSlice{} +) + +type ValueOrKeyValue struct { + Key string + Value string +} + +func (v *ValueOrKeyValue) IsKV() bool { + return v.Key != "" +} + +type SliceOrMapSlice struct { + Value []string + KV *mapsutil.OrderedMap[string, string] +} + +// UnmarshalJSON implements json.Unmarshaler interface. +func (v *SliceOrMapSlice) UnmarshalJSON(data []byte) error { + // try to unmashal as a string and fallback to map + if err := json.Unmarshal(data, &v.Value); err == nil { + return nil + } + err := json.Unmarshal(data, &v.KV) + if err != nil { + return fmt.Errorf("object can be a key:value or a string") + } + return nil +} + +// MarshalJSON implements json.Marshaler interface. +func (v SliceOrMapSlice) MarshalJSON() ([]byte, error) { + if v.KV != nil { + return json.Marshal(v.KV) + } + return json.Marshal(v.Value) +} + +// UnmarshalYAML implements yaml.Unmarshaler interface. +func (v *SliceOrMapSlice) UnmarshalYAML(callback func(interface{}) error) error { + // try to unmarshal it as a string and fallback to map + if err := callback(&v.Value); err == nil { + return nil + } + + // try with a mapslice + var node yaml.MapSlice + if err := callback(&node); err == nil { + tmpx := mapsutil.NewOrderedMap[string, string]() + // preserve order + for _, v := range node { + tmpx.Set(v.Key.(string), v.Value.(string)) + } + v.KV = &tmpx + return nil + } + return fmt.Errorf("object can be a key:value or a string") +} + +// MarshalYAML implements yaml.Marshaler interface. +func (v SliceOrMapSlice) MarshalYAML() (any, error) { + if v.KV != nil { + return v.KV, nil + } + return v.Value, nil +} diff --git a/pkg/input/README.md b/pkg/input/README.md new file mode 100644 index 00000000..4e8e5932 --- /dev/null +++ b/pkg/input/README.md @@ -0,0 +1,29 @@ +## input + +input package contains and provides loading, parsing , validating and normalizing of input data + + +## [transform](./transform.go) + +Transform package transforms or normalizes the input data before it is sent to protocol executer this step mainly involves changes like adding default ports (if missing) , validating if input is file or directory or url and adjusting the input accordingly etc. + + +## Provider + +Provider package contains the interface that every input format should implement for providing that input format to nuclei. + +Currently Nuclei Supports three input providers: + +1. SimpleInputProvider = A No-Op provider that takes a list of urls and implements the provider interface. + +2. HttpInputProvider = A provider that supports loading and parsing input formats that contain complete Http Data like Entire Request, Response etc. Supported formats include Burp,openapi,swagger,postman,proxify etc. + +3. ListInputProvider = Legacy/Default Provider that handles all list type inputs like urls,domains,ips,cidrs,files etc. + + +```go +func NewInputProvider(opts InputOptions) (InputProvider, error) +``` + +This function returns a InputProvider based by appropriately selecting input provider based on the input format (i.e either list or http) and returns the provider that can handle that input format. + diff --git a/pkg/input/formats/README.md b/pkg/input/formats/README.md new file mode 100644 index 00000000..3db9cdc1 --- /dev/null +++ b/pkg/input/formats/README.md @@ -0,0 +1,90 @@ +# formats + +Formats implements support for passing a number of request source as input providers to nuclei to be tested for fuzzing related issues. + +Currently the following formats are implemented - + +- Burp Suite XML Request/Response file +- Proxify JSONL output file +- OpenAPI Specification file +- Postman Collection file +- Swagger Specification file + +Each implementation implements either the entire or a subset of the features of the specifications. These can be increased further to add support as new things or requirements are identified. + +Refer to the specific code for each implementation to understand supported features of the specs. + + +## OpenAPI Specification File + +It is designed to generate HTTP requests based on an OpenAPI 3.0 Schema. Here is how these schema components are processed: + +### Servers + +The module supports multiple server URLs defined in the `Servers` section of the OpenAPI document. It will send requests to all the server URLs defined in the schema. + +### Paths and Operations + +The module supports all HTTP methods defined under each path in the `Paths` section. For each operation on a path, HTTP requests are generated and sent to the defined server URL. If the operation cannot generate a valid request, a warning will be logged. + +### Parameters + +The module recognizes parameters defined in the `query`, `header`, `path`, and `cookie` categories. When generating requests, if the `requiredOnly` flag is true, only the required parameters are included. Otherwise, all parameters, regardless of their required status, are used. + +The `generateExampleFromSchema` function is used to generate suitable example data for each parameter from their respective schema definitions. + +### RequestBody + +The module also comprehends request bodies and supports various media types defined in the `Content` field. Currently, the following content-types are supported: + +- `application/json`: The module creates application-specific JSON from the defined example schema. + +- `application/xml`: The example schema is converted into xml format using `mxj` library. + +- `application/x-www-form-urlencoded`: The example schema is converted into URL-encoded form data. + +- `multipart/form-data`: The module supports multipart form-data and differentiates between fields and files using the `binary` format under the property schema. + +- `text/plain`: Converts the example schema into string format and send as plain text. + +For unsupported media types, no appropriate content type is found for the body. After setting up the body of the request, the module dumps the request and sends it to the defined server URL. + +### Example Request Generation + +This module converts each operation into one or more example HTTP requests. Each request is dumped into a string format, accompanied by its method, URL, headers, and body. These are send as a callback for further processing. + +_Please note: This document does not cover other features of OpenAPI specification like responses, security schemes, links, callbacks, etc. as these are not currently handled by the module._ + +## Postman Collection file + +This module parser Postman Collection JSON files. + +### 1. Request Parsing: + Able to parse requests detailed in the Postman package. The parser is capable of interpreting the HTTP method, URL, and Body of each request present in the collection. + +### 2. Header Parsing: + All HTTP headers set in the collection's request are parsed and set in the request. + +### 3. Auth Type Parsing: + Able to parse and set the `Authentication` options provided in the postman collection in the request headers. + Supported types of authentiction: + + 1. **API Key**: In header + 2. **Basic**: Setting basic auth through username, password. + 3. **Bearer Token**: Involves setting bearer auth using tokens. + 4. **No Auth**: No authentication is set. + +Note: Not all parts of the Postman Collection specification are supported. This parser does not currently support Postman variables or collection level variables and items. It also does not support more authentication types than detailed above. + +### Limitations: +* Does not support Postman variables +* Does not support Collection level variables and items +* Limited Authentication types supported + +## Swagger Specification file + +Swagger specification file is converted from OpenAPI 2.0 format to OpenAPI 3.0 format. After this, the OpenAPI parser from above is used. + +## Burp XML / Proxify JSONL + +These modules are generic and parse raw requests from these respective tools. \ No newline at end of file diff --git a/pkg/input/formats/burp/burp.go b/pkg/input/formats/burp/burp.go new file mode 100644 index 00000000..6ad5f548 --- /dev/null +++ b/pkg/input/formats/burp/burp.go @@ -0,0 +1,67 @@ +package burp + +import ( + "encoding/base64" + "os" + "strings" + + "github.com/pkg/errors" + "github.com/projectdiscovery/nuclei/v3/pkg/input/formats" + "github.com/projectdiscovery/nuclei/v3/pkg/input/types" + "github.com/projectdiscovery/utils/conversion" + "github.com/seh-msft/burpxml" +) + +// BurpFormat is a Burp XML File parser +type BurpFormat struct { + opts formats.InputFormatOptions +} + +// New creates a new Burp XML File parser +func New() *BurpFormat { + return &BurpFormat{} +} + +var _ formats.Format = &BurpFormat{} + +// Name returns the name of the format +func (j *BurpFormat) Name() string { + return "burp" +} + +func (j *BurpFormat) SetOptions(options formats.InputFormatOptions) { + j.opts = options +} + +// Parse parses the input and calls the provided callback +// function for each RawRequest it discovers. +func (j *BurpFormat) Parse(input string, resultsCb formats.ParseReqRespCallback) error { + file, err := os.Open(input) + if err != nil { + return errors.Wrap(err, "could not open data file") + } + defer file.Close() + + items, err := burpxml.Parse(file, true) + if err != nil { + return errors.Wrap(err, "could not decode burp xml schema") + } + + // Print the parsed data for verification + for _, item := range items.Items { + item := item + binx, err := base64.StdEncoding.DecodeString(item.Request.Raw) + if err != nil { + return errors.Wrap(err, "could not decode base64") + } + if strings.TrimSpace(conversion.String(binx)) == "" { + continue + } + rawRequest, err := types.ParseRawRequestWithURL(conversion.String(binx), item.Url) + if err != nil { + return errors.Wrap(err, "could not parse raw request") + } + resultsCb(rawRequest) // TODO: Handle false and true from callback + } + return nil +} diff --git a/pkg/input/formats/burp/burp_test.go b/pkg/input/formats/burp/burp_test.go new file mode 100644 index 00000000..330218a9 --- /dev/null +++ b/pkg/input/formats/burp/burp_test.go @@ -0,0 +1,33 @@ +package burp + +import ( + "testing" + + "github.com/projectdiscovery/nuclei/v3/pkg/input/types" + "github.com/stretchr/testify/require" +) + +func TestBurpParse(t *testing.T) { + format := New() + + proxifyInputFile := "../testdata/burp.xml" + + var gotMethodsToURLs []string + + err := format.Parse(proxifyInputFile, func(request *types.RequestResponse) bool { + gotMethodsToURLs = append(gotMethodsToURLs, request.URL.String()) + return false + }) + if err != nil { + t.Fatal(err) + } + + if len(gotMethodsToURLs) != 2 { + t.Fatalf("invalid number of methods: %d", len(gotMethodsToURLs)) + } + var expectedURLs = []string{ + "http://localhost:8087/scans", + "http://google.com/", + } + require.ElementsMatch(t, expectedURLs, gotMethodsToURLs, "could not get burp urls") +} diff --git a/pkg/input/formats/formats.go b/pkg/input/formats/formats.go new file mode 100644 index 00000000..af2b4569 --- /dev/null +++ b/pkg/input/formats/formats.go @@ -0,0 +1,103 @@ +package formats + +import ( + "errors" + "os" + "strings" + + "github.com/projectdiscovery/nuclei/v3/pkg/input/types" + fileutil "github.com/projectdiscovery/utils/file" + "gopkg.in/yaml.v3" +) + +// ParseReqRespCallback is a callback function for discovered raw requests +type ParseReqRespCallback func(rr *types.RequestResponse) bool + +// InputFormatOptions contains options for the input +// this can be variables that can be passed or +// overrides or some other options +type InputFormatOptions struct { + // Variables is list of variables that can be used + // while generating requests in given format + Variables map[string]interface{} + // SkipFormatValidation is used to skip format validation + // while debugging or testing if format is invalid then + // requests are skipped instead of creating invalid requests + SkipFormatValidation bool + // RequiredOnly only uses required fields when generating requests + // instead of all fields + RequiredOnly bool +} + +// Format is an interface implemented by all input formats +type Format interface { + // Name returns the name of the format + Name() string + // Parse parses the input and calls the provided callback + // function for each RawRequest it discovers. + Parse(input string, resultsCb ParseReqRespCallback) error + // SetOptions sets the options for the input format + SetOptions(options InputFormatOptions) +} + +var ( + DefaultVarDumpFileName = "required_openapi_params.yaml" + ErrNoVarsDumpFile = errors.New("no required params file found") +) + +// == OpenAPIParamsCfgFile == +// this file is meant to be used in CLI mode +// to be more interactive and user-friendly when +// running nuclei with openapi format + +// OpenAPIParamsCfgFile is the structure of the required vars dump file +type OpenAPIParamsCfgFile struct { + Var []string `yaml:"var"` + OptionalVars []string `yaml:"-"` // this will be written to the file as comments +} + +// ReadOpenAPIVarDumpFile reads the required vars dump file +func ReadOpenAPIVarDumpFile() (*OpenAPIParamsCfgFile, error) { + var vars OpenAPIParamsCfgFile + if !fileutil.FileExists(DefaultVarDumpFileName) { + return nil, ErrNoVarsDumpFile + } + bin, err := os.ReadFile(DefaultVarDumpFileName) + if err != nil { + return nil, err + } + err = yaml.Unmarshal(bin, &vars) + if err != nil { + return nil, err + } + filtered := []string{} + for _, v := range vars.Var { + v = strings.TrimSpace(v) + if !strings.HasSuffix(v, "=") { + filtered = append(filtered, v) + } + } + vars.Var = filtered + return &vars, nil +} + +// WriteOpenAPIVarDumpFile writes the required vars dump file +func WriteOpenAPIVarDumpFile(vars *OpenAPIParamsCfgFile) error { + f, err := os.OpenFile(DefaultVarDumpFileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + return err + } + defer f.Close() + bin, err := yaml.Marshal(vars) + if err != nil { + return err + } + _, _ = f.Write(bin) + if len(vars.OptionalVars) > 0 { + _, _ = f.WriteString("\n # Optional parameters\n") + for _, v := range vars.OptionalVars { + _, _ = f.WriteString(" # - " + v + "=\n") + } + } + return f.Sync() +} diff --git a/pkg/input/formats/json/json.go b/pkg/input/formats/json/json.go new file mode 100644 index 00000000..fecb1c6b --- /dev/null +++ b/pkg/input/formats/json/json.go @@ -0,0 +1,74 @@ +package json + +import ( + "encoding/json" + "io" + "os" + + "github.com/pkg/errors" + "github.com/projectdiscovery/gologger" + "github.com/projectdiscovery/nuclei/v3/pkg/input/formats" + "github.com/projectdiscovery/nuclei/v3/pkg/input/types" +) + +// JSONFormat is a JSON format parser for nuclei +// input HTTP requests +type JSONFormat struct { + opts formats.InputFormatOptions +} + +// New creates a new JSON format parser +func New() *JSONFormat { + return &JSONFormat{} +} + +var _ formats.Format = &JSONFormat{} + +// proxifyRequest is a request for proxify +type proxifyRequest struct { + URL string `json:"url"` + Request struct { + Header map[string]string `json:"header"` + Body string `json:"body"` + Raw string `json:"raw"` + } `json:"request"` +} + +// Name returns the name of the format +func (j *JSONFormat) Name() string { + return "jsonl" +} + +func (j *JSONFormat) SetOptions(options formats.InputFormatOptions) { + j.opts = options +} + +// Parse parses the input and calls the provided callback +// function for each RawRequest it discovers. +func (j *JSONFormat) Parse(input string, resultsCb formats.ParseReqRespCallback) error { + file, err := os.Open(input) + if err != nil { + return errors.Wrap(err, "could not open json file") + } + defer file.Close() + + decoder := json.NewDecoder(file) + for { + var request proxifyRequest + err := decoder.Decode(&request) + if err == io.EOF { + break + } + if err != nil { + return errors.Wrap(err, "could not decode json file") + } + + rawRequest, err := types.ParseRawRequestWithURL(request.Request.Raw, request.URL) + if err != nil { + gologger.Warning().Msgf("jsonl: Could not parse raw request %s: %s\n", request.URL, err) + continue + } + resultsCb(rawRequest) + } + return nil +} diff --git a/pkg/input/formats/json/json_test.go b/pkg/input/formats/json/json_test.go new file mode 100644 index 00000000..b72bf4c1 --- /dev/null +++ b/pkg/input/formats/json/json_test.go @@ -0,0 +1,57 @@ +package json + +import ( + "testing" + + "github.com/projectdiscovery/nuclei/v3/pkg/input/types" + "github.com/stretchr/testify/require" +) + +var expectedURLs = []string{ + "https://ginandjuice.shop/", + "https://ginandjuice.shop/catalog/product?productId=1", + "https://ginandjuice.shop/resources/js/stockCheck.js", + "https://ginandjuice.shop/resources/js/xmlStockCheckPayload.js", + "https://ginandjuice.shop/resources/js/xmlStockCheckPayload.js", + "https://ginandjuice.shop/resources/js/stockCheck.js", + "https://ginandjuice.shop/catalog/product/stock", + "https://ginandjuice.shop/catalog/cart", + "https://ginandjuice.shop/catalog/product?productId=1", + "https://ginandjuice.shop/catalog/subscribe", + "https://ginandjuice.shop/blog", + "https://ginandjuice.shop/blog/?search=dadad&back=%2Fblog%2F", + "https://ginandjuice.shop/logger", + "https://ginandjuice.shop/blog/", + "https://ginandjuice.shop/blog/post?postId=3", + "https://ginandjuice.shop/about", + "https://ginandjuice.shop/my-account", + "https://ginandjuice.shop/login", + "https://ginandjuice.shop/login", + "https://ginandjuice.shop/login", + "https://ginandjuice.shop/my-account", + "https://ginandjuice.shop/catalog/cart", + "https://ginandjuice.shop/my-account", + "https://ginandjuice.shop/logout", + "https://ginandjuice.shop/", + "https://ginandjuice.shop/catalog", +} + +func TestJSONFormatterParse(t *testing.T) { + format := New() + + proxifyInputFile := "../testdata/ginandjuice.proxify.json" + + var urls []string + err := format.Parse(proxifyInputFile, func(request *types.RequestResponse) bool { + urls = append(urls, request.URL.String()) + return false + }) + if err != nil { + t.Fatal(err) + } + + if len(urls) != len(expectedURLs) { + t.Fatalf("invalid number of urls: %d", len(urls)) + } + require.ElementsMatch(t, urls, expectedURLs) +} diff --git a/pkg/input/formats/openapi/examples.go b/pkg/input/formats/openapi/examples.go new file mode 100644 index 00000000..35c4292c --- /dev/null +++ b/pkg/input/formats/openapi/examples.go @@ -0,0 +1,290 @@ +package openapi + +import ( + "fmt" + + "github.com/getkin/kin-openapi/openapi3" + "github.com/pkg/errors" +) + +// From: https://github.com/danielgtaylor/apisprout/blob/master/example.go + +func getSchemaExample(schema *openapi3.Schema) (interface{}, bool) { + if schema.Example != nil { + return schema.Example, true + } + + if schema.Default != nil { + return schema.Default, true + } + + if schema.Enum != nil && len(schema.Enum) > 0 { + return schema.Enum[0], true + } + return nil, false +} + +// stringFormatExample returns an example string based on the given format. +// http://json-schema.org/latest/json-schema-validation.html#rfc.section.7.3 +func stringFormatExample(format string) string { + switch format { + case "date": + // https://tools.ietf.org/html/rfc3339 + return "2018-07-23" + case "date-time": + // This is the date/time of API Sprout's first commit! :-) + return "2018-07-23T22:58:00-07:00" + case "time": + return "22:58:00-07:00" + case "email": + return "email@example.com" + case "hostname": + // https://tools.ietf.org/html/rfc2606#page-2 + return "example.com" + case "ipv4": + // https://tools.ietf.org/html/rfc5737 + return "198.51.100.0" + case "ipv6": + // https://tools.ietf.org/html/rfc3849 + return "2001:0db8:85a3:0000:0000:8a2e:0370:7334" + case "uri": + return "https://tools.ietf.org/html/rfc3986" + case "uri-template": + // https://tools.ietf.org/html/rfc6570 + return "http://example.com/dictionary/{term:1}/{term}" + case "json-pointer": + // https://tools.ietf.org/html/rfc6901 + return "#/components/parameters/term" + case "regex": + // https://stackoverflow.com/q/3296050/164268 + return "/^1?$|^(11+?)\\1+$/" + case "uuid": + // https://www.ietf.org/rfc/rfc4122.txt + return "f81d4fae-7dec-11d0-a765-00a0c91e6bf6" + case "password": + return "********" + case "binary": + return "sagefuzzertest" + } + return "" +} + +// excludeFromMode will exclude a schema if the mode is request and the schema +// is read-only +func excludeFromMode(schema *openapi3.Schema) bool { + if schema == nil { + return true + } + + if schema.ReadOnly { + return true + } + return false +} + +// isRequired checks whether a key is actually required. +func isRequired(schema *openapi3.Schema, key string) bool { + for _, req := range schema.Required { + if req == key { + return true + } + } + + return false +} + +type cachedSchema struct { + pending bool + out interface{} +} + +var ( + // ErrRecursive is when a schema is impossible to represent because it infinitely recurses. + ErrRecursive = errors.New("Recursive schema") + + // ErrNoExample is sent when no example was found for an operation. + ErrNoExample = errors.New("No example found") +) + +func openAPIExample(schema *openapi3.Schema, cache map[*openapi3.Schema]*cachedSchema) (out interface{}, err error) { + if ex, ok := getSchemaExample(schema); ok { + return ex, nil + } + + cached, ok := cache[schema] + if !ok { + cached = &cachedSchema{ + pending: true, + } + cache[schema] = cached + } else if cached.pending { + return nil, ErrRecursive + } else { + return cached.out, nil + } + + defer func() { + cached.pending = false + cached.out = out + }() + + // Handle combining keywords + if len(schema.OneOf) > 0 { + var ex interface{} + var err error + + for _, candidate := range schema.OneOf { + ex, err = openAPIExample(candidate.Value, cache) + if err == nil { + break + } + } + return ex, err + } + if len(schema.AnyOf) > 0 { + var ex interface{} + var err error + + for _, candidate := range schema.AnyOf { + ex, err = openAPIExample(candidate.Value, cache) + if err == nil { + break + } + } + return ex, err + } + if len(schema.AllOf) > 0 { + example := map[string]interface{}{} + + for _, allOf := range schema.AllOf { + candidate, err := openAPIExample(allOf.Value, cache) + if err != nil { + return nil, err + } + + value, ok := candidate.(map[string]interface{}) + if !ok { + return nil, ErrNoExample + } + + for k, v := range value { + example[k] = v + } + } + return example, nil + } + + switch { + case schema.Type == "boolean": + return true, nil + case schema.Type == "number", schema.Type == "integer": + value := 0.0 + + if schema.Min != nil && *schema.Min > value { + value = *schema.Min + if schema.ExclusiveMin { + if schema.Max != nil { + // Make the value half way. + value = (*schema.Min + *schema.Max) / 2.0 + } else { + value++ + } + } + } + + if schema.Max != nil && *schema.Max < value { + value = *schema.Max + if schema.ExclusiveMax { + if schema.Min != nil { + // Make the value half way. + value = (*schema.Min + *schema.Max) / 2.0 + } else { + value-- + } + } + } + + if schema.MultipleOf != nil && int(value)%int(*schema.MultipleOf) != 0 { + value += float64(int(*schema.MultipleOf) - (int(value) % int(*schema.MultipleOf))) + } + + if schema.Type == "integer" { + return int(value), nil + } + return value, nil + case schema.Type == "string": + if ex := stringFormatExample(schema.Format); ex != "" { + return ex, nil + } + example := "string" + + for schema.MinLength > uint64(len(example)) { + example += example + } + + if schema.MaxLength != nil && *schema.MaxLength < uint64(len(example)) { + example = example[:*schema.MaxLength] + } + return example, nil + case schema.Type == "array", schema.Items != nil: + example := []interface{}{} + + if schema.Items != nil && schema.Items.Value != nil { + ex, err := openAPIExample(schema.Items.Value, cache) + if err != nil { + return nil, fmt.Errorf("can't get example for array item: %+v", err) + } + + example = append(example, ex) + + for uint64(len(example)) < schema.MinItems { + example = append(example, ex) + } + } + return example, nil + case schema.Type == "object", len(schema.Properties) > 0: + example := map[string]interface{}{} + + for k, v := range schema.Properties { + if excludeFromMode(v.Value) { + continue + } + + ex, err := openAPIExample(v.Value, cache) + if err == ErrRecursive { + if isRequired(schema, k) { + return nil, fmt.Errorf("can't get example for '%s': %+v", k, err) + } + } else if err != nil { + return nil, fmt.Errorf("can't get example for '%s': %+v", k, err) + } else { + example[k] = ex + } + } + + if schema.AdditionalProperties.Has != nil && schema.AdditionalProperties.Schema != nil { + addl := schema.AdditionalProperties.Schema.Value + + if !excludeFromMode(addl) { + ex, err := openAPIExample(addl, cache) + if err == ErrRecursive { + // We just won't add this if it's recursive. + } else if err != nil { + return nil, fmt.Errorf("can't get example for additional properties: %+v", err) + } else { + example["additionalPropertyName"] = ex + } + } + } + return example, nil + } + return nil, ErrNoExample +} + +// generateExampleFromSchema creates an example structure from an OpenAPI 3 schema +// object, which is an extended subset of JSON Schema. +// +// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#schemaObject +func generateExampleFromSchema(schema *openapi3.Schema) (interface{}, error) { + return openAPIExample(schema, make(map[*openapi3.Schema]*cachedSchema)) // TODO: Use caching +} diff --git a/pkg/input/formats/openapi/generator.go b/pkg/input/formats/openapi/generator.go new file mode 100644 index 00000000..090b761c --- /dev/null +++ b/pkg/input/formats/openapi/generator.go @@ -0,0 +1,461 @@ +package openapi + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "mime/multipart" + "net/http" + "net/http/httputil" + "net/url" + "os" + "strings" + + "github.com/clbanning/mxj/v2" + "github.com/getkin/kin-openapi/openapi3" + "github.com/pkg/errors" + "github.com/projectdiscovery/gologger" + "github.com/projectdiscovery/nuclei/v3/pkg/catalog/config" + "github.com/projectdiscovery/nuclei/v3/pkg/input/formats" + httpTypes "github.com/projectdiscovery/nuclei/v3/pkg/input/types" + "github.com/projectdiscovery/nuclei/v3/pkg/types" + errorutil "github.com/projectdiscovery/utils/errors" + "github.com/projectdiscovery/utils/generic" + mapsutil "github.com/projectdiscovery/utils/maps" + "github.com/valyala/fasttemplate" +) + +const ( + globalAuth = "globalAuth" +) + +// GenerateRequestsFromSchema generates http requests from an OpenAPI 3.0 document object +func GenerateRequestsFromSchema(schema *openapi3.T, opts formats.InputFormatOptions, callback formats.ParseReqRespCallback) error { + if len(schema.Servers) == 0 { + return errors.New("no servers found in openapi schema") + } + + // new set of globalParams obtained from security schemes + globalParams := openapi3.NewParameters() + + if len(schema.Security) > 0 { + params, err := GetGlobalParamsForSecurityRequirement(schema, &schema.Security) + if err != nil { + return err + } + globalParams = append(globalParams, params...) + } + + // validate global param requirements + for _, param := range globalParams { + if val, ok := opts.Variables[param.Value.Name]; ok { + param.Value.Example = val + } else { + // if missing check for validation + if opts.SkipFormatValidation { + gologger.Verbose().Msgf("openapi: skipping all requests due to missing global auth parameter: %s\n", param.Value.Name) + return nil + } else { + // fatal error + gologger.Fatal().Msgf("openapi: missing global auth parameter: %s\n", param.Value.Name) + } + } + } + + missingVarMap := make(map[string]struct{}) + optionalVarMap := make(map[string]struct{}) + missingParamValueCallback := func(param *openapi3.Parameter, opts *generateReqOptions) { + if !param.Required { + optionalVarMap[param.Name] = struct{}{} + return + } + missingVarMap[param.Name] = struct{}{} + } + + for _, serverURL := range schema.Servers { + pathURL := serverURL.URL + + for path, v := range schema.Paths.Map() { + // a path item can have parameters + ops := v.Operations() + requestPath := path + for method, ov := range ops { + if err := generateRequestsFromOp(&generateReqOptions{ + requiredOnly: opts.RequiredOnly, + method: method, + pathURL: pathURL, + requestPath: requestPath, + op: ov, + schema: schema, + globalParams: globalParams, + reqParams: v.Parameters, + opts: opts, + callback: callback, + missingParamValueCallback: missingParamValueCallback, + }); err != nil { + gologger.Warning().Msgf("Could not generate requests from op: %s\n", err) + } + } + } + } + + if len(missingVarMap) > 0 && !opts.SkipFormatValidation { + gologger.Error().Msgf("openapi: Found %d missing parameters, use -skip-format-validation flag to skip requests or update missing parameters generated in %s file,you can also specify these vars using -var flag in (key=value) format\n", len(missingVarMap), formats.DefaultVarDumpFileName) + gologger.Verbose().Msgf("openapi: missing params: %+v", mapsutil.GetSortedKeys(missingVarMap)) + if config.CurrentAppMode == config.AppModeCLI { + // generate var dump file + vars := &formats.OpenAPIParamsCfgFile{} + for k := range missingVarMap { + vars.Var = append(vars.Var, k+"=") + } + vars.OptionalVars = mapsutil.GetSortedKeys(optionalVarMap) + if err := formats.WriteOpenAPIVarDumpFile(vars); err != nil { + gologger.Error().Msgf("openapi: could not write params file: %s\n", err) + } + // exit with status code 1 + os.Exit(1) + } + } + + return nil +} + +type generateReqOptions struct { + // requiredOnly specifies whether to generate only required fields + requiredOnly bool + // method is the http method to use + method string + // pathURL is the base url to use + pathURL string + // requestPath is the path to use + requestPath string + // schema is the openapi schema to use + schema *openapi3.T + // op is the operation to use + op *openapi3.Operation + // post request generation callback + callback formats.ParseReqRespCallback + + // global parameters + globalParams openapi3.Parameters + // requestparams map + reqParams openapi3.Parameters + // global var map + opts formats.InputFormatOptions + // missingVar Callback + missingParamValueCallback func(param *openapi3.Parameter, opts *generateReqOptions) +} + +// generateRequestsFromOp generates requests from an operation and some other data +// about an OpenAPI Schema Path and Method object. +// +// It also accepts an optional requiredOnly flag which if specified, only returns the fields +// of the structure that are required. If false, all fields are returned. +func generateRequestsFromOp(opts *generateReqOptions) error { + req, err := http.NewRequest(opts.method, opts.pathURL+opts.requestPath, nil) + if err != nil { + return errors.Wrap(err, "could not make request") + } + + reqParams := opts.reqParams + if reqParams == nil { + reqParams = openapi3.NewParameters() + } + // add existing req params + reqParams = append(reqParams, opts.op.Parameters...) + // check for endpoint specific auth + if opts.op.Security != nil { + params, err := GetGlobalParamsForSecurityRequirement(opts.schema, opts.op.Security) + if err != nil { + return err + } + reqParams = append(reqParams, params...) + } else { + reqParams = append(reqParams, opts.globalParams...) + } + + query := url.Values{} + for _, parameter := range reqParams { + value := parameter.Value + + // paramValue or default value to use + var paramValue interface{} + + // accept override from global variables + if val, ok := opts.opts.Variables[value.Name]; ok { + paramValue = val + } else if value.Schema.Value.Default != nil { + paramValue = value.Schema.Value.Default + } else if value.Schema.Value.Example != nil { + paramValue = value.Schema.Value.Example + } else if value.Schema.Value.Enum != nil && len(value.Schema.Value.Enum) > 0 { + paramValue = value.Schema.Value.Enum[0] + } else { + if !opts.opts.SkipFormatValidation { + if opts.missingParamValueCallback != nil { + opts.missingParamValueCallback(value, opts) + } + // skip request if param in path else skip this param only + if value.Required { + // gologger.Verbose().Msgf("skipping request [%s] %s due to missing value (%v)\n", opts.method, opts.requestPath, value.Name) + return nil + } else { + // if it is in path then remove it from path + opts.requestPath = strings.Replace(opts.requestPath, fmt.Sprintf("{%s}", value.Name), "", -1) + if !opts.opts.RequiredOnly { + gologger.Verbose().Msgf("openapi: skipping optional param (%s) in (%v) in request [%s] %s due to missing value (%v)\n", value.Name, value.In, opts.method, opts.requestPath, value.Name) + } + continue + } + } + exampleX, err := generateExampleFromSchema(value.Schema.Value) + if err != nil { + // when failed to generate example + // skip request if param in path else skip this param only + if value.Required { + gologger.Verbose().Msgf("openapi: skipping request [%s] %s due to missing value (%v)\n", opts.method, opts.requestPath, value.Name) + return nil + } else { + // if it is in path then remove it from path + opts.requestPath = strings.Replace(opts.requestPath, fmt.Sprintf("{%s}", value.Name), "", -1) + if !opts.opts.RequiredOnly { + gologger.Verbose().Msgf("openapi: skipping optinal param (%s) in (%v) in request [%s] %s due to missing value (%v)\n", value.Name, value.In, opts.method, opts.requestPath, value.Name) + } + continue + } + } + paramValue = exampleX + } + if opts.requiredOnly && !value.Required { + // remove them from path if any + opts.requestPath = strings.Replace(opts.requestPath, fmt.Sprintf("{%s}", value.Name), "", -1) + continue // Skip this parameter if it is not required and we want only required ones + } + + switch value.In { + case "query": + query.Set(value.Name, types.ToString(paramValue)) + case "header": + req.Header.Set(value.Name, types.ToString(paramValue)) + case "path": + opts.requestPath = fasttemplate.ExecuteStringStd(opts.requestPath, "{", "}", map[string]interface{}{ + value.Name: types.ToString(paramValue), + }) + case "cookie": + req.AddCookie(&http.Cookie{Name: value.Name, Value: types.ToString(paramValue)}) + } + } + req.URL.RawQuery = query.Encode() + req.URL.Path = opts.requestPath + + if opts.op.RequestBody != nil { + for content, value := range opts.op.RequestBody.Value.Content { + cloned := req.Clone(req.Context()) + + example, err := generateExampleFromSchema(value.Schema.Value) + if err != nil { + continue + } + + // var body string + switch content { + case "application/json": + if marshalled, err := json.Marshal(example); err == nil { + // body = string(marshalled) + cloned.Body = io.NopCloser(bytes.NewReader(marshalled)) + cloned.ContentLength = int64(len(marshalled)) + cloned.Header.Set("Content-Type", "application/json") + } + case "application/xml": + exampleVal := mxj.Map(example.(map[string]interface{})) + + if marshalled, err := exampleVal.Xml(); err == nil { + // body = string(marshalled) + cloned.Body = io.NopCloser(bytes.NewReader(marshalled)) + cloned.ContentLength = int64(len(marshalled)) + cloned.Header.Set("Content-Type", "application/xml") + } else { + gologger.Warning().Msgf("openapi: could not encode xml") + } + case "application/x-www-form-urlencoded": + if values, ok := example.(map[string]interface{}); ok { + cloned.Form = url.Values{} + for k, v := range values { + cloned.Form.Set(k, types.ToString(v)) + } + encoded := cloned.Form.Encode() + cloned.ContentLength = int64(len(encoded)) + // body = encoded + cloned.Body = io.NopCloser(strings.NewReader(encoded)) + cloned.Header.Set("Content-Type", "application/x-www-form-urlencoded") + } + case "multipart/form-data": + if values, ok := example.(map[string]interface{}); ok { + buffer := &bytes.Buffer{} + multipartWriter := multipart.NewWriter(buffer) + for k, v := range values { + // This is a file if format is binary, otherwise field + if property, ok := value.Schema.Value.Properties[k]; ok && property.Value.Format == "binary" { + if writer, err := multipartWriter.CreateFormFile(k, k); err == nil { + _, _ = writer.Write([]byte(types.ToString(v))) + } + } else { + _ = multipartWriter.WriteField(k, types.ToString(v)) + } + } + multipartWriter.Close() + // body = buffer.String() + cloned.Body = io.NopCloser(buffer) + cloned.ContentLength = int64(len(buffer.Bytes())) + cloned.Header.Set("Content-Type", multipartWriter.FormDataContentType()) + } + case "text/plain": + str := types.ToString(example) + // body = str + cloned.Body = io.NopCloser(strings.NewReader(str)) + cloned.ContentLength = int64(len(str)) + cloned.Header.Set("Content-Type", "text/plain") + case "application/octet-stream": + str := types.ToString(example) + if str == "" { + // use two strings + str = "string1\nstring2" + } + if value.Schema != nil && generic.EqualsAny(value.Schema.Value.Format, "bindary", "byte") { + cloned.Body = io.NopCloser(bytes.NewReader([]byte(str))) + cloned.ContentLength = int64(len(str)) + cloned.Header.Set("Content-Type", "application/octet-stream") + } else { + // use string placeholder + cloned.Body = io.NopCloser(strings.NewReader(str)) + cloned.ContentLength = int64(len(str)) + cloned.Header.Set("Content-Type", "text/plain") + } + default: + gologger.Verbose().Msgf("openapi: no correct content type found for body: %s\n", content) + // LOG: return errors.New("no correct content type found for body") + continue + } + + dumped, err := httputil.DumpRequestOut(cloned, true) + if err != nil { + return errors.Wrap(err, "could not dump request") + } + + rr, err := httpTypes.ParseRawRequestWithURL(string(dumped), cloned.URL.String()) + if err != nil { + return errors.Wrap(err, "could not parse raw request") + } + opts.callback(rr) + continue + } + } + if opts.op.RequestBody != nil { + return nil + } + + dumped, err := httputil.DumpRequestOut(req, true) + if err != nil { + return errors.Wrap(err, "could not dump request") + } + + rr, err := httpTypes.ParseRawRequestWithURL(string(dumped), req.URL.String()) + if err != nil { + return errors.Wrap(err, "could not parse raw request") + } + opts.callback(rr) + return nil +} + +// GetGlobalParamsForSecurityRequirement returns the global parameters for a security requirement +func GetGlobalParamsForSecurityRequirement(schema *openapi3.T, requirement *openapi3.SecurityRequirements) ([]*openapi3.ParameterRef, error) { + globalParams := openapi3.NewParameters() + if len(schema.Components.SecuritySchemes) == 0 { + return nil, errorutil.NewWithTag("openapi", "security requirements (%+v) without any security schemes found in openapi file", schema.Security) + } + found := false + // this api is protected for each security scheme pull its corresponding scheme +schemaLabel: + for _, security := range *requirement { + for name := range security { + if scheme, ok := schema.Components.SecuritySchemes[name]; ok { + found = true + param, err := GenerateParameterFromSecurityScheme(scheme) + if err != nil { + return nil, err + + } + globalParams = append(globalParams, &openapi3.ParameterRef{Value: param}) + continue schemaLabel + } + } + if !found && len(security) > 1 { + // if this is case then both security schemes are required + return nil, errorutil.NewWithTag("openapi", "security requirement (%+v) not found in openapi file", security) + } + } + if !found { + return nil, errorutil.NewWithTag("openapi", "security requirement (%+v) not found in openapi file", requirement) + } + + return globalParams, nil +} + +// generateExampleFromSchema generates an example from a schema object +func GenerateParameterFromSecurityScheme(scheme *openapi3.SecuritySchemeRef) (*openapi3.Parameter, error) { + if !generic.EqualsAny(scheme.Value.Type, "http", "apiKey") { + return nil, errorutil.NewWithTag("openapi", "unsupported security scheme type (%s) found in openapi file", scheme.Value.Type) + } + if scheme.Value.Type == "http" { + // check scheme + if !generic.EqualsAny(scheme.Value.Scheme, "basic", "bearer") { + return nil, errorutil.NewWithTag("openapi", "unsupported security scheme (%s) found in openapi file", scheme.Value.Scheme) + } + if scheme.Value.Name == "" { + return nil, errorutil.NewWithTag("openapi", "security scheme (%s) name is empty", scheme.Value.Scheme) + } + // create parameters using the scheme + switch scheme.Value.Scheme { + case "basic": + h := openapi3.NewHeaderParameter(scheme.Value.Name) + h.Required = true + h.Description = globalAuth // differentiator for normal variables and global auth + return h, nil + case "bearer": + h := openapi3.NewHeaderParameter(scheme.Value.Name) + h.Required = true + h.Description = globalAuth // differentiator for normal variables and global auth + return h, nil + } + + } + if scheme.Value.Type == "apiKey" { + // validate name and in + if scheme.Value.Name == "" { + return nil, errorutil.NewWithTag("openapi", "security scheme (%s) name is empty", scheme.Value.Type) + } + if !generic.EqualsAny(scheme.Value.In, "query", "header", "cookie") { + return nil, errorutil.NewWithTag("openapi", "unsupported security scheme (%s) in (%s) found in openapi file", scheme.Value.Type, scheme.Value.In) + } + // create parameters using the scheme + switch scheme.Value.In { + case "query": + q := openapi3.NewQueryParameter(scheme.Value.Name) + q.Required = true + q.Description = globalAuth // differentiator for normal variables and global auth + return q, nil + case "header": + h := openapi3.NewHeaderParameter(scheme.Value.Name) + h.Required = true + h.Description = globalAuth // differentiator for normal variables and global auth + return h, nil + case "cookie": + c := openapi3.NewCookieParameter(scheme.Value.Name) + c.Required = true + c.Description = globalAuth // differentiator for normal variables and global auth + return c, nil + } + } + return nil, errorutil.NewWithTag("openapi", "unsupported security scheme type (%s) found in openapi file", scheme.Value.Type) +} diff --git a/pkg/input/formats/openapi/openapi.go b/pkg/input/formats/openapi/openapi.go new file mode 100644 index 00000000..afbe379f --- /dev/null +++ b/pkg/input/formats/openapi/openapi.go @@ -0,0 +1,39 @@ +package openapi + +import ( + "github.com/getkin/kin-openapi/openapi3" + "github.com/pkg/errors" + "github.com/projectdiscovery/nuclei/v3/pkg/input/formats" +) + +// OpenAPIFormat is a OpenAPI Schema File parser +type OpenAPIFormat struct { + opts formats.InputFormatOptions +} + +// New creates a new OpenAPI format parser +func New() *OpenAPIFormat { + return &OpenAPIFormat{} +} + +var _ formats.Format = &OpenAPIFormat{} + +// Name returns the name of the format +func (j *OpenAPIFormat) Name() string { + return "openapi" +} + +func (j *OpenAPIFormat) SetOptions(options formats.InputFormatOptions) { + j.opts = options +} + +// Parse parses the input and calls the provided callback +// function for each RawRequest it discovers. +func (j *OpenAPIFormat) Parse(input string, resultsCb formats.ParseReqRespCallback) error { + loader := openapi3.NewLoader() + schema, err := loader.LoadFromFile(input) + if err != nil { + return errors.Wrap(err, "could not decode openapi 3.0 schema") + } + return GenerateRequestsFromSchema(schema, j.opts, resultsCb) +} diff --git a/pkg/input/formats/openapi/openapi_test.go b/pkg/input/formats/openapi/openapi_test.go new file mode 100644 index 00000000..f48385a8 --- /dev/null +++ b/pkg/input/formats/openapi/openapi_test.go @@ -0,0 +1,63 @@ +package openapi + +import ( + "strings" + "testing" + + "github.com/projectdiscovery/nuclei/v3/pkg/input/types" + "github.com/stretchr/testify/require" +) + +const baseURL = "http://hackthebox:5000" + +var methodToURLs = map[string][]string{ + "GET": { + "{{baseUrl}}/createdb", + "{{baseUrl}}/", + "{{baseUrl}}/users/v1/John.Doe", + "{{baseUrl}}/users/v1", + "{{baseUrl}}/users/v1/_debug", + "{{baseUrl}}/books/v1", + "{{baseUrl}}/books/v1/bookTitle77", + }, + "POST": { + "{{baseUrl}}/users/v1/register", + "{{baseUrl}}/users/v1/login", + "{{baseUrl}}/books/v1", + }, + "PUT": { + "{{baseUrl}}/users/v1/name1/email", + "{{baseUrl}}/users/v1/name1/password", + }, + "DELETE": { + "{{baseUrl}}/users/v1/name1", + }, +} + +func TestOpenAPIParser(t *testing.T) { + format := New() + + proxifyInputFile := "../testdata/openapi.yaml" + + gotMethodsToURLs := make(map[string][]string) + + err := format.Parse(proxifyInputFile, func(rr *types.RequestResponse) bool { + gotMethodsToURLs[rr.Request.Method] = append(gotMethodsToURLs[rr.Request.Method], + strings.Replace(rr.URL.String(), baseURL, "{{baseUrl}}", 1)) + return false + }) + if err != nil { + t.Fatal(err) + } + + if len(gotMethodsToURLs) != len(methodToURLs) { + t.Fatalf("invalid number of methods: %d", len(gotMethodsToURLs)) + } + + for method, urls := range gotMethodsToURLs { + if len(urls) != len(methodToURLs[method]) { + t.Fatalf("invalid number of urls for method %s: %d", method, len(urls)) + } + require.ElementsMatch(t, urls, methodToURLs[method], "invalid urls for method %s", method) + } +} diff --git a/pkg/input/formats/swagger/swagger.go b/pkg/input/formats/swagger/swagger.go new file mode 100644 index 00000000..2828bb29 --- /dev/null +++ b/pkg/input/formats/swagger/swagger.go @@ -0,0 +1,69 @@ +package swagger + +import ( + "encoding/json" + "os" + "path" + + "github.com/getkin/kin-openapi/openapi2" + "github.com/getkin/kin-openapi/openapi3" + "github.com/pkg/errors" + "github.com/projectdiscovery/nuclei/v3/pkg/input/formats" + "github.com/projectdiscovery/nuclei/v3/pkg/input/formats/openapi" + "gopkg.in/yaml.v2" + + "github.com/getkin/kin-openapi/openapi2conv" +) + +// SwaggerFormat is a Swagger Schema File parser +type SwaggerFormat struct { + opts formats.InputFormatOptions +} + +// New creates a new Swagger format parser +func New() *SwaggerFormat { + return &SwaggerFormat{} +} + +var _ formats.Format = &SwaggerFormat{} + +// Name returns the name of the format +func (j *SwaggerFormat) Name() string { + return "swagger" +} + +func (j *SwaggerFormat) SetOptions(options formats.InputFormatOptions) { + j.opts = options +} + +// Parse parses the input and calls the provided callback +// function for each RawRequest it discovers. +func (j *SwaggerFormat) Parse(input string, resultsCb formats.ParseReqRespCallback) error { + file, err := os.Open(input) + if err != nil { + return errors.Wrap(err, "could not open data file") + } + defer file.Close() + + schemav2 := &openapi2.T{} + ext := path.Ext(input) + + if ext == ".yaml" || ext == ".yml" { + err = yaml.NewDecoder(file).Decode(schemav2) + } else { + err = json.NewDecoder(file).Decode(schemav2) + } + if err != nil { + return errors.Wrap(err, "could not decode openapi 2.0 schema") + } + schema, err := openapi2conv.ToV3(schemav2) + if err != nil { + return errors.Wrap(err, "could not convert openapi 2.0 schema to 3.0") + } + loader := openapi3.NewLoader() + err = loader.ResolveRefsIn(schema, nil) + if err != nil { + return errors.Wrap(err, "could not resolve openapi schema references") + } + return openapi.GenerateRequestsFromSchema(schema, j.opts, resultsCb) +} diff --git a/pkg/input/formats/swagger/swagger_test.go b/pkg/input/formats/swagger/swagger_test.go new file mode 100644 index 00000000..601c20d9 --- /dev/null +++ b/pkg/input/formats/swagger/swagger_test.go @@ -0,0 +1,34 @@ +package swagger + +import ( + "testing" + + "github.com/projectdiscovery/nuclei/v3/pkg/input/types" + "github.com/stretchr/testify/require" +) + +func TestSwaggerAPIParser(t *testing.T) { + format := New() + + proxifyInputFile := "../testdata/swagger.yaml" + + var gotMethodsToURLs []string + + err := format.Parse(proxifyInputFile, func(request *types.RequestResponse) bool { + gotMethodsToURLs = append(gotMethodsToURLs, request.URL.String()) + return false + }) + if err != nil { + t.Fatal(err) + } + + if len(gotMethodsToURLs) != 2 { + t.Fatalf("invalid number of methods: %d", len(gotMethodsToURLs)) + } + + expectedURLs := []string{ + "https://localhost/users", + "https://localhost/users/1?test=asc", + } + require.ElementsMatch(t, gotMethodsToURLs, expectedURLs, "could not get swagger urls") +} diff --git a/pkg/input/formats/testdata/burp.xml b/pkg/input/formats/testdata/burp.xml new file mode 100644 index 00000000..84796341 --- /dev/null +++ b/pkg/input/formats/testdata/burp.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + +]> + + + + + localhost + 8087 + http + + + null + + 200 + 152 + JSON + + + + + + + google.com + 80 + http + + + null + + 301 + 792 + HTML + + + + \ No newline at end of file diff --git a/pkg/input/formats/testdata/ginandjuice.proxify.json b/pkg/input/formats/testdata/ginandjuice.proxify.json new file mode 100644 index 00000000..64282b66 --- /dev/null +++ b/pkg/input/formats/testdata/ginandjuice.proxify.json @@ -0,0 +1,26 @@ +{"timestamp":"2023-09-07T21:03:38+05:30","url":"https://ginandjuice.shop/","request":{"header":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Cache-Control":"max-age=0","Connection":"close","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=K3apVBuA9VPgjvRhmuEIK81dRquSyRPC8gXbOq4toRUpky4GpcmmPzP2j1c7KVfskcjCGih6K1kxXVYeKlClX5Rx60P+G6gHWE6hNhos6T/CuvhaP5uLb0BZgaZ7; AWSALBCORS=K3apVBuA9VPgjvRhmuEIK81dRquSyRPC8gXbOq4toRUpky4GpcmmPzP2j1c7KVfskcjCGih6K1kxXVYeKlClX5Rx60P+G6gHWE6hNhos6T/CuvhaP5uLb0BZgaZ7","Sec-Ch-Ua":"\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"macOS\"","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"none","Sec-Fetch-User":"?1","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"GET","path":"/","scheme":"https"},"raw":"GET / HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nCache-Control: max-age=0\r\nConnection: close\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=K3apVBuA9VPgjvRhmuEIK81dRquSyRPC8gXbOq4toRUpky4GpcmmPzP2j1c7KVfskcjCGih6K1kxXVYeKlClX5Rx60P+G6gHWE6hNhos6T/CuvhaP5uLb0BZgaZ7; AWSALBCORS=K3apVBuA9VPgjvRhmuEIK81dRquSyRPC8gXbOq4toRUpky4GpcmmPzP2j1c7KVfskcjCGih6K1kxXVYeKlClX5Rx60P+G6gHWE6hNhos6T/CuvhaP5uLb0BZgaZ7\r\nSec-Ch-Ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"\r\nSec-Ch-Ua-Mobile: ?0\r\nSec-Ch-Ua-Platform: \"macOS\"\r\nSec-Fetch-Dest: document\r\nSec-Fetch-Mode: navigate\r\nSec-Fetch-Site: none\r\nSec-Fetch-User: ?1\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Content-Encoding":"gzip","Content-Length":"2127","Content-Type":"text/html; charset=utf-8","Date":"Thu, 07 Sep 2023 15:33:38 GMT","Set-Cookie":"AWSALB=Pg2tRxpbJS4v1ZtiZEW7wdRuiV8VAiZ3b3l3tQPQqlbmlzdtxR43DjPK5Jy+fdqkGwxM1KZJ4rYEZno7EU9fm5zKFOYXkqdmZLntqAPODwer/3D3AJPY82NCJZfi; Expires=Thu, 14 Sep 2023 15:33:38 GMT; Path=/, AWSALBCORS=Pg2tRxpbJS4v1ZtiZEW7wdRuiV8VAiZ3b3l3tQPQqlbmlzdtxR43DjPK5Jy+fdqkGwxM1KZJ4rYEZno7EU9fm5zKFOYXkqdmZLntqAPODwer/3D3AJPY82NCJZfi; Expires=Thu, 14 Sep 2023 15:33:38 GMT; Path=/; SameSite=None; Secure","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"body":"\u001f\ufffd\u0008\u0000\u0000\u0000\u0000\u0000\u0000\ufffd\ufffd\u001a\ufffdr\ufffd8\ufffd\ufffd=\ufffd\ufffdwGa\u0006\ufffdMR\ufffdtHrs\ufffd@\ufffd\n-\u0003\u0003s\ufffdad[\ufffdUd\ufffdXr\ufffd\u001f\ufffd^\ufffd\ufffd\ufffdV\ufffd\ufffd:\ufffd\u001d;\ufffd\ufffdp3t:m$\ufffd\ufffdV\ufffd\ufffdRF?\ufffd]\ufffd\ufffd\ufffd\ufffd\ufffd\t\ufffdU\ufffd\u0026?\ufffd\ufffd?\u0004?\ufffd\ufffd\ufffd\ufffd~4CF\ufffd'\u0014gd:\ufffd2\"E\ufffd\u0005Dz\u000c\ufffd\u001a\ufffdd^ \ufffd'\u0003\ufffd_\ufffds3у\t\ufffd\u00116\ufffd\ufffd`DƄ\ufffd6b\ufffd\u0004\u0010\ufffdo\ufffdLB\ufffd\u0013H\ufffdˆㄌ\ufffd\u0019%\ufffdTd\ufffdA\ufffd\ufffd\ufffdp5v\ufffd4T\ufffd8$3\u001a\u0010\ufffd\u000c\ufffd\ufffd\\\ufffd\ufffd\u0005\u000ea\u0007F\ufffd\\8\u0015j2\ufffdh\ufffd\ufffd̂\ufffdSa\ufffdZ\ufffd\u0000\u0007\ufffd\u0007\ufffd\u0008\u0013i\u0002\ufffd{\ufffdҙ\ufffd\u003c\ufffdѝ\ufffd\u001b\ufffdd\u00072\ufffdH\ufffdd\ufffd|Q\ufffd5\ufffda;\ufffd\ufffdQ\ufffd\u003c\ufffd\u0019\ufffd\u003e\ufffd\ufffdc\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdɹH\u0008r\ufffd3\ufffd\ufffd\u001d\ufffd\ufffd\ufffdЋ\u001c\ufffd\ufffd\ufffd\ufffd\"\u001dy\u0016\ufffdj޻Q\ufffd\ufffd\u0017a\ufffdx\ufffd\ufffd4\ufffd\u0010\u000c\ufffd\u000c\ufffdp\ufffdT5^\u0011\ufffd=\n\t\u0014\u0015\u001c\u0005\u000cK9v\ufffd\ufffd\ufffd!\ufffd\ufffd\ufffd\ufffd\u001a\ufffdAJ7\ufffd\ufffd\ufffdۘJ\u0004\ufffd\u0018\ufffd\ufffdQ\ufffddX\u0011V\ufffdY\ufffd8|\u0006\ufffd\ufffd9\ufffd\u0011\ufffd\ufffdh\ufffdͮ\ufffd\u000f\ufffd8\t\ufffdTdH\u0011\ufffd(\ufffd4\ufffd\u001d\ufffd\ufffd\ufffd\ufffd\u0002\ufffd2\ufffd\n;\ufffd\ufffd\u0002s\ufffdW\ufffd\ufffdH\ufffdxy\u0014m\ufffd\u000e\u0012\u003c\u0011`T\ufffdL\ufffdi\ufffd㡘\ufffd\u003e~\ufffd\ufffdGc\ufffd_.\ufffd\ufffd\ufffd\ufffd`e5\ufffd\ufffd\ufffd\ufffdЮ.\ufffda\ufffdW\ufffd\ufffdV\ufffd\ufffdt\ufffd\ufffd)\ufffd[\ufffd\ufffd\ufffd\u001dT\ufffdke8\\\u001d\u001e\ufffdCL\ufffd\ufffd\u001a#\ufffd\ufffd\ufffd\ufffdb\ufffd\ufffd\ufffd?{\ufffd\ufffd\ufffd)\ufffd¦\ufffd3Ƃ\ufffdx|po\ufffd+#X\ufffd\u000cz \ufffdT\ufffd\ufffd\u001f\ufffd\ufffd\ufffdA\ufffdܰf\ufffdș\\\ufffd\n\u0015`֨\ufffd\u0003R\u0002\ufffd\ufffd\u0018\ufffd\ufffd\u000c\u0026\r\ufffd\ufffd1\u0017o\ufffd^\u0000\ufffdZ\ufffdV[\u003c\ufffdf\ufffd\ufffdltPCV\ufffd\ufffd\u0005\ufffd!\u0005S\u0000\ufffd\ufffd\ufffd\u0011\ufffdQ\ufffd\ufffd\ufffd!SCN\ufffd\ufffd\ufffd2\u0011\t\ufffdS\ufffdo \ufffdᆅ\u001bR\ufffda\ufffd\u0016\ufffd\u000c\u000e\ufffd\r\ufffd,!r\ufffd0m\ufffd\ufffd\ufffd\ufffd\t\ufffdtXm\ufffd\u0012\ufffd\u0019MpV\ufffdQ\ufffd\ufffdn\ufffdd\ufffd\u001d\ufffd\u0000\ufffd\ufffd\ufffd~\ufffd\u0014\u0004\u0005+P\ufffd\u0003\ufffd\ufffd '\ufffd\ufffdL\ufffdy\ufffdd\ufffdPV\ufffdy]6\ufffd=w\ufffda\ufffd1\ufffd\ufffd\ufffd\ufffd¾ȕ3\ufffd\ufffd~\ufffdDV|#\ufffdF^\ufffdnmB\ufffd\ufffd\ufffd\ufffd{6\"\u001c\u0004\"\ufffdʥAE*I\ufffd\ufffd\ufffd\ufffd\u001d\ufffd,\ufffd\ufffd \ufffd-\u0016o\ufffd)Xx\ufffd\ufffd\u0001\ufffd\u0002\ufffd\u0007п\ufffd\ufffd\u0004\ufffd\u0005d\ufffdd5\ufffd.\ufffd-\ufffd\ufffd\rp\ufffdco\u001e\ufffd\u001c\ufffd\u0007\ufffd\u0010\u0011\ufffd\ufffd3džn'\ufffd\ufffd/\ufffd/\u000bT~\ufffd\u0013\ufffd\ufffd\ufffdݝ\ufffdW\u0018l\ufffd\ufffd5k-\u0003\ufffd\ufffdW\ufffd\ufffd\ufffd\ufffdwXf\ufffd\ufffd֫Q\ufffd\ufffd\ufffdP\ufffd\ufffdn;\u001f\ufffd\ufffd\ufffd\ufffd\u0012Q\ufffd\u0008\ufffd\ufffdWf3\u001aE\ufffdB\ufffd\ufffdY\u001e\ufffdq\u0000\ufffd\ufffd6*\ufffd\ufffdƼאyk\ufffdk\ufffd\ufffd5(\ufffd\ufffd\ufffdPѝH\u000c\u0005vK\u0015\ufffd@\ufffd^\ufffd\ufffd\ufffd\u0002\ufffd\u0026\ufffd;\ufffd\ufffd\u000c\ufffd\r\ufffdKA\u000bE\ufffde\ufffd[F\ufffd\u0026D\ufffd.7\ufffd\ufffd\ufffdl\n\r[+m\ufffd\ufffd7\n\ufffd\ufffd\ufffd\"g?n\ufffd\ufffd\ufffd\u0000\u0006aƠ\ufffdhI\ufffd\r\u0015\ufffd\u0006\ufffd\ufffd\u001bY\u0010\ufffdʈfb\u001b\ufffd\u00151/\ufffdζv\u0026+\ufffd\ufffd\ufffd\u0014\ufffd:\u0005\ufffd\u0005\ufffdR\ufffd\ufffd\ufffd\u0000\ufffd\ufffd\ufffdV\u0013h\u0012\ufffd(4\u001b\ufffdAe)\ufffd{\ufffd\ufffd\u0010\ufffdX\"h\u003c\u0019\ufffd;\u001a\n\ufffdc(\"\ufffd\ufffd\u0012\ufffd)\ufffd(w\ufffd۶\ufffdҒ\ufffdvG\ufffd\u0004\ufffd\u001fyǺ\u001a\ufffd\ufffdY9-\ufffd\ufffd\u0000\ufffd\u0014\ufffd?ڒ=\ufffd%\u001d:F; 3\ufffdr\ufffdu\u0019\ufffd\u0010'$\ufffd\ufffd\ufffd\u0016eո\u001b[\ufffd\u0026+-m\ufffd\u0002\ufffd\ufffd\ufffdJ\ufffd\ufffdx\u0019N\ufffd\ufffd\ufffd\ufffd\u0005\u001b%\ufffd\ufffd0\ufffd\ufffd\ufffdJV\ufffd(\ufffd?\u000f\ufffd\ufffd\u000et\u000c-\ufffdD\ufffd%\ufffdIW\ufffd\ufffd\ufffd\u0016\u0014\ufffd6\ufffd^\n\ufffdꦞ|$!\ufffdg\ufffd\ufffd\u003c\ufffd\ufffdW\u003c\ufffd\\-\ufffd\ufffd\u0013\ufffd\ufffdNE\ufffd\t\ufffd\ufffd \u0004\u000ew\ufffdy#\ufffdfX_+\u000cw\ufffd\ufffd\ufffd\ufffd\ufffdT\u0005\ufffd0~\u001d\u001e\ufffd\u001e\u001c6\ufffd\ufffd\ufffdT\ufffd\ufffd\ufffd\ufffdԐ\ufffd\ufffdɮĺ\u0015\u0002\u001d,`\ufffd'\u000b\ufffd\ufffdwT\ufffd\r\ufffd\ufffdo\u001d\u001d/\ufffd\ufffdQ\ufffd\ufffd\ufffd*\ufffd\ufffdQ\ufffd\ufffd\ufffd\ufffd\ufffd‡\ufffdR\ufffd\ufffd\ufffd\n\ufffd\ufffdT\ufffdK\ufffdg\u000c\u0017\ufffd\ufffd\ufffd\ufffd\ufffdA\ufffd\ufffd\ufffd\ufffdTsKYeA\ufffd'\ufffdu#\ufffdWU\ufffd\ufffdZv\ufffd\ufffd\u000c\ufffd\ufffd\ufffdK\ufffd\\CsSю\ufffd\ufffd\ufffd@\ufffd\ufffd\ufffdC\ufffd\ufffd^\ufffdd\ufffdKX\ufffd=\ufffd]\ufffd\ufffd\ufffdޅ\ufffd\ufffd\ufffd\ufffdOt\ufffdiV\ufffd3\\\ufffdm\u000fZ9[1\u001e\ufffd!]\ufffd\ufffdF\ufffdm\u001c\u001b\ufffd\ufffda\u0007\ufffd\ufffdj\u0012\u0006\ufffd\ufffd\n\ufffd\ufffd\ufffd@\ufffd;\u0008\ufffd-\u0014\ufffd\ufffd\"I\u0019\ufffdBj/\"=\ufffd\ufffd\"\ufffd\ufffd\ufffd\ufffd\ufffd\u0007\ufffd\ufffda\ufffd\ufffd|\ufffd['l\ufffd+\ufffd\ufffd\n\u0001=\ufffd;Ϡ\ufffd\ufffdon\ufffd*R\ufffd\ufffdԤnRfd\ufffdoD\ufffdܛo\ufffd\u0004\ufffd\u0011\ufffd\u00168\ufffd\u0002^\u0011H\u001f(\ufffdҾ0a\ufffd\\$s_\ufffd\ufffd\u003eA\\̷\ufffd\t\ufffd\ufffd/\u0004\ufffdo\ufffd\ufffd\ufffd\ufffd\ufffdoNC\ufffd\u0002\ufffd$9\ufffdn\ufffd\ufffd\ufffd\u0010\ufffd!\ufffd6\ufffd\ufffd\u0014s\ufffd0\ufffd\ufffdo^\ufffdȗ\ufffd\ufffd\ufffd\ufffd`'2\ufffd\ufffd\ufffd\u0012bX?}\u0018n\u0000\u000eV\ufffdc(\ufffd\ufffd\ufffd(\u0010\ufffda_d\ufffd߀\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdt\u001b\ufffd7ʃ\ufffdW\ufffd\ufffda_A\ufffds-\u000fj\u001e\u0011kem\ufffd\u0000\u0017\ufffd΍\ufffd\ufffd\ufffdA!\ufffd\u00057!*\u0016\ufffd5\ufffdQ\ufffdNac\u0003\ufffd\ufffd\ufffd\ufffd@\ufffd\ufffdP\ufffd\ufffd\n\ufffd\ufffd9\ufffd\u0019\ufffd\ufffd摔$\ufffd\u0017\ufffdK\ufffd\ufffd\u0008\ufffd\u001d\ufffdX\ufffdP7\ufffdO\ufffd\u0014\u000eC8m[\ufffdSKމi\u0018\u0012\ufffd\ufffdó̦\u000eҭ\u001e\u000c\ufffdtv\ufffd\ufffd\ufffdɻ\ufffd\ufffd\ufffd_\u001f\u001e\ufffdǟs\ufffd䃺\u000e/_\ufffd\ufffd\ufffd\ufffd\ufffde\ufffd~e@Y\ufffd\u00035ۂH\u0012\ufffd\u0026o\u0016\ufffd\u0019yv\ufffd6\ufffd\ufffd\ufffd\ufffdQX \ufffdTp\ufffd\ufffd\ufffd\ufffd\u001fK\ufffdf\ufffdn9a\ufffd\ufffd$\ufffd✂\u0007\ufffd\u0006\ufffd\ufffd\ufffdSC\ufffd̰t\u0017\ufffd\ufffd\ufffd{\ufffd\ufffd\ufffdN\ufffd\ufffdMʧ\ufffds\u0019ٟ\u000c\u000e\u0003?\ufffd\"\u001d\t\n\u0015C\ufffd\u0007\u003e\ufffd\ufffd\ufffd_\ufffd{\ufffd\ufffd\ufffd\ufffdU\u0016\ufffd\ufffdn\ufffdB\ufffdg\ufffd\ufffd\ufffd3\ufffd\ufffd\ufffd\u0018\ufffd V\ufffd\u003c\ufffd^\ufffd\u001a\ufffd\ufffd\ufffdU\ufffd.\ufffd.\ufffd\ufffdd\u001a\ufffd6\u0015\u0003\ufffdv\ufffd\ufffd\ufffdȊl*V\u0007D)\t]\ufffd~\ufffd\ufffd5\ufffdU\ufffdP\ufffdvۂ\ufffd\ufffdN;\ufffd\ufffd\ufffd?Ӕ\ufffd;;*\ufffd\ufffd^_\ufffd\ufffd;5}\ufffd\ufffd\ufffdo\u000b\ufffd\ufffdL!\ufffd@4#\u0026\ufffd\ufffd9\u0003!2\ufffd\u0005\ufffd\ufffd\ufffdv\u0007\ufffd:T\u0000\ufffd+\ufffd\ufffd\ufffd\ufffd\ufffd\u0007\ufffd\ufffdh\u0014\ufffdR\ufffd\ufffd\ufffd\ufffdȹ\ufffd\ufffd7\u000fԝ\ufffd\ufffd\tL\ufffd\ufffdG_\ufffd\u000e\ufffd\u0015Pxc)\ufffd\u000b\u0015\ufffd\ufffd\ufffd\ufffd\ufffd-Ǘ'm\ufffd\ufffdo\ufffdsj\u000b\u00128\ufffd|\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdc{\"E\u0000\u001a\ufffdhy\u0004m\u0013ޏW\ufffdo\ufffdݏW\ufffd\ufffd\ufffd\ufffd\ufffdg\ufffd-\ufffdڃ\ufffdڰ\ufffd(\ufffd\ufffdejS\ufffdu\ufffdͪ\u0014\ufffd\ufffd\u0008\ufffd\ufffdO\ufffd\u0012ͷ\u0016\ufffd\u0003*%\u0016\ufffd\ufffd(\u0000\u0000","raw":"HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 2127\r\nContent-Encoding: gzip\r\nContent-Type: text/html; charset=utf-8\r\nDate: Thu, 07 Sep 2023 15:33:38 GMT\r\nSet-Cookie: AWSALB=Pg2tRxpbJS4v1ZtiZEW7wdRuiV8VAiZ3b3l3tQPQqlbmlzdtxR43DjPK5Jy+fdqkGwxM1KZJ4rYEZno7EU9fm5zKFOYXkqdmZLntqAPODwer/3D3AJPY82NCJZfi; Expires=Thu, 14 Sep 2023 15:33:38 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=Pg2tRxpbJS4v1ZtiZEW7wdRuiV8VAiZ3b3l3tQPQqlbmlzdtxR43DjPK5Jy+fdqkGwxM1KZJ4rYEZno7EU9fm5zKFOYXkqdmZLntqAPODwer/3D3AJPY82NCJZfi; Expires=Thu, 14 Sep 2023 15:33:38 GMT; Path=/; SameSite=None; Secure\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n"}} +{"timestamp":"2023-09-07T21:03:43+05:30","url":"https://ginandjuice.shop/catalog/product?productId=1","request":{"header":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Connection":"close","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=Pg2tRxpbJS4v1ZtiZEW7wdRuiV8VAiZ3b3l3tQPQqlbmlzdtxR43DjPK5Jy+fdqkGwxM1KZJ4rYEZno7EU9fm5zKFOYXkqdmZLntqAPODwer/3D3AJPY82NCJZfi; AWSALBCORS=Pg2tRxpbJS4v1ZtiZEW7wdRuiV8VAiZ3b3l3tQPQqlbmlzdtxR43DjPK5Jy+fdqkGwxM1KZJ4rYEZno7EU9fm5zKFOYXkqdmZLntqAPODwer/3D3AJPY82NCJZfi","Referer":"https://ginandjuice.shop/","Sec-Ch-Ua":"\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"macOS\"","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"same-origin","Sec-Fetch-User":"?1","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"GET","path":"/catalog/product","scheme":"https"},"raw":"GET /catalog/product?productId=1 HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nConnection: close\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=Pg2tRxpbJS4v1ZtiZEW7wdRuiV8VAiZ3b3l3tQPQqlbmlzdtxR43DjPK5Jy+fdqkGwxM1KZJ4rYEZno7EU9fm5zKFOYXkqdmZLntqAPODwer/3D3AJPY82NCJZfi; AWSALBCORS=Pg2tRxpbJS4v1ZtiZEW7wdRuiV8VAiZ3b3l3tQPQqlbmlzdtxR43DjPK5Jy+fdqkGwxM1KZJ4rYEZno7EU9fm5zKFOYXkqdmZLntqAPODwer/3D3AJPY82NCJZfi\r\nReferer: https://ginandjuice.shop/\r\nSec-Ch-Ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"\r\nSec-Ch-Ua-Mobile: ?0\r\nSec-Ch-Ua-Platform: \"macOS\"\r\nSec-Fetch-Dest: document\r\nSec-Fetch-Mode: navigate\r\nSec-Fetch-Site: same-origin\r\nSec-Fetch-User: ?1\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Content-Encoding":"gzip","Content-Length":"2461","Content-Type":"text/html; charset=utf-8","Date":"Thu, 07 Sep 2023 15:33:43 GMT","Set-Cookie":"AWSALB=Bw29bzxm1ZFCqwAsdTUat6udxhYzHJE8Aw2bx6njc30OZmjrKCbp0DgE11GbzOh9pBT4AdR8D0cQN23zLa91oF0u5g/d1YCSaToyLqB6++toCM6Ie1Xj8dPErU8g; Expires=Thu, 14 Sep 2023 15:33:43 GMT; Path=/, AWSALBCORS=Bw29bzxm1ZFCqwAsdTUat6udxhYzHJE8Aw2bx6njc30OZmjrKCbp0DgE11GbzOh9pBT4AdR8D0cQN23zLa91oF0u5g/d1YCSaToyLqB6++toCM6Ie1Xj8dPErU8g; Expires=Thu, 14 Sep 2023 15:33:43 GMT; Path=/; SameSite=None; Secure","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"body":"\u001f\ufffd\u0008\u0000\u0000\u0000\u0000\u0000\u0000\ufffd\ufffdZ\ufffdr\ufffd6\u0012\ufffdާ@y\ufffd(\ufffd1ES\ufffd\u001b\ufffd\"\ufffd\ufffd8Nz\ufffd\u0013k\ufffd\\\ufffdޗ\u000cDB$b\ufffd`\u0008P\ufffd2\ufffdB\ufffd\u001a\ufffdd\ufffd\u0000H\ufffd\ufffdH\ufffd\ufffd\ufffd\ufffd~\ufffd\ufffdc\ufffd\ufffd\ufffd\ufffd\u0002\ufffd\ufffdbw\ufffdѷ/o.\ufffd\ufffd1\ufffdB\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd|!\ufffd\ufffd\"\ufffd\u0003\ufffdS?2\ufffdܡ(#\ufffd\ufffd\ufffd\u0011\ufffd\ufffd\ufffd'\ufffdax\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdp\ufffd\ufffd\ufffd7\ufffd']Ї\u0002\ufffd\u00116\u0016rɈ\ufffd\u0008\ufffdm`\n\u0002\u0000\ufffd-\ufffd\ufffdd?@L$F\t\ufffd\ufffdؚS\ufffdHy\u0026-\ufffd\ufffdD\ufffdD\ufffd\ufffd\u0005\rd4\u000eȜ\ufffd\ufffd\ufffd\u000f'(\u0017$\ufffdAB\u0018\ufffd\ufffdq­\n\ufffd\ufffd3\ufffdJ$2lU\u0004\ufffd(\ufffd\u0001\ufffd\ufffd\u000fH\ufffd\ufffd4\u0006\ufffd\ufffdGay#\ufffd\ufffd\ufffd\u000ea\u0007\u003c\u003e\u0000F.S\ufffd\ufffd$\ufffd\ufffd\ufffd\ufffd\ufffdؔZu\ufffd8\ts\ufffd\ufffd\u000f\ufffd\ufffd\ufffd~ڄ*\ufffdděЄ\ufffd4e\u0004]\u0005TR\ufffd\ufffdK\ufffd\ufffdIL\u0019\ufffd\ufffd$\ufffdA\ufffdK\ufffd\ufffd\ufffd\u0026\ufffd\u0011\ufffd\ufffdg\ufffd\ufffd\u001c\ufffd\u000f\ufffdF\u003c\u001d9\u0006\ufffd0\ufffdYSc4\ufffd\ufffd\u0012%\ufffd\r\ufffd\ufffd\u0001\u0003:G4\u0018[UFT\u0016\ufffdL\ufffd\ufffdZ\u0008\ufffda!Ɩ\ufffd\ufffd\u001d\u0010#=\ufffdluН\ufffd\ufffd2\ufffdy\u0017Q\ufffd\ufffd\u000f\ufffd\ufffd0:%\u0019\ufffd\ufffd-\ufffd\u003cg\t\ufffd\ufffd\rG\u000b2Ej\ufffd\ufffd\ufffdzT\u0018\ufffd\ufffd\t\tЌgH\u0012!i\u0012\ufffdF\ufffd\ufffd\ufffdH\ufffd\ufffd\u001d)\ufffdri\ufffd\ufffdT\ufffdL\ufffdk\u0005\u0018\ufffd\u0014\ufffd\ufffd\ufffd\ufffdm!\ufffd\ufffd\u001cH\u0007\ufffdM2E\ufffd$\ufffd\ufffd\ufffd\ufffd\u000f\ufffd.\u001a#wU\ufffd盵\ufffd\ufffd\ufffd\u0018\ufffd\ufffd\ufffdCS[VCiR\ufffd\u003e\ufffd蜧c\ufffd\ufffd\ufffd\ufffd\ufffdЏ\u001e\ufffd\ufffdX\u001b\ufffd\ufffd\ufffddz'\ufffd\ufffdb\ufffd\ufffd\u0008K\u003e}ܻ\u001e\ufffd۝\ufffd\u003eg~\ufffd\ufffd\ufffd\ufffd\ufffdi\ufffd\ufffd11\u001e\ufffd\ufffd\ufffdFe\u0004+\ufffda\u001f\ufffd$\u00158\ufffd\u0019\ufffd)\u001bԔ\rk\ufffd\ufffd,o\ufffdK\ufffd\u0004ڣb\u001f\ufffd\ufffdHFDoݷ@yX\ufffd\u001a\ufffd8[|\ufffd\ufffd\ufffdu{\ufffd\ufffd3\ufffd}\ufffd\u0007\ufffd\ufffd\ufffd*z\ufffd\ufffd\ufffd\ufffd`Ы\ufffd\ufffd\ufffd#l\ufffd\\ϩ\ufffd\ufffd\ufffd\u0013\ufffd\ufffdٌ\ufffd\u001c\ufffd\u0018\ufffd\u001b`\u001d\ufffdP\ufffd\ufffd\ufffd\nk\ufffdȧ\ufffd$\ufffda\ufffdU\ufffd\ufffd\ufffdԆ\ufffd4Ԝ\ufffd\u000b\ufffdUd\u0017(\ufffdh\ufffd\ufffd\ufffdy\ufffdQ\ufffd\u001dHF\ufffd\u001b醸\u001cz\ufffdK\t[\ufffd\u0018\ufffdK\u0012Xfe-\u0007(\ufffda\ufffd\ufffd\"Ƅ\ufffd\ufffd\ufffd\ufffd\ufffdu\ufffdH\ufffd\ufffdb\ufffd\ufffd\ufffdj\ufffd^\ufffd\ufffd?\ufffdXx\ufffdsiy7J\ufffd$ϖ\u000f$\ufffd\ufffd\ufffd\ufffd\ufffd\\\u0002e\ufffdI\ufffd\ufffdل}\ufffd牴\ufffd_Y\ufffdxi\u0017\ufffdp\ufffd\ufffdy\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdc\u0017\ufffd\u001e\ufffd/\ufffdZ\ufffd\ufffdC\ufffd\ufffdm`\ufffd\ufffdBgu\u001a+\ufffd.\ufffd[,O\ufffd\ufffd\u0007M{w\ufffd[t\ufffdN\ufffd\ufffd\ufffd\ufffd\u0026\ufffd%ָ\ufffdX\ufffdu\ufffd~\ufffdD\ufffd\ufffd\ufffd$z;\ufffd\ufffd\ufffd}\u0001a}\ufffdm\ufffd\ufffd0|\ufffd\ufffdQ|UG\ufffdiq\u0014vd\ufffdꪩ\ufffdF?\ufffd\ufffd\ufffd\ufffd\\Q|[\ufffd0d\u0004F\ufffd\u001b\ufffd\u0019\rC\ufffd*\ufffd٬\u0026\ufffd\ufffd\u0000\ufffd\u001dk\ufffdFN\ufffd\u0001\ufffdp\u0004\ufffd\u0014\ufffd8\u0016[\ufffd\ufffd\ufffd\u000c\u001e\ufffd\nY\ufffd\ufffd\ufffd1\ufffdݰSsf\ufffd\ufffd\ufffd1\u001c᫓\ufffd\ufffd-^\u001f\ufffd֪\u001d\ufffd\ufffdv\n[\ufffd`?G\ufffd\ufffd\ufffdV\ufffdK:+\ufffd\ufffd\ufffd\u001c7utLuC\ufffdھO!\ufffd\t\ufffd,\ufffd\ufffd\ufffdl\ufffdR\ufffd\ufffdM\ufffd\u001c\ufffd\ufffd\ufffdǤ}[7{պ\u0000\ufffd\u0000\ufffd\u0001Qs\ufffdf.moZ\ufffd\ufffdn\ufffdht\ufffdV-h\u001cn\ufffd\ufffdZ\ufffdXP?8\ufffdmt\ufffd\u0016\ufffd](\u001e\ufffd\ufffd\ufffd\ufffd\ufffd@\ufffd\u003c\ufffdi\u0012\ufffd\ufffd\ufffd(v4ܻ\ufffdP\ufffd'\ufffd\ufffd\u001e\ufffd\u001aǁS\u000b*\n\ufffd$m\ufffdW߷C'\ufffd\ufffd\ufffd\ufffd\ufffd~\ufffd\ufffd\ufffd\ufffdG9;\ufffd\ufffd\ufffdm\ufffd\n\n\ufffdo\u00263\ufffd\ufffd\u0012\u001dFۘ\ufffd\ufffd\ufffd}\ufffd+\ufffdS¼\ufffd\ufffdN\ufffd\u00189\ufffd\ufffd\ufffdo\ufffd\ufffd\ufffdAA\u0013\ufffd \ufffdLjјBD\ufffd\ufffdm\ufffd`\ufffdK\ufffdR('\u0008\u001c\u00064\ufffd\ufffd\u001d\ufffd.Q\ufffdS\ufffd*\ufffd\ufffd2\ufffdIp\ufffd\ufffd\u001fJ\ufffd\u0002\ufffd\ufffd\ufffd\ufffdC\ufffd\u0005\ufffd\ufffdy\ufffdc4\ufffdr*\ufffdG\ufffd\u00029A\ufffddVY\u0012\n\ufffdj\ufffd\u0011\u0015\ufffdf\ufffdm\u0013\ufffd.xƂ\u003ez\u001fa\ufffd\u0002NDғHb\u0001\ufffd\ufffd\ufffdH\t\u0006W\ufffd\ufffd\ufffd\ufffd\u0010\ufffd\ufffd\u0015I\u0011\u000e\u0002\u0012\ufffd\u0008C}\ufffd\ufffdD\ufffdI\ufffd1\u0004q\ufffdBE3\u001aF\u0000\ufffd0yB\u0010\ufffd\ufffd:o\ufffd\u0006\ufffdxhN\u0004\u000c\u0008rD*\ufffd\ufffd\ufffd\ufffd\ufffdH\u0007s\ufffdr\ufffdB\ufffd\u0003(\ufffdy\u0018\ufffduH\u000c\u000b88?}s\ufffd\ufffd\ufffd\ufffd\u0001\ufffd\u003c\ufffd\ufffd\u0005~]|\ufffd\ufffd\ufffd\ufffdm'd\u003e\ufffd\u0017\u0010_\ufffdw\ufffd\u001f\u0011\ufffd\ufffd\u000b/`^\ufffd\ufffd\u001d\ufffd~\ufffd\ufffd\ufffd+(\ufffd\ufffd\ufffdS\u000b_\ufffd{\nEwtC\u000b\ufffd\u0004\ufffd\u0008\ufffd\u00267\ufffd\ufffd:*ވ\u0026),eF\u003e\ufffd4\u0003\u0002\ufffd\ufffd[Da'\ufffd\ufffd2\ufffd\ufffdb\ufffdB\ufffd9\ufffd,\ufffd\u0012\ufffd+\ufffd\tU\u000b\u001c\u0015f\u0011@9\ufffd5\ufffdZ/\ufffd\ufffd\"\ufffd͓\ufffd\ufffdonj\ufffd\u0018j\u0000P\u0013\ufffdQq4\ufffd\u0010\ufffd\ufffdP\ufffd\u000f\ufffdI\ufffdKjm:\ufffd.B\ufffd=\"\ufffd\ufffd\ufffd[[!\ufffd\ufffd\ufffd\ufffd4\u0017F\ufffd)\ufffd\ufffd\ufffd*\ufffdu5훬\ufffd\ufffd\ufffd\ufffdI\ufffd\ufffdl\ufffd\ufffdS\ufffd\ufffd1\ufffd]\ufffdN\ufffd\ufffdq\u001c4\ufffdr\ufffd\u0000}-\ufffd\u0001\ufffd\u0007\ufffd\ufffd\ufffd \ufffd\ufffd\ufffd\ufffd\ufffd`\ufffd\ufffd\ufffdK諔\ufffd\ufffd\ufffd\ufffdЦTl\ufffd\ufffdG\ufffd\ufffd\ufffdj\ufffd\ufffd+\ufffd.h\ufffd\u003e\u0010.\u0014Ҭ\ufffd\ufffd\ufffdz\ufffd\ufffd_\ufffd]%.LE\ufffd,}\ufffdO9N$\ufffd\ufffd\ufffdzL=NY~\ufffd-)\ufffdh\ufffd\ufffd\u0000\ufffd\ufffd\u001b\u003c\ufffd%\ufffd\ufffdG\ufffd\ufffdY\ufffd\ufffd\ufffd \ufffd\ufffdw~4\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd O-\ufffd\ufffd\ufffd \u0017\ufffdwq4\ufffd\u000f\ufffd\ufffd\ufffd\ufffd \ufffd\ufffd幧\ufffd\ufffd\ufffd\u0001\ufffd\u001e\ufffdZ\u0017h\ufffd\u001e\ufffd[\u0017\ufffd\ufffd\u001e\ufffd\\\u0017\ufffd\ufffd\u001e\ufffd]\u0017\ufffd\ufffd\u001e\ufffd\ufffd#\u000ecs\u0016\u0017f\ufffd8t\ufffd\u0007\ufffd\ufffd\ufffd\ufffd\ufffd~؃\ufffd\ufffd#I\ufffd\u000c\ufffd\u0007\ufffdKM\ufffd\ufffd\ufffdd\ufffdo\ufffd\\̰%A\ufffd\u0026\ufffd\ufffd[\ufffd\ufffd˪\ufffd^MW[\ufffd\ufffdЌs\u0008@\ufffdE\u0006\u0001G}\u001ei+\ufffda:4\ufffd\ufffdv\ufffd\u0019\ufffdi\ufffd\ufffdyJ\ufffd}|}A\ufffd/\u001b\u0011\r\ufffd\ufffdd\u000e\ufffdSL\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u001a\ufffd\ufffd\u001c\ufffd)QA\ufffdȁV\ufffd\u0010\ufffd\ufffd3\ufffd\ufffdh\u0015\ufffd-h@\ufffdJ\ufffd\ufffd\t\u001c\ufffd\ufffd\ufffdtĤ\u0002B\u001d\ufffd\ufffd\u0019N P\ufffdʼn\u000e\ufffdȽ\ufffdrA\ufffd0\u0012Y\u0008\ufffd\ufffd4\u0010\ufffd\ufffd\ufffdQK\u0003\ufffd\ufffdF\ufffd\ufffd@\ufffd \ufffd\ufffds\u0006\ufffd,\ufffdt:M\ufffdP3\u0006\ufffd\ufffd\ufffd7\ufffd\ufffd\ufffdp\ufffd-\u001f\ufffd\ufffd\ufffd[\ufffd\u0000/\ufffd\ufffd\ufffd\ufffd[_—}\ufffd\ufffdҫ\ufffd*\u0000J\ufffdeԔr!\ufffd\ufffd\ufffd@k\rЖ\ufffd\ufffd\ufffd\ufffd ئ̸-\ufffdgʰO\"\ufffd\u0002u\ufffd}\ufffd\ufffd\ufffd\t\ufffdٶ\ufffd\ufffdt\ufffd\ufffd|\ufffd\ufffdV\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdY\ufffd\ufffdՕ{u\ufffdy\"\ufffdӋ\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd~{5\ufffdo_\ufffd\ufffdWX\ufffd-\ufffdP1|\ufffdm\ufffd2햮=\ufffd\ufffd\ufffdZo\ufffd\ufffd\ufffd\ufffd'\ufffd)i\u0013r#\ufffd\ufffd\ufffd5\ufffdw\ufffd\u000c}\ufffd\u0005)m%\ufffd\ufffdgԿ+\ufffd/5\ufffdK-\ufffdc\ufffd\ufffdD\u003eQ\ufffd\ufffdپ\ufffd\ufffdI\ufffd\u0019\ufffd\u001aIG\ufffd78\ufffd\u000e\ufffdt\ufffd\ufffd%X\ufffdLG\u0008:\ufffd\ufffdq\ufffd\u001b=\ufffd\ufffd\ufffdfwE\u0018\u0016{\ufffd.\ufffd\u000b%\ufffd\ufffd\ufffd\ufffdS\ufffd\rR)\ufffd\u003c\tڳ\ufffd\u001b\ufffd\ufffd\u000e\ufffd\ufffd\u0005\ufffdj\u0013VBC\ufffd\ufffd\ufffd@\ufffd\u0019\ufffd\u000bvdcm*\ufffd\u0003PJ\u0002\u001b\ufffdߝ\ufffd\ufffdf\ufffd*T\ufffd\ufffd\u003ec\ufffd3\ufffd\u0001MS\ufffdy\ufffd\ufffd\ufffdJi\td\ufffdWN\ufffdΰ\ufffd\u0017\ufffdR\ufffd\ufffd\ufffd\n\u001bє\ufffd5#ڪ\ufffd2݂g\ufffd\r\ufffd\ufffdTVG\ufffd\ufffd4\ufffd1\ufffd\ufffdb\ufffd\ufffdN=غL%\ufffd\u000ePo\ufffd\ufffd\ufffdX\ufffd\u000b2\ufffd\ufffdH\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u0017\rN\u0007C4\u0001\ufffd[\ufffd\ufffd\ufffde\ufffd\ufffd0\ufffd\ufffdn\ufffd\u001af[̴\ufffd:\ufffd\ufffdϩuH`\ufffd\ufffd\u0003ܒ\ufffd\ufffd\ufffd\u000cZ\ufffdbV\u0019K\ufffdÎ^\ufffd\ufffd|жx\ufffdV\ufffd\ufffdb\ufffd\ufffdZMm\ufffd\ufffd\u0017\ufffd\ufffd\ufffd\ufffd֍\ufffd\ufffdc\ufffdwj\ufffdL;\ufffd\ufffd\ufffd\ufffd]\ufffd\u0014N%\u001e,\ufffdo\ufffdl\ufffd\ufffd\u0017\ufffd\u001fZ\ufffdMaw,\u0000\u0000","raw":"HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 2461\r\nContent-Encoding: gzip\r\nContent-Type: text/html; charset=utf-8\r\nDate: Thu, 07 Sep 2023 15:33:43 GMT\r\nSet-Cookie: AWSALB=Bw29bzxm1ZFCqwAsdTUat6udxhYzHJE8Aw2bx6njc30OZmjrKCbp0DgE11GbzOh9pBT4AdR8D0cQN23zLa91oF0u5g/d1YCSaToyLqB6++toCM6Ie1Xj8dPErU8g; Expires=Thu, 14 Sep 2023 15:33:43 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=Bw29bzxm1ZFCqwAsdTUat6udxhYzHJE8Aw2bx6njc30OZmjrKCbp0DgE11GbzOh9pBT4AdR8D0cQN23zLa91oF0u5g/d1YCSaToyLqB6++toCM6Ie1Xj8dPErU8g; Expires=Thu, 14 Sep 2023 15:33:43 GMT; Path=/; SameSite=None; Secure\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n"}} +{"timestamp":"2023-09-07T21:03:46+05:30","url":"https://ginandjuice.shop/resources/js/stockCheck.js","request":{"header":{"Accept":"*/*","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Connection":"close","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=Bw29bzxm1ZFCqwAsdTUat6udxhYzHJE8Aw2bx6njc30OZmjrKCbp0DgE11GbzOh9pBT4AdR8D0cQN23zLa91oF0u5g/d1YCSaToyLqB6++toCM6Ie1Xj8dPErU8g; AWSALBCORS=Bw29bzxm1ZFCqwAsdTUat6udxhYzHJE8Aw2bx6njc30OZmjrKCbp0DgE11GbzOh9pBT4AdR8D0cQN23zLa91oF0u5g/d1YCSaToyLqB6++toCM6Ie1Xj8dPErU8g","Referer":"https://ginandjuice.shop/catalog/product?productId=1","Sec-Ch-Ua":"\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"macOS\"","Sec-Fetch-Dest":"script","Sec-Fetch-Mode":"no-cors","Sec-Fetch-Site":"same-origin","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"GET","path":"/resources/js/stockCheck.js","scheme":"https"},"raw":"GET /resources/js/stockCheck.js HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: */*\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nConnection: close\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=Bw29bzxm1ZFCqwAsdTUat6udxhYzHJE8Aw2bx6njc30OZmjrKCbp0DgE11GbzOh9pBT4AdR8D0cQN23zLa91oF0u5g/d1YCSaToyLqB6++toCM6Ie1Xj8dPErU8g; AWSALBCORS=Bw29bzxm1ZFCqwAsdTUat6udxhYzHJE8Aw2bx6njc30OZmjrKCbp0DgE11GbzOh9pBT4AdR8D0cQN23zLa91oF0u5g/d1YCSaToyLqB6++toCM6Ie1Xj8dPErU8g\r\nReferer: https://ginandjuice.shop/catalog/product?productId=1\r\nSec-Ch-Ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"\r\nSec-Ch-Ua-Mobile: ?0\r\nSec-Ch-Ua-Platform: \"macOS\"\r\nSec-Fetch-Dest: script\r\nSec-Fetch-Mode: no-cors\r\nSec-Fetch-Site: same-origin\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Cache-Control":"public, max-age=3600","Content-Encoding":"gzip","Content-Length":"420","Content-Type":"application/javascript; charset=utf-8","Date":"Thu, 07 Sep 2023 15:33:46 GMT","Set-Cookie":"AWSALB=30LBKfbh2Lt0gdYEB/KlscXoCMozAKMWv5H8QdDbvd2tyRAQtUSxv6TIpUDkNZjWUApcBBe5s3QgdnwKvYJdI5qhmWtR1ZZu5JLJR+b2ysTEDXFnAafJVsgArarg; Expires=Thu, 14 Sep 2023 15:33:46 GMT; Path=/, AWSALBCORS=30LBKfbh2Lt0gdYEB/KlscXoCMozAKMWv5H8QdDbvd2tyRAQtUSxv6TIpUDkNZjWUApcBBe5s3QgdnwKvYJdI5qhmWtR1ZZu5JLJR+b2ysTEDXFnAafJVsgArarg; Expires=Thu, 14 Sep 2023 15:33:46 GMT; Path=/; SameSite=None; Secure","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"body":"\u001f\ufffd\u0008\u0000\u0000\u0000\u0000\u0000\u0000\ufffd\ufffdR\ufffdn\ufffd0\u000c\ufffd\ufffd+8]*\ufffd\ufffd\ufffdm7\u0017n\ufffd\ufffd\u001d6\ufffd\ufffda\ufffd\u000f(\u00163\u000bu\ufffd\ufffd\ufffd\ufffd\u0019A\ufffd}\ufffd\ufffd\u0006uS\ufffd\u0004\u000cX\ufffd\ufffd\ufffd\u0013\ufffdlh\ufffd\u001a=\ufffd\ufffd\ufffdn:\ufffd߯\ufffd\u000f\ufffdU\ufffd\ufffd\u003c.Zl\u001e\ufffd\ufffd~\ufffd\ufffd\ufffdX{\ufffd\ufffdӷ.\u0012z\ufffd\ufffd$-׎\ufffd\u000cV\ufffd7\ufffd\ufffd\ufffdX\ufffd\ufffd\u00048\u001ai\ufffd\u0017\nM\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdw\ufffdD\ufffd\ufffd\u001a\ufffd\rV\u00153x%i2\ufffd$=nA\ufffd_\u001b2\ufffd\ufffd(.2;\ufffd\ufffd\u001eE\ufffd5\ufffdL\ufffdH3\ufffd\ufffd\ufffd\ufffdI\ufffd\ufffd\ufffd\ufffd\ufffd\u0019l\u000c\ufffd3\ufffd\ufffdu\u0010\u0019|$\ufffd\ufffd\ufffd\u0001jЬ\u0000c\u0001\ufffd%\ufffd?\ufffdk\ufffd\ufffd:\ufffd+\ufffd\ufffd\ufffd\u000e\ufffd\nVHM\ufffd\u000f\ufffdD\ufffd0Av\ufffd\ufffd\ufffd5Gx\ufffd\ufffdb\u001f+\ufffd\ufffd\ufffd\"x\ufffd0\ufffd?\u000c\u001b\u003c\ufffd`\ufffd\ufffd\r۲\u0019Q\u0001aL\ufffd\u000cv\ufffdX\ufffd\ufffd\u0005cu\ufffd\ufffd\ufffdf\ufffd\ufffd4͔Ԣ׽\ufffd\ufffd\ufffd\ufffd\u0011\ufffdH\ufffd\ufffdܿ\ufffdO\ufffd\ufffdѤ\ufffd\\D\ufffd\ufffd\ufffd\u003e\ufffd\ufffdt\ufffdxg\ufffd4\u0015\ufffd'\ufffd\u000f\ufffd\u0019(H\ufffdQT\ufffd\u0011G\u0005j\u0011Rg\ufffd\u0007\u001aW\t\ufffdp\ufffd\ufffd\ufffdv\ufffd\ufffd\ufffdt\ufffd\ufffd׾\ufffd\ufffd_\u0018\ufffd#\ufffd`\ufffdٶ\ufffd\u001f~\ufffd\ufffdks\ufffd\u000b\ufffd\ufffdȃ\ufffd\ufffd\u0004\ufffd\ufffd\ufffd\u00078\ufffd\ufffdb\ufffd\\;\ufffd\ufffd\ufffdm\ufffd\u0000\ufffd\u000e\u001b\ufffd8\u0003\u0000\u0000","raw":"HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 420\r\nCache-Control: public, max-age=3600\r\nContent-Encoding: gzip\r\nContent-Type: application/javascript; charset=utf-8\r\nDate: Thu, 07 Sep 2023 15:33:46 GMT\r\nSet-Cookie: AWSALB=30LBKfbh2Lt0gdYEB/KlscXoCMozAKMWv5H8QdDbvd2tyRAQtUSxv6TIpUDkNZjWUApcBBe5s3QgdnwKvYJdI5qhmWtR1ZZu5JLJR+b2ysTEDXFnAafJVsgArarg; Expires=Thu, 14 Sep 2023 15:33:46 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=30LBKfbh2Lt0gdYEB/KlscXoCMozAKMWv5H8QdDbvd2tyRAQtUSxv6TIpUDkNZjWUApcBBe5s3QgdnwKvYJdI5qhmWtR1ZZu5JLJR+b2ysTEDXFnAafJVsgArarg; Expires=Thu, 14 Sep 2023 15:33:46 GMT; Path=/; SameSite=None; Secure\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n"}} +{"timestamp":"2023-09-07T21:03:46+05:30","url":"https://ginandjuice.shop/resources/js/xmlStockCheckPayload.js","request":{"header":{"Accept":"*/*","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Connection":"close","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=Bw29bzxm1ZFCqwAsdTUat6udxhYzHJE8Aw2bx6njc30OZmjrKCbp0DgE11GbzOh9pBT4AdR8D0cQN23zLa91oF0u5g/d1YCSaToyLqB6++toCM6Ie1Xj8dPErU8g; AWSALBCORS=Bw29bzxm1ZFCqwAsdTUat6udxhYzHJE8Aw2bx6njc30OZmjrKCbp0DgE11GbzOh9pBT4AdR8D0cQN23zLa91oF0u5g/d1YCSaToyLqB6++toCM6Ie1Xj8dPErU8g","Referer":"https://ginandjuice.shop/catalog/product?productId=1","Sec-Ch-Ua":"\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"macOS\"","Sec-Fetch-Dest":"script","Sec-Fetch-Mode":"no-cors","Sec-Fetch-Site":"same-origin","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"GET","path":"/resources/js/xmlStockCheckPayload.js","scheme":"https"},"raw":"GET /resources/js/xmlStockCheckPayload.js HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: */*\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nConnection: close\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=Bw29bzxm1ZFCqwAsdTUat6udxhYzHJE8Aw2bx6njc30OZmjrKCbp0DgE11GbzOh9pBT4AdR8D0cQN23zLa91oF0u5g/d1YCSaToyLqB6++toCM6Ie1Xj8dPErU8g; AWSALBCORS=Bw29bzxm1ZFCqwAsdTUat6udxhYzHJE8Aw2bx6njc30OZmjrKCbp0DgE11GbzOh9pBT4AdR8D0cQN23zLa91oF0u5g/d1YCSaToyLqB6++toCM6Ie1Xj8dPErU8g\r\nReferer: https://ginandjuice.shop/catalog/product?productId=1\r\nSec-Ch-Ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"\r\nSec-Ch-Ua-Mobile: ?0\r\nSec-Ch-Ua-Platform: \"macOS\"\r\nSec-Fetch-Dest: script\r\nSec-Fetch-Mode: no-cors\r\nSec-Fetch-Site: same-origin\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Cache-Control":"public, max-age=3600","Content-Encoding":"gzip","Content-Length":"230","Content-Type":"application/javascript; charset=utf-8","Date":"Thu, 07 Sep 2023 15:33:46 GMT","Set-Cookie":"AWSALB=FIbJDpsPHxD3iVXrbXy2p7UvQ9uAlbRSlw874/GWrsFNOxfbOd0u7+85vW0SZs9YGmUcYmxZmmOgurXrLi0df6YnHYB3ewMFh94Jk5QX+F+cQRPIP+zU0iZTIiE+; Expires=Thu, 14 Sep 2023 15:33:46 GMT; Path=/, AWSALBCORS=FIbJDpsPHxD3iVXrbXy2p7UvQ9uAlbRSlw874/GWrsFNOxfbOd0u7+85vW0SZs9YGmUcYmxZmmOgurXrLi0df6YnHYB3ewMFh94Jk5QX+F+cQRPIP+zU0iZTIiE+; Expires=Thu, 14 Sep 2023 15:33:46 GMT; Path=/; SameSite=None; Secure","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"body":"\u001f\ufffd\u0008\u0000\u0000\u0000\u0000\u0000\u0000\ufffdU\ufffd͎\ufffd0\u0010\ufffd\ufffd}\ufffd\t\u00170dAo\ufffd z\ufffd\ufffd'Г\ufffd0)\ufffdn\u0003\ufffdM)(1\ufffd\ufffd\u001d]\u000e\ufffd\ufffdL~\ufffd\ufffd\ufffd)ݘ[!\ufffd\ufffd\ufffd\ufffdq\ufffd\u00045\ufffdhm\ufffd$zety\ufffd\ufffdi%D;j\ufffd7X\ufffd{\ufffdM֠\ufffd\u0015\u003c\u0004\ufffd\ufffd\ufffdAбu\ufffd\ufffde\"7\u0004q\ufffdl\ufffdu\u0002\ufffd\ufffdi\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdw!\ufffd},\ufffd\ufffd4x#\ufffd\ufffd?\ufffd\u001d\ufffd7k\ufffd\ufffd8עr`Z\ufffd\ufffd\"\ufffd\ufffd\u0014\r\ufffdji^\ufffd;\ufffdC;k\ufffd\ufffdK\u0015\ufffd\t\ufffd\ufffd\u0016\ufffd\ufffd\ufffd\ufffdG\ufffd)\ufffd\ufffd\ufffd\u001c\ufffd\u001d\ufffd\u001fK\ufffd\ufffde\ufffd\u003e\ufffdO\u0011?_\ufffd\ufffd3s\ufffdG\ufffdYR\ufffd\ufffd\u000b\ufffd\ufffd\ufffd\ufffdd\u0001\u0000\u0000","raw":"HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 230\r\nCache-Control: public, max-age=3600\r\nContent-Encoding: gzip\r\nContent-Type: application/javascript; charset=utf-8\r\nDate: Thu, 07 Sep 2023 15:33:46 GMT\r\nSet-Cookie: AWSALB=FIbJDpsPHxD3iVXrbXy2p7UvQ9uAlbRSlw874/GWrsFNOxfbOd0u7+85vW0SZs9YGmUcYmxZmmOgurXrLi0df6YnHYB3ewMFh94Jk5QX+F+cQRPIP+zU0iZTIiE+; Expires=Thu, 14 Sep 2023 15:33:46 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=FIbJDpsPHxD3iVXrbXy2p7UvQ9uAlbRSlw874/GWrsFNOxfbOd0u7+85vW0SZs9YGmUcYmxZmmOgurXrLi0df6YnHYB3ewMFh94Jk5QX+F+cQRPIP+zU0iZTIiE+; Expires=Thu, 14 Sep 2023 15:33:46 GMT; Path=/; SameSite=None; Secure\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n"}} +{"timestamp":"2023-09-07T21:03:46+05:30","url":"https://ginandjuice.shop/resources/js/xmlStockCheckPayload.js","request":{"header":{"Accept":"*/*","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Connection":"close","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=FIbJDpsPHxD3iVXrbXy2p7UvQ9uAlbRSlw874/GWrsFNOxfbOd0u7+85vW0SZs9YGmUcYmxZmmOgurXrLi0df6YnHYB3ewMFh94Jk5QX+F+cQRPIP+zU0iZTIiE+; AWSALBCORS=FIbJDpsPHxD3iVXrbXy2p7UvQ9uAlbRSlw874/GWrsFNOxfbOd0u7+85vW0SZs9YGmUcYmxZmmOgurXrLi0df6YnHYB3ewMFh94Jk5QX+F+cQRPIP+zU0iZTIiE+","Sec-Fetch-Dest":"empty","Sec-Fetch-Mode":"cors","Sec-Fetch-Site":"none","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"GET","path":"/resources/js/xmlStockCheckPayload.js","scheme":"https"},"raw":"GET /resources/js/xmlStockCheckPayload.js HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: */*\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nConnection: close\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=FIbJDpsPHxD3iVXrbXy2p7UvQ9uAlbRSlw874/GWrsFNOxfbOd0u7+85vW0SZs9YGmUcYmxZmmOgurXrLi0df6YnHYB3ewMFh94Jk5QX+F+cQRPIP+zU0iZTIiE+; AWSALBCORS=FIbJDpsPHxD3iVXrbXy2p7UvQ9uAlbRSlw874/GWrsFNOxfbOd0u7+85vW0SZs9YGmUcYmxZmmOgurXrLi0df6YnHYB3ewMFh94Jk5QX+F+cQRPIP+zU0iZTIiE+\r\nSec-Fetch-Dest: empty\r\nSec-Fetch-Mode: cors\r\nSec-Fetch-Site: none\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Cache-Control":"public, max-age=3600","Content-Encoding":"gzip","Content-Length":"230","Content-Type":"application/javascript; charset=utf-8","Date":"Thu, 07 Sep 2023 15:33:47 GMT","Set-Cookie":"AWSALB=Og/rMkIG6TdnxmKZZY/dHf/nbCJoVtem7Xf2gR2asoHlHQ4cRDVDhCHAELX8TKOz9FBJ38LxoNlB7BQHKwTbaHIhExVtj6B34UaUrguamGInLdvBYBiSoYTHu7x3; Expires=Thu, 14 Sep 2023 15:33:47 GMT; Path=/, AWSALBCORS=Og/rMkIG6TdnxmKZZY/dHf/nbCJoVtem7Xf2gR2asoHlHQ4cRDVDhCHAELX8TKOz9FBJ38LxoNlB7BQHKwTbaHIhExVtj6B34UaUrguamGInLdvBYBiSoYTHu7x3; Expires=Thu, 14 Sep 2023 15:33:47 GMT; Path=/; SameSite=None; Secure","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"body":"\u001f\ufffd\u0008\u0000\u0000\u0000\u0000\u0000\u0000\ufffdU\ufffd͎\ufffd0\u0010\ufffd\ufffd}\ufffd\t\u00170dAo\ufffd z\ufffd\ufffd'Г\ufffd0)\ufffdn\u0003\ufffdM)(1\ufffd\ufffd\u001d]\u000e\ufffd\ufffdL~\ufffd\ufffd\ufffd)ݘ[!\ufffd\ufffd\ufffd\ufffdq\ufffd\u00045\ufffdhm\ufffd$zety\ufffd\ufffdi%D;j\ufffd7X\ufffd{\ufffdM֠\ufffd\u0015\u003c\u0004\ufffd\ufffd\ufffdAбu\ufffd\ufffde\"7\u0004q\ufffdl\ufffdu\u0002\ufffd\ufffdi\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdw!\ufffd},\ufffd\ufffd4x#\ufffd\ufffd?\ufffd\u001d\ufffd7k\ufffd\ufffd8עr`Z\ufffd\ufffd\"\ufffd\ufffd\u0014\r\ufffdji^\ufffd;\ufffdC;k\ufffd\ufffdK\u0015\ufffd\t\ufffd\ufffd\u0016\ufffd\ufffd\ufffd\ufffdG\ufffd)\ufffd\ufffd\ufffd\u001c\ufffd\u001d\ufffd\u001fK\ufffd\ufffde\ufffd\u003e\ufffdO\u0011?_\ufffd\ufffd3s\ufffdG\ufffdYR\ufffd\ufffd\u000b\ufffd\ufffd\ufffd\ufffdd\u0001\u0000\u0000","raw":"HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 230\r\nCache-Control: public, max-age=3600\r\nContent-Encoding: gzip\r\nContent-Type: application/javascript; charset=utf-8\r\nDate: Thu, 07 Sep 2023 15:33:47 GMT\r\nSet-Cookie: AWSALB=Og/rMkIG6TdnxmKZZY/dHf/nbCJoVtem7Xf2gR2asoHlHQ4cRDVDhCHAELX8TKOz9FBJ38LxoNlB7BQHKwTbaHIhExVtj6B34UaUrguamGInLdvBYBiSoYTHu7x3; Expires=Thu, 14 Sep 2023 15:33:47 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=Og/rMkIG6TdnxmKZZY/dHf/nbCJoVtem7Xf2gR2asoHlHQ4cRDVDhCHAELX8TKOz9FBJ38LxoNlB7BQHKwTbaHIhExVtj6B34UaUrguamGInLdvBYBiSoYTHu7x3; Expires=Thu, 14 Sep 2023 15:33:47 GMT; Path=/; SameSite=None; Secure\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n"}} +{"timestamp":"2023-09-07T21:03:46+05:30","url":"https://ginandjuice.shop/resources/js/stockCheck.js","request":{"header":{"Accept":"*/*","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Connection":"close","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=30LBKfbh2Lt0gdYEB/KlscXoCMozAKMWv5H8QdDbvd2tyRAQtUSxv6TIpUDkNZjWUApcBBe5s3QgdnwKvYJdI5qhmWtR1ZZu5JLJR+b2ysTEDXFnAafJVsgArarg; AWSALBCORS=30LBKfbh2Lt0gdYEB/KlscXoCMozAKMWv5H8QdDbvd2tyRAQtUSxv6TIpUDkNZjWUApcBBe5s3QgdnwKvYJdI5qhmWtR1ZZu5JLJR+b2ysTEDXFnAafJVsgArarg","Sec-Fetch-Dest":"empty","Sec-Fetch-Mode":"cors","Sec-Fetch-Site":"none","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"GET","path":"/resources/js/stockCheck.js","scheme":"https"},"raw":"GET /resources/js/stockCheck.js HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: */*\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nConnection: close\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=30LBKfbh2Lt0gdYEB/KlscXoCMozAKMWv5H8QdDbvd2tyRAQtUSxv6TIpUDkNZjWUApcBBe5s3QgdnwKvYJdI5qhmWtR1ZZu5JLJR+b2ysTEDXFnAafJVsgArarg; AWSALBCORS=30LBKfbh2Lt0gdYEB/KlscXoCMozAKMWv5H8QdDbvd2tyRAQtUSxv6TIpUDkNZjWUApcBBe5s3QgdnwKvYJdI5qhmWtR1ZZu5JLJR+b2ysTEDXFnAafJVsgArarg\r\nSec-Fetch-Dest: empty\r\nSec-Fetch-Mode: cors\r\nSec-Fetch-Site: none\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Cache-Control":"public, max-age=3600","Content-Encoding":"gzip","Content-Length":"420","Content-Type":"application/javascript; charset=utf-8","Date":"Thu, 07 Sep 2023 15:33:47 GMT","Set-Cookie":"AWSALB=SPTiMN+mfMtStgumCVLwgd1VcPfrsYrAnqZCYHHQ7TRAPgUCdr/rOizYyvCKNzNUwzB293sDNclHBUdj6U5My2LYYFltNmVa4PiV/kqZAshwIro9uPCI5+IPobxn; Expires=Thu, 14 Sep 2023 15:33:47 GMT; Path=/, AWSALBCORS=SPTiMN+mfMtStgumCVLwgd1VcPfrsYrAnqZCYHHQ7TRAPgUCdr/rOizYyvCKNzNUwzB293sDNclHBUdj6U5My2LYYFltNmVa4PiV/kqZAshwIro9uPCI5+IPobxn; Expires=Thu, 14 Sep 2023 15:33:47 GMT; Path=/; SameSite=None; Secure","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"body":"\u001f\ufffd\u0008\u0000\u0000\u0000\u0000\u0000\u0000\ufffd\ufffdR\ufffdn\ufffd0\u000c\ufffd\ufffd+8]*\ufffd\ufffd\ufffdm7\u0017n\ufffd\ufffd\u001d6\ufffd\ufffda\ufffd\u000f(\u00163\u000bu\ufffd\ufffd\ufffd\ufffd\u0019A\ufffd}\ufffd\ufffd\u0006uS\ufffd\u0004\u000cX\ufffd\ufffd\ufffd\u0013\ufffdlh\ufffd\u001a=\ufffd\ufffd\ufffdn:\ufffd߯\ufffd\u000f\ufffdU\ufffd\ufffd\u003c.Zl\u001e\ufffd\ufffd~\ufffd\ufffd\ufffdX{\ufffd\ufffdӷ.\u0012z\ufffd\ufffd$-׎\ufffd\u000cV\ufffd7\ufffd\ufffd\ufffdX\ufffd\ufffd\u00048\u001ai\ufffd\u0017\nM\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdw\ufffdD\ufffd\ufffd\u001a\ufffd\rV\u00153x%i2\ufffd$=nA\ufffd_\u001b2\ufffd\ufffd(.2;\ufffd\ufffd\u001eE\ufffd5\ufffdL\ufffdH3\ufffd\ufffd\ufffd\ufffdI\ufffd\ufffd\ufffd\ufffd\ufffd\u0019l\u000c\ufffd3\ufffd\ufffdu\u0010\u0019|$\ufffd\ufffd\ufffd\u0001jЬ\u0000c\u0001\ufffd%\ufffd?\ufffdk\ufffd\ufffd:\ufffd+\ufffd\ufffd\ufffd\u000e\ufffd\nVHM\ufffd\u000f\ufffdD\ufffd0Av\ufffd\ufffd\ufffd5Gx\ufffd\ufffdb\u001f+\ufffd\ufffd\ufffd\"x\ufffd0\ufffd?\u000c\u001b\u003c\ufffd`\ufffd\ufffd\r۲\u0019Q\u0001aL\ufffd\u000cv\ufffdX\ufffd\ufffd\u0005cu\ufffd\ufffd\ufffdf\ufffd\ufffd4͔Ԣ׽\ufffd\ufffd\ufffd\ufffd\u0011\ufffdH\ufffd\ufffdܿ\ufffdO\ufffd\ufffdѤ\ufffd\\D\ufffd\ufffd\ufffd\u003e\ufffd\ufffdt\ufffdxg\ufffd4\u0015\ufffd'\ufffd\u000f\ufffd\u0019(H\ufffdQT\ufffd\u0011G\u0005j\u0011Rg\ufffd\u0007\u001aW\t\ufffdp\ufffd\ufffd\ufffdv\ufffd\ufffd\ufffdt\ufffd\ufffd׾\ufffd\ufffd_\u0018\ufffd#\ufffd`\ufffdٶ\ufffd\u001f~\ufffd\ufffdks\ufffd\u000b\ufffd\ufffdȃ\ufffd\ufffd\u0004\ufffd\ufffd\ufffd\u00078\ufffd\ufffdb\ufffd\\;\ufffd\ufffd\ufffdm\ufffd\u0000\ufffd\u000e\u001b\ufffd8\u0003\u0000\u0000","raw":"HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 420\r\nCache-Control: public, max-age=3600\r\nContent-Encoding: gzip\r\nContent-Type: application/javascript; charset=utf-8\r\nDate: Thu, 07 Sep 2023 15:33:47 GMT\r\nSet-Cookie: AWSALB=SPTiMN+mfMtStgumCVLwgd1VcPfrsYrAnqZCYHHQ7TRAPgUCdr/rOizYyvCKNzNUwzB293sDNclHBUdj6U5My2LYYFltNmVa4PiV/kqZAshwIro9uPCI5+IPobxn; Expires=Thu, 14 Sep 2023 15:33:47 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=SPTiMN+mfMtStgumCVLwgd1VcPfrsYrAnqZCYHHQ7TRAPgUCdr/rOizYyvCKNzNUwzB293sDNclHBUdj6U5My2LYYFltNmVa4PiV/kqZAshwIro9uPCI5+IPobxn; Expires=Thu, 14 Sep 2023 15:33:47 GMT; Path=/; SameSite=None; Secure\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n"}} +{"timestamp":"2023-09-07T21:03:48+05:30","url":"https://ginandjuice.shop/catalog/product/stock","request":{"header":{"Accept":"*/*","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Connection":"close","Content-Length":"107","Content-Type":"application/xml","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=SPTiMN+mfMtStgumCVLwgd1VcPfrsYrAnqZCYHHQ7TRAPgUCdr/rOizYyvCKNzNUwzB293sDNclHBUdj6U5My2LYYFltNmVa4PiV/kqZAshwIro9uPCI5+IPobxn; AWSALBCORS=SPTiMN+mfMtStgumCVLwgd1VcPfrsYrAnqZCYHHQ7TRAPgUCdr/rOizYyvCKNzNUwzB293sDNclHBUdj6U5My2LYYFltNmVa4PiV/kqZAshwIro9uPCI5+IPobxn","Origin":"https://ginandjuice.shop","Referer":"https://ginandjuice.shop/catalog/product?productId=1","Sec-Ch-Ua":"\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"macOS\"","Sec-Fetch-Dest":"empty","Sec-Fetch-Mode":"cors","Sec-Fetch-Site":"same-origin","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"POST","path":"/catalog/product/stock","scheme":"https"},"body":"\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\u003cstockCheck\u003e\u003cproductId\u003e1\u003c/productId\u003e\u003cstoreId\u003e1\u003c/storeId\u003e\u003c/stockCheck\u003e","raw":"POST /catalog/product/stock HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: */*\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nConnection: close\r\nContent-Length: 107\r\nContent-Type: application/xml\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=SPTiMN+mfMtStgumCVLwgd1VcPfrsYrAnqZCYHHQ7TRAPgUCdr/rOizYyvCKNzNUwzB293sDNclHBUdj6U5My2LYYFltNmVa4PiV/kqZAshwIro9uPCI5+IPobxn; AWSALBCORS=SPTiMN+mfMtStgumCVLwgd1VcPfrsYrAnqZCYHHQ7TRAPgUCdr/rOizYyvCKNzNUwzB293sDNclHBUdj6U5My2LYYFltNmVa4PiV/kqZAshwIro9uPCI5+IPobxn\r\nOrigin: https://ginandjuice.shop\r\nReferer: https://ginandjuice.shop/catalog/product?productId=1\r\nSec-Ch-Ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"\r\nSec-Ch-Ua-Mobile: ?0\r\nSec-Ch-Ua-Platform: \"macOS\"\r\nSec-Fetch-Dest: empty\r\nSec-Fetch-Mode: cors\r\nSec-Fetch-Site: same-origin\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Content-Encoding":"gzip","Content-Length":"23","Content-Type":"text/plain; charset=utf-8","Date":"Thu, 07 Sep 2023 15:33:49 GMT","Set-Cookie":"AWSALB=II4xh/SWIyNVdqMNb9xxg7whOOfkjlkAp/GSV7a/7p/GoE6na6h0XovVJJpRLDJ4YojSVX+O84vR+vPs4ggyEa6K4A6+mHsWdAKVQpgy7EKXsvJsKwR1+SmmUThb; Expires=Thu, 14 Sep 2023 15:33:49 GMT; Path=/, AWSALBCORS=II4xh/SWIyNVdqMNb9xxg7whOOfkjlkAp/GSV7a/7p/GoE6na6h0XovVJJpRLDJ4YojSVX+O84vR+vPs4ggyEa6K4A6+mHsWdAKVQpgy7EKXsvJsKwR1+SmmUThb; Expires=Thu, 14 Sep 2023 15:33:49 GMT; Path=/; SameSite=None; Secure","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"body":"\u001f\ufffd\u0008\u0000\u0000\u0000\u0000\u0000\u0000\ufffd32\ufffd\u0000\u0000\u0003\u0004\ufffd\u001d\u0003\u0000\u0000\u0000","raw":"HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 23\r\nContent-Encoding: gzip\r\nContent-Type: text/plain; charset=utf-8\r\nDate: Thu, 07 Sep 2023 15:33:49 GMT\r\nSet-Cookie: AWSALB=II4xh/SWIyNVdqMNb9xxg7whOOfkjlkAp/GSV7a/7p/GoE6na6h0XovVJJpRLDJ4YojSVX+O84vR+vPs4ggyEa6K4A6+mHsWdAKVQpgy7EKXsvJsKwR1+SmmUThb; Expires=Thu, 14 Sep 2023 15:33:49 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=II4xh/SWIyNVdqMNb9xxg7whOOfkjlkAp/GSV7a/7p/GoE6na6h0XovVJJpRLDJ4YojSVX+O84vR+vPs4ggyEa6K4A6+mHsWdAKVQpgy7EKXsvJsKwR1+SmmUThb; Expires=Thu, 14 Sep 2023 15:33:49 GMT; Path=/; SameSite=None; Secure\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n"}} +{"timestamp":"2023-09-07T21:03:50+05:30","url":"https://ginandjuice.shop/catalog/cart","request":{"header":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Cache-Control":"max-age=0","Connection":"close","Content-Length":"36","Content-Type":"application/x-www-form-urlencoded","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=II4xh/SWIyNVdqMNb9xxg7whOOfkjlkAp/GSV7a/7p/GoE6na6h0XovVJJpRLDJ4YojSVX+O84vR+vPs4ggyEa6K4A6+mHsWdAKVQpgy7EKXsvJsKwR1+SmmUThb; AWSALBCORS=II4xh/SWIyNVdqMNb9xxg7whOOfkjlkAp/GSV7a/7p/GoE6na6h0XovVJJpRLDJ4YojSVX+O84vR+vPs4ggyEa6K4A6+mHsWdAKVQpgy7EKXsvJsKwR1+SmmUThb","Origin":"https://ginandjuice.shop","Referer":"https://ginandjuice.shop/catalog/product?productId=1","Sec-Ch-Ua":"\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"macOS\"","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"same-origin","Sec-Fetch-User":"?1","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"POST","path":"/catalog/cart","scheme":"https"},"body":"productId=1\u0026redir=PRODUCT\u0026quantity=1","raw":"POST /catalog/cart HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nCache-Control: max-age=0\r\nConnection: close\r\nContent-Length: 36\r\nContent-Type: application/x-www-form-urlencoded\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=II4xh/SWIyNVdqMNb9xxg7whOOfkjlkAp/GSV7a/7p/GoE6na6h0XovVJJpRLDJ4YojSVX+O84vR+vPs4ggyEa6K4A6+mHsWdAKVQpgy7EKXsvJsKwR1+SmmUThb; AWSALBCORS=II4xh/SWIyNVdqMNb9xxg7whOOfkjlkAp/GSV7a/7p/GoE6na6h0XovVJJpRLDJ4YojSVX+O84vR+vPs4ggyEa6K4A6+mHsWdAKVQpgy7EKXsvJsKwR1+SmmUThb\r\nOrigin: https://ginandjuice.shop\r\nReferer: https://ginandjuice.shop/catalog/product?productId=1\r\nSec-Ch-Ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"\r\nSec-Ch-Ua-Mobile: ?0\r\nSec-Ch-Ua-Platform: \"macOS\"\r\nSec-Fetch-Dest: document\r\nSec-Fetch-Mode: navigate\r\nSec-Fetch-Site: same-origin\r\nSec-Fetch-User: ?1\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Content-Encoding":"gzip","Content-Length":"0","Date":"Thu, 07 Sep 2023 15:33:50 GMT","Location":"/catalog/product?productId=1","Set-Cookie":"AWSALB=HED+UhOe9ddVq10zx/UgyutiRdCjvmuRvh+sa/qbQGY3nnK226bQ0Jf/chpK1rFMWxQnmPtGS5sFVIOS6VzeIswsOCe/5rsJYczuq2C6wKHJXXi2MyIqRMNqclZg; Expires=Thu, 14 Sep 2023 15:33:50 GMT; Path=/, AWSALBCORS=HED+UhOe9ddVq10zx/UgyutiRdCjvmuRvh+sa/qbQGY3nnK226bQ0Jf/chpK1rFMWxQnmPtGS5sFVIOS6VzeIswsOCe/5rsJYczuq2C6wKHJXXi2MyIqRMNqclZg; Expires=Thu, 14 Sep 2023 15:33:50 GMT; Path=/; SameSite=None; Secure","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"raw":"HTTP/1.1 302 Found\r\nConnection: close\r\nContent-Length: 0\r\nContent-Encoding: gzip\r\nDate: Thu, 07 Sep 2023 15:33:50 GMT\r\nLocation: /catalog/product?productId=1\r\nSet-Cookie: AWSALB=HED+UhOe9ddVq10zx/UgyutiRdCjvmuRvh+sa/qbQGY3nnK226bQ0Jf/chpK1rFMWxQnmPtGS5sFVIOS6VzeIswsOCe/5rsJYczuq2C6wKHJXXi2MyIqRMNqclZg; Expires=Thu, 14 Sep 2023 15:33:50 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=HED+UhOe9ddVq10zx/UgyutiRdCjvmuRvh+sa/qbQGY3nnK226bQ0Jf/chpK1rFMWxQnmPtGS5sFVIOS6VzeIswsOCe/5rsJYczuq2C6wKHJXXi2MyIqRMNqclZg; Expires=Thu, 14 Sep 2023 15:33:50 GMT; Path=/; SameSite=None; Secure\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n"}} +{"timestamp":"2023-09-07T21:03:51+05:30","url":"https://ginandjuice.shop/catalog/product?productId=1","request":{"header":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Cache-Control":"max-age=0","Connection":"close","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=HED+UhOe9ddVq10zx/UgyutiRdCjvmuRvh+sa/qbQGY3nnK226bQ0Jf/chpK1rFMWxQnmPtGS5sFVIOS6VzeIswsOCe/5rsJYczuq2C6wKHJXXi2MyIqRMNqclZg; AWSALBCORS=HED+UhOe9ddVq10zx/UgyutiRdCjvmuRvh+sa/qbQGY3nnK226bQ0Jf/chpK1rFMWxQnmPtGS5sFVIOS6VzeIswsOCe/5rsJYczuq2C6wKHJXXi2MyIqRMNqclZg","Referer":"https://ginandjuice.shop/catalog/product?productId=1","Sec-Ch-Ua":"\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"macOS\"","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"same-origin","Sec-Fetch-User":"?1","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"GET","path":"/catalog/product","scheme":"https"},"raw":"GET /catalog/product?productId=1 HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nCache-Control: max-age=0\r\nConnection: close\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=HED+UhOe9ddVq10zx/UgyutiRdCjvmuRvh+sa/qbQGY3nnK226bQ0Jf/chpK1rFMWxQnmPtGS5sFVIOS6VzeIswsOCe/5rsJYczuq2C6wKHJXXi2MyIqRMNqclZg; AWSALBCORS=HED+UhOe9ddVq10zx/UgyutiRdCjvmuRvh+sa/qbQGY3nnK226bQ0Jf/chpK1rFMWxQnmPtGS5sFVIOS6VzeIswsOCe/5rsJYczuq2C6wKHJXXi2MyIqRMNqclZg\r\nReferer: https://ginandjuice.shop/catalog/product?productId=1\r\nSec-Ch-Ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"\r\nSec-Ch-Ua-Mobile: ?0\r\nSec-Ch-Ua-Platform: \"macOS\"\r\nSec-Fetch-Dest: document\r\nSec-Fetch-Mode: navigate\r\nSec-Fetch-Site: same-origin\r\nSec-Fetch-User: ?1\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Content-Encoding":"gzip","Content-Length":"2459","Content-Type":"text/html; charset=utf-8","Date":"Thu, 07 Sep 2023 15:33:51 GMT","Set-Cookie":"AWSALB=pP/J8f16lsfhCBOQLxBx2gL95pTIbJWbhm+SWYXVZJslAfv/IqAnVRqRQjThWb4kOcgB3DvGx/sZN+IBBSlkzBdNXp6NZQNUwkZFyEyYez/+H3CGY/gr8tlR6yEB; Expires=Thu, 14 Sep 2023 15:33:51 GMT; Path=/, AWSALBCORS=pP/J8f16lsfhCBOQLxBx2gL95pTIbJWbhm+SWYXVZJslAfv/IqAnVRqRQjThWb4kOcgB3DvGx/sZN+IBBSlkzBdNXp6NZQNUwkZFyEyYez/+H3CGY/gr8tlR6yEB; Expires=Thu, 14 Sep 2023 15:33:51 GMT; Path=/; SameSite=None; Secure","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"body":"\u001f\ufffd\u0008\u0000\u0000\u0000\u0000\u0000\u0000\ufffd\ufffdZ\ufffdr\ufffd6\u0012\ufffdާ@y\ufffd(\ufffd1ES\ufffd\ufffd\ufffd\"\ufffd\ufffd\ufffdiz\ufffdc\ufffdu\ufffd\ufffdݗ\u000cDB\u0014b\ufffd`\u0008P\ufffd2\ufffdB\ufffd\u001a\ufffdd\ufffd\u0000H\ufffd\ufffdH\ufffd2\ufffd\ufffd~\ufffd\ufffdc\ufffd\ufffd\ufffd\ufffd\u0002\ufffd\ufffdbw\ufffdѷ\u0017\ufffd\ufffd\ufffd\ufffdu\ufffd\u001a\ufffddļoF\ufffd\u000b\ufffdg4#80?\ufffd#\ufffd\ufffd\u001d\ufffd\ufffdd:vR\"x\ufffd\ufffdD8\u000cOT3\ufffd:\ufffd\u0010\ufffd\ufffdq\ufffd\ufffd\ufffd\ufffd\u000b\ufffdP\ufffdR\ufffd\ufffdB.\u0019\u00113Bd\u0013\ufffd\ufffd\u0000@q\u000b0\u0011\ufffd\u000f\u0010\u0011\ufffdQ\ufffd#2\ufffd\ufffd\ufffd,\u0012\ufffdJ\u000b\ufffd\u003c\ufffd$\ufffdckA\u00039\u001b\u0007dN}b\ufffd\ufffd#\ufffd\t\ufffd\ufffd !\ufffd\ufffd\ufffd8\ufffdV\tM\ufffd)M$\u0012\ufffd?\ufffdJ\u0002}\u0012\ufffd\ufffd}\ufffd\u0007$\ufffdx\u0012\u0001x\ufffd\ufffd\ufffd\ufffd\ufffdcz\ufffd\ufffd\ufffd\u0003\u001e\u001d\u0000#\ufffd\t\ufffdL\ufffd{\ufffd|\ufffdslJ\ufffd*t\u001c\ufffd\u0019\ufffd\ufffdG\ufffd~n?\ufffdC\ufffdT2\ufffd\ufffdИ\ufffd$a\u0004\ufffd\u000e\ufffd\ufffd\u003cF\ufffdܿ\ufffd\ufffd2d\ufffd\ufffd\ufffd\u0007\ufffd/\ufffd\ufffd\u001b\u001a\ufffd'8J^\ufffd_2X?t;\ufffd\ufffd\ufffd1\u0010\ufffd\u0019Κ\u001a\ufffd\t\u000f\ufffd(\u000em\u0000.\r\u0018\ufffd9\ufffd\ufffd\ufffd*3\ufffd\ufffd\ufffdf\ufffd\ufffd\ufffdB\ufffd\u000c\u000b1\ufffd\u000c\ufffd\ufffd\ufffd\u0018\ufffd\ufffdf\ufffd\ufffd\ufffd\ufffd얩\ufffd\ufffd\u0019\u0015\u0008\ufffd0\n\u0008\ufffd\u0013\ufffdbI\ufffd\u0012\ufffd3\u0016\ufffdo\ufffdp\ufffd \u0013\ufffd\u0026O}\ufffdG\ufffdqh\u0018\ufffd\u0000My\ufffd$\u0011\ufffdơj\ufffd$\ufffd\ufffd\ufffdeё2*\ufffd\ufffdHM\u0005\ufffdD\ufffdR\ufffd\ufffdH\ufffdj*\ufffd\ufffd\u0016\ufffdqāt\ufffd\ufffd$U\ufffd\ufffd\u0003\ufffd\ufffd\ufffdx\ufffd\ufffd1rW\ufffd|\ufffdY;ب\ufffd\ufffd\ufffd\\;4\ufffdE5\ufffd\ufffd\ufffd꓍\ufffdY2\ufffd\ufffd\u0014=-\u000f\ufffd\ufffd\t*\ufffd\ufffd\ufffd8\ufffd|\u003cy\ufffd\u0018\ufffdWk\ufffd\ufffd䓧\ufffd\ufffd\ufffd\ufffd\ufffdɛS\ufffdG?Q\ufffd\ufffd\u003e\t\ufffd0\ufffd/\ufffd\ufffd޳ը\ufffd`%3\ufffd\u0003\ufffd\ufffd\u0004\ufffd\ufffdD;e\ufffd\ufffd\ufffdaEى\ufffd\ufffdd\u0012-\ufffd\ufffd(\ufffd\u0007$9\ufffd3\ufffd\ufffd\ufffd[\ufffd\u003c,\u0005]\ufffd-\ufffd@Cú\ufffd\\\ufffd\u0019ھ\ufffd\u0003\ufffd*`\u0015\ufffdMSer0\ufffdUZ\ufffd\ufffd\u00116V\ufffd\ufffdT\ufffdT\ufffd\tm\ufffdl\ufffdC\u000ez\u000c\ufffd5\ufffd\u000e\ufffd\ufffdXCi\ufffd\ufffd\u001b\ufffd\ufffd}b\\3̪E\ufffd\njC[\u001ajNع\ufffd*\ufffd\u000b\ufffd\ufffd4\ufffd\ufffd\ufffd\u003cU(\ufffd\u000e$\ufffd͍tC\\\u000c=ɤ\ufffd-R\u000c\ufffd%\t,\ufffd\ufffd\ufffd\u0003\u0014Ű`@\u0011c\ufffdD\ufffd\ufffdl\ufffd:m$x\ufffd\ufffd+\ufffd\u0026Z\ufffdW\ufffd\ufffdO%\u0016\ufffd\ufffdLZ޵R(\ufffd\ufffd\ufffd#\t7r2֙K\ufffd\ufffd\u003c\u000e\ufffd2\ufffd\ufffd\ufffd\ufffd,\ufffd6\ufffdK\ufffd\u0012-\ufffd\ufffd\u001c\ufffdQ1\u000f\ufffd\u0011\u0018\ufffd\ufffdr}\ufffd\u0002\ufffdC\ufffd\u0005]\ufffd\ufffd}h\ufffd\ufffd\rL\ufffd_\ufffd\ufffdNc\ufffd\ufffdfy\ufffd\ufffdi\ufffd\ufffd\ufffdi\ufffdN\ufffd\u000e\ufffd\ufffd_\ufffd\u0010Ѹ\ufffd\ufffd\u001a\ufffd\u0015k\ufffd\ufffd\ufffd\ufffd\ufffd(\ufffd\ufffd\ufffdDo\ufffdv{\ufffd\u0007\u0010\ufffd\ufffd\ufffd\u0016[s\ufffd\ufffd\ufffd\u001a\ufffdWu\u0004\ufffd\ufffdQؒ\ufffd\ufffd\ufffd\ufffd\ufffd\u001a\ufffdp޶\ufffdrI\ufffdm\ufffdÐ\u0011\u0018\ro\ufffd\ufffd4\u000c\ufffd\ufffdhf\ufffd\ufffdL\ufffd\u0003\ufffd\ufffd\ufffd*\ufffd\ufffd\ufffd\u0003\ufffd\ufffd\u0008\ufffd(\ufffdp,\ufffdZ\ufffd\ufffd\u0019\u003c\u0016\u0015\ufffd\ufffd\ufffd\ufffd\"\u0002\ufffda'\ufffd\ufffdj\ufffd\ufffd#8\ufffdW'y\ufffd[\ufffd\u003e\ufffd\ufffdU;\ufffd\ufffd\ufffd\u0004\ufffd\ufffd\ufffd~\ufffd\ufffd\ufffd]\ufffd\u003e\ufffdt\ufffd;ǹ9\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdڵ}\ufffd@\ufffd\u0013\ufffdi\u0016M\ufffd\ufffdp\ufffd\n\ufffd\ufffdd9\ufffd\ufffd3\ufffdH\ufffd\ufffdn\ufffd\ufffdt\u0001\ufffd\u0001\ufffd\u0003\ufffd\ufffd\ufffd\ufffd\\\ufffd޴\ufffd\ufffd\ufffd\ufffdQ\ufffd\ufffd\ufffdZ\ufffd(\ufffd³\ufffd\ufffd\u0016\ufffd\ufffd~p\ufffd\ufffd\ufffd\ufffd-\n\ufffd\ufffd?\n')\ufffd\ufffd\ufffd\ufffdy\ufffd\ufffd8\ufffd'W\ufffd\ufffd\ufffdR\ufffd\ufffdp\ufffd\ufffdB\ufffd\ufffdp\ufffdz\ufffdr\u001c\u0007N-\ufffd(Ē\ufffd\ufffd_u\ufffd\u0016\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdq\u000b;[\u001d\ufffd\ufffd\ufffdS\ufffd\ufffd\ufffd*ȭ\ufffd\ufffd̰\ufffdJ\ufffd\u0018mc\ufffd\ufffd#\ufffd\ufffd\ufffd\u000cO\u0008\ufffd.֝\ufffd1rLYs\ufffdĻ\ufffd\ufffd\ufffdƘA\ufffd\ufffd\u0011\ufffd\u0011\ufffd\ufffd\u0000\ufffd\ufffd\u000c\ufffd\u0008\ufffd\\\ufffdP\ufffd\u00108\u000ch\ufffd\ufffd;4Y\ufffd\ufffd'*U\u0002\ufffde\ufffd\ufffd\ufffd\u0008\ufffd?\u0014\ufffd\u00054'\u0011\ufffd\ufffd\ufffd\u000b*g\ufffd)\ufffd\ufffd4ͨD\ufffdT\n\ufffd\u0008\ufffd\ufffdYeI(\ufffd\ufffdYJT(\ufffd\u0002\ufffdM|\ufffd\ufffd)\u000b\ufffd\ufffd\ufffd\u000cK\u0014p\"\ufffd\ufffdD\u0012\u000b\ufffdg'DJ0\ufffdz\u0014%\ufffd\ufffd\u0000\r\ufffdH\ufffdp\u0010\ufffd\ufffdG\u0018\ufffd\ufffd\u0017\ufffd#\ufffd@z\ufffd!\ufffd\ufffd\u0015*\ufffd\ufffdp\u00060\n\ufffd\ufffd\u0004\ufffd\ufffd\ufffd\ufffd\u0016j@\ufffd\ufffd\ufffdD\ufffd\ufffd \ufffdL\ufffd\ufffdTT\u001c\u001a\ufffd`.R.Q\ufffdy\u0000\ufffd\u003c\u000bg}\u001d\u0012\ufffd\u0002\u000eN\ufffd\ufffd]\ufffd\ufffd w\ufffdn\ufffd\ufffd\ufffd¯\u0017ߡ\ufffdW\ufffd\ufffd̝y\u0001\ufffd\ufffdg\ufffd3\ufffdߵ\ufffd\u0005\ufffd+2\ufffd#\ufffd\ufffd\\u\ufffd\t\ufffd,\ufffd\ufffd\ufffd—\ufffd\ufffd\\\ufffd\u001d\ufffd\ufffdB\u0011\ufffd)B\ufffd\ufffd\ufffd\ufffd\ufffd-\u0015oD\ufffd\u0004\ufffd2%\ufffd3\ufffd\u0002\u0001L\ufffdmFa'\ufffd\ufffd2\ufffd\ufffd|\ufffdB\ufffd9\ufffd,\ufffd\u0012\ufffd-\ufffd\tUs\u001c\u0015f\u0011@9\ufffd5\ufffdZ/\ufffd\ufffd\"\ufffd\ufffd〃onj\u001e\u000c5\u0000\ufffd\u001b\ufffdR\ufffd\u0019i\u0008H\ufffd(\ufffd\u0007ʤ\ufffd%\ufffd6-[硿\ufffd\u001e\ufffdM@ݭ\ufffd\ufffd\ufffd\ufffdLA\ufffd\u000b#\ufffd\u0014\ufffdq]\u0015\ufffdښ\ufffdMV\ufffdFDƤ\ufffd\ufffd6\ufffd\ufffd)\ufffd\ufffd\ufffdݮpo\ufffd\ufffdq\u001c\ufffd\ufffdr\u001f\ufffd\ufffd\u0016\ufffd\u0000̃\ufffd\\G\u0010\ufffdf\ufffdWs\ufffdz\ufffd\ufffd9\ufffdUJ^\ufffd\ufffdFhS(\ufffd\ufffd\ufffd\u000ejm\ufffd\ufffd(\ufffdJ\ufffds\u001a\ufffd\ufffd\ufffd\u000b\ufffd4\ufffd1o~\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdĹ\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd9ñ\ufffdr\ufffd[\ufffd\ufffd\ufffd)ʻؒ\"\ufffd\ufffdB\ufffd\ufffd\ufffd\ufffd\u001b\u003c\ufffd%\ufffd\ufffd\ufffdAN,\ufffd\ufffd3ȩ\ufffd\ufffdv\u0006\ufffd\ufffd\ufffd\ufffd\ufffd\u000c\ufffd\ufffd\ufffd\ufffdw\u0006yay/:\ufffd\ufffd`y?t\u0006q\ufffd-\ufffd=\ufffd\u000e\u0003\u0007\ufffd۝\ufffd.\ufffd\ufffd\ufffd\ufffd[\u0017\ufffd\ufffdvg\ufffd\u000b\ufffdu\ufffds\ufffd\u0005\ufffd\ufffd\u0007\ufffd\ufffd\ufffdal\ufffd\ufffd\ufffdl\ufffd\ufffd\ufffdY\u0010(\ufffdX\ufffd\ufffd\ufffd=\ufffd\u000f\u003e\ufffd\ufffdͰ}\ufffd\ufffdT\ufffdj\ufffdL\ufffd\ufffd\u0001\ufffd\ufffd\u000c\u001b\u0012dMB\ufffd\ufffd婼\ufffd\ufffd\ufffdUw\ufffdUJ\rM9\ufffd\u0000\ufffd^\ufffd\u0010pT瑶R\u0018\ufffdC]\u001eh\u0017\ufffd\ufffd\ufffdv\ufffd꧴\ufffd\ufffd\ufffd\u0017\ufffd\ufffd\ufffd\u0011\ufffd\ufffdwE\ufffd\u00109ET\ufffd+^\ufffdn\ufffd\ufffd{ʑ\ufffd\u0010\u0015č\u001chU\u000f\ufffdx\ufffdp\u0008\ufffdV\ufffdق\u0006\u0004\ufffd$\\\u0016\ufffd1\ufffd\ufffdTGL* \ufffd\u0001\u001f\ufffd\ufffd\u0018\u0002e_\u001c\ufffd0\ufffd\ufffd\ufffd,\u0013t\u000e#\ufffd\ufffd\ufffd\u0008L\u00031\ufffd\ufffd\u001e\ufffd4\ufffd\u000ej\ufffd\ufffd\n\u0004\u000b\u0002O\u003eg\u0010\ufffd\ufffdT\ufffdӄ\t5#\ufffd\ufffd\ufffd{ë\ufffd\u000f\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd^\ufffd\u0005xq\ufffd\u0016^\ufffd\ufffd\u0012\ufffd\ufffdk\ufffd\ufffd^\u0005T\u0001P\ufffd.\ufffd\ufffd\ufffd\u000b\ufffd\u0017\ufffd\u0004Zk\ufffd\ufffd\ufffdM\ufffd\u0004\ufffd6e\ufffdm1?\u0013\ufffd}2\ufffd,P\u0017߯u\u00118\ufffd0ۦ\ufffd\ufffd6њ/\ufffd\ufffd*P{\ufffdk\ufffd\ufffd%\ufffdE\ufffd\ufffdW\ufffd\ufffd\ufffd\ufffd*\ufffd;\ufffd\ufffdno\ufffdr\u001d\\}Β\ufffd\ufffdrK\ufffde\u0010J\ufffdϻ-V\ufffd\ufffd\ufffd5'\ufffd\ufffdZ\ufffd\r\ufffdy\ufffd\ufffd\ufffd6%MBn\ufffd~u\ufffdz\ufffdn\ufffd\ufffdϸ \ufffd\ufffd\u0004\ufffd\ufffd\ufffd\ufffdwy\ufffd\ufffdƾ\ufffd\"=\u0005m\ufffd\ufffd3\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd4\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u0006\ufffd߁\ufffdN\ufffd\ufffd\u0004K\ufffd\ufffd\u0008A\ufffd\ufffd:\ufffdU\ufffd'پ\ufffd\ufffd\ufffd\u0008\ufffd|o\ufffd\ufffdz\ufffd\ufffd68\u0010w\ufffdo\ufffdA*%\ufffd\ufffdAsvs\u00036߁\u0002:\ufffd]m\ufffdJh\ufffd\ufffd\ufffd\u0018(5\ufffd?`G6֦\ufffd:\u0000\ufffd$\ufffd\ufffd\ufffd\ufffdY[kV\ufffdB\ufffd\ufffd\ufffd3\ufffd;#\u001d\ufffd4\ufffdΒ\ufffd-UJK 3\ufffdr*t\ufffdM\ufffd\ufffd\ufffd\ufffd\ufffduT؈\u0026\u0004\ufffd\u0019\ufffdVE\ufffd\ufffd\u0016\u003cUo\ufffd5\ufffd\ufffdZ\nդ\ufffd\ufffdQ\ufffd\u0006K\ufffdu\ufffd\ufffd֥*\ufffdw\ufffdz\ufffd\ufffd\ufffd\ufffdB_\ufffd\ufffd7DZ-\ufffdN\ufffd\ufffd\ufffd\ufffdhp\u003c\u0018\ufffd\u001b@\ufffd5\u0008\ufffdR\u0006\ufffd\u00163;\ufffdf\ufffdf\ufffd\ufffdLk\ufffd\ufffd\ufffd\ufffd\ufffdJ\ufffd\u0004f\ufffd8\ufffd-\ufffdZ\ufffdg\ufffdp\u0017\ufffd\ufffdXr\u001fv\ufffd\ufffd\ufffd僦\ufffd\ufffd뵚\ufffd\u0017\ufffd\ufffd\ufffdj*k\u000f\ufffd\ufffd\ufffd\ufffd\u0015\ufffdn\ufffd\ufffd\u001ek\ufffdS\ufffdd\ufffdI5/\ufffdp*\ufffd`\ufffd}\u0003g\ufffd~\ufffd\ufffd\ufffdf\u0001L\\w,\u0000\u0000","raw":"HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 2459\r\nContent-Encoding: gzip\r\nContent-Type: text/html; charset=utf-8\r\nDate: Thu, 07 Sep 2023 15:33:51 GMT\r\nSet-Cookie: AWSALB=pP/J8f16lsfhCBOQLxBx2gL95pTIbJWbhm+SWYXVZJslAfv/IqAnVRqRQjThWb4kOcgB3DvGx/sZN+IBBSlkzBdNXp6NZQNUwkZFyEyYez/+H3CGY/gr8tlR6yEB; Expires=Thu, 14 Sep 2023 15:33:51 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=pP/J8f16lsfhCBOQLxBx2gL95pTIbJWbhm+SWYXVZJslAfv/IqAnVRqRQjThWb4kOcgB3DvGx/sZN+IBBSlkzBdNXp6NZQNUwkZFyEyYez/+H3CGY/gr8tlR6yEB; Expires=Thu, 14 Sep 2023 15:33:51 GMT; Path=/; SameSite=None; Secure\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n"}} +{"timestamp":"2023-09-07T21:03:58+05:30","url":"https://ginandjuice.shop/catalog/subscribe","request":{"header":{"Accept":"*/*","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Connection":"close","Content-Length":"69","Content-Type":"application/json;charset=UTF-8","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=pP/J8f16lsfhCBOQLxBx2gL95pTIbJWbhm+SWYXVZJslAfv/IqAnVRqRQjThWb4kOcgB3DvGx/sZN+IBBSlkzBdNXp6NZQNUwkZFyEyYez/+H3CGY/gr8tlR6yEB; AWSALBCORS=pP/J8f16lsfhCBOQLxBx2gL95pTIbJWbhm+SWYXVZJslAfv/IqAnVRqRQjThWb4kOcgB3DvGx/sZN+IBBSlkzBdNXp6NZQNUwkZFyEyYez/+H3CGY/gr8tlR6yEB","Origin":"https://ginandjuice.shop","Referer":"https://ginandjuice.shop/catalog/product?productId=1","Sec-Ch-Ua":"\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"macOS\"","Sec-Fetch-Dest":"empty","Sec-Fetch-Mode":"cors","Sec-Fetch-Site":"same-origin","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"POST","path":"/catalog/subscribe","scheme":"https"},"body":"{\"email\":\"eqeq@gfmail.com\",\"csrf\":\"GQrKlppDdKQale2DNpk4mASSUzOdNqup\"}","raw":"POST /catalog/subscribe HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: */*\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nConnection: close\r\nContent-Length: 69\r\nContent-Type: application/json;charset=UTF-8\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=pP/J8f16lsfhCBOQLxBx2gL95pTIbJWbhm+SWYXVZJslAfv/IqAnVRqRQjThWb4kOcgB3DvGx/sZN+IBBSlkzBdNXp6NZQNUwkZFyEyYez/+H3CGY/gr8tlR6yEB; AWSALBCORS=pP/J8f16lsfhCBOQLxBx2gL95pTIbJWbhm+SWYXVZJslAfv/IqAnVRqRQjThWb4kOcgB3DvGx/sZN+IBBSlkzBdNXp6NZQNUwkZFyEyYez/+H3CGY/gr8tlR6yEB\r\nOrigin: https://ginandjuice.shop\r\nReferer: https://ginandjuice.shop/catalog/product?productId=1\r\nSec-Ch-Ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"\r\nSec-Ch-Ua-Mobile: ?0\r\nSec-Ch-Ua-Platform: \"macOS\"\r\nSec-Fetch-Dest: empty\r\nSec-Fetch-Mode: cors\r\nSec-Fetch-Site: same-origin\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Content-Encoding":"gzip","Content-Length":"68","Content-Type":"application/json; charset=utf-8","Date":"Thu, 07 Sep 2023 15:33:58 GMT","Set-Cookie":"AWSALB=2SKCWqUz3j0AasFKb1dBCnRQrU3mM16R6pX5fClkcKjvp9b8dRFcFCYjXp5pHaVHN6uVcVD0B4tt7MAs81yd5Unvr/oSF0i53t8n7wjb/pw+/XRO6yEaCLu2sCW1; Expires=Thu, 14 Sep 2023 15:33:58 GMT; Path=/, AWSALBCORS=2SKCWqUz3j0AasFKb1dBCnRQrU3mM16R6pX5fClkcKjvp9b8dRFcFCYjXp5pHaVHN6uVcVD0B4tt7MAs81yd5Unvr/oSF0i53t8n7wjb/pw+/XRO6yEaCLu2sCW1; Expires=Thu, 14 Sep 2023 15:33:58 GMT; Path=/; SameSite=None; Secure","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"body":"\u001f\ufffd\u0008\u0000\u0000\u0000\u0000\u0000\u0000\ufffd\ufffdVJ\ufffd/-\ufffd\ufffdS\ufffdR\ufffd\ufffd\ufffdS\ufffd\n\ufffdtv5\ufffd0\u0008P\ufffdQJ\ufffdM\ufffd\ufffd\u0001\ufffd\ufffd\u0016\ufffd\u0016:\ufffd\ufffd\ufffdxz\ufffd\ufffd\ufffdJ\ufffd\u0000-/{\ufffd4\u0000\u0000\u0000","raw":"HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 68\r\nContent-Encoding: gzip\r\nContent-Type: application/json; charset=utf-8\r\nDate: Thu, 07 Sep 2023 15:33:58 GMT\r\nSet-Cookie: AWSALB=2SKCWqUz3j0AasFKb1dBCnRQrU3mM16R6pX5fClkcKjvp9b8dRFcFCYjXp5pHaVHN6uVcVD0B4tt7MAs81yd5Unvr/oSF0i53t8n7wjb/pw+/XRO6yEaCLu2sCW1; Expires=Thu, 14 Sep 2023 15:33:58 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=2SKCWqUz3j0AasFKb1dBCnRQrU3mM16R6pX5fClkcKjvp9b8dRFcFCYjXp5pHaVHN6uVcVD0B4tt7MAs81yd5Unvr/oSF0i53t8n7wjb/pw+/XRO6yEaCLu2sCW1; Expires=Thu, 14 Sep 2023 15:33:58 GMT; Path=/; SameSite=None; Secure\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n"}} +{"timestamp":"2023-09-07T21:04:03+05:30","url":"https://ginandjuice.shop/blog","request":{"header":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Connection":"close","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=2rYKe8I1LwfOtI/423ImCGauGOtHRl2cXGdbbyWcVzzI5FqsI0s8Rij7fJUDNtQn4HPmweWUufw1GsgLXkGVie58e4PGUBD4je7UWtZobziKhxVouiWJeEFiNMkR; AWSALBCORS=2rYKe8I1LwfOtI/423ImCGauGOtHRl2cXGdbbyWcVzzI5FqsI0s8Rij7fJUDNtQn4HPmweWUufw1GsgLXkGVie58e4PGUBD4je7UWtZobziKhxVouiWJeEFiNMkR","Referer":"https://ginandjuice.shop/catalog/product?productId=1","Sec-Ch-Ua":"\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"macOS\"","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"same-origin","Sec-Fetch-User":"?1","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"GET","path":"/blog","scheme":"https"},"raw":"GET /blog HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nConnection: close\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=2rYKe8I1LwfOtI/423ImCGauGOtHRl2cXGdbbyWcVzzI5FqsI0s8Rij7fJUDNtQn4HPmweWUufw1GsgLXkGVie58e4PGUBD4je7UWtZobziKhxVouiWJeEFiNMkR; AWSALBCORS=2rYKe8I1LwfOtI/423ImCGauGOtHRl2cXGdbbyWcVzzI5FqsI0s8Rij7fJUDNtQn4HPmweWUufw1GsgLXkGVie58e4PGUBD4je7UWtZobziKhxVouiWJeEFiNMkR\r\nReferer: https://ginandjuice.shop/catalog/product?productId=1\r\nSec-Ch-Ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"\r\nSec-Ch-Ua-Mobile: ?0\r\nSec-Ch-Ua-Platform: \"macOS\"\r\nSec-Fetch-Dest: document\r\nSec-Fetch-Mode: navigate\r\nSec-Fetch-Site: same-origin\r\nSec-Fetch-User: ?1\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Content-Encoding":"gzip","Content-Length":"2646","Content-Type":"text/html; charset=utf-8","Date":"Thu, 07 Sep 2023 15:34:04 GMT","Set-Cookie":"AWSALB=+kt6Ib2lLrBXa4YCECeKwNyIl2PFt7T1knMc4Ed88f+NSWWr4GdaZPWIqyaJBNi2eoCbLJMcYgimR45FELKCqRmtTLdXLFD6x+MBEnFw43yIfk3L1QGW79pv1ip2; Expires=Thu, 14 Sep 2023 15:34:04 GMT; Path=/, AWSALBCORS=+kt6Ib2lLrBXa4YCECeKwNyIl2PFt7T1knMc4Ed88f+NSWWr4GdaZPWIqyaJBNi2eoCbLJMcYgimR45FELKCqRmtTLdXLFD6x+MBEnFw43yIfk3L1QGW79pv1ip2; Expires=Thu, 14 Sep 2023 15:34:04 GMT; Path=/; SameSite=None; Secure","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"body":"\u001f\ufffd\u0008\u0000\u0000\u0000\u0000\u0000\u0000\ufffd\ufffdZ\ufffdn\ufffd8\u0016\ufffd\ufffd\ufffd`\ufffdئ\u0005b{\ufffd\ufffd\ufffd\u0005j{п\ufffd\ufffdh\ufffd\ufffd\ufffdآ{S\ufffd\u0012m1\ufffdH\ufffdH\ufffd\ufffdݾ\ufffd\u003e¾\ufffd\u003e\ufffd\u003e\ufffd~\ufffd\ufffd\u001d\ufffd_i\u001b\ufffd\u0017S\u0014\ufffdD\u001e\u001e\u001e\ufffd|\u003c\ufffd\ufffdދw\ufffd?|z\ufffd\ufffd\ufffd.S\ufffd\ufffd\u0006\ufffd\ufffd\ufffd\ufffd \u0015\u003c\t\ufffd\ufffdUI}\ufffd\ufffdBL\ufffd\ufffdBXS\u0016\ufffd\ufffd=\ufffd\ufffdD\u0026\ufffd^lm\ufffd\ufffd\\_\ufffd\ufffd}C\u0017\r\ufffd\u0010jh]\ufffd\ufffdM\ufffdp\ufffd\ufffd\u0011\u000b0\ufffdϔ\ufffd~\ufffd\ufffdKH\ufffd\ufffd\ufffd\u000c2\ufffd8\ufffd\u003c\u0013\ufffdh\u0026\ufffd\u003c7\ufffd\ufffdXl\ufffd\u0013\ufffd\r\ufffd\ufffdL\\:L\ufffdLƢ\ufffd_\ufffdYiE\ufffd\ufffd\u00021\ufffd\u0012Cm\ufffd\u00067\u001b\u00172w\ufffd\u0016\ufffd0j\u0008te\ufffd\ufffdc\ufffd\u0005'\ufffdL\ufffd\ufffdy\ufffd\ufffdF\ufffdA/\ufffdhϢ\ufffd\ufffd\ufffd\u0016l\\\ufffdceN|q\ufffd+\u003e\ufffd\ufffd5\ufffdƝ\ufffdi\ufffdx\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd.\ufffdN:%F\ufffd\u0016\ufffda\ufffd\ufffdf\ufffdy\ufffd?a\ufffdK\ufffd\u000f\ufffdLM\u003e\ufffd\u0005\ufffd\u0000\ufffd\ufffd\rr\u0006c\ufffdTLO;\u003c\ufffd\u001b\u000c\u00139c2\u0019FM\ufffd464,E\ufffdN\u001a\ufffdbŭ\u001dF\u0001f\ufffdD\u0004\ufffdг6\ufffd\u000f\ufffd7\ufffd\ufffd߇TZ\ufffd\ufffd\ufffd%Bɱ(\ufffd\u0013\ufffdb\ufffdRi\u003cC\ufffdl.\ufffd\u000c2*\u0019s?+\ufffd\ufffdS-\u001261\u0005s\ufffd:\ufffd\ufffdDt_\ufffdm\ufffdd1P*\ufffd\ufffd\ufffdDKA\ufffd\ufffdn\u0015``s\ufffd\\\n\ufffd7bFg\u0006\ufffd\ufffd2EA\ufffdӉ\ufffdw?\ufffd\ufffdgC\ufffd_v\ufffd\ufffdj\ufffd\ufffdJo\ufffd\ufffdf\ufffdi\ufffd]t\ufffdU7\ufffd\ufffdV\u0006\ufffd\ufffd0\ufffd\u0013\ufffd\ufffd9\ufffd\ufffd\ufffd\ufffd9\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdC\ufffdL\ufffd[CƝ\u0019?8zs\ufffd\ufffd\ufffd\ufffd\ufffd#\u0015g\ufffdI\ufffd\ufffdK\ufffd\ufffdR*\ufffd\u000f\ufffdG\u000f\ufffd\ufffd*\ufffdIf\ufffdA8\ufffd`\ufffd\ufffd6\ufffdN\ufffd\ufffd\ufffdni;\ufffdF\ufffdK\ufffd*\ufffd\ufffd\ufffdz`\ufffd0\ufffd\n\ufffd\ufffd{\ufffd4\ufffd\u000b\\zkx\u0001a@\ufffd^,\u001e\u0005\ufffd\u003e\ufffd\u0013\u001dmaK\ufffd\u000e\ufffddR\ufffd\u0004\ufffdv\\\u000ex\ufffdbG\ufffd-l\ufffd\ufffd\ufffd޴up\u0010\r\ufffd)\ufffdw\ufffd\ufffd\ufffd\u001d\u001d7\ufffd\ufffd\ufffd\ufffd\u001c\ufffdϏ\ufffd|\ufffd4K\ufffdR-\ufffd\rZ9\ufffd\ufffd\ufffd\ufffd\u0007\ufffd\ufffdnY^Ȍ\u0017Ux\ufffdrp7X*y\ufffd\ufffd\u0013\ufffd\ufffd\ufffd\ufffd\ufffd9\u0018\ufffd\ufffd\ufffdQ\u000f\ufffd\ufffd\ufffd' \ufffd0I\u0019;\ufffdsSV\ufffd\ufffd\ufffdL\ufffd\ufffd\ufffd1\ufffd\ufffdD\ufffd\u0014s\ufffde$\ufffd\ufffd#ȷ\u0014\ufffd\ufffdM\ufffd\ufffd\ufffd;:P\ufffd\u0014\ufffdw\u0012n\ufffd+\ufffd7c\t\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdؔ\ufffdud\ufffdؕ\ufffd\ufffd\ufffd\ufffdp\ufffdv6\u001d\r`\ufffd\u0016\ufffd7n\u0015P\ufffd\ufffd\u000f\ufffd.軠\ufffd\ufffd\nO\ufffd_\u000c\u0026oK|\ufffdlo\ufffd=\ufffd\u0008o\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd7\ufffd\u0002\ufffdn-\ufffd\ufffd\ufffd\n\ufffdw+\ufffdE\ufffd\ufffd\ufffd;\u0012\ufffd0\ufffd\ufffd\ufffd\ufffd\n\ufffdƼXCkm\u0001{\ufffdCx%\u0017د]aK\ufffd\ufffdP\u000f\ufffd\u001b\ufffd\ufffd\ufffdm\ufffd%7\u000e~Ǚ\ufffdT\t\ufffd\ufffdWZ\u000b9\ufffdR\ufffd\u0018V\ufffd\\\ufffd\ufffd\u0017\ufffd\ufffd\ufffdVi\ufffd\ufffd\ufffd\u0000w\ufffd\ufffd-\ufffd[\u0002\ufffd5*\ufffd\ufffd\u0011\ufffdPJ\u0012\ufffd\ufffd\ufffdp8\ufffd\ufffd^:\ufffdm\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdt\u0008\ufffd;9\ufffd\ufffd\ufffdd\u000e\ufffd\ufffd]n\ufffdqrR\ufffdõ\u0005\ufffd5\ufffd\u0017\ufffdw\ufffd\ufffd\ufffd\u000e\u000e\u000f;\ufffd\ufffd\ufffd\ufffd\ufffdh5?ID\ufffd\u000b\ufffd!39jd\u0026{\u0006X\ufffd\ufffd8\ufffd\ufffd\u0002N\ufffdFm\ufffdze_\ufffd\ufffd=`@:\ufffd\ufffd\ufffdА\ufffdw\ufffd=\ufffd\\25\ufffd\ufffd\ufffd\ufffd\u000f\u0007`\u0026u^ֹ\u0019\ufffdf,W\u003c\u0016\ufffdQع\ufffdѥ\ufffd\ufffdǬĶ\ufffd\ufffd\u001e\ufffd\u0004u]\ufffd\u0026\ufffdT\u0026\ufffdЁn\ufffd\ufffdk6\ufffd\ufffd\u0014uX\ufffd;\ufffd\u0019\u0007uL\ufffd9\ufffdr\ufffdIW\ufffdB\ufffd\u0018\u0005\ufffd\u0006\ufffd\ufffdu\ufffd\t\ufffd]\ufffd\u0005\ufffd\ufffd\u0011\ufffd\ufffd{\r\ufffd$7ܺu{doB\ufffd\ufffd\ufffdf?=o\u0006[=\ufffd\ufffd\ufffd\ufffd\ufffd'\ufffdSX\u0012\ufffdM\ufffd\ufffd\ufffd[\ufffd^\ufffd\ufffdohm\ufffdQ\ufffd*\ufffdF\ufffd\ufffd\ufffd =\u0019=e\ufffdsYT\ufffd\u0005G\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdaI\ufffd$\ufffd\u001e{\ufffd\u000be,KE!\u0018\ufffd\ufffd\ufffdv\ufffd9\ufffds\ufffd|\n#\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdS\ufffdX\ufffd\u003cUPj\ufffdD\ufffd\ufffdB\u0001\ufffd\ufffdO\u0006\n\ufffd\ufffd\ufffd\ufffd.\ufffd\u003e\ufffd|,\ufffd\n\ufffd\ufffdW\u000bvr\ufffd:F9%\ufffd\ufffd\ufffd1Ȕ\ufffdr\ufffd\ufffd\ufffd;a\ufffd\ufffd:\ufffd\ufffdX\ufffdI\ufffdLX\u0004\ufffd]vi\ufffd\u000fx\ufffd\u0018\u0019t\ufffd\u001a-X̕\u0002B7\ufffd\ufffd\ufffdm_\ufffd}a|l\ufffd\ufffd\ufffd\u003eu\ufffd]\ufffd9\ufffd\ufffd\ufffd\ufffd\ufffd;\ufffd\ufffd\ufffd\ufffd\u000f!g\ufffd\u0010r\ufffd\u0012!\u001f\ufffd\ufffd\ufffd\u0026\ufffd\ufffd\ufffd\u0014\ufffd\u001c\ufffd\u0008B\u00162\ufffd\ufffd\ufffd\ufffd{d\ufffd\u0005\ufffd(\u0005\ufffdq\ufffd~\ufffd\\\ufffdj\u001d\u0019\ufffdK\ufffd\ufffd\u000c\n2\ufffd\ufffd\u0000\ufffdt\ufffd\ufffd\ufffdHk\ufffd'K\ufffdL\ufffd\ufffd\ufffd\u001d\ufffd\ufffdf\u000e8\u0019F9r\ufffd\ufffdj\u003cI\ufffdLI\ufffd\ufffd\ufffd\ufffd\ufffdz\u000e4\ufffd\ufffdע˞\ufffdj\ufffd+\u0010\ufffd\u0005\ufffd\u0000uW\ufffd\ufffd\u0003 9\ufffd\ufffd\u0017\ufffd\ufffd\u0001\ufffdㅠ\u0004\ufffd\ufffd\ufffd\ufffd*\ufffd\u0000Ew\u0000\ufffd\ufffd\u001f\u000f:\ufffd[A\ufffd\ufffd-t\n@\ufffd\ufffdq\u001b\ufffd\u0002\ufffdU\ufffd\ufffdL\u001b\ufffd\u001e*}ivn\u0015\ufffd\ufffd\ufffdD\ufffd\ufffdQ\u0018 \ufffd\u003cS\u0004C\u0003\u0013Q\u0008\ufffd:`(\ufffd\ufffd֡\ro4\ufffd\ufffd\ufffdN\ufffd\n(,\ufffd\ufffdMq\r\ufffd\u0000@\r\ufffdd\u0015\ufffd\ufffd\ufffdZ\u0018\ufffd\ufffdS\u001d\u000e\ufffdx\ufffd\u00263\ufffd\u0015\ufffd\ufffdA\u000fH\ufffd\u0014S\u003e\u0013`}\u0007\u0018y\ufffd\ufffda\ufffd\ufffd\u0015F\u001e\ufffd¼|He\ufffd\ufffd\ufffd\ufffd\u0010\ufffd\ufffd\u0013JM\u0026\ufffd\u000by\ufffd\ufffdy\ufffdpx\ufffdo^I\ufffd+\ufffdֲ\ufffd,\\J6\u0006ˆ\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd%A\ufffd\ufffd}\u0012\ufffd\ufffd\u0013*\ufffdѠ2\ufffd\ufffd|\ufffdu\u0012,\u0011\ufffd\u000bo\ufffd\ufffd\ufffdJ\ufffdK\ufffdDL\ufffd\u000e\u0006\ufffd\u0011Z\ufffd\ufffd\u0002x\ufffd\ufffd\"\ufffd\ufffd\ufffd0\ufffd\ufffd\u0013ai*\u000b5)`^\u0012\ufffd'd\u0013\ufffd\u0015\ufffd\ufffd\ufffd\u0003\ufffd\ufffd\ufffdx\ufffdy\ufffd\n0\ufffd\ufffd\ufffd\ufffdc\ufffd\u000b$\u0008\u0006J\ufffd\ufffd\u0015?s\u0010?\ufffd\u0013\u001f\ufffdx+\ufffd\ufffdcz\u0001X\ufffd\ufffd\ufffdR\u0005t\ufffd\ufffd.2E\ufffd,\u0003\ufffd\ufffd\ufffdK\ufffd\u000bN\u000e\u0008:!.ށ\ufffd0\ufffd\ufffd\u0006\t;\ufffd\u0026\u000f¼\ufffd~/%\u000b1\u0016\u0008|9+|DC\ufffd\u0007\ufffdS\u0011\ufffd\u0015r`\ufffd,\ufffd9\ufffd\u0007A\u0016\u0000\ufffd\ufffd;\t\ufffd\u0000\u0019E3\ufffd\ufffd\ufffd5\ufffd\ufffd\u0019H\ufffd\u0003\ufffd\u003c\ufffd\ufffd\ufffd\ufffdo\u0005\ufffd\ufffd\ufffd\ufffd\ufffd\u0010\ufffdǕ\ufffdH\u001c\ufffdJP\u0001\ufffdJo\ufffd\ufffd\ufffdB3:\u0016В\ufffd'o\ufffd\ufffd:\ufffd\ufffd\ufffd\ufffdCX\u0003\ufffd\u0015\ufffd֋0\ufffd\ufffdB\ufffd7P\ufffd\ufffd\ufffd\ufffdC\ufffd\ufffd|aPV\ufffdR\ufffd37\ufffdJ\ufffd-\ufffd\u000b~i\"\ufffd\ufffd\ufffd\ufffd\r\ufffd\ufffd\ufffd;\ufffd\ufffd\ufffdW\ufffd\ufffdx\t\ufffd\ufffd0E\ufffd\ufffd\ufffdp\u000f\u0004\ufffd\ufffd\u00001\ufffd=\ufffdV\ufffd\ufffd\ufffdon@nb\u000cNog^Pֱ\ufffdҰ\ufffd…\u0001\ufffd*\u0005\ufffd\ufffd\ufffd\ufffdPyjϒ6\ufffd\ufffd\ufffd\ufffdl\ufffd!\u0000\u001a\ufffd\n\ufffds2\u0004\ufffd\ufffd\ufffd/\ufffd\ufffdrLՀ\ufffd\u0000\u0010\ufffd\u00071\ufffd\u001a0et\ufffd\ufffd\ufffdF%s\u0000\u0016\ufffdͲRKW\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdו\ufffd\ufffd\ufffdk\ufffd#F\u001cC\u003eN|\ufffdUi%\ufffd\ufffd\ufffd\ufffd\ufffd6b\ufffd8]Hyi@\ufffd\u001e\ufffd\ufffd\u0016\ufffd\n\ufffd\ufffdF)\ufffd\ufffd\ufffd\u0017\\\ufffdN\ufffd3 \ufffd\u0000\ufffd\ufffd\ufffdB\u0016\ufffd}k\ufffd-J!˽\ufffd7\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u0017YD'\ufffdw\ufffd\ufffdG\ufffd-Qh\ufffdK\"7U\ufffd\u001b\u0006\ufffd\ufffd\"\ufffd\ufffdg)\u000b\ufffd\u0001\ufffd\u001a!\ufffd\ufffd\ufffdP\ufffd\u0008\ufffd\ufffdRI\ufffd\ufffd7\ufffd$\ufffdj\u000f\ufffd\ufffd\ufffd\ufffd\ufffdB\ufffd$\ufffd\ufffd\u0000\ufffdm1\ufffd\u0016\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd~\u001e\ufffd\ufffd\ufffd.Ӌ\ufffd\ufffd^O\ufffd\ufffd\ufffd\ufffdwo\ufffdx\ufffd\ufffd˖E\ufffd\ufffd\ufffdt\ufffd\ufffd2\ufffd\\\ufffdL\ufffd\ufffd\ufffd!\ufffd@{\ufffd\u0015F\u0001\ufffdѝ\ufffdrHȕ\ufffd\ufffd\u001f\ufffd\ufffd|\u001fXa\ufffd\u0000Bt\u0016\ufffd4:V2\ufffd\ufffd\ufffd\ufffd{\ufffd/\ufffdH\u000fp\u001a\ufffd{H'\ufffdЪ\ufffd\ufffd)\ufffdĴ\ufffdяN\ufffd\ufffd\ufffd\ufffd\ufffd\ufffds:ad\t*\ufffd[\ufffd\u0014![͞\ufffd\ufffd\\\ufffd\ufffd\u0008\ufffd\ufffdn\ufffd\ufffd\ufffd2\ufffd\u0004\u003e\ufffd(l\ufffdw\ufffd\u0013S\ufffd\u0004R\ufffdނ\ufffd£Ԭk\ufffdK%,\ufffdFצb\ufffd\u001a\ufffd\ufffd\n\ufffd\ufffd\ufffdM\u0003u`*Eҁ\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdc\ufffd\ufffd\ufffdo\ufffdt\u000b\ufffd|\ufffd4\ufffdU\u0015\nUaz\ufffd\ufffd\ufffdg\u0001\ufffd\ufffdJN\ufffd\u001b!\ufffdC\ufffd8\ufffd\ufffd\ufffd\ufffd\n\ufffdy\nS\ufffdWK{m\ufffd-\ufffd:tr{ᠶ\u000b\ufffd\u0016^\u000f\ufffd\ufffd\ufffd\ufffd-\ufffd7}\ufffdd\ufffd\ufffd\ufffd\ufffd6\ufffdj\ufffd\ufffd\ufffd\ufffd\ufffd\ufffda'?\ufffd\ufffd\ufffd\ufffd\ufffdp\u00198\ufffd7.\ufffd\ufffdX\ufffd\ufffd\ufffdKv\ufffd\ufffd^\ufffd\ufffd;\ufffd\ufffdq\ufffdր\u0004\ufffd\ufffd\ufffd\",y\ufffd\ufffd|nY\ufffd\ufffd\ufffdR\ufffd7\u0007n\ufffd\u000fm\ufffd\u001f\ufffdZ|7\ufffd\ufffd\ufffd\ufffd\ufffd{\ufffdjn\ufffd\u0015֮1\ufffd^wD\ufffd\ufffd\ufffd\ufffd 5|\ufffd\ufffd\u0019\ufffd\ufffd+\ufffd\ufffd\u001a\ufffd\u0004\ufffd\ufffd\ufffdI\ufffd?\ufffd\ufffd%C\ufffd*\u0000\u0000","raw":"HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 2646\r\nContent-Encoding: gzip\r\nContent-Type: text/html; charset=utf-8\r\nDate: Thu, 07 Sep 2023 15:34:04 GMT\r\nSet-Cookie: AWSALB=+kt6Ib2lLrBXa4YCECeKwNyIl2PFt7T1knMc4Ed88f+NSWWr4GdaZPWIqyaJBNi2eoCbLJMcYgimR45FELKCqRmtTLdXLFD6x+MBEnFw43yIfk3L1QGW79pv1ip2; Expires=Thu, 14 Sep 2023 15:34:04 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=+kt6Ib2lLrBXa4YCECeKwNyIl2PFt7T1knMc4Ed88f+NSWWr4GdaZPWIqyaJBNi2eoCbLJMcYgimR45FELKCqRmtTLdXLFD6x+MBEnFw43yIfk3L1QGW79pv1ip2; Expires=Thu, 14 Sep 2023 15:34:04 GMT; Path=/; SameSite=None; Secure\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n"}} +{"timestamp":"2023-09-07T21:04:07+05:30","url":"https://ginandjuice.shop/blog/?search=dadad\u0026back=%2Fblog%2F","request":{"header":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Connection":"close","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=+kt6Ib2lLrBXa4YCECeKwNyIl2PFt7T1knMc4Ed88f+NSWWr4GdaZPWIqyaJBNi2eoCbLJMcYgimR45FELKCqRmtTLdXLFD6x+MBEnFw43yIfk3L1QGW79pv1ip2; AWSALBCORS=+kt6Ib2lLrBXa4YCECeKwNyIl2PFt7T1knMc4Ed88f+NSWWr4GdaZPWIqyaJBNi2eoCbLJMcYgimR45FELKCqRmtTLdXLFD6x+MBEnFw43yIfk3L1QGW79pv1ip2","Referer":"https://ginandjuice.shop/blog","Sec-Ch-Ua":"\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"macOS\"","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"same-origin","Sec-Fetch-User":"?1","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"GET","path":"/blog/","scheme":"https"},"raw":"GET /blog/?search=dadad\u0026back=%2Fblog%2F HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nConnection: close\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=+kt6Ib2lLrBXa4YCECeKwNyIl2PFt7T1knMc4Ed88f+NSWWr4GdaZPWIqyaJBNi2eoCbLJMcYgimR45FELKCqRmtTLdXLFD6x+MBEnFw43yIfk3L1QGW79pv1ip2; AWSALBCORS=+kt6Ib2lLrBXa4YCECeKwNyIl2PFt7T1knMc4Ed88f+NSWWr4GdaZPWIqyaJBNi2eoCbLJMcYgimR45FELKCqRmtTLdXLFD6x+MBEnFw43yIfk3L1QGW79pv1ip2\r\nReferer: https://ginandjuice.shop/blog\r\nSec-Ch-Ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"\r\nSec-Ch-Ua-Mobile: ?0\r\nSec-Ch-Ua-Platform: \"macOS\"\r\nSec-Fetch-Dest: document\r\nSec-Fetch-Mode: navigate\r\nSec-Fetch-Site: same-origin\r\nSec-Fetch-User: ?1\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Content-Encoding":"gzip","Content-Length":"2075","Content-Type":"text/html; charset=utf-8","Date":"Thu, 07 Sep 2023 15:34:07 GMT","Set-Cookie":"AWSALB=ZJm/2Bx+PPBvRylO0Q4ANLyMc0beelNKLpyYHasVfthZ3FRdadKIrIFFGUK658OSRpW4bg89XZl70Af+XA6qbw2pno1/pPHTdoRXm4gW2bRwl59JyYaXuecXXx4s; Expires=Thu, 14 Sep 2023 15:34:07 GMT; Path=/, AWSALBCORS=ZJm/2Bx+PPBvRylO0Q4ANLyMc0beelNKLpyYHasVfthZ3FRdadKIrIFFGUK658OSRpW4bg89XZl70Af+XA6qbw2pno1/pPHTdoRXm4gW2bRwl59JyYaXuecXXx4s; Expires=Thu, 14 Sep 2023 15:34:07 GMT; Path=/; SameSite=None; Secure","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"body":"\u001f\ufffd\u0008\u0000\u0000\u0000\u0000\u0000\u0000\ufffd\ufffdZ͒\ufffd6\u0012\ufffd\ufffd)`\ufffd\ufffd\u001cUB13\ufffd\ufffd\u000f#jk\ufffd\ufffdz+5v\ufffd;\ufffd\ufffd\ufffd\ufffd\ufffd@\ufffd\ufffd\u0007$\u0018\u0000\ufffd\ufffd\ufffd\ufffd\u0003\ufffd5\ufffddi\u0000$EI\ufffdH\ufffd\ufffdj\u000f\ufffd]%\u0012@7\u001a\ufffd\u000f\ufffd_C3{\ufffd\ufffd\ufffd'\ufffdz\ufffd\u000c\ufffdt\ufffd\ufffd_\ufffd\ufffd\u0017\ufffd\ufffdlEq\ufffd\u001e\ufffd+g\ufffd\u001dZI\ufffd\ufffdBI\ufffd($\ufffd*\ufffdxa\ufffdQ\u0019\u0012\ufffdBEp\ufffd\ufffd\ufffd\ufffd6L\ufffd\u0001I\ufffd#\ufffdKNՊR=\ufffd̨\u0000\ufffdꚋ\ufffd#\ufffdo\ufffd\ufffd\ufffd\u001eW\ufffdR\ufffdQ\ufffdS\u001aykF7\ufffd\ufffd\ufffdCDd\ufffdf:\ufffd6,֫(\ufffdkFh`_\ufffdF\ufffd\ufffd2\ufffd\u0005\ufffd\u000c\ufffdF\ufffd\ufffdZ\ufffd\u0014\ufffd,\ufffdHI\u0012y-\ufffd\ufffd+x\ufffdDOA\u0013\ufffd\"OA\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdI\ufffdW\u0011\ufffd\"=A\ufffd.sX\ufffd\ufffd\ufffd:|\ufffd\ufffdصz]\ufffdq\ufffd\u0014\u001c\ufffdw\ufffd\ufffd\ufffd\ufffdq\ufffdV\ufffd4\ufffd\ufffd\ufffd,C\u000fq\ufffd_\ufffd\ufffd\np\u000c\ufffd]\ufffd|\u0016\ufffd\u003e\ufffd\ufffdp\u000b\ufffd\ufffdB\ufffd%ʒ\u0000\ufffdyKS\ufffdֈő\ufffdFJ˓n\r\ufffdh\u00262D8V*\ufffd\u001c\ufffd\ufffd\ufffd:\ufffd\ufffdgO\ufffd\n\ufffd\ufffdm\ufffd\ufffdz\ufffd\u0014\ufffd\ufffd\u0018Ŕ\ufffd\u0005\ufffdXS^\ufffdu\ufffd3x\ufffd\ufffdD\u001b\ufffd@`#g\u0004\ufffdYa\u001e\ufffdd4FK!\ufffd\ufffdJ\ufffd,1\ufffd\u001ef\u000b\ufffd_Ղ\ufffd3]\ufffd\u0026\ufffd\u0014hS\ufffdN\u0003f*\ufffd\ufffdR\u000cl=$\ufffdT\u0000\ufffd`\u0017\ufffd4X\ufffdb\ufffd\ufffd\ufffd{w\ufffd\"t\ufffdt\ufffd\ufffdn\ufffd\ufffdNo\n\ufffd\ufffd\ufffdK\ufffd[wCk\ufffd\ufffd~\ufffd#\\\ufffd\ufffdǖ\ufffd\ufffd=\ufffdÇ\ufffd=\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\t\ufffd\ufffd\ufffdV\ufffd\ufffd\u0016\ufffd3\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdo9I\ufffd\ufffd\ufffd\ufffd\ufffdy\ufffd\ufffds\ufffd\ufffd\"\ufffd\ufffd\ufffd)66\ufffd\u003ePM[\ufffdί\ufffdA\ufffdEG\ufffdeG\ufffd#o\ufffd\ufffdШ\u0004\u003c\ufffdj\u001f\ufffd\u0016H\ufffd\ufffdݺ\u0007\ufffdep\u0007\\\ufffd=\ufffd\ufffd@\ufffd\ufffd\ufffdX\ufffd\u001dl\ufffd\ufffdD~\ufffdZ\u0003o7\ufffd\ufffd\u0012\ufffd`X7.g؅/?\ufffdPӡN٘\u0016@`\u0014p@\ufffd\ufffdGm\ufffd{:\ufffd\ufffd\ufffd\ufffd\r\u0006\ufffd\ufffd2\u0019\u0019Q\ufffd\u001a\ufffd0\ufffd%\u0016\u0013Au`\r\ufffd\u0015\ufffd%K\ufffd,\ufffd[\ufffd\ufffd=P\ufffd\ufffd\ufffd ;\u0010\ufffdS/\n\ufffd!(8\ufffdz! \u0013\ufffd\ufffd\u0000\u0019R\ufffd\u0005Ѫ\ufffd);\ufffd\ufffd1\u0013\ufffdu\ufffd\ufffd\ufffdh\u001a7f.\ufffd\ufffd\u0026\ufffd\ufffd?\ufffdט\ufffd\u0017\ufffd\ufffd\ufffd\ufffd{s\ufffd\ufffd\ufffd\ufffd'2n\u0016\u0016\ufffd\ufffd\ufffd\u0004\ufffdQd\ufffdgF\u0013\u0026D\u0014\ufffd\u000e\u0018iy%-\ufffd\ufffd\u001d\ufffd\ufffdZ'\ufffd\u0019\u0004\ufffd\ufffds\ufffdO\u0001\ufffd\ufffd\ufffd\u0002\ufffdz\ufffd\u0014F\ufffd\ufffd\u0013\ufffd/\u0008\ufffd4k\ufffd\ufffdqo\ufffd\ufffdq\u0003OZ\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd^\ufffd\ufffdH\u0010\ufffdF[l\ufffd\ufffdB\ufffd\ufffd5\ufffdE\ufffd\ufffd\ufffd\ufffdd\ufffd0\ufffd\ufffd\ufffd\ufffd\u0000\ufffd\u0012,\ufffd\ufffdZE\ufffd\ufffd\ufffd\u0018\ufffd\ufffd\u0014x^\ufffd‘\ufffd5\ufffd\u0016\ufffd[\ufffd\ufffd\ufffdv\ufffd\ufffd[\u0007?\ufffd\"I8\ufffd\ufffd\ufffdN\ufffddIb\ufffd\ufffd[M\ufffd\ufffd\ufffd\u0017\ufffd\ufffdc\ufffd\ufffd,\ufffdM\ufffd=)\ufffd\ufffd\ufffd\ufffdX썲\ufffd\u0019\u0018\ufffd\ufffdE\\R8J\ufffdS\ufffd\ufffdM\ufffd\ufffdb\ufffd\ufffdL\ufffd5\ufffd\ufffd\u0002\u00079\ufffdnOȜ\ufffd0\ufffd8\\h\ufffd\ufffd\ufffdp\u0015\ufffd\ufffd\u0004C\ufffdݧ\ufffd\ufffdJp\ufffd\ufffdÎ\ufffde\ufffd\ufffd[\ufffd\ufffd4\ufffd\u0012\ufffdP\ufffd\ufffd\ufffd\ufffd䈀\ufffdX\ufffd\u0015\u0004)\ufffdɞT\ufffd\ufffd;~u\ufffdG\ufffd\u0000\ufffd@\n\ufffdĈD6y\ufffd\u0008\ufffdȕ\ufffd\ufffd\ufffd\ufffd^\u000f\ufffd\ufffdeyQ\u0015e\ufffd\u0026C9DŽ\ufffd\u0004\u0007\ufffdE\ufffd\ufffd\ufffd\ufffdrV\ufffdv:\ufffd\ufffd\ufffd2u\u0026\ufffd5\ufffd\ufffd\ufffd\ufffd\u0018\ufffd\ufffd\ufffdΖ\ufffd\u0015\ufffdc\ufffd9\ufffd\u0005\u0026w\ufffd\ufffd\ufffds O\ufffd*\ufffdb5\ufffdb\ufffd2]\ufffd\ufffdu̝\ufffd\ufffd\ufffdz=r^\ufffd\ufffd\ufffd\ufffd\ufffdɯ\ufffd\ufffdc;e\u003e\ufffd\"s\ufffd\ufffd%\ufffd\ufffdYt\ufffdKAe9A\ufffd;\ufffd\ufffdX\ufffd\ufffdV\ufffd\u001b\ufffd4=\ufffdg,M\u000e\n\ufffd*\ufffdY倞\ufffd-\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u0014x\ufffdWv\ufffd\ufffd|o\ufffdO\ufffdz\ufffd\ufffd\ufffd\ufffdg\ufffd%\ufffd*\ufffd`:\ufffd\ufffd\u0006\ufffd\ufffd\ufffd\u001b\ufffd\ufffdW\u0006\ufffd\ufffd.\ufffd\ufffd\ufffdj\ufffdf\ufffdL\ufffd\t\ufffdg\ufffd{;65[\ufffd\ufffdơ\ufffdN]\ufffdI\ufffd\ufffd\u00064`kJ\ufffdL\u0004\ufffd\ufffd\ufffd\ufffdc\ufffd\ufffd\u001d\ufffdF\t\ufffd\u000b\ufffdIk0\ufffd%6\ufffd(\ufffd\ufffd\ufffd\ufffd\ufffd\\\ufffd[o5\u0026\ufffd.\ufffd\ufffdB\ufffd\ufffd\u0001z)\ufffd\ufffd\u0000-\ufffds\ufffd\u0010\ufffd.\ufffd͟_\ufffdQ\ufffdE\ufffdk\ufffd\ufffd5EE\ufffd\u0002DV\u0014\ufffd\ufffd\ufffd\u000b*\ufffd\ufffd\ufffd{\u0008s%\u0001 \u0002\u0013\ufffdi$\u0026L\ufffd*Nh\u0003\ufffd週C\ufffd[WB\ufffd0,ۄ\ufffdVЩ\ufffd\ufffd/}(\ufffd\tg\ufffd.\ufffd\ufffd\ufffd\u001c\ufffd\\\ufffd\ufffd\ufffdt\ufffda\ufffdg\ufffd\ufffd\ufffd%A\u0017\ufffd\ufffd\ufffdn\ufffd\ufffd\ufffd\ufffd\ufffd\t\ufffd\ufffdWT\ufffdHW\ufffd\ufffdڄ4(\ufffd\u0007\ufffd\ufffd\ufffd\ufffdqo\u0000\u001a\ufffd\ufffd\ufffd\ufffd\ufffd-\ufffd\u0000\ufffd\u0007\u001b\ufffd\ufffd\ufffd;\u0011\ufffd\u001d\u0005'ЗH\u000f5s\ufffd4\ufffd\ufffdȒ\u000ee\ufffd\ufffdT:v\ufffd\u0000\ufffd/a\ufffd$J\ufffdr\ufffdb\ufffd\u0003\ufffd \ufffd\ufffdc\ufffd\ufffdpN7\ufffd1;\ufffd\ufffd\ufffd\t\ufffd!s\ufffd\ufffd\u0011\ufffd\ufffd\u001b\u0016SDD\ufffd\u0016\u0019\ufffd%\u0012K\ufffdp\ufffdp\u0016\ufffd\ufffd\ufffd\ufffdp\ufffd3\ufffdh\ufffd\ufffd\ufffdWj\ufffd\ufffd\ufffdB\u0019\ufffd\u0003(\u0014\ufffd\ufffd*\ufffd\ufffd\ufffd\ufffdXk`\ufffd\ufffd\ufffd4\u0017F\ufffd\ufffd\u001b\u0011\ufffdCY*-Z\ufffd\ufffdhN\ufffd\ufffdӃ[\ufffd]\ufffd\ufffd3\ufffdz\ufffd/!\ufffd\u000e3\ufffd\ufffd\ufffd\ufffdⲖ\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdb\ufffd\ufffdAE\u0008\ufffd\\(]5U\ufffdaKʷ\nF\ufffd\u0006I)\ufffd\ufffd\ufffdK\ufffd\u0014\ufffd\u001fwy\ufffd=\ufffd\ufffd\ufffd\ufffd\ufffd6\ufffd8\ufffd\ufffd\u000e\u0006\ufffd.\ufffd\ufffd#\u0012^u3N\ufffd\\z5\ufffdx\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\t}\ufffd\ufffd\ufffd\ufffd}|\ufffdCI\ufffde\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u000bzw\ufffdf$\ufffdث\ufffdZ\ufffdc~[{f\u000c\ufffd\u0018\nc\ufffd\ufffdvà\ufffdˁƺ\ufffd\u00132I%\ufffd\ufffd\u0007VH\ufffdP4\ufffd\ufffdY\u0007K\ufffd\ufffd\ufffd\ufffd~jM:\ufffdasbN\ufffdЪ\ufffd\ufffdɲ\ufffd\u0018a\ufffd\ufffd\u00062~\ufffd\ufffd\ufffd\ufffd\ufffd.\ufffd\ufffd\u0004\ufffd^Aj\ufffd\ufffd\ufffd\ufffdf\ufffdͥ\ufffd\ufffd\ufffdzuY\ufffdM^\ufffdK\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd1\u0010\ufffd\ufffd\ufffd\ufffd\ufffd\u0004\ufffd\ufffd\u000eԪ+\ufffd\ufffd\u00264FC\ufffd\ufffd\ufffd@\ufffd\ufffd\ufffd\u0003vd\ufffd7-ԁRF\ufffd\u0000\ufffdߝ\ufffd\ufffd\ufffdV\u0017\ufffd\ufffd\ufffd\u0018\ufffd\ufffd\ufffdt\ufffd\ufffd|\ufffd\ufffd\u003c\ufffd%P\t\ufffd\ufffd\ufffdޤS{kn~\ufffd\ufffd\r\ufffdx\u0002\ufffd\u003eZP\ufffdf\ufffdF\u0015\ufffdfG\u0008i~\ufffd;\u001akO0j\u0004?1\ufffdb\u001cE\ufffd\ufffd\u001el\ufffdd\ufffdj\u0008\ufffdmQ\ufffd\ufffd\ufffd\ufffd\ufffdK\u0005{\ufffd\u003e\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd;\ufffd\ufffd\ufffd\ufffd\u0012\ufffd\u0002\r\ufffdN\u0003\ufffd\ufffd\ufffdG0\ufffd\ufffdr|\ufffd\ufffd\ufffd+\ufffd~\ufffd\ufffdIH`\ufffd\ufffd\u0013h\ufffd\ufffd\ufffdnv$\ufffdW\ufffd\ufffd\ufffd\ufffd\u000c\\\ufffd\u000e9ﯟ\"\u003e\ufffdu\ufffd\u0014\ufffd\ufffd/\ufffd\ufffdk\ufffd\ufffd[\ufffd\ufffd\ufffd\u001ev\ufffd\u000e\ufffd%\ufffd\ufffd\ufffd\u0018\u000e\ufffd)d%\u0011\ufffd\ufffd/ 7ڿ\ufffd\ufffd\u0013o\u0004\ufffdo\ufffd!\u0000\u0000","raw":"HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 2075\r\nContent-Encoding: gzip\r\nContent-Type: text/html; charset=utf-8\r\nDate: Thu, 07 Sep 2023 15:34:07 GMT\r\nSet-Cookie: AWSALB=ZJm/2Bx+PPBvRylO0Q4ANLyMc0beelNKLpyYHasVfthZ3FRdadKIrIFFGUK658OSRpW4bg89XZl70Af+XA6qbw2pno1/pPHTdoRXm4gW2bRwl59JyYaXuecXXx4s; Expires=Thu, 14 Sep 2023 15:34:07 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=ZJm/2Bx+PPBvRylO0Q4ANLyMc0beelNKLpyYHasVfthZ3FRdadKIrIFFGUK658OSRpW4bg89XZl70Af+XA6qbw2pno1/pPHTdoRXm4gW2bRwl59JyYaXuecXXx4s; Expires=Thu, 14 Sep 2023 15:34:07 GMT; Path=/; SameSite=None; Secure\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n"}} +{"timestamp":"2023-09-07T21:04:08+05:30","url":"https://ginandjuice.shop/logger","request":{"header":{"Accept":"*/*","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Connection":"close","Content-Length":"34","Content-Type":"text/plain;charset=UTF-8","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=ZJm/2Bx+PPBvRylO0Q4ANLyMc0beelNKLpyYHasVfthZ3FRdadKIrIFFGUK658OSRpW4bg89XZl70Af+XA6qbw2pno1/pPHTdoRXm4gW2bRwl59JyYaXuecXXx4s; AWSALBCORS=ZJm/2Bx+PPBvRylO0Q4ANLyMc0beelNKLpyYHasVfthZ3FRdadKIrIFFGUK658OSRpW4bg89XZl70Af+XA6qbw2pno1/pPHTdoRXm4gW2bRwl59JyYaXuecXXx4s","Origin":"https://ginandjuice.shop","Referer":"https://ginandjuice.shop/blog/?search=dadad\u0026back=%2Fblog%2F","Sec-Ch-Ua":"\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"macOS\"","Sec-Fetch-Dest":"empty","Sec-Fetch-Mode":"cors","Sec-Fetch-Site":"same-origin","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"POST","path":"/logger","scheme":"https"},"body":"{\"search\":\"dadad\",\"back\":\"/blog/\"}","raw":"POST /logger HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: */*\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nConnection: close\r\nContent-Length: 34\r\nContent-Type: text/plain;charset=UTF-8\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=ZJm/2Bx+PPBvRylO0Q4ANLyMc0beelNKLpyYHasVfthZ3FRdadKIrIFFGUK658OSRpW4bg89XZl70Af+XA6qbw2pno1/pPHTdoRXm4gW2bRwl59JyYaXuecXXx4s; AWSALBCORS=ZJm/2Bx+PPBvRylO0Q4ANLyMc0beelNKLpyYHasVfthZ3FRdadKIrIFFGUK658OSRpW4bg89XZl70Af+XA6qbw2pno1/pPHTdoRXm4gW2bRwl59JyYaXuecXXx4s\r\nOrigin: https://ginandjuice.shop\r\nReferer: https://ginandjuice.shop/blog/?search=dadad\u0026back=%2Fblog%2F\r\nSec-Ch-Ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"\r\nSec-Ch-Ua-Mobile: ?0\r\nSec-Ch-Ua-Platform: \"macOS\"\r\nSec-Fetch-Dest: empty\r\nSec-Fetch-Mode: cors\r\nSec-Fetch-Site: same-origin\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Content-Encoding":"gzip","Content-Length":"0","Date":"Thu, 07 Sep 2023 15:34:08 GMT","Set-Cookie":"AWSALB=JGWQqbBbFYF0Ruume5uOLTakW9Tzw1jCFErY/0i5fb/9LxF9/fQL7ULoDMACjp2MTQT5l73mWyrEKN3g8afgJLXwSSzXbEsrTwBpQ0FUsHhnjYm0/64kwA590M8g; Expires=Thu, 14 Sep 2023 15:34:08 GMT; Path=/, AWSALBCORS=JGWQqbBbFYF0Ruume5uOLTakW9Tzw1jCFErY/0i5fb/9LxF9/fQL7ULoDMACjp2MTQT5l73mWyrEKN3g8afgJLXwSSzXbEsrTwBpQ0FUsHhnjYm0/64kwA590M8g; Expires=Thu, 14 Sep 2023 15:34:08 GMT; Path=/; SameSite=None; Secure","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"raw":"HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 0\r\nContent-Encoding: gzip\r\nDate: Thu, 07 Sep 2023 15:34:08 GMT\r\nSet-Cookie: AWSALB=JGWQqbBbFYF0Ruume5uOLTakW9Tzw1jCFErY/0i5fb/9LxF9/fQL7ULoDMACjp2MTQT5l73mWyrEKN3g8afgJLXwSSzXbEsrTwBpQ0FUsHhnjYm0/64kwA590M8g; Expires=Thu, 14 Sep 2023 15:34:08 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=JGWQqbBbFYF0Ruume5uOLTakW9Tzw1jCFErY/0i5fb/9LxF9/fQL7ULoDMACjp2MTQT5l73mWyrEKN3g8afgJLXwSSzXbEsrTwBpQ0FUsHhnjYm0/64kwA590M8g; Expires=Thu, 14 Sep 2023 15:34:08 GMT; Path=/; SameSite=None; Secure\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n"}} +{"timestamp":"2023-09-07T21:04:10+05:30","url":"https://ginandjuice.shop/blog/","request":{"header":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Connection":"close","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=JGWQqbBbFYF0Ruume5uOLTakW9Tzw1jCFErY/0i5fb/9LxF9/fQL7ULoDMACjp2MTQT5l73mWyrEKN3g8afgJLXwSSzXbEsrTwBpQ0FUsHhnjYm0/64kwA590M8g; AWSALBCORS=JGWQqbBbFYF0Ruume5uOLTakW9Tzw1jCFErY/0i5fb/9LxF9/fQL7ULoDMACjp2MTQT5l73mWyrEKN3g8afgJLXwSSzXbEsrTwBpQ0FUsHhnjYm0/64kwA590M8g","Referer":"https://ginandjuice.shop/blog/?search=dadad\u0026back=%2Fblog%2F","Sec-Ch-Ua":"\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"macOS\"","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"same-origin","Sec-Fetch-User":"?1","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"GET","path":"/blog/","scheme":"https"},"raw":"GET /blog/ HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nConnection: close\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=JGWQqbBbFYF0Ruume5uOLTakW9Tzw1jCFErY/0i5fb/9LxF9/fQL7ULoDMACjp2MTQT5l73mWyrEKN3g8afgJLXwSSzXbEsrTwBpQ0FUsHhnjYm0/64kwA590M8g; AWSALBCORS=JGWQqbBbFYF0Ruume5uOLTakW9Tzw1jCFErY/0i5fb/9LxF9/fQL7ULoDMACjp2MTQT5l73mWyrEKN3g8afgJLXwSSzXbEsrTwBpQ0FUsHhnjYm0/64kwA590M8g\r\nReferer: https://ginandjuice.shop/blog/?search=dadad\u0026back=%2Fblog%2F\r\nSec-Ch-Ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"\r\nSec-Ch-Ua-Mobile: ?0\r\nSec-Ch-Ua-Platform: \"macOS\"\r\nSec-Fetch-Dest: document\r\nSec-Fetch-Mode: navigate\r\nSec-Fetch-Site: same-origin\r\nSec-Fetch-User: ?1\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Content-Encoding":"gzip","Content-Length":"2651","Content-Type":"text/html; charset=utf-8","Date":"Thu, 07 Sep 2023 15:34:10 GMT","Set-Cookie":"AWSALB=mMf3rdKgea2WMm1zlXRLuVlGnzwzbnCekLUEJC1ovH7rG4GvyvUURlqixQ3pFQSyaGvl4sxhMoY/ptxpaAO2y0WTm/iV8QTUpZ7boE6tcrN3sR+Ibwx2OvFmWPN+; Expires=Thu, 14 Sep 2023 15:34:10 GMT; Path=/, AWSALBCORS=mMf3rdKgea2WMm1zlXRLuVlGnzwzbnCekLUEJC1ovH7rG4GvyvUURlqixQ3pFQSyaGvl4sxhMoY/ptxpaAO2y0WTm/iV8QTUpZ7boE6tcrN3sR+Ibwx2OvFmWPN+; Expires=Thu, 14 Sep 2023 15:34:10 GMT; Path=/; SameSite=None; Secure","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"body":"\u001f\ufffd\u0008\u0000\u0000\u0000\u0000\u0000\u0000\ufffd\ufffdZێۺ\u0015}?_\ufffd\ufffdh\u0026\u0001\ufffd֙KrZ\ufffd\ufffdAng2An\ufffd\u0004Mӗ\ufffd\ufffdh\ufffd\u0019\ufffdTEʎ\ufffd\ufffd\ufffd\ufffd'\ufffd7\ufffd)\ufffd\ufffd\ufffdM\ufffd\u001e\ufffd\ufffd\ufffd\u0026\ufffd\u0000y8A0\ufffd\ufffd\ufffd\ufffdM\ufffd\ufffd}\ufffd\u0006w\ufffd\ufffd}\ufffd\ufffd\ufffd\ufffd\ufffd,s\ufffd\u001a\ufffd4\u0008?\u000c\ufffd\u0006\ufffd\ufffdix\ufffd\ufffdJ\ufffdK\ufffd\ufffdb2\ufffdKaMU\u0026\ufffdƊ\ufffd\ufffdL\ufffdqbml\u0013\ufffd_\ufffd\u0017\ufffd\ufffd\ufffd\u0006V\n5\ufffd\ufffdV\ufffdfB\ufffd}̈\u0005\u0018\ufffd'\ufffdL\ufffda\ufffd\u0005\ufffd\ufffd\ufffdn\u0006\ufffdp\ufffdi\ufffd\ufffda4\ufffdb^\ufffd\ufffdE,1\ufffd\t\ufffd\ufffd\ufffd\\\ufffd.\u001b\ufffdb\u0026\u0013\ufffd\ufffd/\ufffd\ufffd\ufffd\ufffd\ufffda\ufffd\ufffdA\ufffd\ufffd6Q\ufffd\ufffdMJY8f\ufffdd\u0018\ufffd\u0004\ufffdl\ufffd\ufffd\u0013\ufffd\u0007'\ufffdL\ufffd\ufffdy\ufffd\ufffd\ufffdF\ufffd8\ufffd\ufffd΢\ufffd\ufffd\ufffd\u0006l\\]`eN|q\ufffdg\u003e\ufffd\ufffd5\ufffdĝ\ufffdi\ufffdx\ufffd\ufffd\ufffd\ufffdK\ufffd\ufffdm\\\ufffdtJ\ufffdH-\ufffd\ufffdΤfwy^\u003cb/+\ufffd\u000f\ufffd\ufffdL1\ufffd\u0003I\u0000N|\ufffd\ufffd\ufffdؤ5\ufffd\ufffd\u001e/\ufffd\u0016\ufffdTΘL\ufffdQ\u001b0\ufffd\r\rK\u0011\ufffd\ufffdF\ufffdDqk\ufffdQ\ufffdY/\u0015A:\ufffd\ufffd\r\ufffd\ufffd\ufffd\ufffdm\ufffd\ufffd}\u0026-\ufffd\ufffdR\ufffd\ufffdX\ufffd\ufffd\tU\ufffdY\ufffd4\ufffd\ufffdP6\u0017c\u0006\u0019\ufffdL\ufffd\ufffd\u0015\ufffdȩ\u0016)\ufffd\ufffd\ufffd9a\ufffd\ufffdS\"\ufffd\ufffdǶx\ufffd\u0018(\ufffdtuh\ufffd\ufffd\ufffd\ufffd\ufffd7\n0\ufffd\u0005_.\ufffd\ufffd\u001b1\ufffds\u0003PA\ufffd\ufffd$\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u003e}9bCv\ufffd\ufffd6\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u001c\ufffd\ufffdޓл\ufffdF\ufffdnw\ufffd\ufffd\u000c\ufffd\ufffda$'\ufffd^{\ufffd\ufffdwY{\ufffd\ufffdד\ufffd\ufffd\ufffd\ufffdL\ufffdf\ufffd\ufffd\ufffd;3\ufffdw\ufffd\ufffd\ufffd\ufffdG\ufffd\ufffd\u0007*\ufffd\ufffd\ufffd×\"=S*\ufffd\u000f\ufffd\u0007\ufffd\ufffd\ufffd*\ufffdIf\ufffdA8\ufffdbw\ufffd\ufffd]k;\ufffd\ufffdv\ufffd\ufffd\ufffd4\u001a\ufffd\ufffd\u001c\ufffd\u0001k\ufffd\ufffd\ufffd9\ufffd\\\u0026\ufffd\ufffd\ufffd\u0000\ufffd\ufffd\ufffd\rp\ufffd\ufffd\ufffd\u0002€\ufffd\ufffdX\u003c\u0008\ufffd}\ufffd':\ufffd\ufffd\ufffd\ufffd\u001dHɤp\t\ufffd͸\u001c\ufffd`\ufffd\u000e\ufffd\rl6\ufffd\ufffd޴\ufffdp\u0010\r\ufffd)ڷ\ufffd\ufffd\ufffd\ufffd\ufffd+V\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdh\ufffde\ufffd%E\ufffd\u0016\ufffd\u0006\ufffd\ufffdzL\ufffd\ufffd\u0003K`\ufffd\ufffd(e\ufffd\ufffd:\ufffdm8\ufffd\ufffdX*\ufffd\ufffd\ufffd\u0013\ufffd\ufffd\ufffd\ufffd\ufffd9\u0018\ufffd\ufffd\ufffdQ\u000cdr\ufffd\u0013\ufffdQ\ufffd\ufffdJ\ufffdݺ)+\ufffd\ufffd.\u0013\ufffdt\ufffd\ufffd\ufffd8\ufffd.\ufffd\u001c{\u0019ɴ\ufffd\u0008\ufffd-\ufffd\ufffdcS\ufffdh\ufffd\ufffd\u000e\ufffd3e\ufffd\ufffd\ufffd\u001bĕ\ufffdf,\ufffd0\u001a\ufffd\ufffd2\ufffdx\ufffd\ufffdJ\ufffd\ufffdLZ\ufffd\ufffd׽\ufffd\u001dn\ufffdΦ\ufffd\u0001\u000cڢ\ufffdʭ\u0002\ufffdS\ufffd\ufffd\ufffd\u0005}\u001f\ufffdX\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdm\ufffdO\ufffd\ufffdm\ufffd\ufffd\u001bፖ}}\ufffdkph/\ufffd\u0015\ufffd\u0000\ufffd;K\ufffd\ufffdvB\ufffd\ufffd\ufffd\ufffd\ufffdf\ufffd\ufffd-\ufffd\ufffd\u001f\ufffd\ufffd\ufffd}\u0005`\u0013^\ufffd\ufffd\ufffd\ufffd\ufffd1\ufffd\u0010^\ufffd\u0005\u001e5\ufffd\ufffd#zi\ufffd\ufffd\ufffd\u0015\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd[\u0007\ufffd\ufffd\ufffdt\ufffd\u0004f\ufffd+\ufffd\ufffd\ufffdN)T\u000c\ufffdY.f\ufffd\u000b\ufffd\ufffdV\ufffd4\ufffd\ufffd:\ufffd-.xC\ufffd\ufffd\ufffdb\ufffdʻfD,\ufffd\ufffd\u0004\ufffd\ufffd3\u001c\ufffdᵗ\ufffd{S$|\ufffd\ufffd\ufffd%\u001dB\ufffd^\u0001\ufffdn1\ufffd\ufffd`f\ufffd\u001bn\ufffd\ufffd4\ufffdpc\ufffd\ufffd\r\ufffdC\ufffd6\ufffdG\ufffd\ufffd\ufffdÖ\ufffd\ufffd*\ufffd9X\ufffdORQ\ufffd\ufffd\ufffd\ufffdL\u000eZ\ufffdɎ\u0001V\ufffd2\ufffd`\ufffd\ufffd\ufffd\ufffdQ\ufffd\ufffd^\ufffd\ufffd0z\u0007\u0018\ufffd\u000e\ufffd0$4d\ufffd\ufffdw̐Kf\u0026\u001d\ufffd=\ufffd\u0007fR\u0017U\ufffd\ufffdQj\ufffd\n\ufffd\u0013\ufffd\u0019\ufffd\ufffd\u001b\u001e\\\ufffd\ufffd}\ufffdJl\ufffd\ufffd\ufffdAHP\ufffdEj\ufffd\ufffdd\ufffd\n\u001d\ufffd\ufffd\u003c\ufffdd3\ufffd*ф\u0015\ufffd\u001e\ufffd8hb\u0012\ufffd\ufffdV\ufffd\\\ufffdf\u0017B\ufffd(\ufffd4\ufffd\ufffd\ufffd\u001d'\ufffdve\u001b46F\ufffd\ufffd\ufffd5X\ufffd\ufffdp\ufffd\ufffd퐽\rmO_\ufffd\ufffd\ufffd\ufffd\u001dl\ufffdD\ufffd+\ufffd9O\ufffd'\ufffd$2\ufffd6\ufffd\ufffd\ufffd|q\ufffd\ufffd\ufffdhm\ufffd\ufffd\ufffd\ufffd\ufffdF\ufffd\ufffd\ufffd ;\u001e=f/\ufffd,k\ufffd\ufffd#^\ufffd\ufffdv\ufffdb\ufffdѰ\ufffdB\ufffdy\ufffd=\ufffd\ufffd2\ufffde\ufffd\u0014\ufffdOqN\ufffd\ufffd\ufffd͹v\u003e\ufffd\u0011JQ^\ufffd|@\ufffd\ufffd\u0019w,C\ufffd*(5E\"Np\ufffd\u0000M\ufffd'\ufffd?%Ky\ufffdg\ufffd\ufffd\u003e\ufffd|,\ufffd\n\ufffd\ufffdW\u000bvr\ufffd9F9%\ufffd\ufffd\ufffd1\ufffdT\ufffdr\ufffd\ufffd\ufffd;e\ufffd\ufffd:\ufffd\ufffdX\ufffdI\ufffdLX\u0004\ufffd}va\ufffd\u000fx\ufffd\u0018\u0019t\ufffd\u0019-X•\u0002B\ufffd\ufffdOk۾\u001a\ufffd\ufffd\ufffd\ufffd\u001c\u0003\ufffd]\ufffd\ufffd\ufffd\u0014sF\ufffd\ufffdw{{\ufffd㻿\u001fBN;!\ufffd\ufffd#B\ufffdCIOM\ufffd\ufffd/)\u000eك\u0011\ufffd,d\ufffd\ufffdG\ufffdw\ufffdxK\ufffdA\n2\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd^\ufffd:2b\ufffdA\ufffd9\u0014d\u0026\u0013\u0001\u003c\ufffd\ufffd%\ufffd\ufffd\ufffd\u0002O\ufffd\u0014\ufffd\u001a\r];v\ufffd\ufffd\u001cp2\ufffdr\ufffd\ufffd\ufffd\ufffdx\ufffd\ufffd\ufffd\ufffd\ufffd1\u0001i!\ufffd\u001ch\u0012%/E\ufffd=\ufffd\ufffd\ufffd\ufffd \ufffd\u000bp\u0001\ufffd\u003eW\u0016\u000f\ufffd\ufffdL\u0012_\ufffd'\u0001D\u000f\u0017\ufffd\u0012\ufffd\u001a\u0012\ufffd\ufffd@\u0002\u0014\ufffd\u0002tN\u003c\ufffd\u003c\ufffd\u0004\ufffdӮ\ufffd)\u0001\u0005:\ufffd]\ufffd\u000bpVӎ3m\u003cz\ufffd\ufffd\ufffdٹUPg\ufffd:\n\u003c\ufffd\ufffd\u0000\t\ufffd\ufffd2\u0018\u001a\ufffd\ufffdR`\ufffd\u0001C\ufffd5\ufffd\u000emx\ufffdA\ufffdWt\ufffdV@a\ufffdnn\ufffdK@\u0007\u0000jA%\ufffd\u0001\u001e\ufffd\ufffd\ufffdPe\ufffd\ufffdp\ufffd\ufffd\u000b4\ufffd\t\ufffd\u0018L\rz@꥘\ufffd\ufffd\u0000\ufffd[\ufffd\ufffd\ufffd\u001f\u000f#ǝ0\ufffd\ufffd\u0006\ufffd\ufffd}\u0026˔}ȄP]\ufffdPfrI^Ȼ\u001c\ufffd\u001b\ufffd\ufffd\u001bs\u0026\ufffd\ufffd\ufffdZ\ufffdƲt\u0019\ufffd\u0018,\u001b\ufffd\u0016\ufffd\ufffd߸\ufffd\u0014\ufffd\ufffd\ufffdI\ufffd\"N\ufffd\ufffdF\ufffd\ufffd\ufffds\ufffdi\ufffdI\ufffdD\u003c/\ufffd\ufffd\ufffdz*\ufffd.\u0019\u00121\ufffd;\u00184Gh\ufffd\u0014\u000b\ufffd1[\ufffdD\ufffd\ufffdǬ7N\ufffd\ufffd\ufffd,դ\ufffdyI\u0019\ufffd\ufffdM$WD^\ufffd\u0016\u0000s\ufffd\ufffd\u0001\ufffdA'\ufffd\u001cu\u0004L\u0013{\ufffdF\ufffd`\ufffd\ufffd\ufffd]\ufffd3{\ufffds\u003e񱉷\"\ufffd8\ufffd\u0017\ufffd\u0005\ufffd\u000e-u@ת\ufffd\"S\ufffd\ufffd2P\ufffd۸\ufffd\ufffd\ufffd䀠\u0013\ufffd\ufffd\u001d\u0018\u000cC\ufffdm\ufffd\ufffd\ufffdk\ufffd ,*\ufffd\ufffd2\ufffd\u0010c\ufffd\ufffd\ufffd\ufffd\ufffdG4\ufffd~\ufffd$\u0013!\\!\u0007FȂ\ufffd\u0003~\u0010d\u0001P~\ufffd\ufffd\ufffd\u000c\ufffdQ4\u0003\ufffd\ufffd\\3\ufffd\ufffd\ufffd\ufffd[\u0000σ\u001f\u000f\u003cG\ufffd\ufffds\ufffd\u0011\u003c\ufffd\ufffd\ufffdq\ufffd7\u0012\ufffd\ufffd\u0012T\ufffd\ufffd\ufffd[d\ufffdЌN\u0004\ufffd\ufffd\ufffd\ufffd\ufffd'\ufffd\tp\u003c\ufffd\ufffd\u0010\ufffd@se\ufffd\ufffd\"\ufffd\ufffd\ufffdP\ufffd\rT\ufffd+\u003c\ufffd\ufffd58_\u0018\ufffd\u0015\ufffd\u0014\ufffd\ufffdM\ufffdRf+\ufffd\ufffd_\ufffd\u0008=xx\ufffdc\ufffd\ufffdνo\ufffd\u0015h\"^\u0002\ufffd8L\u0011\ufffd\ufffd\u003c\ufffd\u0003A\ufffd[@\ufffd\ufffdw@̎|\ufffd[Y\ufffd7\ufffd 71\u0006\ufffd\ufffd7/)\ufffd\ufffdXiXK\ufffd€m\ufffd\ufffd뜕\ufffdPyjǒ\ufffd\ufffdI\ufffd\ufffdٮC\u00004\ufffd\u0011\ufffd\ufffd\ufffd\u0008~\ufffd\ufffd_\ufffd#՘\ufffd\u0001c\u0001 \ufffd\ufffdb\ufffd%`\ufffd\ufffdR\t\ufffd\ufffdJ\ufffd\u0000,\ufffd\ufffd畖\ufffd\u0026\u000b5E7\ufffd\ufffd\ufffd\u001e\ufffd\u0013\ufffd\u0001\ufffd\u0004q\u000c\ufffd8\ufffd%Q\ufffd\ufffd@\ufffd\u0016soۈ\ufffd\ufffdt!\ufffd\ufffd\u0001\u001dz\ufffd\ufffdZp+\ufffd\ufffd\u0018\ufffd\ufffdvK_p\ufffd;q΁\ufffd=8\ufffdQ\nY,\ufffd\ufffd\ufffdw(\ufffd,\ufffd\ufffd\ufffd\ufffd.\ufffdFW[_\ufffd\u0011\ufffd\u0000\ufffdk*\u001e\ufffd\ufffdD\ufffd\ufffd)\ufffd\\U\u001d\ufffd\u0018t\ufffd\ufffd\ufffd\ufffd\u001f\ufffd,)\u0006\ufffdj\ufffd@ޣBY#\u003c\ufffdK%\ufffds\ufffd\ufffd\ufffd\u0014\ufffd\ufffdW\ufffd\ufffd\ufffd\u003e\nu\ufffd\ufffd\ufffd\u0002 \ufffd\ufffd$Z\ufffdN.N\ufffd,\ufffdt~\ufffd\ufffd\ufffd\ufffd_\u0026\u0017g\ufffd\ufffd\ufffd\ufffd?\ufffd\u003e\ufffd\ufffd\ufffd7\ufffd/\ufffd\u0007\u001f_\ufffd\u000f\u001d\ufffd*k\u0005\ufffdV\ufffdet\ufffdؙ.\ufffd\ufffd}\ufffd\ufffd\ufffd\ufffd+\ufffd\u0002j\ufffd{\ufffde\ufffd\ufffd+\ufffdA?n\ufffd\ufffd޳\ufffd\u0004\u0001\ufffd\ufffd-\ufffdit\ufffddr\ufffd4?\ufffd\ufffd\ufffdy\ufffd\ufffd\ufffd4jw\ufffdN\ufffd\ufffdU\ufffd\u0010S\ufffd\ufffd\ufffd \ufffd\u001f\ufffd\u001d\ufffd\ufffd\ufffd#\ufffd鄑%\ufffd)o\ufffdQ\ufffd\ufffd4{Q\ufffdr\ufffd\ufffdE8itS\ufffd\ufffd\ufffdA/\ufffd\ufffdFac\ufffd3\ufffd\ufffdJ\ufffd\ufffd\ufffd\ufffd\u0006l\u0017\u001e\ufffdaݰ]*a)4\ufffd\ufffd+\u0006\ufffda\ufffd\ufffd\ufffd\ufffd\ufffd޴P\u0007\ufffdR\ufffd=X\ufffd\ufffdhm\ufffdZ]\ufffd9v\ufffd\ufffd\ufffd\ufffd\ufffdn@Z\ufffd\u001e\u0017\ufffd\ufffdC\ufffd*LOў\ufffd,\ufffd\ufffdR)\ufffdw#\ufffdu\u0008\u001e'\u00142\ufffdU\ufffd6OaJ\ufffdji\ufffd\ufffd\ufffd\ufffdP\ufffdNn\u001c\u000ej\ufffd\ufffdo\ufffd\ufffd\ufffd:\u001f\ufffd\ufffd\ufffdxӗMv\ufffdoM\ufffdg\u0003\ufffd6\ufffd\u0018\ufffd\ufffd\ufffd\u001fv\ufffd\ufffd\ufffd\t{\u0007\u000e\u0017\ufffd\u0003{\ufffd\ufffd~\ufffd\ufffd\ufffd\ufffd\ufffdd\ufffdj\ufffd\ufffdn\ufffdc\ufffd\u001e\ufffdl\u000cH\ufffd\ufffd\ufffd\r’7\ufffd\ufffd玕hk(U}\ufffd\ufffdFz\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdw\ufffd\ufffd\ufffdo-\ufffd\ufffd\ufffd\ufffd\ufffd\\a\ufffd\u001as\ufffduKt\u001a\u000e\ufffd\u000fR\ufffdך\ufffd\ufffdSx%\ufffd֣\ufffd\ufffd\u001b\ufffd7\ufffd\ufffd\u0007ts\ufffdѫ*\u0000\u0000","raw":"HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 2651\r\nContent-Encoding: gzip\r\nContent-Type: text/html; charset=utf-8\r\nDate: Thu, 07 Sep 2023 15:34:10 GMT\r\nSet-Cookie: AWSALB=mMf3rdKgea2WMm1zlXRLuVlGnzwzbnCekLUEJC1ovH7rG4GvyvUURlqixQ3pFQSyaGvl4sxhMoY/ptxpaAO2y0WTm/iV8QTUpZ7boE6tcrN3sR+Ibwx2OvFmWPN+; Expires=Thu, 14 Sep 2023 15:34:10 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=mMf3rdKgea2WMm1zlXRLuVlGnzwzbnCekLUEJC1ovH7rG4GvyvUURlqixQ3pFQSyaGvl4sxhMoY/ptxpaAO2y0WTm/iV8QTUpZ7boE6tcrN3sR+Ibwx2OvFmWPN+; Expires=Thu, 14 Sep 2023 15:34:10 GMT; Path=/; SameSite=None; Secure\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n"}} +{"timestamp":"2023-09-07T21:04:13+05:30","url":"https://ginandjuice.shop/blog/post?postId=3","request":{"header":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Connection":"close","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=mMf3rdKgea2WMm1zlXRLuVlGnzwzbnCekLUEJC1ovH7rG4GvyvUURlqixQ3pFQSyaGvl4sxhMoY/ptxpaAO2y0WTm/iV8QTUpZ7boE6tcrN3sR+Ibwx2OvFmWPN+; AWSALBCORS=mMf3rdKgea2WMm1zlXRLuVlGnzwzbnCekLUEJC1ovH7rG4GvyvUURlqixQ3pFQSyaGvl4sxhMoY/ptxpaAO2y0WTm/iV8QTUpZ7boE6tcrN3sR+Ibwx2OvFmWPN+","Referer":"https://ginandjuice.shop/blog/","Sec-Ch-Ua":"\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"macOS\"","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"same-origin","Sec-Fetch-User":"?1","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"GET","path":"/blog/post","scheme":"https"},"raw":"GET /blog/post?postId=3 HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nConnection: close\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=mMf3rdKgea2WMm1zlXRLuVlGnzwzbnCekLUEJC1ovH7rG4GvyvUURlqixQ3pFQSyaGvl4sxhMoY/ptxpaAO2y0WTm/iV8QTUpZ7boE6tcrN3sR+Ibwx2OvFmWPN+; AWSALBCORS=mMf3rdKgea2WMm1zlXRLuVlGnzwzbnCekLUEJC1ovH7rG4GvyvUURlqixQ3pFQSyaGvl4sxhMoY/ptxpaAO2y0WTm/iV8QTUpZ7boE6tcrN3sR+Ibwx2OvFmWPN+\r\nReferer: https://ginandjuice.shop/blog/\r\nSec-Ch-Ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"\r\nSec-Ch-Ua-Mobile: ?0\r\nSec-Ch-Ua-Platform: \"macOS\"\r\nSec-Fetch-Dest: document\r\nSec-Fetch-Mode: navigate\r\nSec-Fetch-Site: same-origin\r\nSec-Fetch-User: ?1\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Content-Encoding":"gzip","Content-Length":"3942","Content-Type":"text/html; charset=utf-8","Date":"Thu, 07 Sep 2023 15:34:13 GMT","Set-Cookie":"AWSALB=85BJo1Zpdk9Zp+yPBrFftDnRpkda2GE1/JD01eSP9Ex//O3wSyqO/AF6ykA8rziJvN/Q7QNg9UV1oNa53YrhOnHvoGPe6yk0hzUoN1V5Rcp/SYNljF/y+T7fkl7C; Expires=Thu, 14 Sep 2023 15:34:13 GMT; Path=/, AWSALBCORS=85BJo1Zpdk9Zp+yPBrFftDnRpkda2GE1/JD01eSP9Ex//O3wSyqO/AF6ykA8rziJvN/Q7QNg9UV1oNa53YrhOnHvoGPe6yk0hzUoN1V5Rcp/SYNljF/y+T7fkl7C; Expires=Thu, 14 Sep 2023 15:34:13 GMT; Path=/; SameSite=None; Secure","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"body":"\u001f\ufffd\u0008\u0000\u0000\u0000\u0000\u0000\u0000\ufffd\ufffd[\ufffdn\u001c7\ufffd}\ufffd\ufffd\ufffd;\u0018\ufffd\u0006\ufffd\u0012I\ufffd\u003cHj×\ufffd\ufffd\u001c[\u0016\ufffd\u001e\u0004\ufffd\ufffd\ufffd]\ufffd\ufffd\ufffdUU\ufffd!Yݮ\u0003?\ufffd3f\ufffd9\u001fp~\ufffd|J\ufffd䬽\ufffd*UK}\ufffd\u0012\u0007\u0018`\ufffd\u0008\ufffd.\u0016\ufffd\ufffd/k\ufffdX\ufffd\ufffd\ufffd\u0017o\ufffd\ufffd\ufffd\ufffd\ufffd;\ufffd\ufffd\"\ufffd\ufffd\ufffd4\ufffd%\ufffd\ufffd4S2\r?\ufffd1\ufffd\ufffd\ufffdȬ\ufffd\ufffdM\ufffdr\ufffd\ufffd\ufffdr\ufffd\\\ufffdh\ufffd\ufffd\ufffdĹ\ufffdKd\ufffdF\ufffd\ufffd\ufffd1\u0006\ufffdU\ufffd\ufffd\ufffdM\ufffd\\\ufffd\ufffd\ufffdG\ufffdH\ufffd\ufffd{\u00072\ufffd\ufffdM\ufffdP^\ufffdR\u0016\ufffdl\ufffd\ufffdjU\u0019\ufffd\u0007\"1\ufffdW\ufffd?\u001b\ufffdt곳T-u\ufffdF\ufffd0\u0014\ufffdSv\u0004\u000e\ufffdC\ufffd\ufffdJ3\ufffdQs\ufffdՕ\u0017\ufffd\u0026g\ufffd\u001eC\u001f\u001c\u001ed\ufffdǠ\ufffdrS\u0015 \u003e\ufffd\ufffd\u0006\ufffd\ufffdIXqw\u0012\ufffd\ufffd\u0014\ufffd \ufffd\ufffd\n\ufffdy\ufffd\ufffdO\u003eȥ\u000c\ufffd\ufffdM\ufffde\ufffd\ufffdsi:\u001c};\ufffdv\u001bU\ufffd}\ufffd\ufffdO\ufffd+\ufffdm#^\ufffdF\ufffdij\ufffd,\ufffd\ufffdK]\ufffd\ufffd\ufffd\ufffdN\ufffd\ufffd5\ufffd%\ufffde\ufffd:\ufffd\ufffd\u0005\u0001\u0007\ufffdk \ufffd\ufffdLڈr1\ufffdU\ufffd#\ufffd\ufffd\ufffd\ufffd\ufffd٠o\ufffd\ufffdz\ufffd`*\ufffdڔ\"ɥsg\ufffd\ufffd\ufffdQ\ufffd\u0002\ufffdxsc\u0001/\ufffdn\ufffdџ\ufffd\ufffdv\u0002\ufffdI\ufffd\ufffd\\ϔ\ufffd^\ufffd\ufffdX\ufffdy\ufffd\ufffd0\ufffdX\ufffd\ufffd\u0000\ufffd\ufffdN$\ufffd\ufffd}\ufffd\ufffdT\ufffd\ufffd\u001b+\ufffdr^\ufffd\u000b\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd]\ufffds\ufffd\ufffd0D\ufffd`̍72p\ufffd*ىBX\u001e\u0008S\u0016\u0006\u0010\ufffdi\ufffd%\u0000\ufffd\ufffdY\ufffd\ufffd\ufffd\ufffd\ufffd8\u0013\ufffd\ufffdk\ufffd\\{\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd}\ufffdѲ\ufffd\ufffd\ufffd\ufffd\ufffduu6\ufffds\ufffd\ufffd\ufffd\ufffdÇ\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdW\ufffdEn\ufffd\ufffd΄\ufffdf\ufffd\ufffd\ufffd\ufffd\ufffd_\u000fg/\ufffdΓ\ufffdOZ\ufffd\ufffd\ufffdJ_\ufffdy\ufffd:;;x\ufffd\ufffd\ufffd+I\u003c\ufffd\u000eʫ\u001e\ufffd\ufffd\u0013qk\ufffdh\ufffd\ufffd񆱯\u0006\ufffd\ufffdڋ\u0006 \u0017\ufffd\u000e\ufffd\u001b\ufffd3Ŧ{\u0000\ufffdC\ufffd\u001b\ufffd2\ufffd\ufffd\u0017L\u000c\ufffdۉŃ\u0000\ufffdg\ufffd\ufffd\ufffd\u0006\ufffd\u0004\ufffd0\ufffd\u0002\ufffdԘ\ufffd\u0019\ufffd\ufffd2Ĵ\ufffd\ufffd\u00062\u001b\ufffd9\u000et#8\ufffd\ufffd\ufffdb|\u000bى\ufffd\ufffd\ufffd\ufffd\u0014;\ufffdh\u000f\ufffd\ufffd\ufffd[\ufffd\ufffdf\ufffdy\u000bm\ufffd\ufffd\u000b\ufffd\ufffd(:,\ufffd݉\ufffd\ufffdB\ufffd\u0026\u003cmp\ufffd[$s\ufffd\u0012O\ufffd\ufffdֳ\ufffd{\u0004\ufffd\ufffd\ufffd\ufffd\u0004Ȕ\ufffd\u0013\ufffdaMZ'\ufffdmU\ufffd\u001a\ufffd\ufffd]6\ufffd\ufffd\ufffd\t\ufffd\ufffdUڱ9c\u001e)\ufffd\ufffd+\ufffdױ%g\ufffd\ufffd\ufffd\ufffd[r(ol\ufffd\ufffd\ufffd;\ufffd\ufffd\ufffdo\ufffd\u0012\ufffdє\ufffd\ufffd\ufffd\u0026\ufffd$\ufffd.\ufffdH'=\ufffd\u0014\ufffd(\ufffd#i\ufffd\ufffdbz\ufffd\ufffd־\ufffdN\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdv\ufffd\u0018\ufffd\ufffdX\ufffd\t\ufffd\ufffdbʽD\ufffd.\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd{\ufffd}[\ufffd\u001bp\ufffd\u000b\ufffd\u001aU\ufffd.\ufffd\ufffd1ӽ\u0013j_\ufffd\ufffd4\"\ufffd\ufffd\ufffdXߏ\ufffd\ufffd\ufffd\ufffd\u0015\ufffdM\ufffd\ufffd\ufffd\ufffd\u0018\u0001'\ufffd\ufffd\ufffdJ)\ufffd0\ufffd\ufffd;\ufffd\ufffd\ufffd2t\ufffd\ufffd\ufffd\u001f\ufffdw\u0016\ufffd\ufffd\ufffd#o\u0016\ufffd\\a7\ufffd6j\ufffdbA\ufffdb\ufffd\ufffd\u0013f\ufffd\u0003\ufffd\ufffd\ufffdQ\ufffdt\ufffd5\u0001nI\ufffd\u001b\ufffd7\u0014\u00167fqjF\ufffdB\r\n%\ufffd\u00114\ufffd\u0013R\ufffdβ\ufffd@\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd:\ufffd\u000f\ufffdy(\ufffdG\u0015\ufffd\ufffd%t\ufffd\ufffdp\ufffd)\ufffdx=\ufffduq\ufffd\ufffd\ufffd\u0016N\ufffd\ufffd-o\ufffdC\ufffd\u000c-N\ufffdغ\ufffd\ufffd\n\ufffd\ufffd\u0005m\ufffd4\ufffd\u000c\ufffd\ufffdL\ufffd\ufffd[t}\ufffdz~ݿ\ufffd\ufffd\ufffdl\ufffd\ufffd\u001d0}u\ufffd\u0015+\ufffd\ufffd\ufffd\ufffdy\ufffde\ufffdnIv\ufffd\ufffd+\ufffd\ufffd\ufffd\u0010\t4\ufffd:b4k\ufffd3P?\u0015pS\ufffd\ufffd\ufffd\ufffdsis\ufffd\ufffd\u001b\ufffd\ufffd42\ufffd\ufffd\ufffd$\ufffd'\ufffd 2=\u003c\u0012O\ufffdE\rF\ufffd\ufffd\u003c:\ufffd\\gs\ufffd̜\ufffdb\u0011[K\ufffd\u0016\ufffdP\ufffd\ufffd\ufffd'$\ufffd\ufffd|=\ufffdP-\ufffdJ\ufffd\r!\ufffd\ufffdG#\ufffd\u001a\ufffd\ufffd\u0003\u0011\u0005ȔUB.\ufffd۱8\u0017+Yz.\ufffdU\ufffdS\ufffd/\ufffdP\u0011\ufffdLz\ufffd\ufffdSԲ\ufffdA\ufffdҟ\n\u0017ſ\u000c\ufffdgE*\ufffd\ufffdx#\ufffd!\ufffdqb\ufffd\u003cwu\u000e\ufffd\ufffd\"\ufffd\ufffdz-\ufffd=^C\ufffd5\ufffd\ufffd\ufffd\ufffd\ufffd?R1#\ufffdH\u0001\ufffdL*\ufffdʡ\ufffd\u001b\ufffdw\ufffd\ufffd\ufffd\u0006\u0026\ufffd\ufffd\ufffd2S*\ufffdH\ufffd\u0005VJxY\ufffdFa,\u001eA\ufffd\ufffd\u000e\ufffd\ufffd\ufffd\ufffd̘\ufffdV\u0002=\ufffd*]xQ)S\ufffd\u001b\ufffd`|a\ufffd*#*\ufffd\ufffdX\"\ufffdR;\ufffd\ufffd\"\ufffdTr%\ufffd\n=U\u0010\ufffd\ufffd,\ufffd\ufffd\ufffd\ufffd\ufffd4 y\ufffd8\ufffd\ufffd;\u0016(\ufffd\ufffd\ufffd\u0016\ufffd\u000f\ufffdc\ufffd+E[\ufffd#W\ufffdh\u0004:F\ufffd\u0019\ufffddC\u0026\ufffd\ufffd\ufffd|%\u001bh\ufffdx\ufffdR\ufffd\ufffd6\ufffd\ufffd\u0002\ufffd\ufffd\u0010\ufffdӨ\ufffdB^\ufffd2%6\ufffd`\ufffd\ufffdQ\ufffd\ufffd9\ufffdi\u0002/\u0019\ufffd\ufffd6i5\ufffd\nK\ufffd%:9+~\ufffd0\ufffd\u001d2]X\u001a\ufffd\ufffd\ufffd_\ufffdݭR\ufffd\ufffdK\ufffd\u003c\u0018\ufffd\ufffd\ufffdT^\ufffdF2\ufffd\ufffd49F\ufffd/=\ufffdvЊ*ɔ$5\u0018\ufffdp\ufffdN\ufffd+k\ufffd\ufffd\ufffdV\ufffd\ufffd\u0003a`G\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd-Iۿ\ufffd\ufffdw\ufffd\ufffd\ufffd2U\ufffd`x\ufffd\ufffd\u00131k\ufffd\ufffdrؚa\u0005\ufffd\u000c[\ufffdT)*c\ufffd\ufffd\ufffdJ\ufffd$\ufffd\u00120S\ufffde\u0001\ufffd8\ufffd\u0010\ufffd\u000b\ufffd\ufffd@E\ufffd\ufffdC\ufffd\ufffdN\u0000\ufffd%\u000b\ufffd\ufffdy5\u001dcd\u0018 \ufffd\u0010\u000c\ufffd/,\u000cʁ\u0026\u003e\ufffd\ufffdgƣ\ufffds\ufffdB\u001fjG+\u001d\ufffd8\ufffd\ufffd\ufffd08\ufffdFW\ufffd]\ufffd\u0001\ufffd8v\u0002\ufffd\u000f\ufffd\\F\"95\ufffd\ufffd\ufffd\ufffd!CD\n\ufffd\u0000\u0014\ufffdഠ\ufffd\ufffdg\u0001\ufffd\ufffd\\\ufffdWb.\ufffd\u0016k\ufffdFL\ufffd\t\ufffd\ufffd\ufffdӲ\t\ufffd\ufffd\ufffdt\ufffd*d2ca\ufffd\ufffdf\"Χn\ufffd\ufffd\ufffdY \ufffd\u00042^\ufffdfEk\ufffdbӚε@\ufffd\u0013bH\ufffd\ufffd\ufffdΌ\u001c2\u0008p\ufffd\ufffd]VL{\u001c\ufffd\ufffdA!}\ufffd\ufffd]\u0017\ufffd\ufffd\ufffd\u0005)ׇʒf\ufffd\ufffdj5\u001b\ufffd\ufffd\ufffd\ufffd\u0002~\u0012\ufffdӀ\ufffdaD\ufffd--\ufffd_\u0003(\ufffd\ufffdKl\ufffd\ufffd\u0011\u000c\u0004\ufffd\ufffdmx®\ufffd\ufffd!ۊ\ufffd\u0026\no\ufffd1, \ufffd\ufffdD\u0008{@\"\"\u0003\ufffdy\u0013\ufffd\ufffd\u0002b\ufffdo\ufffd\u0004re-\ufffdsc;\u0006\ufffd38E\ufffd\rǭ\u0010\u000c\ufffd\u001c=\u0007\ufffdT\ufffd\ufffdk\ufffd\ufffd.\ufffd\u0026_\ufffdy\ufffdکI\u0017\ufffdH\ufffd/K\ufffd8\ufffd\ufffd:\ufffd\u000bf\ufffd0 [ J\ufffd\ufffd\u0005m\u0002W\ufffd\ufffd\ufffd\ufffd|\ufffd\ufffd.\ufffdCԕW}\ufffd\ufffd\ufffd}\u00151\ufffd\u003e\ufffd\ufffd\ufffd$\ufffd\ufffdQ\ufffd\ufffdHn,\u0006\ufffd\u00036|aXk\ufffd\ufffd\ufffdj\ufffd\ufffd\ufffd\ufffd$\ufffd\u0018@\ufffd`\u000bC\ufffd\ufffd\u0008\ufffd?*\ufffd`\ufffd\ufffd\ufffdiz=\ufffd\u001b\ufffd֕\ufffd+\ufffd\u0016P\u0005Ŕ\ufffd)\ufffd\ufffd\u0003\ufffd\ufffdw\ufffd|(\ufffd\ufffdx!S\ufffd\ufffd\u0000\u001e\ufffd\u0012\ufffdd\u0008\ufffd\ufffd!HєY\ufffd\ufffd\u000bc:!\ufffd8#\ufffd\ufffd\ufffd\ufffd\u00033J@i)\ufffd\u0010BD]f\u0026o\ufffdz\ufffdb\u001agFb\ufffd\ufffd\u001a\ufffd\ufffd\ufffd4\u0026\u000fFR\u000cC+\ufffdƾ\ufffd\ufffd\ufffdo\ufffdku\ufffdeI\ufffd\ufffd{\n\ufffd\ufffd\\\u0016\ufffd\ufffd\ufffd\u003c\ufffd\u000bVl\ufffd\ufffd\ufffd\n\u001cϮ\u0003\u0019\ufffd\ufffd\ufffdH\ufffd\\\u0026\ufffd\ufffd\u0008\ufffdz\ufffd\ufffdx9\ufffdY\ufffd$D!\u0018Й\ufffdI\ufffd\ufffd6ף\nP\ufffd\u0019\u0016\ufffd\ufffd\ufffd\u0007\u003exf\ufffd \u0008\ufffd\ufffd\ufffd\u0005\ufffd\u0004w)\ufffdϠ̑eC\ufffd\u0007r\ufffd\u001b S\ufffd\u0005Q_\u000bS\ufffd\ufffd\ufffdN\ufffd\u0004\ufffd\ufffd$넅\ufffdb\u0010\ufffd\ufffdf\ufffd\ufffd%\ufffd\ufffd\ufffd\u000bF%\ufffd\u000eXI2\ufffd\ufffd1`\ufffdw\ufffd\ufffdr\ufffd\ufffd\ufffdԔ\ufffd(\ufffd\ufffdW\u0014t\ufffd\ufffd**\ufffd\u0000\ufffd\u00172\ufffdxR!-\"\ufffdk\ufffd2g\ufffd\ufffdT;S\ufffd\ufffd\u001b1\ufffd̞\ufffdJp\u0008\ufffdm\ufffdU1\u003e\u0007\u0001\ufffd)ڍ\u000e\ufffd\ufffd𘠂\u0000\ufffdB\ufffd9\u000f\u0018\ufffd\ufffd\ufffd\u0014\ufffdK\ufffd\ufffd8*\ufffd\u0004\u0006\ufffd\u0013֘\ufffd\ufffdi\ufffd\ufffd\ufffd\"\ufffd\ufffdt\u001c+'\nFe,T\ufffd\ufffd\ufffd\ufffd\u000c\ufffd\ufffd\ufffdg \ufffd\u0014BLp\u0012B\ufffd\ufffdLQ\ufffd\ufffd$4\ufffd~\ufffd\ufffd6\ufffd^\ufffd\ufffd\ufffd\ufffdI;-@\ufffd2(E\ufffd6\ufffd\ufffd\ufffd\ufffd1J\ufffdy\u001a\u00177\ufffdץ\ufffd\ufffd\ufffdy[Ǡ\ufffd\ufffd|\u003e\ufffd(\u0007\ufffdY{\ufffd#\ufffd#\ufffd-j\ufffd\u001f{\ufffd\u0013qަ\ufffdX4 \ufffd\ufffdP\u0008yJ`\"\ufffdЁ\r8\ufffd\u003e\ufffd\ufffd\ufffd\u0026\ufffdIr]͌\ufffd\ufffd\ufffd\u000f\u0011f\u001c\ufffd\u0002I\ufffd\u0019\ufffd|\ufffdp\ufffd\u0017\\\u000f\ufffd\ufffdq)c\ufffdX\ufffd\ufffd\ufffdl\n\ufffd\u0015p\u0010У]\ufffd\ufffd'!ER\u0015\u001a\u0012\u0004D%\ufffd\ufffdB\ufffd\ufffd\ufffd t \ufffd\u0015\ufffd#\ufffd\u001eV\ufffd4\u0008\ufffd\u0014a\u0015\ufffd̦u\ufffduB\ufffd\ufffd'\u0008D\ufffd\ufffd\ufffd%\u0005\ufffd\ufffd\ufffdw+*\ufffd\ufffd\t\ufffd\ufffd\ufffd'\ufffd\ufffd \ufffd1\ufffd\ufffd\ufffdNFe\ufffd\ufffd\ufffdg\u000f\ufffd\ufffd\u0014\u0015I]\u0015\u001c\ufffdh\ufffd\ufffd\u0010\ufffdP\ufffdH\ufffd1fo\ufffd\u00011\u0002r\ufffd7G\ufffd\u000f\ufffd`\ufffd'\ufffd\r[\u003e\u0010/\rj1\ufffd\u0000{\ufffd\ufffd\ufffd\ufffd-\u0004^\u0012\u0004\ufffd$\ufffdD4\ufffdN\ufffd_\\\ufffd\ufffd\ufffd\u0010\ufffdo\ufffdaN\ufffd\ufffd\u001eu\ufffdS\u0011t\u003c\u0026\ufffd^1/m\ufffd\ufffd\ufffdg\n\u0018\ufffd,\ufffdV1w(\ufffd:d\u0005\ufffd\u001a2C}À\ufffd\ufffdĺ\ufffd\ufffd\ufffd΄\u0013\ufffd\u0013\u0012\ufffdy\ufffd\ufffd\u001b\ufffdU\ufffd\ufffd\ufffd\ufffd\u001dT\ufffd\ufffdܤ,8fõU\ufffd~4\u000b.\ufffd6(\ufffd\ufffd\u00083\ufffd\u001b\ufffd\u0008\u0002\ufffd`\ufffd\u0017¾\r\ufffd\ufffdU\u0002\u0017c\ufffd\ufffd\ufffd\ufffdF\ufffd;\ufffd\ufffd\ufffd}\n\u001e\ufffd\ufffd\u000c\ufffd\ufffd2:\ufffd\ufffd+\ufffdC\ufffdL\ufffd8\ufffd\u0005]\ufffd\\8\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd@uȲĂk\ufffd\u0018|\u001fJ\ufffdЪ\ufffdk\ufffdmd\ufffd\ufffd1Ss\ufffd\ufffd\ufffd\ufffdX\ufffd\ufffd\ufffd\ufffd:\ufffd\ufffd/\ufffd\ufffdZU\ufffde\ufffd\n\ufffd\tz\u0002*\u0013\ufffd\ufffd\u001dۈ\ufffd\ufffd\"\ufffd(\ufffd޹5E?\ufffd\ufffdX\u0016\ufffd\u0013\ufffdR.\ufffdv)e\u000c\ufffd\u000b\ufffd\ufffd^6\ufffd\ufffd\u0005\ufffd\"\ufffd\ufffd@\ufffdK\u000cf\ufffd~2\ufffdJ*\u001a\u0010\ufffd\ufffd2Ĺ\ufffd\ufffd9\ufffd\u003ePQ\ufffd\ufffdH;RJ\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd)\ufffd\ufffdݮc\ufffd\ufffd\u0013\ufffd$\ufffd\ufffdu\u000e\ufffd\ufffdG~w}\u0014q\ufffd\u0008S.%\u0002\u0008\ufffd_\u000e\ufffdc}\u001e\u001aL\ufffd\ufffd\ufffd?\u0017\u001a\ufffd\ufffd\ufffdCY\u0019w\ufffd\u003cӕ\u0013\ufffd\ufffd\ufffdq\ufffd\ufffdd\ufffdY\ufffd.V\ufffd\ufffd\ufffdt@qK/Ȳ\ufffd\r\u0010Wl7\ufffd\ufffdO\ufffd\ufffd\ufffdu-\ufffd\u001d\ufffd\ufffd2\ufffd\ufffd\ufffdo?\ufffdn\ufffdo\ufffd\ufffd\u001fFߞQ\u0004e_D\ufffd\t\u0006]r\ufffdD\ufffd/|\ufffd\ufffd\u0019\u003e\u0015z\ufffd_\ufffdpPT捈\ufffd[\ufffd\ufffd\ufffdö Q\ufffd\u0007\ufffdWtK\ufffd\ufffdF\ufffdЈho\ufffd\u0026\ufffdS\u001c\ufffd$\ufffd\u000f?\ufffdg\ufffd\ufffd\ufffd\ufffd1\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd3T\u003c\ufffd%r3\ufffd\u0008\ufffd\ufffd\ufffd\ufffdx\ufffd*\ufffd\ufffd\u00192\ufffdoR\ufffd\ufffd\ufffdJ\u0018\ufffd\ufffd\u001b\ufffd*\ufffdL\ufffdю\ufffdb\u0015\ufffdV\u003c\u0026E\nJ\ufffd\ufffdH!E\ufffd-e\ufffdBQ7\ufffd}(\u000b\ufffd\ufffdn\ufffd靟k\ufffd\u001fx)\ufffd\ufffd\ufffdO{\ufffd\ufffd\ufffd\u0003\u0008\ufffdz:\ufffd:;\ufffd\u003c\ufffd\u001f\ufffd\ufffd\ufffd\ufffd_\ufffd\ufffd\ufffds\ufffd\ufffd\ufffdI\ufffd\ufffdJ\ufffdV\ufffd/~\ufffdNQ\ufffdu)\ufffd,ܣ\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd1j\ufffdG\u0003f\ufffd\ufffd\ufffd\ufffdI\ufffd\ufffd\t'\u0007\ufffdg\ufffdڡ\ufffd\ufffd\u001f\ufffd\ufffd硛ژ\u001b\ufffd\ufffdi\ufffd\ufffdtʽ\ufffdK\ufffd\rh\ufffd\u0005ێ\ufffdoS\ufffdzj\ufffd\ufffdV\ufffd\ufffd5\t\ufffd^\ufffde\ufffd\ufffdhz\ufffdek\ufffd]\ufffd%s1\u0012\ufffd\ufffd\ufffd=.\u0014\ufffd\ufffdXP+\u001c\ufffdD\ufffd\ufffdh\u0026\u0004\ufffdc\ufffdf\ufffd\ufffd\ufffd\ufffd#ǭK\ufffd\ufffd\u0016\ufffd\ufffdN\u0006B)N\ufffd\u0002s4\ufffd^'(\ufffd\ufffd}S\u001f\ufffd\ufffdv\ufffdtaq\ufffd\u000eL(\ufffdt1\ufffd\ufffd\ufffd\u003c‚U|V\ufffd\ufffdĠ\ufffd\ufffd\u0019\ufffdP\ufffd'\ufffdt\ufffd\ufffd'(\ufffd\ufffd\ufffd\u001d\ufffd߸넽0\ufffd\ufffd\u0007w\ufffd\ufffd\ufffd\ufffdF]\ufffd\r\ufffdv\ufffd\ufffdZ\ufffd\ufffd\u0018\ufffdZ\ufffdrDM\ufffd\ufffd$\ufffd\ufffd\u0012\ufffd$c\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd[\ufffd\ufffdo\ufffd\u000b\ufffd\ufffd\ufffd(n\ufffd\ufffdd\ufffd\ufffdǦh\ufffdR\ufffd\ufffd\ufffd\u001d\u000f\ufffd4\ufffd\ufffd\ufffd\ufffdal$?\u0008'\u000b\ufffdx/3qv\u003e\u0010K\ufffd\ufffdx\ufffd[\ufffd\ufffd\ufffd\"\ufffde\u0017\ufffdO\ufffd\\\ufffdy\ufffd\ufffd\ufffd.|u\ufffd\ufffd\ufffd]\ufffd\ufffdw\ufffd/\ufffd߷_\ufffdps\ufffdb\u0000o\u000b\ufffd\u0014\ufffdOߵ\ufffd9\ufffd\ufffd׻\ufffd\u0001o\ufffd\ufffdh-\ufffd5\u001b,1ue\ufffdQ\u0018\ufffd\ufffd\ufffd\ufffd\ufffdY^\ufffdݿ\ufffdH\ufffd\ufffd1٨\ufffd\ufffd\ufffd\ufffda\ufffd9\ufffd~\ufffd,=\ufffd\ufffd\ufffd\ufffd\u003c}\ufffd\ufffd;\ufffd\ufffd\ufffd\ufffd܁G^\ufffdF\ufffd\ufffd\ufffd?\ufffdy\ufffdus\ufffd\ufffd?\ufffd\ufffd\ufffdU\ufffd+g\ufffdf\ufffd8ڦ\ufffd\u0002n\u0014\ufffd\u000c\ufffdA1\ufffd)5u\ufffd\ufffd\ufffd\ufffd{\ufffd\ufffd\u0016hIG\ufffd\ufffd\u0011:\ufffd\ufffd\ufffd\ufffda0\u001a\ufffd\ufffd\u0015\u0016Y\ufffdM\u000fu \ufffdU:B\ufffd\ufffd\u001a\ufffd\ufffdY\ufffdU\u003c\ufffd\ufffd\u0019\ufffdo\ufffdt\ufffd\ufffd\ufffd\ufffdiU\ufffdBډ\ufffd=\ufffdJ\ufffd\ufffdC\ufffd\ufffd+\ufffd\ufffd\ufffdS\ufffd\u0013\ufffdn\ufffd*4\ufffd3P~\ufffd=}\ufffd=\ufffd\ufffd繓\ufffd\ufffdw\ufffd}\ufffde=\ufffd\ufffd\ufffd]\ufffd\ufffd\ufffdt\ufffdܭ\ufffd\ufffd\n_߼\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u000b?q\t\n\ufffd\u0002\u0005\ufffdڧ\ufffd;Hv\ufffdk+[\ufffd\ufffd\ufffdn-f\ufffd\ufffds\ufffd\u000b\u0012H\ufffd\ufffdGYr\ufffd]\u0002\ufffdUe\ufffd\ufffd9\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u000cܧ\ufffd\ufffd\ufffdy\ufffdl\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd]\ufffd\ufffdW\ufffdq\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd48\u0019\u0017\ufffd\ufffd\ufffd\ufffd\ufffd.O\ufffd\ufffdL\ufffdL\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u0001j\ufffdl#\u00024\u0000\u0000","raw":"HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 3942\r\nContent-Encoding: gzip\r\nContent-Type: text/html; charset=utf-8\r\nDate: Thu, 07 Sep 2023 15:34:13 GMT\r\nSet-Cookie: AWSALB=85BJo1Zpdk9Zp+yPBrFftDnRpkda2GE1/JD01eSP9Ex//O3wSyqO/AF6ykA8rziJvN/Q7QNg9UV1oNa53YrhOnHvoGPe6yk0hzUoN1V5Rcp/SYNljF/y+T7fkl7C; Expires=Thu, 14 Sep 2023 15:34:13 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=85BJo1Zpdk9Zp+yPBrFftDnRpkda2GE1/JD01eSP9Ex//O3wSyqO/AF6ykA8rziJvN/Q7QNg9UV1oNa53YrhOnHvoGPe6yk0hzUoN1V5Rcp/SYNljF/y+T7fkl7C; Expires=Thu, 14 Sep 2023 15:34:13 GMT; Path=/; SameSite=None; Secure\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n"}} +{"timestamp":"2023-09-07T21:04:18+05:30","url":"https://ginandjuice.shop/about","request":{"header":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Connection":"close","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=FuPzkvEYylog+dQhM7EDdfOxlO4LNNlDYYFUu86LrSuM/kx3kGryxBucqnFtsBKkCp4hyX30SKkFqbN+LcVwM135w3aUgOugwu1lG3qZ1Hig6rAW0C0h0PS0EdtD; AWSALBCORS=FuPzkvEYylog+dQhM7EDdfOxlO4LNNlDYYFUu86LrSuM/kx3kGryxBucqnFtsBKkCp4hyX30SKkFqbN+LcVwM135w3aUgOugwu1lG3qZ1Hig6rAW0C0h0PS0EdtD","Referer":"https://ginandjuice.shop/blog/post?postId=3","Sec-Ch-Ua":"\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"macOS\"","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"same-origin","Sec-Fetch-User":"?1","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"GET","path":"/about","scheme":"https"},"raw":"GET /about HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nConnection: close\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=FuPzkvEYylog+dQhM7EDdfOxlO4LNNlDYYFUu86LrSuM/kx3kGryxBucqnFtsBKkCp4hyX30SKkFqbN+LcVwM135w3aUgOugwu1lG3qZ1Hig6rAW0C0h0PS0EdtD; AWSALBCORS=FuPzkvEYylog+dQhM7EDdfOxlO4LNNlDYYFUu86LrSuM/kx3kGryxBucqnFtsBKkCp4hyX30SKkFqbN+LcVwM135w3aUgOugwu1lG3qZ1Hig6rAW0C0h0PS0EdtD\r\nReferer: https://ginandjuice.shop/blog/post?postId=3\r\nSec-Ch-Ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"\r\nSec-Ch-Ua-Mobile: ?0\r\nSec-Ch-Ua-Platform: \"macOS\"\r\nSec-Fetch-Dest: document\r\nSec-Fetch-Mode: navigate\r\nSec-Fetch-Site: same-origin\r\nSec-Fetch-User: ?1\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Content-Encoding":"gzip","Content-Length":"2591","Content-Type":"text/html; charset=utf-8","Date":"Thu, 07 Sep 2023 15:34:19 GMT","Set-Cookie":"AWSALB=Prgzdf44LBH/gIvkpQ7RQidBUe8DBb1sOfSy2cOoObGCZj/Pw+JO24xtHjKhCruF8b6CtxRw/8xqmTUMB3Guew6HtEFfVT+oRIyi5OX8fbUB1fodnvla28WXfiAB; Expires=Thu, 14 Sep 2023 15:34:19 GMT; Path=/, AWSALBCORS=Prgzdf44LBH/gIvkpQ7RQidBUe8DBb1sOfSy2cOoObGCZj/Pw+JO24xtHjKhCruF8b6CtxRw/8xqmTUMB3Guew6HtEFfVT+oRIyi5OX8fbUB1fodnvla28WXfiAB; Expires=Thu, 14 Sep 2023 15:34:19 GMT; Path=/; SameSite=None; Secure","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"body":"\u001f\ufffd\u0008\u0000\u0000\u0000\u0000\u0000\u0000\ufffd\ufffdZ\ufffdr۸\u0019\ufffdߧ\ufffd\ufffd\ufffd(\ufffd\tŵ\ufffd6\ufffdF\ufffdL\u000e\ufffd\ufffdns\ufffd؝4\ufffd\ufffd@$$\ufffd\u0006\t\ufffd\u0000%\ufffd\ufffd}\ufffd\ufffd\ufffd5\ufffd(}\ufffd~?@ɔDIT\ufffd̴\ufffdx\u003c6\ufffdÏ\ufffd|\u0002\u0007\ufffd?{\ufffd\ufffd\ufffd\ufffd۟Xj35\ufffdn\ufffd\ufffd1\ufffd\u000cR\ufffd\u0013\ufffd\ufffd^\ufffd̯XZ\ufffd\ufffd0*\ufffd\ufffdU\u0019\u000b\u0013)\u003e\ufffde\ufffd\ufffdbc\"\u0013\ufffd\ufffd\ufffdx\ufffd\u0006\ufffd\u0018`\ufffdPCc\u0017J\ufffdT\u0008{\u0008\u0018\ufffd\u0000@s\u000e0\ufffd\ufffd\u000f \u0013\ufffd\ufffd\ufffdgb\u0018̤\ufffd\u0017\ufffd\ufffd\u0001\ufffdunEn\ufffd\ufffd\\\u00266\u001d\u0026b\u0026c\u0011\ufffd\ufffd\ufffd\ufffd2\ufffd\u000c\ufffd!NPb\ufffd\ufffd\ufffd\u0001\ufffdĥ,,3e\u003c\u000c\u001a\u0008]\u001a\ufffd\ufffd\ufffd\ufffd\u0001I(]d\u0000޿4\ufffdh\u0010\ufffd\u001d\ufffdA\ufffd\ufffdΎ\u0000c\u0017\u0005(\ufffd\ufffd\ufffdF\ufffd|\ufffd\ufffdh\ufffd\u0006\ufffd\ufffd\ufffdJ\ufffd\ufffd\ufffdI\ufffd0|\ufffd\u000b\ufffd\ufffdV\ufffdћ\ufffdd\ufffd\ufffdr\ufffdB\ufffd\\\ufffd\ufffd\u000eϊG\ufffd\ufffd\nLb\ufffd\ufffd.\u0006\ufffd_\ufffd\ufffd\u001f\ufffd\ufffd0\ufffdɂ\ufffdӐ\u0017E\u0003j\"gL\u0026à)\ufffd\u0006W==\"\ufffdR\ufffd,Vܘa\ufffd\ufffd%L\ufffdG\u00113\u001b\u001bܦb{\ufffd~.Ri\u0018~9K\ufffd\ufffdcQr+Ԃ\ufffd*\ufffd\ufffd\u0019Res1f\ufffdQɘ\ufffdSq\ufffd\ufffd\ufffd\"a\u0013]2+\ufffd\ufffd\ufffd\ufffd\u0016\ufffd\ufffdǦx\ufffd\ufffd(\ufffd\ufffd\u000b?D\ufffd`\ufffd\ufffd[\u0011\u0018\ufffd\ufffd\ufffdH!\u0015\u000e\ufffd\ufffd3\r͂DEIz\ufffd'z\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\r\ufffd\ufffdjZ\ufffd\ufffdgO\ufffdf3L7g\ufffd\ufffd\ufffdr\u001a\ufffdys\ufffd\ufffd\ufffd\ufffd\ufffd\u0018\u0006r\ufffd\ufffd6\ufffd\ufffds\ufffd5\ufffdZ{=[}p\ufffd)]skȸ\ufffd㻽\ufffdg=\u0019?\ufffd\ufffd\ufffd\ufffd\ufffd%]$ϕ\ufffd\ufffd\ufffda\ufffd\ufffd\ufffdT%8\ufffd\u000c9\u0008+\u001a\ufffdN\u001e\ufffd\ufffd\ufffdӖ\ufffd\ufffd\ufffd\ufffd\u0007\ufffd\ufffdme\ufffdB\ufffd\ufffdz90\ufffd\ufffdM\ufffd\u0013\ufffd\ufffd\ufffdk\ufffd\ufffdE]\ufffd\r}\ufffdB\ufffdu{u\ufffd\ufffd\ufffd\ufffd\ufffd;\ufffd\ufffd\u0002\ufffd\ufffd\ufffd/%\ufffd\ufffd%\ufffd\ufffd\ufffd\ufffd\ufffd{W֋Z\ufffd\ufffd\ufffd3ο\ufffdJO5\ufffd\u0015\ufffd;\ufffdF|\ufffd\ufffd\r(g\ufffd\ufffd\u0001\ufffdܞ\ufffd\ufffd8f\ufffd\ufffdRK\ufffd\ufffdZ9u:\u0011\ufffd\u0006K\ufffdnXQʌ\ufffd\u000b\ufffd\ufffdb\ufffd[ \ufffd\u003c\ufffd\ufffd-\ufffdˣǕ\ufffdp\n\ufffd\ufffdA\u0004\ufffd\ufffd\ufffd\u00134\ufffd\ufffdI\u0015[\ufffd\ufffd)k\ufffd\ufffd.\u0007\ufffd\u001e\ufffd\ufffdC\ufffd\t\ufffd\ufffd7\ufffd\ufffd\ufffd,c+\ufffd\u0015~|\ufffd+\u001b\ufffd\ufffd\ufffd/\ufffd\ufffd \ufffdԭu\tƨ\ufffd\ufffd+k\u0013\ufffdc]\ufffd6\ufffdqCj\ufffd\"\ufffd\ufffd\u0011+\ufffdl:\u001a\ufffd\ufffd-'ob+T}J\ufffd\ufffdu\ufffd\ufffd\ufffdտY\ufffd\t\ufffd\ufffdf\n\ufffd\u0004\ufffd\u000b{k\ufffdt[x\u0014\ufffd\ufffd\ufffdo\ufffdk\ufffd\ufffd\ufffdz\ufffdd\ufffd\u0019c\u0007\ufffd\ufffd\ufffd~]\ufffd_-X\ufffd\ufffd\ufffdP?\ufffd\ufffd\ufffd\ufffd}\ufffd\ufffdƼ\ufffd\ufffd\ufffd\ufffd\u0003F4C\ufffdJ!\ufffd\ufffd\u000e\ufffd\u001d\ufffd\ufffd\ufffd:ս\ufffd~\ufffd\ufffdv\u0026\ufffda\ufffd\ufffd\ufffdө\u00128\ufffd\ufffd\ufffd\ufffdr:\ufffdT\ufffdS\ufffd\"f\ufffd\u000b\ufffd\ufffd\ufffdW\u001aD;\u0003\ufffd\ufffd\u0010\ufffd2ܒXl\ufffdr\ufffd\u0019\u0019\u000b\ufffd%\ufffd\ufffdݛ\u000fg\u0008۫\ufffdݖ\n߄\ufffd`\ufffd\u000e9pX@\ufffd;|\ufffd\ufffd\ufffd\ufffd\u0015ǵ\ufffd\ufffd:!\ufffd]𮍑\ufffd\ufffd1\ufffd\ufffd\ufffdK=|\ufffd\ufffd\u0013^k\u001e\ufffd\u0005\ufffdj\u0008\ufffd\ufffd9\ufffd\ufffd=q`Uo8\ufffd^\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdP\ufffdP:\u0010I\u0006\ufffd\ufffd\ufffd߱͟\ufffd\ufffd7\ufffd\u003e\ufffd\ufffd\ufffd}:\ufffd37\ufffd\ufffd\ufffd!\ufffd\ufffdן\u001bX\ufffdU\ufffd!\ufffd\u001a\ufffd\ufffd\ufffdf2\u0011:\ufffd\ufffd(hv\ufffd\ufffd\u0003\ufffd\u000e;\ufffdn\ufffdؕХV\ufffde\u00152\tVh\ufffd\n\ufffdm\ufffd2\ufffd\u0008\ufffdy\u0012^R\ufffd\u0018Z\ufffd\ufffd\ufffde1\ufffdx\ufffdg\ufffd\u0003\ufffdU\ufffd\u0015\u000f\ufffd\ufffd2v\ufffdF4\ufffd\ufffd\ufffd\u000fTX\ufffdK=70\ufffdD\u000b\ufffd`\u001a\ufffdT\u00055\u000f\\\ufffd\ufffdY`\ufffd\ufffd\ufffd\ufffdۢ r\u001b\ufffd\ufffdν\ufffd\ufffdZ\ufffd%@Nj\ufffd\u0002\ufffdٴ\u001b3\u0013\ufffdBX)Q.\ufffd\ufffd\u0018Wv\u0018PC\ufffdf\"\ufffd\ufffd\u0008U]\ufffd\ufffdY\ufffd\ufffdFc`\ufffd\u0015\ufffd[\u0006\ufffd\ufffd^\ufffdk{\ufffd\ufffd+0֕\ufffd\ufffd\ufffd\ufffd\u0002\ufffdQTe\ufffd\u0004\ufffdD\ufffdc\u0001\ufffdq\ufffdq{1z\ufffdK\ufffd\r{\u0005\ufffd\ufffd\u000b\ufffdg\ufffdY\ufffds\ufffd'\ufffdo\ufffd\ufffdX\ufffdGv\ufffdS\ufffd\ufffd#\ufffdy\ufffd\u001f\ufffdߘ\u00163\ufffd\ufffdT\ufffd\ufffd2\u0004\u0003\ufffd\ufffds,d\u0010\ufffd\ufffdu\ufffd\ufffd\u0002\ufffd\ufffd\ufffd\ufffd\ufffd,\ufffdՙ\ufffd]8\ufffdC\ufffd\ufffd\ufffd\ufffd\ufffd\u0017\ufffdY\ufffd\ufffd\u0004\ufffdU\u0002\ufffdw=\ufffdF\u0008\t#;\ufffd\ufffd\ufffd\ufffd\ufffdE\ufffd]\ufffd!\ufffd/*\ufffd\ufffd=\ufffd\u0026\ufffd\ufffd]g\u0026\ufffd\ufffdk\ufffdɸR\u001ee\u003c\ufffd,\ufffd{\ufffdΐ\ufffd\ufffd\ufffd\ufffd0l.m\n\ufffd\ufffdRp%\ufffdF}\u001ank245\ufffd\ufffds*}\u0003k\ufffd\raC\r\ufffd\u00144\ufffd\ufffdV\u0013\ufffdU\u0012\ufffdk\ufffd\ufffd\u001ci\ufffd\ufffdÑ\ufffdY\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdo\ufffd\ufffdQ\u001e൞\ufffd\ufffd/\ufffd\ufffd\u0016\ufffd2\ufffdgc\ufffd\ufffd\ufffd\ufffd}6\ufffd\t=\ufffd\ufffdk◶\ufffd\ufffd‚\ufffd\ufffdЙ̷\u0016\ufffd\ufffdnQ\ufffd\u003c\ufffd:\u0007\ufffd\u001c\u0015\ufffde̚}d)L]\ufffdx\ufffd5G\u003c\ufffd\ufffdT\u0005\ufffd5I\ufffd\ufffd\ufffd\ufffd \ufffd\ufffd0?F\u0011\u0019\ufffd\ufffd\ufffd\ufffd\ufffd\u000f\ufffdFc8\ufffd\ufffd\t\ufffd(\ufffd\u0011\r\ufffdN\ufffd\u001c\u0015U(f\\U܊\ufffd\ufffdЋp\ufffd\ufffd\u001dֺ\u0019\ufffd\n\ufffdR\n.\ufffd:kK\ufffdz\ufffdTa\ufffd\ufffd\u0013l\ufffd\ufffd\u0007\ufffd\ufffd\u0011\u001b\ufffd~\ufffdh\ufffd\ufffd\u000bͪ\u001d\ufffd;\ufffd\ufffdT\ufffd\u003e\ufffd\ufffd\ufffd)9\u0011\ufffdG(̗w\ufffdk6}v\ufffd\ufffd\u003e\ufffd,\ufffd\ufffdQ\ufffd\ufffds\ufffd3\ufffdV\u0019\ufffd\ufffdX\ufffd\ufffd\ufffd\ufffd\ufffd`D\ufffd\u003c(]\ufffd\u0018ꆪ\ufffd$\ufffd\ufffd{\u0008\ufffd\ufffd~c\ufffdc\u0014_\ufffdk\ufffd\ufffd_\ufffd\ufffd\u0014q\u000e\ufffd\ufffd\ufffd\ufffd\ufffd1\u000e\u0003_\ufffd\u001f\ufffd6uP\ufffd\ufffd\ufffdR\ufffdW\ufffd3\ufffd\ufffd\\\ufffd*\ufffd\ufffdLE\ufffd{\ufffd\ufffd\u0026\ufffd\u0000\ufffd#~\u0016\ufffd\ufffd\ufffd\ufffdޤ\ufffd\ufffd\ufffd?\ufffdA\ufffd7K\ufffd\ufffd*0!ZkL\ufffd[\ufffd\ufffde\ufffdj\ufffd\ufffd)\ufffd\ufffd䦴\u000fÊ\ufffd\ufffd\ufffd\ufffdٟ\ufffd(\ufffdyL,\ufffd\u0026P\ufffdt$\u0017s\ufffd\ufffd\ufffdH\ufffd\ufffd5\u0002\ufffd\ufffd\ufffd\ufffd\u0015\ufffd\ufffd6NE^\ufffd/\ufffd\rYEi\ufffd۶a\\\u001e\ufffdA6\u00033\ufffd\ufffd8\rFo1s\ufffdgػzԫ\ufffd{уmre4\ufffd\"\ufffd\ufffd\ufffd9\ufffd\u0004p\ufffdr\u0016i\ufffdG\u0026\ufffd\u000b\\\u0011\ufffd\ufffd\ufffdY\ufffd\ufffd\ufffd\ufffd\ufffd\u000e\ufffd-\ufffdG\ufffd\ufffd\ufffd8\ufffdH\ufffdIDD\ufffd\ufffd:\ufffd\ufffd\ufffd'\ufffd\ufffde¡\u003e\ufffd\ufffd\ufffdn=\u001c7\ufffd`\ufffdDkx\ufffd=\ufffd\ufffdf+\ufffdo\ufffd\ufffd\u0005ن\ufffdĄzy{\ufffd~\ufffd\ufffd\ufffd.$\ufffdk\ufffd\ufffd\ufffd\ufffd\ufffdP\ufffd\ufffde\ufffd\ufffdKR\ufffdV\ufffd\ufffdlL\ufffd\ufffd\u0018\ufffd\ufffd\ufffdpu\ufffd{@\u0014\ufffd_\ufffd\u0004\u0018\ufffdwΉ̑\ufffd\ufffd\u0018\ufffd\ufffd\ufffd\ufffdCy\ufffd\ufffd\ufffdԩ\u0008\ufffd\ufffd\ufffd\u001c#6\ufffd\ufffd\ufffd\ufffdXUFΜ\ufffd\u001ar\u000c\u0004Hq\ufffd6\u00116XG\u0026]Rl6\u0002o\ufffdV\n*\\\ufffd\ufffd\ufffdw\ufffdL\ufffdg\ufffd\ufffd4\ufffd\ufffd{\ufffd\ufffd\ufffd+b\ufffdo\ufffd/M\ufffd\ufffd2\ufffd\ufffd\ufffd\ufffd\u001a{\ufffd7\ufffda}\ufffd\u0005,ᖇ\ufffd@y\ufffdE\ufffd\ufffd\ufffdC\ufffd\ufffd@\ufffdE{\u0003\ufffd\ufffd\ufffd˼p\ufffdΧJ\ufffd\ufffd\ufffd)\u0013\u0017\u0019GU\ufffd\ufffdh\ufffd\ufffd\ufffdB\ufffd\ufffdRR\ufffd\ufffd\ufffd'7\ufffd\ufffd\u0010\ufffd\u001e\ufffd+i\u0005\u001f\ufffd\u0012Y%\ufffdj\ufffd\ufffdDl\ufffdI\ufffd(\ufffd\ufffdK\u003e\ufffd\ufffdT.\ufffd/\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u0017\ufffd\ufffdw\ufffd\ufffd2~\ufffd\ufffd\ufffd\ufffd\ufffd'\ufffdt\ufffdsU_?mt\ufffdݱ`I\u0026\ufffd\ufffd|əA\ufffd\ufffd?\ufffd\ufffd\ufffdH\ufffd\ufffd\u0013\u0018\ufffdD\ufffd\ufffdЏ\u001c\ufffd^\ufffd\ufffd\u001d\ufffdY[\ufffd0F\u003c\u0014\ufffd\ufffdN\ufffd\ufffd\ufffd˯\ufffd\ufffd\ufffd\u000e\ufffd3\ufffd\ufffd]Xcn\u001f\ufffdz\u000f\ufffd2\ufffdt.=ӓ\ufffd\ufffd\u000f\ufffd\ufffd\ufffdN\u0018y\ufffd\ufffdM\u0011\ufffd\ufffd79\u000f\ufffd^l5\ufffd\ufffd\ufffdpV˦XP\ufffd\u0012z8\ufffd\ufffd3\ufffd\ufffd\"\ufffd\ufffd\ufffd=\ufffdr`k\t,A\ufffd`WBX!\ufffd\ufffdm\ufffd`\ufffd\u001f\ufffd\u0019\u0012Y\ufffdMC\ufffd\u0000T\ufffd$\ufffd\ufffd\ufffd\n6x֘b\ufffd\ufffd\ufffds\ufffd['\u001d\ufffd\ufffd\u0018=FY\ufffd@V\ufffd\ufffd\ufffd\u001fO\ufffd\ufffd\ufffd\ufffd\ufffdRt\ufffd\ufffd \ufffd\ufffd\ufffdU'\ufffdґW\ufffd1\ufffdB\ufffd\ufffd\ufffd֗\ufffdM\u000eFqo\ufffd\ufffd\u003e\ufffdXE=\ufffd\ufffd\ufffd\ufffd~\ufffd0\ufffdF.辱\ufffd\ufffd|\u00020\ufffd\ufffd?\ufffd\ufffduƚ9\ufffdK\ufffd\ufffd;Pv\ufffd\ufffd\ufffd\u000ejkJw^H\ufffd\ufffdsZ\u0013\u0012P??\"-y\ufffd\ufffd\ufffd?t\tSo3:\ufffdD_\u001e\ufffd\ufffd?ļo\u001f\ufffd|y\ufffd\ufffd}\ufffd\ufffd:{\ufffd\u0015p{\ufffd\ufffdq\ufffd\ufffd\ufffd\ufffd#;\ufffdF\ufffd\ufffdT\ufffd}\ufffdvz\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd;\ufffdF\ufffd\u0019\ufffd\u0000j\ufffd\ufffd\ufffd\ufffd+\u0000\u0000","raw":"HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 2591\r\nContent-Encoding: gzip\r\nContent-Type: text/html; charset=utf-8\r\nDate: Thu, 07 Sep 2023 15:34:19 GMT\r\nSet-Cookie: AWSALB=Prgzdf44LBH/gIvkpQ7RQidBUe8DBb1sOfSy2cOoObGCZj/Pw+JO24xtHjKhCruF8b6CtxRw/8xqmTUMB3Guew6HtEFfVT+oRIyi5OX8fbUB1fodnvla28WXfiAB; Expires=Thu, 14 Sep 2023 15:34:19 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=Prgzdf44LBH/gIvkpQ7RQidBUe8DBb1sOfSy2cOoObGCZj/Pw+JO24xtHjKhCruF8b6CtxRw/8xqmTUMB3Guew6HtEFfVT+oRIyi5OX8fbUB1fodnvla28WXfiAB; Expires=Thu, 14 Sep 2023 15:34:19 GMT; Path=/; SameSite=None; Secure\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n"}} +{"timestamp":"2023-09-07T21:04:25+05:30","url":"https://ginandjuice.shop/my-account","request":{"header":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Connection":"close","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=Prgzdf44LBH/gIvkpQ7RQidBUe8DBb1sOfSy2cOoObGCZj/Pw+JO24xtHjKhCruF8b6CtxRw/8xqmTUMB3Guew6HtEFfVT+oRIyi5OX8fbUB1fodnvla28WXfiAB; AWSALBCORS=Prgzdf44LBH/gIvkpQ7RQidBUe8DBb1sOfSy2cOoObGCZj/Pw+JO24xtHjKhCruF8b6CtxRw/8xqmTUMB3Guew6HtEFfVT+oRIyi5OX8fbUB1fodnvla28WXfiAB","Referer":"https://ginandjuice.shop/about","Sec-Ch-Ua":"\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"macOS\"","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"same-origin","Sec-Fetch-User":"?1","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"GET","path":"/my-account","scheme":"https"},"raw":"GET /my-account HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nConnection: close\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=Prgzdf44LBH/gIvkpQ7RQidBUe8DBb1sOfSy2cOoObGCZj/Pw+JO24xtHjKhCruF8b6CtxRw/8xqmTUMB3Guew6HtEFfVT+oRIyi5OX8fbUB1fodnvla28WXfiAB; AWSALBCORS=Prgzdf44LBH/gIvkpQ7RQidBUe8DBb1sOfSy2cOoObGCZj/Pw+JO24xtHjKhCruF8b6CtxRw/8xqmTUMB3Guew6HtEFfVT+oRIyi5OX8fbUB1fodnvla28WXfiAB\r\nReferer: https://ginandjuice.shop/about\r\nSec-Ch-Ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"\r\nSec-Ch-Ua-Mobile: ?0\r\nSec-Ch-Ua-Platform: \"macOS\"\r\nSec-Fetch-Dest: document\r\nSec-Fetch-Mode: navigate\r\nSec-Fetch-Site: same-origin\r\nSec-Fetch-User: ?1\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Content-Encoding":"gzip","Content-Length":"0","Date":"Thu, 07 Sep 2023 15:34:26 GMT","Location":"/login","Set-Cookie":"AWSALB=YQrYtEDgBJg08T9WcAOq2QBpXgV1lXFcJkxdUrJJT3bwm6199fl2Pa96roWYv8FnW2sr+bIcQUEV+98gRuQOfXRbsr5zMpj/eW72rpGJOfeDtsUozRwI+Qhi1WCm; Expires=Thu, 14 Sep 2023 15:34:26 GMT; Path=/, AWSALBCORS=YQrYtEDgBJg08T9WcAOq2QBpXgV1lXFcJkxdUrJJT3bwm6199fl2Pa96roWYv8FnW2sr+bIcQUEV+98gRuQOfXRbsr5zMpj/eW72rpGJOfeDtsUozRwI+Qhi1WCm; Expires=Thu, 14 Sep 2023 15:34:26 GMT; Path=/; SameSite=None; Secure","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"raw":"HTTP/1.1 302 Found\r\nConnection: close\r\nContent-Encoding: gzip\r\nDate: Thu, 07 Sep 2023 15:34:26 GMT\r\nLocation: /login\r\nSet-Cookie: AWSALB=YQrYtEDgBJg08T9WcAOq2QBpXgV1lXFcJkxdUrJJT3bwm6199fl2Pa96roWYv8FnW2sr+bIcQUEV+98gRuQOfXRbsr5zMpj/eW72rpGJOfeDtsUozRwI+Qhi1WCm; Expires=Thu, 14 Sep 2023 15:34:26 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=YQrYtEDgBJg08T9WcAOq2QBpXgV1lXFcJkxdUrJJT3bwm6199fl2Pa96roWYv8FnW2sr+bIcQUEV+98gRuQOfXRbsr5zMpj/eW72rpGJOfeDtsUozRwI+Qhi1WCm; Expires=Thu, 14 Sep 2023 15:34:26 GMT; Path=/; SameSite=None; Secure\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\nContent-Length: 0\r\n\r\n"}} +{"timestamp":"2023-09-07T21:04:26+05:30","url":"https://ginandjuice.shop/login","request":{"header":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Connection":"close","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=YQrYtEDgBJg08T9WcAOq2QBpXgV1lXFcJkxdUrJJT3bwm6199fl2Pa96roWYv8FnW2sr+bIcQUEV+98gRuQOfXRbsr5zMpj/eW72rpGJOfeDtsUozRwI+Qhi1WCm; AWSALBCORS=YQrYtEDgBJg08T9WcAOq2QBpXgV1lXFcJkxdUrJJT3bwm6199fl2Pa96roWYv8FnW2sr+bIcQUEV+98gRuQOfXRbsr5zMpj/eW72rpGJOfeDtsUozRwI+Qhi1WCm","Referer":"https://ginandjuice.shop/about","Sec-Ch-Ua":"\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"macOS\"","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"same-origin","Sec-Fetch-User":"?1","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"GET","path":"/login","scheme":"https"},"raw":"GET /login HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nConnection: close\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=YQrYtEDgBJg08T9WcAOq2QBpXgV1lXFcJkxdUrJJT3bwm6199fl2Pa96roWYv8FnW2sr+bIcQUEV+98gRuQOfXRbsr5zMpj/eW72rpGJOfeDtsUozRwI+Qhi1WCm; AWSALBCORS=YQrYtEDgBJg08T9WcAOq2QBpXgV1lXFcJkxdUrJJT3bwm6199fl2Pa96roWYv8FnW2sr+bIcQUEV+98gRuQOfXRbsr5zMpj/eW72rpGJOfeDtsUozRwI+Qhi1WCm\r\nReferer: https://ginandjuice.shop/about\r\nSec-Ch-Ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"\r\nSec-Ch-Ua-Mobile: ?0\r\nSec-Ch-Ua-Platform: \"macOS\"\r\nSec-Fetch-Dest: document\r\nSec-Fetch-Mode: navigate\r\nSec-Fetch-Site: same-origin\r\nSec-Fetch-User: ?1\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Content-Encoding":"gzip","Content-Length":"1793","Content-Type":"text/html; charset=utf-8","Date":"Thu, 07 Sep 2023 15:34:26 GMT","Set-Cookie":"AWSALB=bqZoSugibrUn2bfJXYlvAufNHb6V5/dDhGQEYNEgWk9i8ARnQb/nXdgJ2kUC/ruOt3kfOm5GioxnCSPhWXHQq7eqXcg+qfYT1aWoQTJiY/4PmV/OUGHZlL2oJDKx; Expires=Thu, 14 Sep 2023 15:34:26 GMT; Path=/, AWSALBCORS=bqZoSugibrUn2bfJXYlvAufNHb6V5/dDhGQEYNEgWk9i8ARnQb/nXdgJ2kUC/ruOt3kfOm5GioxnCSPhWXHQq7eqXcg+qfYT1aWoQTJiY/4PmV/OUGHZlL2oJDKx; Expires=Thu, 14 Sep 2023 15:34:26 GMT; Path=/; SameSite=None; Secure","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"body":"\u001f\ufffd\u0008\u0000\u0000\u0000\u0000\u0000\u0000\ufffd\ufffdY\ufffdn\ufffd6\u0014\ufffd\ufffd\ufffd`5\ufffdi\ufffd*\ufffd\ufffd^6\ufffd2\ufffd\ufffdm\ufffd\"M\ufffd\ufffdٚ\ufffd)(\ufffd\ufffd\ufffdR\ufffd\u0026Rv\ufffdH{\ufffd=\ufffd\u000eIY\ufffdmɒ{\u0001\ufffd\ufffdA\u0010\ufffd\ufffd\ufffd\u001f\u000f\u000fϕ\ufffd\ufffd{uq\ufffd\ufffd\ufffd\ufffd5JTʦ?L\ufffd\u0007\ufffd\ufffdIBpd\ufffd\ufffd!\ufffd\ufffd\u0013Jr2\ufffd\ufffd\ufffdHQ\ufffd!\ufffd\u001eÁf#\ufffd\u0017J\ufffd\ufffd\u0010\ufffdw\ufffdWC8\u0004\u0002\ufffd\t\ufffd\ufffd*\u0019\ufffd\t!\ufffd\u000fLC\u0000\ufffd\ufffd\u0002\ufffd\ufffd\ufffd\u0006H\ufffdˆ\ufffd\ufffd\ufffdΜ\ufffdE\u0026r\ufffd\ufffdPpE\ufffd\ufffd\ufffd\u0005\ufffdT\ufffdGdNC\ufffd\ufffd\ufffd#TH\ufffd\ufffd !\ufffd\ufffd\ufffdυ\ufffd@\ufffdaN3\ufffdd\u001e\ufffdNC\ufffd[\t\u0003\u001c\ufffdC@\"Ld)\ufffd\u001f\ufffdJg:\ufffd\ufffd\ufffd\ufffd\u0010n$\ufffd=`T\ufffd\ufffd\ufffd\u0014\ufffdS\ufffd-\ufffdcKu\ufffd\ufffd1\ufffd\u000b\ufffd\ufffd\ufffd#\ufffd\ufffd\ufffd\ufffd\u000bUQ\ufffd\ufffd\ufffdLĔ#\u0017\ufffd\ufffd\ufffd\ufffd8\ufffd^\ufffd\ufffd\u0005(\u0008]%\"\ufffdx\ufffd\ufffd^\ufffd\ufffd\ufffd\ufffdI \ufffd\u0012\ufffd\ufffd\ufffdY\ufffd@\ufffd\ufffd\u001c\ufffd\ufffdw\ufffdW\ufffdШ=\u000b\t\u0015\u0015\u001c\ufffd\u000cK\ufffd;\ufffdP܈X\ufffd`fc\ufffdY\ufffdm\ufffd\ufffd\ufffd\ufffd\ufffdJ\u0004\ufffd\u0018E\ufffdр\ufffdX\u0011V\ufffdy\ufffd8|\ufffd\u001bE\u000b\u0012 \ufffd\ufffd\ufffd\u0010\ufffd]a\u001f\u001as\u0012\ufffd\ufffdȑ\"RQ\u001ek\ufffd\ufffd\u003c\ufffdً\ufffdBʨ*-I\u001f\u0005h\ufffd\ufffdU\ufffd\ufffd\ufffdp}\u0014m\ufffd\u000e\u0012\u003c\u0015`Up\ufffd$\ufffd6\ufffd#\ufffd8\ufffd\ufffd\ufffdn\ufffd|4\ufffd\ufffd\ufffd|}\ufffdhm6\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd.\ufffd\ufffdʛ\ufffdO\ufffd\u0016\u0017\ufffd\ufffd\ufffd\u0019z\ufffd\ufffd\ufffd\ufffd}\ufffd\ufffdkm8^\u001f\u003ey\ufffd\ufffd\ufffd\ufffd\ufffd#\ufffdD\ufffd\ufffd\ufffdl\ufffd\ufffd(8}\ufffd\ufffd\ufffd\r\ufffd\ufffde\ufffd)c\ufffd\ufffd\ufffd\u000f\u001eֻ2\ufffd\ufffd\ufffdp\u000fD\ufffd\u0006\ufffd\ufffd\u0005ڢ\u001d\ufffd\ufffd\ufffd-\ufffd'\ufffd\ufffd\ufffdP\ufffd\u0004\ufffdF\ufffd= %\ufffdJ\ufffd\ufffd\ufffd{`Ӡ\ufffd\u0016s\ufffd6\ufffd\u0005\u0018\ufffd\ufffd\ufffd\ufffd\ufffd\u0003k\ufffd/\ufffdF\u0007-\ufffdڼ-\ufffd\ufffd)\ufffd\u0002[\ufffd]N\ufffd\rc\u0007^\u000bL\u000b\ufffd4\ufffd\ufffde\"\u0016\ufffd\ufffd@\ufffd\ufffd\ufffdp\ufffd\ufffd\n\ufffd8\ufffd\ufffd#\ufffdY\ufffdq\ufffd65G\ufffd\ufffd\ufffd\r\ufffd466\ufffdV\u000e\ufffd\ufffd]\ufffd,\ufffd)\ufffdK;jq\ufffd-HF\ufffd\ufffd\u000c#^n\u001d\u0014JAP\ufffd\nu\u003c\ufffdL\u000cz\u0002\ufffd\ufffdET\ufffdJv*e\r\ufffd\u001b\ufffd\ufffd\ufffdK\u0017\u0018\ufffd^\ufffd\ufffd\ufffd\ufffdX8\u0010\ufffdr\ufffd\u0017ڏ\ufffd\ufffd˯$\ufffd\ufffd+\ufffd\u0017\ufffd\u0010\ufffd\ufffd\ufffd\ufffd76\"\u001c\ufffd\ufffd\ufffdʥ\ufffd\u0017\ufffd\ufffd*T$\ufffdՓ\ufffdn\ufffd\u0000yR\ufffd\ufffd\ufffd\u0004\u0002\ufffdrr\ufffdW\ufffd\ufffdc\ufffd\u0001\u0018K\ufffdC\ufffd\ufffd\ufffd\t\ufffd\u0013-,\ufffd\ufffdV\ufffd\u000c\ufffds\ufffd\ufffda\ufffd{\ufffd[\u000f\u001bv\ufffd\u003c8\ufffd\u0001\ufffd\ufffd\ufffd\u0012\u001b\ufffdA\ufffd\ufffdm\ufffd~W\ufffd\ufffd\ufffd7\u0012\ufffd\ufffdȇ\ufffd}\ufffd\ufffd\ufffd8\ufffd,k#\u0002zzF۫N\ufffd\ufffd*\u0015\u000e\ufffd^\ufffdԘ\ufffd\n}\ufffd\u001d|\ufffdF\u0004p\ufffd\ufffdcF`7\ufffdF\ufffdi\u001c\ufffdRў\ufffd\u003eL\ufffd\u0000\ufffd\ufffd\ufffd\ufffd4\ufffd:\u0013`G\nn!\ufffd\u0014\u0016\u001b\\\u00265CŢ{\u0012\ufffdK\ufffd\ufffdz8\ufffd\ufffd]g\ufffd\ufffdRx\ufffdꝚ\u000fj`7\ufffd\ufffd\ufffd\u0008\ufffd\u0013\u001bpk\ufffd\u000bEgUA\\\ufffd⮅\ufffd\ufffd\ufffd\ufffd\u001d\ufffd\ufffd\u0001\ufffdF\u001d,\ufffdu\ufffd\u001a\u0007\u0014\ufffdi%\ufffdяk\u0008\ufffd\ufffd%\"\ufffd//\ufffdރk\u00080\ufffd6\ufffdm\ufffdQ\ufffdA͘\ufffd\ufffd\u000b\ufffdC\ufffdo{\ufffd\ufffdF\u0011\u0001\u0017\ufffd\ufffda(\ufffd\ufffd\ufffd\ufffd\ufffd\u001508M\ufffd\u000fD\ufffd\ufffd8\u003e\ufffd\ufffd\\\ufffdQ\ufffd\ufffd\ufffd\u0014o.\ufffd\ufffd\ufffd\ufffd\ufffdϮ\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u001ad0\ufffd\ufffdNR\ufffdO\ufffd`\ufffd\ufffd\ufffd\ufffd\ufffd]\ufffdi\ufffd\ufffd\n\ufffd\u000e\u001e\ufffd;\ufffd1\ufffd\u0018\u000eI\"X\ufffd;\ufffd%\ufffd\ufffdq\ufffd\ufffd\ufffdW'*j6\\(1\u0013a!{\ufffd\ufffd!\ufffdR|50Ȳ\u0008R\ufffd\ufffd\ufffd\ufffd\u00030\u0001;\ufffdˑ\ufffd\ufffdu\u0019P\ufffd\u0001\u000c\ufffd\u0026Cn\ufffd\ufffdL\u0008\ufffd\ufffd\ufffdE\u000eM]\ufffdl\ufffd\ufffd]\ufffde\ufffd\ufffdȌ\ufffdtT\ufffdQ\ufffdo\ufffd\tM\ufffd\ufffd\ufffdP\u0026\ufffd\ufffd\ufffd\u001c\ufffd\ufffd\u001c\ufffdT\ufffdv\u00153\ufffd\ufffdA׺\ufffd\r\ufffd\u001a\ufffd\u0002\ufffd\ufffdh\u0007D6}+\ufffd\u003et/\ufffd\u00109\ufffd\u00164\"(\u0014iZphV\ufffd\ufffd!\ufffd\ufffdc\u001e\ufffd[ӿ\ufffd0\u0007\u000f\u000f\ufffd#\ufffd꒻\ufffd\u0015\ufffd\ufffda'\ufffd\ufffdд\u0019 \ufffdu\u001fe\ufffd\u0001\u003e\ufffd\ufffdO+\u0004K\u0002\ufffdP0\u0006ucn\ufffd\u0004\ufffd5r*rr\ufffd\ufffd]\ufffd\ufffd\ufffd\ufffdq\ufffd\ufffd\u0026Q\u001f\ufffd\u001c\ufffd\ufffd[y\ufffd\ufffd$Ѫk\ufffd\ufffd\ufffd\\\ufffd\ufffdT\ufffd\ufffd\u000e\ufffd \u0017\ufffdU\ufffdp2!UE\ufffdC\ufffd2Y\ufffd\u0000\u003e#|\u0010\u0008\ufffd\ufffdz\ufffd\ufffd\ufffd梯\r\tG\u0011\ufffd\ufffd\ufffd|\ufffd;:\ufffd\ufffd_\u0018~)f\u001f\ufffd\ufffd\ufffd\ufffd\ufffdwrL\ufffd]\ufffda\ufffd6\u003e\u001e/\ufffd\ufffdiܷߚ\ufffd\ufffduT\ufffdѯ\ufffd\ufffd\u0019\ufffd\ufffd\ufffd^Qݖֵ\ufffd0\ufffd\ufffd2\ufffd.\ufffd\ufffd'\ufffdZJ3\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd!\ufffd]\ufffd.\ufffd)x\ufffdh\ufffd\ufffd\"\ufffd\u0018\ufffdWF\ufffd\u0007\ufffd\ufffd\\=Ԟ\ufffdw\ufffd\u001dbR\u003e\u0013\u0003d4\ufffd!E\u001e=\ufffd\t\ufffdt\ufffdt$(UByܝ9\u0007\ufffd\ufffd\ufffd\ufffdnx͛\ufffd\ufffd\ufffd\ufffdJ\ufffd8\ufffdZ\u001cgj\u0015\u00031HA\ufffd(x\u0004R\ufffd\ufffd\ufffd\ufffdn`\t]\ufffd֗P\u000b\rS\ufffd\u0017\u0003T\ufffd\ufffdg\ufffdȚn\u001aV\u0007\ufffd\ufffdD.D\ufffdOΆ\ufffd\u001aS\ufffdr\ufffd]\ufffd~k\ufffd=X\ufffd\ufffdq\ufffd\ufffd\u0012\ufffd?*\ufffd\ufffd^\ufffd^\ufffd\ufffd,\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u0013(\ufffdQ@ \ufffd\u0011\u0013U4\ufffdp\ufffd\\?\ufffd{\u0008\ufffd繞u\ufffdA\ufffdUuփ\ufffd\ufffd\ufffd=\ufffd3\ufffd\ufffdR\ufffd\"/\u0017\ufffd\ufffd7\ufffd]\ufffd\ufffd\ufffd\u0001\ufffd\ufffd\ufffd\ufffd\ufffd\u001e\u001f\ufffd\ufffd% \\Y\u0004t\ufffd\ufffd\ufffd\u0001'ۯ\ufffd\ufffd8mu\ufffd\ufffd֠\ufffd\ufffdi-H\ufffd\ufffd\ufffd=ʒ\ufffd\ufffd\ufffddW\ufffd\ufffdX\u0026E\u00087z\ufffd\ufffd\ufffdҧ\ufffd\ufffdO\ufffd__\ufffd\ufffdO\ufffd\ufffd\ufffd\ufffd{\ufffd\ufffd\u0016a\ufffd\ufffd\ufffd\u0018v\u0014\ufffdַLmj\ufffd\ufffd\ufffd]\ufffdB2\u0012Q9\ufffd\u0001R\ufffd\ufffd\u001f\ufffd}9\ufffdo\u001b\u001d\u0000\u0000","raw":"HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 1793\r\nContent-Encoding: gzip\r\nContent-Type: text/html; charset=utf-8\r\nDate: Thu, 07 Sep 2023 15:34:26 GMT\r\nSet-Cookie: AWSALB=bqZoSugibrUn2bfJXYlvAufNHb6V5/dDhGQEYNEgWk9i8ARnQb/nXdgJ2kUC/ruOt3kfOm5GioxnCSPhWXHQq7eqXcg+qfYT1aWoQTJiY/4PmV/OUGHZlL2oJDKx; Expires=Thu, 14 Sep 2023 15:34:26 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=bqZoSugibrUn2bfJXYlvAufNHb6V5/dDhGQEYNEgWk9i8ARnQb/nXdgJ2kUC/ruOt3kfOm5GioxnCSPhWXHQq7eqXcg+qfYT1aWoQTJiY/4PmV/OUGHZlL2oJDKx; Expires=Thu, 14 Sep 2023 15:34:26 GMT; Path=/; SameSite=None; Secure\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n"}} +{"timestamp":"2023-09-07T21:04:30+05:30","url":"https://ginandjuice.shop/login","request":{"header":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Cache-Control":"max-age=0","Connection":"close","Content-Length":"53","Content-Type":"application/x-www-form-urlencoded","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=bqZoSugibrUn2bfJXYlvAufNHb6V5/dDhGQEYNEgWk9i8ARnQb/nXdgJ2kUC/ruOt3kfOm5GioxnCSPhWXHQq7eqXcg+qfYT1aWoQTJiY/4PmV/OUGHZlL2oJDKx; AWSALBCORS=bqZoSugibrUn2bfJXYlvAufNHb6V5/dDhGQEYNEgWk9i8ARnQb/nXdgJ2kUC/ruOt3kfOm5GioxnCSPhWXHQq7eqXcg+qfYT1aWoQTJiY/4PmV/OUGHZlL2oJDKx","Origin":"https://ginandjuice.shop","Referer":"https://ginandjuice.shop/login","Sec-Ch-Ua":"\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"macOS\"","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"same-origin","Sec-Fetch-User":"?1","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"POST","path":"/login","scheme":"https"},"body":"csrf=GhyXet8wACfYUo1chNYuFPbOCI36SVSj\u0026username=carlos","raw":"POST /login HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nCache-Control: max-age=0\r\nConnection: close\r\nContent-Length: 53\r\nContent-Type: application/x-www-form-urlencoded\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=bqZoSugibrUn2bfJXYlvAufNHb6V5/dDhGQEYNEgWk9i8ARnQb/nXdgJ2kUC/ruOt3kfOm5GioxnCSPhWXHQq7eqXcg+qfYT1aWoQTJiY/4PmV/OUGHZlL2oJDKx; AWSALBCORS=bqZoSugibrUn2bfJXYlvAufNHb6V5/dDhGQEYNEgWk9i8ARnQb/nXdgJ2kUC/ruOt3kfOm5GioxnCSPhWXHQq7eqXcg+qfYT1aWoQTJiY/4PmV/OUGHZlL2oJDKx\r\nOrigin: https://ginandjuice.shop\r\nReferer: https://ginandjuice.shop/login\r\nSec-Ch-Ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"\r\nSec-Ch-Ua-Mobile: ?0\r\nSec-Ch-Ua-Platform: \"macOS\"\r\nSec-Fetch-Dest: document\r\nSec-Fetch-Mode: navigate\r\nSec-Fetch-Site: same-origin\r\nSec-Fetch-User: ?1\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Content-Encoding":"gzip","Content-Length":"1877","Content-Type":"text/html; charset=utf-8","Date":"Thu, 07 Sep 2023 15:34:30 GMT","Set-Cookie":"AWSALB=f3uSeSLqZLjyLkgMubDnyLSLN4l5RHxJl2qNJIirhEpxbJNK9Q153T7UIWBXqoLUqADaTkYpLyY+e0ZITkQH2RbSlO2SL8pWPSA9bmVjvoTc37kvVwsUU6sGvbkI; Expires=Thu, 14 Sep 2023 15:34:30 GMT; Path=/, AWSALBCORS=f3uSeSLqZLjyLkgMubDnyLSLN4l5RHxJl2qNJIirhEpxbJNK9Q153T7UIWBXqoLUqADaTkYpLyY+e0ZITkQH2RbSlO2SL8pWPSA9bmVjvoTc37kvVwsUU6sGvbkI; Expires=Thu, 14 Sep 2023 15:34:30 GMT; Path=/; SameSite=None; Secure","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"body":"\u001f\ufffd\u0008\u0000\u0000\u0000\u0000\u0000\u0000\ufffd\ufffdY\ufffdn\ufffd6\u0014\ufffdߧ\ufffd4\ufffdN\ufffdʪ\ufffdl\ufffd\u0010\ufffd\ufffdzK/i\u00134-\ufffd\ufffdOA\ufffd\ufffdŔ\"U\ufffd\ufffd\ufffdG\ufffdk\ufffd\ufffdvHʊlK\ufffd\ufffd\u000b\ufffd\u001f5\ufffd\ufffd\u003c\u003c\ufffdxxx\ufffd\ufffd\ufffd\ufffd'\ufffd\ufffd\ufffd\ufffd}\ufffd\u0014E:\ufffd\ufffd{#\ufffd\u0007\ufffdg\u0014QL\ufffdW;\ufffdL|FQJg\ufffd \ufffdJfiHU\ufffd\ufffd԰\ufffd4\u0008\ufffd\nT\ufffd\ufffdk\ufffd\ufffd\u0012\ufffd@@)\ufffdc\ufffdsNUD\ufffdn\u00033\u0010\u0000\ufffd\ufffd\u0001\u0026\ufffd\ufffd\u0001b\ufffd1\u00128\ufffdco\ufffd\ufffd2\ufffd\ufffd\ufffdP(\ufffd\ufffdB\ufffd\ufffd%#:\u001a\u0013\ufffd`!\ufffd\ufffd\ufffd\u0001\ufffd\u0014M}\ufffd\u0010v\ufffdt,\ufffdWASa\ufffd\u0012\ufffdT\u001a\ufffd\ufffd\ufffd@7\n\u00068\ufffd}@\ufffd\\\u00261\ufffd\ufffdo\ufffd7\u0019\u0005nEw\u0008\ufffd\ufffdx\u000f\u0018\ufffd'p2Moup\ufffd\u0017\ufffdQ\ufffd:t,\ufffd\u0019\ufffd駁\ufffd\ufffd4\ufffdj\ufffd9\ufffd\\\ufffd9\u0013\ufffdG\ufffd\ufffd\ufffd\u003e\ufffd\ufffd3\ufffd2\u0003\u0005\ufffd\ufffdH\u0026\ufffd\ufffd\ufffd\ufffd\ufffd\u000f\ufffd\ufffd~4\ufffd$Gb\ufffd\ufffd$\ufffd \u0012\ufffd@\ufffd\ufffd\ufffd\ufffd\ufffdW4\ufffd\ufffdBCͤ@!\ufffdJ\ufffd=g(\u003e\ufffdN\u003c\ufffd\ufffdX`\u0017%\ufffd4\ufffdy\u00171\ufffd\ufffd\u0007#B9\ufffd\ufffd\u0014k\ufffds\ufffdȸ\ufffd\ufffdp\ufffdhI\ufffd\u0008d\ufffd,\ufffdvW؇\ufffd\u0005%h\u0026S\ufffd\ufffd\ufffdL\ufffd\r\ufffd}1U\ufffd\ufffdj!\ufffdL\ufffd\ufffdd\ufffd\u00024կ\u0015`\ufffd\u0012\\\u001eŘ\ufffd\ufffd\ufffd\ufffd%X\u0015\ufffd\u0026M\ufffd\ufffd\t\"\ufffd\ufffdO\ufffdn\u0007h\ufffd\u0006\ufffd\ufffd\\\ufffd\ufffd\u000e\ufffdfc\ufffd\ufffd\ufffd\u001e\ufffd\ufffd\ufffd4PEu\ufffdxmq\ufffd\ufffd=6C\u0007խ\ufffd\ufffdGսֆG\ufffd\ufffd\ufffdC\ufffde\ufffd\ufffd1\ufffdZN\u000fz\u0017G\u001f\u0007\ufffd\ufffd\ufffdy\u0018?c\ufffd\ufffdmB\ufffd9\u000f\ufffd\ufffdq\ufffd\ufffdܕSld\ufffd{\ufffd\ufffdV\ufffd\u0006gh\ufffd6\ufffd\ufffd\u001d\ufffdЎ\ufffd\ufffdU\ufffdQ\u000ev\ufffd\ufffd{@Z\"\u001dQ{u\ufffd\ufffdM\ufffd\ufffdk\ufffd%ذ\u0017`tV\ufffd\ufffd\u0016{\ufffdl\u001fٍz5\ufffdƼ\u001d\ufffd\ufffd)\ufffd\u0001[\ufffd]\ufffd\ufffd\u000bc\ufffd\ufffd\u0006\ufffd\u0006N\ufffd\ufffd\ufffds9\ufffd\ufffd\ufffd@o\ufffd\rp\ufffd\ufffd\u001d\ufffduX\ufffdE\u003e\ufffdF\ufffd\ufffdmJ\ufffd\ufffd\ufffdL\u001bx\ufffd\ufffdڄ_8\ufffd1v\ufffd\ufffd\ufffd\ufffd8\ufffdݨ\ufffdq\ufffd 9kg\ufffd\ufffdx\ufffd\ufffd4\ufffd\u001a\ufffd\ufffdS\ufffd\u0017\ufffdeb\ufffd\u0013XF*I\u0016jը\ufffd5\ufffd\ufffd\ufffd\ufffd\ufffd.\ufffdԊ\ufffd\u0008~\ufffd\ufffd\ufffd\ufffdS\ufffdiori\ufffdH\ufffd4\ufffdN\ufffd\ufffd\ufffd\t\ufffd\u000fJA~\ufffd\u0011\ufffd0\ufffd\ufffd\ufffd\u003e\ufffd\ufffd\ufffd\tV\ufffd\ufffd\ufffdTO\ufffd\ufffd\u0005\u0003\ufffdI\ufffd\ufffdOF\u0010\ufffdV\ufffdwy\u0015L}n\ufffd\u0000Ɗ\ufffd\u000fܿV\ufffdM\ufffd\ufffd\ufffd\u0026\ufffd\u001a\ufffd.z.\ufffdԍq\ufffd\ufffdo\ufffda\ufffd.\ufffd\u0007\ufffd:\u00001\ufffdYb\ufffd\ufffd\ufffd|\ufffdدsT|\ufffdA\ufffd\ufffd\u001byw\ufffd\ufffd\ufffd\ufffd\u0010\ufffd\ufffdemD\ufffd\ufffd\ufffd\u0018{5)pP\ufffdŽ\ufffdk\ufffdZӽC\ufffd\ufffdn;\u001f\ufffd\u0012\u0001|-\ufffdsNa7\ufffdFM\ufffd|nJEw\ufffd\ufffd0\ufffd\u0003\ufffd\ufffd[\ufffd\ufffd(hL\ufffd\r)\ufffd\ufffd\\SXlp\ufffd\ufffd\u000c\u0015\ufffd\ufffdI\ufffd)\ufffd[\ufffd\ufffd\u0018\ufffdv\ufffd\ufffd\ufffdJ\ufffd\ufffdT\ufffd\ufffd|P\u0003\ufffd\t\\oC\ufffd\u001c\ufffd\ufffd[j\\j6+\n\ufffd\"\u00167-\u000c\ufffdt\u0013\ufffd\ufffdu\u000f\ufffd6h`\ufffd\ufffd\ufffd\ufffd8\ufffd\u0010\ufffd\u000bɬ~|K\ufffdN.\ufffdd|uy\ufffd\u000e\ufffd\ufffd@\ufffd1\ufffd\ufffdo\u000b\ufffd\ufffd\u0004jƔ~\ufffdX\nu\ufffd\ufffd\ufffd\"F\u0008\u0005\u0017r\ufffda\ufffdҙ\ufffd\u0016\ufffdg0\ufffd8%ǯ\u0014;_\u003c[\ufffd\ufffd\ufffd\ufffd,\u000c\ufffd/\ufffd\ufffd\ufffd\ufffd\ufffd/NO\ufffd̻\ufffd\ufffd\ufffdi\u0010\ufffdގ\u001f\ufffd\ufffd\ufffd\ufffdF\ufffd\u0013Sῇ\ufffd\ufffdl\ufffdmH\ufffd\u0004\ufffd\ufffdRV\ufffd\ufffd0@+\u0011WĶ\ufffd7\ufffd\ufffd\ufffd\ufffd\u0002\ufffdh\ufffd\te}\u000f\u001c\ufffdK\ufffd;k]Hd\ufffd\ufffd\ufffdtN\ufffdSN\ufffd\ufffdG\ufffd\u000br\ufffd[\u0013\ufffdwط\ufffd\u0004\ufffd\u0015}7\ufffdv;Z\u003c\ufffd\ufffd\ufffd\ufffd\u0015\ufffd\ufffdR\ufffdd\u0014L'(\ufffdLo5l\ufffd\ufffd\ufffd\u0000v\ufffd\ufffd\nv\ufffdv\ufffd\ufffd^\\FR\ufffd\ufffdL\ufffd\u0019hD\ufffdl\ufffd\u0012Va\ufffd\ufffd\ufffd\"\ufffdl\u001a3=A.ۂ\u001a\ufffd\ufffd\ufffd0e\ufffd\ufffd\ufffd=\u001bݫ[\ufffd\ufffd\ufffdJ@\ufffdI\t\ufffd\ufffd\ufffd)\ufffd\ufffd\ufffd\ufffdg#Z\ufffd\u0005M\ufffdc\u001b\ufffdә\ufffdY;Z\ufffd\ufffd5\ufffdm\ufffdw8\ufffd(\u001aN\ufffdPh\ufffdQ̔{\u000c\ufffd\u001c\ufffd\u0008tm\ufffdlJ\ufffd\ufffdK\ufffdQ\ufffd\u001d\u0010\ufffd䥄\ufffd0\ufffd\u0026\ufffd4'KF(\ne\u001cg\ufffd\ufffd\u001c\ufffd\u00192\ufffd$X\u0010tc_GfX@\ufffd\u000c\ufffd\u0003\ufffd\ufffd@oC\ufffd)\ufffd\ufffd\ufffd\ufffdRAKl\ufffd86]\ufffd\ufffd\u0006\ufffd`\ufffd\u003c\\Q\ufffd(\ufffdB\ufffd9T\ufffd\ufffd\r\ufffd06ȱLi\ufffdw]W\ufffd\ufffd{Ro\ufffdŧ\u003c\ufffd\u001b\ufffd\ufffdoT\ufffd\ufffd\ufffd\ufffdV\ufffd\ufffd\ufffdf\ufffdֻS}\u001a{\ufffd@\ufffd\ufffd\u0017\ufffd\ufffdK\ufffd\ufffd\u0005\ufffd\u000cȫR\ufffd\u000e\ufffd+\ufffd3\ufffd\ufffdǝ\ufffd\ufffd\ufffdk.\ufffdԒ0!pڶ\ufffd`\ufffd\ufffd|\ufffds\ufffd\ufffd\ufffd\u0017\ufffd$y{\ufffd\ufffd\ufffd\u0026\ufffd\ufffd񇷯\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd'o\ufffd\ufffd[s\ufffd\ufffdJ\ufffd:\ufffd\ufffdJ3]\u003c\ufffd\ufffd+\ufffd\ufffd2\ufffd\ufffd\u0017\u0006\ufffdn\u0002\ufffd\ufffdQڄ\\+\u0018\ufffd\ufffdf\ufffdn9a\u00089\ufffd\ufffd\ufffdsJ\u0011r\u0016~.ȏ-\ufffd\u0013+\ufffd\u0001x\ufffdЇ\ufffd\ufffd\ufffdN\ufffdCL\u0026f\ufffd\ufffd\ufffdv5\u0014 Ç\ufffd\ufffd\ufffdΐ\ufffd\u0004\ufffd\ufffd\ufffd\ufffd7\ufffd%\ufffdv7\ufffdx\ufffd\ufffd\":*\ufffd\u0026\ufffd\ufffdӣ\ufffdp\ufffd\ufffdS\u000c\ufffd \r\ufffd\"\u0013\ufffdâ\ufffd=`\ufffd\u001bXA\u0017\ufffd\ufffd%\ufffdB\ufffd\ufffd\ufffd\ufffd\u0000\ufffdm\ufffd\u00157\ufffd\ufffd\ufffd\ufffd\ufffd\u0001(\ufffdć\ufffd\ufffd\ufffd\ufffd\ufffdYe\n\u0015n\ufffd+\ufffdo\ufffd\ufffd\u0007k2\ufffd+Ix\u000e\ufffd5S\ufffdmo\ufffd\u0006\ufffd[\ufffdy\ufffdN\ufffd\ufffd\ufffdchoДB4\ufffd6\ufffd\u0018\ufffd\ufffd\ufffddN\ufffdݱv\u000f\ufffd\ufffd\u003c7p\ufffd\ufffd\ufffd)\ufffd\ufffdzpu\ufffd\u0015Em\u0026X]j\ufffdߡ\ufffd\ufffd\ufffd\ufffdo\ufffd\ufffd\ufffdo\u0000\u0026\ufffd\ufffd\ufffd\ufffd\u000f\ufffdG\ufffd\n\u0010\ufffd\u001d\u0002\ufffdФ\ufffd\ufffdd\ufffd\ufffdP\r\ufffd-N\ufffd\ufffdx5\ufffd9\ufffd\u0005\t\ufffd~\ufffdGY\ufffd\ufffd|\ufffd\ufffdUeT\ufffd)\u0019^\ufffd\ufffdW\ufffd)\ufffd\ufffd\u0003\ufffd\ufffd\ufffd\ufffd\ufffd\u0003luv\ufffd\u0017\ufffd\ufffd\u0016a\ufffdIcc\ufffdP\ufffd:߲\ufffd\ufffd\ufffd\ufffd\ufffdvU\n\ufffdH\ufffd|r\u000fR\ufffd\ufffd\u000f\ufffdZ\ufffd\ufffday\u001e\u0000\u0000","raw":"HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 1877\r\nContent-Encoding: gzip\r\nContent-Type: text/html; charset=utf-8\r\nDate: Thu, 07 Sep 2023 15:34:30 GMT\r\nSet-Cookie: AWSALB=f3uSeSLqZLjyLkgMubDnyLSLN4l5RHxJl2qNJIirhEpxbJNK9Q153T7UIWBXqoLUqADaTkYpLyY+e0ZITkQH2RbSlO2SL8pWPSA9bmVjvoTc37kvVwsUU6sGvbkI; Expires=Thu, 14 Sep 2023 15:34:30 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=f3uSeSLqZLjyLkgMubDnyLSLN4l5RHxJl2qNJIirhEpxbJNK9Q153T7UIWBXqoLUqADaTkYpLyY+e0ZITkQH2RbSlO2SL8pWPSA9bmVjvoTc37kvVwsUU6sGvbkI; Expires=Thu, 14 Sep 2023 15:34:30 GMT; Path=/; SameSite=None; Secure\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n"}} +{"timestamp":"2023-09-07T21:04:33+05:30","url":"https://ginandjuice.shop/login","request":{"header":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Cache-Control":"max-age=0","Connection":"close","Content-Length":"70","Content-Type":"application/x-www-form-urlencoded","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=f3uSeSLqZLjyLkgMubDnyLSLN4l5RHxJl2qNJIirhEpxbJNK9Q153T7UIWBXqoLUqADaTkYpLyY+e0ZITkQH2RbSlO2SL8pWPSA9bmVjvoTc37kvVwsUU6sGvbkI; AWSALBCORS=f3uSeSLqZLjyLkgMubDnyLSLN4l5RHxJl2qNJIirhEpxbJNK9Q153T7UIWBXqoLUqADaTkYpLyY+e0ZITkQH2RbSlO2SL8pWPSA9bmVjvoTc37kvVwsUU6sGvbkI","Origin":"https://ginandjuice.shop","Referer":"https://ginandjuice.shop/login","Sec-Ch-Ua":"\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"macOS\"","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"same-origin","Sec-Fetch-User":"?1","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"POST","path":"/login","scheme":"https"},"body":"csrf=gW8d4KsiGvFwrnUHucc4OumZ28lv879y\u0026username=carlos\u0026password=hunter2","raw":"POST /login HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nCache-Control: max-age=0\r\nConnection: close\r\nContent-Length: 70\r\nContent-Type: application/x-www-form-urlencoded\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=rXVoplR77zJ2sMgzZWa5Cp5X2y0YMzSK; AWSALB=f3uSeSLqZLjyLkgMubDnyLSLN4l5RHxJl2qNJIirhEpxbJNK9Q153T7UIWBXqoLUqADaTkYpLyY+e0ZITkQH2RbSlO2SL8pWPSA9bmVjvoTc37kvVwsUU6sGvbkI; AWSALBCORS=f3uSeSLqZLjyLkgMubDnyLSLN4l5RHxJl2qNJIirhEpxbJNK9Q153T7UIWBXqoLUqADaTkYpLyY+e0ZITkQH2RbSlO2SL8pWPSA9bmVjvoTc37kvVwsUU6sGvbkI\r\nOrigin: https://ginandjuice.shop\r\nReferer: https://ginandjuice.shop/login\r\nSec-Ch-Ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"\r\nSec-Ch-Ua-Mobile: ?0\r\nSec-Ch-Ua-Platform: \"macOS\"\r\nSec-Fetch-Dest: document\r\nSec-Fetch-Mode: navigate\r\nSec-Fetch-Site: same-origin\r\nSec-Fetch-User: ?1\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Content-Encoding":"gzip","Content-Length":"0","Date":"Thu, 07 Sep 2023 15:34:33 GMT","Location":"/my-account","Set-Cookie":"AWSALB=A8FP7QaUyTZcc1eqKIrwbXu1bU7eejb169j6dtYkZIoq+xNxSgeFjvTG3fo5IxkRKmFnCjcicOqFaNkotzDAeVhz7bj4YTjbFF9pI+euPCOy8scUu1Pu53wub9sf; Expires=Thu, 14 Sep 2023 15:34:33 GMT; Path=/, AWSALBCORS=A8FP7QaUyTZcc1eqKIrwbXu1bU7eejb169j6dtYkZIoq+xNxSgeFjvTG3fo5IxkRKmFnCjcicOqFaNkotzDAeVhz7bj4YTjbFF9pI+euPCOy8scUu1Pu53wub9sf; Expires=Thu, 14 Sep 2023 15:34:33 GMT; Path=/; SameSite=None; Secure, session=wrNhEoOD0CeeH8cagJk19XHRP32b6YM2; Secure; HttpOnly; SameSite=None","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"raw":"HTTP/1.1 302 Found\r\nConnection: close\r\nContent-Length: 0\r\nContent-Encoding: gzip\r\nDate: Thu, 07 Sep 2023 15:34:33 GMT\r\nLocation: /my-account\r\nSet-Cookie: AWSALB=A8FP7QaUyTZcc1eqKIrwbXu1bU7eejb169j6dtYkZIoq+xNxSgeFjvTG3fo5IxkRKmFnCjcicOqFaNkotzDAeVhz7bj4YTjbFF9pI+euPCOy8scUu1Pu53wub9sf; Expires=Thu, 14 Sep 2023 15:34:33 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=A8FP7QaUyTZcc1eqKIrwbXu1bU7eejb169j6dtYkZIoq+xNxSgeFjvTG3fo5IxkRKmFnCjcicOqFaNkotzDAeVhz7bj4YTjbFF9pI+euPCOy8scUu1Pu53wub9sf; Expires=Thu, 14 Sep 2023 15:34:33 GMT; Path=/; SameSite=None; Secure\r\nSet-Cookie: session=wrNhEoOD0CeeH8cagJk19XHRP32b6YM2; Secure; HttpOnly; SameSite=None\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n"}} +{"timestamp":"2023-09-07T21:04:33+05:30","url":"https://ginandjuice.shop/my-account","request":{"header":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Cache-Control":"max-age=0","Connection":"close","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; AWSALB=A8FP7QaUyTZcc1eqKIrwbXu1bU7eejb169j6dtYkZIoq+xNxSgeFjvTG3fo5IxkRKmFnCjcicOqFaNkotzDAeVhz7bj4YTjbFF9pI+euPCOy8scUu1Pu53wub9sf; AWSALBCORS=A8FP7QaUyTZcc1eqKIrwbXu1bU7eejb169j6dtYkZIoq+xNxSgeFjvTG3fo5IxkRKmFnCjcicOqFaNkotzDAeVhz7bj4YTjbFF9pI+euPCOy8scUu1Pu53wub9sf; session=wrNhEoOD0CeeH8cagJk19XHRP32b6YM2","Referer":"https://ginandjuice.shop/login","Sec-Ch-Ua":"\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"macOS\"","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"same-origin","Sec-Fetch-User":"?1","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"GET","path":"/my-account","scheme":"https"},"raw":"GET /my-account HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nCache-Control: max-age=0\r\nConnection: close\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; AWSALB=A8FP7QaUyTZcc1eqKIrwbXu1bU7eejb169j6dtYkZIoq+xNxSgeFjvTG3fo5IxkRKmFnCjcicOqFaNkotzDAeVhz7bj4YTjbFF9pI+euPCOy8scUu1Pu53wub9sf; AWSALBCORS=A8FP7QaUyTZcc1eqKIrwbXu1bU7eejb169j6dtYkZIoq+xNxSgeFjvTG3fo5IxkRKmFnCjcicOqFaNkotzDAeVhz7bj4YTjbFF9pI+euPCOy8scUu1Pu53wub9sf; session=wrNhEoOD0CeeH8cagJk19XHRP32b6YM2\r\nReferer: https://ginandjuice.shop/login\r\nSec-Ch-Ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"\r\nSec-Ch-Ua-Mobile: ?0\r\nSec-Ch-Ua-Platform: \"macOS\"\r\nSec-Fetch-Dest: document\r\nSec-Fetch-Mode: navigate\r\nSec-Fetch-Site: same-origin\r\nSec-Fetch-User: ?1\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Cache-Control":"no-cache","Content-Encoding":"gzip","Content-Length":"2092","Content-Type":"text/html; charset=utf-8","Date":"Thu, 07 Sep 2023 15:34:34 GMT","Set-Cookie":"AWSALB=Yj6jGTtNRQBhM6lgLDvgcMeE6a2Q8QoLKIpMK31LCyuD8hBeyNGoGebt0J65jEdsn6MA10IU78o/xHz/IblQi5lC4NmL51AiQOxVjyyqyBHIkfTffkZOuAaB8jta; Expires=Thu, 14 Sep 2023 15:34:34 GMT; Path=/, AWSALBCORS=Yj6jGTtNRQBhM6lgLDvgcMeE6a2Q8QoLKIpMK31LCyuD8hBeyNGoGebt0J65jEdsn6MA10IU78o/xHz/IblQi5lC4NmL51AiQOxVjyyqyBHIkfTffkZOuAaB8jta; Expires=Thu, 14 Sep 2023 15:34:34 GMT; Path=/; SameSite=None; Secure","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"body":"\u001f\ufffd\u0008\u0000\u0000\u0000\u0000\u0000\u0000\ufffd\ufffd[\ufffdr\ufffd6\u0012\ufffdާ@ywq2\u0013\ufffd\ufffdc[\ufffdD\ufffdL\ufffd\ufffdɤn\ufffd\u0019g\ufffdi\ufffdd \u0010\u0012a\ufffd\u0000\ufffd\u0000%\ufffd\u003e\ufffdk\ufffd#\ufffd5\ufffdQ\ufffdIn\u0001P2%\ufffd\u0012\u0019\ufffdN?ȓ\ufffdE`\ufffd\ufffdb\ufffd\ufffd\ufffdB\\\ufffd\ufffd\ufffd\ufffdëO\ufffd}|\ufffd\"\u001d\ufffd\ufffd7#\ufffd\u000b\ufffd\ufffd(\ufffd8t\u001f\ufffd#g\ufffd\u0006E)\ufffd\ufffd\ufffd\ufffd*\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u0011\ufffdi@\ufffd\n\u0014\ufffd\ufffdg\ufffd\ufffd6t\ufffd\u0001\ufffd\ufffd\ufffd\ufffd\ufffd9U\u0011\ufffd\ufffd\u0010\ufffd\ufffd\u0000@u\u000501\ufffd\u000f\u0010S\ufffd\ufffd\ufffd1\u001d{\u000bF\ufffd\ufffdL\ufffd\ufffd\ufffd\u0014\ufffd\n=\ufffd\ufffd,\ufffd\ufffd8\ufffd\u000bF\ufffdo\u001f\ufffd\ufffdL\ufffd\ufffd\u0007\ra\u0006N\ufffdBz%4ER\ufffdh\ufffdR2\ufffdJ\n]+x\ufffdDw\u0000\ufffdr\ufffd\ufffd\u0000޹V\ufffdd\u0014\ufffd\u0011\ufffd!\ufffdP\ufffd-`t\ufffd\ufffd\ufffd4\ufffd\ufffd\ufffd5^`\ufffd\ufffdU\ufffdc1\ufffd8N?\ufffd\ufffd\ufffd?\ufffdC\ufffdLs:\ufffd9G\ufffd\u0010\ufffd\t\ufffd|\ufffd\ufffd\t\ufffd\u0004\ufffd\ufffdK\ufffd.\u0003+\ufffd\ufffdH\u0026\ufffd\ufffd\t\ufffd\ufffd\u000f\ufffd\u001c`4\ufffda\ufffd\ufffd\ufffd\ufffdIR\ufffd\r\ufffd\u0002\ufffdp\ufffd\ufffd\ufffd\ufffddV\ufffd J4\ufffd\u0002\u0011\ufffd\ufffd\u001a{\ufffd[\ufffd\ufffd:\u001d\ufffdgk\ufffd\u001d\ufffd춙\ufffdO\u0011S\u0008\ufffda\u0014RΦ4Ś\ufffd\u001c-2.\ufffd3l+Z\ufffd)\u0002\u001d9#\ufffd\ufffd\n󰹠!\ufffd\ufffd\u0014i\ufffd4\u0013s#\ufffdDLU\ufffdr5\ufffdq\ufffds\ufffdd\ufffd\u0002m\ufffdS\ufffd\ufffdH%x\ufffd\u0014\ufffd\ufffd\u001e\ufffd\"\ufffd\ufffdZ\ufffd\ufffd45\ufffd'B\ufffd\ufffd|\ufffd|\ufffdCc\ufffd[w\ufffd\ufffdfo\ufffd7\ufffd\ufffdr\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdU\ufffd\ufffdO7\u0006g\ufffd\ufffdc3\ufffd\ufffd\u003c\ufffd\ufffd'\ufffd\u003c\ufffd\ufffd\ufffd`\ufffd\ufffd\ufffd\u0019Ⲱ\ufffd\u0018a-\ufffdOO.\u0007\ufffd\ufffd\ufffdo\ufffd8\ufffdd\ufffd\ufffd\ufffd$|\ufffd9Y\ufffd\ufffd'\ufffdֳr\ufffd\ufffdΰ\u000fT\ufffd\u0012\\\ufffd%\ufffdi\ufffdW\ufffd\r*\ufffdN\ufffd\ufffd\ufffdL\ufffd\u001c\ufffd\u001b\u0015\ufffd\ufffd\ufffdD:\ufffdv\ufffd\ufffd\u0005\ufffd\u0006\ufffdW\ufffdK\ufffd\ufffd/ \ufffd\ufffdn\ufffd/\ufffd8\ufffd\ufffd\ufffdNtR\u0001k\ufffdۉ\u001ab\ufffd\u000cĪ\ufffdr\ufffd\u001d\ufffd\ufffd\u0004\u00150\u0015p\ufffd\u0012\ufffd\ufffd\ufffd\\B\ufffdB{\rl\ufffdk:\ufffd\ufffdl\ufffd\ufffd\u0007\ufffd\ufffdc\u0004\ufffd\ufffdf-\ufffd\ufffd\ufffdk\ufffd,\ufffd[\ufffd\ufffd\ufffd\ufffd5ήP\ufffd\ufffd\u0018\ufffd\ufffd{\ufffd\u0008\ufffd\u001dH\ufffd\u000e\u000bYA\ufffd\ufffdz\ufffdi\r\ufffd\ufffd\u000c\ufffd\u0005\ufffd\ufffd\u0018\ufffd\u0004\ufffd\ufffd\ufffd0#Z\ufffd\u001ae\u0003.h2\ufffd\ufffd\ufffd\ufffdZվ\ufffd\ufffd\ufffdRj\ufffd\ufffd̴7\ufffd`\ufffdH\ufffd4 \ufffdFA\ufffd\ufffd\ufffdB\u0010\ufffdR\ufffd\ufffd\u0013\u0015ǜ\ufffd`.80L\ufffd\ufffdi\ufffdÑg\ufffd\ufffdh\u001a\ufffdm\u0015\ufffd~!\r'\ufffdZ\ufffd'#`\ufffdU\ufffd\ufffdI\u000b~?7\ufffd\u0000p%\ufffd\u0001鿕g2G/\u000c6\u0007\ufffd\ufffd9\u0008F\"Jn|\ufffdR©\u0005+7l\ufffd5\ufffd\ufffdb\u0007\ufffd\t\ufffd\ufffd쮅\ufffd\u003c\ufffdP\ufffdq\ufffdK9G𡱺\u0016\ufffdQT\u003c\ufffd\ufffd坿K\ufffd\ufffd$\ufffd\u000f\ufffdNs\ufffd/\u0008\u0008\ufffd\ufffd\ufffdG\ufffd\ufffd50=\ufffd\ufffd\ufffd\ufffd\ufffd+N؆q`\ufffd:\ufffd]\ufffd\ufffdw\ufffd\ufffdK.\u0011\ufffd\ufffd!\ufffdm\ufffd\ufffd\ufffd֔A\ufffd\ufffd\ufffdլ\u0017S\ufffd\u0000\ufffdݗ\ufffdFA\ufffd\ufffdZs\ufffdW4W\ufffd+[R\ufffdćD\ufffd\ufffdw\ufffd~\ufffd7׎!%Xg\u0006Ui\ufffd]\u001a\ufffd\ufffd\ufffd\u000c]\u0026\ufffd\ufffd5\ufffd\u003crd\ufffd6\ufffd\ufffdlV$\ufffd\u0005\ufffd\ufffd\r\u000c\\w\u001dl\ufffd\ufffd\ufffdw\ufffd\u0018\ufffd\ufffd\ufffd\ufffd\u0007\ufffd\u001c+\ufffd-n|{\ufffdg\ufffd\u002625G\u0011\ufffd#̹x\ufffd\ufffd)\ufffdǍc\ufffd\ufffdMN,\u0016Ϸ\u0006\ufffd\ufffdY\ufffd\ufffd\ufffdC\ufffd\ufffd\ufffd qy\ufffd*\u001c\ufffdG\u0015$Y\ufffdp\ufffd9º\ufffd\ufffdy\ufffdIw\ufffd\u00153\ufffd`\ufffd\u001d\u001c\r \ufffd63\ufffd\ufffdX\ufffd\ufffd!\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd洐$\u0012p\ufffd\ufffd\ufffd\ufffd\ufffdN\ufffd;\n\ufffd\rav\ufffd\u000f\ufffd\ufffd\ufffdx\ufffd\u0014a\ufffda\ufffd\ufffd\ufffd\u0019\ufffd\ufffd\ufffd\ufffdn\ufffd\ufffdt8\ufffd\ufffd]\u001c\ufffd\ufffd\ufffdp\ufffdD\u0017Ao\u0018\ufffd\ufffd\ufffd\ufffd\u0017!\ufffd\ufffdQ\ufffdM\ufffd\ufffdnqJc\ufffd\ufffdY\ufffd\ufffd3\ufffd\ufffd\ufffd%@A\ufffd\ufffdJ\u000c\ufffdLHs\ufffd\u003c\ufffd\ufffdۘ\ufffd\ufffd\ufffd\ufffd\ufffdY\ufffdDlg\ufffd[٦\ufffdM\u001a\u001a\ufffd\ufffd\ufffdMD\ufffd\u0026o\ufffd=\u0016]\ufffd\ufffd\ufffd^\ufffd\ufffd\u0014\u0013 \ufffd\ufffd\ufffd\ufffdE\ufffdsz\ufffd\ufffd\ufffd\ufffd?|\u0000\ufffd\ufffd^\u001c\ufffd\ufffd${\ufffd\ufffdV\u000bzD\ufffd\ufffdu\u001f\ufffd/~L3\ufffd\ufffd\u0007\ufffd/ǹ\ufffd?]\ufffd\ufffdw\ufffd/\ufffd:]\ufffd\u001b\u001d\ufffdI\u0017\ufffdZ\ufffd\u001f\ufffd\ufffd${\ufffd\ufffdV\u000bzD\ufffd\ufffdx4\ufffd\ufffd8f\ufffd\ufffd\ufffd\u000fz%ɍ\ufffdT\ufffd\ufffd\ufffd\ufffd\u0007`\ufffd\ufffd\u0017\ufffd\ufffd\ufffd\ufffdg\ufffd\ufffd\ufffd\u00030G\ufffd\ufffd\ufffd\u001c%\ufffd#s\ufffdZ\ufffd\ufffd^R\u001e\ufffd\ufffd\ufffd\ufffd\ufffd\u000fN\u001f\ufffd.\ufffd/\ufffdtQ\ufffd=\ufffdEmw\ufffd\u001b\ufffd\u0003\ufffd\ufffd}\ufffdm\ufffdKƚI\ufffd\ufffdZ\ufffd\u0014'I\ufffd\ufffd\ufffd[\ufffd\ufffd\ufffd\u0001u\ufffd3\ufffd\"s:3\ufffd8\ufffd\ufffdh\ufffd\u001dClA\ufffd\u001e[\ufffd\ufffd\ufffd\ufffd=\ufffd]A1S\ufffd$\u0005s\ufffd#\ufffdMMi˔B\ufffd.\ufffd|\ufffd{ \ufffd\ufffd;\ufffd\u00042\ufffd\u000eK\ufffd\ufffdp\ufffdB\ufffd\ufffd\ufffd\ufffdL0\ufffd#9Cs\ufffd\ufffd\"Dז\ufffdfX`͈zn\ufffdY\ufffd-\ufffd\ufffd\u0002\ufffd@\ufffd.\u0015\ufffd\u000e\ufffdcS+a\ufffd\u00019\ufffd15T\u0014C\ufffd\ufffd\u001c\ufffd9\ufffdS\ufffdگ\ufffd\ufffd\ufffd \ufffd2\ufffd\ufffd\ufffd\n\ufffdM\ufffdߕ6\ufffdl\u0016\u001f\ufffd\u0017\ufffd^.;\ufffdꤢ\ufffd\ufffd\ufffdֶhh5ֻ3}\u001a{\ufffd\ufffd\ufffd\u001fS\u001dI\u0010J\ufffd\ufffdE\u0013\ufffd\u003ePzsu\u0007p\ufffdՙH2\rf\ufffdg\ufffd \ufffd\\m\u0015\ufffdMJi\u000b\ufffd\ufffdDŽcB#\ufffdCS\ufffd\ufffd\ufffd6\ufffd0\ufffd\ufffd\u001e\n\ufffdJx/baH\ufffdW\ufffd\ufffd\u0011\ufffd\ufffd\u003c\ufffd\ufffd\u003c\ufffd\u0007\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdw\ufffd\ufffdkF\ufffdO݋˫\ufffdf\ufffd\ufffd\ufffd\ufffdL\ufffd\ufffd\ufffdp\ufffd\ufffd\ufffd\ufffd|\ufffd5\ufffd\ufffdKM;-\ufffd$fzr\ufffd\ufffd\u000c\ufffdZ\ufffd{_\ufffd\ufffd灐\u0019[\ufffd\r#2K\ufffd\ufffd]K\ufffdW'Ÿ\ufffd\ufffd\u003e\ufffdB¥\ufffd\ufffdj\ufffdR\u0010\ufffd\ufffdM\ufffd\ufffd\ufffdb\ufffd`Uz\n\ufffd(\ufffd3\u0013\ufffd\ufffdV\ufffdG\ufffdV\ufffdFo\ufffd\ufffd\ufffd\u0003\ufffdt\ufffd\u000c\u0013\ufffd:bb^\ufffd\ufffd\ufffd\ufffd\ufffdI\ufffd\ufffdZo\ufffd\n\ufffdbo\ufffd\ufffd\u0014\ufffd\ufffd\u000eǛ8\ufffd\u0000\u0007i\ufffd\ufffdL\ufffd\ufffd\ufffd \u000b[\ufffd\ufffd\n\ufffd\ufffd]o\ufffdZi\ufffd\ufffd\ufffd\u0018hu\ufffd\ufffd\ufffdlئ\ufffdu\u0000\n\ufffd\ufffd\u0003\ufffd\ufffdx[6+u\ufffd\"\ufffd\ufffd\ufffd\ufffd\ufffdL-D\ufffd\ufffdwI\ufffds\ufffdM!\ufffd\ufffd\ufffdT\ufffdي5S@\ufffd\ufffd\ufffd\ufffdW8\ufffdhJ\ufffdͨe\u0015\ufffdf%lV\ufffd\ufffdk[(u\ufffd\u0004w\ufffdz\ufffd\ufffd\ufffdN=غ\ufffdͣC.X\u001ejJo\ufffdҾy\ufffd\u0015m\ufffd\ufffdo\u0000\u0026\ufffd\ufffd\u0003\ufffdd\u0014}\u0004\ufffd+\ufffd\ufffd.u\ufffdi\ufffd\ufffdv\ufffd\ufffdkV[\ufffd\ufffd6\ufffd\ufffdM\ufffd\ufffd\u0013\u0012X\ufffd\ufffdEZ\ufffd~] \ufffd/\ufffd(\rS\ufffd\ufffd\ufffd^\u001e\ufffd\ufffd:d\ufffdc\u0019\ufffd\ufffdkw,\u0003,\ufffd\ufffd/\ufffd\ufffd\ufffd\"lU\ufffdl=\ufffd$\ufffd.\ufffdln\ufffd\ufffd\ufffd`7+\ufffd\ufffdH\ufffd\ufffd\ufffd\u001b8\u0012\ufffd\u001f;\ufffd\u001fq\u0007%\ufffd\u00041\u0000\u0000","raw":"HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 2092\r\nCache-Control: no-cache\r\nContent-Encoding: gzip\r\nContent-Type: text/html; charset=utf-8\r\nDate: Thu, 07 Sep 2023 15:34:34 GMT\r\nSet-Cookie: AWSALB=Yj6jGTtNRQBhM6lgLDvgcMeE6a2Q8QoLKIpMK31LCyuD8hBeyNGoGebt0J65jEdsn6MA10IU78o/xHz/IblQi5lC4NmL51AiQOxVjyyqyBHIkfTffkZOuAaB8jta; Expires=Thu, 14 Sep 2023 15:34:34 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=Yj6jGTtNRQBhM6lgLDvgcMeE6a2Q8QoLKIpMK31LCyuD8hBeyNGoGebt0J65jEdsn6MA10IU78o/xHz/IblQi5lC4NmL51AiQOxVjyyqyBHIkfTffkZOuAaB8jta; Expires=Thu, 14 Sep 2023 15:34:34 GMT; Path=/; SameSite=None; Secure\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n"}} +{"timestamp":"2023-09-07T21:04:39+05:30","url":"https://ginandjuice.shop/catalog/cart","request":{"header":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Connection":"close","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=wrNhEoOD0CeeH8cagJk19XHRP32b6YM2; AWSALB=Yj6jGTtNRQBhM6lgLDvgcMeE6a2Q8QoLKIpMK31LCyuD8hBeyNGoGebt0J65jEdsn6MA10IU78o/xHz/IblQi5lC4NmL51AiQOxVjyyqyBHIkfTffkZOuAaB8jta; AWSALBCORS=Yj6jGTtNRQBhM6lgLDvgcMeE6a2Q8QoLKIpMK31LCyuD8hBeyNGoGebt0J65jEdsn6MA10IU78o/xHz/IblQi5lC4NmL51AiQOxVjyyqyBHIkfTffkZOuAaB8jta","Referer":"https://ginandjuice.shop/my-account","Sec-Ch-Ua":"\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"macOS\"","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"same-origin","Sec-Fetch-User":"?1","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"GET","path":"/catalog/cart","scheme":"https"},"raw":"GET /catalog/cart HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nConnection: close\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=wrNhEoOD0CeeH8cagJk19XHRP32b6YM2; AWSALB=Yj6jGTtNRQBhM6lgLDvgcMeE6a2Q8QoLKIpMK31LCyuD8hBeyNGoGebt0J65jEdsn6MA10IU78o/xHz/IblQi5lC4NmL51AiQOxVjyyqyBHIkfTffkZOuAaB8jta; AWSALBCORS=Yj6jGTtNRQBhM6lgLDvgcMeE6a2Q8QoLKIpMK31LCyuD8hBeyNGoGebt0J65jEdsn6MA10IU78o/xHz/IblQi5lC4NmL51AiQOxVjyyqyBHIkfTffkZOuAaB8jta\r\nReferer: https://ginandjuice.shop/my-account\r\nSec-Ch-Ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"\r\nSec-Ch-Ua-Mobile: ?0\r\nSec-Ch-Ua-Platform: \"macOS\"\r\nSec-Fetch-Dest: document\r\nSec-Fetch-Mode: navigate\r\nSec-Fetch-Site: same-origin\r\nSec-Fetch-User: ?1\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Content-Encoding":"gzip","Content-Length":"1806","Content-Type":"text/html; charset=utf-8","Date":"Thu, 07 Sep 2023 15:34:39 GMT","Set-Cookie":"AWSALB=+OTzt/cHdN9mzp1pbOgaURPkES7i2qq1lhilq3fOQUe1jfh6+KYbRfxvolgT0DjiZvlhhdhoctO4ecMahMhz5hagBN8ye+HbQJnxFvNdj2yozEGUDHaWbKKErer+; Expires=Thu, 14 Sep 2023 15:34:39 GMT; Path=/, AWSALBCORS=+OTzt/cHdN9mzp1pbOgaURPkES7i2qq1lhilq3fOQUe1jfh6+KYbRfxvolgT0DjiZvlhhdhoctO4ecMahMhz5hagBN8ye+HbQJnxFvNdj2yozEGUDHaWbKKErer+; Expires=Thu, 14 Sep 2023 15:34:39 GMT; Path=/; SameSite=None; Secure","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"body":"\u001f\ufffd\u0008\u0000\u0000\u0000\u0000\u0000\u0000\ufffd\ufffdZ\ufffdS\ufffd6\u0018\ufffd޿B\ufffdvM{7ǘ@\ufffd%\ufffd\u000e\u0018e\ufffdx[\ufffd֮_8\ufffdVbQ\ufffdrm9\ufffd\ufffd\ufffd{$9\ufffd\t6\u0011`v\ufffd\ufffd\u001cG\ufffd\ufffd\ufffd\u001e=\ufffd\u000f\ufffd\ufffd\ufffd\ufffd\ufffd}v\ufffd\"\u00113\ufffd\ufffdP!\ufffd\u000c#\ufffdC\ufffd\ufffd\ufffd\ufffd\u0026_Q\ufffd\ufffd\ufffd\ufffdd$\ufffdE\u0016\ufffd\ufffdax$\ufffd\ufffd\ufffd\t\ufffd\ufffd\ufffd\u0003\ufffd\u001c\ufffd\ufffdTG\u001f:PF\ufffd\ufffd\ufffd\u0019#yD\ufffdX\u0005\u0026!\u00000\ufffd\u000801\ufffd\u001b \u0026\u0002\ufffd\u0004\ufffdij\ufffd\ufffd\\\ufffd\u003c\u0013\u0016\nx\"H\"\u003c늆\"\ufffdB2\ufffd\u0001\ufffdU\ufffdgT\ufffd$\ufffd\ufffdB؁\u0011/\ufffdV\r-\u000f2\ufffd\n\ufffdg\ufffdg\ufffd\u0008\ufffd̡\ufffd\u0003\ufffd\u0007$\ufffdx\u001a\u0003x\ufffd2\ufffd\ufffd\ufffd\ufffdW\ufffdC\ufffd!\ufffd\ufffd\u0001#f)\ufffdL\ufffdk\ufffd\\\ufffd)ֽV\u0013:N\u0026\u0005\ufffdمko\ufffd[m\ufffd\ufffd\nF\ufffd}\ufffd\td\ufffdC\ufffd\ufffd\ufffd8Nw\ufffd\ufffd\u0002\ufffd\ufffd\u003eF\u003c\u001d:z\ufffd\ufffd\ufffds#\ufffdሇ3\ufffdLl\ufffd\ufffd5\ufffd\ufffdN\u0011\r=\ufffd.\ufffd\u001aC\ufffdQH (OP\ufffdp\ufffd{\ufffd\ufffd\u0013;$\ufffd:\u0018YZ\ufffd\u0016\ufffd\ufffd\ufffd\ufffd\ufffd\u003c\ufffd9\ufffd?\ufffdB\ufffd\ufffd\ufffddX\u00106Cӂ%\ufffd\u000c\u0002EWd\ufffd\ufffdFF\u0003\ufffdv\ufffd}\ufffd$!!\u001a\ufffd\u000c\t\ufffd\u000b\ufffdL\ufffd\ufffd\ufffd\ufffd(Ow\ufffd\u000b)\ufffdb\ufffd\ufffd\ufffdQ\ufffd/\ufffd7\u00120\ufffdS\\\u001dEj\ufffd\ufffdx\u0012sP*\u0010\u0026ɤ\ufffd%!\ufffd\ufffd_\\\\\ufffd\ufffdCn5̧\ufffd\ufffd\ufffd\u000b\ufffd1\u000c\ufffdG\u0007zt\u003e\u000c\ufffdI}xcaq\ufffdz\u0016\u001d\ufffd\u0017\ufffd\ufffd\ufffd?G\ufffd\ufffd\u0016\ufffd\ufffd\ufffd\ufffd\ufffdK\ufffdx\ufffd-\u000fa\ufffdG/zG\ufffd/\ufffd\ufffdp\ufffd\u0005\ufffd[\ufffd?]\ufffd\ufffd!c\ufffd\ufffd\ufffd\ufffd^V\ufffd2\ufffd%\ufffd \u0007\"H\r\ufffd\ufffdA\ufffd\ufffd\ufffd\u001b\ufffd\u0006\r}\u001b\ufffdV\u00084\u0003\ufffdF\ufffd\u001c\ufffd\ufffdHDD\ufffd\ufffd\u0007Pi`\ufffd\ufffd8K\ufffd\u0002\u0013\ufffd\ufffdݩ\ufffd=\ufffd\ufffd{j\ufffd^\u0003\ufffdTo=U\ufffd\u0014LaZ\ufffd^\u000e\ufffd\ufffdb=\ufffd\u0001\ufffd\u0001.W\ufffd\ufffdf|\ufffd\ufffdN\ufffd\ufffd\u0005\ufffd\ufffd-\u00037P\ufffd`\ufffd\u0015\ufffd\ufffd5\tn٦\ufffdQ\ufffd\ufffdj\ufffd\\:Q:a\ufffd\u0006+\ufffd=GiFc\ufffd\ufffdt\ufffd\ufffdpoA2\ufffdz\ufffd\ufffd\ufffd\ufffd[\ufffd\n!\ufffd)h\ufffdZ\u000eh\u0026\u0006\u003e\ufffdfd\u003c,\u0002\ufffd\ufffd2e\u0001\ufffd1\ufffd\ufffd\ufffdԍ\u0014i{\ufffd\ufffdE\u0016\u001e\ufffdBX\ufffd\ufffd\ufffd#\ufffd\ufffdYG\ufffd\r\ufffd\ufffd=Z\ufffd\ufffd\u0006y\u0012\u003e\ufffd\u0012\ufffd \ufffdE\"l\n{A\ufffd\ufffd\ufffd6!\ufffdMoX\u0014\ufffd\ufffdr\u0012\ufffd\ufffd|:\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd7\ufffd\u0015\ufffd}\"\ufffd\u0000g\u003e\ufffd\u000f\ufffd\ufffdo c-,\ufffd\u0011W\ufffd\ufffd\u0004\u000b\"\u0012|\ufffd\u0003\ufffd\u0005\ufffd(\ufffdz\ufffd\u0012\ufffd\ufffd\ufffdJƛM\ufffd\u0017Co3vIѤ\u0007\ufffd\ufffdv\ufffd'\u0008\u001e\ufffd\ufffdU\ufffdF\ufffd\ufffd=\ufffdu\ufffd\u001f\ufffdP\ufffd\ufffdD\ufffd\ufffd6\u0019s\ufffd\u0007\ufffdA\u0000\ufffd\ufffd6\u0002\u0019\ufffd\u0003A\ufffde\ufffd\ufffd\ufffd)\ufffd\u0004ddu\ufffd\u0008kh\u0010r\ufffdV\ufffd\ufffd6\u000f\ufffd^\ufffd\ufffd\ufffd\u001c\ufffd-\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdތ\ufffd\ufffdg\ufffd\ufffdT\ufffdim\u0000}\ufffd\ufffdzC\ufffd5\ufffd\ufffdD\ufffd\ufffd\ufffd\ufffd|ei\ufffd\ufffd\ufffd\ufffd\u0008\ufffdJGK\ufffd\ufffd,;\ufffdd\ufffd\ufffd\t\ufffd\u0012\ufffd\ufffd\u0004ª\ufffdIG\ufffd\ufffdt[\\\ufffdP\ufffd\ufffd\ufffd\ufffd\\\ufffdq\ufffdf\ufffd\u001e\ufffdm\ufffd\ufffd\ufffd[FS\u0010\ufffd\ufffdx2Q\ufffd\t\ufffdB7n%u\ufffd\ufffdKŻ\ufffd \ufffdo\ufffd\ufffd\ufffd%6#\ufffdeV\ufffd\\K\u0005\ufffdM\ufffdV\ufffdT\ufffdkle:\u0006+\ufffdj\u001aO\ufffd:O-st\ufffd\ufffd\ufffd:\u000b\ufffd\u001bo\ufffd̝\u0014\u0004(\ufffd\u001erAB*E\ufffdO\ufffd\ufffd\t\ufffd-)\ufffd\ufffdӄ@ƒؠ2VS6\ufffd\ufffd\ufffd\ufffdt\ufffd_\ufffd\ufffdw\ufffd\ufffd\ufffdg\ufffd\ufffd\ufffd\u0003}v\ufffdσ\ufffd\ufffd\ufffd\ufffd\ufffd\u0001=\ufffd\u0005\ufffd\r\u0007p\ufffd\ufffd\u0006k\ufffd\ufffd5S\ufffdf\ufffdo\u0005N\ufffd1\ufffd\ufffd\ufffd\ufffdC\ufffd\ufffd\u0018SV\ufffdB\ufffd\u001bC@\ufffd\u003c\ufffd\u0016\u001c\u003c\ufffd\ufffd\ufffdx蝝~\u003cG\ufffd\u0026\u0005J7\ufffd\ufffd\u000c\ufffd64\ufffdL\ufffdT\"\ufffd^4I\ufffdV\ufffdȷ\ufffdfP_\ufffd\ufffd\ufffd\ufffd\ufffd!I\ufffd\ufffdL%!4Ŭ \ufffd\ufffd\ufffd\ufffd\ufffd5/\ufffdJ\ufffd\ufffdO\ufffd\ufffd\ufffd\ufffdn\ufffd~(O\ufffd\u0008'\u0013ym\u0012Ѽ/9\ufffdϋQLŋ\ufffd;\ufffd\u003c\ufffd\ufffd\ufffd\ufffdK\ufffd\ufffd\u0010֚UER\u0019-\ufffd\ufffd\ufffdQ]\ufffd_\ufffd\u000cl\ufffd\ufffd\u0007\ufffd\ufffd\r,\ufffd30\ufffd\ufffd7;\u0003۴\ufffdW\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd,\ufffd3\ufffdm\ufffd\ufffd\u0019\ufffdk\ufffdw׺\ufffd\ufffd5\ufffd\ufffd\ufffd\u0008\ufffd\n\ufffd\ufffd\ufffd\ufffd\u0005;p\ufffd3\u0004\u0017,\ufffd\ufffd\ufffd\u0014\\\ufffd\u0005\ufffd\ufffd\ufffd \ufffdE\ufffd\ufffd:v\ufffd\ufffdI\ufffd\ufffd\ufffd\ufffd\ufffd\u000f\ufffd\ufffd)\ufffd\ufffd\ufffd5-\ufffd\u000c\ufffd\ufffdC\ufffdr\ufffdnR\ufffdJČ\ufffd\ufffd(d\u003ee\ufffd\\\ufffd]\u0005G\rmw\ufffd\ufffd\u0008\ufffd\ufffdu\ufffd\ufffd\ufffd!\ufffd\ufffds/\ufffd\ufffd~_\ufffd\u0006\ufffdB\ufffd\ufffd\ufffdA\tv\ufffd\ufffd%\ufffd\ufffdf\ufffd\ufffd\u0026\ufffdv\ufffd%\ufffd\ufffdp[\ufffd\u0001\ufffdjt\ufffd\ufffd\ufffdg\ufffd2\ufffd,\ufffdL2\ufffdh\ufffd\ufffdsP\ufffd7P\ufffd\rL,J\u0017`UZ\ufffd\ufffd\ufffd\ufffd)SҔ\ufffd\u0011\ufffd\u0008U\u0007\r\ufffd\ufffd\u0017)/\ufffdM\ufffd\ufffd\ufffd\t\u000b\u0016g(c\ufffdG\ufffd\ufffd\ufffdj=\ufffdO7\r+\ufffd\u0006Ͷ\ufffdj[\ufffd;\ufffd \ufffd\ufffd\ufffd\ufffdev\ufffd^\ufffd\ufffd\ufffd\ufffd]0\ufffd\ufffd߲\ufffd\ufffd\ufffd\ufffd\ufffdߏ\ufffd9\ufffd\ufffd\ufffd =\ufffdd\\\ufffd-\ufffd\u000b\u000e% \u0011g\ufffd|A\ufffd\u001b\ufffdH3\ufffd\ufffd\u0004\ufffdw\ufffd\u0018\ufffdm\ufffdGiZZ\u0018\ufffd\ufffd\ufffd߅rifnNƦԬ\u0002\ufffd2\ufffd\ufffd\ufffd\ufffdX\ufffd\ufffdO$\ufffd\ufffd\ufffd\ufffd\ufffdɫ\ufffd\ufffd\ufffd\ufffd\ufffdG\ufffd\ufffd?\ufffd\u001b\ufffd\ufffd\ufffdͽ\u003c99\u003e\u0008x\ufffd\ufffd5\ufffd\ufffd*\ufffd\ufffdIQ\"e\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdQwP\ufffd\ufffd\ufffd~Ɯ\u000bp=\ufffd\ufffd-\ufffd\ufffdP\ufffd\u0002Ư\ufffd/\ufffdn\ufffd\ufffdT/\u000e\u000cotr\u001eP̎V\ufffdMX\ufffd\ufffd\ufffd\ufffdǺ\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd/\ufffd\ufffd-r\ufffdfx\ufffdY\ufffd\u0005J\ufffdvo\ufffdmK\ufffdTD\ufffd$\ufffd/\ufffd\ufffd\ufffdoD\ufffdg\ufffdp\ufffd?\ufffd4A\ufffd\ufffd\ufffd_\ufffd\u0017\ufffd\ufffd\u0016$\u0000\u0000","raw":"HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 1806\r\nContent-Encoding: gzip\r\nContent-Type: text/html; charset=utf-8\r\nDate: Thu, 07 Sep 2023 15:34:39 GMT\r\nSet-Cookie: AWSALB=+OTzt/cHdN9mzp1pbOgaURPkES7i2qq1lhilq3fOQUe1jfh6+KYbRfxvolgT0DjiZvlhhdhoctO4ecMahMhz5hagBN8ye+HbQJnxFvNdj2yozEGUDHaWbKKErer+; Expires=Thu, 14 Sep 2023 15:34:39 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=+OTzt/cHdN9mzp1pbOgaURPkES7i2qq1lhilq3fOQUe1jfh6+KYbRfxvolgT0DjiZvlhhdhoctO4ecMahMhz5hagBN8ye+HbQJnxFvNdj2yozEGUDHaWbKKErer+; Expires=Thu, 14 Sep 2023 15:34:39 GMT; Path=/; SameSite=None; Secure\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n"}} +{"timestamp":"2023-09-07T21:04:41+05:30","url":"https://ginandjuice.shop/my-account","request":{"header":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Connection":"close","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=wrNhEoOD0CeeH8cagJk19XHRP32b6YM2; AWSALB=+n84438yZsDA5AHP8fkJy0HrFaX/8Yq0pknF0+7HT5H3HB2hEd3G3yvICmv5HTZkzoDUZB/b7S77KFxPM4mi7pHuQYLCafFCtH2zJrM/l9i9JIn+2wcdTRh/fxDy; AWSALBCORS=+n84438yZsDA5AHP8fkJy0HrFaX/8Yq0pknF0+7HT5H3HB2hEd3G3yvICmv5HTZkzoDUZB/b7S77KFxPM4mi7pHuQYLCafFCtH2zJrM/l9i9JIn+2wcdTRh/fxDy","Referer":"https://ginandjuice.shop/catalog/cart","Sec-Ch-Ua":"\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"macOS\"","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"same-origin","Sec-Fetch-User":"?1","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"GET","path":"/my-account","scheme":"https"},"raw":"GET /my-account HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nConnection: close\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=wrNhEoOD0CeeH8cagJk19XHRP32b6YM2; AWSALB=+n84438yZsDA5AHP8fkJy0HrFaX/8Yq0pknF0+7HT5H3HB2hEd3G3yvICmv5HTZkzoDUZB/b7S77KFxPM4mi7pHuQYLCafFCtH2zJrM/l9i9JIn+2wcdTRh/fxDy; AWSALBCORS=+n84438yZsDA5AHP8fkJy0HrFaX/8Yq0pknF0+7HT5H3HB2hEd3G3yvICmv5HTZkzoDUZB/b7S77KFxPM4mi7pHuQYLCafFCtH2zJrM/l9i9JIn+2wcdTRh/fxDy\r\nReferer: https://ginandjuice.shop/catalog/cart\r\nSec-Ch-Ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"\r\nSec-Ch-Ua-Mobile: ?0\r\nSec-Ch-Ua-Platform: \"macOS\"\r\nSec-Fetch-Dest: document\r\nSec-Fetch-Mode: navigate\r\nSec-Fetch-Site: same-origin\r\nSec-Fetch-User: ?1\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Cache-Control":"no-cache","Content-Encoding":"gzip","Content-Length":"2094","Content-Type":"text/html; charset=utf-8","Date":"Thu, 07 Sep 2023 15:34:41 GMT","Set-Cookie":"AWSALB=W++/+JQbTxiN/OHBUJnq0z7nTriL7qXmdSqtD8FuMwXVMB+DdSW4k3k6e2NDavgrFaNmXo7qvphCRsGZUXDaguARWYOYLs5NMs+ZFvg0Q1fcK87mjQYc3/zgcDdq; Expires=Thu, 14 Sep 2023 15:34:41 GMT; Path=/, AWSALBCORS=W++/+JQbTxiN/OHBUJnq0z7nTriL7qXmdSqtD8FuMwXVMB+DdSW4k3k6e2NDavgrFaNmXo7qvphCRsGZUXDaguARWYOYLs5NMs+ZFvg0Q1fcK87mjQYc3/zgcDdq; Expires=Thu, 14 Sep 2023 15:34:41 GMT; Path=/; SameSite=None; Secure","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"body":"\u001f\ufffd\u0008\u0000\u0000\u0000\u0000\u0000\u0000\ufffd\ufffd[\ufffdr۸\u0011\ufffd~O\ufffdc\ufffd8\ufffd\tEQr\ufffdg\"i\u0026\ufffd\ufffd|s\ufffd\ufffd\ufffd\ufffd\ufffdn\ufffd/\u0019\u0008\ufffd$\ufffd \ufffd\u0012\ufffddާ\ufffdF\u001f\ufffd\ufffd\ufffdG\ufffd\ufffdt\u0001P2%\ufffd\"\u0015\ufffd\ufffd\ufffd O\u0026\u0016\ufffd\ufffd\u000f\ufffd\ufffd\ufffd\u000f\u000bq=\ufffd\ufffd\ufffd닟\ufffd\ufffd\ufffd\u0003\ufffd阏\ufffd\u0019\ufffd_\u0008~\u00063\ufffd#\ufffd\ufffd\u003er\u0026\ufffd\ufffd,\ufffd\ufffda\ufffdR%\ufffd\ufffdP\u0015p\u003c6b4\r\ufffdR\ufffd\"X\ufffdD\ufffd\r\u001dh@)\ufffdC\ufffdsNՌR\ufffd\u0004f \u0000P\ufffd\u0000LLw\u0003\ufffdTc$pL\ufffdޜ\ufffdE\"S\ufffd!\"\ufffd\ufffdB\u000f\ufffd\u0005\ufffd\ufffdl\u0018\ufffd9#Է\u000f\ufffdQ\ufffdhꃆ0\u0003\ufffdC!\ufffd\u0012\ufffd\")K4R)\u0019z%\ufffdn\u0015\u003c`\ufffd;\ufffdD\ufffdLb\u0000\ufffd\ufffd*o4\u0008܈\ufffd\u0010~$\ufffd=`t\ufffd\ufffd\ufffd4\ufffd\ufffd\ufffd-\ufffdc\ufffd\ufffdU\ufffdc1\ufffd8N?\ufffd\ufffd\ufffdZ\ufffd\ufffd\ufffd\ufffdt\ufffdS\ufffd0!2\u0013\u001a\ufffd\ufffd\ufffd\t\ufffd\u0002\ufffd\ufffd[\ufffdc\u0006VB73\ufffd\u000c\u0002'\ufffd\ufffd?xp\ufffd\ufffdXF9\u0012S\u001f'I\t6bsĢ\ufffdW\ufffd\ufffd\ufffdY݂(\ufffdL\nD8Vj\ufffd9o\ufffd#\ufffdt\ufffd\ufffd\ufffd\u0001vP\ufffd\ufffdf~~\ufffd1\ufffd\ufffd\u001fF\u0011\ufffdlLS\ufffd)\ufffd\ufffd\u003c\ufffd\u0002\u003eö\ufffd\u0005\u001d#Б3\ufffd\ufffd\ufffd0\u000f\ufffd\n\u001a\ufffd\ufffdL\ufffd\ufffdJ315B/\ufffdX%o\ufffd\u0003\u0019g:wMf)Ц:\ufffd\n\u000cT\ufffdWK1\u003e\ufffd!)b\t\ufffd\u0005[JS\ufffdx\"\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd!\u001a\ufffdp\ufffd-\ufffd뽽\ufffd\ufffd\u0018\ufffd˽}׻\ufffd\ufffdVQ\ufffd\u003e^\u001b\ufffd%C\ufffdM\ufffd\ufffd\ufffd\ufffd/^\ufffd\ufffd\\k\ufffd\ufffd\ufffd\ufffd\ufffdW\ufffd\ufffd\ufffdZC\ufffd\ufffd\u001c\ufffd\u003c\ufffd\ufffd\ufffdG8\ufffd|\ufffdI\ufffd=ÿ\ufffd'\ufffd%\ufffdd1\u001c\u001e\ufffdZ\ufffd\ufffd)6:\ufffd\u003ePMKp\ufffd[\ufffd\ufffd֫h\ufffdW\ufffd\u001d{\ufffdO\ufffdF987*\ufffd\u0001i\ufffd\ufffd\ufffdڭ\ufffd\u0016\u001c\u001b\ufffd_\ufffd.\ufffd\ufffd\ufffd\ufffd\ufffd󺝾x\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u0002ָ\ufffd\u00135Ă\u0019\ufffdU\ufffd\ufffd\u0000;.;\n*`*\ufffd\ufffd%8\ufffd˩\ufffdh\ufffd\ufffd\u001a\ufffd\u0000\ufffdt\u003c@ـ\ufffd\u001b\ufffd\ufffdc\u0004\ufffd\ufffdf%\ufffd\ufffd\ufffdk\ufffd,\ufffdZ\ufffd\ufffd\ufffd\ufffd5ήP\ufffd\ufffd\u0018\ufffd\ufffd{\ufffd\u0008\ufffd-HΚ\ufffd\ufffd ^N=δ\u0006Rp\u0006\ufffd\u0002\ufffdL\u000cv\u0002\ufffdHe\ufffd\u0011\ufffdj\ufffd\ufffd\u0006\u0017\ufffd\ufffd\ufffd\ufffdڍ\ufffdj\ufffd\ufffd\ufffd?\ufffdZx,3퍮M\u001ci\ufffd\ufffdO\ufffd\ufffd \ufffd\ufffd\ufffd]\u0008bP\ufffd\ufffdwv\ufffd\ufffd\ufffd\ufffd\u0019\ufffd\u0005\u0007\ufffd\ufffd\ufffd)\ufffd|8\ufffd\u000cs\u0011M\ufffd\ufffd\ufffd\ufffd\ufffd/\ufffd\ufffd\ufffdT\ufffd\ufffdh\u0000\ufffd\ufffd\ufffd|8i\ufffd\ufffd\ufffd\ufffd\u0017\u0000.\ufffd; \ufffd\ufffd\ufffdL\ufffd\ufffd\ufffd\ufffd\ufffd\u000068\ufffd`dFɝOXJ8\ufffd`\ufffd\ufffd\r\ufffd6;X\ufffd@;\ufffd\ufffd,\ufffdm\ufffd\r\ufffd3Tf\\\"\ufffd\ufffdZ]\u000b\ufffd**\ufffd^\ufffd\ufffd\ufffd?\ufffdF\ufffd\ufffd\ufffdͱ\ufffd\u001e\ufffd\u000b\u0002\ufffd\ufffd\ufffd\ufffd\ufffd\rb\rL\ufffd\ufffd|s\ufffd\ufffd\ufffd\t\ufffd2\u000e\ufffdP\ufffd\ufffd+\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdK\ufffd\ufffdk\ufffdg\u001b\"x\ufffd5e\u0010\ufffd\ufffdr5\ufffd\ufffd\ufffd\u003e\ufffd~\ufffde\ufffdAP{\ufffd֜\ufffd\u0015\ufffd\u0015\ufffdʆ\ufffd=\ufffd!\u00112\ufffd\ufffd\ufffd\ufffd\ufffd̵cH\tV\ufffdAU\ufffd\ufffd\ufffdFx+9C\ufffd\t\ufffdq\r1\u000f\u001c\ufffd\ufffd\ufffd.5\ufffd\u0014\ufffdv\ufffd\ufffdu\u0003\u0003\ufffd]\u0007\u001b\ufffd\u0018|\ufffd\ufffdAx\ufffd\ufffd|\ufffdʱ\ufffd\ufffd\ufffdƷc6l\"Ss\u0014\ufffd=œ\ufffd\r\ufffdO\ufffd\u003en\u001c\ufffd4nsb\ufffdx\ufffd1Є\ufffd\ufffd\ufffdf\u001f\u0002\ufffd\u0004\u0006\ufffd\ufffdk\ufffd\ufffdX\u003c\ufffd \ufffd҄\ufffd\ufffd3\ufffd;\ufffd\ufffd\ufffd\ufffdt[[1\ufffd-\u0006\ufffd\ufffd\ufffd\u003e\ufffd\ufffdfF\ufffd\u0003\ufffd\ufffdr\u0008{\ufffdo9v\ufffd\u003e-$\ufffd\u0004\ufffd\ufffd\ufffda\ufffd\ufffd\ufffdv\u0007\ufffd\ufffd%̖\ufffd\u0011\ufffd\ufffd\u0019o\ufffd\"\ufffd0l\\_\ufffd\ufffdH\ufffd\u000e\ufffd\ufffd\ufffd\u001c\ufffd\ufffd\ufffdu\u0017\ufffd60\u0011\ufffd1\ufffdY\u0010\ufffd\u0006\ufffdn\ufffd\ufffdEH\ufffdx\ufffdpӤ\ufffd[\ufffd\ufffdXg\ufffd\ufffd\ufffd\ufffd\u0019\ufffd\ufffd\ufffd\u0012\ufffd\ufffdds%\ufffd[\u0026\ufffd9n\u001e\ufffd\ufffd\ufffd\ufffdn\ufffd\ufffd\ufffd\ufffd\ufffd|\ufffd\ufffd\ufffd\ufffd\ufffde\ufffdV6ii\ufffd\u0016\u001a\ufffd\u0011\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd}m\ufffd\ufffd^\ufffd\ufffd\u0000\ufffd\u003e\ufffd/\ufffd\ufffd\ufffd\ufffd\ufffdק\ufffd\ufffd\ufffd\u0013\ufffdE\ufffd\ufffd@\u0017%\ufffd\u0003]쵠g\ufffd\ufffd\ufffd\ufffd\\|\ufffd}\ufffd1\ufffd\ufffd\ufffd\ufffd\u001c\ufffd\ufffd\ufffdtq\ufffd뜞u\ufffd81:\u003c\ufffd. \ufffd89\ufffdEI\ufffd@\u0017{-\ufffd\u0019\ufffd\ufffd\ufffd\ufffd؂㘉\ufffd\ufffd\ufffd\ufffd\ufffdB\ufffd;\u0013\ufffd\ufffd\ufffd\ufffd\ufffd'`\ufffd\ufffd\ufffd\ufffdY\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\t\ufffd#\u003c\u003e0GI\ufffd\ufffd\u001c{-\ufffdp/)\u000f\ufffdC\ufffdKN\ufffd\ufffdO@\u0017\ufffd\ufffd\u0003]\ufffdd\u000ftQ\ufffd]\ufffd\u0006\ufffdat\ufffd\ufffd\ufffdms\ufffdX\u0013)5Xk\ufffd\ufffd$\ufffd\ufffd\ufffdz\ufffd;\\7\ufffd\ufffd{\ufffdmdN'\ufffd\u0015ǎ\u0017\ufffd\ufffdc\ufffd-\ufffd\ufffda\ufffd\ufffd\ufffd7\ufffd\ufffd\ufffd+(fʕ\ufffd`\ufffd|\ufffd\ufffd\ufffd)m\u0019S\ufffd\ufffd\u0005\ufffdOo\u0007D2\ufffdQ2\ufffdL\ufffd\ufffdB\ufffd\u003cZ\ufffd\ufffd\"\"\ufffd8\u0013L\ufffdHN\ufffd\u0014\ufffd\ufffd\ufffdЭ%\ufffd\t\u0016X3\ufffd^\ufffdr\u0016zOx\ufffd\ufffd1\ufffd\ufffd\u000b\ufffd\ufffd\u0003\ufffd\ufffd\ufffdJXm@\u000ezL\r\u0015Ő8\ufffd\u0006h\ufffd\ufffdX\ufffd\ufffd\ufffdzx6ȱLig\ufffd\ufffdb\ufffd\ufffd\u000f\ufffdMG\ufffd\ufffdG\ufffd\ufffd^\ufffdE\ufffdV\u001dU\ufffd\u001eU\ufffd\ufffd\u0016\r-\ufffdz\u000f\ufffdOc\ufffd0\u0015\ufffdc\ufffdg\u0012\ufffd\u0012\ufffdtф\ufffd\u000f\ufffd\ufffd\\=\u00004\ufffd:\u0013I\ufffd\ufffd\u000c\ufffd\ufffd\u0018Đ\ufffd\ufffd\ufffd\ufffdI)m\u0001\ufffd\ufffd\ufffdpL\ufffdL\ufffd\ufffd\u0014\ufffd|\ufffdM8\ufffd`\ufffdM\u0001\\\t\ufffd\ufffdX\u0014Q\ufffd\u0015%jD\ufffd\u0013\u000f\ufffd1\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdn\u003c\u0017\ufffd/\ufffd\ufffd]\ufffdO~\ufffd=\ufffd\ufffd}\ufffdk\ufffd\ufffd\ufffd\ufffd~k:\ufffd\u0006\ufffd5\ufffd\ufffdKM;-\ufffd$fzt\ufffd\ufffd\u000c\ufffdZ\ufffd{W\ufffd\ufffd恈\u0019[\ufffd\r#2K\ufffd\ufffd]\ufffd\u001e\ufffdN\ufffdq\ufffd\ufffdݰB¥\ufffd\ufffdr\ufffdR\u0010\ufffd\ufffd]\ufffd|a\ufffd\ufffd\ufffd*\ufffd\ufffdh\u0014\ufffd\ufffd\ufffd\ufffd\ufffdU\ufffdPs\ufffdl#\u001c\ufffd\ufffd\ufffd8\ufffd \ufffd\u0004\ufffd\ufffd11\ufffdy\ufffdj\ufffd$\ufffd|\ufffd\ufffd[\ufffd~\ufffd7In\n\ufffd|\ufffd㍜a\ufffd\ufffd4pE\u0026\ufffd\ufffdi\ufffd\ufffd-v`\t]\ufffd\ufffd6a\ufffd4tmo\u000c\ufffd\ufffd\ufffd\ufffd`G\ufffdlS\ufffd:\u0000e\ufffd\u0013\u0000\ufffd\ufffdy\u001b6+u\ufffd\"\ufffdv\ufffd\ufffd\ufffdL{\ufffd\u0026\ufffdwI\ufffds\ufffdM!\ufffd\ufffd\ufffdT\ufffdي5S@\ufffd\ufffd\ufffd\ufffd\u000b\ufffdj4\ufffd\ufffdfԲ\ufffdi\ufffd\u00126+\ufffd͵{(\ufffdx\ufffd\ufffd@m`\ufffd\ufffdS\u000f\ufffd.e\ufffdY\ufffd\u000b\ufffd\ufffd\ufffd\ufffd[\ufffd\ufffdo\ufffdmE[+\ufffd\u001b\ufffd\ufffd\ufffd\ufffdL2\ufffd\u003e\u0001C@\u001fu\ufffdi\ufffd\ufffd\ufffd^\ufffd׬\ufffdXim2S\ufffd\u0002U'$\ufffd\ufffd\ufffd\u001ei\ufffdժ@jW\ufffdQ\u001a\ufffd$\ufffd\u001d\ufffd\ufffdP5\ufffdd\ufffdC\u0019\ufffd\ufffdkw(\u0003,\ufffd\ufffd_\u0010S}Eب\ufffd\ufffdx\ufffdIJ]l\ufffd\ufffd\ufffd\ufffd\u0015\ufffdvV\n\ufffd\ufffd\ufffd\ufffd\ufffd7p$\ufffd?v\ufffd?\ufffd\ufffd;\ufffd\u00041\u0000\u0000","raw":"HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 2094\r\nCache-Control: no-cache\r\nContent-Encoding: gzip\r\nContent-Type: text/html; charset=utf-8\r\nDate: Thu, 07 Sep 2023 15:34:41 GMT\r\nSet-Cookie: AWSALB=W++/+JQbTxiN/OHBUJnq0z7nTriL7qXmdSqtD8FuMwXVMB+DdSW4k3k6e2NDavgrFaNmXo7qvphCRsGZUXDaguARWYOYLs5NMs+ZFvg0Q1fcK87mjQYc3/zgcDdq; Expires=Thu, 14 Sep 2023 15:34:41 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=W++/+JQbTxiN/OHBUJnq0z7nTriL7qXmdSqtD8FuMwXVMB+DdSW4k3k6e2NDavgrFaNmXo7qvphCRsGZUXDaguARWYOYLs5NMs+ZFvg0Q1fcK87mjQYc3/zgcDdq; Expires=Thu, 14 Sep 2023 15:34:41 GMT; Path=/; SameSite=None; Secure\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n"}} +{"timestamp":"2023-09-07T21:04:46+05:30","url":"https://ginandjuice.shop/logout","request":{"header":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Connection":"close","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=wrNhEoOD0CeeH8cagJk19XHRP32b6YM2; AWSALB=W++/+JQbTxiN/OHBUJnq0z7nTriL7qXmdSqtD8FuMwXVMB+DdSW4k3k6e2NDavgrFaNmXo7qvphCRsGZUXDaguARWYOYLs5NMs+ZFvg0Q1fcK87mjQYc3/zgcDdq; AWSALBCORS=W++/+JQbTxiN/OHBUJnq0z7nTriL7qXmdSqtD8FuMwXVMB+DdSW4k3k6e2NDavgrFaNmXo7qvphCRsGZUXDaguARWYOYLs5NMs+ZFvg0Q1fcK87mjQYc3/zgcDdq","Referer":"https://ginandjuice.shop/my-account","Sec-Ch-Ua":"\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"macOS\"","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"same-origin","Sec-Fetch-User":"?1","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"GET","path":"/logout","scheme":"https"},"raw":"GET /logout HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nConnection: close\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=wrNhEoOD0CeeH8cagJk19XHRP32b6YM2; AWSALB=W++/+JQbTxiN/OHBUJnq0z7nTriL7qXmdSqtD8FuMwXVMB+DdSW4k3k6e2NDavgrFaNmXo7qvphCRsGZUXDaguARWYOYLs5NMs+ZFvg0Q1fcK87mjQYc3/zgcDdq; AWSALBCORS=W++/+JQbTxiN/OHBUJnq0z7nTriL7qXmdSqtD8FuMwXVMB+DdSW4k3k6e2NDavgrFaNmXo7qvphCRsGZUXDaguARWYOYLs5NMs+ZFvg0Q1fcK87mjQYc3/zgcDdq\r\nReferer: https://ginandjuice.shop/my-account\r\nSec-Ch-Ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"\r\nSec-Ch-Ua-Mobile: ?0\r\nSec-Ch-Ua-Platform: \"macOS\"\r\nSec-Fetch-Dest: document\r\nSec-Fetch-Mode: navigate\r\nSec-Fetch-Site: same-origin\r\nSec-Fetch-User: ?1\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Content-Encoding":"gzip","Content-Length":"0","Date":"Thu, 07 Sep 2023 15:34:47 GMT","Location":"/","Set-Cookie":"AWSALB=L9mzxS2E7L48F2f8vDl+MO39oxE9+M9tFcy+STvbOJYFf3nTXFODardtaxqjkhm4leDa2G+3/sDz4EMXNmu/mSP4LIZBJANKWnr2cfqgpLpusUPISK8wkBli4N0D; Expires=Thu, 14 Sep 2023 15:34:47 GMT; Path=/, AWSALBCORS=L9mzxS2E7L48F2f8vDl+MO39oxE9+M9tFcy+STvbOJYFf3nTXFODardtaxqjkhm4leDa2G+3/sDz4EMXNmu/mSP4LIZBJANKWnr2cfqgpLpusUPISK8wkBli4N0D; Expires=Thu, 14 Sep 2023 15:34:47 GMT; Path=/; SameSite=None; Secure, session=UeLc4S4hyi19aLba4fvsmba8ZhjwmwH2; Secure; HttpOnly; SameSite=None","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"raw":"HTTP/1.1 302 Found\r\nConnection: close\r\nContent-Encoding: gzip\r\nDate: Thu, 07 Sep 2023 15:34:47 GMT\r\nLocation: /\r\nSet-Cookie: AWSALB=L9mzxS2E7L48F2f8vDl+MO39oxE9+M9tFcy+STvbOJYFf3nTXFODardtaxqjkhm4leDa2G+3/sDz4EMXNmu/mSP4LIZBJANKWnr2cfqgpLpusUPISK8wkBli4N0D; Expires=Thu, 14 Sep 2023 15:34:47 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=L9mzxS2E7L48F2f8vDl+MO39oxE9+M9tFcy+STvbOJYFf3nTXFODardtaxqjkhm4leDa2G+3/sDz4EMXNmu/mSP4LIZBJANKWnr2cfqgpLpusUPISK8wkBli4N0D; Expires=Thu, 14 Sep 2023 15:34:47 GMT; Path=/; SameSite=None; Secure\r\nSet-Cookie: session=UeLc4S4hyi19aLba4fvsmba8ZhjwmwH2; Secure; HttpOnly; SameSite=None\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\nContent-Length: 0\r\n\r\n"}} +{"timestamp":"2023-09-07T21:04:47+05:30","url":"https://ginandjuice.shop/","request":{"header":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Connection":"close","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; AWSALB=L9mzxS2E7L48F2f8vDl+MO39oxE9+M9tFcy+STvbOJYFf3nTXFODardtaxqjkhm4leDa2G+3/sDz4EMXNmu/mSP4LIZBJANKWnr2cfqgpLpusUPISK8wkBli4N0D; AWSALBCORS=L9mzxS2E7L48F2f8vDl+MO39oxE9+M9tFcy+STvbOJYFf3nTXFODardtaxqjkhm4leDa2G+3/sDz4EMXNmu/mSP4LIZBJANKWnr2cfqgpLpusUPISK8wkBli4N0D; session=UeLc4S4hyi19aLba4fvsmba8ZhjwmwH2","Referer":"https://ginandjuice.shop/my-account","Sec-Ch-Ua":"\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"macOS\"","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"same-origin","Sec-Fetch-User":"?1","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"GET","path":"/","scheme":"https"},"raw":"GET / HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nConnection: close\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; AWSALB=L9mzxS2E7L48F2f8vDl+MO39oxE9+M9tFcy+STvbOJYFf3nTXFODardtaxqjkhm4leDa2G+3/sDz4EMXNmu/mSP4LIZBJANKWnr2cfqgpLpusUPISK8wkBli4N0D; AWSALBCORS=L9mzxS2E7L48F2f8vDl+MO39oxE9+M9tFcy+STvbOJYFf3nTXFODardtaxqjkhm4leDa2G+3/sDz4EMXNmu/mSP4LIZBJANKWnr2cfqgpLpusUPISK8wkBli4N0D; session=UeLc4S4hyi19aLba4fvsmba8ZhjwmwH2\r\nReferer: https://ginandjuice.shop/my-account\r\nSec-Ch-Ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"\r\nSec-Ch-Ua-Mobile: ?0\r\nSec-Ch-Ua-Platform: \"macOS\"\r\nSec-Fetch-Dest: document\r\nSec-Fetch-Mode: navigate\r\nSec-Fetch-Site: same-origin\r\nSec-Fetch-User: ?1\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Content-Encoding":"gzip","Content-Length":"2128","Content-Type":"text/html; charset=utf-8","Date":"Thu, 07 Sep 2023 15:34:47 GMT","Set-Cookie":"AWSALB=EeUP0iKoblECp3EJrR7G3VkVXCB824uzYMUKxXSQm3dzHWM8O+zBi9JJ1ycx4rw89sTDpO5ron/v2igr7ki2rVUBc9cHkO/BE+HpmdhpXzFd01fKZVWnAa9/Muom; Expires=Thu, 14 Sep 2023 15:34:47 GMT; Path=/, AWSALBCORS=EeUP0iKoblECp3EJrR7G3VkVXCB824uzYMUKxXSQm3dzHWM8O+zBi9JJ1ycx4rw89sTDpO5ron/v2igr7ki2rVUBc9cHkO/BE+HpmdhpXzFd01fKZVWnAa9/Muom; Expires=Thu, 14 Sep 2023 15:34:47 GMT; Path=/; SameSite=None; Secure","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"body":"\u001f\ufffd\u0008\u0000\u0000\u0000\u0000\u0000\u0000\ufffd\ufffd\u001a\ufffdr\ufffd6\ufffd\ufffdb˻\ufffd\ufffd\ufffdP\ufffd$\ufffd\ufffdL$u\u0012;\u001f\ufffd\ufffd\ufffd[{\ufffd\ufffd\ufffdd \u0012\ufffd\ufffd\ufffd\u0000K\ufffd\ufffd\ufffdH}\ufffd{\ufffd[\u0000\ufffdLI\ufffdH\ufffd\ufffdLn\u0026\u001e\ufffd-\u0000\ufffd\ufffd\ufffd~/\ufffd\ufffd\ufffdg\ufffdO\ufffd\u003e\\\ufffd\ufffd\ufffdN\ufffd仑\ufffd\u0007\ufffd3\ufffdS\u0012\ufffd\ufffdvș\ufffd\u0004\ufffd\ufffd\ufffd\ufffdAF\ufffd̳\ufffd\ufffd\ufffd\ufffd\ufffd\u0001\ufffdY\u0010*\u0015\ufffd\ufffd\ufffd_\ufffd[;\ufffd\ufffd\t\ufffd(\u001f+]p\ufffd\ufffd\ufffd\ufffd6b\ufffd\u0004\u0012T\ufffdH\u0026\ufffd\ufffd\t$T\u0013\u0010$\ufffdco\ufffd\ufffd2\ufffd\ufffd\ufffd \ufffdBS\ufffd\ufffdޒEz\u003e\ufffd肅Է\ufffdǐ+\ufffd\ufffd\ufffd!\ufffd\ufffd\ufffdXH\ufffdBM\ufffd\u0019K5\ufffd,\u001c{\u0015\ufffd\ufffd\u0015\u000eH\ufffd{H\ufffdr\ufffd\u0026H\ufffdw\ufffd\ufffd\ufffd(p\u0018\ufffdI\ufffd\ufffdL\u000e \ufffd\ufffd\u0014O\ufffd\ufffd\ufffd\u000e\ufffdɂ\ufffdY\ufffd\ufffd:\u0011q\ufffdI\ufffd\ufffd\ufffd?\ufffd\ufffd6Q\ufffdLs:y+\u0013\n\u003e\ufffda\u0002\u001e\ufffd$}\u000e?\ufffd(\u001f\ufffd\ufffd\ufffdt\u00148\u0010\ufffd\ufffd\ufffdV\ufffd\ufffd\ufffd\ufffd\n\u0010\ufffdOҴB0b\u000b`\ufffdثj\ufffd\"Pw\u0014\u001aj\u0026\u0005\ufffd\ufffd(5\ufffd\ufffd\ufffd\ufffd\u0011u\ufffd\ufffd\ufffd\u0016\ufffdEJw\ufffd\ufffd\ufffd՜)\ufffd_\u0002\u0011\ufffdlJ3\ufffd)/`\ufffds\ufffd\ufffdQ\ufffd\ufffd\ufffdS@\u001e9\u000b\ufffd\ufffd\u0015\ufffda\ufffd\ufffd\u0011\ufffdd\u0006\ufffd*\ufffdDl\ufffd\u001e\ufffd\ufffdJ\ufffd\ufffd\u0010\u0019g\ufffdpS\ufffd(8\ufffdz\ufffd\u000c\ufffdTJ\ufffdG1\ufffd\ufffd\ufffd\u0014\ufffdD\ufffdBe\ufffd̘\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdM\u001f\ufffd\ufffd_/\ufffd\ufffd\ufffd\ufffd`c5\ufffd\ufffd\ufffd\ufffdЭ\ufffd\ufffdqVT\ufffdO6\ufffd\ufffdt\ufffd\ufffd\u0019\u003c\ufffdn\ufffd\ufffd\u0001T\ufffd\ufffd\u0018\u000e7\ufffd'\ufffd\ufffd\ufffdRZc ZN\u001f\u001e\ufffd\u000f\ufffdӟ\ufffdy\ufffd\ufffd\ufffd5#ܤ\ufffd\u001b\ufffd\ufffd\ufffdx|\ufffdh\ufffd+\ufffd\ufffd\ufffd\ufffdz\ufffd\ufffdV\ufffd\ufffd\ufffd\ufffd\ufffdܠfnX3w\ufffdM.r\r\u0005\ufffd5\ufffdz\u0000-AϩU\ufffd\ufffdh\ufffd(\ufffd\u001as\t\ufffd\ufffd\u0005\u0001\ufffd\ufffd\ufffd\ufffd\ufffd#g\ufffd/\ufffdFG5d\ufffdy;P\u0013R\u0008C\ufffdz\ufffd\u001c\u0011\u0017Ŏ\ufffd\u001a25\ufffd\ufffd\rm\u003e\ufffd\ufffdD?\ufffd\ufffd\u0006\ufffd\u0001iX\ufffd%e\u001d\ufffdo\ufffd\ufffd\ufffd\u0008Ұ\ufffd\u001a\"\ufffd+\ufffdFX\u0016[\ufffd\ufffdK\ufffd5Ʈ \ufffdXB\ufffdj\u001cw\ufffd$g\ufffd@\u0016\ufffd\ufffd\ufffd\ufffd\ufffdZcPp\u0002\ufffd\u0002\ufffdL\ufffdrB\ufffd\ufffdd\ufffd\ufffdZ5\ne\ufffd\\\ufffde\ufffd\ufffds7\ufffd\ufffd\ufffdĿ_\u0015[d*s\ufffdM\ufffd\u001b?\ufffd2+\ufffd\u0010s\ufffd \ufffdw6!\ufffdA)\ufffd{6\"\u0012\ufffd2\u0017\ufffdgaE*I\ufffd\ufffd\ufffd\ufffd\u001d\ufffd\"\ufffd\ufffd0\ufffd\ufffd\u0016o\ufffd)Zxl\ufffd!\ufffd\n\ufffd\ufffd\ufffd\ufffdؠ\ufffdn\ufffd\ufffd\u0026\ufffd\u001a:]\ufffd[\ufffd\ufffd\u001b\ufffdA\ufffd\ufffd=\ufffd\ufffd9T\u000f~.c`\ufffd3ǖn'\ufffd\ufffd_\ufffd)\ufffd\ufffd|O\ufffd\ufffd\ufffdvwz\ufffda\ufffd!ɶ\ufffd\ufffd\u000c|\ufffdY1\ufffdj2_\ufffd̀\u001d\ufffdנZӽ\ufffd~\ufffd\ufffdv\u003er\ufffd\ufffd}-\ufffd\ufffdS܍l\ufffdf,\ufffdM\ufffd\ufffdN\ufffd\u003eL\ufffd\u0000\ufffd\ufffdkT\u001a\u0005\ufffdy\ufffd!\ufffd\ufffdL\ufffd\ufffd\u0013[P6#c\ufffdb:\ufffd9\u0016\ufffd-Up\ufffd\ufffdz\ufffd\ufffd\ufffd\n\ufffd\ufffd\u0004\ufffd탳\ufffd.\ufffd\ufffd\u0005-5\ufffd\ufffd\ufffdo\u0019y\ufffd\u0010\u0003\ufffdܰ\ufffdŲ-4\\\ufffd\ufffd'f\ufffd*\ufffd\ufffdc\ufffd\ufffd\ufffd\ufffd!\ufffd\ufffd\ufffd0@8Ǻ\ufffd%\ufffd7Tt;̚ndE\ufffd\"\ufffd\ufffd\ufffd}\ufffdVļ\ufffd:\ufffdۙl`\ufffd\ufffdS\ufffd\ufffd4v\u0017\ufffdK\r\ufffd\u0007\u0003\ufffd\u0016\ufffdZM\ufffdI8c\ufffdlp\ufffd\ufffd\ufffd\ufffd?d\ufffd#\ufffd\u0013\u0005\ufffdxr\ufffd\ufffd\u000c\u0014r\ufffd!\ufffd\ufffd\ufffd\n\ufffd\u000cb\u0026\ufffdǮ-\ufffd\ufffdd\ufffdݑ)E\ufffd'\ufffdSS\r+Ԭ\ufffd\u0015Vl\ufffde\ufffd\ufffdom\ufffd=\ufffd%\u001d:F7\ufffd\u000b\ufffds\ufffdu\u0019\ufffd@P\u001a\ufffd\ufffdN\ufffd\ufffdi܍-@\ufffd\ufffd\ufffd\ufffd}\ufffdF\ufffdf\ufffd[~\ufffd\u000e'X)\ufffd\ufffd\ufffd\ufffdNE\u0018\ufffdNL%+?\ufffd\ufffd\ufffd\ufffd\ufffd\u000et,-\ufffd\ufffd\ufffd%\ufffdMW\ufffd\ufffd\ufffdV\u0014\ufffd6PA\ufffd욦\ufffd~\ufffd\u00113g\ufffd\ufffd\"\ufffd\ufffd\ufffd|8\ufffdX\ufffd\ufffd+\ufffd\u000f\ufffd2\ufffd\ufffdR\ufffd\u0018\u0002\ufffd\u0007\ufffd\ufffd\ufffdl3b\ufffd\u0015\ufffd\ufffdpUuXl\ufffdBL\u0018\ufffd\u001c\u001e\ufffd\ufffd\u001c7\ufffd\ufffd\ufffdT\ufffd\ufffd\ufffd\ufffdԈ\ufffdө\ufffdĺ\u0015\u0002\u001d,`pO\u0016\ufffd\ufffd\u001f\ufffdr\u0017\ufffd჉\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd'wU\ufffd\ufffdOz\ufffd~\ufffd\ufffdV\ufffd\ufffd\ufffd\u0014~|\ufffd\ufffd_g9\ufffd\ufffd\u001e\ufffd\u0019'\ufffd\ufffd\ufffd\ufffd\ufffd٠\ufffd\ufffd\ufffdש斲ʁ\ufffdON\ufffdFr_U\ufffd\ufffdj9\ufffd\ufffd\ufffd\u0018R\ufffd.Ir\u000b\ufffdOe;\u000e\ufffd\ufffd\u0002\u0005\u0006\ufffdG\ufffd\ufffdyE\ufffd\ufffd\ufffdaU\ufffd\ufffdw\ufffd\ufffd^{\u00176\ufffd\u000f\u0026/\ufffd-aY\u0001g\ufffd@\ufffd\u001e\ufffdr\ufffda\u003cXC\ufffd*A\ufffdx\ufffd8\ufffdj3\ufffd\u000eV\ufffd\ufffd$,ȗ\u0015\ufffdI'\ufffd\u000e\u000f\u0010\ufffd\u0015\u0016\ufffd\ufffd2I9\ufffdB\ufffd^Dz\ufffd\u0005E\ufffd\ufffd\t\ufffd\ufffd\u001eh\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdn\ufffd\ufffd\ufffd\ufffd\ufffd{\u0026%\ufffd\u001c\ufffd2Ò\ufffd\ufffd\ufffdݪH\u001dBS\ufffd\ufffdK\ufffdә\ufffd\u0011\ufffdso\ufffd\ufffd\u0013\ufffdFh_\ufffd@\u000bxG1}@”{a\"\u001c|P\ufffd\ufffd\ufffd\ufffdS\nB.\ufffd\ufffd\t\ufffd\ufffd?Kl\ufffdLͱ4\u001dߒE\u0014B\ufffd$\ufffd\ufffdn\ufffd\ufffd\ufffd\ufffd\ufffd\u0008\ufffdm7#\u00023L\ufffd\u001e\ufffd\ufffd)z\u0013\ufffd\\\ufffd\u0005\ufffdD\ufffd\ufffd\ufffd9B\ufffd\ufffd\ufffd\u000f\ufffd\r\ufffd\ufffd\ufffdy\u000c\ufffdDQ\u001c\ufffd\ufffds2\ufffd\ufffd\ufffd7pl('2\ufffd-\ufffdF\ufffd\ufffd\ufffdh\ufffd\u0015q}\ufffdw\ufffd\ufffd\\\ufffd\ufffd\ufffdG\ufffdZY\ufffd7\ufffd\u0015\ufffdw+\ufffd,\ufffd ´\ufffd'Tϥi\ufffdM\u0014pS\ufffd\ufffd@\ufffd\ufffd\ufffd%\ufffd\ufffd*L\ufffd\ufffdF1\ufffd\ufffd\ufffd\u000c{i\ufffdHJ\u0013̋\ufffd%\ufffd}D\ufffd\u000e\ufffd\\\ufffd\ufffd4\ufffd\ufffd\ufffd\u0014\ufffd\"\u003cm[\ufffdSKޛ\ufffd(\ufffd\ufffd+ߚC\ufffd\ufffd\u003c0\ufffd\u001e\u000e\ufffd͟\ufffdIt\u001a\ufffd.^\u003cS\ufffd\ufffd)5\ufffd\ufffd\ufffd\ufffd\u001f\ufffd_/\ufffd^\ufffd\ufffd\ufffdж_\u0019P\ufffd\ufffd@\ufffd\ufffd(\ufffd\ufffd\ufffd\ufffd\ufffdJ2\ufffd\ufffd-ߥ\ufffd4\ufffd\ufffd\n\u000be\ufffdJộ\u0003\ufffdc\ufffd\ufffd\ufffd\ufffd-'\u000c\ufffdT\ufffd_\ufffdS\ufffd\ufffd\ufffd\ufffdS9}ji\ufffdY\ufffd\u001e\ufffd7\n\ufffd\ufffdxz۩\ufffd\ufffd\ufffd\ufffdLv.#\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdOg`\"A\ufffd\ufffdX\ufffd\ufffd\ufffd\ufffd;\ufffd\ufffd\ufffd\ufffd\ufffdmio\ufffd\ufffda\ufffd\ufffd\ufffd0\ufffdپ\ufffd\ufffdM\ufffd`0\u0006i\ufffd\u0015\ufffd\ufffd\ufffd\u0017\ufffd\ufffd\ufffd*i\ufffd\ufffdK\ufffdk%\ufffd\ufffdƥ]\ufffd\ufffd\ufffd\ufffd\ufffd34\ufffd!\ufffd\ufffd\ufffd!QF#\u001f\ufffd\ufffd'oKf\ufffd%(\ufffdn_\ufffd\ufffd\ufffd\ufffd\u0000\ufffdt\ufffd\"M\ufffd\ufffd\ufffdc\n\ufffd\ufffd\ufffd\ufffd\ufffdީ\ufffd˷\ufffd|[\ufffd\ufffdd\u001a\ufffd\u0014\ufffd\u0019\ufffdQ\ufffd\ufffdY\u0008\ufffd\ufffd/\ufffd썵\u00070ա\u00020^ѭ\ufffd]e=T]\ufffd\ufffd\ufffd!\ufffd\ufffd\ufffd\u000e\ufffdZڋz\ufffd@\ufffdI\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdK\ufffd!\\ \ufffdKG\u0001\ufffdu\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u001c_\ufffd\ufffd\ufffdZ\ufffd\ufffdΩ-H\ufffd\ufffd\ufffd\u0003ʒw\ufffd\ufffdΎ퉒!j\ufffd\ufffd\ufffd\u0011\ufffdMx\ufffd^\ufffd\ufffd\u003cw\ufffd^\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdշ\u0008[\u000ff[Æ\ufffd\ufffd\ufffd\ufffd\ufffdM\ufffd\ufffd\u0001w\ufffdRLF2*\u0026\ufffdaJ\ufffd\ufffdZ\ufffd\u001f\u000blT\ufffd(\u0000\u0000","raw":"HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 2128\r\nContent-Encoding: gzip\r\nContent-Type: text/html; charset=utf-8\r\nDate: Thu, 07 Sep 2023 15:34:47 GMT\r\nSet-Cookie: AWSALB=EeUP0iKoblECp3EJrR7G3VkVXCB824uzYMUKxXSQm3dzHWM8O+zBi9JJ1ycx4rw89sTDpO5ron/v2igr7ki2rVUBc9cHkO/BE+HpmdhpXzFd01fKZVWnAa9/Muom; Expires=Thu, 14 Sep 2023 15:34:47 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=EeUP0iKoblECp3EJrR7G3VkVXCB824uzYMUKxXSQm3dzHWM8O+zBi9JJ1ycx4rw89sTDpO5ron/v2igr7ki2rVUBc9cHkO/BE+HpmdhpXzFd01fKZVWnAa9/Muom; Expires=Thu, 14 Sep 2023 15:34:47 GMT; Path=/; SameSite=None; Secure\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n"}} +{"timestamp":"2023-09-07T21:04:52+05:30","url":"https://ginandjuice.shop/catalog","request":{"header":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-US,en;q=0.9,hi;q=0.8","Connection":"close","Cookie":"TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=UeLc4S4hyi19aLba4fvsmba8ZhjwmwH2; AWSALB=EeUP0iKoblECp3EJrR7G3VkVXCB824uzYMUKxXSQm3dzHWM8O+zBi9JJ1ycx4rw89sTDpO5ron/v2igr7ki2rVUBc9cHkO/BE+HpmdhpXzFd01fKZVWnAa9/Muom; AWSALBCORS=EeUP0iKoblECp3EJrR7G3VkVXCB824uzYMUKxXSQm3dzHWM8O+zBi9JJ1ycx4rw89sTDpO5ron/v2igr7ki2rVUBc9cHkO/BE+HpmdhpXzFd01fKZVWnAa9/Muom","Referer":"https://ginandjuice.shop/","Sec-Ch-Ua":"\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"macOS\"","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"same-origin","Sec-Fetch-User":"?1","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36","host":"ginandjuice.shop","method":"GET","path":"/catalog","scheme":"https"},"raw":"GET /catalog HTTP/1.1\r\nHost: ginandjuice.shop\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hi;q=0.8\r\nConnection: close\r\nCookie: TrackingId=eyJ0eXBlIjoiY2xhc3MiLCJ2YWx1ZSI6InkyRTQ5UzdBNFdiWUVDZEYifQ==; session=UeLc4S4hyi19aLba4fvsmba8ZhjwmwH2; AWSALB=EeUP0iKoblECp3EJrR7G3VkVXCB824uzYMUKxXSQm3dzHWM8O+zBi9JJ1ycx4rw89sTDpO5ron/v2igr7ki2rVUBc9cHkO/BE+HpmdhpXzFd01fKZVWnAa9/Muom; AWSALBCORS=EeUP0iKoblECp3EJrR7G3VkVXCB824uzYMUKxXSQm3dzHWM8O+zBi9JJ1ycx4rw89sTDpO5ron/v2igr7ki2rVUBc9cHkO/BE+HpmdhpXzFd01fKZVWnAa9/Muom\r\nReferer: https://ginandjuice.shop/\r\nSec-Ch-Ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"\r\nSec-Ch-Ua-Mobile: ?0\r\nSec-Ch-Ua-Platform: \"macOS\"\r\nSec-Fetch-Dest: document\r\nSec-Fetch-Mode: navigate\r\nSec-Fetch-Site: same-origin\r\nSec-Fetch-User: ?1\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36\r\n\r\n"},"response":{"header":{"Content-Encoding":"gzip","Content-Length":"2876","Content-Type":"text/html; charset=utf-8","Date":"Thu, 07 Sep 2023 15:34:52 GMT","Set-Cookie":"AWSALB=AymL6NoUb7IQ+yLeqn8Nx2AnsAEQE7J/dECy9Mv2IjZxSjlYL3ClOPOJQywrvrB2uDIqCLT8bh+PpOR56TWHYLLnsfn2bXbh+12VUozjFNV+c3PYsBusyKnEB1Ut; Expires=Thu, 14 Sep 2023 15:34:52 GMT; Path=/, AWSALBCORS=AymL6NoUb7IQ+yLeqn8Nx2AnsAEQE7J/dECy9Mv2IjZxSjlYL3ClOPOJQywrvrB2uDIqCLT8bh+PpOR56TWHYLLnsfn2bXbh+12VUozjFNV+c3PYsBusyKnEB1Ut; Expires=Thu, 14 Sep 2023 15:34:52 GMT; Path=/; SameSite=None; Secure","X-Backend":"ecfca00b-5a9e-4a89-91bd-de41ecbc4611","X-Frame-Options":"SAMEORIGIN"},"body":"\u001f\ufffd\u0008\u0000\u0000\u0000\u0000\u0000\u0000\ufffd\ufffd\\}r\ufffd6\u0016\ufffd\ufffd\ufffd@\ufffd\ufffd\ufffd=#Q\ufffd\ufffd\ufffdĒ:\ufffd\ufffd\ufffdi\ufffd8\ufffd\ufffd\ufffdvwv\u003c\u0010\tI\ufffdA\ufffd\u0005@)\ufffdLf\ufffd\u001a{\ufffd\ufffd\ufffd\u001eeO\ufffd\u000f\u0000EQ\u0012)\ufffd.;^\ufffd֓\ufffdE\u0000\ufffd\ufffd\ufffd\ufffd\ufffd\u0017(\ufffd\ufffdg\ufffdn.\ufffd\ufffd\ufffd\ufffd\u0012\ufffdU\ufffd\ufffd_t\ufffd/\u0004?\ufffd1\ufffd\ufffd\ufffdh\u001e\u0019\r\ufffd\ufffdX\ufffda\ufffd)\ufffd\ufffd\ufffd\ufffd\ufffdl2\u003c\ufffdÈhzR6\ufffd\ufffd\ufffd\ufffd\ufffdצ\ufffd\ufffd\u0006$\u0008\ufffdI5cD\ufffd\tQۘi\u0016\ufffdP^z\u003c\u0008\u00084\ufffd\n\u001e\ufffd J\ufffd\ufffdA@\u0014F!\u000eHϙP2\ufffd\ufffdP\u000e\ufffdx\ufffdH\ufffdzΔ\ufffdj\ufffd\ufffdɄz\ufffda\u001e\ufffd(\ufffdD4`\ufffd0\u0003#\ufffd\ufffd;\u0019n\ufffd\u00134RH\n\ufffd\ufffdd\u0004\ufffd \ufffd\u0001{\ufffd\u0005N\ufffd\ufffd(\u0000\ufffd\ufffd\u0007\ufffd\ufffd\ufffdMKQ\ufffdE\ufffd\ufffd\ufffd\u0003بY\u0004+S\ufffd\ufffdj~\ufffd\u0013l[\ufffd\u003c\ufffd8\u001c\ufffd\u000c\ufffd\ufffdV\ufffd\ufffdqR\ufffdUQ\ufffdH\ufffd\ufffd\ufffd~\ufffd)\ufffd\u001a芆\ufffd9\u000e\ufffd3\ufffdM\u000c:B\ufffdc\u001eu\ufffdv\ufffd\ufffd\ufffd\ufffd„\ufffd\u0003\ufffd\ufffdP8j\ufffd(\ufffd0\ufffd\ufffd\u0004Q\ufffd\ufffdd-'\ufffdT\ufffd\u001c\ufffd)\ufffdC\ufffd1,eϱ\ufffd\ufffd\ufffd\ufffd\ufffd\u0010zV\u0008\u000cQ\ufffdަޏ\ufffdD\ufffd\u000f#\ufffd0: \u0002+\ufffdfh\u0012\ufffd\u0010\u003e\u0003\ufffdhJ\u0006\u0008dd\ufffd\ufffdfV\ufffd\ufffd\ufffdB\ufffd\ufffd!\u0017H\u0011\ufffdh8҃\ufffd\ufffd\u0003\u0019\ufffd\ufffd\t)\ufffdjf\ufffd\ufffdR\ufffdM\ufffd\ufffd\u0002te\ufffdӥh\u000bv\u0010\u000f\u0003\u000e\ufffd\u0005\ufffd\u0012\ufffd\ufffd.\ufffd\ufffdԽ\ufffd\ufffd\ufffdB=\ufffdJ\ufffd\ufffdd\ufffd\ufffd\ufffd\ufffd\u001b@w\ufffd\ufffdc{\ufffd\ufffd\ufffd\u001af\ufffd\u000f\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd!\ufffd\ufffdN\ufffd\ufffd9\ufffdε\ufffd\ufffdY~\u003c\ufffdG\ufffd'\ufffd\ufffd!\ufffd\ufffd`\ufffdv\ufffd\ufffdkkpuļ\ufffd5\ufffd\ufffd\u0018\ufffdW\ufffdy\ufffd^\ufffd\ufffd\ufffd\ufffd\ufffd\u0008\ufffd2\u0003\u000eD\ufffd\u000c\ufffd\ufffd\u0019Zkk\ufffd\ufffdur\ufffd\u000e\ufffd\ufffd\ufffdX\ufffd\u0019\ufffd6Jp@\ufffd#5\u0026\u0006\ufffdg`֠\ufffd\u001csi\ufffd\ufffd\u000b\u000c\ufffdV\ufffd\ufffd\u0016k\ufffdl\ufffd\ufffdD\ufffd\u001c\ufffdڼ\ufffdP\u001dV0\ufffda\ufffdv\ufffd\ufffd6\ufffd՚9lr\ufffdI\u0013\ufffd\u001a\ufffd\ufffd8\ufffd*\ufffd\u0017\ufffdm₎\u0005+㰍-\ufffd\u0019\ufffd\u0010\u0017L\ufffd\ufffd\ufffd\ufffdܴa,\u001d\u0019\ufffdh$\u000e\ufffd\ufffd]\ufffdH\ufffd\u0000\ufffd\ufffd}\ufffdq\ufffd5\ufffd\ufffdn\u001fd\u0006\ufffd\ufffdԃX)\ufffdH[\ufffd\ufffd\ufffd\ufffdX\ufffd:M0Q\u000c\ns\ufffd\u0018V\ufffd\ufffd%\ufffd\ufffd2\u0012\u003cZ\ufffdT\ufffd\ufffd\u0011\ufffd\u001c\ufffd\ufffd\ufffd\u0012\u000b\u000fx\ufffd\ufffd\ufffd\ufffdv(\ufffdŬ\"\ufffd\ufffd͘\ufffdj[\u0002g\ufffd\ufffd\ufffd\u001b[\u0013\ufffd\u003c\u001e\ufffd\ufffdA\ufffd\ufffdV\ufffdY#i\ufffdT)'\ufffd~\u0017\u0002ڼs\ufffdZ\ufffd\ufffdG\ufffd\u0017\ufffd\ufffdǻ0\ufffd\u000fK\u003c\ufffd\ufffdXg\\ͧ\ufffdz\u0013\ufffd\ufffd\u001b\ufffd\ufffde\ufffd/\ufffd\u001c\ufffd\u000b\ufffd\ufffd#D\ufffd\ufffd\u0012\u001b\ufffd\ufffd\ufffd\ufffd\ufffd\u0015\ufffd\ufffd\u0019J\u003e\ufffdF\ufffdo\ufffd\ufffd\ufffd\ufffd\u001ea\ufffd\u001e\u0016+֚\u0004\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdS`+I\ufffd%\ufffdW\ufffd\u001a\ufffd]p\ufffdݖ^r\ufffd\ufffd\u001b\ufffd\ufffdF\ufffd\ufffdlx\ufffdU\ufffd\ufffdH\ufffd\ufffdv5\ufffdb\n\u001f@\ufffd_\u001b\ufffd\ufffd\ufffd\ufffd\u0004X\ufffd\ufffds\ufffds\n\ufffd\ufffdQ\u00265CŢ\ufffd%d\ufffd\u0013\ufffdR\u0013\u0007\ufffd\ufffd\ufffd\u000c\ufffdW\u000e/ҽ\ufffd\ufffd\ufffd:\ufffd\u0011\u0001\ufffd\u0005q\ufffdkcm\ufffdu\ufffd\ufffd0)\ufffd\ufffd0\\Dش\ufffdEl[\ufffd\u0004\u000c\u000f\u0005e\ufffd\ufffd\u0002%\ufffd\ufffd\u001bo\ufffd\u0006\ufffd\ufffd\u0000\ufffdZ\ufffdd2=\ufffd\u001dޘ\ufffd\ufffd\ufffd\ufffd\ufffd[\ufffd\ufffda\u0014';\u0026\ufffda\ufffd{\u00123\ufffd9\u0016\u000e\ufffd\u0018\ufffdȘ3_\ufffd෦\u001d*\u0019\ufffd\u0008'\ufffdB\ufffd\ufffd\ufffd\ufffd\u0008\ufffd$\ufffd\ufffd\ufffd֪\ufffdg\ufffd!\ufffd\u0026\\A\ufffd\u001e\ufffd\ufffdζ\u0012\ufffd܋\ufffd\ufffdpD\ufffd%#\ufffd\ufffd\ufffd썿WK\ufffdS\ufffdw'\ufffd\ufffd\u0004\u0018.\ufffdof\ufffd\ufffd\u001d\ufffd\u001d\ufffdT^F\ufffd2\u001e\u0004T%\ufffdَ\ufffd\ufffd\\\ufffd\ufffd\u003cn\ufffd4\ufffdg\ufffdu\ufffdV\ufffdi\ufffd\ufffdp4\ufffdv׼p\ufffd\ufffd-\ufffde7\ufffd\ufffd\ufffd\ufffdkS\ufffdЅ\ufffd\u003ea\ufffd\u001f\ufffd\u0010\ufffd\ufffdm\ufffd\ufffd}\ufffdmڮb\ufffdͻpK\u0008\ufffdI\ufffd\ufffd\ufffd\t\ufffd\ufffd\ufffd\ufffd)\ufffd@nE\u0012\ufffd\ufffd2\u0003\ufffd\ufffdɈ\u000bJ$\ufffd\ufffd\ufffd|Ř\ufffdra\ufffdu\ufffd+\u000f\ufffd\ufffd4\u00032\u001d_\u0026d\ufffd^\ufffdߌ\ufffd\u0001\u0004G\ufffd\ufffd)$\ufffd\u000e\ufffd;\ufffd\ufffd\ufffd\ufffd\u000f\ufffd=u犆\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd7\u0005\ufffd\ufffd\ufffd\ufffdsv\ufffd\ufffd\n\ufffd\"\u0019\u0003\ufffd\u000ecƲC\u0004\ufffd\ufffd\ufffd^\ufffd|\ufffd\ufffd\ufffd\u0007h\ufffd+t\ufffd\u0015\ufffd\ufffd\ufffd\ufffd\ufffd}\ufffd\ufffd\ufffd\ufffdm2;\ufffd\ufffd\ufffd\u001e^O\ufffd\ufffdG\ufffd\u003ez\ufffdl\ufffd\ufffd\ufffd4\u0011\u0000\ufffdz\ufffdt\u0008z\ufffdh38\ufffdNqM\ufffd\ufffd\ufffdf\ufffd\u0001\ufffd\\\u0010P#\ufffd\ufffd@y\ufffd\rp\ufffd7o\ufffd\ufffd\u00115SJ\ufffdβ\ufffd3\ufffd\\\ufffdv\ufffd\ufffd\ufffd\ufffd\u0011:\ufffdgG\ufffd\ufffdF\ufffd\ufffdjW\u0015\ufffd\ufffd\ufffd9\u001a\ufffd\ufffd,\ufffds\ufffd\ufffd\ufffddtS\ufffdn֘\ufffdM\ufffdj\ufffd\ufffd\ufffd\ufffdL\ufffd\u0013D\ufffd\"\ufffd;\ufffd^\r\ufffd\ufffd\ufffd\u0013\ufffd'\ufffd\ufffd\ufffd\ufffd\u0017\ufffd\ufffd\ufffd\ufffd\u003e׍\u001a\u0012\ufffd\u003e/Cj'\ufffd\ufffd\u0007\ufffd\ufffd\ufffd %\u0002GmP\ufffd%@\u0012\u0016ڲ\\AB\u0008\ufffd{)\ufffdf\ufffd9\u0016\ufffd\rO\ufffd\ufffd\u0018\ufffd0R5\u0014edc\u003c«5]\ufffd\ufffd\ufffdL~\ufffd\ufffd{\ufffdm\ufffd\ufffd\u0006\ufffd\ufffd%\ufffd\ufffd\ufffd\ufffd\ufffd]œ\ufffd\ncٌ@\u003c\ufffdB\ufffd\ufffd\u0011\ufffdj\ufffd\ufffd(\u001cm\ufffdc\ufffd鿛ӡKK\ufffd.\ufffdw\u000f\ufffde\ufffd\ufffd;\ufffde\\\ufffdG\u0005֯\ufffd:e\ufffdȾk\ufffd\ufffd\ufffd/\ufffd?v\u000eܣ\ufffd\ufffd\ufffd@\ufffd\ufffd\ufffd~\ufffd\ufffd#%S\ufffd\u0013\ufffd\n\ufffd\ufffd\ufffdƚ\ufffd\u000c\ufffd튑l\ufffdJBwa\ufffd(\ufffdY绛i\ufffd\ufffd\u001d=\u0016\ufffd\ufffdC\ufffd\ufffd\ufffdn\u0000ש\u001a\ufffd\ufffd\ufffd\ufffd\ufffd\u00161\ufffd\\7\u0013\"\u0018\ufffd\ufffd'\ufffd\ufffd\u0017m\ufffd\ufffd\ufffdn\ufffduX1\\\ufffdL\u001c\ufffd\ufffd_\ufffd\\~T\u0002\n\u0012.\ufffdֻZ\ufffd\ufffd\ufffdx\ufffdQ\ufffdp\ufffd\ufffdu.\ufffd\u0003\u001a\ufffd\ufffd\u001f\ufffdL\ufffd!\ufffd\ufffdj \ufffd8Z\ufffd؜\ufffd\ufffd\ufffd\u000b\ufffd\ufffd\ufffd\u001b\ufffd\u001dW\ufffd\ufffd\u0000+o|\ufffd\ufffdtNJBx\ufffd\t\ufffd\u0026\ufffd4\ufffd\u001d\ufffd\u001f\u0014#'\u0015cW*\ufffd\ufffd)햁\ufffd\u0004ڻ\ufffd\u0001\ufffdۊ\ufffd\ufffd\ufffdR\ufffd\ufffd-\ufffd\ufffd#1\ufffd\ufffd\ufffd\ufffd\ufffd\u0001)\ufffd\ufffd\ufffdq\ufffd\ufffd\u0019:\u0007\ufffd\ufffd?mQr춏v\u0003\ufffd\u0017\u0015#V6D\ufffdx\ufffd\"\ufffdݎ\ufffd=\ufffd\ufffdHi?\u0016\u003e\ufffd\ufffd\ufffdڻ\u0001_\ufffd\ufffdb\ufffdJ)\r\ufffd\ufffde\ufffd\ufffd\ufffd9\ufffdi\u0000\u0011\ufffd[\ufffd\ufffd\ufffd\ufffdN\ufffdܓ\ufffd\u001d\ufffd\ufffd\ufffd7'\ufffd\ufffd\ufffd]\ufffdR\ufffd\ufffdJp\ufffd\ufffd\ufffd\ufffdx\ufffdI}\ufffd\ufffd\ufffd\u001e\ufffd\ufffd\u0008pU\ufffd(y\ufffd\ufffd\ufffd\ufffd\"\u0016\ufffdN\ufffdՄ\ufffdG\ufffd\u0004\ufffd\u0007\ufffd\ufffd\ufffd\ufffd#\ufffd\ufffd_\ufffd\u001c\ufffd\ufffd\ufffdF\ufffdpd\u0003\ufffdk\u001a\u0012YIt|\ufffd\ufffd\u001d\ufffdLJ;\ufffdX\ufffdoI\ufffd\ufffdR\ufffd\ufffdIE\u0019+\t޷\ufffd\u0004\ufffdj\ufffd\ufffd@\ufffd\ufffdgP\ufffd\ufffd\ufffd\ufffdс{\ufffd#{\ufffdVկN\ufffd\ufffd#\u001abv\ufffd\ufffdٝ\ufffd\ufffd\ufffd\ufffd^h\ufffd\ufffd+1C\ufffd\ufffd_\ufffd߷\ufffd\ufffd\ufffd2PV\ufffd.%\ufffd\ufffd\ufffd\ufffdg\ufffdU\ufffd\ufffdw\u000c\u0001\ufffd\u001a\ufffd'w\ufffd\ufffd\ufffd\u000e\ufffd`\ufffd/R\u0018\ufffdꎆw\ufffdnLfD\ufffd\ufffd\ufffd\u0001\u0015\ufffd$\ufffd\ufffdך\ufffd\ufffd!ܥ(Z\ufffd˔R\ufffd\n\ufffd1E\u000b\ufffd\u0013P\ufffd\ufffd\ufffdpE\ufffd\n\ufffd\ufffd\ufffdO\ufffd\ufffd;\u003euO\ufffd\u0018\ufffd\r_\ufffd\ufffd;\ufffdg\ufffd3'䆜\ufffd3\tS\ufffd\ufffd(\ufffd8\ufffdʷ\ufffd\ufffd\ufffd\ufffd8\ufffd:gF\ufffd\ufffd \ufffd\ufffds\ufffd\ufffd4\ufffd\ufffd\u001f\ufffd\ufffdl\ufffd\ufffd\ufffdK\u0026\ufffd燝\ufffd\ufffd\ufffd\u0002\t\ufffd\ufffdd\u003c\ufffd\u0007\u0016\u0006\u0004\ufffd|\n\ufffd\ufffd\ufffd\ufffd\"\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdS.\ufffd?\ufffd\u003eA\ufffd\u000cb\u001cR5C|\ufffdF:f\ufffd\u003e\ufffd`\ufffd\ufffd\u001a\ufffd\u0010\ufffdǓus;\ufffd|\ufffdX,\ufffd\u0004f\"S\ufffd\ufffdeİ\ufffdza\ufffd\ufffdqУ/d\u0011,\t\u003cy\ufffd1\u003c\ufffdœ*\ufffdg\ufffd9\ufffd\ufffd\ufffdk\u00172\ufffdտ\ufffd'U[\ufffdɔ.\ufffd-\ufffd\ufffd\u001fd-\ufffd\"S\ufffd\ufffd\ufffdy\ufffd9\ufffd\ufffdP\ufffd\u0008\u001c䃿6\ufffd\ufffd\ufffdN\u0004q3iZ=n\ufffd\ufffd|k|0\ufffd\r\u0005\ufffd%\ufffd\u0002\ufffd\ufffd\ufffd\ufffd\u0018\t\ufffd\ufffd\ufffd\u001c%\ufffd\u001f\ufffd\ufffd\u001c^\ufffd\u0026\ufffd\ufffd\ufffd\ufffdm\u0007\ufffds\ufffd;c\ufffd\ufffd$\ufffd\u001fV\ufffd\ufffd\u0018:\ufffd\u001c\u0003\ufffd9\ufffdG\ufffdO\ufffd㛟n\ufffd\ufffd\ufffd{\ufffd\ufffdtz|=\ufffd.\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdk\ufffdӟ\ufffd͗\u001c\ufffd[9z\ufffd9\u0005ؿ\ufffdk\ufffd\ufffdٿB\ufffdH\ufffdҺ6\ufffdy\u003c\ufffdxذ-ۄ\\:\u0001k\ufffd\ufffd\ufffd{\ufffd\n=H\ufffd\ufffd1_'\u000f=F\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd~eD\ufffd\u0003o\u000cվ\ufffd\ufffdm\ufffd\ufffd \u0026\r\ufffd\ufffd\ufffd\ufffd\ufffdz\ufffd\ufffd\ufffd\u000f\ufffd\u0004~:D:\u0012\ufffd\ufffd\u0018b{\ufffd)\ufffdR\ufffdG\ufffd\ufffdG)r+B'\ufffd\u0026\ufffd\ufffd\ufffdt\r\ufffd\ufffd\ufffd[\ufffd@\u000cR\u0010+\ufffd\ufffdߞ\ufffd\ufffd\ufffd\u0026\u0008\ufffdY'lS\u0010R\ufffd\ufffdk\u001d\u0018h\ufffd\ufffd?\u0002\ufffd%\ufffdd\ufffd\u000e\ufffdR\ufffd7 \ufffd\ufffd;+:\ufffdt\ufffd\ufffd\ufffd6\u0005\ufffd\ufffd\ufffd\u001e04\ufffd\u0015El\ufffd\ufffd\ufffd\ufffdh\ufffd\ufffd\u0017\ufffd\ufffdIU}\u001f3҅\ufffd\u0005\u0016\n\r\u0008D3b\ufffd\ufffdn3#\ufffd\ufffd\u0017u7\ufffd\ufffd\u0007\u0008\ufffd\ufffds\ufffd\ufffdQ\ufffdD╬\u0007\ufffd\ufffd^q\ufffd\ufffd\u0004\ufffd\ufffd\ufffd\u001e\ufffd\ufffd\ufffd\ufffd\u0001\ufffd\ufffd\\)\ufffdk\u0006\ufffd\ufffd\u000b\ufffd\u000f\ufffd\u001d\ufffd\u000e8\ufffdZ\u000e\ufffdZ\ufffdn\ufffd\ufffd=\ufffdb@\ufffdj\ufffd\ufffd\u0016\ufffd\u0026(\ufffdsr\u000b\u0012X\ufffd\ufffd\u0001e\ufffd\ufffd\ufffd\ufffdզ*#C\u0026\ufffd\u0007\ufffd^o\ufffd{\ufffdMy\ufffd\ufffd*\ufffd^\ufffd\ufffdo\u0015\ufffd\ufffd\u003e\ufffd\ufffdN\ufffd^a\ufffd\ufffd\ufffd\ufffdcAuj\ufffd\ufffd\u0014\ufffd\ufffdo\u0013\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd?\ufffd\u0001\ufffd\ufffd\ufffd\u0019\ufffd\ufffd\u0002\ufffdYQ\ufffd\ufffdA\u0000\u0000","raw":"HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 2876\r\nContent-Encoding: gzip\r\nContent-Type: text/html; charset=utf-8\r\nDate: Thu, 07 Sep 2023 15:34:52 GMT\r\nSet-Cookie: AWSALB=AymL6NoUb7IQ+yLeqn8Nx2AnsAEQE7J/dECy9Mv2IjZxSjlYL3ClOPOJQywrvrB2uDIqCLT8bh+PpOR56TWHYLLnsfn2bXbh+12VUozjFNV+c3PYsBusyKnEB1Ut; Expires=Thu, 14 Sep 2023 15:34:52 GMT; Path=/\r\nSet-Cookie: AWSALBCORS=AymL6NoUb7IQ+yLeqn8Nx2AnsAEQE7J/dECy9Mv2IjZxSjlYL3ClOPOJQywrvrB2uDIqCLT8bh+PpOR56TWHYLLnsfn2bXbh+12VUozjFNV+c3PYsBusyKnEB1Ut; Expires=Thu, 14 Sep 2023 15:34:52 GMT; Path=/; SameSite=None; Secure\r\nX-Backend: ecfca00b-5a9e-4a89-91bd-de41ecbc4611\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n"}} diff --git a/pkg/input/formats/testdata/ginandjuice.proxify.yaml b/pkg/input/formats/testdata/ginandjuice.proxify.yaml new file mode 100644 index 00000000..4ebbb308 --- /dev/null +++ b/pkg/input/formats/testdata/ginandjuice.proxify.yaml @@ -0,0 +1,269 @@ +timestamp: 2024-02-20T19:24:13+05:30 +url: https://ginandjuice.shop/blog/post?postId=3&source=proxify +request: + header: + Accept-Encoding: gzip + Connection: close + User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 + host: ginandjuice.shop + method: GET + path: /blog/post + scheme: https + raw: |+ + GET /blog/post?postId=3&source=proxify HTTP/1.1 + Host: ginandjuice.shop + Accept-Encoding: gzip + Connection: close + User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 + +response: + header: + Content-Encoding: gzip + Content-Type: text/html; charset=utf-8 + Date: Tue, 20 Feb 2024 13:54:13 GMT + Set-Cookie: AWSALB=9l3X2bRs5JVQp5cO8o7xLckrdo3FZIQzbS0ga0n4ctfDApb/nn0K6AiSLLAMbG7CCDHqKOj9kQdyj6T2RzBDULszJ+2Oy8KcyuOKhFsCGVTVabnJTkVwhyXLciFt; Expires=Tue, 27 Feb 2024 13:54:13 GMT; Path=/, AWSALBCORS=9l3X2bRs5JVQp5cO8o7xLckrdo3FZIQzbS0ga0n4ctfDApb/nn0K6AiSLLAMbG7CCDHqKOj9kQdyj6T2RzBDULszJ+2Oy8KcyuOKhFsCGVTVabnJTkVwhyXLciFt; Expires=Tue, 27 Feb 2024 13:54:13 GMT; Path=/; SameSite=None; Secure, session=ymwLa8dKmWjLl43BBpQnrp5LkFZraYkp; Secure; HttpOnly; SameSite=None + X-Backend: ea6c1d8c-a8e5-4bef-b8db-879bbb13cf62 + X-Frame-Options: SAMEORIGIN + raw: |+ + HTTP/1.1 200 OK + Connection: close + Content-Encoding: gzip + Content-Type: text/html; charset=utf-8 + Date: Tue, 20 Feb 2024 13:54:13 GMT + Set-Cookie: AWSALB=9l3X2bRs5JVQp5cO8o7xLckrdo3FZIQzbS0ga0n4ctfDApb/nn0K6AiSLLAMbG7CCDHqKOj9kQdyj6T2RzBDULszJ+2Oy8KcyuOKhFsCGVTVabnJTkVwhyXLciFt; Expires=Tue, 27 Feb 2024 13:54:13 GMT; Path=/ + Set-Cookie: AWSALBCORS=9l3X2bRs5JVQp5cO8o7xLckrdo3FZIQzbS0ga0n4ctfDApb/nn0K6AiSLLAMbG7CCDHqKOj9kQdyj6T2RzBDULszJ+2Oy8KcyuOKhFsCGVTVabnJTkVwhyXLciFt; Expires=Tue, 27 Feb 2024 13:54:13 GMT; Path=/; SameSite=None; Secure + Set-Cookie: session=ymwLa8dKmWjLl43BBpQnrp5LkFZraYkp; Secure; HttpOnly; SameSite=None + X-Backend: ea6c1d8c-a8e5-4bef-b8db-879bbb13cf62 + X-Frame-Options: SAMEORIGIN + +--- +timestamp: 2024-02-20T19:24:13+05:32 +url: https://ginandjuice.shop/users/3 +request: + header: + Accept-Encoding: gzip + Authorization: Bearer 3x4mpl3t0k3n + Connection: close + User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 + host: ginandjuice.shop + method: POST + path: /catalog/product + scheme: https + raw: |+ + POST /catalog/product?productId=3 HTTP/1.1 + Host: ginandjuice.shop + Authorization: Bearer 3x4mpl3t0k3n + Accept-Encoding: gzip + Connection: close + User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 + +response: + header: + Content-Encoding: gzip + Content-Type: text/html; charset=utf-8 + Date: Tue, 20 Feb 2024 13:54:13 GMT + Set-Cookie: AWSALB=Rzm8ZSSL/yQq1mG1SdGmWAahqexWvOcjIhlNKm0wBnk4jvY3Hdy3mRc+NKoMRNJ2RW+FNbtRk7DX+itnfzjMvKNpwtLWWAafxeKybc+v351g0MsLycRNQF5fG78y; Expires=Tue, 27 Feb 2024 13:54:13 GMT; Path=/, AWSALBCORS=Rzm8ZSSL/yQq1mG1SdGmWAahqexWvOcjIhlNKm0wBnk4jvY3Hdy3mRc+NKoMRNJ2RW+FNbtRk7DX+itnfzjMvKNpwtLWWAafxeKybc+v351g0MsLycRNQF5fG78y; Expires=Tue, 27 Feb 2024 13:54:13 GMT; Path=/; SameSite=None; Secure, session=fFcCUjmQguQy820Y8xrnRypp3KBWSPk6; Secure; HttpOnly; SameSite=None + X-Backend: 2235790d-f089-4324-8ac0-f64cc96f2460 + X-Frame-Options: SAMEORIGIN + body: | + + + + + + + + + + Fruit Overlays - Product - Gin & Juice Shop + + +
+
+

+ This is a deliberately vulnerable web application designed for testing web vulnerability scanners. + Put your scanner to the test! +

+
+
+
+ + + +
+ +
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+

Fruit Overlays

+ + + + $92.79 + + + + + +

When it comes to hospitality presentation is key, and nothing looks better than a well-dressed drink. We know gin fans like plenty of fruit in their glasses, some a bit more than they really need, but hey ho, each to their own. But what about fruit not inside your glass, but classily arranged over your glass? In comes Fruitus overlayus, the best way to jazz up any party. The possible colour combinations are endless, just picture that! All you need is a nice selection of small fruits, or maybe even use our Fruit Curliwurlier to add a dash of even more drama, and we will do the rest. This one is a real winner at our Christmas and New year’s outings, give it a go and turn some heads.

+

CONTENTS: 12 cocktail sticks.

+

HOW TO USE: Let your creative juices flow (Pun intended), and spend some time working on your colour coordination, try not to think too much about it, just do it! Pick up one of the Fruitus overlayus sticks and carefully slide the fruit along until there is a small space on either end of the stick. Balance the stick across the rim of the glass. Ta-Da! Your first fruit overlay. Keep going until you have as many overlays as you need. You can always purchase more at any time with a discount on bulk buys.

+
+ +
+ + + +
+ + + +
+ +
+ + + + +
+
+ + View cart + +
+
+
+
+ +
+ + + + raw: |+ + HTTP/1.1 200 OK + Connection: close + Content-Encoding: gzip + Content-Type: text/html; charset=utf-8 + Date: Tue, 20 Feb 2024 13:54:13 GMT + Set-Cookie: AWSALB=Rzm8ZSSL/yQq1mG1SdGmWAahqexWvOcjIhlNKm0wBnk4jvY3Hdy3mRc+NKoMRNJ2RW+FNbtRk7DX+itnfzjMvKNpwtLWWAafxeKybc+v351g0MsLycRNQF5fG78y; Expires=Tue, 27 Feb 2024 13:54:13 GMT; Path=/ + Set-Cookie: AWSALBCORS=Rzm8ZSSL/yQq1mG1SdGmWAahqexWvOcjIhlNKm0wBnk4jvY3Hdy3mRc+NKoMRNJ2RW+FNbtRk7DX+itnfzjMvKNpwtLWWAafxeKybc+v351g0MsLycRNQF5fG78y; Expires=Tue, 27 Feb 2024 13:54:13 GMT; Path=/; SameSite=None; Secure + Set-Cookie: session=fFcCUjmQguQy820Y8xrnRypp3KBWSPk6; Secure; HttpOnly; SameSite=None + X-Backend: 2235790d-f089-4324-8ac0-f64cc96f2460 + X-Frame-Options: SAMEORIGIN \ No newline at end of file diff --git a/pkg/input/formats/testdata/openapi.yaml b/pkg/input/formats/testdata/openapi.yaml new file mode 100644 index 00000000..93a37df7 --- /dev/null +++ b/pkg/input/formats/testdata/openapi.yaml @@ -0,0 +1,582 @@ +openapi: 3.1.0 +info: + title: VAmPI + description: OpenAPI v3 specs for VAmPI + version: '0.1' +servers: + - url: http://hackthebox:5000 +components: {} +paths: + /createdb: + get: + tags: + - db-init + summary: Creates and populates the database with dummy data + description: Creates and populates the database with dummy data + operationId: api_views.main.populate_db + responses: + '200': + description: Creates and populates the database with dummy data + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: 'Database populated.' + /: + get: + tags: + - home + summary: VAmPI home + description: >- + VAmPI is a vulnerable on purpose API. It was created in order to + evaluate the efficiency of third party tools in identifying + vulnerabilities in APIs but it can also be used in learning/teaching + purposes. + operationId: api_views.main.basic + responses: + '200': + description: Home - Help + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: 'VAmPI the Vulnerable API' + help: + type: string + example: 'VAmPI is a vulnerable on purpose API. It was created in order to evaluate the efficiency of third party tools in identifying vulnerabilities in APIs but it can also be used in learning/teaching purposes.' + vulnerable: + type: number + example: 1 + /users/v1: + get: + tags: + - users + summary: Retrieves all users + description: Displays all users with basic information + operationId: api_views.users.get_all_users + responses: + '200': + description: See basic info about all users + content: + application/json: + schema: + type: array + items: + type: object + properties: + email: + type: string + example: 'mail1@mail.com' + username: + type: string + example: 'name1' + /users/v1/_debug: + get: + tags: + - users + summary: Retrieves all details for all users + description: Displays all details for all users + operationId: api_views.users.debug + responses: + '200': + description: See all details of the users + content: + application/json: + schema: + type: array + items: + type: object + properties: + admin: + type: boolean + example: false + email: + type: string + example: 'mail1@mail.com' + password: + type: string + example: 'pass1' + username: + type: string + example: 'name1' + /users/v1/register: + post: + tags: + - users + summary: Register new user + description: Register new user + operationId: api_views.users.register_user + requestBody: + description: Username of the user + content: + application/json: + schema: + type: object + properties: + username: + type: string + example: 'John.Doe' + password: + type: string + example: 'password123' + email: + type: string + example: 'user@tempmail.com' + required: true + responses: + '200': + description: Sucessfully created user + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: 'Successfully registered. Login to receive an auth token.' + status: + type: string + enum: ['success', 'fail'] + example: 'success' + '400': + description: Invalid request + content: {} + /users/v1/login: + post: + tags: + - users + summary: Login to VAmPI + description: Login to VAmPI + operationId: api_views.users.login_user + requestBody: + description: Username of the user + content: + application/json: + schema: + type: object + properties: + username: + type: string + example: 'John.Doe' + password: + type: string + example: 'password123' + required: true + responses: + '200': + description: Sucessfully logged in user + content: + application/json: + schema: + type: object + properties: + auth_token: + type: string + example: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NzAxNjA2MTcsImlhdCI6MTY3MDE2MDU1Nywic3ViIjoiSm9obi5Eb2UifQ.n17N4AxTbL4_z65-NR46meoytauPDjImUxrLiUMSTQw' + message: + type: string + example: 'Successfully logged in.' + status: + type: string + enum: ['success', 'fail'] + example: 'success' + '400': + description: Invalid request + content: + application/json: + schema: + type: object + properties: + status: + type: string + enum: ['fail'] + example: 'fail' + message: + type: string + example: 'Password is not correct for the given username.' + /users/v1/{username}: + get: + tags: + - users + summary: Retrieves user by username + description: Displays user by username + operationId: api_views.users.get_by_username + parameters: + - name: username + in: path + description: retrieve username data + required: true + schema: + type: string + example: 'John.Doe' + responses: + '200': + description: Successfully display user info + content: + application/json: + schema: + type: array + items: + type: object + properties: + username: + type: string + example: 'John.Doe' + email: + type: string + example: 'user@tempmail.com' + '404': + description: User not found + content: + application/json: + schema: + type: object + properties: + status: + type: string + enum: ['fail'] + example: 'fail' + message: + type: string + example: 'User not found' + + delete: + tags: + - users + summary: Deletes user by username (Only Admins) + description: Deletes user by username (Only Admins) + operationId: api_views.users.delete_user + parameters: + - name: username + in: path + description: Delete username + required: true + schema: + type: string + example: 'name1' + responses: + '200': + description: Sucessfully deleted user + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: 'User deleted.' + status: + type: string + enum: ['success', 'fail'] + example: 'success' + '401': + description: User not authorized + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: 'fail' + enum: ['fail'] + message: + type: string + example: 'Only Admins may delete users!' + '404': + description: User not found + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: 'fail' + enum: ['fail'] + message: + type: string + example: 'User not found!' + /users/v1/{username}/email: + put: + tags: + - users + summary: Update users email + description: Update a single users email + operationId: api_views.users.update_email + parameters: + - name: username + in: path + description: username to update email + required: true + schema: + type: string + example: 'name1' + requestBody: + description: field to update + content: + application/json: + schema: + type: object + properties: + email: + type: string + example: 'mail3@mail.com' + required: true + responses: + '204': + description: Sucessfully updated user email + content: {} + '400': + description: Invalid request + content: + application/json: + schema: + type: object + properties: + status: + type: string + enum: ['fail'] + example: 'fail' + message: + type: string + example: 'Please Provide a valid email address.' + '401': + description: User not authorized + content: + application/json: + schema: + type: object + properties: + status: + type: string + enum: ['fail'] + example: 'fail' + message: + type: string + example: 'Invalid Token' + /users/v1/{username}/password: + put: + tags: + - users + summary: Update users password + description: Update users password + operationId: api_views.users.update_password + parameters: + - name: username + in: path + description: username to update password + required: true + schema: + type: string + example: 'name1' + requestBody: + description: field to update + content: + application/json: + schema: + type: object + properties: + password: + type: string + example: 'pass4' + required: true + responses: + '204': + description: Sucessfully updated users password + content: {} + '400': + description: Invalid request + content: + application/json: + schema: + type: object + properties: + status: + type: string + enum: ['fail'] + example: 'fail' + message: + type: string + example: 'Malformed Data' + '401': + description: User not authorized + content: + application/json: + schema: + type: object + properties: + status: + type: string + enum: ['fail'] + example: 'fail' + message: + type: string + example: 'Invalid Token' + /books/v1: + get: + tags: + - books + summary: Retrieves all books + description: Retrieves all books + operationId: api_views.books.get_all_books + responses: + '200': + description: See all books + content: + application/json: + schema: + type: object + properties: + Books: + type: array + items: + type: object + properties: + book_title: + type: string + user: + type: string + example: + Books: + - book_title: 'bookTitle77' + user: 'name1' + - book_title: 'bookTitle85' + user: 'name2' + - book_title: 'bookTitle47' + user: 'admin' + post: + tags: + - books + summary: Add new book + description: Add new book + operationId: api_views.books.add_new_book + requestBody: + description: >- + Add new book with title and secret content only available to the user + who added it. + content: + application/json: + schema: + type: object + properties: + book_title: + type: string + example: 'book99' + secret: + type: string + example: 'pass1secret' + required: true + responses: + '200': + description: Sucessfully added a book + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: 'Book has been added.' + status: + type: string + enum: ['success', 'fail'] + example: 'success' + '400': + description: Invalid request + content: + application/json: + schema: + type: object + properties: + status: + type: string + enum: ['fail'] + example: 'fail' + message: + type: string + example: 'Book Already exists!' + '401': + description: User not authorized + content: + application/json: + schema: + type: object + properties: + status: + type: string + enum: ['fail'] + example: 'fail' + message: + type: string + example: 'Invalid Token' + /books/v1/{book_title}: + get: + tags: + - books + summary: Retrieves book by title along with secret + description: >- + Retrieves book by title along with secret. Only the owner may retrieve + it + operationId: api_views.books.get_by_title + parameters: + - name: book_title + in: path + description: retrieve book data + required: true + schema: + type: string + example: 'bookTitle77' + responses: + '200': + description: Successfully retrieve book info + content: + application/json: + schema: + type: array + items: + type: object + properties: + book_title: + type: string + example: 'bookTitle77' + owner: + type: string + example: 'name1' + secret: + type: string + example: 'secret for bookTitle77' + '401': + description: User not authorized + content: + application/json: + schema: + type: object + properties: + status: + type: string + enum: ['fail'] + example: 'fail' + message: + type: string + example: 'Invalid Token' + '404': + description: Book not found + content: + application/json: + schema: + type: object + properties: + status: + type: string + enum: ['fail'] + example: 'fail' + message: + type: string + example: 'Book not found!' \ No newline at end of file diff --git a/pkg/input/formats/testdata/postman.json b/pkg/input/formats/testdata/postman.json new file mode 100644 index 00000000..50044e4e --- /dev/null +++ b/pkg/input/formats/testdata/postman.json @@ -0,0 +1,159 @@ +{ + "info": { + "_postman_id": "20a3fd41-6a86-4e49-8860-f796559d0223", + "name": "advancedsearch", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "List Projects, Assets and Hosts", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://127.0.0.1:8000/api/v1/search/", + "protocol": "http", + "host": ["127", "0", "0", "1"], + "port": "8000", + "path": ["api", "v1", "search", ""] + } + }, + "response": [] + }, + { + "name": "List Assets and Hosts", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://127.0.0.1:8000/api/v1/search/?projectId=1,2", + "protocol": "http", + "host": ["127", "0", "0", "1"], + "port": "8000", + "path": ["api", "v1", "search", ""], + "query": [ + { + "key": "projectId", + "value": "1,2" + } + ] + } + }, + "response": [] + }, + { + "name": "List Hosts", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://127.0.0.1:8000/api/v1/search/?projectId=1,2&assetId=1,2", + "protocol": "http", + "host": ["127", "0", "0", "1"], + "port": "8000", + "path": ["api", "v1", "search", ""], + "query": [ + { + "key": "projectId", + "value": "1,2" + }, + { + "key": "assetId", + "value": "1,2" + } + ] + } + }, + "response": [] + }, + { + "name": "Search Request", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"query\": \"query\",\n\t\"projectId\": [4,3,4],\n\t\"assetId\": [2,3,4],\n\t\"hostId\": [1,2,3],\n \"limit\": 10,\n \"offset\": 10\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://127.0.0.1:8000/api/v1/search/", + "protocol": "http", + "host": ["127", "0", "0", "1"], + "port": "8000", + "path": ["api", "v1", "search", ""] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} +} diff --git a/pkg/input/formats/testdata/swagger.yaml b/pkg/input/formats/testdata/swagger.yaml new file mode 100644 index 00000000..cd20b184 --- /dev/null +++ b/pkg/input/formats/testdata/swagger.yaml @@ -0,0 +1,37 @@ +swagger: "2.0" +info: + title: Sample API + description: API description in Markdown. + version: 1.0.0 +host: localhost +basePath: /v1 +schemes: + - https +paths: + /users: + get: + summary: Returns a list of users. + description: Optional extended description in Markdown. + produces: + - application/json + responses: + 200: + description: OK + /users/{userId}: + get: + summary: Returns a user by ID. + parameters: + - in: path + name: userId + required: true + type: integer + default: 1 + description: Parameter description in Markdown. + - in: query + name: test + type: string + enum: [asc, desc] + description: Type of query + responses: + 200: + description: OK \ No newline at end of file diff --git a/pkg/input/formats/yaml/multidoc.go b/pkg/input/formats/yaml/multidoc.go new file mode 100644 index 00000000..dc258408 --- /dev/null +++ b/pkg/input/formats/yaml/multidoc.go @@ -0,0 +1,78 @@ +package yaml + +import ( + "io" + "os" + "strings" + + "github.com/pkg/errors" + "github.com/projectdiscovery/gologger" + "github.com/projectdiscovery/nuclei/v3/pkg/input/formats" + "github.com/projectdiscovery/nuclei/v3/pkg/input/types" + YamlUtil "gopkg.in/yaml.v3" +) + +// YamlMultiDocFormat is a Yaml format parser for nuclei +// input HTTP requests with multiple documents separated by --- +type YamlMultiDocFormat struct { + opts formats.InputFormatOptions +} + +// New creates a new JSON format parser +func New() *YamlMultiDocFormat { + return &YamlMultiDocFormat{} +} + +var _ formats.Format = &YamlMultiDocFormat{} + +// proxifyRequest is a request for proxify +type proxifyRequest struct { + URL string `json:"url"` + Request struct { + Header map[string]string `json:"header"` + Body string `json:"body"` + Raw string `json:"raw"` + } `json:"request"` +} + +// Name returns the name of the format +func (j *YamlMultiDocFormat) Name() string { + return "yaml" +} + +func (j *YamlMultiDocFormat) SetOptions(options formats.InputFormatOptions) { + j.opts = options +} + +// Parse parses the input and calls the provided callback +// function for each RawRequest it discovers. +func (j *YamlMultiDocFormat) Parse(input string, resultsCb formats.ParseReqRespCallback) error { + file, err := os.Open(input) + if err != nil { + return errors.Wrap(err, "could not open json file") + } + defer file.Close() + + decoder := YamlUtil.NewDecoder(file) + for { + var request proxifyRequest + err := decoder.Decode(&request) + if err == io.EOF { + break + } + if err != nil { + return errors.Wrap(err, "could not decode json file") + } + if strings.TrimSpace(request.Request.Raw) == "" { + continue + } + + rawRequest, err := types.ParseRawRequestWithURL(request.Request.Raw, request.URL) + if err != nil { + gologger.Warning().Msgf("multidoc-yaml: Could not parse raw request %s: %s\n", request.URL, err) + continue + } + resultsCb(rawRequest) + } + return nil +} diff --git a/pkg/input/formats/yaml/multidoc_test.go b/pkg/input/formats/yaml/multidoc_test.go new file mode 100644 index 00000000..6275eae5 --- /dev/null +++ b/pkg/input/formats/yaml/multidoc_test.go @@ -0,0 +1,28 @@ +package yaml + +import ( + "testing" + + "github.com/projectdiscovery/nuclei/v3/pkg/input/types" + "github.com/stretchr/testify/require" +) + +func TestYamlFormatterParse(t *testing.T) { + format := New() + + proxifyInputFile := "../testdata/ginandjuice.proxify.yaml" + + expectedUrls := []string{ + "https://ginandjuice.shop/blog/post?postId=3&source=proxify", + "https://ginandjuice.shop/users/3", + } + + var urls []string + err := format.Parse(proxifyInputFile, func(request *types.RequestResponse) bool { + urls = append(urls, request.URL.String()) + return false + }) + require.Nilf(t, err, "error parsing yaml file: %v", err) + require.Len(t, urls, len(expectedUrls), "invalid number of urls") + require.ElementsMatch(t, urls, expectedUrls, "invalid urls") +} diff --git a/pkg/input/provider/chunked.go b/pkg/input/provider/chunked.go new file mode 100644 index 00000000..904acd5b --- /dev/null +++ b/pkg/input/provider/chunked.go @@ -0,0 +1,29 @@ +package provider + +import ( + "github.com/projectdiscovery/nuclei/v3/pkg/input/types" + "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" +) + +// TODO: Implement ChunkedInputProvider +// 1. Lazy loading of input targets +// 2. Load and execute in chunks that fit in memory +// 3. Eliminate use of HybridMap since it performs worst due to marshal/unmarshal overhead + +// ChunkedInputProvider is an input providing chunked targets instead of loading all at once +type ChunkedInputProvider interface { + // Count returns total targets for input provider + Count() int64 + // Iterate over all inputs in order + Iterate(callback func(value *contextargs.MetaInput) bool) + // Set adds item to input provider + Set(value string) + // SetWithProbe adds item to input provider with http probing + SetWithProbe(value string, probe types.InputLivenessProbe) error + // SetWithExclusions adds item to input provider if it doesn't match any of the exclusions + SetWithExclusions(value string) error + // InputType returns the type of input provider + InputType() string + // Switches to the next chunk/batch of input + NextChunk() bool +} diff --git a/pkg/input/provider/http/multiformat.go b/pkg/input/provider/http/multiformat.go new file mode 100644 index 00000000..ee95079e --- /dev/null +++ b/pkg/input/provider/http/multiformat.go @@ -0,0 +1,120 @@ +package http + +import ( + "strings" + + "github.com/pkg/errors" + "github.com/projectdiscovery/gologger" + "github.com/projectdiscovery/nuclei/v3/pkg/input/formats" + "github.com/projectdiscovery/nuclei/v3/pkg/input/formats/burp" + "github.com/projectdiscovery/nuclei/v3/pkg/input/formats/json" + "github.com/projectdiscovery/nuclei/v3/pkg/input/formats/openapi" + "github.com/projectdiscovery/nuclei/v3/pkg/input/formats/swagger" + "github.com/projectdiscovery/nuclei/v3/pkg/input/formats/yaml" + "github.com/projectdiscovery/nuclei/v3/pkg/input/types" + "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" +) + +// HttpMultiFormatOptions contains options for the http input provider +type HttpMultiFormatOptions struct { + // Options for the http input provider + Options formats.InputFormatOptions + // InputFile is the file containing the input + InputFile string + // InputMode is the mode of input + InputMode string +} + +// HttpInputProvider implements an input provider for nuclei that loads +// inputs from multiple formats like burp, openapi, postman,proxify, etc. +type HttpInputProvider struct { + format formats.Format + inputFile string + count int64 +} + +// NewHttpInputProvider creates a new input provider for nuclei from a file +func NewHttpInputProvider(opts *HttpMultiFormatOptions) (*HttpInputProvider, error) { + var format formats.Format + for _, provider := range providersList { + if provider.Name() == opts.InputMode { + format = provider + } + } + if format == nil { + return nil, errors.Errorf("invalid input mode %s", opts.InputMode) + } + format.SetOptions(opts.Options) + // Do a first pass over the input to identify any errors + // and get the count of the input file as well + count := int64(0) + parseErr := format.Parse(opts.InputFile, func(request *types.RequestResponse) bool { + count++ + return false + }) + if parseErr != nil { + return nil, errors.Wrap(parseErr, "could not parse input file") + } + return &HttpInputProvider{format: format, inputFile: opts.InputFile, count: count}, nil +} + +// Count returns the number of items for input provider +func (i *HttpInputProvider) Count() int64 { + return i.count +} + +// Iterate over all inputs in order +func (i *HttpInputProvider) Iterate(callback func(value *contextargs.MetaInput) bool) { + err := i.format.Parse(i.inputFile, func(request *types.RequestResponse) bool { + return callback(&contextargs.MetaInput{ + ReqResp: request, + }) + }) + if err != nil { + gologger.Warning().Msgf("Could not parse input file while iterating: %s\n", err) + } +} + +// Set adds item to input provider +// No-op for this provider +func (i *HttpInputProvider) Set(value string) {} + +// SetWithProbe adds item to input provider with http probing +// No-op for this provider +func (i *HttpInputProvider) SetWithProbe(value string, probe types.InputLivenessProbe) error { + return nil +} + +// SetWithExclusions adds item to input provider if it doesn't match any of the exclusions +// No-op for this provider +func (i *HttpInputProvider) SetWithExclusions(value string) error { + return nil +} + +// InputType returns the type of input provider +func (i *HttpInputProvider) InputType() string { + return "MultiFormatInputProvider" +} + +// Close closes the input provider and cleans up any resources +// No-op for this provider +func (i *HttpInputProvider) Close() {} + +// Supported Providers +var providersList = []formats.Format{ + burp.New(), + json.New(), + yaml.New(), + openapi.New(), + swagger.New(), +} + +// SupportedFormats returns the list of supported formats in comma-separated +// manner +func SupportedFormats() string { + var formats []string + for _, provider := range providersList { + formats = append(formats, provider.Name()) + } + return strings.Join(formats, ", ") +} diff --git a/pkg/input/provider/interface.go b/pkg/input/provider/interface.go new file mode 100644 index 00000000..cb48fc76 --- /dev/null +++ b/pkg/input/provider/interface.go @@ -0,0 +1,126 @@ +package provider + +import ( + "errors" + "fmt" + "strings" + + "github.com/projectdiscovery/gologger" + "github.com/projectdiscovery/nuclei/v3/pkg/input/formats" + "github.com/projectdiscovery/nuclei/v3/pkg/input/provider/http" + "github.com/projectdiscovery/nuclei/v3/pkg/input/provider/list" + "github.com/projectdiscovery/nuclei/v3/pkg/input/types" + "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" + "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators" + configTypes "github.com/projectdiscovery/nuclei/v3/pkg/types" + errorutil "github.com/projectdiscovery/utils/errors" +) + +var ( + ErrNotImplemented = errorutil.NewWithFmt("provider %s does not implement %s") + ErrInactiveInput = fmt.Errorf("input is inactive") +) + +const ( + MultiFormatInputProvider = "MultiFormatInputProvider" + ListInputProvider = "ListInputProvider" + SimpleListInputProvider = "SimpleInputProvider" +) + +// IsErrNotImplemented checks if an error is a not implemented error +func IsErrNotImplemented(err error) bool { + if err == nil { + return false + } + if strings.Contains(err.Error(), "provider") && strings.Contains(err.Error(), "does not implement") { + return true + } + return false +} + +// Validate all Implementations +var ( + // SimpleInputProvider is more like a No-Op and returns given list of urls as input + _ InputProvider = &SimpleInputProvider{} + // HttpInputProvider provides support for formats that contain complete request/response + // like burp, openapi, postman,proxify, etc. + _ InputProvider = &http.HttpInputProvider{} + // ListInputProvider provides support for simple list of urls or files etc + _ InputProvider = &list.ListInputProvider{} +) + +// InputProvider is unified input provider interface that provides +// processed inputs to nuclei by parsing and providing different +// formats such as list,openapi,postman,proxify,burp etc. +type InputProvider interface { + // Count returns total targets for input provider + Count() int64 + // Iterate over all inputs in order + Iterate(callback func(value *contextargs.MetaInput) bool) + // Set adds item to input provider + Set(value string) + // SetWithProbe adds item to input provider with http probing + SetWithProbe(value string, probe types.InputLivenessProbe) error + // SetWithExclusions adds item to input provider if it doesn't match any of the exclusions + SetWithExclusions(value string) error + // InputType returns the type of input provider + InputType() string + // Close the input provider and cleanup any resources + Close() +} + +// InputOptions contains options for input provider +type InputOptions struct { + // Options for global config + Options *configTypes.Options + // NotFoundCallback is the callback to call when input is not found + // only supported in list input provider + NotFoundCallback func(template string) bool +} + +// NewInputProvider creates a new input provider based on the options +// and returns it +func NewInputProvider(opts InputOptions) (InputProvider, error) { + // optionally load generated vars values if available + val, err := formats.ReadOpenAPIVarDumpFile() + if err != nil && !errors.Is(err, formats.ErrNoVarsDumpFile) { + // log error and continue + gologger.Error().Msgf("Could not read vars dump file: %s\n", err) + } + extraVars := make(map[string]interface{}) + if val != nil { + for _, v := range val.Var { + v = strings.TrimSpace(v) + // split into key value + parts := strings.SplitN(v, "=", 2) + if len(parts) == 2 { + extraVars[parts[0]] = parts[1] + } + } + } + + // check if input provider is supported + if strings.EqualFold(opts.Options.InputFileMode, "list") { + // create a new list input provider + return list.New(&list.Options{ + Options: opts.Options, + NotFoundCallback: opts.NotFoundCallback, + }) + } else { + // use HttpInputProvider + return http.NewHttpInputProvider(&http.HttpMultiFormatOptions{ + InputFile: opts.Options.TargetsFilePath, + InputMode: opts.Options.InputFileMode, + Options: formats.InputFormatOptions{ + Variables: generators.MergeMaps(extraVars, opts.Options.Vars.AsMap()), + SkipFormatValidation: opts.Options.SkipFormatValidation, + RequiredOnly: opts.Options.FormatUseRequiredOnly, + }, + }) + } +} + +// SupportedFormats returns all supported input formats of nuclei +func SupportedInputFormats() string { + return "list, " + http.SupportedFormats() +} diff --git a/pkg/core/inputs/hybrid/hmap.go b/pkg/input/provider/list/hmap.go similarity index 87% rename from pkg/core/inputs/hybrid/hmap.go rename to pkg/input/provider/list/hmap.go index bbc675dc..f7e3bd14 100644 --- a/pkg/core/inputs/hybrid/hmap.go +++ b/pkg/input/provider/list/hmap.go @@ -1,6 +1,6 @@ -// Package hybrid implements a hybrid hmap/filekv backed input provider +// package list implements a hybrid hmap/filekv backed input provider // for nuclei that can either stream or store results using different kv stores. -package hybrid +package list import ( "bufio" @@ -19,6 +19,7 @@ import ( "github.com/projectdiscovery/hmap/filekv" "github.com/projectdiscovery/hmap/store/hybrid" "github.com/projectdiscovery/mapcidr/asn" + providerTypes "github.com/projectdiscovery/nuclei/v3/pkg/input/types" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/uncover" @@ -34,8 +35,9 @@ import ( const DefaultMaxDedupeItemsCount = 10000 -// Input is a hmap/filekv backed nuclei Input provider -type Input struct { +// ListInputProvider is a hmap/filekv backed nuclei ListInputProvider provider +// it supports list type of input ex: urls,file,stdin,uncover,etc. (i.e just url not complete request/response) +type ListInputProvider struct { ipOptions *ipOptions inputCount int64 excludedCount int64 @@ -59,7 +61,7 @@ type Options struct { // New creates a new hmap backed nuclei Input Provider // and initializes it based on the passed options Model. -func New(opts *Options) (*Input, error) { +func New(opts *Options) (*ListInputProvider, error) { options := opts.Options hm, err := hybrid.New(hybrid.DefaultDiskOptions) @@ -67,7 +69,7 @@ func New(opts *Options) (*Input, error) { return nil, errors.Wrap(err, "could not create temporary input file") } - input := &Input{ + input := &ListInputProvider{ hostMap: hm, ipOptions: &ipOptions{ ScanAllIPs: options.ScanAllIPs, @@ -105,109 +107,39 @@ func New(opts *Options) (*Input, error) { return input, nil } -// Close closes the input provider -func (i *Input) Close() { - i.hostMap.Close() - if i.hostMapStream != nil { - i.hostMapStream.Close() - } +// Count returns the input count +func (i *ListInputProvider) Count() int64 { + return i.inputCount } -// initializeInputSources initializes the input sources for hmap input -func (i *Input) initializeInputSources(opts *Options) error { - options := opts.Options - - // Handle targets flags - for _, target := range options.Targets { - switch { - case iputil.IsCIDR(target): - ips := expand.CIDR(target) - i.addTargets(ips) - case asn.IsASN(target): - ips := expand.ASN(target) - i.addTargets(ips) - default: - i.Set(target) - } - } - - // Handle stdin - if options.Stdin { - i.scanInputFromReader(readerutil.TimeoutReader{Reader: os.Stdin, Timeout: time.Duration(options.InputReadTimeout)}) - } - - // Handle target file - if options.TargetsFilePath != "" { - input, inputErr := os.Open(options.TargetsFilePath) - if inputErr != nil { - // Handle cloud based input here. - if opts.NotFoundCallback == nil || !opts.NotFoundCallback(options.TargetsFilePath) { - return errors.Wrap(inputErr, "could not open targets file") +// Iterate over all inputs in order +func (i *ListInputProvider) Iterate(callback func(value *contextargs.MetaInput) bool) { + if i.hostMapStream != nil { + i.hostMapStreamOnce.Do(func() { + if err := i.hostMapStream.Process(); err != nil { + gologger.Warning().Msgf("error in stream mode processing: %s\n", err) } - } - if input != nil { - i.scanInputFromReader(input) - input.Close() - } + }) } - if options.Uncover && options.UncoverQuery != nil { - gologger.Info().Msgf("Running uncover query against: %s", strings.Join(options.UncoverEngine, ",")) - uncoverOpts := &uncoverlib.Options{ - Agents: options.UncoverEngine, - Queries: options.UncoverQuery, - Limit: options.UncoverLimit, - MaxRetry: options.Retries, - Timeout: options.Timeout, - RateLimit: uint(options.UncoverRateLimit), - RateLimitUnit: time.Minute, // default unit is minute - } - ch, err := uncover.GetTargetsFromUncover(context.TODO(), options.UncoverField, uncoverOpts) - if err != nil { + callbackFunc := func(k, _ []byte) error { + metaInput := &contextargs.MetaInput{} + if err := metaInput.Unmarshal(string(k)); err != nil { return err } - for c := range ch { - i.Set(c) + if !callback(metaInput) { + return io.EOF } + return nil } - - if len(options.ExcludeTargets) > 0 { - for _, target := range options.ExcludeTargets { - switch { - case iputil.IsCIDR(target): - ips := expand.CIDR(target) - i.removeTargets(ips) - case asn.IsASN(target): - ips := expand.ASN(target) - i.removeTargets(ips) - default: - i.Del(target) - } - } - } - - return nil -} - -// scanInputFromReader scans a line of input from reader and passes it for storage -func (i *Input) scanInputFromReader(reader io.Reader) { - scanner := bufio.NewScanner(reader) - for scanner.Scan() { - item := scanner.Text() - switch { - case iputil.IsCIDR(item): - ips := expand.CIDR(item) - i.addTargets(ips) - case asn.IsASN(item): - ips := expand.ASN(item) - i.addTargets(ips) - default: - i.Set(item) - } + if i.hostMapStream != nil { + _ = i.hostMapStream.Scan(callbackFunc) + } else { + i.hostMap.Scan(callbackFunc) } } // Set normalizes and stores passed input values -func (i *Input) Set(value string) { +func (i *ListInputProvider) Set(value string) { URL := strings.TrimSpace(value) if URL == "" { return @@ -289,21 +221,138 @@ func (i *Input) Set(value string) { } } +// SetWithProbe only sets the input if it is live +func (i *ListInputProvider) SetWithProbe(value string, probe providerTypes.InputLivenessProbe) error { + probedValue, err := probe.ProbeURL(value) + if err != nil { + return err + } + i.Set(probedValue) + return nil +} + // SetWithExclusions normalizes and stores passed input values if not excluded -func (i *Input) SetWithExclusions(value string) { +func (i *ListInputProvider) SetWithExclusions(value string) error { URL := strings.TrimSpace(value) if URL == "" { - return + return nil } if i.isExcluded(URL) { i.skippedCount++ - return + return nil } i.Set(URL) + return nil +} + +// ListInputProvider is a hmap/filekv backed nuclei ListInputProvider provider +func (i *ListInputProvider) InputType() string { + return "ListInputProvider" +} + +// Close closes the input provider +func (i *ListInputProvider) Close() { + i.hostMap.Close() + if i.hostMapStream != nil { + i.hostMapStream.Close() + } +} + +// initializeInputSources initializes the input sources for hmap input +func (i *ListInputProvider) initializeInputSources(opts *Options) error { + options := opts.Options + + // Handle targets flags + for _, target := range options.Targets { + switch { + case iputil.IsCIDR(target): + ips := expand.CIDR(target) + i.addTargets(ips) + case asn.IsASN(target): + ips := expand.ASN(target) + i.addTargets(ips) + default: + i.Set(target) + } + } + + // Handle stdin + if options.Stdin { + i.scanInputFromReader(readerutil.TimeoutReader{Reader: os.Stdin, Timeout: time.Duration(options.InputReadTimeout)}) + } + + // Handle target file + if options.TargetsFilePath != "" { + input, inputErr := os.Open(options.TargetsFilePath) + if inputErr != nil { + // Handle cloud based input here. + if opts.NotFoundCallback == nil || !opts.NotFoundCallback(options.TargetsFilePath) { + return errors.Wrap(inputErr, "could not open targets file") + } + } + if input != nil { + i.scanInputFromReader(input) + input.Close() + } + } + if options.Uncover && options.UncoverQuery != nil { + gologger.Info().Msgf("Running uncover query against: %s", strings.Join(options.UncoverEngine, ",")) + uncoverOpts := &uncoverlib.Options{ + Agents: options.UncoverEngine, + Queries: options.UncoverQuery, + Limit: options.UncoverLimit, + MaxRetry: options.Retries, + Timeout: options.Timeout, + RateLimit: uint(options.UncoverRateLimit), + RateLimitUnit: time.Minute, // default unit is minute + } + ch, err := uncover.GetTargetsFromUncover(context.TODO(), options.UncoverField, uncoverOpts) + if err != nil { + return err + } + for c := range ch { + i.Set(c) + } + } + + if len(options.ExcludeTargets) > 0 { + for _, target := range options.ExcludeTargets { + switch { + case iputil.IsCIDR(target): + ips := expand.CIDR(target) + i.removeTargets(ips) + case asn.IsASN(target): + ips := expand.ASN(target) + i.removeTargets(ips) + default: + i.Del(target) + } + } + } + + return nil +} + +// scanInputFromReader scans a line of input from reader and passes it for storage +func (i *ListInputProvider) scanInputFromReader(reader io.Reader) { + scanner := bufio.NewScanner(reader) + for scanner.Scan() { + item := scanner.Text() + switch { + case iputil.IsCIDR(item): + ips := expand.CIDR(item) + i.addTargets(ips) + case asn.IsASN(item): + ips := expand.ASN(item) + i.addTargets(ips) + default: + i.Set(item) + } + } } // isExcluded checks if a URL is in the exclusion list -func (i *Input) isExcluded(URL string) bool { +func (i *ListInputProvider) isExcluded(URL string) bool { metaInput := &contextargs.MetaInput{Input: URL} key, err := metaInput.MarshalString() if err != nil { @@ -315,7 +364,7 @@ func (i *Input) isExcluded(URL string) bool { return exists } -func (i *Input) Del(value string) { +func (i *ListInputProvider) Del(value string) { URL := strings.TrimSpace(value) if URL == "" { return @@ -398,7 +447,7 @@ func (i *Input) Del(value string) { } // setItem in the kv store -func (i *Input) setItem(metaInput *contextargs.MetaInput) { +func (i *ListInputProvider) setItem(metaInput *contextargs.MetaInput) { key, err := metaInput.MarshalString() if err != nil { gologger.Warning().Msgf("%s\n", err) @@ -417,7 +466,7 @@ func (i *Input) setItem(metaInput *contextargs.MetaInput) { } // setItem in the kv store -func (i *Input) delItem(metaInput *contextargs.MetaInput) { +func (i *ListInputProvider) delItem(metaInput *contextargs.MetaInput) { targetUrl, err := urlutil.ParseURL(metaInput.Input, true) if err != nil { gologger.Warning().Msgf("%s\n", err) @@ -450,52 +499,20 @@ func (i *Input) delItem(metaInput *contextargs.MetaInput) { } // setHostMapStream sets item in stream mode -func (i *Input) setHostMapStream(data string) { +func (i *ListInputProvider) setHostMapStream(data string) { if _, err := i.hostMapStream.Merge([][]byte{[]byte(data)}); err != nil { gologger.Warning().Msgf("%s\n", err) return } } -// Count returns the input count -func (i *Input) Count() int64 { - return i.inputCount -} - -// Scan iterates the input and each found item is passed to the -// callback consumer. -func (i *Input) Scan(callback func(value *contextargs.MetaInput) bool) { - if i.hostMapStream != nil { - i.hostMapStreamOnce.Do(func() { - if err := i.hostMapStream.Process(); err != nil { - gologger.Warning().Msgf("error in stream mode processing: %s\n", err) - } - }) - } - callbackFunc := func(k, _ []byte) error { - metaInput := &contextargs.MetaInput{} - if err := metaInput.Unmarshal(string(k)); err != nil { - return err - } - if !callback(metaInput) { - return io.EOF - } - return nil - } - if i.hostMapStream != nil { - _ = i.hostMapStream.Scan(callbackFunc) - } else { - i.hostMap.Scan(callbackFunc) - } -} - -func (i *Input) addTargets(targets []string) { +func (i *ListInputProvider) addTargets(targets []string) { for _, target := range targets { i.Set(target) } } -func (i *Input) removeTargets(targets []string) { +func (i *ListInputProvider) removeTargets(targets []string) { for _, target := range targets { metaInput := &contextargs.MetaInput{Input: target} i.delItem(metaInput) diff --git a/pkg/core/inputs/hybrid/hmap_test.go b/pkg/input/provider/list/hmap_test.go similarity index 97% rename from pkg/core/inputs/hybrid/hmap_test.go rename to pkg/input/provider/list/hmap_test.go index c11c6efd..c3fb4a66 100644 --- a/pkg/core/inputs/hybrid/hmap_test.go +++ b/pkg/input/provider/list/hmap_test.go @@ -1,4 +1,4 @@ -package hybrid +package list import ( "net" @@ -32,7 +32,7 @@ func Test_expandCIDR(t *testing.T) { for _, tt := range tests { hm, err := hybrid.New(hybrid.DefaultDiskOptions) require.Nil(t, err, "could not create temporary input file") - input := &Input{hostMap: hm} + input := &ListInputProvider{hostMap: hm} ips := expand.CIDR(tt.cidr) input.addTargets(ips) @@ -127,7 +127,7 @@ func Test_scanallips_normalizeStoreInputValue(t *testing.T) { for _, tt := range tests { hm, err := hybrid.New(hybrid.DefaultDiskOptions) require.Nil(t, err, "could not create temporary input file") - input := &Input{ + input := &ListInputProvider{ hostMap: hm, ipOptions: &ipOptions{ ScanAllIPs: true, @@ -169,7 +169,7 @@ func Test_expandASNInputValue(t *testing.T) { for _, tt := range tests { hm, err := hybrid.New(hybrid.DefaultDiskOptions) require.Nil(t, err, "could not create temporary input file") - input := &Input{hostMap: hm} + input := &ListInputProvider{hostMap: hm} // get the IP addresses for ASN number ips := expand.ASN(tt.asn) input.addTargets(ips) diff --git a/pkg/core/inputs/hybrid/tests/AS134029.txt b/pkg/input/provider/list/tests/AS134029.txt similarity index 100% rename from pkg/core/inputs/hybrid/tests/AS134029.txt rename to pkg/input/provider/list/tests/AS134029.txt diff --git a/pkg/core/inputs/hybrid/tests/AS14421.txt b/pkg/input/provider/list/tests/AS14421.txt similarity index 100% rename from pkg/core/inputs/hybrid/tests/AS14421.txt rename to pkg/input/provider/list/tests/AS14421.txt diff --git a/pkg/core/inputs/hybrid/options.go b/pkg/input/provider/list/utils.go similarity index 83% rename from pkg/core/inputs/hybrid/options.go rename to pkg/input/provider/list/utils.go index 8c7f13af..221b40a8 100644 --- a/pkg/core/inputs/hybrid/options.go +++ b/pkg/input/provider/list/utils.go @@ -1,4 +1,4 @@ -package hybrid +package list type ipOptions struct { ScanAllIPs bool diff --git a/pkg/input/provider/simple.go b/pkg/input/provider/simple.go new file mode 100644 index 00000000..71b280c2 --- /dev/null +++ b/pkg/input/provider/simple.go @@ -0,0 +1,73 @@ +package provider + +import ( + "github.com/projectdiscovery/nuclei/v3/pkg/input/types" + "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" +) + +// SimpleInputProvider is a simple input provider for nuclei +// that acts like a No-Op and returns given list of urls as input +type SimpleInputProvider struct { + Inputs []*contextargs.MetaInput +} + +// NewSimpleInputProvider creates a new simple input provider +func NewSimpleInputProvider() *SimpleInputProvider { + return &SimpleInputProvider{ + Inputs: make([]*contextargs.MetaInput, 0), + } +} + +// NewSimpleInputProviderWithUrls creates a new simple input provider with the given urls +func NewSimpleInputProviderWithUrls(urls ...string) *SimpleInputProvider { + provider := NewSimpleInputProvider() + for _, url := range urls { + provider.Set(url) + } + return provider +} + +// Count returns the total number of targets for the input provider +func (s *SimpleInputProvider) Count() int64 { + return int64(len(s.Inputs)) +} + +// Iterate over all inputs in order +func (s *SimpleInputProvider) Iterate(callback func(value *contextargs.MetaInput) bool) { + for _, input := range s.Inputs { + if !callback(input) { + break + } + } +} + +// Set adds an item to the input provider +func (s *SimpleInputProvider) Set(value string) { + s.Inputs = append(s.Inputs, &contextargs.MetaInput{Input: value}) +} + +// SetWithProbe adds an item to the input provider with HTTP probing +func (s *SimpleInputProvider) SetWithProbe(value string, probe types.InputLivenessProbe) error { + probedValue, err := probe.ProbeURL(value) + if err != nil { + return err + } + s.Inputs = append(s.Inputs, &contextargs.MetaInput{Input: probedValue}) + return nil +} + +// SetWithExclusions adds an item to the input provider if it doesn't match any of the exclusions +func (s *SimpleInputProvider) SetWithExclusions(value string) error { + s.Inputs = append(s.Inputs, &contextargs.MetaInput{Input: value}) + return nil +} + +// InputType returns the type of input provider +func (s *SimpleInputProvider) InputType() string { + return "SimpleInputProvider" +} + +// Close the input provider and cleanup any resources +func (s *SimpleInputProvider) Close() { + // no-op +} diff --git a/pkg/input/input.go b/pkg/input/transform.go similarity index 100% rename from pkg/input/input.go rename to pkg/input/transform.go diff --git a/pkg/input/input_test.go b/pkg/input/transform_test.go similarity index 100% rename from pkg/input/input_test.go rename to pkg/input/transform_test.go diff --git a/pkg/input/types/http.go b/pkg/input/types/http.go new file mode 100644 index 00000000..62b25249 --- /dev/null +++ b/pkg/input/types/http.go @@ -0,0 +1,305 @@ +package types + +import ( + "bufio" + "bytes" + "crypto/sha256" + "encoding/json" + "fmt" + "io" + "net/textproto" + "strings" + "sync" + + "github.com/projectdiscovery/retryablehttp-go" + "github.com/projectdiscovery/utils/conversion" + mapsutil "github.com/projectdiscovery/utils/maps" + urlutil "github.com/projectdiscovery/utils/url" +) + +var ( + _ json.Marshaler = &RequestResponse{} + _ json.Unmarshaler = &RequestResponse{} +) + +// RequestResponse is a struct containing request and response +// obtained from one of the input formats. +// this struct can be considered as pd standard for request and response +type RequestResponse struct { + // Timestamp is the timestamp of the request + // Timestamp string `json:"timestamp"` + // URL is the URL of the request + URL urlutil.URL `json:"url"` + // Request is the request of the request + Request *HttpRequest `json:"request"` + // Response is the response of the request + Response *HttpResponse `json:"response"` + + // unexported / internal fields + // lazy build request + req *retryablehttp.Request `json:"-"` + reqErr error `json:"-"` + once sync.Once `json:"-"` +} + +// Clone clones the request response +func (rr *RequestResponse) Clone() *RequestResponse { + cloned := &RequestResponse{ + URL: *rr.URL.Clone(), + } + if rr.Request != nil { + cloned.Request = rr.Request.Clone() + } + if rr.Response != nil { + cloned.Response = rr.Response.Clone() + } + return cloned +} + +// BuildRequest builds a retryablehttp request from the request response +func (rr *RequestResponse) BuildRequest() (*retryablehttp.Request, error) { + rr.once.Do(func() { + urlx := rr.URL.Clone() + var body io.Reader = nil + if rr.Request.Body != "" { + body = strings.NewReader(rr.Request.Body) + } + req, err := retryablehttp.NewRequestFromURL(rr.Request.Method, urlx, body) + if err != nil { + rr.reqErr = fmt.Errorf("could not create request: %s", err) + return + } + rr.Request.Headers.Iterate(func(k, v string) bool { + req.Header.Add(k, v) + return true + }) + rr.req = req + }) + return rr.req, rr.reqErr +} + +// To be implemented in the future +// func (rr *RequestResponse) BuildUnsafeRequest() + +// ID returns a unique id/hash for request response +func (rr *RequestResponse) ID() string { + var buff bytes.Buffer + buff.WriteString(rr.URL.String()) + if rr.Request != nil { + buff.WriteString(rr.Request.ID()) + } + if rr.Response != nil { + buff.WriteString(rr.Response.ID()) + } + val := sha256.Sum256(buff.Bytes()) + return string(val[:]) +} + +// MarshalJSON marshals the request response to json +func (rr *RequestResponse) MarshalJSON() ([]byte, error) { + m := make(map[string]interface{}) + m["url"] = rr.URL.String() + reqBin, err := json.Marshal(rr.Request) + if err != nil { + return nil, err + } + m["request"] = reqBin + respBin, err := json.Marshal(rr.Response) + if err != nil { + return nil, err + } + m["response"] = respBin + return json.Marshal(m) +} + +// UnmarshalJSON unmarshals the request response from json +func (rr *RequestResponse) UnmarshalJSON(data []byte) error { + var m map[string]json.RawMessage + if err := json.Unmarshal(data, &m); err != nil { + return err + } + urlStr, ok := m["url"] + if !ok { + return fmt.Errorf("missing url in request response") + } + parsed, err := urlutil.ParseAbsoluteURL(string(urlStr), false) + if err != nil { + return err + } + rr.URL = *parsed + + reqBin, ok := m["request"] + if ok { + var req HttpRequest + if err := json.Unmarshal(reqBin, &req); err != nil { + return err + } + rr.Request = &req + } + + respBin, ok := m["response"] + if ok { + var resp HttpResponse + if err := json.Unmarshal(respBin, &resp); err != nil { + return err + } + rr.Response = &resp + } + return nil +} + +// HttpRequest is a struct containing the http request +type HttpRequest struct { + // method of the request + Method string `json:"method"` + // headers of the request + Headers mapsutil.OrderedMap[string, string] `json:"headers"` + // body of the request + Body string `json:"body"` + // raw request (includes everything including method, headers, body, etc) + Raw string `json:"raw"` +} + +// ID returns a unique id/hash for raw request +func (hr *HttpRequest) ID() string { + val := sha256.Sum256([]byte(hr.Raw)) + return string(val[:]) +} + +// Clone clones the request +func (hr *HttpRequest) Clone() *HttpRequest { + return &HttpRequest{ + Method: hr.Method, + Headers: hr.Headers.Clone(), + Body: hr.Body, + Raw: hr.Raw, + } +} + +type HttpResponse struct { + // status code of the response + StatusCode int `json:"status_code"` + // headers of the response + Headers mapsutil.OrderedMap[string, string] `json:"headers"` + // body of the response + Body string `json:"body"` + // raw response (includes everything including status code, headers, body, etc) + Raw string `json:"raw"` +} + +// Id returns a unique id/hash for raw response +func (hr *HttpResponse) ID() string { + val := sha256.Sum256([]byte(hr.Raw)) + return string(val[:]) +} + +// Clone clones the response +func (hr *HttpResponse) Clone() *HttpResponse { + return &HttpResponse{ + StatusCode: hr.StatusCode, + Headers: hr.Headers.Clone(), + Body: hr.Body, + Raw: hr.Raw, + } +} + +// ParseRawRequest parses a raw request from a string +// and returns the request and response object +// Note: it currently does not parse response and is meant to be added manually since its a optional field +func ParseRawRequest(raw string) (rr *RequestResponse, err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("panic: %v", r) + } + }() + protoReader := textproto.NewReader(bufio.NewReader(strings.NewReader(raw))) + methodLine, err := protoReader.ReadLine() + if err != nil { + return nil, fmt.Errorf("failed to read method line: %s", err) + } + rr = &RequestResponse{ + Request: &HttpRequest{}, + } + /// must contain at least 3 parts + parts := strings.Split(methodLine, " ") + if len(parts) < 3 { + return nil, fmt.Errorf("invalid method line: %s", methodLine) + } + method := parts[0] + rr.Request.Method = method + + // parse relative url + urlx, err := urlutil.ParseRawRelativePath(parts[1], true) + if err != nil { + return nil, fmt.Errorf("failed to parse url: %s", err) + } + rr.URL = *urlx + + // parse host line + hostLine, err := protoReader.ReadLine() + if err != nil { + return nil, fmt.Errorf("failed to read host line: %s", err) + } + sep := strings.Index(hostLine, ":") + if sep <= 0 || sep >= len(hostLine)-1 { + return nil, fmt.Errorf("invalid host line: %s", hostLine) + } + hostLine = hostLine[sep+2:] + rr.URL.Host = hostLine + + // parse headers + rr.Request.Headers = mapsutil.NewOrderedMap[string, string]() + for { + line, err := protoReader.ReadLine() + if err != nil { + return nil, fmt.Errorf("failed to read header line: %s", err) + } + if line == "" { + // end of headers next is body + break + } + parts := strings.SplitN(line, ":", 2) + if len(parts) != 2 { + return nil, fmt.Errorf("invalid header line: %s", line) + } + rr.Request.Headers.Set(parts[0], parts[1][1:]) + } + + // parse body + rr.Request.Body = "" + var buff bytes.Buffer + _, err = buff.ReadFrom(protoReader.R) + if err != nil && err != io.EOF { + return nil, fmt.Errorf("failed to read body: %s", err) + } + if buff.Len() > 0 { + // yaml may include trailing newlines + // remove them if present + bin := buff.Bytes() + if bin[len(bin)-1] == '\n' { + bin = bin[:len(bin)-1] + } + if bin[len(bin)-1] == '\r' || bin[len(bin)-1] == '\n' { + bin = bin[:len(bin)-1] + } + rr.Request.Body = conversion.String(bin) + } + + // set raw request + rr.Request.Raw = raw + return rr, nil +} + +// ParseRawRequestWithURL parses a raw request from a string with given url +func ParseRawRequestWithURL(raw, url string) (rr *RequestResponse, err error) { + rr, err = ParseRawRequest(raw) + if err != nil { + return nil, err + } + urlx, err := urlutil.ParseAbsoluteURL(url, false) + if err != nil { + return nil, err + } + rr.URL = *urlx + return rr, nil +} diff --git a/pkg/input/types/http_test.go b/pkg/input/types/http_test.go new file mode 100644 index 00000000..d674c563 --- /dev/null +++ b/pkg/input/types/http_test.go @@ -0,0 +1,67 @@ +package types + +import ( + "io" + "net/http" + "net/http/httputil" + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +// Possibly add more tests here. +func TestParseHttpRequest(t *testing.T) { + tests := []struct { + name string + method string + url string + headerKey string + headerValue string + body string + contentLength string + }{ + {"GET Request", "GET", "example.com/", "X-Test", "test", "", "0"}, + {"POST Request with body", "POST", "example.com/resource", "Content-Type", "application/json", `{"key":"value"}`, "15"}, + {"PUT Request with body", "PUT", "example.com/update", "Content-Type", "text/plain", "update data", "11"}, + {"DELETE Request", "DELETE", "example.com/delete", "X-User", "user1", "", "0"}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var bodyReader io.Reader + if tc.body != "" { + bodyReader = strings.NewReader(tc.body) + } + req, err := http.NewRequest(tc.method, "http://"+tc.url, bodyReader) + if err != nil { + t.Fatal(err) + } + req.Header.Add(tc.headerKey, tc.headerValue) + if tc.contentLength != "" { + req.Header.Add("Content-Length", tc.contentLength) + } + binx, err := httputil.DumpRequestOut(req, true) + if err != nil { + t.Fatal(err) + } + rr, err := ParseRawRequest(string(binx)) + if err != nil { + t.Fatal(err) + } + if rr.Request.Method != tc.method { + t.Fatalf("invalid method: got %v want %v", rr.Request.Method, tc.method) + } + require.Equal(t, tc.url, rr.URL.String()) + val, _ := rr.Request.Headers.Get(tc.headerKey) + require.Equal(t, tc.headerValue, val) + if tc.body != "" { + require.Equal(t, tc.body, rr.Request.Body) + contentLengthVal, _ := rr.Request.Headers.Get("Content-Length") + require.Equal(t, tc.contentLength, contentLengthVal) + } + + t.Log(*rr.Request) + }) + } +} diff --git a/pkg/input/types/probe.go b/pkg/input/types/probe.go new file mode 100644 index 00000000..006faada --- /dev/null +++ b/pkg/input/types/probe.go @@ -0,0 +1,7 @@ +package types + +// InputLivenessProbe is an interface for probing the liveness of an input +type InputLivenessProbe interface { + // ProbeURL probes the scheme for a URL. first HTTPS is tried + ProbeURL(input string) (string, error) +} diff --git a/pkg/operators/operators.go b/pkg/operators/operators.go index 1d6103fe..41fafc83 100644 --- a/pkg/operators/operators.go +++ b/pkg/operators/operators.go @@ -287,7 +287,7 @@ func (operators *Operators) Execute(data map[string]interface{}, match MatchFunc } if isMatch, matched := match(data, matcher); isMatch { if isDebug { // matchers without an explicit name or with AND condition should only be made visible if debug is enabled - matcherName := getMatcherName(matcher, matcherIndex) + matcherName := GetMatcherName(matcher, matcherIndex) result.Matches[matcherName] = matched } else { // if it's a "named" matcher with OR condition, then display it if matcherCondition == matchers.ORCondition && matcher.Name != "" { @@ -331,7 +331,8 @@ func (operators *Operators) Execute(data map[string]interface{}, match MatchFunc return nil, false } -func getMatcherName(matcher *matchers.Matcher, matcherIndex int) string { +// GetMatcherName returns matchername of given matcher +func GetMatcherName(matcher *matchers.Matcher, matcherIndex int) string { if matcher.Name != "" { return matcher.Name } else { @@ -376,3 +377,20 @@ func getExtractedValue(values []string) any { return values } } + +// EvalBoolSlice evaluates a slice of bools using a logical AND +func EvalBoolSlice(slice []bool, isAnd bool) bool { + if len(slice) == 0 { + return false + } + + result := slice[0] + for _, b := range slice[1:] { + if isAnd { + result = result && b + } else { + result = result || b + } + } + return result +} diff --git a/pkg/parsers/parser.go b/pkg/parsers/parser.go index 16ebb379..25e01b63 100644 --- a/pkg/parsers/parser.go +++ b/pkg/parsers/parser.go @@ -146,6 +146,7 @@ const ( HeadlessFlagWarningStats = "headless-flag-missing-warnings" TemplatesExecutedStats = "templates-executed" CodeFlagWarningStats = "code-flag-missing-warnings" + FuzzFlagWarningStats = "fuzz-flag-missing-warnings" // Note: this is redefined in workflows.go to avoid circular dependency, so make sure to keep it in sync SkippedUnsignedStats = "skipped-unsigned-stats" // tracks loading of unsigned templates ) @@ -161,6 +162,7 @@ func init() { stats.NewEntry(HeadlessFlagWarningStats, "Excluded %d headless template[s] (disabled as default), use -headless option to run headless templates.") stats.NewEntry(CodeFlagWarningStats, "Excluded %d code template[s] (disabled as default), use -code option to run code templates.") stats.NewEntry(TemplatesExecutedStats, "Excluded %d template[s] with known weak matchers / tags excluded from default run using .nuclei-ignore") + stats.NewEntry(FuzzFlagWarningStats, "Excluded %d fuzz template[s] (disabled as default), use -fuzz option to run fuzz templates.") stats.NewEntry(SkippedUnsignedStats, "Skipping %d unsigned template[s]") } diff --git a/pkg/protocols/common/automaticscan/automaticscan.go b/pkg/protocols/common/automaticscan/automaticscan.go index 4a6f0491..81939471 100644 --- a/pkg/protocols/common/automaticscan/automaticscan.go +++ b/pkg/protocols/common/automaticscan/automaticscan.go @@ -15,7 +15,7 @@ import ( "github.com/projectdiscovery/nuclei/v3/pkg/catalog/config" "github.com/projectdiscovery/nuclei/v3/pkg/catalog/loader" "github.com/projectdiscovery/nuclei/v3/pkg/core" - "github.com/projectdiscovery/nuclei/v3/pkg/core/inputs" + "github.com/projectdiscovery/nuclei/v3/pkg/input/provider" "github.com/projectdiscovery/nuclei/v3/pkg/output" "github.com/projectdiscovery/nuclei/v3/pkg/protocols" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" @@ -45,7 +45,7 @@ type Options struct { ExecuterOpts protocols.ExecutorOptions Store *loader.Store Engine *core.Engine - Target core.InputProvider + Target provider.InputProvider } // Service is a service for automatic scan execution @@ -53,7 +53,7 @@ type Service struct { opts protocols.ExecutorOptions store *loader.Store engine *core.Engine - target core.InputProvider + target provider.InputProvider wappalyzer *wappalyzer.Wappalyze childExecuter *core.ChildExecuter httpclient *retryablehttp.Client @@ -129,7 +129,7 @@ func (s *Service) Execute() error { gologger.Info().Msgf("Executing Automatic scan on %d target[s]", s.target.Count()) // setup host concurrency sg := sizedwaitgroup.New(s.opts.Options.BulkSize) - s.target.Scan(func(value *contextargs.MetaInput) bool { + s.target.Iterate(func(value *contextargs.MetaInput) bool { sg.Add() go func(input *contextargs.MetaInput) { defer sg.Done() @@ -185,7 +185,8 @@ func (s *Service) executeAutomaticScanOnTarget(input *contextargs.MetaInput) { execOptions := s.opts.Copy() execOptions.Progress = &testutils.MockProgressClient{} // stats are not supported yet due to centralized logic and cannot be reinitialized eng.SetExecuterOptions(execOptions) - tmp := eng.ExecuteScanWithOpts(finalTemplates, &inputs.SimpleInputProvider{Inputs: []*contextargs.MetaInput{input}}, true) + + tmp := eng.ExecuteScanWithOpts(finalTemplates, provider.NewSimpleInputProviderWithUrls(input.Input), true) s.hasResults.Store(tmp.Load()) } diff --git a/pkg/protocols/common/contextargs/metainput.go b/pkg/protocols/common/contextargs/metainput.go index 4027bd7d..afda2fda 100644 --- a/pkg/protocols/common/contextargs/metainput.go +++ b/pkg/protocols/common/contextargs/metainput.go @@ -7,6 +7,8 @@ import ( "strings" jsoniter "github.com/json-iterator/go" + "github.com/projectdiscovery/nuclei/v3/pkg/input/types" + urlutil "github.com/projectdiscovery/utils/url" ) // MetaInput represents a target with metadata (TODO: replace with https://github.com/projectdiscovery/metainput) @@ -17,6 +19,9 @@ type MetaInput struct { CustomIP string `json:"customIP,omitempty"` // hash of the input hash string `json:"-"` + + // ReqResp is the raw request for the input + ReqResp *types.RequestResponse `json:"raw-request,omitempty"` } func (metaInput *MetaInput) marshalToBuffer() (bytes.Buffer, error) { @@ -25,11 +30,31 @@ func (metaInput *MetaInput) marshalToBuffer() (bytes.Buffer, error) { return b, err } +// Target returns the target of the metainput +func (metaInput *MetaInput) Target() string { + if metaInput.ReqResp != nil && metaInput.ReqResp.URL.URL != nil { + return metaInput.ReqResp.URL.String() + } + return metaInput.Input +} + +// URL returns request url +func (metaInput *MetaInput) URL() (*urlutil.URL, error) { + instance, err := urlutil.ParseAbsoluteURL(metaInput.Target(), false) + if err != nil { + return nil, err + } + return instance, nil +} + // ID returns a unique id/hash for metainput func (metaInput *MetaInput) ID() string { if metaInput.CustomIP != "" { return fmt.Sprintf("%s-%s", metaInput.Input, metaInput.CustomIP) } + if metaInput.ReqResp != nil { + return metaInput.ReqResp.ID() + } return metaInput.Input } @@ -58,16 +83,23 @@ func (metaInput *MetaInput) Unmarshal(data string) error { } func (metaInput *MetaInput) Clone() *MetaInput { - return &MetaInput{ + input := &MetaInput{ Input: metaInput.Input, CustomIP: metaInput.CustomIP, } + if metaInput.ReqResp != nil { + input.ReqResp = metaInput.ReqResp.Clone() + } + return input } func (metaInput *MetaInput) PrettyPrint() string { if metaInput.CustomIP != "" { return fmt.Sprintf("%s [%s]", metaInput.Input, metaInput.CustomIP) } + if metaInput.ReqResp != nil { + return fmt.Sprintf("%s [%s]", metaInput.ReqResp.URL.String(), metaInput.ReqResp.Request.Method) + } return metaInput.Input } @@ -77,7 +109,11 @@ func (metaInput *MetaInput) GetScanHash(templateId string) string { // but that totally changes the scanID/hash so to avoid that we compute hash only once // and reuse it for all subsequent calls if metaInput.hash == "" { - metaInput.hash = getMd5Hash(templateId + ":" + metaInput.Input + ":" + metaInput.CustomIP) + var rawRequest string + if metaInput.ReqResp != nil { + rawRequest = metaInput.ReqResp.ID() + } + metaInput.hash = getMd5Hash(templateId + ":" + metaInput.Input + ":" + metaInput.CustomIP + rawRequest) } return metaInput.hash } diff --git a/pkg/protocols/common/fuzz/execute.go b/pkg/protocols/common/fuzz/execute.go deleted file mode 100644 index 1a4700b0..00000000 --- a/pkg/protocols/common/fuzz/execute.go +++ /dev/null @@ -1,157 +0,0 @@ -package fuzz - -import ( - "regexp" - "strings" - - "github.com/pkg/errors" - "github.com/projectdiscovery/nuclei/v3/pkg/protocols" - "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" - "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators" - "github.com/projectdiscovery/retryablehttp-go" - errorutil "github.com/projectdiscovery/utils/errors" -) - -// ExecuteRuleInput is the input for rule Execute function -type ExecuteRuleInput struct { - // Input is the context args input - Input *contextargs.Context - // Callback is the callback for generated rule requests - Callback func(GeneratedRequest) bool - // InteractURLs contains interact urls for execute call - InteractURLs []string - // Values contains dynamic values for the rule - Values map[string]interface{} - // BaseRequest is the base http request for fuzzing rule - BaseRequest *retryablehttp.Request -} - -// GeneratedRequest is a single generated request for rule -type GeneratedRequest struct { - // Request is the http request for rule - Request *retryablehttp.Request - // InteractURLs is the list of interactsh urls - InteractURLs []string - // DynamicValues contains dynamic values map - DynamicValues map[string]interface{} -} - -// Execute executes a fuzzing rule accepting a callback on which -// generated requests are returned. -// -// Input is not thread safe and should not be shared between concurrent -// goroutines. -func (rule *Rule) Execute(input *ExecuteRuleInput) error { - if input.BaseRequest == nil { - return errorutil.NewWithTag("fuzz", "base request is nil for rule %v", rule) - } - if !rule.isExecutable(input.BaseRequest) { - return errorutil.NewWithTag("fuzz", "rule is not executable on %v", input.BaseRequest.URL.String()) - } - baseValues := input.Values - if rule.generator == nil { - evaluatedValues, interactURLs := rule.options.Variables.EvaluateWithInteractsh(baseValues, rule.options.Interactsh) - input.Values = generators.MergeMaps(evaluatedValues, baseValues, rule.options.Constants) - input.InteractURLs = interactURLs - err := rule.executeRuleValues(input) - return err - } - iterator := rule.generator.NewIterator() - for { - values, next := iterator.Value() - if !next { - return nil - } - evaluatedValues, interactURLs := rule.options.Variables.EvaluateWithInteractsh(generators.MergeMaps(values, baseValues), rule.options.Interactsh) - input.InteractURLs = interactURLs - input.Values = generators.MergeMaps(values, evaluatedValues, baseValues, rule.options.Constants) - - if err := rule.executeRuleValues(input); err != nil { - return err - } - } -} - -// isExecutable returns true if the rule can be executed based on provided input -func (rule *Rule) isExecutable(req *retryablehttp.Request) bool { - if !req.Query().IsEmpty() && rule.partType == queryPartType { - return true - } - if len(req.Header) > 0 && rule.partType == headersPartType { - return true - } - return false -} - -// executeRuleValues executes a rule with a set of values -func (rule *Rule) executeRuleValues(input *ExecuteRuleInput) error { - for _, payload := range rule.Fuzz { - if err := rule.executePartRule(input, payload); err != nil { - return err - } - } - return nil -} - -// Compile compiles a fuzzing rule and initializes it for operation -func (rule *Rule) Compile(generator *generators.PayloadGenerator, options *protocols.ExecutorOptions) error { - // If a payload generator is specified from base request, use it - // for payload values. - if generator != nil { - rule.generator = generator - } - rule.options = options - - // Resolve the default enums - if rule.Mode != "" { - if valueType, ok := stringToModeType[rule.Mode]; !ok { - return errors.Errorf("invalid mode value specified: %s", rule.Mode) - } else { - rule.modeType = valueType - } - } else { - rule.modeType = multipleModeType - } - if rule.Part != "" { - if valueType, ok := stringToPartType[rule.Part]; !ok { - return errors.Errorf("invalid part value specified: %s", rule.Part) - } else { - rule.partType = valueType - } - } else { - rule.partType = queryPartType - } - - if rule.Type != "" { - if valueType, ok := stringToRuleType[rule.Type]; !ok { - return errors.Errorf("invalid type value specified: %s", rule.Type) - } else { - rule.ruleType = valueType - } - } else { - rule.ruleType = replaceRuleType - } - - // Initialize other required regexes and maps - if len(rule.Keys) > 0 { - rule.keysMap = make(map[string]struct{}) - } - for _, key := range rule.Keys { - rule.keysMap[strings.ToLower(key)] = struct{}{} - } - for _, value := range rule.ValuesRegex { - compiled, err := regexp.Compile(value) - if err != nil { - return errors.Wrap(err, "could not compile value regex") - } - rule.valuesRegex = append(rule.valuesRegex, compiled) - } - for _, value := range rule.KeysRegex { - compiled, err := regexp.Compile(value) - if err != nil { - return errors.Wrap(err, "could not compile key regex") - } - rule.keysRegex = append(rule.keysRegex, compiled) - } - return nil -} diff --git a/pkg/protocols/common/fuzz/execute_test.go b/pkg/protocols/common/fuzz/execute_test.go deleted file mode 100644 index a922fbb7..00000000 --- a/pkg/protocols/common/fuzz/execute_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package fuzz - -import ( - "github.com/projectdiscovery/retryablehttp-go" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestRuleIsExecutable(t *testing.T) { - rule := &Rule{Part: "query"} - err := rule.Compile(nil, nil) - require.NoError(t, err, "could not compile rule") - - req, err := retryablehttp.NewRequest("GET", "https://example.com/?url=localhost", nil) - require.NoError(t, err, "could not build request") - - result := rule.isExecutable(req) - require.True(t, result, "could not get correct result") - - req, err = retryablehttp.NewRequest("GET", "https://example.com/", nil) - require.NoError(t, err, "could not build request") - - result = rule.isExecutable(req) - require.False(t, result, "could not get correct result") -} diff --git a/pkg/protocols/common/fuzz/parts.go b/pkg/protocols/common/fuzz/parts.go deleted file mode 100644 index 533de60a..00000000 --- a/pkg/protocols/common/fuzz/parts.go +++ /dev/null @@ -1,211 +0,0 @@ -package fuzz - -import ( - "context" - "io" - "net/http" - "strings" - - "github.com/pkg/errors" - "github.com/projectdiscovery/gologger" - "github.com/projectdiscovery/useragent" - - "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/expressions" - "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators" - "github.com/projectdiscovery/nuclei/v3/pkg/types" - "github.com/projectdiscovery/retryablehttp-go" - sliceutil "github.com/projectdiscovery/utils/slice" - urlutil "github.com/projectdiscovery/utils/url" -) - -// executePartRule executes part rules based on type -func (rule *Rule) executePartRule(input *ExecuteRuleInput, payload string) error { - switch rule.partType { - case queryPartType: - return rule.executeQueryPartRule(input, payload) - case headersPartType: - return rule.executeHeadersPartRule(input, payload) - } - return nil -} - -// executeHeadersPartRule executes headers part rules -func (rule *Rule) executeHeadersPartRule(input *ExecuteRuleInput, payload string) error { - // clone the request to avoid modifying the original - originalRequest := input.BaseRequest - req := originalRequest.Clone(context.TODO()) - // Also clone headers - headers := req.Header.Clone() - - for key, values := range originalRequest.Header { - cloned := sliceutil.Clone(values) - for i, value := range values { - if !rule.matchKeyOrValue(key, value) { - continue - } - var evaluated string - evaluated, input.InteractURLs = rule.executeEvaluate(input, key, value, payload, input.InteractURLs) - cloned[i] = evaluated - - if rule.modeType == singleModeType { - headers[key] = cloned - if err := rule.buildHeadersInput(input, headers, input.InteractURLs); err != nil && err != io.EOF { - gologger.Error().Msgf("Could not build request for headers part rule %v: %s\n", rule, err) - return err - } - cloned[i] = value // change back to previous value for headers - } - } - headers[key] = cloned - } - - if rule.modeType == multipleModeType { - if err := rule.buildHeadersInput(input, headers, input.InteractURLs); err != nil { - return err - } - } - return nil -} - -// executeQueryPartRule executes query part rules -func (rule *Rule) executeQueryPartRule(input *ExecuteRuleInput, payload string) error { - requestURL, err := urlutil.Parse(input.Input.MetaInput.Input) - if err != nil { - return err - } - origRequestURL := requestURL.Clone() - // clone the params to avoid modifying the original - temp := origRequestURL.Params.Clone() - - origRequestURL.Query().Iterate(func(key string, values []string) bool { - cloned := sliceutil.Clone(values) - for i, value := range values { - if !rule.matchKeyOrValue(key, value) { - continue - } - var evaluated string - evaluated, input.InteractURLs = rule.executeEvaluate(input, key, value, payload, input.InteractURLs) - cloned[i] = evaluated - - if rule.modeType == singleModeType { - temp.Update(key, cloned) - requestURL.Params = temp - if qerr := rule.buildQueryInput(input, requestURL, input.InteractURLs); qerr != nil { - err = qerr - return false - } - cloned[i] = value // change back to previous value for temp - } - } - temp.Update(key, cloned) - return true - }) - - if rule.modeType == multipleModeType { - requestURL.Params = temp - if err := rule.buildQueryInput(input, requestURL, input.InteractURLs); err != nil { - return err - } - } - - return err -} - -// buildHeadersInput returns created request for a Headers Input -func (rule *Rule) buildHeadersInput(input *ExecuteRuleInput, headers http.Header, interactURLs []string) error { - var req *retryablehttp.Request - if input.BaseRequest == nil { - return errors.New("Base request cannot be nil when fuzzing headers") - } else { - req = input.BaseRequest.Clone(context.TODO()) - req.Header = headers - // update host of request and not URL - // URL.Host is used to dial the connection - req.Request.Host = req.Header.Get("Host") - } - request := GeneratedRequest{ - Request: req, - InteractURLs: interactURLs, - DynamicValues: input.Values, - } - if !input.Callback(request) { - return io.EOF - } - return nil -} - -// buildQueryInput returns created request for a Query Input -func (rule *Rule) buildQueryInput(input *ExecuteRuleInput, parsed *urlutil.URL, interactURLs []string) error { - var req *retryablehttp.Request - var err error - if input.BaseRequest == nil { - req, err = retryablehttp.NewRequestFromURL(http.MethodGet, parsed, nil) - if err != nil { - return err - } - userAgent := useragent.PickRandom() - req.Header.Set("User-Agent", userAgent.Raw) - } else { - req = input.BaseRequest.Clone(context.TODO()) - req.SetURL(parsed) - } - request := GeneratedRequest{ - Request: req, - InteractURLs: interactURLs, - DynamicValues: input.Values, - } - if !input.Callback(request) { - return types.ErrNoMoreRequests - } - return nil -} - -// executeEvaluate executes evaluation of payload on a key and value and -// returns completed values to be replaced and processed -// for fuzzing. -func (rule *Rule) executeEvaluate(input *ExecuteRuleInput, key, value, payload string, interactshURLs []string) (string, []string) { - // TODO: Handle errors - values := generators.MergeMaps(input.Values, map[string]interface{}{ - "value": value, - }) - firstpass, _ := expressions.Evaluate(payload, values) - interactData, interactshURLs := rule.options.Interactsh.Replace(firstpass, interactshURLs) - evaluated, _ := expressions.Evaluate(interactData, values) - replaced := rule.executeReplaceRule(input, value, evaluated) - return replaced, interactshURLs -} - -// executeReplaceRule executes replacement for a key and value -func (rule *Rule) executeReplaceRule(input *ExecuteRuleInput, value, replacement string) string { - var builder strings.Builder - if rule.ruleType == prefixRuleType || rule.ruleType == postfixRuleType { - builder.Grow(len(value) + len(replacement)) - } - var returnValue string - - switch rule.ruleType { - case prefixRuleType: - builder.WriteString(replacement) - builder.WriteString(value) - returnValue = builder.String() - case postfixRuleType: - builder.WriteString(value) - builder.WriteString(replacement) - returnValue = builder.String() - case infixRuleType: - if len(value) <= 1 { - builder.WriteString(value) - builder.WriteString(replacement) - returnValue = builder.String() - } else { - middleIndex := len(value) / 2 - builder.WriteString(value[:middleIndex]) - builder.WriteString(replacement) - builder.WriteString(value[middleIndex:]) - returnValue = builder.String() - } - case replaceRuleType: - returnValue = replacement - } - return returnValue -} diff --git a/pkg/protocols/common/fuzz/parts_test.go b/pkg/protocols/common/fuzz/parts_test.go deleted file mode 100644 index 90b1b2ab..00000000 --- a/pkg/protocols/common/fuzz/parts_test.go +++ /dev/null @@ -1,144 +0,0 @@ -package fuzz - -import ( - "github.com/projectdiscovery/retryablehttp-go" - "net/http" - "testing" - - "github.com/projectdiscovery/nuclei/v3/pkg/protocols" - "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" - "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/interactsh" - "github.com/stretchr/testify/require" -) - -func TestExecuteHeadersPartRule(t *testing.T) { - options := &protocols.ExecutorOptions{ - Interactsh: &interactsh.Client{}, - } - req, err := retryablehttp.NewRequest("GET", "http://localhost:8080/", nil) - require.NoError(t, err, "can't build request") - - req.Header.Set("X-Custom-Foo", "foo") - req.Header.Set("X-Custom-Bar", "bar") - - t.Run("single", func(t *testing.T) { - rule := &Rule{ - ruleType: postfixRuleType, - partType: headersPartType, - modeType: singleModeType, - options: options, - } - var generatedHeaders []http.Header - err := rule.executeHeadersPartRule(&ExecuteRuleInput{ - Input: contextargs.New(), - BaseRequest: req, - Callback: func(gr GeneratedRequest) bool { - generatedHeaders = append(generatedHeaders, gr.Request.Header.Clone()) - return true - }, - }, "1337'") - require.NoError(t, err, "could not execute part rule") - require.ElementsMatch(t, []http.Header{ - { - "X-Custom-Foo": {"foo1337'"}, - "X-Custom-Bar": {"bar"}, - }, - { - "X-Custom-Foo": {"foo"}, - "X-Custom-Bar": {"bar1337'"}, - }, - }, generatedHeaders, "could not get generated headers") - }) - - t.Run("multiple", func(t *testing.T) { - rule := &Rule{ - ruleType: postfixRuleType, - partType: headersPartType, - modeType: multipleModeType, - options: options, - } - var generatedHeaders http.Header - err := rule.executeHeadersPartRule(&ExecuteRuleInput{ - Input: contextargs.New(), - BaseRequest: req, - Callback: func(gr GeneratedRequest) bool { - generatedHeaders = gr.Request.Header.Clone() - return true - }, - }, "1337'") - require.NoError(t, err, "could not execute part rule") - require.Equal(t, http.Header{ - "X-Custom-Foo": {"foo1337'"}, - "X-Custom-Bar": {"bar1337'"}, - }, generatedHeaders, "could not get generated headers") - }) -} -func TestExecuteQueryPartRule(t *testing.T) { - URL := "http://localhost:8080/?url=localhost&mode=multiple&file=passwdfile" - options := &protocols.ExecutorOptions{ - Interactsh: &interactsh.Client{}, - } - t.Run("single", func(t *testing.T) { - rule := &Rule{ - ruleType: postfixRuleType, - partType: queryPartType, - modeType: singleModeType, - options: options, - } - var generatedURL []string - input := contextargs.NewWithInput(URL) - err := rule.executeQueryPartRule(&ExecuteRuleInput{ - Input: input, - Callback: func(gr GeneratedRequest) bool { - generatedURL = append(generatedURL, gr.Request.URL.String()) - return true - }, - }, "1337'") - require.NoError(t, err, "could not execute part rule") - require.ElementsMatch(t, []string{ - "http://localhost:8080/?url=localhost1337'&mode=multiple&file=passwdfile", - "http://localhost:8080/?url=localhost&mode=multiple1337'&file=passwdfile", - "http://localhost:8080/?url=localhost&mode=multiple&file=passwdfile1337'", - }, generatedURL, "could not get generated url") - }) - t.Run("multiple", func(t *testing.T) { - rule := &Rule{ - ruleType: postfixRuleType, - partType: queryPartType, - modeType: multipleModeType, - options: options, - } - var generatedURL string - input := contextargs.NewWithInput(URL) - err := rule.executeQueryPartRule(&ExecuteRuleInput{ - Input: input, - Callback: func(gr GeneratedRequest) bool { - generatedURL = gr.Request.URL.String() - return true - }, - }, "1337'") - require.NoError(t, err, "could not execute part rule") - require.Equal(t, "http://localhost:8080/?url=localhost1337'&mode=multiple1337'&file=passwdfile1337'", generatedURL, "could not get generated url") - }) -} - -func TestExecuteReplaceRule(t *testing.T) { - tests := []struct { - ruleType ruleType - value string - replacement string - expected string - }{ - {replaceRuleType, "test", "replacement", "replacement"}, - {prefixRuleType, "test", "prefix", "prefixtest"}, - {postfixRuleType, "test", "postfix", "testpostfix"}, - {infixRuleType, "", "infix", "infix"}, - {infixRuleType, "0", "infix", "0infix"}, - {infixRuleType, "test", "infix", "teinfixst"}, - } - for _, test := range tests { - rule := &Rule{ruleType: test.ruleType} - returned := rule.executeReplaceRule(nil, test.value, test.replacement) - require.Equal(t, test.expected, returned, "could not get correct value") - } -} diff --git a/pkg/protocols/headless/headless.go b/pkg/protocols/headless/headless.go index 81cb3ca4..373880f1 100644 --- a/pkg/protocols/headless/headless.go +++ b/pkg/protocols/headless/headless.go @@ -3,10 +3,10 @@ package headless import ( "github.com/pkg/errors" + "github.com/projectdiscovery/nuclei/v3/pkg/fuzz" useragent "github.com/projectdiscovery/nuclei/v3/pkg/model/types/userAgent" "github.com/projectdiscovery/nuclei/v3/pkg/operators" "github.com/projectdiscovery/nuclei/v3/pkg/protocols" - "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/fuzz" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/headless/engine" uagent "github.com/projectdiscovery/useragent" diff --git a/pkg/protocols/headless/request.go b/pkg/protocols/headless/request.go index f800c261..f45afdd3 100644 --- a/pkg/protocols/headless/request.go +++ b/pkg/protocols/headless/request.go @@ -12,10 +12,10 @@ import ( "golang.org/x/exp/maps" "github.com/projectdiscovery/gologger" + "github.com/projectdiscovery/nuclei/v3/pkg/fuzz" "github.com/projectdiscovery/nuclei/v3/pkg/output" "github.com/projectdiscovery/nuclei/v3/pkg/protocols" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" - "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/fuzz" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/helpers/eventcreator" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/helpers/responsehighlighter" diff --git a/pkg/protocols/http/build_request.go b/pkg/protocols/http/build_request.go index d8ebb397..7408925a 100644 --- a/pkg/protocols/http/build_request.go +++ b/pkg/protocols/http/build_request.go @@ -13,6 +13,7 @@ import ( "github.com/projectdiscovery/useragent" "github.com/projectdiscovery/gologger" + "github.com/projectdiscovery/nuclei/v3/pkg/authprovider" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/expressions" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators" @@ -49,6 +50,32 @@ type generatedRequest struct { customCancelFunction context.CancelFunc } +// ApplyAuth applies the auth provider to the generated request +func (g *generatedRequest) ApplyAuth(provider authprovider.AuthProvider) { + if provider == nil { + return + } + if g.request != nil { + auth := provider.LookupURLX(g.request.URL) + if auth != nil { + auth.ApplyOnRR(g.request) + } + } + if g.rawRequest != nil { + parsed, err := urlutil.ParseAbsoluteURL(g.rawRequest.FullURL, true) + if err != nil { + gologger.Warning().Msgf("[authprovider] Could not parse URL %s: %s\n", g.rawRequest.FullURL, err) + return + } + auth := provider.LookupURLX(parsed) + if auth != nil { + // here we need to apply it custom because we don't have a standard/official + // rawhttp request format ( which we probably should have ) + g.rawRequest.ApplyAuthStrategy(auth) + } + } +} + func (g *generatedRequest) URL() string { if g.request != nil { return g.request.URL.String() diff --git a/pkg/protocols/http/http.go b/pkg/protocols/http/http.go index 4796f339..ede8291e 100644 --- a/pkg/protocols/http/http.go +++ b/pkg/protocols/http/http.go @@ -8,10 +8,11 @@ import ( json "github.com/json-iterator/go" "github.com/pkg/errors" + "github.com/projectdiscovery/nuclei/v3/pkg/fuzz" "github.com/projectdiscovery/nuclei/v3/pkg/operators" + "github.com/projectdiscovery/nuclei/v3/pkg/operators/matchers" "github.com/projectdiscovery/nuclei/v3/pkg/protocols" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/expressions" - "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/fuzz" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/http/httpclientpool" httputil "github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils/http" @@ -208,6 +209,14 @@ type Request struct { // description: | // DisablePathAutomerge disables merging target url path with raw request path DisablePathAutomerge bool `yaml:"disable-path-automerge,omitempty" json:"disable-path-automerge,omitempty" jsonschema:"title=disable auto merging of path,description=Disable merging target url path with raw request path"` + // description: | + // Filter is matcher-like field to check if fuzzing should be performed on this request or not + FuzzingFilter []*matchers.Matcher `yaml:"filters,omitempty" json:"filter,omitempty" jsonschema:"title=filter for fuzzing,description=Filter is matcher-like field to check if fuzzing should be performed on this request or not"` + // description: | + // Filter condition is the condition to apply on the filter (AND/OR). Default is OR + FuzzingFilterCondition string `yaml:"filters-condition,omitempty" json:"filter-condition,omitempty" jsonschema:"title=condition between the filters,description=Conditions between the filters,enum=and,enum=or"` + // cached variables that may be used along with request. + fuzzingFilterCondition matchers.ConditionType `yaml:"-" json:"-"` } // Options returns executer options for http request @@ -314,6 +323,20 @@ func (request *Request) Compile(options *protocols.ExecutorOptions) error { request.CompiledOperators = compiled } + // === fuzzing filters ===== // + + if request.FuzzingFilterCondition != "" { + request.fuzzingFilterCondition = matchers.ConditionTypes[request.FuzzingFilterCondition] + } else { + request.fuzzingFilterCondition = matchers.ORCondition + } + + for _, filter := range request.FuzzingFilter { + if err := filter.CompileMatchers(); err != nil { + return errors.Wrap(err, "could not compile matcher") + } + } + // Resolve payload paths from vars if they exists for name, payload := range request.options.Options.Vars.AsMap() { payloadStr, ok := payload.(string) diff --git a/pkg/protocols/http/raw/raw.go b/pkg/protocols/http/raw/raw.go index d4004683..a1eb544a 100644 --- a/pkg/protocols/http/raw/raw.go +++ b/pkg/protocols/http/raw/raw.go @@ -3,11 +3,14 @@ package raw import ( "bufio" "bytes" + "encoding/base64" "errors" "fmt" "io" "strings" + "github.com/projectdiscovery/gologger" + "github.com/projectdiscovery/nuclei/v3/pkg/authprovider/authx" "github.com/projectdiscovery/rawhttp/client" errorutil "github.com/projectdiscovery/utils/errors" stringsutil "github.com/projectdiscovery/utils/strings" @@ -267,3 +270,44 @@ func (r *Request) TryFillCustomHeaders(headers []string) error { return errors.New("no host header found") } + +// ApplyAuthStrategy applies the auth strategy to the request +func (r *Request) ApplyAuthStrategy(strategy authx.AuthStrategy) { + if strategy == nil { + return + } + switch s := strategy.(type) { + case *authx.QueryAuthStrategy: + parsed, err := urlutil.Parse(r.FullURL) + if err != nil { + gologger.Error().Msgf("auth strategy failed to parse url: %s got %v", r.FullURL, err) + return + } + _ = parsed + for _, p := range s.Data.Params { + parsed.Params.Add(p.Key, p.Value) + } + case *authx.CookiesAuthStrategy: + var buff bytes.Buffer + for _, cookie := range s.Data.Cookies { + buff.WriteString(fmt.Sprintf("%s=%s; ", cookie.Key, cookie.Value)) + } + if buff.Len() > 0 { + if val, ok := r.Headers["Cookie"]; ok { + r.Headers["Cookie"] = strings.TrimSuffix(strings.TrimSpace(val), ";") + "; " + buff.String() + } else { + r.Headers["Cookie"] = buff.String() + } + } + case *authx.HeadersAuthStrategy: + for _, header := range s.Data.Headers { + r.Headers[header.Key] = header.Value + } + case *authx.BearerTokenAuthStrategy: + r.Headers["Authorization"] = "Bearer " + s.Data.Token + case *authx.BasicAuthStrategy: + r.Headers["Authorization"] = "Basic " + base64.StdEncoding.EncodeToString([]byte(s.Data.Username+":"+s.Data.Password)) + default: + gologger.Warning().Msgf("[raw-request] unknown auth strategy: %T", s) + } +} diff --git a/pkg/protocols/http/request.go b/pkg/protocols/http/request.go index 09b6c43f..81df15b2 100644 --- a/pkg/protocols/http/request.go +++ b/pkg/protocols/http/request.go @@ -24,7 +24,6 @@ import ( "github.com/projectdiscovery/nuclei/v3/pkg/protocols" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/expressions" - "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/fuzz" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/helpers/eventcreator" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/helpers/responsehighlighter" @@ -341,98 +340,6 @@ func (request *Request) executeTurboHTTP(input *contextargs.Context, dynamicValu return multierr.Combine(spmHandler.CombinedResults()...) } -// executeFuzzingRule executes fuzzing request for a URL -func (request *Request) executeFuzzingRule(input *contextargs.Context, previous output.InternalEvent, callback protocols.OutputEventCallback) error { - // If request is self-contained we don't need to parse any input. - if !request.SelfContained { - // If it's not self-contained we parse user provided input - if _, err := urlutil.Parse(input.MetaInput.Input); err != nil { - return errors.Wrap(err, "could not parse url") - } - } - fuzzRequestCallback := func(gr fuzz.GeneratedRequest) bool { - hasInteractMatchers := interactsh.HasMatchers(request.CompiledOperators) - hasInteractMarkers := len(gr.InteractURLs) > 0 - if request.options.HostErrorsCache != nil && request.options.HostErrorsCache.Check(input.MetaInput.Input) { - return false - } - request.options.RateLimiter.Take() - req := &generatedRequest{ - request: gr.Request, - dynamicValues: gr.DynamicValues, - interactshURLs: gr.InteractURLs, - original: request, - } - var gotMatches bool - requestErr := request.executeRequest(input, req, gr.DynamicValues, hasInteractMatchers, func(event *output.InternalWrappedEvent) { - if hasInteractMarkers && hasInteractMatchers && request.options.Interactsh != nil { - requestData := &interactsh.RequestData{ - MakeResultFunc: request.MakeResultEvent, - Event: event, - Operators: request.CompiledOperators, - MatchFunc: request.Match, - ExtractFunc: request.Extract, - } - request.options.Interactsh.RequestEvent(gr.InteractURLs, requestData) - gotMatches = request.options.Interactsh.AlreadyMatched(requestData) - } else { - callback(event) - } - // Add the extracts to the dynamic values if any. - if event.OperatorsResult != nil { - gotMatches = event.OperatorsResult.Matched - } - }, 0) - // If a variable is unresolved, skip all further requests - if errors.Is(requestErr, errStopExecution) { - return false - } - if requestErr != nil { - if request.options.HostErrorsCache != nil { - request.options.HostErrorsCache.MarkFailed(input.MetaInput.Input, requestErr) - } - gologger.Verbose().Msgf("[%s] Error occurred in request: %s\n", request.options.TemplateID, requestErr) - } - request.options.Progress.IncrementRequests() - - // If this was a match, and we want to stop at first match, skip all further requests. - shouldStopAtFirstMatch := request.options.Options.StopAtFirstMatch || request.StopAtFirstMatch - if shouldStopAtFirstMatch && gotMatches { - return false - } - return true - } - - // Iterate through all requests for template and queue them for fuzzing - generator := request.newGenerator(true) - for { - value, payloads, result := generator.nextValue() - if !result { - break - } - generated, err := generator.Make(context.Background(), input, value, payloads, nil) - if err != nil { - continue - } - input.MetaInput = &contextargs.MetaInput{Input: generated.URL()} - for _, rule := range request.Fuzzing { - err = rule.Execute(&fuzz.ExecuteRuleInput{ - Input: input, - Callback: fuzzRequestCallback, - Values: generated.dynamicValues, - BaseRequest: generated.request, - }) - if err == types.ErrNoMoreRequests { - return nil - } - if err != nil { - return errors.Wrap(err, "could not execute rule") - } - } - } - return nil -} - // ExecuteWithResults executes the final request on a URL func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicValues, previous output.InternalEvent, callback protocols.OutputEventCallback) error { if request.Pipeline || request.Race && request.RaceNumberRequests > 0 || request.Threads > 0 { @@ -655,6 +562,12 @@ func (request *Request) executeRequest(input *contextargs.Context, generatedRequ } } } + + // === apply auth strategies === + if generatedRequest.request != nil { + generatedRequest.ApplyAuth(request.options.AuthProvider) + } + var formedURL string var hostname string timeStart := time.Now() diff --git a/pkg/protocols/http/request_fuzz.go b/pkg/protocols/http/request_fuzz.go new file mode 100644 index 00000000..b9f22131 --- /dev/null +++ b/pkg/protocols/http/request_fuzz.go @@ -0,0 +1,305 @@ +package http + +// === Fuzzing Documentation (Scoped to this File) ===== +// -> request.executeFuzzingRule [iterates over payloads(+requests) and executes] +// -> request.executePayloadUsingRules [executes single payload on all rules (if more than 1)] +// -> request.executeGeneratedFuzzingRequest [execute final generated fuzzing request and get result] + +import ( + "context" + "fmt" + "strings" + + "github.com/pkg/errors" + "github.com/projectdiscovery/gologger" + "github.com/projectdiscovery/nuclei/v3/pkg/fuzz" + "github.com/projectdiscovery/nuclei/v3/pkg/operators" + "github.com/projectdiscovery/nuclei/v3/pkg/operators/matchers" + "github.com/projectdiscovery/nuclei/v3/pkg/output" + "github.com/projectdiscovery/nuclei/v3/pkg/protocols" + "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" + "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/interactsh" + "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/utils/vardump" + protocolutils "github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils" + "github.com/projectdiscovery/nuclei/v3/pkg/types" + "github.com/projectdiscovery/retryablehttp-go" +) + +// executeFuzzingRule executes fuzzing request for a URL +// TODO: +// 1. use SPMHandler and rewrite stop at first match logic here +// 2. use scanContext instead of contextargs.Context +func (request *Request) executeFuzzingRule(input *contextargs.Context, _ output.InternalEvent, callback protocols.OutputEventCallback) error { + // methdology: + // to check applicablity of rule, we first try to execute it with one value + // if it is applicable, we execute all requests + // if it is not applicable, we log and fail silently + + // check if target should be fuzzed or not + if !request.ShouldFuzzTarget(input) { + urlx, _ := input.MetaInput.URL() + if urlx != nil { + gologger.Verbose().Msgf("[%s] fuzz: target(%s) not applicable for fuzzing\n", request.options.TemplateID, urlx.String()) + } else { + gologger.Verbose().Msgf("[%s] fuzz: target(%s) not applicable for fuzzing\n", request.options.TemplateID, input.MetaInput.Input) + } + return nil + } + + // Iterate through all requests for template and queue them for fuzzing + generator := request.newGenerator(true) + + // this will generate next value along with request it is meant to be used with + currRequest, payloads, result := generator.nextValue() + if !result && input.MetaInput.ReqResp == nil { + // this case is only true if input is not a full http request + return fmt.Errorf("no values to generate requests") + } + + // if it is a full http request obtained from target file + if input.MetaInput.ReqResp != nil { + // Note: in case of full http request, we only need to build it once + // and then reuse it for all requests and completely abandon the request + // returned by generator + _ = currRequest + generated, err := input.MetaInput.ReqResp.BuildRequest() + if err != nil { + return errors.Wrap(err, "fuzz: could not build request obtained from target file") + } + input.MetaInput.Input = generated.URL.String() + // execute with one value first to checks its applicability + err = request.executePayloadUsingRules(input, payloads, generated, callback) + if err != nil { + // in case of any error, return it + if fuzz.IsErrRuleNotApplicable(err) { + // log and fail silently + gologger.Verbose().Msgf("[%s] fuzz: %s\n", request.options.TemplateID, err) + return nil + } + if errors.Is(err, errStopExecution) { + return err + } + gologger.Verbose().Msgf("[%s] fuzz: inital payload request execution failed : %s\n", request.options.TemplateID, err) + } + + // if it is applicable, execute all requests + for { + _, payloads, result := generator.nextValue() + if !result { + break + } + err = request.executePayloadUsingRules(input, payloads, generated, callback) + if err != nil { + // continue to next request since this is payload specific + gologger.Verbose().Msgf("[%s] fuzz: payload request execution failed : %s\n", request.options.TemplateID, err) + continue + } + } + return nil + } + + // ==== fuzzing when only URL is provided ===== + + generated, err := generator.Make(context.Background(), input, currRequest, payloads, nil) + if err != nil { + return errors.Wrap(err, "fuzz: could not build request from url") + } + // we need to use this url instead of input + inputx := input.Clone() + inputx.MetaInput.Input = generated.request.URL.String() + // execute with one value first to checks its applicability + err = request.executePayloadUsingRules(inputx, generated.dynamicValues, generated.request, callback) + if err != nil { + // in case of any error, return it + if fuzz.IsErrRuleNotApplicable(err) { + // log and fail silently + gologger.Verbose().Msgf("[%s] fuzz: rule not applicable : %s\n", request.options.TemplateID, err) + return nil + } + if errors.Is(err, errStopExecution) { + return err + } + gologger.Verbose().Msgf("[%s] fuzz: inital payload request execution failed : %s\n", request.options.TemplateID, err) + } + + // continue to next request since this is payload specific + for { + currRequest, payloads, result = generator.nextValue() + if !result { + break + } + generated, err := generator.Make(context.Background(), input, currRequest, payloads, nil) + if err != nil { + return errors.Wrap(err, "fuzz: could not build request from url") + } + // we need to use this url instead of input + inputx := input.Clone() + inputx.MetaInput.Input = generated.request.URL.String() + // execute with one value first to checks its applicability + err = request.executePayloadUsingRules(inputx, generated.dynamicValues, generated.request, callback) + if err != nil { + gologger.Verbose().Msgf("[%s] fuzz: payload request execution failed : %s\n", request.options.TemplateID, err) + continue + } + } + return nil +} + +// executePayloadUsingRules executes a payload using rules with given payload i.e values +func (request *Request) executePayloadUsingRules(input *contextargs.Context, values map[string]interface{}, baseRequest *retryablehttp.Request, callback protocols.OutputEventCallback) error { + applicable := false + for _, rule := range request.Fuzzing { + err := rule.Execute(&fuzz.ExecuteRuleInput{ + Input: input, + Callback: func(gr fuzz.GeneratedRequest) bool { + // TODO: replace this after scanContext Refactor + return request.executeGeneratedFuzzingRequest(gr, input, callback) + }, + Values: values, + BaseRequest: baseRequest, + }) + if err == nil { + applicable = true + continue + } + if fuzz.IsErrRuleNotApplicable(err) { + continue + } + if err == types.ErrNoMoreRequests { + return nil + } + return errors.Wrap(err, "could not execute rule") + } + + if !applicable { + return fuzz.ErrRuleNotApplicable.Msgf(fmt.Sprintf("no rule was applicable for this request: %v", input.MetaInput.Input)) + } + return nil +} + +// executeGeneratedFuzzingRequest executes a generated fuzzing request after building it using rules and payloads +func (request *Request) executeGeneratedFuzzingRequest(gr fuzz.GeneratedRequest, input *contextargs.Context, callback protocols.OutputEventCallback) bool { + hasInteractMatchers := interactsh.HasMatchers(request.CompiledOperators) + hasInteractMarkers := len(gr.InteractURLs) > 0 + if request.options.HostErrorsCache != nil && request.options.HostErrorsCache.Check(input.MetaInput.Input) { + return false + } + request.options.RateLimiter.Take() + req := &generatedRequest{ + request: gr.Request, + dynamicValues: gr.DynamicValues, + interactshURLs: gr.InteractURLs, + original: request, + } + var gotMatches bool + requestErr := request.executeRequest(input, req, gr.DynamicValues, hasInteractMatchers, func(event *output.InternalWrappedEvent) { + if hasInteractMarkers && hasInteractMatchers && request.options.Interactsh != nil { + requestData := &interactsh.RequestData{ + MakeResultFunc: request.MakeResultEvent, + Event: event, + Operators: request.CompiledOperators, + MatchFunc: request.Match, + ExtractFunc: request.Extract, + } + request.options.Interactsh.RequestEvent(gr.InteractURLs, requestData) + gotMatches = request.options.Interactsh.AlreadyMatched(requestData) + } else { + callback(event) + } + // Add the extracts to the dynamic values if any. + if event.OperatorsResult != nil { + gotMatches = event.OperatorsResult.Matched + } + }, 0) + // If a variable is unresolved, skip all further requests + if errors.Is(requestErr, errStopExecution) { + return false + } + if requestErr != nil { + if request.options.HostErrorsCache != nil { + request.options.HostErrorsCache.MarkFailed(input.MetaInput.Input, requestErr) + } + gologger.Verbose().Msgf("[%s] Error occurred in request: %s\n", request.options.TemplateID, requestErr) + } + request.options.Progress.IncrementRequests() + + // If this was a match, and we want to stop at first match, skip all further requests. + shouldStopAtFirstMatch := request.options.Options.StopAtFirstMatch || request.StopAtFirstMatch + if shouldStopAtFirstMatch && gotMatches { + return false + } + return true +} + +// ShouldFuzzTarget checks if given target should be fuzzed or not using `filter` field in template +func (request *Request) ShouldFuzzTarget(input *contextargs.Context) bool { + if len(request.FuzzingFilter) == 0 { + return true + } + status := []bool{} + for index, filter := range request.FuzzingFilter { + isMatch, _ := request.Match(request.filterDataMap(input), filter) + status = append(status, isMatch) + if request.options.Options.MatcherStatus { + gologger.Debug().Msgf("[%s] [%s] Filter => %s : %v", input.MetaInput.Target(), request.options.TemplateID, operators.GetMatcherName(filter, index), isMatch) + } + } + if len(status) == 0 { + return true + } + var matched bool + if request.fuzzingFilterCondition == matchers.ANDCondition { + matched = operators.EvalBoolSlice(status, true) + } else { + matched = operators.EvalBoolSlice(status, false) + } + if request.options.Options.MatcherStatus { + gologger.Debug().Msgf("[%s] [%s] Final Filter Status => %v", input.MetaInput.Target(), request.options.TemplateID, matched) + } + return matched +} + +// input data map returns map[string]interface{} from input +func (request *Request) filterDataMap(input *contextargs.Context) map[string]interface{} { + m := make(map[string]interface{}) + parsed, err := input.MetaInput.URL() + if err != nil { + m["host"] = input.MetaInput.Input + return m + } + m = protocolutils.GenerateVariables(parsed, true, m) + for k, v := range m { + m[strings.ToLower(k)] = v + } + m["path"] = parsed.Path // override existing + m["query"] = parsed.RawQuery + // add request data like headers, body etc + if input.MetaInput.ReqResp != nil && input.MetaInput.ReqResp.Request != nil { + req := input.MetaInput.ReqResp.Request + m["method"] = req.Method + m["body"] = req.Body + + sb := &strings.Builder{} + req.Headers.Iterate(func(k, v string) bool { + k = strings.ToLower(strings.ReplaceAll(strings.TrimSpace(k), "-", "_")) + if strings.EqualFold(k, "Cookie") { + m["cookie"] = v + } + if strings.EqualFold(k, "User_Agent") { + m["user_agent"] = v + } + if strings.EqualFold(k, "content_type") { + m["content_type"] = v + } + sb.WriteString(fmt.Sprintf("%s: %s\n", k, v)) + return true + }) + m["header"] = sb.String() + } + + // dump if svd is enabled + if request.options.Options.ShowVarDump { + gologger.Debug().Msgf("Fuzz Filter Variables: \n%s\n", vardump.DumpVariables(m)) + } + return m +} diff --git a/pkg/protocols/protocols.go b/pkg/protocols/protocols.go index c8323dcb..4faa9610 100644 --- a/pkg/protocols/protocols.go +++ b/pkg/protocols/protocols.go @@ -10,6 +10,7 @@ import ( "github.com/logrusorgru/aurora" + "github.com/projectdiscovery/nuclei/v3/pkg/authprovider" "github.com/projectdiscovery/nuclei/v3/pkg/catalog" "github.com/projectdiscovery/nuclei/v3/pkg/input" "github.com/projectdiscovery/nuclei/v3/pkg/js/compiler" @@ -116,6 +117,8 @@ type ExecutorOptions struct { // based on given logic. by default nuclei reverts to using value of `-c` when threads count // is not specified or is 0 in template OverrideThreadsCount PayloadThreadSetterCallback + // AuthProvider is a provider for auth strategies + AuthProvider authprovider.AuthProvider //TemporaryDirectory is the directory to store temporary files TemporaryDirectory string } diff --git a/pkg/templates/templates.go b/pkg/templates/templates.go index 1bd5a683..bd81d145 100644 --- a/pkg/templates/templates.go +++ b/pkg/templates/templates.go @@ -191,6 +191,29 @@ func (template *Template) Type() types.ProtocolType { } } +// IsFuzzing returns true if the template is a fuzzing template +func (template *Template) IsFuzzing() bool { + if len(template.RequestsHTTP) == 0 && len(template.RequestsHeadless) == 0 { + // fuzzing is only supported for http and headless protocols + return false + } + if len(template.RequestsHTTP) > 0 { + for _, request := range template.RequestsHTTP { + if len(request.Fuzzing) > 0 { + return true + } + } + } + if len(template.RequestsHeadless) > 0 { + for _, request := range template.RequestsHeadless { + if len(request.Fuzzing) > 0 { + return true + } + } + } + return false +} + // HasCodeProtocol returns true if the template has a code protocol section func (template *Template) HasCodeProtocol() bool { return len(template.RequestsCode) > 0 diff --git a/pkg/testutils/fuzzplayground/db.go b/pkg/testutils/fuzzplayground/db.go new file mode 100644 index 00000000..8344f5dc --- /dev/null +++ b/pkg/testutils/fuzzplayground/db.go @@ -0,0 +1,150 @@ +package fuzzplayground + +import ( + "database/sql" + "encoding/xml" + "fmt" + "os" + "strconv" + "strings" + + _ "github.com/mattn/go-sqlite3" +) + +var ( + db *sql.DB + tempDBDir string +) + +func init() { + dir, err := os.MkdirTemp("", "fuzzplayground-*") + if err != nil { + panic(err) + } + tempDBDir = dir + + db, err = sql.Open("sqlite3", fmt.Sprintf("file:%v/test.db?cache=shared&mode=memory", tempDBDir)) + if err != nil { + panic(err) + } + addDummyUsers(db) + addDummyPosts(db) +} + +// Cleanup cleans up the temporary database directory +func Cleanup() { + if db != nil { + _ = db.Close() + } + if tempDBDir != "" { + _ = os.RemoveAll(tempDBDir) + } +} + +type User struct { + XMLName xml.Name `xml:"user"` + ID int `xml:"id"` + Name string `xml:"name"` + Age int `xml:"age"` + Role string `xml:"role"` +} + +func addDummyUsers(db *sql.DB) { + _, err := db.Exec("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER, role TEXT)") + if err != nil { + panic(err) + } + _, err = db.Exec("INSERT INTO users (id , name, age, role) VALUES (1,'admin', 30, 'admin')") + if err != nil { + panic(err) + } + _, err = db.Exec("INSERT INTO users (id , name, age, role) VALUES (75,'user', 30, 'user')") + if err != nil { + panic(err) + } +} + +func patchUnsanitizedUser(db *sql.DB, user User) error { + setClauses := "" + + if user.Name != "" { + setClauses += "name = '" + user.Name + "', " + } + if user.Age > 0 { + setClauses += "age = " + strconv.Itoa(user.Age) + ", " + } + if user.Role != "" { + setClauses += "role = '" + user.Role + "', " + } + if setClauses == "" { + // No fields to update + return nil + } + setClauses = strings.TrimSuffix(setClauses, ", ") + + query := "UPDATE users SET " + setClauses + " WHERE id = ?" + _, err := db.Exec(query, user.ID) + if err != nil { + return err + } + return nil +} + +func getUnsanitizedUser(db *sql.DB, id string) (User, error) { + var user User + err := db.QueryRow("SELECT id, name, age, role FROM users WHERE id = "+id).Scan(&user.ID, &user.Name, &user.Age, &user.Role) + if err != nil { + return user, err + } + return user, nil +} + +type Posts struct { + ID int + Title string + Content string + Lang string +} + +func addDummyPosts(db *sql.DB) { + _, err := db.Exec("CREATE TABLE IF NOT EXISTS posts (id INTEGER PRIMARY KEY, title TEXT, content TEXT, lang TEXT)") + if err != nil { + panic(err) + } + // Inserting English dummy posts + _, err = db.Exec("INSERT INTO posts (id, title, content, lang) VALUES (1, 'The Joy of Programming', 'Programming is like painting a canvas with logic.', 'en')") + if err != nil { + panic(err) + } + _, err = db.Exec("INSERT INTO posts (id, title, content, lang) VALUES (2, 'A Journey Through Code', 'Every line of code tells a story.', 'en')") + if err != nil { + panic(err) + } + // Inserting a Spanish dummy post + _, err = db.Exec("INSERT INTO posts (id, title, content, lang) VALUES (3, 'La belleza del código', 'Cada función es un poema en un mar de algoritmos.', 'es')") + if err != nil { + panic(err) + } +} + +func getUnsanitizedPostsByLang(db *sql.DB, lang string) ([]Posts, error) { + var posts []Posts + query := "SELECT id, title, content, lang FROM posts WHERE lang = '" + lang + "'" + rows, err := db.Query(query) + if err != nil { + return nil, err + } + defer rows.Close() + + for rows.Next() { + var post Posts + if err := rows.Scan(&post.ID, &post.Title, &post.Content, &post.Lang); err != nil { + return nil, err + } + posts = append(posts, post) + } + if err = rows.Err(); err != nil { + return nil, err + } + return posts, nil +} diff --git a/pkg/testutils/fuzzplayground/server.go b/pkg/testutils/fuzzplayground/server.go new file mode 100644 index 00000000..04f00a99 --- /dev/null +++ b/pkg/testutils/fuzzplayground/server.go @@ -0,0 +1,212 @@ +// This package provides a mock server for testing fuzzing templates +package fuzzplayground + +import ( + "encoding/xml" + "fmt" + "io" + "net/http" + "net/url" + "os/exec" + "strconv" + "strings" + + "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" + "github.com/projectdiscovery/retryablehttp-go" +) + +func GetPlaygroundServer() *echo.Echo { + e := echo.New() + e.Use(middleware.Recover()) + e.Use(middleware.Logger()) + + e.GET("/", indexHandler) + e.GET("/info", infoHandler) + e.GET("/redirect", redirectHandler) + e.GET("/request", requestHandler) + e.GET("/email", emailHandler) + e.GET("/permissions", permissionsHandler) + e.GET("/blog/post", numIdorHandler) // for num based idors like ?id=44 + e.POST("/reset-password", resetPasswordHandler) + e.GET("/host-header-lab", hostHeaderLabHandler) + e.GET("/user/:id/profile", userProfileHandler) + e.POST("/user", patchUnsanitizedUserHandler) + e.GET("/blog/posts", getPostsHandler) + return e +} + +var bodyTemplate = ` + +Fuzz Playground + + +%s + +` + +func indexHandler(ctx echo.Context) error { + return ctx.HTML(200, fmt.Sprintf(bodyTemplate, `

Fuzzing Playground


+ +`)) +} + +func infoHandler(ctx echo.Context) error { + return ctx.HTML(200, fmt.Sprintf(bodyTemplate, fmt.Sprintf("Name of user: %s%s%s", ctx.QueryParam("name"), ctx.QueryParam("another"), ctx.QueryParam("random")))) +} + +func redirectHandler(ctx echo.Context) error { + url := ctx.QueryParam("redirect_url") + return ctx.Redirect(302, url) +} + +func requestHandler(ctx echo.Context) error { + url := ctx.QueryParam("url") + data, err := retryablehttp.DefaultClient().Get(url) + if err != nil { + return ctx.HTML(500, err.Error()) + } + defer data.Body.Close() + + body, _ := io.ReadAll(data.Body) + return ctx.HTML(200, fmt.Sprintf(bodyTemplate, string(body))) +} + +func emailHandler(ctx echo.Context) error { + text := ctx.QueryParam("text") + if strings.Contains(text, "{{") { + trimmed := strings.SplitN(strings.Trim(text[strings.Index(text, "{"):], "{}"), "*", 2) + if len(trimmed) < 2 { + return ctx.HTML(500, "invalid template") + } + first, _ := strconv.Atoi(trimmed[0]) + second, _ := strconv.Atoi(trimmed[1]) + text = strconv.Itoa(first * second) + } + return ctx.HTML(200, fmt.Sprintf(bodyTemplate, fmt.Sprintf("Text: %s", text))) +} + +func permissionsHandler(ctx echo.Context) error { + command := ctx.QueryParam("cmd") + fields := strings.Fields(command) + cmd := exec.Command(fields[0], fields[1:]...) + data, _ := cmd.CombinedOutput() + + return ctx.HTML(200, fmt.Sprintf(bodyTemplate, string(data))) +} + +func numIdorHandler(ctx echo.Context) error { + // validate if any numerical query param is present + // if not, return 400 if so, return 200 + for k := range ctx.QueryParams() { + if _, err := strconv.Atoi(ctx.QueryParam(k)); err == nil { + return ctx.JSON(200, "Profile Info for user with id "+ctx.QueryParam(k)) + } + } + return ctx.JSON(400, "No numerical query param found") +} + +func patchUnsanitizedUserHandler(ctx echo.Context) error { + var user User + + contentType := ctx.Request().Header.Get("Content-Type") + // manually handle unmarshalling data + if strings.Contains(contentType, "application/json") { + err := ctx.Bind(&user) + if err != nil { + return ctx.JSON(500, "Invalid JSON data") + } + } else if strings.Contains(contentType, "application/x-www-form-urlencoded") { + user.Name = ctx.FormValue("name") + user.Age, _ = strconv.Atoi(ctx.FormValue("age")) + user.Role = ctx.FormValue("role") + user.ID, _ = strconv.Atoi(ctx.FormValue("id")) + } else if strings.Contains(contentType, "application/xml") { + bin, _ := io.ReadAll(ctx.Request().Body) + err := xml.Unmarshal(bin, &user) + if err != nil { + return ctx.JSON(500, "Invalid XML data") + } + } else if strings.Contains(contentType, "multipart/form-data") { + user.Name = ctx.FormValue("name") + user.Age, _ = strconv.Atoi(ctx.FormValue("age")) + user.Role = ctx.FormValue("role") + user.ID, _ = strconv.Atoi(ctx.FormValue("id")) + } else { + return ctx.JSON(500, "Invalid Content-Type") + } + + err := patchUnsanitizedUser(db, user) + if err != nil { + return ctx.JSON(500, err.Error()) + } + return ctx.JSON(200, "User updated successfully") +} + +// resetPassword mock +func resetPasswordHandler(c echo.Context) error { + var m map[string]interface{} + if err := c.Bind(&m); err != nil { + return c.JSON(500, "Something went wrong") + } + + host := c.Request().Header.Get("X-Forwarded-For") + if host == "" { + return c.JSON(500, "Something went wrong") + } + resp, err := http.Get("http://internal." + host + "/update?user=1337&pass=" + m["password"].(string)) + if err != nil { + return c.JSON(500, "Something went wrong") + } + defer resp.Body.Close() + return c.JSON(200, "Password reset successfully") +} + +func hostHeaderLabHandler(c echo.Context) error { + // vulnerable app has custom routing and trusts x-forwarded-host + // to route to internal services + if c.Request().Header.Get("X-Forwarded-Host") != "" { + resp, err := http.Get("http://" + c.Request().Header.Get("X-Forwarded-Host")) + if err != nil { + return c.JSON(500, "Something went wrong") + } + defer resp.Body.Close() + c.Response().Header().Set("Content-Type", resp.Header.Get("Content-Type")) + c.Response().WriteHeader(resp.StatusCode) + _, err = io.Copy(c.Response().Writer, resp.Body) + if err != nil { + return c.JSON(500, "Something went wrong") + } + } + return c.JSON(200, "Not a Teapot") +} + +func userProfileHandler(ctx echo.Context) error { + val, _ := url.PathUnescape(ctx.Param("id")) + fmt.Printf("Unescaped: %s\n", val) + user, err := getUnsanitizedUser(db, val) + if err != nil { + return ctx.JSON(500, err.Error()) + } + return ctx.JSON(200, user) +} + +func getPostsHandler(c echo.Context) error { + lang, err := c.Cookie("lang") + if err != nil { + // If the language cookie is missing, default to English + lang = new(http.Cookie) + lang.Value = "en" + } + posts, err := getUnsanitizedPostsByLang(db, lang.Value) + if err != nil { + return c.JSON(http.StatusInternalServerError, err.Error()) + } + return c.JSON(http.StatusOK, posts) +} diff --git a/pkg/types/types.go b/pkg/types/types.go index fb42ea6f..45a8788a 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -270,6 +270,8 @@ type Options struct { DisableRedirects bool // SNI custom hostname SNI string + // InputFileMode specifies the mode of input file (jsonl, burp, openapi, swagger, etc) + InputFileMode string // DialerTimeout sets the timeout for network requests. DialerTimeout time.Duration // DialerKeepAlive sets the keep alive duration for network requests. @@ -370,6 +372,17 @@ type Options struct { ScanID string // JsConcurrency is the number of concurrent js routines to run JsConcurrency int + // Fuzz enabled execution of fuzzing templates + // Note: when Fuzz is enabled other templates will not be executed + FuzzTemplates bool + // SecretsFile is file containing secrets for nuclei + SecretsFile goflags.StringSlice + // PreFetchSecrets pre-fetches the secrets from the auth provider + PreFetchSecrets bool + // FormatUseRequiredOnly only uses required fields when generating requests + FormatUseRequiredOnly bool + // SkipFormatValidation is used to skip format validation + SkipFormatValidation bool // PayloadConcurrency is the number of concurrent payloads to run per template PayloadConcurrency int } diff --git a/pkg/utils/http_probe.go b/pkg/utils/http_probe.go index 1e3db9ab..19059b2a 100644 --- a/pkg/utils/http_probe.go +++ b/pkg/utils/http_probe.go @@ -5,6 +5,7 @@ import ( "net/http" "github.com/projectdiscovery/httpx/common/httpx" + "github.com/projectdiscovery/nuclei/v3/pkg/input/types" "github.com/projectdiscovery/useragent" ) @@ -32,3 +33,18 @@ func ProbeURL(input string, httpxclient *httpx.HTTPX) string { } return "" } + +type inputLivenessChecker struct { + client *httpx.HTTPX +} + +// ProbeURL probes the scheme for a URL. first HTTPS is tried +func (i *inputLivenessChecker) ProbeURL(input string) (string, error) { + return ProbeURL(input, i.client), nil +} + +// GetInputLivenessChecker returns a new input liveness checker using provided httpx client +func GetInputLivenessChecker(client *httpx.HTTPX) types.InputLivenessProbe { + x := &inputLivenessChecker{client: client} + return x +}