From 65b43ad7f0a1120d390883711e37786335b9280d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20St=C3=A1rek?= Date: Wed, 23 Oct 2019 13:45:57 +0200 Subject: [PATCH 1/2] Added .travisci file Also few tests were fixed --- .travis.yml | 21 ++++++ README.md | 2 + fuzzer/src/fuzzer.py | 9 +-- fuzzer/src/progress_reporter.py | 19 ++++-- .../unit_tests/fuzzing_json_decoder_tests.py | 4 ++ fuzzer/src/wapifuzz.py | 2 +- parser/OpenApiAnyConvertor.cs | 68 +++++++++++++++++++ parser/Parser.Tests/EndpointParserTests.cs | 8 +++ .../Parser.Tests/OpenApiAnyConvertorTests.cs | 10 ++- run.sh | 2 +- 10 files changed, 132 insertions(+), 13 deletions(-) create mode 100644 .travis.yml create mode 100644 parser/OpenApiAnyConvertor.cs diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..62d1bd8 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,21 @@ +dist: xenial +language: csharp +mono: none +dotnet: 2.2.402 +script: + - sudo apt update + - sudo apt install --yes libssl-dev + - sudo apt install --yes build-essential checkinstall + - sudo apt install --yes libreadline-gplv2-dev libncursesw5-dev libssl-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev libffi-dev zlib1g-dev + - sudo apt install --yes dos2unix + - cd /usr/src && sudo wget https://www.python.org/ftp/python/3.7.3/Python-3.7.3.tgz && sudo tar xzf Python-3.7.3.tgz && cd Python-3.7.3 && sudo ./configure --enable-optimizations && sudo make altinstall + - sudo ln -s /usr/local/bin/python3.7 /usr/local/bin/python3 + - sudo ln -s /usr/local/bin/pip3.7 /usr/local/bin/pip3 + - export PATH="/usr/local/bin:$PATH" + - sudo pip3 install --upgrade pip && sudo pip3 install git+https://github.com/jtpereyda/boofuzz.git && sudo pip3 install junit-xml && sudo pip3 install virtualenv + - find ~/build/ysoftdevs/wapifuzz/ -type f -exec dos2unix {} \; + - find ~/build/ysoftdevs/wapifuzz/ -type f -name "*.sh" -exec chmod u+x {} \; + - cd ~/build/ysoftdevs/wapifuzz/parser/ && dotnet restore && dotnet test + - cd ~/build/ysoftdevs/wapifuzz/fuzzer/src/ && python3 -m unittest unit_tests.fuzzing_json_decoder_tests + - cd ~/build/ysoftdevs/wapifuzz/fuzzer/src/ && python3 -m unittest unit_tests.json_schema_parser_tests + - cd ~/build/ysoftdevs/wapifuzz/tests/ && chmod +x run_tests.sh && travis_wait ./run_tests.sh diff --git a/README.md b/README.md index 4e3e9a5..9266162 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![Build Status](https://travis-ci.com/ysoftdevs/wapifuzz.svg?branch=master)](https://travis-ci.com/ysoftdevs/wapifuzz) + # WapiFuzz - fully autonomous web APIs fuzzer Fuzzing is popular testing technique for various error types detection. There are many fuzzing engines and fuzzers, which can help you with fuzzing itself. But there is currently no tool which can fully automate fuzzing just by providing API specification. diff --git a/fuzzer/src/fuzzer.py b/fuzzer/src/fuzzer.py index 0c31eb2..8fad5e5 100644 --- a/fuzzer/src/fuzzer.py +++ b/fuzzer/src/fuzzer.py @@ -9,9 +9,10 @@ from blocks_generator import generate_http_fuzzed_blocks, generate_url_attribute class Fuzzer: - def __init__(self, endpoints, loggers: List, protocol: str): + def __init__(self, endpoints, text_logger, junit_logger, protocol: str): self._endpoints = endpoints - self._loggers = loggers + self._text_logger = text_logger + self._junit_logger = junit_logger self._protocol = protocol self._session = None @@ -39,7 +40,7 @@ class Fuzzer: self._session = Session( target=target, - fuzz_loggers=self._loggers, + fuzz_loggers=[self._text_logger, self._junit_logger], post_test_case_callbacks=[PostTestCaseCallback.post_test_callback], restart_sleep_time=0, keep_web_open=False, @@ -67,5 +68,5 @@ class Fuzzer: self._endpoints[:] = [endpoint for endpoint in self._endpoints if keyword not in endpoint.get('Uri')] def fuzz(self): - report_progress(self._session) + report_progress(self._session, self._junit_logger) self._session.fuzz() diff --git a/fuzzer/src/progress_reporter.py b/fuzzer/src/progress_reporter.py index 6926736..2a253da 100644 --- a/fuzzer/src/progress_reporter.py +++ b/fuzzer/src/progress_reporter.py @@ -5,27 +5,34 @@ import datetime from configuration_manager import ConfigurationManager DID_FUZZING_STARTED_CHECKS_TIME_INTERVAL_IN_SECONDS = 5 +HANGED_TIMEOUT = 120 -def report_progress(session): +def close_testing_and_kill_fuzzer(junit_logger, session): + if is_fuzzing_hanged(session): + junit_logger.close_test() + os._exit(1) + + +def report_progress(session, junit_logger): if did_fuzzing_already_started(session) > 0: if is_fuzzing_hanged(session): message = create_hanged_message(session) print(message, file=sys.stderr) - os._exit(1) + threading.Timer(HANGED_TIMEOUT, close_testing_and_kill_fuzzer, [junit_logger, session]).start() if is_fuzzing_still_in_progress(session): - plan_another_report(session, ConfigurationManager.get_reporting_interval()) + plan_another_report(session, junit_logger, ConfigurationManager.get_reporting_interval()) message = create_report_message(session) print(message, file=sys.stderr) else: - plan_another_report(session, DID_FUZZING_STARTED_CHECKS_TIME_INTERVAL_IN_SECONDS) + plan_another_report(session, junit_logger, DID_FUZZING_STARTED_CHECKS_TIME_INTERVAL_IN_SECONDS) -def plan_another_report(session, reporting_interval): - threading.Timer(reporting_interval, report_progress, [session]).start() +def plan_another_report(session, junit_logger, reporting_interval): + threading.Timer(reporting_interval, report_progress, [session, junit_logger]).start() def did_fuzzing_already_started(session): diff --git a/fuzzer/src/unit_tests/fuzzing_json_decoder_tests.py b/fuzzer/src/unit_tests/fuzzing_json_decoder_tests.py index 2938dae..5a1dffa 100644 --- a/fuzzer/src/unit_tests/fuzzing_json_decoder_tests.py +++ b/fuzzer/src/unit_tests/fuzzing_json_decoder_tests.py @@ -3,6 +3,7 @@ import json from boofuzz import * from fuzzing_json_decoder import FuzzingJsonDecoder from fuzz_payloads import FuzzPayloads +from configuration_manager import ConfigurationManager class FuzzingJsonDecoderTests(unittest.TestCase): @@ -14,6 +15,9 @@ class FuzzingJsonDecoderTests(unittest.TestCase): FuzzPayloads.add_payload_to_list("payload 1", FuzzPayloads.CUSTOM_PAYLOADS_KEY) FuzzPayloads.add_payload_to_list("payload 2", FuzzPayloads.CUSTOM_PAYLOADS_KEY) + # Generate fake configuration + ConfigurationManager.config = [] + def __json_equality_assertion(self, original_json, generated_json): self.assertDictEqual(json.loads(original_json), json.loads(generated_json)) diff --git a/fuzzer/src/wapifuzz.py b/fuzzer/src/wapifuzz.py index dbcc6b6..7284033 100644 --- a/fuzzer/src/wapifuzz.py +++ b/fuzzer/src/wapifuzz.py @@ -34,7 +34,7 @@ def main(): with open(endpoints_description, 'r') as endpoints_description_file_pointer: endpoints = json.loads(endpoints_description_file_pointer.read()) - fuzzer = Fuzzer(endpoints, [text_logger, junit_logger], protocol) + fuzzer = Fuzzer(endpoints, text_logger, junit_logger, protocol) fuzzer.fuzz() diff --git a/parser/OpenApiAnyConvertor.cs b/parser/OpenApiAnyConvertor.cs new file mode 100644 index 0000000..42defa1 --- /dev/null +++ b/parser/OpenApiAnyConvertor.cs @@ -0,0 +1,68 @@ +using System; +using System.Globalization; +using System.IO; +using System.Text; +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Writers; + +namespace Parser +{ + public static class OpenApiAnyConvertor + { + public static string GetPrimitiveValue(IOpenApiAny value) + { + IOpenApiPrimitive primitive = (IOpenApiPrimitive) value; + + switch (primitive.PrimitiveType) + { + case PrimitiveType.String: + OpenApiString stringValue = (OpenApiString) primitive; + return stringValue.Value; + case PrimitiveType.Boolean: + OpenApiBoolean booleanValue = (OpenApiBoolean) primitive; + return booleanValue.Value.ToString(); + case PrimitiveType.Integer: + OpenApiInteger integerValue = (OpenApiInteger) primitive; + return integerValue.Value.ToString(); + case PrimitiveType.Long: + OpenApiLong longValue = (OpenApiLong) primitive; + return longValue.Value.ToString(); + case PrimitiveType.Float: + OpenApiFloat floatValue = (OpenApiFloat) primitive; + return floatValue.Value.ToString(CultureInfo.InvariantCulture); + case PrimitiveType.Double: + OpenApiDouble doubleValue = (OpenApiDouble) primitive; + return doubleValue.Value.ToString(CultureInfo.InvariantCulture); + case PrimitiveType.Byte: + OpenApiByte byteValue = (OpenApiByte) primitive; + return Encoding.Default.GetString(byteValue.Value); + case PrimitiveType.Binary: + OpenApiBinary binaryValue = (OpenApiBinary) primitive; + StringBuilder builder = new StringBuilder(); + foreach (byte byteVal in binaryValue.Value) + { + builder.Append(Convert.ToString(byteVal, 2).PadLeft(8, '0')); + } + return builder.ToString(); + case PrimitiveType.Date: + OpenApiDate dateValue = (OpenApiDate) primitive; + return dateValue.Value.ToString(CultureInfo.InvariantCulture); + case PrimitiveType.DateTime: + OpenApiDateTime dateTimeValue = (OpenApiDateTime) primitive; + return dateTimeValue.Value.ToString(CultureInfo.InvariantCulture); + case PrimitiveType.Password: + OpenApiPassword passwordValue = (OpenApiPassword) primitive; + return passwordValue.Value; + default: + throw new NotImplementedException("This data example type is not supported yet!"); + } + } + + public static string GetJsonValue(IOpenApiAny value) + { + StringBuilder builder = new StringBuilder(); + value.Write(new OpenApiJsonWriter(new StringWriter(builder)), OpenApiDocumentParser.Version); + return builder.ToString(); + } + } +} diff --git a/parser/Parser.Tests/EndpointParserTests.cs b/parser/Parser.Tests/EndpointParserTests.cs index cf4daf5..58dad05 100644 --- a/parser/Parser.Tests/EndpointParserTests.cs +++ b/parser/Parser.Tests/EndpointParserTests.cs @@ -1,4 +1,6 @@ using System.Collections.Generic; +using System.Globalization; +using System.Threading; using Microsoft.OpenApi.Models; using NUnit.Framework; using Models; @@ -7,6 +9,12 @@ namespace Parser.Tests { public class EndpointParserTests { + [SetUp] + public void SetUp() + { + Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); + } + readonly OpenApiDocument _document = new OpenApiDocument {Paths = new OpenApiPaths()}; private void AddTwoTestingPaths() { diff --git a/parser/Parser.Tests/OpenApiAnyConvertorTests.cs b/parser/Parser.Tests/OpenApiAnyConvertorTests.cs index b90a998..c31b060 100644 --- a/parser/Parser.Tests/OpenApiAnyConvertorTests.cs +++ b/parser/Parser.Tests/OpenApiAnyConvertorTests.cs @@ -1,4 +1,6 @@ using System; +using System.Globalization; +using System.Threading; using Microsoft.OpenApi.Any; using NUnit.Framework; @@ -6,6 +8,12 @@ namespace Parser.Tests { public class OpenApiAnyConvertorTests { + [SetUp] + public void SetUp() + { + Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); + } + [Test] public void ConvertStringPrimitiveShouldReturnCorrectValue() { @@ -51,7 +59,7 @@ namespace Parser.Tests public void ConvertDateTimePrimitiveShouldReturnCorrectValue() { var primitiveValue = OpenApiAnyConvertor.GetPrimitiveValue(new OpenApiDateTime(DateTime.UnixEpoch)); - Assert.AreEqual("1/1/1970 12:00:00 AM +00:00", primitiveValue); + Assert.AreEqual("01/01/1970 00:00:00 +00:00", primitiveValue); } [Test] diff --git a/run.sh b/run.sh index cf629a2..2cb993b 100644 --- a/run.sh +++ b/run.sh @@ -77,7 +77,7 @@ ${PYTHON3_BIN} -m virtualenv env echo "Started fuzzing" . ./env/bin/activate ; \ pip install --upgrade pip ; pip install git+https://github.com/jtpereyda/boofuzz.git ; pip install junit-xml ; \ -python fuzzer/src/wapifuzz.py ${WAPIFUZZ_CONFIG} ${API_REQUESTS_JSON} ${JUNIT_TEST_REPORT} ${CUSTOM_PAYLOADS_FILE} > ${FUZZER_LOG} || { echo 'Fuzzing failed. HTML report will not be produced.' ; exit 1; } ; deactivate +python fuzzer/src/wapifuzz.py ${WAPIFUZZ_CONFIG} ${API_REQUESTS_JSON} ${JUNIT_TEST_REPORT} ${CUSTOM_PAYLOADS_FILE} > ${FUZZER_LOG} || { echo 'Fuzzing failed. Trying to generate HTML result of procceeded test cases.' ; } ; deactivate echo "Fuzzing finished" echo "Starting generating HTML test report" From 17bd85da15eebba917e755258341079b6ddd000b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20St=C3=A1rek?= Date: Wed, 23 Oct 2019 14:07:05 +0200 Subject: [PATCH 2/2] Adding invariant of culture for DateTime --- parser/OpenApiAnyConvertor.cs | 68 ---------------------------- parser/Parser/OpenApiAnyConvertor.cs | 2 +- 2 files changed, 1 insertion(+), 69 deletions(-) delete mode 100644 parser/OpenApiAnyConvertor.cs diff --git a/parser/OpenApiAnyConvertor.cs b/parser/OpenApiAnyConvertor.cs deleted file mode 100644 index 42defa1..0000000 --- a/parser/OpenApiAnyConvertor.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Globalization; -using System.IO; -using System.Text; -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Writers; - -namespace Parser -{ - public static class OpenApiAnyConvertor - { - public static string GetPrimitiveValue(IOpenApiAny value) - { - IOpenApiPrimitive primitive = (IOpenApiPrimitive) value; - - switch (primitive.PrimitiveType) - { - case PrimitiveType.String: - OpenApiString stringValue = (OpenApiString) primitive; - return stringValue.Value; - case PrimitiveType.Boolean: - OpenApiBoolean booleanValue = (OpenApiBoolean) primitive; - return booleanValue.Value.ToString(); - case PrimitiveType.Integer: - OpenApiInteger integerValue = (OpenApiInteger) primitive; - return integerValue.Value.ToString(); - case PrimitiveType.Long: - OpenApiLong longValue = (OpenApiLong) primitive; - return longValue.Value.ToString(); - case PrimitiveType.Float: - OpenApiFloat floatValue = (OpenApiFloat) primitive; - return floatValue.Value.ToString(CultureInfo.InvariantCulture); - case PrimitiveType.Double: - OpenApiDouble doubleValue = (OpenApiDouble) primitive; - return doubleValue.Value.ToString(CultureInfo.InvariantCulture); - case PrimitiveType.Byte: - OpenApiByte byteValue = (OpenApiByte) primitive; - return Encoding.Default.GetString(byteValue.Value); - case PrimitiveType.Binary: - OpenApiBinary binaryValue = (OpenApiBinary) primitive; - StringBuilder builder = new StringBuilder(); - foreach (byte byteVal in binaryValue.Value) - { - builder.Append(Convert.ToString(byteVal, 2).PadLeft(8, '0')); - } - return builder.ToString(); - case PrimitiveType.Date: - OpenApiDate dateValue = (OpenApiDate) primitive; - return dateValue.Value.ToString(CultureInfo.InvariantCulture); - case PrimitiveType.DateTime: - OpenApiDateTime dateTimeValue = (OpenApiDateTime) primitive; - return dateTimeValue.Value.ToString(CultureInfo.InvariantCulture); - case PrimitiveType.Password: - OpenApiPassword passwordValue = (OpenApiPassword) primitive; - return passwordValue.Value; - default: - throw new NotImplementedException("This data example type is not supported yet!"); - } - } - - public static string GetJsonValue(IOpenApiAny value) - { - StringBuilder builder = new StringBuilder(); - value.Write(new OpenApiJsonWriter(new StringWriter(builder)), OpenApiDocumentParser.Version); - return builder.ToString(); - } - } -} diff --git a/parser/Parser/OpenApiAnyConvertor.cs b/parser/Parser/OpenApiAnyConvertor.cs index 9b23a33..42defa1 100644 --- a/parser/Parser/OpenApiAnyConvertor.cs +++ b/parser/Parser/OpenApiAnyConvertor.cs @@ -49,7 +49,7 @@ namespace Parser return dateValue.Value.ToString(CultureInfo.InvariantCulture); case PrimitiveType.DateTime: OpenApiDateTime dateTimeValue = (OpenApiDateTime) primitive; - return dateTimeValue.Value.ToString(); + return dateTimeValue.Value.ToString(CultureInfo.InvariantCulture); case PrimitiveType.Password: OpenApiPassword passwordValue = (OpenApiPassword) primitive; return passwordValue.Value;