"A small dirty script to check that none of the password config options have been set to real passwords"
from collections.abc import Generator
import yaml
from yaml.parser import ParserError
from yaml.scanner import ScannerError
from yaml.constructor import ConstructorError
import sys


ALLOWED_PASSWORDS = ["PassW0rd!", "password", "PASSWORD@", "PASSW0RD!", "PASSWORD!"]

def key_finder(d: dict, key: str = "password", top_level = None) -> Generator:
    """This recursive function yields all the keys in {d} that _contains_ the string {key}

    :param dict d: The dictionary to dive through
    :param str key: The phrase we are going to match keys against
    :return: A generator that creates tuples containing Optional[top_level_key], key, value
    :rtype Union[tuple[str, str], tuple[str, str, str]]
    """
    if d is None:
        return {}
    for k, v in d.items():
        if isinstance(v, dict):
            if top_level is None:
                yield from key_finder(v, key, k) # Pass the top level name into the recursive descent
            else:
                yield from key_finder(v, key, top_level) # name isn't the top level key
        if key in str(k): # Sometimes yaml gets parsed with key True
            if top_level is None:
                yield k, v # Key is already top level
            else:
                yield top_level, k, v # Use the top level name

WE_DUN_GOOFED: bool = False

changed_files = sys.argv[1:] # Ignore filename of this script
for file in changed_files:
    with open(file, 'r') as f:
        try:
            yml = yaml.safe_load(f)
            gen = key_finder(yml)
            for password_keys in gen:
                if password_keys[-1] not in ALLOWED_PASSWORDS:
                    if len(password_keys) == 2:
                        print(f"top level key '{password_keys[0]}' in {file} contains a real password!")
                    else:
                        print(f"top level key '{password_keys[0]}' with subkey '{password_keys[1]}' in {file} contains a real password!")
                    WE_DUN_GOOFED = True
        except (ScannerError, ParserError):
            print(f"Couldn't parse yaml file for: {file}")
            pass
        except ConstructorError:
            print(f"Couldn't construct yaml file: {file}")
            pass

if WE_DUN_GOOFED:
    exit(1)
