Пример #1
0
 def test_api_random_caller(self, display=False):
     methods = restfuzz.method.load_methods(API_DIR)
     api = FakeApi()
     fuzzer = ApiRandomCaller(api, methods)
     method_called = set()
     for i in xrange(10):
         event = fuzzer.step()
         self.assertTrue(len(event.url) > 5)
         method_called.add(event.name)
         if display:
             print event
             time.sleep(0.3)
     self.assertTrue(len(method_called) > 1)
     return
Пример #2
0
 def test_api_random_caller(self, display=False):
     methods = restfuzz.method.load_methods(API_DIR)
     api = FakeApi()
     fuzzer = ApiRandomCaller(api, methods)
     method_called = set()
     for i in range(10):
         event = fuzzer.step()
         self.assertTrue(len(event.url) > 5)
         method_called.add(event.name)
         if display:
             print(event)
             time.sleep(0.3)
     self.assertTrue(len(method_called) > 1)
     return
Пример #3
0
    def test_api_random_caller_ressouce_mgmt(self):
        methods = {
            'update': restfuzz.method.Method({
                'name': 'update',
                'url': ['PUT', '%(resource_id)s.json'],
                'inputs': {'url_input': {'resource_id': {'type': 'resource', 'required': 'True'}}}
            }, base_url='http://localhost')
        }
        api = FakeApi(resp_code=404)
        fuzzer = ApiRandomCaller(api, methods, chaos_monkey=False)
        fuzzer.ig.resources['resource_id'] = ['aaaa-aa']
        event = fuzzer.step()
        # Test resources is used in url
        self.assertEquals(event.url, 'http://localhost/aaaa-aa.json')
        # Test resource is removed because api returned 404
        self.assertEquals(len(fuzzer.ig.resources), 0)

        methods = {
            'delete': restfuzz.method.Method({
                'name': 'delete',
                'url': ['DELETE', '%(resource_id)s.json'],
                'inputs': {'url_input': {'resource_id': {'type': 'resource', 'required': 'True'}}}
            }, base_url='http://localhost')
        }
        api = FakeApi(resp_code=200)
        fuzzer = ApiRandomCaller(api, methods, chaos_monkey=False)
        fuzzer.ig.resources['resource_id'] = ['aaaa-aa']
        # Test resource is removed because DELETE method called
        fuzzer.step()
        self.assertEquals(len(fuzzer.ig.resources), 0)

        methods = {
            'resource_list': restfuzz.method.Method({
                'name': 'resource_list',
                'url': ['GET', 'list.json'],
                'outputs': {'resource_id': {'type': 'resource', 'json_extract': 'lambda x: [i["id"] for i in x]'}}
            }, base_url='http://localhost'),
            'delete': restfuzz.method.Method({
                'name': 'delete',
                'url': ['DELETE', '%(resource_id)s.json'],
                'inputs': {'url_input': {'resource_id': {'type': 'resource', 'required': 'True'}}}
            }, base_url='http://localhost')
        }
        api = FakeApi(resp_content='[{"id": "41"}, {"id": "43"}]', resp_code=200)
        fuzzer = ApiRandomCaller(api, methods, chaos_monkey=False)
        fuzzer.sync_resources()
        self.assertTrue('41' in fuzzer.ig.resources['resource_id'])
Пример #4
0
def do_restfuzz():
    parser = argparse.ArgumentParser()
    parser.add_argument("--api",
                        action="append",
                        metavar="file_or_dir",
                        help="Api description",
                        required=True)
    parser.add_argument("--os-api",
                        action="append",
                        metavar="file_or_dir",
                        help="Os-Api-Ref documentation",
                        required=True)
    parser.add_argument("--base_url", help="The base url")
    parser.add_argument("--method",
                        action="append",
                        help="Only fuzz this method.")
    parser.add_argument("--token", help="X-Auth-Token to use")
    parser.add_argument("--tenant_id",
                        nargs='+',
                        default=[],
                        help="Adds tenant ids")
    parser.add_argument("--db", help="File path to store event in")
    parser.add_argument("--health",
                        help="Python module path to call after each call")
    parser.add_argument("--debug", action="store_const", const=True)
    parser.add_argument("--verbose", action="store_const", const=True)
    parser.add_argument("--seed", help="PRNG seed")
    parser.add_argument("--max_events",
                        default=1e6,
                        type=int,
                        help="Maximum number of event")
    parser.add_argument("--max_time",
                        default=3600 * 12,
                        type=int,
                        help="Maximum running time")

    args = parser.parse_args()

    def verbose(msg):
        if args.verbose:
            print(msg)

    methods = {}
    for api in args.api:
        api_methods = method.load_methods(api, args.base_url)
        methods.update(api_methods)

    for os_api in args.os_api:
        osapiref = OsApiRefFile(os_api)
        methods.update(osapiref.render())

    api = Api()
    if args.token:
        api.set_header("X-Auth-Token", args.token)

    fuzzer = ApiRandomCaller(api, methods, args.seed)
    if args.db:
        db = EventDb(open(args.db, "wb"))

    health = None
    if args.health:
        import imp
        health = imp.load_source('health', args.health).Health()

    for tenant_id in args.tenant_id:
        fuzzer.ig.resource_add("tenant_id", tenant_id)

    def refresh_keystone_token():
        token, tenant_id = os.popen("openstack token issue | "
                                    "grep ' id\|project_id' | "
                                    "awk '{ print $4 }'").read().split()
        api.set_header("X-Auth-Token", token)
        fuzzer.ig.resource_add("tenant_id", tenant_id)
        fuzzer.sync_resources()
        return token, tenant_id

    if "OS_USERNAME" in os.environ and not args.token:
        refresh_keystone_token()
    else:
        fuzzer.sync_resources()

    stats = {
        "total": 0,
        "http_code": {},
        "start_time": time.monotonic(),
        "last_speed": [0, time.monotonic()]
    }
    while (stats["total"] < args.max_events
           and (time.monotonic() - stats["start_time"]) < args.max_time):
        new_traceback = False
        event = fuzzer.step(args.debug, args.method)
        if event is None:
            continue
        if health:
            for health_event in health.check():
                if "tb_id" in health_event:
                    if "uniq_tb" in health_event:
                        new_traceback = True
                    event.tracebacks.append(health_event)
                else:
                    print("Unknown health event", health_event)

        if args.db:
            db.append(event)

        if new_traceback:
            print("\n\n%s\n" % event.render('\033[91m'))
        else:
            if event.code in (400, 404, 409):
                verbose(event.render('\033[94m'))
            elif event.code >= 200 and event.code < 300:
                verbose(event.render('\033[92m'))
            else:
                verbose(event.render('\033[91m'))

        if event.code == 401 and \
           event.json_output and \
           "auth" in event.json_output.lower() and \
           "OS_USERNAME" in os.environ:
            print("[+] Requesting a new token")
            try:
                refresh_keystone_token()
            except Exception:
                print("[+] Could not get keystone token")
                raise

        stats["http_code"][event.code] = 1 + stats["http_code"].setdefault(
            event.code, 0)
        stats["total"] += 1
        if stats["total"] % 100 == 0:
            time_now = time.monotonic()
            status_line = "\r%d sec elapsed, %dk events, " \
                          "(%03.02f events/seconds), http_code: %s " % (
                              time_now - stats["start_time"],
                              stats["total"] / 1000,
                              (stats["total"] - stats["last_speed"][0]) / (
                                  time_now - stats["last_speed"][1]),
                              stats["http_code"],
                          )
            stats["last_speed"] = [stats["total"], time_now]
            print(status_line, end='')
            stdout.flush()
    print("\nOver.")
Пример #5
0
    def test_api_random_caller_ressouce_mgmt(self):
        methods = {
            'update': restfuzz.method.Method({
                'name': 'update',
                'url': ['PUT', '%(resource_id)s.json'],
                'inputs': {'url_input': {
                    'resource_id': {'_type': 'resource', 'required': 'True'}}}
            }, base_url='http://localhost')
        }
        api = FakeApi(resp_code=404)
        fuzzer = ApiRandomCaller(api, methods, chaos_monkey=False)
        fuzzer.ig.resources['resource_id'] = ['aaaa-aa']
        event = fuzzer.step()
        # Test resources is used in url
        self.assertEquals(event.url, 'http://localhost/aaaa-aa.json')
        # Test resource is removed because api returned 404
        self.assertEquals(len(fuzzer.ig.resources), 0)

        methods = {
            'delete': restfuzz.method.Method({
                'name': 'delete',
                'url': ['DELETE', '%(resource_id)s.json'],
                'inputs': {'url_input': {
                    'resource_id': {'_type': 'resource', 'required': 'True'}}}
            }, base_url='http://localhost')
        }
        api = FakeApi(resp_code=200)
        fuzzer = ApiRandomCaller(api, methods, chaos_monkey=False)
        fuzzer.ig.resources['resource_id'] = ['aaaa-aa']
        # Test resource is removed because DELETE method called
        fuzzer.step()
        self.assertEquals(len(fuzzer.ig.resources), 0)

        methods = {
            'resource_list': restfuzz.method.Method({
                'name': 'resource_list',
                'url': ['GET', 'list.json'],
                'outputs': {'resource_id': {
                    '_type': 'resource',
                    'json_extract': 'lambda x: [i["id"] for i in x]'}}
            }, base_url='http://localhost'),
            'delete': restfuzz.method.Method({
                'name': 'delete',
                'url': ['DELETE', '%(resource_id)s.json'],
                'inputs': {'url_input': {'resource_id': {'_type': 'resource',
                                                         'required': 'True'}}}
            }, base_url='http://localhost')
        }
        api = FakeApi(resp_content='[{"id": "41"}, {"id": "43"}]',
                      resp_code=200)
        fuzzer = ApiRandomCaller(api, methods, chaos_monkey=False)
        fuzzer.sync_resources()
        self.assertTrue('41' in fuzzer.ig.resources['resource_id'])