def f(self): patch_file = parse.PatchFile(test_filename) patch_file.getPatch() # There should only be one hunk in this file. self.assertEqual(len(patch_file.patches), 1) for hunk in patch_file.patches: result = context.context_changes(hunk) testcase = self.id().split('.')[-1] if testcase in self.errors: expected = self.errors[testcase] else: expected = { 'message': r'This patch can be applied\.', 'canApply': False } self.assertRegex(result.messages, expected['message']) filename = os.path.join(os.getcwd(), hunk.getFileName()) self.assertTrue(os.path.isfile(filename), filename) result = hunk.canApply() self.assertEqual(result, expected['canApply'], hunk)
def test_patch_CVE_2013_7348(self): patch_file = parse.PatchFile( "../vulnerableforks/patches/CVE-2013-7348.patch") patch_file.getPatch() result = context.context_changes(patch_file.patches[0]) self.assertFalse(result.status) self.assertFalse(result.is_comment)
def test_patch_CVE_2014_8481(self): patch_file = parse.PatchFile( "../vulnerableforks/patches/CVE-2014-8481.patch") patch_file.getPatch() # Look into why it fails! result = context.context_changes(patch_file.patches[0]) self.assertFalse(result.status) self.assertFalse(result.is_comment)
def test_name_change(self): patch_file = parse.PatchFile("patches/name-change.patch") patch_file.getPatch() # There should only be one hunk in this file. self.assertEqual(len(patch_file.patches), 1) for hunk in patch_file.patches: result = context.context_changes(hunk) self.assertRegex(result.messages, r'^The file .* does not exist$')
def f(self): patch_file = parse.PatchFile(test_filename) patch_file.getPatch() testcase = self.id().split('.')[-1] if testcase in self.errors: expected = self.errors[testcase] else: expected = {'result': '^$', 'success': True} patch_file.runPatch(dry_run=True) self.assertEqual(patch_file.runSuccess, expected['success']) self.assertRegex(patch_file.runResult, expected['result'])
def test_patch_CVE_2014_8172(self): patch_file = parse.PatchFile( "../vulnerableforks/patches/CVE-2014-8172.patch") patch_file.getPatch() result = context.context_changes(patch_file.patches[0]) self.assertTrue(result.status) self.assertFalse(result.is_comment) result = context.context_changes(patch_file.patches[1]) self.assertTrue(result.status) self.assertFalse(result.is_comment) result = context.context_changes(patch_file.patches[2]) self.assertTrue(result.status) self.assertFalse(result.is_comment) result = context.context_changes(patch_file.patches[3]) self.assertTrue(result.status) self.assertFalse(result.is_comment) result = context.context_changes(patch_file.patches[4]) self.assertTrue(result.status) self.assertFalse(result.is_comment) result = context.context_changes(patch_file.patches[5]) self.assertTrue(result.status) self.assertFalse(result.is_comment) result = context.context_changes(patch_file.patches[6]) self.assertTrue(result.status) self.assertFalse(result.is_comment) result = context.context_changes(patch_file.patches[7]) self.assertTrue(result.status) self.assertFalse(result.is_comment) result = context.context_changes(patch_file.patches[8]) self.assertTrue(result.status) self.assertFalse(result.is_comment) result = context.context_changes(patch_file.patches[9]) # print(result.messages) self.assertFalse(result.status) self.assertFalse(result.is_comment) result = context.context_changes(patch_file.patches[10]) self.assertFalse(result.status) self.assertFalse(result.is_comment)
def f(self): patch_file = parse.PatchFile(test_filename) patch_file.getPatch() # There should only be one hunk in this file. self.assertEqual(len(patch_file.patches), 1) for hunk in patch_file.patches: result = context.context_changes(hunk) self.assertEqual(result.messages, "No context related issues found.") for hunk in patch_file.patches: filename = os.path.join(os.getcwd(), hunk.getFileName()) self.assertTrue(os.path.isfile(filename), filename) result = hunk.canApply() self.assertTrue(result, hunk)
def test_patch_CVE_2014_9710(self): patch_file = parse.PatchFile( "../vulnerableforks/patches/CVE-2014-9710.patch") patch_file.getPatch() # Subpatch has been applied - look into why it fails! result = context.context_changes(patch_file.patches[0]) self.assertFalse(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[1]) self.assertFalse(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[2]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[3]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[4]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[5]) self.assertFalse(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[6]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[7]) self.assertFalse(result.status) self.assertFalse(result.is_comment)
def test_patch_CVE_2012_2390(self): patch_file = parse.PatchFile( "../vulnerableforks/patches/CVE-2012-2390.patch") patch_file.getPatch() result = context.context_changes(patch_file.patches[0]) self.assertFalse(result.status) self.assertFalse(result.is_comment) result = context.context_changes(patch_file.patches[1]) self.assertTrue(result.status) self.assertFalse(result.is_comment) result = context.context_changes(patch_file.patches[2]) self.assertTrue(result.status) self.assertFalse(result.is_comment) result = context.context_changes(patch_file.patches[3]) self.assertTrue(result.status) self.assertFalse(result.is_comment) result = context.context_changes(patch_file.patches[4]) self.assertTrue(result.status) self.assertFalse(result.is_comment)
def apply(pathToPatch, **kwargs): patch_file = parse.PatchFile(pathToPatch) patch_file.runPatch(reverse=kwargs['reverse'], dry_run=kwargs['dry_run']) if patch_file.runSuccess == True: print("Successfully applied") return 0 else: # Git apply failed to apply error_message = patch_file.runResult if kwargs['verbose'] > 0: print("Patch failed while it was run with git apply with error:") print(error_message) else: print("Patch failed to apply with git apply.") error_message_lines = error_message.split("\n") already_exists = set() file_not_found = set() does_not_apply = set() for line in error_message_lines: print(line) split_line = [s.strip() for s in line.split(":")] if line[0:2] == " ": pass elif split_line[0] == "error": if split_line[1].startswith("corrupt patch"): line_num = re.findall(r'\d+', split_line[1]) print( "The patch is corrupted at line %s, stop processing..." % line_num[0]) return 1 if split_line[2] == "patch does not apply": does_not_apply.add(split_line[1]) elif split_line[2] == "already exists": already_exists.add(split_line[1]) elif split_line[2] == "No such file or directory": file_not_found.add(split_line[1]) elif split_line[2] == 'skipped': # GIT does not translate the file name in this case. filename = os.path.join(findGitPrefix(split_line[1]), split_line[1]) does_not_apply.add(filename) patch_file.getPatch() # TODO: Handle file that already exists # Handling sub patches do not apply # try_all_subpatches_input = input("Would you like to try and apply all subpatches? [Y/n] ") try_all_subpatches = True successful_subpatches = [] already_applied_subpatches = [] failed_subpatches_with_matched_code = [] subpatches_without_matched_code = [] no_match_patches = [] applied_by_git_apply = [] # not_tried_subpatches = [] if not kwargs['dry_run']: see_patches = input( "We have found {} subpatches in the patch file. Would you like to see them? [Y/n] " .format(len(patch_file.patches))) see_patches = see_patches.upper() == "Y" else: see_patches = False for patch in patch_file.patches: fileName = patch.getFileName() # GIT has the behaviour where it prepends elements to the # path in order to get up to the root of the GIT # repository. This means that the output of git apply is # not going to have the same file names as the patch file # itself. We need to fix up the name we pulled from the # file so it matches the name returned by GIT. gitFileName = os.path.join(findGitPrefix(fileName), fileName) if see_patches: print("\n" + ":".join([fileName, str(patch._lineschanged[0])])) print(patch) subpatch_name = ":".join([fileName, str(patch._lineschanged[0])]) if gitFileName in file_not_found: correct_loc = check_exist.checkFileExistsElsewhere(patch) if correct_loc != None: does_not_apply.add(correct_loc) file_not_found.remove(fileName) fileName = correct_loc patch._fileName = "/" + correct_loc elif gitFileName in does_not_apply: # [1:] is used to remove the leading slash # skip_current_patch = True # if not try_all_subpatches: # print("\nFailed Subpatch : {}\n".format(subpatch_name)) # print(patch) # skip_current_patch = (input("\nWould you like to try apply this subpatch? [Y/n] ").upper() != "Y") # if not try_all_subpatches and skip_current_patch: # not_tried_subpatches.append(subpatch_name) # continue # Try applying the subpatch as normal subpatch_run_status = patch.canApply(fileName) if subpatch_run_status == precheckStatus.CAN_APPLY: successful_subpatches.append([patch, subpatch_name]) elif subpatch_run_status == precheckStatus.ALREADY_APPLIED: already_applied_subpatches.append(subpatch_name) else: context_change_obj = cc.context_changes(patch) diff_obj = context_change_obj.diff_obj context_decision = context_change_obj.status context_decision_msg = context_change_obj.messages if diff_obj and diff_obj.match_status == MatchStatus.MATCH_FOUND: match_found_helper( patch, diff_obj, already_applied_subpatches, failed_subpatches_with_matched_code, subpatch_name, context_decision, fileName, successful_subpatches, context_decision_msg, ) else: subpatches_without_matched_code.append(subpatch_name) no_match_patches.append(patch) elif gitFileName not in already_exists: applied_by_git_apply.append(subpatch_name) if len(successful_subpatches) > 0: print("-" * 70) print("{} subpatches can be applied successfully:\n".format( len(successful_subpatches))) if not kwargs['dry_run']: start_apply = input( "Would you like to see these patches and try applying them? [Y/n] " ) start_apply = start_apply.upper() == "Y" else: start_apply = True if start_apply: for patch in successful_subpatches: if kwargs['verbose'] >= 1: print() print(patch[1]) print(patch[0]) if not kwargs['dry_run']: apply_subpatch_input = input( "The above subpatch can be applied successfully. Would you like to apply? [Y/n] " ) apply_subpatch_input = apply_subpatch_input.upper( ) == "Y" else: apply_subpatch_input = True success = False if apply_subpatch_input: fileName = patch[0]._fileName patchObj = patch[0] success = patchObj.Apply(fileName, dry_run=kwargs['dry_run']) if success: if kwargs['dry_run']: print( "%s would have been successfully applied (dry run)." % patch[1]) else: print("%s successfully applied!" % patch[1]) else: print("%s Ignored" % patch[1]) if len(failed_subpatches_with_matched_code) > 0: # failed_subpatches_with_matched_code.sort() print('\n' + '-' * 70) print( "Subpatches that we can't automatically apply, but think we have found where the patch should be applied:\n" ) for (percentages, sp_name, line_number, context_decision_msg, patch) in failed_subpatches_with_matched_code: add_percent, removed_percent, context_percent = percentages print("{} - Line Number: {}".format(sp_name, line_number)) if kwargs['verbose'] >= 2: print(patch) print( " Percentage of Added Lines Applied: {:>6.2f}%".format( add_percent)) print( " Percentage of Removed Lines Applied: {:>6.2f}%".format( removed_percent)) print( " Percentage of Context Lines Found: {:>6.2f}%".format( context_percent)) print( f" Context related reason for not applying the patch: {context_decision_msg}" ) print( "\nNote that if all added lines are added and removed lines are removed, that means that the context may have changed in ways that affect the code" ) if len(applied_by_git_apply) > 0: print("\n" + "-" * 70) print("Subpatches that were applied by git apply:") print("\n".join(applied_by_git_apply)) if len(already_applied_subpatches) > 0: print("\n" + "-" * 70) print("Subpatches that were already applied:") print("\n".join(already_applied_subpatches)) if len(subpatches_without_matched_code) > 0: print("\n" + "-" * 70) print( "Subpatches that did not apply, and we could not find where the patch should be applied:" ) print("\n".join(subpatches_without_matched_code)) if len(file_not_found) > 0: print("\n" + '-' * 70) print("The following files could not be found:") print("\n".join(file_not_found)) # if len(not_tried_subpatches) > 0: # print("\nSubpatches that we did not try and apply:") # print("\n".join(not_tried_subpatches)) return 1
def test_two_changes(self): patch_file = parse.PatchFile("patches/clean/two-changes.patch") patch_file.getPatch() # There should only be one hunk in this file. self.assertEqual(len(patch_file.patches), 2)
def test_no_patch(self): patch_file = parse.PatchFile("patches/does-not-exist.patch") self.assertRaises(FileNotFoundError, patch_file.getPatch)
def test_patch_CVE_2014_9644(self): patch_file = parse.PatchFile( "../vulnerableforks/patches/CVE-2014-9644.patch") patch_file.getPatch() # Subpatch has been applied result = context.context_changes(patch_file.patches[0]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[1]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[2]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[3]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[4]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[5]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[5]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[6]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[7]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[8]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[9]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[10]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[11]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[12]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[13]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[14]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[15]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[16]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # This file has been deleted. # result = context.context_changes(patch_file.patches[17]) # self.assertTrue(result.status) # Subpatch has been applied result = context.context_changes(patch_file.patches[18]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[19]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[20]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[21]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[22]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[23]) self.assertTrue(result.status) self.assertFalse(result.is_comment)
def test_patch_CVE_2013_7281(self): patch_file = parse.PatchFile( "../vulnerableforks/patches/CVE-2013-7281.patch") patch_file.getPatch() # Patch has already been applied result = context.context_changes(patch_file.patches[0]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # No context changes result = context.context_changes(patch_file.patches[1]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # No context changes result = context.context_changes(patch_file.patches[2]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Context has changed a little result = context.context_changes(patch_file.patches[3]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Context has changed a little result = context.context_changes(patch_file.patches[4]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[5]) self.assertFalse(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[6]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[7]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[8]) self.assertTrue(result.status) self.assertFalse(result.is_comment) # Subpatch has been applied result = context.context_changes(patch_file.patches[9]) self.assertTrue(result.status) self.assertFalse(result.is_comment) result = context.context_changes(patch_file.patches[10]) self.assertTrue(result.status) self.assertFalse(result.is_comment) result = context.context_changes(patch_file.patches[11]) self.assertTrue(result.status) self.assertFalse(result.is_comment) result = context.context_changes(patch_file.patches[12]) self.assertTrue(result.status) self.assertFalse(result.is_comment) result = context.context_changes(patch_file.patches[13]) self.assertTrue(result.status) self.assertFalse(result.is_comment) result = context.context_changes(patch_file.patches[14]) self.assertTrue(result.status) self.assertFalse(result.is_comment) result = context.context_changes(patch_file.patches[15]) self.assertTrue(result.status) self.assertFalse(result.is_comment) result = context.context_changes(patch_file.patches[16]) self.assertTrue(result.status) self.assertFalse(result.is_comment)