/// <inheritdoc/> public override void Run(CommandLine commandLine) { if (commandLine.HasHelpOption) { Console.WriteLine(usage); Program.Exit(0); } var path = commandLine.Arguments.ElementAtOrDefault(0); if (string.IsNullOrEmpty(path)) { Console.Error.WriteLine("*** ERROR: The PATH argument is required."); Program.Exit(1); } if (!NeonVault.IsEncrypted(path, out var passwordName)) { Console.Error.WriteLine($"*** ERROR: The [{path}] file is not encrypted."); Program.Exit(1); } var vault = new NeonVault(Program.LookupPassword); var fileName = Path.GetFileName(path); using (var tempFolder = new TempFolder()) { var tempPath = Path.Combine(tempFolder.Path, fileName); // Decrypt the file to a secure temporary folder, launch the // editor and re-encrypt the file after the editor returns. vault.Decrypt(path, tempPath); NeonHelper.OpenEditor(tempPath); vault.Encrypt(tempPath, path, passwordName); } Program.Exit(0); }
public void FileToFile() { var vault = new NeonVault(GetPassword); using (var tempFolder = new TempFolder()) { var sourcePath = Path.Combine(tempFolder.Path, "source.txt"); var targetPath = Path.Combine(tempFolder.Path, "target.txt"); File.WriteAllText(sourcePath, unencryptedText); vault.Encrypt(sourcePath, targetPath, "password-1"); using (var target = new FileStream(targetPath, FileMode.Open, FileAccess.Read)) { var decrypted = vault.Decrypt(target); Assert.Equal(unencryptedBytes, decrypted); } Assert.False(NeonVault.IsEncrypted(sourcePath)); Assert.True(NeonVault.IsEncrypted(targetPath)); } }
/// <inheritdoc/> public override async Task RunAsync(CommandLine commandLine) { if (commandLine.HasHelpOption) { Console.WriteLine(usage); Program.Exit(0); } var sourcePath = commandLine.Arguments.ElementAtOrDefault(0); var targetPath = commandLine.Arguments.ElementAtOrDefault(1); if (string.IsNullOrEmpty(sourcePath)) { Console.Error.WriteLine("*** ERROR: The SOURCE argument is required."); Program.Exit(1); } if (string.IsNullOrEmpty(targetPath)) { Console.Error.WriteLine("*** ERROR: The TARGET argument is required."); Program.Exit(1); } if (!NeonVault.IsEncrypted(sourcePath)) { Console.Error.WriteLine($"*** ERROR: The [{sourcePath}] file is not encrypted."); Program.Exit(1); } var vault = new NeonVault(Program.LookupPassword); vault.Decrypt(sourcePath, targetPath); Program.Exit(0); await Task.CompletedTask; }
public void SwitchLineEndings() { // Verify that we can still decrypt en encrypted file after // changing the line endings from CRLF --> LF: var vault = new NeonVault(GetPassword, lineEnding: "\r\n"); using (var tempFolder = new TempFolder()) { var sourcePath = Path.Combine(tempFolder.Path, "source.txt"); var targetPath = Path.Combine(tempFolder.Path, "target.txt"); var target2Path = Path.Combine(tempFolder.Path, "target2.txt"); File.WriteAllText(sourcePath, unencryptedText); vault.Encrypt(sourcePath, targetPath, "password-1"); using (var targetReader = new StreamReader(targetPath)) { using (var target2Writer = new StreamWriter(target2Path)) { foreach (var line in targetReader.Lines()) { target2Writer.WriteLine(line + "\n"); } } } using (var target = new FileStream(targetPath, FileMode.Open, FileAccess.Read)) { var decrypted = vault.Decrypt(target); Assert.Equal(unencryptedBytes, decrypted); } } // Verify that we can still decrypt en encrypted file after // changing the line endings from LF --> CRLF: vault = new NeonVault(GetPassword, lineEnding: "\n"); using (var tempFolder = new TempFolder()) { var sourcePath = Path.Combine(tempFolder.Path, "source.txt"); var targetPath = Path.Combine(tempFolder.Path, "target.txt"); var target2Path = Path.Combine(tempFolder.Path, "target2.txt"); File.WriteAllText(sourcePath, unencryptedText); vault.Encrypt(sourcePath, targetPath, "password-1"); using (var targetReader = new StreamReader(targetPath)) { using (var target2Writer = new StreamWriter(target2Path)) { foreach (var line in targetReader.Lines()) { target2Writer.WriteLine(line + "\r\n"); } } } using (var target = new FileStream(targetPath, FileMode.Open, FileAccess.Read)) { var decrypted = vault.Decrypt(target); Assert.Equal(unencryptedBytes, decrypted); } } }
public void BadHEX() { var vault = new NeonVault(GetPassword); using (var tempFolder = new TempFolder()) { // Verify that an invalid character in the HEX part is detected. var sourcePath = Path.Combine(tempFolder.Path, "source.txt"); var targetPath = Path.Combine(tempFolder.Path, "target.txt"); File.WriteAllText(sourcePath, unencryptedText); vault.Encrypt(sourcePath, targetPath, "password-1"); using (var target = new FileStream(targetPath, FileMode.Open, FileAccess.Read)) { var decrypted = vault.Decrypt(target); Assert.Equal(unencryptedBytes, decrypted); } // Modify the last HEX digit to be the invalid HEX digit 'Z'. using (var target = new FileStream(targetPath, FileMode.Open, FileAccess.ReadWrite)) { var encrypted = new byte[(int)target.Length]; target.Read(encrypted, 0, encrypted.Length); encrypted[encrypted.Length - 1] = (byte)'Z'; target.Position = 0; target.Write(encrypted); } Assert.Throws <CryptographicException>( () => { using (var target = new FileStream(targetPath, FileMode.Open, FileAccess.Read)) { var decrypted = vault.Decrypt(target); Assert.Equal(unencryptedBytes, decrypted); } }); // Verify that an odd number of HEX digits is detected by removing // the last character of the file. using (var target = new FileStream(targetPath, FileMode.Open, FileAccess.ReadWrite)) { var encrypted = new byte[(int)target.Length]; target.Read(encrypted, 0, encrypted.Length); target.Position = 0; target.Write(encrypted, 0, encrypted.Length - 1); } Assert.Throws <CryptographicException>( () => { using (var target = new FileStream(targetPath, FileMode.Open, FileAccess.Read)) { var decrypted = vault.Decrypt(target); Assert.Equal(unencryptedBytes, decrypted); } }); } }
/// <inheritdoc/> public override async Task RunAsync(CommandLine commandLine) { if (commandLine.HasHelpOption) { Console.WriteLine(usage); Program.Exit(0); } var zipPath = commandLine.Arguments.ElementAtOrDefault(0); var firstName = commandLine.Arguments.ElementAtOrDefault(1); var fromStdin = commandLine.HasOption("--stdin"); var names = new List <string>(); if (zipPath == null) { Console.Error.WriteLine("*** ERROR: PATH argument is required."); Program.Exit(1); } if (firstName == null) { Console.Error.WriteLine("*** ERROR: At least one NAME argument is required."); Program.Exit(1); } if (firstName == "*") { foreach (var path in Directory.GetFiles(KubeHelper.PasswordsFolder)) { names.Add(Path.GetFileName(path)); } } else { foreach (var name in commandLine.Arguments.Skip(1)) { var validatedName = NeonVault.ValidatePasswordName(name); if (!File.Exists(Path.Combine(KubeHelper.PasswordsFolder, validatedName))) { Console.Error.WriteLine($"*** ERROR: Password [{validatedName}] does not exist."); Program.Exit(1); } names.Add(validatedName); } } if (names.Count == 0) { Console.Error.WriteLine("*** ERROR: No passwords selected for export."); Program.Exit(1); } var zipPassword = (string)null; if (fromStdin) { // Read the password from STDIN and trim. using (var stdin = NeonHelper.OpenStandardInput()) { using (var reader = new StreamReader(stdin)) { zipPassword = reader.ReadLine().Trim(); } } } retryPassword: if (!fromStdin) { if (string.IsNullOrEmpty(zipPassword)) { zipPassword = NeonHelper.ReadConsolePassword("Enter Password: "******"Confirm Password: "******"The passwords don't match. Please try again:"); Console.WriteLine(); goto retryPassword; } } using (var zip = ZipFile.Create(zipPath)) { zip.Password = zipPassword; zip.BeginUpdate(); foreach (var name in names) { zip.Add(Path.Combine(KubeHelper.PasswordsFolder, name), name); } zip.CommitUpdate(); } Console.WriteLine(); Console.WriteLine($"[{names.Count}] passwords exported."); Program.Exit(0); await Task.CompletedTask; }
public async Task ReadVariables_Decrypted() { var orgDirectory = Environment.CurrentDirectory; try { using (var testManager = new KubeTestManager()) { using (var runner = new ProgramRunner()) { using (var tempFolder = new TempFolder()) { Environment.CurrentDirectory = tempFolder.Path; var vault = new NeonVault(Program.LookupPassword); // Create a test password and a [.password-name] file in the // temp test folder. var result = await runner.ExecuteAsync(Program.Main, "tool", "password", "set", "test"); Assert.Equal(0, result.ExitCode); File.WriteAllText(".password-name", "test"); //------------------------------------------------- // Verify that we can read variables from a couple of // unencrypted variable files. File.WriteAllText("test.cmd", "echo %* > output.txt"); File.WriteAllText("var1.txt", @"# This is a comment. TEST_A=A-VALUE TEST_B=B-VALUE "); File.WriteAllText("var2.txt", @"# This is a comment. TEST_C=C-VALUE TEST_D=D-VALUE "); result = await runner.ExecuteAsync(Program.Main, "tool", "run", "var1.txt", "var2.txt", "--", "test.cmd", "_.TEST_A", "_.TEST_B", "_.TEST_C", "_.TEST_D"); Assert.Equal(0, result.ExitCode); var output = File.ReadAllText("output.txt"); Assert.Contains("A-VALUE", output); Assert.Contains("B-VALUE", output); Assert.Contains("C-VALUE", output); Assert.Contains("D-VALUE", output); File.Delete("output.txt"); File.Delete("var1.txt"); File.Delete("var2.txt"); } } } } finally { Environment.CurrentDirectory = orgDirectory; } }
public async Task DecryptFile() { var orgDirectory = Environment.CurrentDirectory; try { using (var testManager = new KubeTestManager()) { using (var runner = new ProgramRunner()) { using (var tempFolder = new TempFolder()) { Environment.CurrentDirectory = tempFolder.Path; var vault = new NeonVault(Program.LookupPassword); // Create a test password and a [.password-name] file in the // temp test folder. var result = await runner.ExecuteAsync(Program.Main, "tool", "password", "set", "test"); Assert.Equal(0, result.ExitCode); //------------------------------------------------- // Verify that we can decrypt a file. File.WriteAllText("test.cmd", "type %1 > output.txt"); const string plainText = "The quick brown fox jumped over the lazy dog."; File.WriteAllText("file.txt", plainText); File.WriteAllBytes("file.txt", vault.Encrypt("file.txt", "test")); Assert.True(NeonVault.IsEncrypted("file.txt")); result = await runner.ExecuteAsync(Program.Main, "tool", "run", "--", "test.cmd", "_...file.txt"); Assert.Equal(0, result.ExitCode); var output = File.ReadAllText("output.txt"); Assert.Contains(plainText, output); File.Delete("output.txt"); File.Delete("file.txt"); //------------------------------------------------- // Try this again calling a command directly (batch files seem to have different behavior). File.WriteAllText("type", "%1"); File.WriteAllText("file.txt", plainText); File.WriteAllBytes("file.txt", vault.Encrypt("file.txt", "test")); Assert.True(NeonVault.IsEncrypted("file.txt")); result = await runner.ExecuteAsync(Program.Main, "tool", "run", "--", "cat", "_...file.txt"); Assert.Equal(0, result.ExitCode); Assert.Contains(plainText, result.OutputText); File.Delete("output.txt"); File.Delete("file.txt"); } } } } finally { Environment.CurrentDirectory = orgDirectory; } }
public async Task VaultCreate() { using (var testManager = new KubeTestManager()) { using (var tempFolder = new TempFolder()) { var orgDir = Environment.CurrentDirectory; Environment.CurrentDirectory = tempFolder.Path; NeonHelper.OpenEditorHandler = path => File.WriteAllText(path, plainText); try { using (var passwordFile = new TempFile(folder: KubeHelper.PasswordsFolder)) { File.WriteAllText(passwordFile.Path, testPassword); var vault = new NeonVault(passwordName => testPassword); using (var runner = new ProgramRunner()) { // Verify that the PATH argument is required. var result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "create"); Assert.NotEqual(0, result.ExitCode); Assert.Contains("*** ERROR: The PATH argument is required.", result.ErrorText); // Verify that the PASSWORD-NAME argument is required when there's // no default [.password-name] file. result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "create", "test1.txt"); Assert.NotEqual(0, result.ExitCode); Assert.Contains("*** ERROR: A PASSWORD-NAME argument or [.password-name] file is required.", result.ErrorText); // Verify that we can create an encrypted file with an explicitly // named password. result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "create", "test2.txt", passwordFile.Name); Assert.Equal(0, result.ExitCode); Assert.True(NeonVault.IsEncrypted("test2.txt", out var passwordName)); Assert.Equal(passwordFile.Name, passwordName); Assert.Equal(plainText, Encoding.UTF8.GetString(vault.Decrypt("test2.txt"))); // Verify that we see an error for a missing password. result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "create", "test3.txt", missingPasswordName); Assert.NotEqual(0, result.ExitCode); Assert.Contains($"*** ERROR: [System.Security.Cryptography.CryptographicException]: Password named [{missingPasswordName}] not found or is blank or whitespace.", result.ErrorText); // Verify that we see an error for an invalid password. result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "create", "test4.txt", badPasswordName); Assert.NotEqual(0, result.ExitCode); Assert.Contains($"*** ERROR: [System.Security.Cryptography.CryptographicException]: Password name [bad/password] contains invalid characters. Only ASCII letters, digits, underscores, dashs and dots are allowed.", result.ErrorText); // Verify that a local [.password-name] file is used successfully when we don't // explicitly pass a password name. File.WriteAllText(".password-name", passwordFile.Name); result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "create", "test5.txt"); Assert.Equal(0, result.ExitCode); Assert.True(NeonVault.IsEncrypted("test5.txt", out passwordName)); Assert.Equal(passwordFile.Name, passwordName); Assert.Equal(plainText, Encoding.UTF8.GetString(vault.Decrypt("test5.txt"))); // Verify that a [.password-name] file in the parent directory is used successfully // when we don't explicitly pass a password name. Directory.CreateDirectory("subfolder"); Environment.CurrentDirectory = Path.Combine(Environment.CurrentDirectory, "subfolder"); result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "create", "test6.txt"); Assert.Equal(0, result.ExitCode); Assert.True(NeonVault.IsEncrypted("test6.txt", out passwordName)); Assert.Equal(passwordFile.Name, passwordName); Assert.Equal(plainText, Encoding.UTF8.GetString(vault.Decrypt("test6.txt"))); } } } finally { Environment.CurrentDirectory = orgDir; NeonHelper.OpenEditorHandler = null; } } } }
public async Task VaultPasswordName() { using (var testManager = new KubeTestManager()) { using (var tempFolder = new TempFolder()) { var orgDir = Environment.CurrentDirectory; Environment.CurrentDirectory = tempFolder.Path; NeonHelper.OpenEditorHandler = path => File.WriteAllText(path, plainText); try { using (var passwordFile = new TempFile(folder: KubeHelper.PasswordsFolder)) { File.WriteAllText(passwordFile.Path, testPassword); var vault = new NeonVault(passwordName => testPassword); using (var runner = new ProgramRunner()) { // Verify that the PATH argument is required. var result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "password-name"); Assert.NotEqual(0, result.ExitCode); Assert.Contains("*** ERROR: The PATH argument is required.", result.ErrorText); // Verify that we can create an encrypted file with an explicitly // named password. result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "create", "test1.txt", passwordFile.Name); Assert.Equal(0, result.ExitCode); Assert.True(NeonVault.IsEncrypted("test1.txt", out var passwordName)); Assert.Equal(passwordFile.Name, passwordName); Assert.Equal(plainText, Encoding.UTF8.GetString(vault.Decrypt("test1.txt"))); // Verify that we can get the password with a line ending. result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "password-name", "test1.txt", passwordFile.Name); Assert.Equal(0, result.ExitCode); Assert.Contains(passwordFile.Name, result.OutputText); Assert.Contains('\n', result.OutputText); // Verify that we can get the password without a line ending. result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "password-name", "-n", "test1.txt", passwordFile.Name); Assert.Equal(0, result.ExitCode); Assert.Equal(passwordFile.Name, result.OutputText); } } } finally { Environment.CurrentDirectory = orgDir; NeonHelper.OpenEditorHandler = null; } } } }
public async Task VaultEncrypt() { using (var testManager = new KubeTestManager()) { using (var tempFolder = new TempFolder()) { var orgDir = Environment.CurrentDirectory; Environment.CurrentDirectory = tempFolder.Path; NeonHelper.OpenEditorHandler = path => File.WriteAllText(path, plainText); try { using (var passwordFile = new TempFile(folder: KubeHelper.PasswordsFolder)) { File.WriteAllText(passwordFile.Path, testPassword); var vault = new NeonVault(passwordName => testPassword); using (var runner = new ProgramRunner()) { // Verify that the PATH argument is required. var result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "encrypt"); Assert.NotEqual(0, result.ExitCode); Assert.Contains("*** ERROR: The PATH argument is required.", result.ErrorText); // Verify that the TARGET argument is required when [--password-name] // or [--p] is not present. result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "decrypt", "source.txt"); Assert.NotEqual(0, result.ExitCode); Assert.Contains("*** ERROR: The TARGET argument is required.", result.ErrorText); // Verify that we can encrypt a file in-place, specifying an // explicit password name (using --password-name=NAME). File.WriteAllText("test1.txt", plainText); result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "encrypt", "test1.txt", $"--password-name={passwordFile.Name}"); Assert.Equal(0, result.ExitCode); Assert.True(NeonVault.IsEncrypted("test1.txt", out var passwordName)); Assert.Equal(passwordFile.Name, passwordName); Assert.Equal(plainText, Encoding.UTF8.GetString(vault.Decrypt("test1.txt"))); // Verify that we can encrypt a file in-place, specifying an // explicit password name (using --p=NAME). File.WriteAllText("test2.txt", plainText); result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "encrypt", "test2.txt", $"-p={passwordFile.Name}"); Assert.Equal(0, result.ExitCode); Assert.True(NeonVault.IsEncrypted("test2.txt", out passwordName)); Assert.Equal(passwordFile.Name, passwordName); Assert.Equal(plainText, Encoding.UTF8.GetString(vault.Decrypt("test2.txt"))); // Verify that we get an error trying to encrypt in-place without a // password name being explicitly specified and also without a // [.password-name] file present. File.WriteAllText("test3.txt", plainText); result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "encrypt", "test3.txt"); Assert.NotEqual(0, result.ExitCode); Assert.Contains("*** ERROR: A PASSWORD-NAME argument or [.password-name] file is required.", result.ErrorText); // Verify that we get an error trying to encrypt (not in-place) without a // password name being explicitly specified and also without a // [.password-name] file present. File.WriteAllText("test4.txt", plainText); result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "encrypt", "test4.txt", "test4.encypted.txt"); Assert.NotEqual(0, result.ExitCode); Assert.Contains("*** ERROR: A PASSWORD-NAME argument or [.password-name] file is required.", result.ErrorText); // Verify that we can encrypt a file to another with // and explicit password argument. File.WriteAllText("test5.txt", plainText); result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "encrypt", "test5.txt", "test5.encypted.txt", passwordName); Assert.Equal(0, result.ExitCode); Assert.True(NeonVault.IsEncrypted("test5.encypted.txt", out passwordName)); Assert.Equal(passwordFile.Name, passwordName); Assert.Equal(plainText, Encoding.UTF8.GetString(vault.Decrypt("test5.encypted.txt"))); // Verify that we can encrypt a file to another with // and explicit [--password-name] option. File.WriteAllText("test6.txt", plainText); result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "encrypt", "test6.txt", "test6.encypted.txt", $"--password-name={passwordName}"); Assert.Equal(0, result.ExitCode); Assert.True(NeonVault.IsEncrypted("test6.encypted.txt", out passwordName)); Assert.Equal(passwordFile.Name, passwordName); Assert.Equal(plainText, Encoding.UTF8.GetString(vault.Decrypt("test6.encypted.txt"))); // Verify that we can encrypt a file in-place using a [.password-name] file. File.WriteAllText("test7.txt", plainText); File.WriteAllText(".password-name", passwordName); result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "encrypt", "test7.txt"); Assert.Equal(0, result.ExitCode); Assert.True(NeonVault.IsEncrypted("test7.txt", out passwordName)); Assert.Equal(passwordFile.Name, passwordName); Assert.Equal(plainText, Encoding.UTF8.GetString(vault.Decrypt("test7.txt"))); // Verify that we can encrypt a file (not in-place) to another where // the source file is located in a different directory from the target // to ensure that we look for the [.password-name] file starting at // the target directory. using (var tempFile = new TempFile()) { File.WriteAllText(tempFile.Path, plainText); result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "encrypt", tempFile.Path, "test8.encrypted.txt"); Assert.Equal(0, result.ExitCode); Assert.True(NeonVault.IsEncrypted("test8.encrypted.txt", out passwordName)); Assert.Equal(passwordFile.Name, passwordName); Assert.Equal(plainText, Encoding.UTF8.GetString(vault.Decrypt("test8.encrypted.txt"))); } } } } finally { Environment.CurrentDirectory = orgDir; NeonHelper.OpenEditorHandler = null; } } } }
/// <inheritdoc/> public override void Run(CommandLine commandLine) { if (commandLine.HasHelpOption) { Console.WriteLine(usage); Program.Exit(0); } var sourcePath = commandLine.Arguments.ElementAtOrDefault(0); var targetPath = commandLine.Arguments.ElementAtOrDefault(1); var passwordName = commandLine.Arguments.ElementAtOrDefault(2); var inPlace = false; if (string.IsNullOrEmpty(sourcePath)) { Console.Error.WriteLine("*** ERROR: The PATH argument is required."); Program.Exit(1); } if (string.IsNullOrEmpty(targetPath)) { targetPath = sourcePath; inPlace = true; } if (string.IsNullOrEmpty(targetPath) || string.IsNullOrEmpty(passwordName)) { passwordName = commandLine.GetOption("--password-name"); if (passwordName != null && string.IsNullOrWhiteSpace(passwordName)) { Console.Error.WriteLine("*** ERROR: [--password-name] specifies a blank password."); Program.Exit(1); } if (passwordName == null) { passwordName = commandLine.GetOption("-p"); if (passwordName != null && string.IsNullOrWhiteSpace(passwordName)) { Console.Error.WriteLine("*** ERROR: [-p] specifies a blank password."); Program.Exit(1); } } } if (NeonVault.IsEncrypted(sourcePath)) { Console.Error.WriteLine($"*** ERROR: The [{sourcePath}] file is already encrypted."); Program.Exit(1); } if (string.IsNullOrEmpty(passwordName)) { passwordName = Program.GetDefaultPasswordName(targetPath); } if (string.IsNullOrEmpty(passwordName)) { Console.Error.WriteLine("*** ERROR: A PASSWORD-NAME argument or [.password-name] file is required."); Program.Exit(1); } var vault = new NeonVault(Program.LookupPassword); if (inPlace) { // For in-place encryption, we'll first copy the file to // a secure folder and then encrypt from there to overwrite // the original file. using (var tempFile = new TempFile()) { File.Copy(sourcePath, tempFile.Path); vault.Encrypt(tempFile.Path, sourcePath, passwordName); } } else { // Otherwise, we can simply encrypt from the source to the target. vault.Encrypt(sourcePath, targetPath, passwordName); } Program.Exit(0); }
/// <inheritdoc/> public override async Task RunAsync(CommandLine commandLine) { if (commandLine.HasHelpOption) { Console.WriteLine(usage); Program.Exit(0); } var nameArg = commandLine.Arguments.ElementAtOrDefault(0); var sourceArg = commandLine.Arguments.ElementAtOrDefault(1); if (nameArg == null) { Console.Error.WriteLine($"*** ERROR: NAME argument is required."); Program.Exit(1); } var passwordName = NeonVault.ValidatePasswordName(nameArg); var password = string.Empty; if (sourceArg == null) { // Generate a 20 character password. password = NeonHelper.GetCryptoRandomPassword(20); } else if (sourceArg == "-") { // Read the password from STDIN and trim. using (var stdin = NeonHelper.OpenStandardInput()) { using (var reader = new StreamReader(stdin)) { password = reader.ReadLine().Trim(); } } } else { // Read the first line from the file. using (var input = new FileStream(sourceArg, FileMode.Open, FileAccess.Read)) { using (var reader = new StreamReader(input)) { password = reader.ReadLine().Trim(); } } } if (password.Length == 0) { Console.Error.WriteLine($"*** ERROR: The password cannot be blank."); Program.Exit(1); } File.WriteAllText(Path.Combine(KubeHelper.PasswordsFolder, passwordName), password); Program.Exit(0); await Task.CompletedTask; }
/// <inheritdoc/> public override async Task RunAsync(CommandLine commandLine) { if (commandLine.HasHelpOption) { Console.WriteLine(usage); Program.Exit(0); } var splitCommandLine = commandLine.Split("--"); var leftCommandLine = splitCommandLine.Left; var rightCommandLine = splitCommandLine.Right; if (rightCommandLine == null || rightCommandLine.Arguments.Length == 0) { Console.Error.WriteLine("*** ERROR: Expected a command after a [--] argument."); Program.Exit(1); } // All arguments on the left command line should be VARIABLES files. // We're going to open each of these and set any enviroment variables // like [NAME=VALUE] we find. // // Note that these files may be encrypted. If any are, we'll decrypt // to a temporary file before we read them. foreach (var path in leftCommandLine.Arguments) { if (!File.Exists(path)) { Console.Error.WriteLine($"*** ERROR: File [{path}] does not exist."); Program.Exit(1); } DecryptWithAction(path, decryptedPath => { var lineNumber = 1; foreach (var line in File.ReadAllLines(decryptedPath)) { var trimmed = line.Trim(); if (line == string.Empty || line.StartsWith("#")) { continue; } var fields = line.Split('=', 2); if (fields.Length != 2 || fields[0] == string.Empty) { Console.Error.WriteLine($"*** ERROR: [{path}:{lineNumber}] is not formatted like: NAME=VALUE"); Program.Exit(1); } var name = fields[0].Trim(); var value = fields[1].Trim(); Environment.SetEnvironmentVariable(name, value); lineNumber++; } }); } // Any left command line options with a "--" prefix also specify environment variables. foreach (var option in leftCommandLine.Options.Where(o => o.Key.StartsWith("--"))) { Environment.SetEnvironmentVariable(option.Key.Substring(2), option.Value); } // We've read all of the variable files and left command line options // and initialized all environment variables. Now we need to process // and then execute the right command line. var tempFiles = new List <TempFile>(); try { var subcommand = rightCommandLine.Items; // Note that the first element of the subcommand specifies the // executable so we don't need to process that. for (int i = 1; i < subcommand.Length; i++) { var arg = subcommand[i]; if (arg.StartsWith("_...")) { // Argument is a reference to a potentially encrypted // file that needs to be passed decrypted. var path = arg.Substring(4); if (!File.Exists(path)) { Console.Error.WriteLine($"*** ERROR: File [{path}] does not exist."); Program.Exit(1); } if (NeonVault.IsEncrypted(path)) { var tempFile = new TempFile(); tempFiles.Add(tempFile); vault.Decrypt(path, tempFile.Path); path = tempFile.Path; } subcommand[i] = path; } else if (arg.StartsWith("_..")) { // Argument is a reference to a potentially encrypted text file // with environment variable references we'll need to update. var path = arg.Substring(3); if (!File.Exists(path)) { Console.Error.WriteLine($"*** ERROR: File [{path}] does not exist."); Program.Exit(1); } if (NeonVault.IsEncrypted(path)) { var tempFile = new TempFile(); tempFiles.Add(tempFile); vault.Decrypt(path, tempFile.Path); path = tempFile.Path; } subcommand[i] = path; // Perform the subsitutions. var unprocessed = File.ReadAllText(path); var processed = string.Empty; var linuxLineEndings = !unprocessed.Contains("\r\n"); using (var reader = new StreamReader(path)) { using (var preprocessor = new PreprocessReader(reader)) { preprocessor.ExpandVariables = true; preprocessor.LineEnding = linuxLineEndings ? LineEnding.LF : LineEnding.CRLF; preprocessor.ProcessStatements = false; preprocessor.StripComments = false; preprocessor.VariableExpansionRegex = PreprocessReader.AngleVariableExpansionRegex; processed = preprocessor.ReadToEnd(); } } File.WriteAllText(path, processed); } else if (arg.StartsWith("_.")) { // Argument is a reference to an environment variable. var name = arg.Substring(2); if (name == string.Empty) { Console.Error.WriteLine($"*** ERROR: Subcommand argument [{arg}] is not valid."); Program.Exit(1); } var value = Environment.GetEnvironmentVariable(name); if (value == null) { Console.Error.WriteLine($"*** ERROR: Subcommand argument [{arg}] references an undefined environment variable."); Program.Exit(2); } subcommand[i] = value; } else if (arg.StartsWith("-")) { // Argument is a command line option. We'll check to see if // it contains a reference to an environment variable. var valuePos = arg.IndexOf("=_."); if (valuePos != -1) { var optionPart = arg.Substring(0, valuePos); var name = arg.Substring(valuePos + 3); if (name == string.Empty) { Console.Error.WriteLine($"*** ERROR: Subcommand argument [{arg}] is not valid."); Program.Exit(1); } var value = Environment.GetEnvironmentVariable(name); if (value == null) { Console.Error.WriteLine($"*** ERROR: Subcommand argument [{arg}] references an undefined environment variable."); Program.Exit(1); } subcommand[i] = $"{optionPart}={value}"; } } else { // Otherwise, expand any envrionment variable references. subcommand[i] = Environment.ExpandEnvironmentVariables(subcommand[i]); } } // Execute the subcommand. var subcommandArgs = new List <object>(); foreach (var subcommandArg in subcommand) { subcommandArgs.Add(subcommandArg); } var exitCode = NeonHelper.Execute(subcommand[0], subcommandArgs.Skip(1).ToArray()); Program.Exit(exitCode); } finally { foreach (var tempFile in tempFiles) { tempFile.Dispose(); } } Program.Exit(0); await Task.CompletedTask; }
public void VaultDecrypt() { using (var manager = new KubeTestManager()) { using (var tempFolder = new TempFolder()) { var orgDir = Environment.CurrentDirectory; Environment.CurrentDirectory = tempFolder.Path; NeonHelper.OpenEditorHandler = path => File.WriteAllText(path, plainText); try { using (var passwordFile = new TempFile(folder: KubeHelper.PasswordsFolder)) { File.WriteAllText(passwordFile.Path, testPassword); var vault = new NeonVault(passwordName => testPassword); using (var runner = new ProgramRunner()) { // Verify that the SOURCE argument is required. var result = runner.Execute(Program.Main, "vault", "decrypt"); Assert.NotEqual(0, result.ExitCode); Assert.Contains("*** ERROR: The SOURCE argument is required.", result.ErrorText); // Verify that the TARGET argument is required. result = runner.Execute(Program.Main, "vault", "decrypt"); Assert.NotEqual(0, result.ExitCode); Assert.Contains("*** ERROR: The SOURCE argument is required.", result.ErrorText); // Verify that the SOURCE-PATH argument is required. result = runner.Execute(Program.Main, "vault", "decrypt", "test.txt"); Assert.NotEqual(0, result.ExitCode); Assert.Contains("*** ERROR: The TARGET argument is required.", result.ErrorText); // Verify that we can create an encrypted file with an explicitly // named password. result = runner.Execute(Program.Main, "vault", "create", "test1.txt", passwordFile.Name); Assert.Equal(0, result.ExitCode); Assert.True(NeonVault.IsEncrypted("test1.txt", out var passwordName)); Assert.Equal(passwordFile.Name, passwordName); Assert.Equal(plainText, Encoding.UTF8.GetString(vault.Decrypt("test1.txt"))); // Verify that we can decrypt the file. result = runner.Execute(Program.Main, "vault", "decrypt", "test1.txt", "decrypted.txt"); Assert.Equal(0, result.ExitCode); Assert.True(NeonVault.IsEncrypted("test1.txt", out passwordName)); Assert.True(!NeonVault.IsEncrypted("decrypted.txt", out passwordName)); Assert.Equal(plainText, File.ReadAllText("decrypted.txt")); } } } finally { Environment.CurrentDirectory = orgDir; NeonHelper.OpenEditorHandler = null; } } } }