{"id":13765272,"url":"https://github.com/Yelp/fuzz-lightyear","last_synced_at":"2025-05-10T20:31:51.318Z","repository":{"id":50185325,"uuid":"204574370","full_name":"Yelp/fuzz-lightyear","owner":"Yelp","description":"A pytest-inspired, DAST framework, capable of identifying vulnerabilities in a distributed, micro-service ecosystem through chaos engineering testing and stateful, Swagger fuzzing.","archived":false,"fork":false,"pushed_at":"2024-05-09T14:42:46.000Z","size":551,"stargazers_count":220,"open_issues_count":18,"forks_count":27,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-04-20T06:04:32.760Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Yelp.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-08-26T22:45:34.000Z","updated_at":"2025-02-28T07:29:05.000Z","dependencies_parsed_at":"2024-01-31T20:26:17.978Z","dependency_job_id":"b290ae25-d1be-4902-bc4e-34d9a2fda1fd","html_url":"https://github.com/Yelp/fuzz-lightyear","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yelp%2Ffuzz-lightyear","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yelp%2Ffuzz-lightyear/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yelp%2Ffuzz-lightyear/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yelp%2Ffuzz-lightyear/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Yelp","download_url":"https://codeload.github.com/Yelp/fuzz-lightyear/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253480622,"owners_count":21915249,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T16:00:36.291Z","updated_at":"2025-05-10T20:31:46.289Z","avatar_url":"https://github.com/Yelp.png","language":"Python","funding_links":[],"categories":["Tools","Python"],"sub_categories":[],"readme":"[![Build Status](https://github.com/Yelp/fuzz-lightyear/actions/workflows/fuzz-lightyear-build.yml/badge.svg)](https://github.com/Yelp/fuzz-lightyear/actions/workflows/fuzz-lightyear-build.yml)\n\n# fuzz-lightyear\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"./docs/logo.png\" width=\"200\"\u003e\n\u003c/p\u003e\n\nfuzz-lightyear is a [pytest-inspired](https://docs.pytest.org/en/latest/),\n[DAST](https://en.wikipedia.org/wiki/Dynamic_Application_Security_Testing) framework,\ncapable of identifying vulnerabilities in a distributed, micro-service ecosystem through\n**stateful** [Swagger](https://swagger.io/) fuzzing.\n\n**What's Special About Stateful Fuzzing?**\n\nTraditional fuzzing operates on the assumption that a command invocation failure is\nindicative of a vulnerability. This approach does not carry over to web service fuzzing\nsince failures are **expected** to happen on bad input -- in fact, successful requests\nwith a purposely malicious payload is so much more dangerous, and should be caught\naccordingly.\n\nStateful fuzzing allows us to do this. By keeping state between requests, we can assemble\na request sequence, and craft it to simulate a malicious attack vector and alert off\nunexpected success. Using [hypothesis testing](https://hypothesis.readthedocs.io/en/latest/),\nwe're able to dynamically generate these test cases so we can continue to discover new\nvectors. Finally, when we find an error, this testing framework outputs a list of cURL\ncommands for easy reproduction.\n\n## Example\n\n```\n$ fuzz-lightyear https://petstore.swagger.io/v2/swagger.json -v --ignore-exceptions\n```\n\n## Installation\n\n```\npip install fuzz-lightyear\n```\n\n## Usage\n\n```\n$ fuzz-lightyear -h\n\nusage: fuzz-lightyear [-h] [-v] [--version] [-n [ITERATIONS]] [--schema SCHEMA]\n                   [-f FIXTURE] [--seed SEED] [-t TEST] [--ignore-exceptions]\n                   [--disable-unicode]\n                   url\n\npositional arguments:\n  url                   URL of server to fuzz.\n\noptional arguments:\n  -h, --help            show this help message and exit\n  -v, --verbose         Increase the verbosity of logging.\n  --version             Displays version information.\n  -n [ITERATIONS], --iterations [ITERATIONS]\n                        Maximum request sequence length to fuzz.\n  --schema SCHEMA       Path to local swagger schema. If provided, this\n                        overrides theswagger file found at the URL.\n  -f FIXTURE, --fixture FIXTURE\n                        Path to custom specified fixtures.\n  --seed SEED           Specify seed for generation of random output.\n  -t TEST, --test TEST  Specifies a single test to run.\n  --ignore-exceptions   Ignores all exceptions raised during fuzzing (aka.\n                        only fails when vulnerabilities are found).\n  --disable-unicode     Disable unicode characters in fuzzing, only use ASCII.\n  --depth DEPTH         Maximum depth for generating nested fuzz parameters.\n\n```\n\n## Fixtures\n\nFixtures are a core component of `fuzz-lightyear`, and allow you to customize factories to\nsupplement fuzzing efforts for various endpoints. This is fundamentally important for\nmicro-service ecosystems, since services may not be CRUD applications by themselves.\nThis means the endpoints to create transient resources as part of the request sequence may\nnot be available in the Swagger specification.\n\nTo address this, we allow developers to supply custom commands necessary to populate\ncertain parts of the fuzzed request parameters.\n\n### Example\n\nLet's say that we have the following Swagger snippet:\n\n```yaml\npaths:\n  /biz_user/{userID}/invoices:\n    get:\n      tags:\n        - business\n      operationId: \"get_business_by_id\"\n      parameters:\n        - name: \"userID\"\n          in: \"path\"\n          required: true\n          type: integer\n      responses:\n        200:\n          description: \"success\"\n        403:\n          description: \"forbidden\"\n        404:\n          description: \"business not found\"\n```\n\nWe need a valid `userID` to access its invoices. Clearly, it would be a waste of time\nfor the fuzzer to put random values for the `userID`, because we don't care if an\nattacker tries to access a business that doesn't exist. Moreover, this service\ndoesn't understand how to create a business (to obtain a valid `userID`), so the fuzzer\nwill not be effective at testing this endpoint.\n\nTo address this issue, we define a fixture to tell `fuzz-lightyear` how to handle such\ncases.\n\n```python\n# fixtures.py\nimport fuzz_lightyear\n\n\n@fuzz_lightyear.register_factory('userID')\ndef create_biz_user_id():\n    return 1\n```\n\nNow, when `fuzz-lightyear` tries to fuzz `/biz_user/{userID}/invoices`, it will\nidentify that there's a user-defined factory for `userID`, and use its value in\nfuzzing.\n\n```bash\n$ fuzz-lightyear -f fixtures.py http://localhost:5000/schema -v\n================================== fuzzing session starts ==================================\nHypothesis Seed: 152367346948224061420843471695694220247\n\nbusiness E\n====================================== Test Failures =======================================\n_________________________ business.get_business_by_id [IDORPlugin] _________________________\nRequest Sequence:\n[\n  \"curl -X GET http://localhost:5000/biz_user/1/invoices\"\n]\n================================== 1 failed in 1.2 seconds =================================\n```\n\nWe can amend this example by specifying a custom method to create a business in the\n`create_business` function.\n\n### Nested Fixtures\n\nIn keeping with the example above, let's say that you needed a business first, before you\ncan create a biz_user. We can accomplish this in the following method:\n\n```python\n# fixtures.py\nimport fuzz_lightyear\n\n\n@fuzz_lightyear.register_factory('userID')\ndef create_biz_user_id(businessID):\n    return businessID + 1\n\n\n@fuzz_lightyear.register_factory('businessID')\ndef create_business():\n    return 1\n```\n\nThen,\n\n```bash\n$ fuzz-lightyear -f fixtures.py http://localhost:5000/schema -v\n================================== fuzzing session starts ==================================\nHypothesis Seed: 152367346948224061420843471695694220247\n\nbusiness E\n====================================== Test Failures =======================================\n_________________________ business.get_business_by_id [IDORPlugin] _________________________\nRequest Sequence:\n[\n  \"curl -X GET http://localhost:5000/biz_user/2/invoices\"\n]\n================================== 1 failed in 1.2 seconds =================================\n```\n\nWe can also do type-casting of nested fixtures, through the use of **type annotations**.\n\n```python\n# fixtures.py\nimport fuzz_lightyear\n\n\n@fuzz_lightyear.register_factory('userID')\ndef create_biz_user_id(businessID: str):\n    return businessID + 'a'\n\n\n@fuzz_lightyear.register_factory('businessID')\ndef create_business():\n    return 1\n```\n\nWhich will produce:\n\n```bash\n$ fuzz-lightyear -f fixtures.py http://localhost:5000/schema -v\n================================== fuzzing session starts ==================================\nHypothesis Seed: 152367346948224061420843471695694220247\n\nbusiness E\n====================================== Test Failures =======================================\n_________________________ business.get_business_by_id [IDORPlugin] _________________________\nRequest Sequence:\n[\n  \"curl -X GET http://localhost:5000/biz_user/1a/invoices\"\n]\n================================== 1 failed in 1.2 seconds =================================\n```\n\n### Endpoint Specific Fixtures\nLet's say that we have another endpoint , `get_user_by_id`, that requires a different kind of `userID`. We can't use the existing `userID` fixture, since it generates the wrong type of ID. We can solve this by writing an endpoint specific fixture.\n\n\n```python\n# fixtures.py\nimport fuzz_lightyear\n\n\n@fuzz_lightyear.register_factory('userID')\ndef create_biz_user_id(businessID):\n    return businessID + 1\n\n\n@fuzz_lightyear.register_factory('userID', endpoint_ids=['get_user_by_id'])\ndef create_user_id():\n    return 'foo'\n\n\n@fuzz_lightyear.register_factory('businessID')\ndef create_business():\n    return 1\n```\n\nWhich will produce:\n\n```bash\n...\n_________________________ user.get_user_by_id [IDORPlugin] _________________________\nRequest Sequence:\n[\n  \"curl -X GET http://localhost:5000/user/foo\"\n]\n================================== 1 failed in 1.2 seconds =================================\n```\n\nWe can combine this with nested fixtures as well, but if we specify an endpoint in a fixture, every fixture that depends on that fixture will also need to specify the endpoint.\n\n```python\n# fixtures.py\nimport fuzz_lightyear\n\n\n# We have to specify get_business_by_id here!\n@fuzz_lightyear.register_factory('userID', endpoint_ids=['get_business_by_id'])\ndef create_biz_user_id(businessID):\n    return businessID + 1\n\n\n@fuzz_lightyear.register_factory('businessID', endpoint_ids=['get_business_by_id'])\ndef create_business():\n    return 1\n```\n\nWhich will produce:\n\n```bash\n...\n_________________________ user.get_user_by_id [IDORPlugin] _________________________\nRequest Sequence:\n[\n  \"curl -X GET http://localhost:5000/biz_user/2/invoices\"\n]\n================================== 1 failed in 1.2 seconds =================================\n```\n\n\n### Authentication Fixtures\n\nWe can use fixtures to specify authentication/authorization methods to the Swagger\nspecification. This allows developers to customize the use of session cookies, or API\ntokens, depending on individual use cases.\n\nThese fixtures are required for the `IDORPlugin`. We can include an `operation_id` argument in the fixture so that the operation id is automatically passed in. Other arguments will not be fuzzed.\n\n```python\n\"\"\"\nThese values are passed into the configured request method as keyword arguments.\nCheck out https://bravado.readthedocs.io/en/stable/advanced.html#adding-request-headers\nfor more info.\n\"\"\"\nimport fuzz_lightyear\n\n\n@fuzz_lightyear.victim_account\ndef victim_factory():\n    return {\n        '_request_options': {\n            'headers': {\n                'session': 'victim_session_id',\n            },\n        }\n    }\n\n\n@fuzz_lightyear.attacker_account\ndef attacker_factory():\n    return {\n        '_request_options': {\n            'headers': {\n                'session': 'attacker_session_id',\n            }\n        }\n    }\n```\n\n### Setup Fixtures\n\nWe can use setup fixtures to specify code that we'd like to run _before_ any\ntests are run. This allows developers to setup any custom configuration or\nexternal applications the test application relies on.\n\n```python\nimport fuzz_lightyear\n\n@fuzz_lightyear.setup\ndef setup_function():\n    print(\"This code will be executed before any tests are run\")\n```\n\n### Including and excluding Swagger tags and operations\n\nWe can use fixtures to control whether fuzz-lightyear fuzzes certain parts of\nthe Swagger specification. This allows developers to only fuzz the parts\nof the specification that can be fuzzed in the test environment.\n\n```python\nimport fuzz_lightyear\n\n@fuzz_lightyear.include.tags\ndef get_tags_to_fuzz():\n    \"\"\"fuzz_lightyear will only fuzz operations from\n    these tags.\n    \"\"\"\n    return ['user', 'transactions']\n\n\n@fuzz_lightyear.exclude.operations\ndef get_operations_to_exclude():\n    \"\"\"fuzz_lightyear will not call these Swagger\n    operations.\n    \"\"\"\n    return [\n        'get_user_id',\n        'operation_doesnt_work_in_test_environment',\n    ]\n\n\n@fuzz_lightyear.exclude.non_vulnerable_operations\ndef get_non_vulnerable_operations():\n    \"\"\"fuzz_lightyear will not check these Swagger\n    operations for vulnerabilities.\n\n    This is different from `fuzz_lightyear.exclude.operations`\n    in that these operations can still be executed by the\n    fuzzer to generate request sequences, but the vulnerability\n    plugins will not verify that these operations are secure.\n    \"\"\"\n    # Accessing a user's public profile shouldn't require\n    # authentication.\n    return ['get_user_public_profile']\n```\n\n### Post-fuzz hooks\n\nSometimes factory fixtures and random fuzzing are not sufficient to\nbuild a valid request. For example, the API could have an undeclared\nrequired header, and it is unfeasible to add the header to the\nSwagger spec. In this case, we can use post-fuzz hooks to transform\nfuzzed data to a valid form.\n\n```python\n@fuzz_lightyear.hooks.post_fuzz(\n    tags='user',\n    operations='some_function',\n    rerun=True,\n)\ndef apply_nonce(\n    operation: bravado.client.CallableOperation,\n    fuzzed_data: Dict[str, Any],\n) -\u003e None:\n    \"\"\"This hook creates and adds a nonce to any request against\n    operations with the 'user' tag, and additionally to the\n    'some_function' operation.\n\n    In addition, this nonce cannot be reused by a fuzz-lightyear\n    request object, so we mark this hook is needing to be `rerun`.\n    \"\"\"\n    nonce = make_nonce()\n    fuzzed_data['nonce'] = nonce\n```\n\nNote: The order in which these hooks are run is not guaranteed.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FYelp%2Ffuzz-lightyear","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FYelp%2Ffuzz-lightyear","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FYelp%2Ffuzz-lightyear/lists"}