Skip to content

Commit

Permalink
adding xfail test for issue #11
Browse files Browse the repository at this point in the history
  • Loading branch information
Aaron Loo committed Oct 1, 2019
1 parent e1b2ae2 commit 0a87809
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 31 deletions.
8 changes: 8 additions & 0 deletions testing/vulnerable_app/models/widget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from ..core.database import Base


class Widget(Base):
"""
We really don't need anything else in this, except an ID.
"""
pass
7 changes: 7 additions & 0 deletions testing/vulnerable_app/views/models/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,10 @@
'user_id': fields.Integer(required=True),
},
)

widget_model = api.model(
'Widget',
{
'id': fields.Integer(required=True),
},
)
54 changes: 54 additions & 0 deletions testing/vulnerable_app/views/sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,19 @@
The `alpha` series of endpoints enforce basic stateful-ness.
"""
from flask import abort
from flask_restplus import reqparse
from flask_restplus import Resource
from sqlalchemy.orm.exc import NoResultFound

from ..core import database
from ..core.auth import requires_user
from ..core.extensions import api
from ..models.widget import Widget
from ..parsers.basic import number_query_parser
from ..util import get_name
from .models.basic import string_model
from .models.database import widget_model


ns = api.namespace(
Expand Down Expand Up @@ -53,3 +59,51 @@ class BravoTwo(Resource):
@api.response(200, 'Success', model=int)
def get(self):
return number_query_parser.parse_args()['id']


@ns.route('/side-effect/create')
class CreateWithSideEffect(Resource):
@api.doc(security='apikey')
@api.response(200, 'Success', model=widget_model)
@requires_user
def post(self, user):
user.has_created_resource = True
user.save()

with database.connection() as session:
entry = Widget()

session.add(entry)
session.commit()

widget_id = entry.id

return {
'id': widget_id,
}


@ns.route('/side-effect/get/<int:id>')
class GetWithSideEffect(Resource):
@api.doc(security='apikey')
@api.response(200, 'Success', model=widget_model)
@api.response(404, 'Not Found')
@requires_user
def get(self, id, user):
print(user.to_dict())
if not user.has_created_resource:
abort(401)

with database.connection() as session:
try:
entry = session.query(Widget).filter(
Widget.id == id,
).one()
except NoResultFound:
abort(404)

widget_id = entry.id

return {
'id': widget_id,
}
33 changes: 33 additions & 0 deletions tests/integration/plugins/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import pytest

import fuzz_lightyear


@pytest.fixture
def mock_api_client(mock_client):
"""
Override victim and attacker account, with proper API keys.
"""
victim_key = mock_client.user.post_create_user().result()
attacker_key = mock_client.user.post_create_user().result()

fuzz_lightyear.victim_account(
lambda: {
'_request_options': {
'headers': {
'X-API-KEY': victim_key,
},
},
},
)
fuzz_lightyear.attacker_account(
lambda: {
'_request_options': {
'headers': {
'X-API-KEY': attacker_key,
},
},
},
)

yield mock_client
64 changes: 64 additions & 0 deletions tests/integration/plugins/idor_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import pytest

from fuzz_lightyear.request import FuzzingRequest
from fuzz_lightyear.response import ResponseSequence
from fuzz_lightyear.runner import run_sequence


def test_basic(mock_client):
responses = run_sequence(
[
FuzzingRequest(
tag='basic',
operation_id='get_private_listing',
id=1,
),
],
ResponseSequence(),
)

assert responses.data['session'] == 'victim_session'
assert responses.test_results['IDORPlugin']


def test_skipped_due_to_no_inputs(mock_client):
responses = run_sequence(
[
FuzzingRequest(
tag='basic',
operation_id='get_no_inputs_required',
),
],
ResponseSequence(),
)

assert responses.data['session'] == 'victim_session'
assert responses.test_results == {}


@pytest.mark.xfail(
reason='https://github.com/Yelp/fuzz-lightyear/issues/11',
)
def test_side_effect(mock_api_client):
responses = run_sequence(
[
FuzzingRequest(
tag='sequence',
operation_id='post_create_with_side_effect',
),
FuzzingRequest(
tag='user',
operation_id='get_get_user',
),

# This goes last, to test for IDOR.
FuzzingRequest(
tag='sequence',
operation_id='get_get_with_side_effect',
),
],
ResponseSequence(),
)

assert responses.responses[1].has_created_resource
assert responses.test_results['IDORPlugin']
31 changes: 0 additions & 31 deletions tests/integration/runner_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,6 @@ def test_invalid_request(mock_client):
)


def test_valid_request_skip_idor_no_inputs(mock_client):
responses = run_sequence(
[
FuzzingRequest(
tag='basic',
operation_id='get_no_inputs_required',
),
],
ResponseSequence(),
)

assert responses.data['session'] == 'victim_session'
assert responses.test_results == {}


@pytest.mark.parametrize(
'non_vulnerable_operations',
[
Expand All @@ -74,22 +59,6 @@ def test_valid_request_skip_idor_manually_excluded(
assert responses.test_results == {}


def test_valid_request_with_idor(mock_client):
responses = run_sequence(
[
FuzzingRequest(
tag='basic',
operation_id='get_private_listing',
id=1,
),
],
ResponseSequence(),
)

assert responses.data['session'] == 'victim_session'
assert responses.test_results['IDORPlugin']


class TestStatefulSequence:

def test_basic(self, mock_client):
Expand Down

0 comments on commit 0a87809

Please sign in to comment.