#!/usr/bin/env python3 # # ex:ts=4:sw=4:sts=4:et # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- # # Security Response Tool Implementation # # Copyright (C) 2019 Wind River Systems # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # Theory of operation # # * This script runs quick sanity tests # * The '--init-test' option will: # * Report the host package versions # * Report errors if any required tables are empty # * Report the running instance of the SRTool # import os import sys import argparse import json # Setup: verbose = False ################################# # extract_custom_patch # def extract_custom_patch(custom_file,clean_file,patch_file,label,patcher_dir): tag_begin = "%s_EXTENSION_BEGIN" % label tag_end = "%s_EXTENSION_END" % label ret = 0 # Insure patcher working directory is ready if not patcher_dir: patcher_dir = os.path.dirname(custom_file) try: os.makedirs(patcher_dir) except: pass # Prepare defaults if not clean_file: clean_file = os.path.join(patcher_dir,os.path.basename(custom_file) + '.clean') if not patch_file: patch_file = os.path.join(patcher_dir,os.path.basename(custom_file) + '.patch') with open(custom_file, 'r') as fs: with open(clean_file, 'w') as fd: state = "find" for line in fs: #print("LINE(%s):%s" % (state,line.strip())) if state == "find": if 0 < line.find(tag_begin): #print("START:$line") state = "found" continue elif state == "found": if 0 < line.find(tag_end): #print("STOP:$line") state = "blank" continue elif state == "blank": state = "find" # if next line after stop is blank, hide that also if not line.strip(): continue # Normal line? if state != "found": fd.write('%s' % line) # Did we end cleanly? if state != "find": print("ERROR: START not STOPPED (%s)" % state) os.system("diff -u %s %s > %s" % (clean_file,custom_file,patch_file)) print("Custom File: %s" % (custom_file)) print("Clean File: %s" % (clean_file)) print("Patch File: %s" % (patch_file)) return(ret,clean_file,patch_file) ################################# # merge_original # def merge_original(custom_file,original_file,patch_file,label,patcher_dir): ret,clean_file,patch_file = extract_custom_patch(custom_file,'',patch_file,label,patcher_dir) custom_saved = os.path.join(os.path.dirname(patch_file),os.path.basename(custom_file) + '.saved') if 0 == ret: print("* Preserving custom file as '%s'" % custom_saved) cmd = "cp %s %s" % (custom_file,custom_saved) print(cmd) os.system(cmd) print("* Merging original file '%s' into custom file" % original_file) cmd = "cp %s %s" % (original_file,custom_file) print(cmd) os.system(cmd) cmd = "patch %s %s" % (custom_file,patch_file) print(cmd) ret = os.system(cmd) if 0 != ret: print("* ERROR: Merge failed, restoring previous custom file") cmd = "cp %s %s" % (custom_saved,custom_file) print(cmd) os.system(cmd) return(ret) ################################# # merge_custom # def merge_custom(custom_file,original_file,patch_file,label,patcher_dir): ret,clean_file,patch_file = extract_custom_patch(custom_file,'',patch_file,label,patcher_dir) if 0 == ret: print("== Copying clean version of custom file to '%s' ===" % original_file) cmd = "cp -f %s %s" % (clean_file, original_file) print(cmd) os.system(cmd) return(ret) ################################# # diff_original # def diff_original(custom_file,original_file,patch_file,label,patcher_dir): ret,clean_file,patch_file = extract_custom_patch(custom_file,'',patch_file,label,patcher_dir) if 0 == ret: print("== DIFF from '%s' to clean version of custom file ===" % original_file) os.system("diff -u %s %s" % (original_file,clean_file)) return(ret) ################################# # load_json_list # def load_json_list(json_file): label = 'CUSTOM' patcher_dir = '' file_list = [] with open(json_file) as json_data: dct = json.load(json_data) if 'label' in dct: label = dct['label'] if 'patcher_dir' in dct: patcher_dir = dct['patcher_dir'] if 'patch_set' in dct: for patch in dct['patch_set']: if 'DISABLE' in patch['options']: continue file_list.append([patch['custom'],patch['original'],patch['patch'],patch['options']]) return file_list,patcher_dir,label ################################# # main loop # def main(argv): global verbose # setup parser = argparse.ArgumentParser(description='srtool_sanity_test.py: SRTool common sanity tests') parser.add_argument('--merge-original', '-O', action='store_const', const='merge_original', dest='command', help='Copy the (updated) original file, merge the custom patches') parser.add_argument('--merge-custom', '-C', action='store_const', const='merge_custom', dest='command', help='Copy the (updated) file to the original, without custom patches') parser.add_argument('--custom', '-c', help='Custom file') parser.add_argument('--original', '-o', help='Original file') parser.add_argument('--label', '-l', help='Custom label tag (default="CUSTOM")') parser.add_argument('--json', '-j', help='Use JSON file for file list') parser.add_argument('--extract-custom-patch', '-e', action='store_const', const='extract_custom_patch', dest='command', help='Extract a patch of the custom content') parser.add_argument('--apply-custom-patch', '-a', action='store_const', const='extract_custom_patch', dest='command', help='Apply the custom patch to the (reset) custom file') parser.add_argument('--patch', '-p', help='Patch file') parser.add_argument('--diff-original', '-d', action='store_const', const='diff_original', dest='command', help='Show how the file compares to original, ignoring custom patches') parser.add_argument('--verbose', '-v', action='store_true', dest='verbose', help='Debugging: verbose output') args = parser.parse_args() def validate_file(filename,msg): if not (filename or os.path.isfile(filename)): print("ERROR: %s file not found '%s'" % (msg,filename)) exit(1) # Extract provided values verbose = args.verbose file_list = [] if args.json: validate_file(args.json,"JSON") file_list,patcher_dir,label = load_json_list(args.json) else: custom_file = '' if args.custom: custom_file = args.custom original_file = '' if args.original: original_file = args.original patch_file = '' if args.patch: patch_file = args.patch options = '' file_list.append([custom_file,original_file,patch_file,options]) label = 'CUSTOM' if args.label: label = args.label patcher_dir = os.path.join(os.path.dirname(custom_file),'patcher') ret = 0 for custom_file,original_file,patch_file,options in file_list: #print("PATCH_FILE:%s,%s,%s,%s,%s" % (custom_file,original_file,patch_file,options,patcher_dir)) if 'merge_original' == args.command: validate_file(custom_file,'Custom') validate_file(original_file,'Original') ret = merge_original(custom_file,original_file,patch_file,label,patcher_dir) elif 'merge_custom' == args.command: validate_file(custom_file,'Custom') validate_file(original_file,'Original') ret = merge_custom(custom_file,original_file,patch_file,label,patcher_dir) elif 'extract_custom_patch' == args.command: validate_file(custom_file,'Custom') ret,clean_file,patch_file = extract_custom_patch(custom_file,'',patch_file,label,patcher_dir) elif 'diff_original' == args.command: validate_file(custom_file,'Custom') validate_file(original_file,'Original') ret = diff_original(custom_file,original_file,patch_file,label,patcher_dir) else: print("Command not found '%s'" % args.command) ret = 1 if 0 != ret: exit(ret) exit(ret) if __name__ == '__main__': srtool_basepath = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))) main(sys.argv[1:])