{"id":20863651,"url":"https://github.com/threat9/threat9-test-bed","last_synced_at":"2025-05-12T10:31:47.186Z","repository":{"id":112690973,"uuid":"103685722","full_name":"threat9/threat9-test-bed","owner":"threat9","description":null,"archived":false,"fork":false,"pushed_at":"2018-09-16T09:10:48.000Z","size":50,"stargazers_count":29,"open_issues_count":3,"forks_count":9,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-04-01T05:11:35.805Z","etag":null,"topics":["python","python-library","test","testing","testing-tools"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/threat9.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2017-09-15T17:50:28.000Z","updated_at":"2025-02-14T10:25:10.000Z","dependencies_parsed_at":"2023-09-11T19:01:01.217Z","dependency_job_id":null,"html_url":"https://github.com/threat9/threat9-test-bed","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/threat9%2Fthreat9-test-bed","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/threat9%2Fthreat9-test-bed/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/threat9%2Fthreat9-test-bed/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/threat9%2Fthreat9-test-bed/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/threat9","download_url":"https://codeload.github.com/threat9/threat9-test-bed/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253719988,"owners_count":21952937,"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":["python","python-library","test","testing","testing-tools"],"created_at":"2024-11-18T05:30:37.771Z","updated_at":"2025-05-12T10:31:47.160Z","avatar_url":"https://github.com/threat9.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# threat9-test-bed\n\n## Installation\n```bash\n$ pip install git+https://github.com/threat9/threat9-test-bed.git\n```\n\n## Test utilities\n\n### `HttpServiceMock`\n`HttpServiceMock` is a `flask` application that allows for  adding \n`unittests.mock`  as view functions. This gives us ability to setup dummy \nhttp services on demand for testing purposes.\n\n```python\nfrom threat9_test_bed.service_mocks import HttpServiceMock\n\nfrom foo import ExploitUnderTest\n\n\ndef test_exploit():\n    with HttpServiceMock(\"localhost\", 8080) as target: \n        cgi_mock = target.get_route_mock(\"/cgi-bin/cgiSrv.cgi\",\n                                         methods=[\"POST\"])\n        cgi_mock.return_value = 'foo status=\"doing\" bar'\n        check_mock = target.get_route_mock(\"/routersploit.check\",\n                                           methods=[\"GET\", \"POST\"])\n        check_mock.return_value = 'root'\n    \n        exploit = ExploitUnderTest(f'http://{target.host}', target.port)\n        assert exploit.check() is True\n        cgi_mock.assert_called_once()\n        assert check_mock.call_count == 2\n```\nIt is very convenient to use `py.test` library and it's fixture abilities. \nSuch fixture will perform setup and teardown automatically before each test. \nAll we have to do is to pass `target` as the test argument.\n```python\nimport pytest\nfrom threat9_test_bed.service_mocks import HttpServiceMock\n\nfrom foo import ExploitUnderTest\n\n\n@pytest.fixture\ndef target():\n    with HttpServiceMock(\"localhost\", 8080) as target_:\n        yield target_\n\n\ndef test_exploit(target):\n    cgi_mock = target.get_route_mock(\"/cgi-bin/cgiSrv.cgi\",\n                                     methods=[\"POST\"])\n    cgi_mock.return_value = 'foo status=\"doing\" bar'\n    check_mock = target.get_route_mock(\"/routersploit.check\",\n                                       methods=[\"GET\", \"POST\"])\n    check_mock.return_value = 'root'\n\n    exploit = ExploitUnderTest(f'http://{target.host}', target.port)\n    assert exploit.check() is True\n    cgi_mock.assert_called_once()\n    assert check_mock.call_count == 2\n```\n#### Adhoc SSL support\nYou can serve `HttpScenarioService` using adhoc SSL certificate by setting\n`ssl` keyword argument to `True`:\n\n```python\nfrom threat9_test_bed.service_mocks import HttpServiceMock\n\n@pytest.fixture\ndef trash_target():\n    with HttpServiceMock(\"127.0.0.1\", 0, ssl=True) as http_service:\n        yield http_service\n```\n\n### `HttpScenarioService`\n`HttpScenarioService` allows for creating test utilities using pre-defined\n[scenarios](#http-scenarios)\n```python\nimport pytest\n\nfrom threat9_test_bed.scenarios import HttpScenario\nfrom threat9_test_bed.service_mocks import HttpScenarioService\n\n\n@pytest.fixture(scope=\"session\")\ndef empty_target():\n    with HttpScenarioService(\"127.0.0.1\", 8081,\n                             HttpScenario.EMPTY_RESPONSE) as http_service:\n        yield http_service\n\n\n@pytest.fixture(scope=\"session\")\ndef trash_target():\n    with HttpScenarioService(\"127.0.0.1\", 8082,\n                             HttpScenario.TRASH) as http_service:\n        yield http_service\n\n```\n\n#### Adhoc SSL support\nYou can serve `HttpScenarioService` using adhoc SSL certificate by setting\n`ssl` keyword argument to `True`:\n\n```python\nfrom threat9_test_bed.service_mocks import HttpScenarioService\n\n@pytest.fixture(scope=\"session\")\ndef trash_target():\n    with HttpScenarioService(\"127.0.0.1\", 8443, HttpScenario.TRASH, \n                             ssl=True) as http_service:\n        yield http_service\n```\n\n### `TelnetServiceMock`\n`TelnetServiceMock` allows for creating test utilities using pre-defined\n[scenarios](#telnet-scenarios) as well as attaching `unittests.mock`\nas command handlers. This gives us ability to setup dummy telnet service\non demand for testing purposes.\n```python\nfrom telnetlib import Telnet\n\nimport pytest\n\nfrom threat9_test_bed.service_mocks.telnet_service_mock import TelnetServiceMock\nfrom threat9_test_bed.scenarios import TelnetScenarios\n\n\n@pytest.fixture\ndef generic_target():\n    with TelnetServiceMock(\"127.0.0.1\", 8023,\n                           TelnetScenarios.AUTHORIZED) as telnet_service:\n        yield telnet_service\n\n\ndef test_telnet(generic_target):\n    command_mock = target.get_command_mock(\"scoobeedoobeedoo\")\n    command_mock.return_value = \"Where are you?\"\n\n    tn = Telnet(target.host, target.port, timeout=5)\n    tn.expect([b\"Login: \", b\"login: \"], 5)\n    tn.write(b\"admin\" + b\"\\r\\n\")\n\n    tn.expect([b\"Password: \", b\"password\"], 5)\n    tn.write(b\"admin\" + b\"\\r\\n\")\n\n    tn.expect([b\"admin@target:~$\"], 5)\n    tn.write(b\"scoobeedoobeedoo\" + b\"\\r\\n\")\n    _, match_object, _ = tn.expect([b\"Where are you?\"], 5)\n\n    tn.close()\n\n    assert match_object\n```\n\n### Random port\nTo avoid `port` collison during tests you can tell test utilities to set\nit for you by passing `0`\n```python\n@pytest.fixture(scope=\"session\")\ndef trash_target():\n    with HttpScenarioService(\"127.0.0.1\", 0,\n                             HttpScenario.TRASH) as http_service:\n        yield http_service\n```\n\n## Services\n### `http`\n```bash\n$ test-bed http\n```\n#### `http` scenarios\n|Scenario \t        |   Behavior    |\n|-------------------|---------------|\n|`EMPTY_RESPONSE`   |   returns empty response with `200` status code                       |\n|`TRASH`            |   returns 100 characters long gibberish with `200` status code        |\n|`NOT_FOUND`        |   returns `404` status code                                           |\n|`FOUND`            |   returns _OK_ with `200` status code                                 |\n|`REDIRECT`         |   redirects you with `302` status code                                |\n|`TIMEOUT`          |   sleep the server for 1 hour which effectively times out the request |\n|`ERROR`            |   returns `500` status code                                           |                                          |\n\n```bash\n$ test-bed http --scenario TRASH\n```\n\n### `https`\n```bash\n$ test-bed https\n```\n\n#### `https` scenarios\n|Scenario \t        |   Behavior    |\n|-------------------|---------------|\n|`EMPTY_RESPONSE`   |   returns empty response with `200` status code                       |\n|`TRASH`            |   returns 100 characters long gibberish with `200` status code        |\n|`NOT_FOUND`        |   returns `404` status code                                           |\n|`FOUND`            |   returns _OK_ with `200` status code                                 |\n|`REDIRECT`         |   redirects you with `302` status code                                |\n|`TIMEOUT`          |   sleep the server for 1 hour which effectively times out the request |\n|`ERROR`            |   returns `500` status code                                           |\n\n```bash\n$ test-bed https --scenario FOUND\n```\n\n### `telnet`\nAfter successful authorization elnet service responds with random\n_Lorem ipsum..._ for every command\n```bash\n$ test-bed telnet\n```\n#### `telnet` scenarios\n|Scenario \t        |   Behavior    |\n|-------------------|---------------|\n|`AUTHORIZED`       |   Any authorization attempt ends with success         |\n|`NOT_AUTHORIZED`   |   Every authorization attempt ends with failure       |\n|`GENERIC`          |   Authorization using `admin/admin` credentials       |\n|`TIMEOUT`          |   Server hangs as soon as client has been connected   |\n\n```bash\n$ test-bed telnet --scenario GENERIC\n```\n\n## Troubleshooting\n\u003e I can't start my `https` service on port 443 due to `PermissionError`\n\nRunning services on it's default port may need extra privileges thus \nprepending command with `sudo` should do the trick e.g.\n```bash\n$ sudo test-bed https --scenario TRASH --port 443\n[2017-09-16 12:51:18,137: INFO/werkzeug]  * Running on https://127.0.0.1:443/ (Press CTRL+C to quit)\n```\nThis solution can be applied to other services and it's default ports as well.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthreat9%2Fthreat9-test-bed","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthreat9%2Fthreat9-test-bed","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthreat9%2Fthreat9-test-bed/lists"}