def test_preserve_old_blockiness(self, quiet_logger, eyamldata_f, old_eyaml_keys, yaml_path, newval, eoformat, yvformat): processor = EYAMLProcessor(quiet_logger, eyamldata_f, privatekey=old_eyaml_keys[0], publickey=old_eyaml_keys[1]) processor.set_eyaml_value(yaml_path, newval, output=eoformat) encvalue = None encformat = YAMLValueFormats.DEFAULT for encnode in processor.get_nodes(yaml_path): encvalue = encnode encformat = YAMLValueFormats.from_node(encvalue) break assert EYAMLProcessor.is_eyaml_value(encvalue) and yvformat == encformat
def print_results(args: Any, processor: EYAMLProcessor, yaml_file: str, yaml_paths: List[Tuple[str, YAMLPath]], document_index: int) -> None: """Dump search results to STDOUT with optional and dynamic formatting.""" in_expressions = len(args.search) print_file_path = not args.nofile print_expression = in_expressions > 1 and not args.noexpression print_yaml_path = not args.noyamlpath print_value = args.values buffers = [ ": " if print_file_path or print_expression and (print_yaml_path or print_value) else "", ": " if print_yaml_path and print_value else "", ] for entry in yaml_paths: expression, result = entry resline = "" if print_file_path: display_file_name = ("STDIN" if yaml_file.strip() == "-" else yaml_file) resline += "{}/{}".format(display_file_name, document_index) if print_expression: resline += "[{}]".format(expression) resline += buffers[0] if print_yaml_path: if args.noescape: use_flash = args.pathsep is PathSeperators.FSLASH seglines = [] join_mark = "/" if use_flash else "." path_prefix = "/" if use_flash else "" for (_, segment) in result.escaped: seglines.append(str(segment)) resline += "{}{}".format(path_prefix, join_mark.join(seglines)) else: resline += "{}".format(result) resline += buffers[1] if print_value: # These results can have only one match, but make sure lest the # output become messy. for node_coordinate in processor.get_nodes(result, mustexist=True): node = node_coordinate.node if isinstance(node, (dict, list, CommentedSet)): resline += "{}".format( json.dumps(Parsers.jsonify_yaml_data(node))) else: resline += "{}".format(str(node).replace("\n", r"\n")) break print(resline)
def test_happy_set_eyaml_value(self, quiet_logger, eyamldata_f, old_eyaml_keys, yaml_path, compare, mustexist, output_format): processor = EYAMLProcessor(quiet_logger, eyamldata_f, privatekey=old_eyaml_keys[0], publickey=old_eyaml_keys[1]) # Set the test value processor.set_eyaml_value(yaml_path, compare, output_format, mustexist) # Ensure the new value is encrypted encvalue = None for encnode in processor.get_nodes(yaml_path): encvalue = encnode break assert EYAMLProcessor.is_eyaml_value(encvalue)
def print_results(args: Any, processor: EYAMLProcessor, yaml_file: str, yaml_paths: List[Tuple[str, YAMLPath]]) -> None: """ Dumps the search results to STDOUT with optional and dynamic formatting. """ in_file_count = len(args.yaml_files) in_expressions = len(args.search) print_file_path = in_file_count > 1 and not args.nofile print_expression = in_expressions > 1 and not args.noexpression print_yaml_path = not args.noyamlpath print_value = args.values buffers = [ ": " if print_file_path or print_expression and (print_yaml_path or print_value) else "", ": " if print_yaml_path and print_value else "", ] for entry in yaml_paths: expression, result = entry resline = "" if print_file_path: resline += "{}".format(yaml_file) if print_expression: resline += "[{}]".format(expression) resline += buffers[0] if print_yaml_path: resline += "{}".format(result) resline += buffers[1] if print_value: # These results can have only one match, but make sure lest the # output become messy. for node in processor.get_nodes(result, mustexist=True): if isinstance(node, (dict, list)): resline += "{}".format(json.dumps(node)) else: resline += "{}".format(str(node).replace("\n", r"\n")) break print(resline)
def main(): """Main code.""" # Process any command-line arguments args = processcli() log = ConsolePrinter(args) validateargs(args, log) processor = EYAMLProcessor(log, None, binary=args.eyaml) # Prep the YAML parser yaml = Parsers.get_yaml_editor() # Process the input file(s) in_file_count = len(args.yaml_files) exit_state = 0 for yaml_file in args.yaml_files: file_changed = False backup_file = yaml_file + ".bak" seen_anchors = [] # Each YAML_FILE must actually be a file if not isfile(yaml_file): log.error("Not a file: {}".format(yaml_file)) exit_state = 2 continue # Don't bother with the file change update when there's only one input # file. if in_file_count > 1: log.info("Processing {}...".format(yaml_file)) # Try to open the file (yaml_data, doc_loaded) = Parsers.get_yaml_data(yaml, log, yaml_file) if not doc_loaded: # An error message has already been logged exit_state = 3 continue # Process all EYAML values processor.data = yaml_data for yaml_path in processor.find_eyaml_paths(): # Use ::get_nodes() instead of ::get_eyaml_values() here in order # to ignore values that have already been rotated via their # Anchors. for node_coordinate in processor.get_nodes(yaml_path, mustexist=True): # Ignore values which are Aliases for those already decrypted node = node_coordinate.node anchor_name = Anchors.get_node_anchor(node) if anchor_name is not None: if anchor_name in seen_anchors: continue seen_anchors.append(anchor_name) log.verbose("Decrypting value(s) at {}.".format(yaml_path)) processor.publickey = args.oldpublickey processor.privatekey = args.oldprivatekey try: txtval = processor.decrypt_eyaml(node) except EYAMLCommandException as ex: log.error(ex) exit_state = 3 continue # Prefer block (folded) values unless the original YAML value # was already a massivly long (string) line. output = EYAMLOutputFormats.BLOCK if not isinstance(node, FoldedScalarString): output = EYAMLOutputFormats.STRING # Re-encrypt the value with new EYAML keys processor.publickey = args.newpublickey processor.privatekey = args.newprivatekey try: processor.set_eyaml_value(yaml_path, txtval, output=output) except EYAMLCommandException as ex: log.error(ex) exit_state = 3 continue file_changed = True # Save the changes if file_changed: if args.backup: log.verbose("Saving a backup of {} to {}.".format( yaml_file, backup_file)) if exists(backup_file): remove(backup_file) copy2(yaml_file, backup_file) log.verbose("Writing changed data to {}.".format(yaml_file)) with open(yaml_file, 'w', encoding='utf-8') as yaml_dump: yaml.dump(yaml_data, yaml_dump) sys.exit(exit_state)
def main(): """Main code.""" args = processcli() log = ConsolePrinter(args) validateargs(args, log) change_path = YAMLPath(args.change, pathsep=args.pathsep) backup_file = args.yaml_file + ".bak" # Obtain the replacement value if args.value: new_value = args.value elif args.stdin: new_value = ''.join(sys.stdin.readlines()) elif args.file: with open(args.file, 'r') as fhnd: new_value = fhnd.read().rstrip() elif args.random is not None: new_value = ''.join( secrets.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(args.random)) # Prep the YAML parser yaml = get_yaml_editor() # Attempt to open the YAML file; check for parsing errors yaml_data = get_yaml_data(yaml, log, args.yaml_file) if yaml_data is None: # An error message has already been logged exit(1) # Load the present value at the specified YAML Path change_nodes = [] old_format = YAMLValueFormats.DEFAULT processor = EYAMLProcessor(log, yaml_data, binary=args.eyaml, publickey=args.publickey, privatekey=args.privatekey) try: for node in processor.get_nodes( change_path, mustexist=(args.mustexist or args.saveto), default_value=("" if new_value else " ")): log.debug('Got "{}" from {}.'.format(node, change_path)) change_nodes.append(node) except YAMLPathException as ex: log.critical(ex, 1) if len(change_nodes) == 1: # When there is exactly one result, its old format can be known. This # is necessary to retain whether the replacement value should be # represented later as a multi-line string when the new value is to be # encrypted. old_format = YAMLValueFormats.from_node(change_nodes[0]) log.debug("Collected nodes:") log.debug(change_nodes) # Check the value(s), if desired if args.check: for node in change_nodes: if processor.is_eyaml_value(node): # Sanity check: If either --publickey or --privatekey were set # then they must both be set in order to decrypt this value. # This is enforced only when the value must be decrypted due to # a --check request. if ((args.publickey and not args.privatekey) or (args.privatekey and not args.publickey)): log.error( "Neither or both private and public EYAML keys must be" + " set when --check is required to decrypt the old" + " value.") exit(1) try: check_value = processor.decrypt_eyaml(node) except EYAMLCommandException as ex: log.critical(ex, 1) else: check_value = node if not args.check == check_value: log.critical( '"{}" does not match the check value.'.format(args.check), 20) # Save the old value, if desired and possible if args.saveto: # Only one can be saved; otherwise it is impossible to meaningfully # convey to the end-user from exactly which other YAML node each saved # value came. if len(change_nodes) > 1: log.critical( "It is impossible to meaningly save more than one matched" + " value. Please omit --saveto or set --change to affect" + " exactly one value.", 1) saveto_path = YAMLPath(args.saveto, pathsep=args.pathsep) log.verbose("Saving the old value to {}.".format(saveto_path)) # Folded EYAML values have their embedded newlines converted to spaces # when read. As such, writing them back out breaks their original # format, despite being properly typed. To restore the original # written form, reverse the conversion, here. old_value = change_nodes[0] if ((old_format is YAMLValueFormats.FOLDED or old_format is YAMLValueFormats.LITERAL) and EYAMLProcessor.is_eyaml_value(old_value)): old_value = old_value.replace(" ", "\n") try: processor.set_value(saveto_path, clone_node(old_value), value_format=old_format) except YAMLPathException as ex: log.critical(ex, 1) # Set the requested value log.verbose("Setting the new value for {}.".format(change_path)) if args.eyamlcrypt: # If the user hasn't specified a format, use the same format as the # value being replaced, if known. format_type = YAMLValueFormats.from_str(args.format) if format_type is YAMLValueFormats.DEFAULT: format_type = old_format output_type = EYAMLOutputFormats.STRING if format_type in [YAMLValueFormats.FOLDED, YAMLValueFormats.LITERAL]: output_type = EYAMLOutputFormats.BLOCK try: processor.set_eyaml_value(change_path, new_value, output=output_type, mustexist=False) except EYAMLCommandException as ex: log.critical(ex, 2) else: processor.set_value(change_path, new_value, value_format=args.format) # Save a backup of the original file, if requested if args.backup: log.verbose("Saving a backup of {} to {}.".format( args.yaml_file, backup_file)) if exists(backup_file): remove(backup_file) copy2(args.yaml_file, backup_file) # Save the changed file log.verbose("Writing changed data to {}.".format(args.yaml_file)) with open(args.yaml_file, 'w') as yaml_dump: yaml.dump(yaml_data, yaml_dump)