module machine import os fn with_temp_config(name string) string { dir := os.join_path(os.temp_dir(), 'koh3_machine_${os.getpid()}_${name}') os.rmdir_all(dir) or {} return dir } // The settings file is machine-wide and lives outside every repo, so these // tests write into a temporary home instead. fn test_remote_settings_round_trip() { home := with_temp_config('remote') defer { os.rmdir_all(home) or {} } // load_remote reads the real machine config, so only the parse and the // shape are checked here. values := parse_config('# comment\nkind=folder\npath=/tmp/remote\n\n') assert values['kind'] == 'folder' assert values['path'] == '/tmp/remote' assert values.len == 2 } fn test_parse_config_ignores_junk_and_keeps_later_values() { values := parse_config(' # a comment kind=s3 endpoint = https://s3.fr-par.scw.cloud kind=folder ') assert values['kind'] == 'folder' assert values['endpoint'] == 'https://s3.fr-par.scw.cloud' assert values.len == 2 } fn test_config_paths_are_outside_the_repo() { assert config_dir().ends_with('.config/koh') assert config_path(remote_file).ends_with('.config/koh/remote') assert config_path(credentials_file).ends_with('.config/koh/credentials') } // The keys file must never be readable by others, not even for the moment // between writing it and tightening it, and an old loose file is tightened. fn test_credentials_are_private_from_the_start() { home := with_temp_config('credentials') defer { os.rmdir_all(home) or {} } old_home := os.getenv('HOME') os.setenv('HOME', home, true) defer { os.setenv('HOME', old_home, true) } os.mkdir_all(config_dir(), mode: 0o755)! os.chmod(config_dir(), 0o755)! os.write_file(config_path(credentials_file), 'access_key=old\n')! os.chmod(config_path(credentials_file), 0o644)! // A stale temp name from an earlier run, as a link: writing through it // would put the keys wherever it points. stale := '${config_path(credentials_file)}.tmp-${os.getpid()}' os.symlink(os.join_path(home, 'elsewhere'), stale)! save_credentials(Credentials{ access_key: 'AK' secret_key: 'SK' })! assert os.stat(config_path(credentials_file))!.get_mode().bitmask() & 0o777 == 0o600 assert os.stat(config_dir())!.get_mode().bitmask() & 0o777 == 0o700 assert load_credentials()!.secret_key == 'SK' assert os.ls(config_dir())! == [credentials_file] assert !os.exists(os.join_path(home, 'elsewhere')) } // A save that fails part way must not leave the keys lying in a temp file. fn test_a_failed_credentials_save_leaves_no_temp_file() { home := with_temp_config('credentials_fail') defer { os.rmdir_all(home) or {} } old_home := os.getenv('HOME') os.setenv('HOME', home, true) defer { os.setenv('HOME', old_home, true) } // A directory where the file should be makes the final rename fail. os.mkdir_all(os.join_path(config_path(credentials_file), 'occupied'))! if _ := save_credentials(Credentials{ access_key: 'AK' secret_key: 'SK' }) { assert false, 'renaming over a directory should have failed' } assert os.ls(config_dir())! == [credentials_file] }