コード例 #1
0
    def test_is_multi_cbn(self):
        r0 = Round.create(num=0)
        our_team = Team.create(name=Team.OUR_NAME)
        # CS single binary
        cs = ChallengeSet.create(name="single")
        cs.rounds = [r0]
        cbn = ChallengeBinaryNode.create(name="foo", cs=cs, blob="aaa1")
        # CS multi binary
        cs_multi = ChallengeSet.create(name="multi")
        cs_multi.rounds = [r0]
        cbn1 = ChallengeBinaryNode.create(name="foo1",
                                          cs=cs_multi,
                                          blob="aaa2")
        cbn2 = ChallengeBinaryNode.create(name="foo2",
                                          cs=cs_multi,
                                          blob="aaa3")
        # create fielding entries
        ChallengeSetFielding.create(cs=cs,
                                    cbns=[cbn],
                                    team=our_team,
                                    available_round=r0)
        ChallengeSetFielding.create(cs=cs_multi,
                                    cbns=[cbn1, cbn2],
                                    team=our_team,
                                    available_round=r0)

        assert_false(cs.is_multi_cbn)
        assert_true(cs_multi.is_multi_cbn)
コード例 #2
0
ファイル: crs.py プロジェクト: firebitsbr/manual-interaction
def create_round(args):
    if not len(args.challenge_sets):
        fielded = [cs.name for cs in CS.fielded_in_round()]
    else:
        fielded = args.challenge_sets
    new_round = Round.create(
        num=args.number if args.number is not None else Round.current_round() +
        1)

    for f in fielded:
        cs = CS.select().where(CS.name == f).get()
        CSF.create(cs=cs, team=Team.get_our(), available_round=new_round)
コード例 #3
0
    def test_fielded_in_round(self):
        now = datetime.now()
        r1 = Round.create(num=0)
        r2 = Round.create(num=1)
        cs1 = ChallengeSet.create(name="foo")
        cs1.rounds = [r1, r2]
        cs2 = ChallengeSet.create(name="bar")
        cs2.rounds = [r1]

        assert_equals(len(ChallengeSet.fielded_in_round(r1)), 2)
        assert_in(cs1, ChallengeSet.fielded_in_round(r1))
        assert_in(cs2, ChallengeSet.fielded_in_round(r1))
コード例 #4
0
ファイル: crs.py プロジェクト: firebitsbr/manual-interaction
def add_test_or_crash(args):
    p = subprocess.Popen([shellphish_qemu.qemu_path('cgc-base'), args.cb],
                         stdin=subprocess.PIPE)
    p.poll()
    test = ""

    if args.batch:
        test = sys.stdin.read()
        p.communicate(test)
    else:
        try:
            while p.returncode is None:
                r, _, _ = select([sys.stdin], [], [], 0.05)
                if r is not None:
                    b = sys.stdin.read(1)
                    test += b
                    p.stdin.write(b)

                p.poll()
        except KeyboardInterrupt:
            p.returncode = 0
        except IOError:
            p.returncode = 1

    if p.returncode == 0:
        print "Finished test, inserting now..."
        cs = CS.select().where(CS.name == args.cs)

        # first we have to make a fake job
        job = Job.create(cs=cs,
                         completed_at=datetime.datetime.now(),
                         worker="garbage")

        Test.create(cs=cs, job=job, blob=test)
        print "Test inserted!"
    else:
        print "Found a crash, inserting now..."

        qc = QuickCrash(args.cb, test)
        print "appears to be of type " + qc.kind

        print "cs = " + args.cs
        cs = CS.select().where(CS.name == args.cs)

        # first we have to make a fake job
        job = Job.create(cs=cs,
                         completed_at=datetime.datetime.now(),
                         worker="garbage")

        Crash.create(cs=cs, job=job, blob=test, kind=qc.kind)
        print "Crash inserted!"
コード例 #5
0
ファイル: test_job.py プロジェクト: firebitsbr/farnsworth
    def test_get_or_create(self):
        cs = ChallengeSet.create(name="foo")
        cbn = ChallengeBinaryNode.create(name="foo", cs=cs, blob="aaa")
        job1, job1_created = RexJob.get_or_create(cbn=cbn, payload={'something': 'xxx'})
        job2, job2_created = AFLJob.get_or_create(cbn=cbn, payload={'something': 'xxx'})

        assert_not_equal(job1.id, job2.id)
        assert_true(job1_created)
        assert_true(job2_created)

        Job.delete().execute()
        ChallengeBinaryNode.delete().execute()
        ChallengeSet.delete().execute()
        Job._meta.database.commit()
コード例 #6
0
ファイル: crs.py プロジェクト: firebitsbr/manual-interaction
def list_patches(args):
    wanted_fields = (CBN.name, CBN.size, CBN.sha256, CBN.patch_type,
                     CBN.is_blacklisted)
    q = CBN.select(*wanted_fields)

    if args.cs is not None:
        cs = CS.select().where(CS.name == args.cs)
        for patch in q.where(CBN.cs == cs):
            print format_patch(patch)
    else:
        for cs in CS.fielded_in_round():
            print "CS {}:".format(cs.name)
            for patch in q.where(CBN.cs == cs):
                print format_patch(patch)
            print ""
コード例 #7
0
    def test_submitted_and_unsubmitted_patches(self):
        r0 = Round.create(num=0)
        team = Team.create(name=Team.OUR_NAME)
        cs = ChallengeSet.create(name="foo")
        cs.rounds = [r0]
        cbn = ChallengeBinaryNode.create(name="cbn", cs=cs, blob="aaa1")
        patchtype1 = PatchType.create(name="PatchType1",
                                      functionality_risk=0,
                                      exploitability=0)
        patchtype2 = PatchType.create(name="PatchType2",
                                      functionality_risk=0,
                                      exploitability=0)

        patch1 = ChallengeBinaryNode.create(name="patch1",
                                            patch_type=patchtype1,
                                            cs=cs,
                                            root=cbn,
                                            blob="aaa2")
        patch2 = ChallengeBinaryNode.create(name="patch2",
                                            patch_type=patchtype2,
                                            cs=cs,
                                            root=cbn,
                                            blob="aaa3")

        assert_equals(len(cbn.unsubmitted_patches), 2)
        assert_in(patch1, cbn.unsubmitted_patches)
        assert_in(patch2, cbn.unsubmitted_patches)
        assert_equals(len(cbn.submitted_patches), 0)

        ChallengeSetFielding.create_or_update_submission(team=team,
                                                         cbns=[patch1, patch2],
                                                         round=r0)
        assert_equals(len(cbn.submitted_patches), 2)
        assert_equals(len(cbn.unsubmitted_patches), 0)
コード例 #8
0
    def test_get_or_create(self):
        r = Round.create(num=0)
        cs = ChallengeSet.create(name="foo")
        cbn1 = ChallengeBinaryNode.create(name="foo1", cs=cs, blob="aaa1")
        cbn2 = ChallengeBinaryNode.create(name="foo1", cs=cs, blob="aaa2")
        ids = IDSRule.create(cs=cs, rules="aaa", sha256="sum")

        cbl1, crtd1 = CSSubmissionCable.get_or_create(cs=cs,
                                                      ids=ids,
                                                      cbns=[cbn1],
                                                      round=r)
        assert_true(crtd1)

        cbl2, crtd2 = CSSubmissionCable.get_or_create(cs=cs,
                                                      ids=ids,
                                                      cbns=[cbn1],
                                                      round=r)
        assert_false(crtd2)
        assert_equals(cbl1.id, cbl2.id)

        cbl3, crtd3 = CSSubmissionCable.get_or_create(cs=cs,
                                                      ids=ids,
                                                      cbns=[cbn1, cbn2],
                                                      round=r)
        assert_true(crtd3)

        cbl4, crtd4 = CSSubmissionCable.get_or_create(cs=cs,
                                                      ids=ids,
                                                      cbns=[cbn1, cbn2],
                                                      round=r)
        assert_false(crtd4)
        assert_equals(cbl3.id, cbl4.id)
コード例 #9
0
    def test_unsubmitted_exploits(self):
        r1 = Round.create(num=0)
        team = Team.create(name=Team.OUR_NAME)
        cs = ChallengeSet.create(name="foo")
        cs.rounds = [r1]
        job = RexJob.create(cs=cs)
        pov1 = Exploit.create(cs=cs,
                              job=job,
                              pov_type='type1',
                              exploitation_method='rop',
                              blob="exploit",
                              c_code="exploit it")
        pov2 = Exploit.create(cs=cs,
                              job=job,
                              pov_type='type2',
                              exploitation_method='rop',
                              blob="exploit",
                              c_code="exploit it")

        assert_equals(len(cs.unsubmitted_exploits), 2)
        assert_in(pov1, cs.unsubmitted_exploits)
        assert_in(pov2, cs.unsubmitted_exploits)

        pov1.submit_to(team, 10)
        assert_equals(len(cs.unsubmitted_exploits), 1)
        assert_not_in(pov1, cs.unsubmitted_exploits)
        assert_in(pov2, cs.unsubmitted_exploits)

        pov2.submit_to(team, 10)
        assert_equals(len(cs.unsubmitted_exploits), 0)
        assert_not_in(pov1, cs.unsubmitted_exploits)
        assert_not_in(pov2, cs.unsubmitted_exploits)
コード例 #10
0
 def test_cbns_by_patch_type(self):
     cs = ChallengeSet.create(name="foo")
     cbn = ChallengeBinaryNode.create(name="foo", cs=cs, blob="aaa")
     patch0 = PatchType.create(
         name="patch0",
         functionality_risk=0,
         exploitability=1,
     )
     patch1 = PatchType.create(
         name="patch1",
         functionality_risk=0,
         exploitability=1,
     )
     cbn1 = ChallengeBinaryNode.create(name="foo1",
                                       cs=cs,
                                       patch_type=patch0,
                                       blob="aaa1")
     cbn2 = ChallengeBinaryNode.create(name="foo2",
                                       cs=cs,
                                       patch_type=patch0,
                                       blob="aaa2")
     cbn3 = ChallengeBinaryNode.create(name="foo3",
                                       cs=cs,
                                       patch_type=patch1,
                                       blob="aaa3")
     assert_in(patch0, cs.cbns_by_patch_type().keys())
     assert_in(patch1, cs.cbns_by_patch_type().keys())
     assert_in(cbn1, cs.cbns_by_patch_type()[patch0])
     assert_in(cbn2, cs.cbns_by_patch_type()[patch0])
     assert_in(cbn3, cs.cbns_by_patch_type()[patch1])
コード例 #11
0
ファイル: feedback.py プロジェクト: firebitsbr/ambassador
    def run(self):
        """A run() method. Happy now pylint?"""
        cgc_polls = self._get_feedback('poll')
        Feedback.update_or_create(self._round,
                                  polls=cgc_polls,
                                  povs=self._get_feedback('pov'),
                                  cbs=self._get_feedback('cb'))

        fielded = {
            c.name: c
            for c in ChallengeSet.fielded_in_round(self._round)
        }
        for c in cgc_polls:
            cs = fielded[c['csid']]

            fieldings = cs.fieldings.where(
                (ChallengeSetFielding.available_round == self._round)
                & (ChallengeSetFielding.team == Team.get_our()))
            if fieldings:
                fielding = fieldings.get()
                if fielding.poll_feedback is None:
                    fnct, perf = c['functionality'], c['performance']
                    pf = PollFeedback.create(
                        cs=cs,
                        round=self._round,
                        success=float(fnct['success']) / 100.0,
                        timeout=float(fnct['timeout']) / 100.0,
                        connect=float(fnct['connect']) / 100.0,
                        function=float(fnct['function']) / 100.0,
                        time_overhead=float(perf['time']) / 100.0 - 1,
                        memory_overhead=float(perf['memory']) / 100.0 - 1)
                    fielding.poll_feedback = pf
                    fielding.save()
コード例 #12
0
ファイル: __init__.py プロジェクト: firebitsbr/meister
    def challenge_sets(self, round_=None):
        """Return the list of challenge sets that are active in a round.

        :keyword round_: The round number for which the binaries should be
                         returned (default: current round).
        """
        return ChallengeSet.fielded_in_round(round_)
コード例 #13
0
 def get_cs_from_id(target_cs_id):
     """
         Get ChallengeSet for given id
     :param target_cs_id: id for which ChallengeSet need to be fetched.
     :return:  ChallengeSet
     """
     return ChallengeSet.get(ChallengeSet.id == target_cs_id)
コード例 #14
0
    def test_has_submissions_in_round(self):
        r0 = Round.create(num=0)
        r1 = Round.create(num=1)
        cs = ChallengeSet.create(name="foo")
        cbn = ChallengeBinaryNode.create(name="foo1", cs=cs, blob="aaa")
        our_team = Team.create(name=Team.OUR_NAME)
        other_team = Team.create(name="enemy")

        ChallengeSetFielding.create(cs=cs,
                                    cbns=[cbn],
                                    team=our_team,
                                    submission_round=r1)
        assert_false(cs.has_submissions_in_round(r0))
        assert_true(cs.has_submissions_in_round(r1))

        ChallengeSetFielding.create(cs=cs,
                                    cbns=[cbn],
                                    team=other_team,
                                    submission_round=r0)
        assert_false(cs.has_submissions_in_round(r0))

        ChallengeSetFielding.create(cs=cs,
                                    cbns=[cbn],
                                    team=our_team,
                                    submission_round=r0)
        assert_true(cs.has_submissions_in_round(r0))
コード例 #15
0
    def test_all_tests_for_this_cs(self):
        cs = ChallengeSet.create(name="foo")
        job = AFLJob.create(cs=cs)
        test1 = farnsworth.models.Test.create(cs=cs, job=job, blob="test1")
        test2 = farnsworth.models.Test.create(cs=cs, job=job, blob="test2")

        assert_equals(len(cs.tests), 2)
コード例 #16
0
ファイル: crs.py プロジェクト: firebitsbr/manual-interaction
def upload_cbns(args):
    cs = CS.select().where(CS.name == args.cs)

    patch_type, _ = PT.get_or_create(name="manual",
                                     functionality_risk=1.0,
                                     exploitability=0.0)

    cbns = []
    for patched_file in args.patched_files:
        with open(patched_file) as f:
            content = f.read()
        try:
            cbn = CBN.create(cs=cs,
                             blob=content,
                             name=patched_file,
                             patch_type=patch_type)
        except peewee.IntegrityError:
            print "CBN already exists. Fetching."
            cbn = CBN.select().where(CBN.name == args.patched_file,
                                     CBN.cs == cs, CBN.blob == content)
        cbns.append(cbn)

    if args.field:
        ids = IDSRule.get_or_create(cs=target_cs, rules='')
        CSSubmissionCable.get_or_create(cs=target_cs,
                                        cbns=cbns,
                                        ids=ids,
                                        round=Round.current_round())

    if args.eF is not None and args.eT is not None and args.eM is not None and cbn.patch_type is not None:
        perf_score = {
            'score': {
                'ref': {
                    'task_clock': 1.0,
                    'rss': 1.0,
                    'flt': 1.0,
                    'file_size': 1.0
                },
                'rep': {
                    'task_clock': args.eT,
                    'file_size': args.size_overhead,
                    'rss': args.eM,
                    'flt': args.eM,
                }
            }
        }
        PS.create(cs=cs,
                  perf_score=perf_score,
                  patch_type=cbn.patch_type,
                  has_failed_polls=args.eF != 0,
                  failed_polls=args.eF)
    if args.pT is not None and args.pM is not None and args.pS is not None:
        csf.poll_feedback = PF.create(cs=cs,
                                      round_id=Round.current_round().id,
                                      success=args.pS,
                                      time_overhead=args.pT,
                                      memory_overhead=args.pM,
                                      size_overhead=args.size_overhead)
        csf.save()
コード例 #17
0
 def test_cs_name_and_sha256_uniqueness(self):
     cs1 = ChallengeSet.create(name="foo")
     cs2 = ChallengeSet.create(name="bar")
     # first binary is ok
     ChallengeBinaryNode.create(name="test1", cs=cs1, blob=BLOB)
     # same binary with different name is ok
     ChallengeBinaryNode.create(name="test2", cs=cs1, blob=BLOB)
     # same binary with different cs is ok
     ChallengeBinaryNode.create(name="test1", cs=cs2, blob=BLOB)
     # same cs and name but different binary is ok
     ChallengeBinaryNode.create(name="test1", cs=cs2, blob=BLOB2)
     # same cs, name and binary raises error
     assert_raises(IntegrityError,
                   ChallengeBinaryNode.create,
                   name="test1",
                   cs=cs1,
                   blob=BLOB)
コード例 #18
0
    def test_get_by_sha256_or_create(self):
        cs = ChallengeSet.create(name="foo")

        assert_equals(len(IDSRule.all()), 0)
        ids1 = IDSRule.get_by_sha256_or_create(rules="aaa", cs=cs)
        ids2 = IDSRule.get_by_sha256_or_create(rules="aaa", cs=cs)
        assert_equals(ids1, ids2)
        assert_equals(len(IDSRule.all()), 1)
コード例 #19
0
    def test_most_recent(self):
        cs = ChallengeSet.create(name="foo")
        cs2 = ChallengeSet.create(name="bar")
        team = Team.create(name="opponent")
        exploit = Exploit.create(cs=cs,
                                 job=RexJob.create(),
                                 pov_type="type1",
                                 blob="abc",
                                 c_code="exploit it")
        exploit2 = Exploit.create(cs=cs2,
                                  job=RexJob.create(),
                                  pov_type="type1",
                                  blob="def",
                                  c_code="delfino")
        Round.create(num=0)

        cable = ExploitSubmissionCable.create(team=team,
                                              cs=cs,
                                              exploit=exploit,
                                              throws=10,
                                              round=Round.current_round())
        cable2 = ExploitSubmissionCable.create(team=team,
                                               cs=cs2,
                                               exploit=exploit2,
                                               throws=10,
                                               round=Round.current_round())
        assert_equals(len(ExploitSubmissionCable.most_recent()), 2)
        assert_items_equal(ExploitSubmissionCable.most_recent(),
                           [cable, cable2])

        # assert we get back only the most recent exploit
        r1 = Round.create(num=1)
        new_exploit = Exploit.create(cs=cs,
                                     job=RexJob.create(),
                                     pov_type="type2",
                                     blob="def",
                                     c_code="don't exploit it")
        new_cable = ExploitSubmissionCable.create(team=team,
                                                  cs=cs,
                                                  exploit=new_exploit,
                                                  throws=10,
                                                  round=r1)
        assert_equals(len(ExploitSubmissionCable.most_recent()), 2)
        assert_items_equal(ExploitSubmissionCable.most_recent(),
                           [new_cable, cable2])
コード例 #20
0
    def test_undrilled_tests_for_cs(self):
        cs = ChallengeSet.create(name="foo")
        job = AFLJob.create(cs=cs)
        new_test = farnsworth.models.Test.create(cs=cs,
                                                 job=job,
                                                 blob="crash",
                                                 drilled=False)

        assert_true(len(cs.undrilled_tests), 1)
コード例 #21
0
    def test_found_crash_for_cs(self):
        cs = ChallengeSet.create(name="foo")
        job = AFLJob.create(cs=cs)
        crash = farnsworth.models.Crash.create(cs=cs,
                                               job=job,
                                               blob="crash",
                                               crash_pc=0x41414141)

        assert_true(cs.found_crash)
コード例 #22
0
    def test_has_type1(self):
        from farnsworth.models.pov_test_result import PovTestResult
        pov_type = 'type1'
        cs = ChallengeSet.create(name="foo")
        job = AFLJob.create(cs=cs)
        Exploit.create(cs=cs,
                       job=job,
                       pov_type=pov_type,
                       blob=BLOB,
                       c_code="code",
                       reliability=0)
        assert_false(cs.has_type1)

        Exploit.create(cs=cs,
                       job=job,
                       pov_type=pov_type,
                       blob=BLOB,
                       c_code="code",
                       reliability=1)
        assert_true(cs.has_type1)

        # if not first condition is not met
        r2 = Round.create(num=2)
        cs2 = ChallengeSet.create(name="bar")
        job2 = AFLJob.create(cs=cs2)
        cbn2 = ChallengeBinaryNode.create(name="bar", cs=cs2, blob="aaa")
        team2 = Team.create(name=Team.OUR_NAME)
        exploit2 = Exploit.create(cs=cs2,
                                  job=job2,
                                  pov_type=pov_type,
                                  blob=BLOB,
                                  c_code="code",
                                  reliability=0)
        csf2 = ChallengeSetFielding.create_or_update_available(team=team2,
                                                               cbn=cbn2,
                                                               round=r2)
        pov_result2 = PovTestResult.create(exploit=exploit2,
                                           cs_fielding=csf2,
                                           num_success=0)
        assert_false(cs2.has_type1)

        pov_result2.num_success = 10
        pov_result2.save()
        assert_true(cs2.has_type1)
コード例 #23
0
ファイル: __init__.py プロジェクト: firebitsbr/meister
    def cbns(self, round_=None):
        """Return the list of binaries that are active in a round.

        :keyword round_: The round instance for which the binaries should be
                         returned (default: current round).
        """
        for cs in ChallengeSet.fielded_in_round(round_):
            for cbn in cs.cbns_original:
                LOG.debug("Found cbid: %s", cbn.name)
                yield cbn
コード例 #24
0
    def test_all(self):
        r1 = Round.create(num=0)
        cs = ChallengeSet.create(name="foo")
        cs.rounds = [r1]
        cbn1 = ChallengeBinaryNode.create(name="foo", cs=cs, blob="blob1")
        cbn2 = ChallengeBinaryNode.create(name="bar", cs=cs, blob="blob2")

        assert_equals(len(ChallengeBinaryNode.all()), 2)
        assert_equals(ChallengeBinaryNode.all()[0], cbn1)
        assert_equals(ChallengeBinaryNode.all()[1], cbn2)
コード例 #25
0
    def test_get_or_create(self):
        assert_equals(len(ChallengeSet.select()), 0)
        r1 = Round.create(num=0)
        cs1, _ = ChallengeSet.get_or_create(name="foo")
        cs2, _ = ChallengeSet.get_or_create(name="foo")
        cs3, _ = ChallengeSet.get_or_create(name="bar")

        for cs in [cs1, cs2, cs3]:
            cs.rounds = [r1.id]

        assert_equals(len(ChallengeSet.select()), 2)
        assert_equals(cs2.id, cs1.id)
        assert_not_equals(cs3.id, cs1.id)

        cs3.delete_instance(recursive=True)
        cs2.delete_instance(recursive=True)
        cs1.delete_instance(recursive=True)
        r1.delete_instance(recursive=True)
        ChallengeSet._meta.database.commit()
コード例 #26
0
ファイル: crs.py プロジェクト: firebitsbr/manual-interaction
def insert_test(args):
    cs = CS.select().where(CS.name == args.cs)
    test = args.test.read()

    # first we have to make a fake job
    job = Job.create(cs=cs,
                     completed_at=datetime.datetime.now(),
                     worker="garbage")

    Test.create(cs=cs, job=job, blob=test)
コード例 #27
0
ファイル: cb.py プロジェクト: firebitsbr/scriba
    def run(self, current_round=None, random_submit=False):  # pylint:disable=no-self-use,unused-argument
        return
        if current_round == 0:
            return

        # As ambassador will take care of actually submitting the binary.
        for cs in ChallengeSet.fielded_in_round():
            #if not self.should_submit(cs):
            #   continue
            CBSubmitter.process_patch_submission(cs)
コード例 #28
0
    def test_pov_type(self):
        cs = ChallengeSet.create(name="foo")
        job = AFLJob.create(cs=cs)
        exploit = Exploit.create(cs=cs, job=job, pov_type="type1", blob="exploit",
                                 c_code="exploit it")
        assert_equals(exploit.pov_type, "type1")

        def invalid_pov_type():
            Exploit.create(cs=cs, job=job, pov_type="bar")
        assert_raises(Exception, invalid_pov_type)
コード例 #29
0
ファイル: crs.py プロジェクト: firebitsbr/manual-interaction
def field_cbns(args):
    cs = CS.select().where(CS.name == args.cs)
    # i know i could use one query for this, but then we might get duplicate CBNs and more than we want
    cbns = [
        CBN.get(CBN.cs == cs, CBN.sha256 == sha) for sha in args.patched_shas
    ]
    ids, _ = IDSRule.get_or_create(cs=cs, rules='')
    CSSubmissionCable.get_or_create(cs=cs,
                                    cbns=cbns,
                                    ids=ids,
                                    round=Round.current_round())
コード例 #30
0
ファイル: crs.py プロジェクト: firebitsbr/manual-interaction
def insert_crash(args):
    cs = CS.select().where(CS.name == args.cs)
    test = args.crash.read()
    kind = args.crash_kind

    # first we have to make a fake job
    job = Job.create(cs=cs,
                     completed_at=datetime.datetime.now(),
                     worker="garbage")

    Crash.create(cs=cs, kind=kind, job=job, blob=test)