Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Have attacker rerun all requests, update xfail test #44

Merged
merged 8 commits into from
Apr 10, 2020
26 changes: 16 additions & 10 deletions fuzz_lightyear/plugins/idor.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,19 @@ def is_vulnerable(
request_sequence: List[FuzzingRequest],
response_sequence: List[Any],
) -> bool:
last_request = request_sequence[-1]
try:
last_request.send(
auth=get_abstraction().get_attacker_session(), # type: ignore
should_log=False,
)

return True
except (HTTPError, SwaggerMappingError, ValidationError):
return False
# We have the attacker execute the same request sequence with different
# values except for the last request.
for request in request_sequence[:-1]:
if request.fuzzed_input:
for query_key in request.fuzzed_input:
request.fuzzed_input[query_key] += 1
tanx16 marked this conversation as resolved.
Show resolved Hide resolved
for request in request_sequence:
try:
request.send(
auth=get_abstraction().get_attacker_session(), # type: ignore
should_log=False,
)

except (HTTPError, SwaggerMappingError, ValidationError):
return False
return True
tanx16 marked this conversation as resolved.
Show resolved Hide resolved
8 changes: 8 additions & 0 deletions testing/vulnerable_app/models/thing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from ..core.database import Base


class Thing(Base):
"""
This is separate from Widget since reusing it causes flakiness.
tanx16 marked this conversation as resolved.
Show resolved Hide resolved
"""
pass
1 change: 1 addition & 0 deletions testing/vulnerable_app/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
'complex',
'constant',
'location',
'nonvulnerable',
'sequence',
'types',
'user',
Expand Down
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 @@ -20,3 +20,10 @@
'id': fields.Integer(required=True),
},
)

thing_model = api.model(
'Thing',
{
'id': fields.Integer(required=True),
},
)
tanx16 marked this conversation as resolved.
Show resolved Hide resolved
69 changes: 69 additions & 0 deletions testing/vulnerable_app/views/nonvulnerable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"""
These endpoints focus on testing the stateful-ness of the fuzzer.

The `alpha` series of endpoints enforce basic stateful-ness.
tanx16 marked this conversation as resolved.
Show resolved Hide resolved
"""
from flask import abort
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.thing import Thing
from ..util import get_name
from .models.database import thing_model


ns = api.namespace(
get_name(__name__),
url_prefix='/{}'.format(get_name(__name__)),
)


# This tests that https://github.com/Yelp/fuzz-lightyear/issues/11 has been fixed.
@ns.route('/no-vuln/create')
class CreateNoVuln(Resource):
@api.doc(security='apikey')
@api.response(200, 'Success', model=thing_model)
@requires_user
def post(self, user):

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

session.add(entry)
session.commit()

user.created_resource = [entry.id]
user.save()

return {
'id': entry.id,
}


@ns.route('/no-vuln/get/<int:id>')
class GetNoVuln(Resource):
@api.doc(security='apikey')
@api.response(200, 'Success', model=thing_model)
@api.response(404, 'Not Found')
@api.response(403, 'Not Authorized')
@requires_user
def get(self, id, user):
if id not in user.created_resource:
abort(403)

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

widget_id = entry.id

return {
'id': widget_id,
}
tanx16 marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions testing/vulnerable_app/views/sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class GetWithSideEffect(Resource):
@api.response(404, 'Not Found')
@requires_user
def get(self, id, user):
print(user.to_dict())
tanx16 marked this conversation as resolved.
Show resolved Hide resolved
if not user.has_created_resource:
abort(401)

Expand Down
28 changes: 23 additions & 5 deletions tests/integration/plugins/idor_test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import pytest

from fuzz_lightyear.request import FuzzingRequest
from fuzz_lightyear.response import ResponseSequence
from fuzz_lightyear.runner import run_sequence
Expand Down Expand Up @@ -36,9 +34,6 @@ def test_skipped_due_to_no_inputs(mock_client):
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(
[
Expand All @@ -62,3 +57,26 @@ def test_side_effect(mock_api_client):

assert responses.responses[1].has_created_resource
assert responses.test_results['IDORPlugin']


def test_no_vuln(mock_api_client):
responses = run_sequence(
[
FuzzingRequest(
tag='nonvulnerable',
operation_id='post_create_no_vuln',
),
FuzzingRequest(
tag='user',
operation_id='get_get_user',
),
FuzzingRequest(
tag='nonvulnerable',
operation_id='get_get_no_vuln',
),
],
ResponseSequence(),
)

assert responses.responses[1].created_resource
assert not responses.test_results['IDORPlugin']