namespace marlin.core.functional; using System.Diagnostics; /// /// SSH auth via a private key. The key is written to a per-invocation temp file /// (owner-read/write only) and deleted as soon as the git process that used it exits — /// ssh has no clean way to take key material via stdin/env, so a short-lived file is /// the least-bad option. /// public sealed class SshKeyGitCredential : IGitCredential { private readonly string _privateKeyPem; public SshKeyGitCredential(string privateKeyPem) { this._privateKeyPem = privateKeyPem; } public async Task ApplyAsync(ProcessStartInfo psi) { string keyPath = Path.Combine(Path.GetTempPath(), $"marlin_{Guid.NewGuid():N}.key"); await File.WriteAllTextAsync(keyPath, this._privateKeyPem); if (!OperatingSystem.IsWindows()) File.SetUnixFileMode(keyPath, UnixFileMode.UserRead | UnixFileMode.UserWrite); psi.Environment["GIT_SSH_COMMAND"] = $"ssh -i \"{keyPath}\" -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new"; return new TempKeyFile(keyPath); } private sealed class TempKeyFile : IAsyncDisposable { private readonly string _path; public TempKeyFile(string path) { this._path = path; } public ValueTask DisposeAsync() { try { File.Delete(this._path); } catch (IOException) { // best-effort cleanup; a leftover temp file is not worth failing the caller over } return ValueTask.CompletedTask; } } }