def test_bad_encryption_keys(self, quiet_logger):
        processor = EYAMLProcessor(quiet_logger, None)
        processor.privatekey = "/no/such/file"
        processor.publickey = "/no/such/file"

        with pytest.raises(EYAMLCommandException):
            processor.encrypt_eyaml("test")
示例#2
0
    def __init__(self, config: DifferConfig, logger: ConsolePrinter,
                 document: Any, **kwargs) -> None:
        """
        Instantiate this class into an object.

        Parameters:
        1. logger (ConsolePrinter) Instance of ConsoleWriter or subclass
        2. document (Any) The basis document

        Keyword Arguments:
        * ignore_eyaml_values (bool) Do not decrypt encrypted YAML value for
          comparison

        Returns:  N/A

        Raises:  N/A
        """
        ignore_eyaml = kwargs.pop("ignore_eyaml_values", True)

        self.config: DifferConfig = config
        self.logger: ConsolePrinter = logger
        self._data: Any = document
        self._diffs: List[DiffEntry] = []
        self._ignore_eyaml: bool = ignore_eyaml
        self._eyamlproc = (None if ignore_eyaml else EYAMLProcessor(
            logger, document, **kwargs))
 def test_happy_get_eyaml_values(self, quiet_logger, eyamldata_f,
                                 old_eyaml_keys, yaml_path, compare):
     processor = EYAMLProcessor(quiet_logger,
                                eyamldata_f,
                                privatekey=old_eyaml_keys[0],
                                publickey=old_eyaml_keys[1])
     for node in processor.get_eyaml_values(yaml_path, True):
         assert unwrap_node_coords(node) == compare
    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 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)
示例#6
0
def main():
    """Main code."""
    args = processcli()
    log = ConsolePrinter(args)
    validateargs(args, log)
    yaml_path = YAMLPath(args.query, pathsep=args.pathsep)

    # Prep the YAML parser
    yaml = Parsers.get_yaml_editor()

    # Attempt to open the YAML file; check for parsing errors
    (yaml_data, doc_loaded) = Parsers.get_yaml_data(
        yaml, log, args.yaml_file if args.yaml_file else "-")
    if not doc_loaded:
        # An error message has already been logged
        sys.exit(1)

    # Seek the queried value(s)
    discovered_nodes = []
    processor = EYAMLProcessor(log,
                               yaml_data,
                               binary=args.eyaml,
                               publickey=args.publickey,
                               privatekey=args.privatekey)
    try:
        for node in processor.get_eyaml_values(yaml_path, mustexist=True):
            log.debug("Got node from {}:".format(yaml_path),
                      data=node,
                      prefix="yaml_get::main:  ")
            discovered_nodes.append(NodeCoords.unwrap_node_coords(node))
    except YAMLPathException as ex:
        log.critical(ex, 1)
    except EYAMLCommandException as ex:
        log.critical(ex, 2)

    try:
        for node in discovered_nodes:
            if isinstance(node, (dict, list, CommentedSet)):
                print(json.dumps(Parsers.jsonify_yaml_data(node)))
            else:
                if node is None:
                    node = "\x00"
                print("{}".format(str(node).replace("\n", r"\n")))
    except RecursionError:
        log.critical(
            "The YAML data contains an infinitely recursing YAML Alias!", 1)
    def test_find_eyaml_paths(self, quiet_logger, eyamldata_f):
        processor = EYAMLProcessor(quiet_logger, eyamldata_f)
        expected = [
            "aliases[&secretIdentity]",
            "aliases[&secretPhrase]",
            "anchored::secrets.aliased_values.ident",
            "anchored::secrets.aliased_values.phrase",
            "anchored::secrets.array_of_array_idents[0][0]",
            "aliased::secrets.novel_values.ident",
            "aliased::secrets.novel_values.phrase",
            "aliased::secrets.string_values.ident",
            "aliased::secrets.string_values.phrase",
        ]
        actual = []
        for path in processor.find_eyaml_paths():
            actual.append(str(path))

        assert actual == expected
示例#8
0
def main():
    """Main code."""
    # Process any command-line arguments
    args = processcli()
    log = ConsolePrinter(args)
    validateargs(args, log)
    search_values = True
    search_keys = False
    include_key_aliases = False
    include_value_aliases = False

    if args.onlykeynames:
        search_values = False
        search_keys = True
    elif args.keynames:
        search_keys = True

    if args.include_aliases is IncludeAliases.INCLUDE_ALL_ALIASES:
        include_key_aliases = True
        include_value_aliases = True
    elif args.include_aliases is IncludeAliases.INCLUDE_KEY_ALIASES:
        include_key_aliases = True
    elif args.include_aliases is IncludeAliases.INCLUDE_VALUE_ALIASES:
        include_value_aliases = True

    # Prepare the YAML processor
    yaml = get_yaml_editor()
    processor = EYAMLProcessor(log,
                               None,
                               binary=args.eyaml,
                               publickey=args.publickey,
                               privatekey=args.privatekey)

    # Process the input file(s)
    exit_state = 0

    # pylint: disable=too-many-nested-blocks
    for yaml_file in args.yaml_files:
        # Try to open the file
        yaml_data = get_yaml_data(yaml, log, yaml_file)
        if yaml_data is None:
            # An error message has already been logged
            exit_state = 3
            continue

        # Process all searches
        processor.data = yaml_data
        yaml_paths = []
        for expression in args.search:
            exterm = get_search_term(log, expression)
            log.debug(("yaml_paths::main:" +
                       "converting search expression '{}' into '{}'").format(
                           expression, exterm))
            if exterm is None:
                exit_state = 1
                continue

            for result in search_for_paths(
                    log,
                    processor,
                    yaml_data,
                    exterm,
                    args.pathsep,
                    search_values=search_values,
                    search_keys=search_keys,
                    search_anchors=args.refnames,
                    include_key_aliases=include_key_aliases,
                    include_value_aliases=include_value_aliases,
                    decrypt_eyaml=args.decrypt,
                    expand_children=args.expand):
                # Record only unique results
                add_entry = True
                for entry in yaml_paths:
                    if str(result) == str(entry[1]):
                        add_entry = False
                        break
                if add_entry:
                    yaml_paths.append((expression, result))

        if not yaml_paths:
            # Nothing further to do when there are no results
            continue

        if args.except_expression:
            for expression in args.except_expression:
                exterm = get_search_term(log, expression)
                log.debug(
                    ("yaml_paths::main:" +
                     "converted except expression '{}' into '{}'").format(
                         expression, exterm))
                if exterm is None:
                    exit_state = 1
                    continue

                for result in search_for_paths(
                        log,
                        processor,
                        yaml_data,
                        exterm,
                        args.pathsep,
                        search_values=search_values,
                        search_keys=search_keys,
                        search_anchors=args.refnames,
                        include_key_aliases=include_key_aliases,
                        include_value_aliases=include_value_aliases,
                        decrypt_eyaml=args.decrypt,
                        expand_children=args.expand):
                    for entry in yaml_paths:
                        if str(result) == str(entry[1]):
                            yaml_paths.remove(entry)
                            break  # Entries are already unique

        print_results(args, processor, yaml_file, yaml_paths)

    exit(exit_state)
示例#9
0
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 test_decrypt_calledprocesserror(self, quiet_logger, force_subprocess_run_cpe):
     processor = EYAMLProcessor(quiet_logger, None)
     with pytest.raises(EYAMLCommandException):
         processor.decrypt_eyaml("ENC[...]")
 def test_impossible_decryption(self, quiet_logger, old_eyaml_keys):
     processor = EYAMLProcessor(quiet_logger, None)
     testval = "ENC[...]"
     with pytest.raises(EYAMLCommandException):
         processor.decrypt_eyaml(testval)
 def test_ignore_already_decrypted_cryps(self, quiet_logger):
     processor = EYAMLProcessor(quiet_logger, None)
     testval = "some value"
     assert testval == processor.decrypt_eyaml(testval)
 def test_ignore_already_encrypted_cryps(self, quiet_logger):
     processor = EYAMLProcessor(quiet_logger, None)
     testval = "ENC[...]"
     assert testval == processor.encrypt_eyaml(testval)
示例#14
0
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)
 def test_not_can_run_eyaml(self, quiet_logger):
     processor = EYAMLProcessor(quiet_logger, None)
     processor.eyaml = None
     assert False == processor._can_run_eyaml()
示例#16
0
    def test_good_multi_replacements(self, script_runner, tmp_path_factory, old_eyaml_keys, new_eyaml_keys, quiet_logger):
        from yamlpath.func import unwrap_node_coords
        from yamlpath.common import Parsers
        from yamlpath import Processor
        from yamlpath.eyaml import EYAMLProcessor

        simple_content = """---
        encrypted_string: ENC[PKCS7,MIIBiQYJKoZIhvcNAQcDoIIBejCCAXYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAHA4rPcTzvgzPLtnGz3yoyX/kVlQ5TnPXcScXK2bwjguGZLkuzv/JVPAsOm4t6GlnROpy4zb/lUMHRJDChJhPLrSj919B8//huoMgw0EU5XTcaN6jeDDjL+vhjswjvLFOux66UwvMo8sRci/e2tlFiam8VgxzV0hpF2qRrL/l84V04gL45kq4PCYDWrJNynOwYVbSIF+qc5HaF25H8kHq1lD3RB6Ob/J942Q7k5Qt7W9mNm9cKZmxwgtUgIZWXW6mcPJ2dXDB/RuPJJSrLsb1VU/DkhdgxaNzvLCA+MViyoFUkCfHFNZbaHKNkoYXBy7dLmoh/E5tKv99FeG/7CzL3DBMBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBCVU5Mjt8+4dLkoqB9YArfkgCDkdIhXR9T1M4YYa1qTE6by61VPU3g1aMExRmo4tNZ8FQ==]
        encrypted_block: >
          ENC[PKCS7,MIIBeQYJKoZIhvcNAQcDoIIBajCCAWYCAQAxggEhMIIBHQIBADAFMAACAQEw
          DQYJKoZIhvcNAQEBBQAEggEAnxQVqyIgRTb/+VP4Q+DLJcnlS8YPouXEW8+z
          it9uwUA02CEPxCEU944GcHpgTY3EEtkm+2Z/jgXI119VMML+OOQ1NkwUiAw/
          wq0vwz2D16X31XzhedQN5FZbfZ1C+2tWSQfCjE0bu7IeHfyR+k2ssD11kNZh
          JDEr2bM2dwOdT0y7VGcQ06vI9gw6UXcwYAgS6FoLm7WmFftjcYiNB+0EJSW0
          VcTn2gveaw9iOQcum/Grby+9Ybs28fWd8BoU+ZWDpoIMEceujNa9okIXNPJO
          jcvv1sgauwJ3RX6WFQIy/beS2RT5EOLhWIZCAQCcgJWgovu3maB7dEUZ0NLG
          OYUR7zA8BgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBAbO16EzQ5/cdcvgB0g
          tpKIgBAEgTLT5n9Jtc9venK0CKso]
        """
        anchored_content = """---
        aliases:
          - &blockStyle >
            ENC[PKCS7,MIIBiQYJKoZIhvcNAQcDoIIBejCCAXYCAQAxggEhMIIBHQIBADAFMAACAQEw
            DQYJKoZIhvcNAQEBBQAEggEArvk6OYa1gACTdrWq2SpCrtGRlc61la5AGU7L
            aLTyKfqD9vqx71RDjobfOF96No07kLsEpoAJ+LKKHNjdG6kjvpGPmttj9Dkm
            XVoU6A+YCmm4iYFKD/NkoSOEyAkoDOXSqdjrgt0f37GefEsXt6cqAavDpUJm
            pmc0KI4TCG5zpfCxqttMs+stOY3Y+0WokkulQujZ7K3SdWUSHIysgMrWiect
            Wdg5unxN1A/aeyvhgvYSNPjU9KBco7SDnigSs9InW/QghJFrZRrDhTp1oTUc
            qK5lKvaseHkVGi91vPWeLQxZt1loJB5zL6j5BxMbvRfJK+wc3ax2u4x8WTAB
            EurCwzBMBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBAwcy7jvcOGcMfLEtug
            LEXbgCBkocdckuDe14mVGmUmM++xN34OEVRCeGVWWUnWq1DJ4Q==]
          - &stringStyle ENC[PKCS7,MIIBiQYJKoZIhvcNAQcDoIIBejCCAXYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAIu44u62q5sVfzC7kytLi2Z/EzH2DKr4vDsoqDBeSZ71aRku/uSrjyiO4lyoq9Kva+eBAyjBay5fnqPVBaU3Rud2pdEoZEoyofi02jn4hxUKpAO1W0AUgsQolGe53qOdM4U8RbwnTR0gr3gp2mCd18pH3SRMP9ryrsBAxGzJ6mR3RgdZnlTlqVGXCeWUeVpbH+lcHw3uvd+o/xkvJ/3ypxz+rWILiAZ3QlCirzn/qb2fHuKf3VBh8RVFuQDaM5voajZlgjD6KzNCsbATOqOA6eJI4j0ngPdDlIjGHAnahuyluQ5f5SIaIjLC+ZeCOfIYni0MQ+BHO0JNbccjq2Unb7TBMBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBCYmAI0Ao3Ok1cSmVw0SgQGgCBK62z1r5RfRjf1xKfqDxTsGUHfsUmM3EjGJfnWzCRvuQ==]
        block: *blockStyle
        string: *stringStyle
        yet_another:
          'more.complex.child': *blockStyle
        """
        simple_file = create_temp_yaml_file(tmp_path_factory, simple_content)
        anchored_file = create_temp_yaml_file(tmp_path_factory, anchored_content)

        result = script_runner.run(
            self.command,
            "--newprivatekey={}".format(new_eyaml_keys[0]),
            "--newpublickey={}".format(new_eyaml_keys[1]),
            "--oldprivatekey={}".format(old_eyaml_keys[0]),
            "--oldpublickey={}".format(old_eyaml_keys[1]),
            simple_file,
            anchored_file
        )
        assert result.success, result.stderr

        with open(simple_file, 'r') as fhnd:
            simple_data = fhnd.read()

        with open(anchored_file, 'r') as fhnd:
            anchored_data = fhnd.read()

        assert not simple_data == simple_content
        assert not anchored_data == anchored_content

        # Verify that block and string formatting is correct
        yaml = Parsers.get_yaml_editor()

        (yaml_rotated_data, doc_loaded) = Parsers.get_yaml_data(
            yaml, quiet_logger,
            anchored_data, literal=True)
        if not doc_loaded:
            # An error message has already been logged
            assert False, "Rotated anchored data failed to load"

        source_processor = Processor(quiet_logger, yaml_rotated_data)
        for node in source_processor.get_nodes('/block', mustexist=True):
            assert not '  ' in unwrap_node_coords(node)


        # Test that the pre- and post-rotated values are identical
        (yaml_anchored_data, doc_loaded) = Parsers.get_yaml_data(
            yaml, quiet_logger,
            anchored_content, literal=True)
        if not doc_loaded:
            # An error message has already been logged
            assert False, "Original anchored data failed to load"

        (yaml_rotated_data, doc_loaded) = Parsers.get_yaml_data(
            yaml, quiet_logger,
            anchored_data, literal=True)
        if not doc_loaded:
            # An error message has already been logged
            assert False, "Rotated anchored data failed to load"

        source_processor = EYAMLProcessor(
            quiet_logger, yaml_anchored_data,
            privatekey=old_eyaml_keys[0],
            publickey=old_eyaml_keys[1])
        for node in source_processor.get_eyaml_values(
            '/block', True
        ):
            assert unwrap_node_coords(node) == 'This is a test value.'

        rotated_processor = EYAMLProcessor(
            quiet_logger, yaml_rotated_data,
            privatekey=new_eyaml_keys[0],
            publickey=new_eyaml_keys[1])
        for node in rotated_processor.get_eyaml_values(
            '/block', True
        ):
            assert unwrap_node_coords(node) == 'This is a test value.'
示例#17
0
def main():
    """Main code."""
    # Process any command-line arguments
    args = processcli()
    log = ConsolePrinter(args)
    validateargs(args, log)
    search_values = True
    search_keys = False
    include_key_aliases = False
    include_value_aliases = False

    if args.onlykeynames:
        search_values = False
        search_keys = True
    elif args.keynames:
        search_keys = True

    if args.include_aliases is IncludeAliases.INCLUDE_ALL_ALIASES:
        include_key_aliases = True
        include_value_aliases = True
    elif args.include_aliases is IncludeAliases.INCLUDE_KEY_ALIASES:
        include_key_aliases = True
    elif args.include_aliases is IncludeAliases.INCLUDE_VALUE_ALIASES:
        include_value_aliases = True

    # Prepare the YAML processor
    yaml = Parsers.get_yaml_editor()
    processor = EYAMLProcessor(
        log, None, binary=args.eyaml,
        publickey=args.publickey, privatekey=args.privatekey)

    # Process the input file(s)
    exit_state = 0
    file_tally = -1
    consumed_stdin = False

    for yaml_file in args.yaml_files:
        file_tally += 1
        if yaml_file.strip() == "-":
            consumed_stdin = True

        log.debug(
            "yaml_merge::main:  Processing file, {}".format(
                "STDIN" if yaml_file.strip() == "-" else yaml_file))

        proc_state = process_yaml_file(
            args, yaml, log, yaml_file, processor, search_values, search_keys,
            include_key_aliases, include_value_aliases, file_tally
        )

        if proc_state != 0:
            exit_state = proc_state

    # Check for a waiting STDIN document
    if (exit_state == 0
        and not consumed_stdin
        and not args.nostdin
        and not sys.stdin.isatty()
    ):
        file_tally += 1
        exit_state = process_yaml_file(
            args, yaml, log, "-", processor, search_values, search_keys,
            include_key_aliases, include_value_aliases, file_tally
        )

    sys.exit(exit_state)
示例#18
0
def main():
    """Main code."""
    args = processcli()
    log = ConsolePrinter(args)
    validateargs(args, log)
    change_path = YAMLPath(args.change, pathsep=args.pathsep)
    must_exist = args.mustexist or args.saveto

    # Obtain the replacement value
    consumed_stdin = False
    new_value = None
    has_new_value = False
    if args.value or args.value == "":
        new_value = args.value
        has_new_value = True
    elif args.stdin:
        new_value = ''.join(sys.stdin.readlines())
        consumed_stdin = True
        has_new_value = True
    elif args.file:
        with open(args.file, 'r') as fhnd:
            new_value = fhnd.read().rstrip()
        has_new_value = True
    elif args.null:
        new_value = None
        has_new_value = True
    elif args.random is not None:
        new_value = ''.join(
            secrets.choice(args.random_from) for _ in range(args.random))
        has_new_value = True

    # Prep the YAML parser
    yaml = Parsers.get_yaml_editor()

    # Attempt to open the YAML file; check for parsing errors
    if args.yaml_file:
        yaml_data = _try_load_input_file(args, log, yaml, change_path,
                                         new_value)
        if args.yaml_file.strip() == '-':
            consumed_stdin = True

    # Check for a waiting STDIN document
    if (not consumed_stdin and not args.yaml_file and not args.nostdin
            and not sys.stdin.isatty()):
        args.yaml_file = "-"
        yaml_data = _try_load_input_file(args, log, yaml, change_path,
                                         new_value)

    # Load the present nodes at the specified YAML Path
    processor = EYAMLProcessor(log,
                               yaml_data,
                               binary=args.eyaml,
                               publickey=args.publickey,
                               privatekey=args.privatekey)
    change_node_coordinates = _get_nodes(
        log,
        processor,
        change_path,
        must_exist=must_exist,
        default_value=("" if new_value else " "))

    old_format = YAMLValueFormats.DEFAULT
    if len(change_node_coordinates) == 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_node_coordinates[0].node)

    # Check the value(s), if desired
    if args.check:
        for node_coordinate in change_node_coordinates:
            if processor.is_eyaml_value(node_coordinate.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.")
                    sys.exit(1)

                try:
                    check_value = processor.decrypt_eyaml(node_coordinate.node)
                except EYAMLCommandException as ex:
                    log.critical(ex, 1)
            else:
                check_value = node_coordinate.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_node_coordinates) > 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_node_coordinates[0].node
        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,
                                Nodes.clone_node(old_value),
                                value_format=old_format,
                                tag=args.tag)
        except YAMLPathException as ex:
            log.critical(ex, 1)

    # Set the requested value
    log.verbose("Applying changes to {}.".format(change_path))
    if args.delete:
        # Destroy the collected nodes (from their parents) in the reverse order
        # they were discovered.  This is necessary lest Array elements be
        # improperly handled, leading to unwanted data loss.
        _delete_nodes(log, processor, change_node_coordinates)
    elif args.aliasof:
        # Assign the change nodes as Aliases of whatever --aliasof points to
        _alias_nodes(log, processor, change_node_coordinates, args.aliasof,
                     args.anchor)
    elif 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)
    elif has_new_value:
        try:
            processor.set_value(change_path,
                                new_value,
                                value_format=args.format,
                                mustexist=must_exist,
                                tag=args.tag)
        except YAMLPathException as ex:
            log.critical(ex, 1)
    elif args.tag:
        _tag_nodes(processor.data, args.tag, change_node_coordinates)

    # Write out the result
    write_output_document(args, log, yaml, yaml_data)
 def test_no_decrypt_without_eyaml(self, quiet_logger):
     processor = EYAMLProcessor(quiet_logger, None)
     processor.eyaml = None
     with pytest.raises(EYAMLCommandException):
         processor.decrypt_eyaml("ENC[...]")