Skip to content

Commit

Permalink
JSON Mutator
Browse files Browse the repository at this point in the history
  • Loading branch information
devl00p committed Dec 9, 2023
1 parent c60eaf4 commit 018f678
Show file tree
Hide file tree
Showing 6 changed files with 446 additions and 0 deletions.
Empty file added tests/mutation/__init__.py
Empty file.
308 changes: 308 additions & 0 deletions tests/mutation/test_json_mutator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
import json

import pytest

from wapitiCore.attack.attack import Parameter, ParameterSituation
from wapitiCore.model import str_to_payloadinfo, PayloadInfo
from wapitiCore.mutation.json_mutator import find_injectable, set_item, get_item, JSONMutator
from wapitiCore.net import Request


@pytest.mark.parametrize(
"obj, paths",
[
[
[],
[[0]],
],
[
{},
[],
],
[
{"dict_with_string_value": "hello"},
[["dict_with_string_value"]],
],
[
{"dict_with_int_value": 42},
[["dict_with_int_value"]],
],
[
{
"nested_dict": {
"list_of_dicts": [
{"a": "b"},
],
}
},
[["nested_dict", "list_of_dicts", 0, "a"]]
],
[
{
"nested_dict": {
"list_of_words": ["hello", "world"]
}
},
[["nested_dict", "list_of_words", 0]]
],
[
[
{"a": "b"},
{"c": "d"},
],
[[0, "a"]],
],
[
{
"nested_dict": {
"list_of_dicts": [
{
"a": "b",
"c": "d",
},
],
"list_of_words": ["yolo"],
"empty_list": [],
"empty_dict": {},
},
"item_string": "hello",
},
[
['nested_dict', 'list_of_dicts', 0, 'a'],
['nested_dict', 'list_of_dicts', 0, 'c'],
['nested_dict', 'list_of_words', 0],
['nested_dict', 'empty_list', 0],
['item_string'],
],
],
[
[
[
{"a": "b"}
],
42
],
[
[0, 0, "a"],
],
]
],
ids=[
"empty list",
"empty dict",
"dict with string value",
"dict with int value",
"nested dict > list > dict",
"nested dict > list",
"nested list > dict",
"nested complex",
"nested list > list > dict",
]
)
def test_find_injectable(obj, paths):
assert paths == list(find_injectable([], obj))


@pytest.mark.parametrize(
"original, path, expected",
[
[
{"a": "b"},
["a"],
{"a": "Hello"},
],
[
["a", "b"],
[0],
["Hello", "b"],
],
[
[],
[0],
["Hello"],
],
[
{
"nested_dict": {
"list_of_dicts": [
{
"a": "b",
"c": "d",
},
],
},
},
['nested_dict', 'list_of_dicts', 0, 'c'],
{
"nested_dict": {
"list_of_dicts": [
{
"a": "b",
"c": "Hello",
},
],
},
},
],
[
[
[
{"a": "b"}
],
42
],
[0, 0, "a"],
[
[
{"a": "Hello"}
],
42
],
]
],
ids=[
"simple dict",
"simple list",
"empty list",
"nested complex",
"nested list",
],
)
def test_set_item(original, path, expected):
set_item(original, path, "Hello")
assert expected == original


@pytest.mark.parametrize(
"obj, path, expected",
[
[
[],
[0],
[],
],
[
{"a": "b"},
["a"],
"b",
],
[
["a", "b"],
[0],
"a",
],
[
{
"nested_dict": {
"list_of_dicts": [
{
"a": "b",
"c": "d",
},
],
},
},
['nested_dict', 'list_of_dicts', 0, 'c'],
"d",
],
[
[[{"a": "b"}], 42],
[0, 0, "a"],
"b",
]
],
ids=[
"empty list",
"simple dict",
"simple list",
"nested dict",
"nested list",
]
)
def test_get_item(obj, path, expected):
assert expected == get_item(obj, path)


def test_json_mutator_replace_values():
mutator = JSONMutator()
# We will ensure we can inject data inside a string value and an int value
request = Request(
"http://perdu.com/api/",
enctype="application/json",
post_params=json.dumps({"a": [{"c": "e"}], "f": 5})
)

expected = [
('{"a": [{"c": "eyolo"}], "f": 5}', Parameter(name='a.0.c', situation=ParameterSituation.JSON_BODY), "eyolo"),
('{"a": [{"c": "e"}], "f": "5yolo"}', Parameter(name='f', situation=ParameterSituation.JSON_BODY), "5yolo"),
]

mutated_request: Request
parameter: Parameter
payload_info: PayloadInfo

for i, (mutated_request, parameter, payload_info) in enumerate(mutator.mutate(
request,
lambda: str_to_payloadinfo(["[VALUE]yolo"]),
)):
assert expected[i] == (mutated_request.post_params, parameter, payload_info.payload)
assert mutated_request.is_json


def test_json_mutator_handle_list():
mutator = JSONMutator()
# We will ensure we can inject data inside a string value and an int value
request = Request(
"http://perdu.com/api/",
enctype="application/json",
post_params=json.dumps({"a": [4]})
)

expected = (
'{"a": ["4yolo"]}',
Parameter(name='a.0', situation=ParameterSituation.JSON_BODY),
"4yolo"
)

mutated_request: Request
parameter: Parameter
payload_info: PayloadInfo

mutated_request, parameter, payload_info = next(mutator.mutate(
request,
lambda: str_to_payloadinfo(["[VALUE]yolo"]),
))
assert expected == (mutated_request.post_params, parameter, payload_info.payload)
assert mutated_request.is_json


def test_json_mutator_handle_empty_list():
mutator = JSONMutator()
# We will ensure we can inject data inside an empty list
request = Request(
"http://perdu.com/api/",
enctype="application/json",
post_params=json.dumps({"a": []})
)

expected = (
'{"a": ["Hello there"]}',
Parameter(name="a.0", situation=ParameterSituation.JSON_BODY),
"Hello there"
)

mutated_request: Request
parameter: Parameter
payload_info: PayloadInfo

mutations = mutator.mutate(
request,
# the first payload should be skipped as it atempts to reuse a valid that doesn't exist
lambda: str_to_payloadinfo(["[VALUE]yolo", "Hello there"]),
)
mutated_request, parameter, payload_info = next(mutations)
assert expected == (mutated_request.post_params, parameter, payload_info.payload)
assert mutated_request.is_json

with pytest.raises(StopIteration):
next(mutations)
1 change: 1 addition & 0 deletions wapitiCore/attack/attack.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ class ParameterSituation(Flag):
POST_BODY = auto()
MULTIPART = auto()
HEADERS = auto()
JSON_BODY = auto()


@dataclasses.dataclass
Expand Down
Empty file added wapitiCore/mutation/__init__.py
Empty file.
Loading

0 comments on commit 018f678

Please sign in to comment.