public AuditTarget(Dictionary <string, object> audit_options, EventHandler <EnvironmentEventArgs> controller_message_handler = null) { if (ReferenceEquals(audit_options, null)) { throw new ArgumentNullException("audit_options"); } this.AuditOptions = audit_options; #region Setup host environment if (this.AuditOptions.ContainsKey("HostEnvironment")) { this.HostEnvironment = (LocalEnvironment)this.AuditOptions["HostEnvironment"]; this.HostEnvironmentInitialised = true; } else { this.ControllerMessage = controller_message_handler; this.HostEnvironmentMessage = AuditTarget_HostEnvironmentMessageHandler; this.HostEnvironment = new LocalEnvironment(this.HostEnvironmentMessage); this.HostEnvironment.ScriptEnvironment.MessageHandler += this.AuditTarget_ScriptEnvironmentMessageHandler; if (this.AuditOptions.ContainsKey("Dockerized")) { this.HostEnvironment.IsDockerContainer = true; } this.HostEnvironmentInitialised = true; } #endregion #region Setup audit environment if (this.AuditOptions.ContainsKey("AuditEnvironment")) { this.AuditEnvironment = (AuditEnvironment)this.AuditOptions["AuditEnvironment"]; this.AuditEnvironmentIntialised = true; } else { if (this.AuditOptions.Keys.Contains("DockerContainer") && !this.AuditOptions.Keys.Contains("RemoteHost")) { DockerAuditEnvironment docker_environment = new DockerAuditEnvironment(this.HostEnvironmentMessage, (string)this.AuditOptions["DockerContainer"], new OperatingSystem(PlatformID.Unix, new Version(0, 0)), this.HostEnvironment); if (string.IsNullOrEmpty(docker_environment.Container)) { this.AuditEnvironmentIntialised = false; throw new Exception("Failed to initialise audit environment."); } else if (!docker_environment.ContainerRunning) { this.AuditEnvironmentIntialised = false; throw new Exception("The Docker container is not currently running and DevAudit does not know how to run your container. Ensure your container is running before attempting to" + "audit it."); } else { this.AuditEnvironment = docker_environment; this.AuditEnvironmentIntialised = true; this.AuditEnvironmentMessage = AuditTarget_AuditEnvironmentMessageHandler; this.AuditEnvironment.MessageHandler -= HostEnvironmentMessage; this.AuditEnvironment.MessageHandler += this.AuditEnvironmentMessage; } } else if (this.AuditOptions.Keys.Contains("DockerContainer") && this.AuditOptions.Keys.Contains("RemoteHost")) { SshDockerAuditEnvironment ssh_environment = null; if (this.AuditOptions.Keys.Contains("RemoteUser") && this.AuditOptions.Keys.Contains("RemoteKeyFile")) { if (this.AuditOptions.Keys.Contains("RemoteKeyPassPhrase")) { ssh_environment = new SshDockerAuditEnvironment(this.HostEnvironmentMessage, "ssh", (string)this.AuditOptions["RemoteHost"], (int)this.AuditOptions["RemoteSshPort"], (string)this.AuditOptions["RemoteUser"], this.AuditOptions["RemoteKeyPassPhrase"], (string)this.AuditOptions["RemoteKeyFile"], (string)this.AuditOptions["DockerContainer"], new OperatingSystem(PlatformID.Unix, new Version(0, 0)), this.HostEnvironment); } else { ssh_environment = new SshDockerAuditEnvironment(this.HostEnvironmentMessage, "ssh", (string)this.AuditOptions["RemoteHost"], (int)this.AuditOptions["RemoteSshPort"], (string)this.AuditOptions["RemoteUser"], null, (string)this.AuditOptions["RemoteKeyFile"], (string)this.AuditOptions["DockerContainer"], new OperatingSystem(PlatformID.Unix, new Version(0, 0)), this.HostEnvironment); } } else if (this.AuditOptions.Keys.Contains("RemoteUser") && this.AuditOptions.Keys.Contains("RemotePass")) { ssh_environment = new SshDockerAuditEnvironment(this.HostEnvironmentMessage, "ssh", (string)this.AuditOptions["RemoteHost"], (int)this.AuditOptions["RemoteSshPort"], (string)this.AuditOptions["RemoteUser"], this.AuditOptions["RemotePass"], null, (string)this.AuditOptions["DockerContainer"], new OperatingSystem(PlatformID.Unix, new Version(0, 0)), this.HostEnvironment); } else { throw new Exception("Unknown remote host authentication options."); } if (ssh_environment.IsConnected) { if (string.IsNullOrEmpty(ssh_environment.Container)) { this.AuditEnvironmentIntialised = false; throw new Exception("Failed to initialise audit environment."); } else if (!ssh_environment.ContainerRunning) { this.AuditEnvironmentIntialised = false; throw new Exception("The Docker container is not currently running and DevAudit does not know how to run your container. Ensure your container is running before attempting to" + "audit it."); } else { this.AuditEnvironment = ssh_environment; this.AuditEnvironmentIntialised = true; this.AuditEnvironmentMessage = AuditTarget_AuditEnvironmentMessageHandler; this.AuditEnvironment.MessageHandler -= HostEnvironmentMessage; this.AuditEnvironment.MessageHandler += this.AuditEnvironmentMessage; } } else { ssh_environment = null; this.AuditEnvironmentIntialised = false; throw new Exception("Failed to initialise SSH Docker audit environment."); } } else if (this.AuditOptions.Keys.Contains("RemoteHost")) { string client; SshAuditEnvironment ssh_environment = null; if (this.HostEnvironment.OS.Platform == PlatformID.Win32NT) { client = this.AuditOptions.Keys.Contains("WindowsUsePlink") ? "plink" : this.AuditOptions.Keys.Contains("WindowsUsePlink") ? "openssh" : "ssh"; } else { client = "ssh"; } if (this.AuditOptions.Keys.Contains("RemoteUser") && this.AuditOptions.Keys.Contains("RemoteKeyFile")) { if (this.AuditOptions.Keys.Contains("RemoteKeyPassPhrase")) { ssh_environment = new SshAuditEnvironment(this.HostEnvironmentMessage, client, (string)this.AuditOptions["RemoteHost"], (int)this.AuditOptions["RemoteSshPort"], (string)this.AuditOptions["RemoteUser"], this.AuditOptions["RemoteKeyPassPhrase"], (string)this.AuditOptions["RemoteKeyFile"], new OperatingSystem(PlatformID.Unix, new Version(0, 0)), this.HostEnvironment); } else { ssh_environment = new SshAuditEnvironment(this.HostEnvironmentMessage, client, (string)this.AuditOptions["RemoteHost"], (int)this.AuditOptions["RemoteSshPort"], (string)this.AuditOptions["RemoteUser"], null, (string)this.AuditOptions["RemoteKeyFile"], new OperatingSystem(PlatformID.Unix, new Version(0, 0)), this.HostEnvironment); } } else if (this.AuditOptions.Keys.Contains("RemoteUser") && this.AuditOptions.Keys.Contains("RemotePass")) { ssh_environment = new SshAuditEnvironment(this.HostEnvironmentMessage, client, (string)this.AuditOptions["RemoteHost"], (int)this.AuditOptions["RemoteSshPort"], (string)this.AuditOptions["RemoteUser"], this.AuditOptions["RemotePass"], null, new OperatingSystem(PlatformID.Unix, new Version(0, 0)), this.HostEnvironment); } else { throw new Exception("Unknown remote host authentication options."); } if (ssh_environment.IsConnected) { this.AuditEnvironment = ssh_environment; this.AuditEnvironmentIntialised = true; this.AuditEnvironmentMessage = AuditTarget_AuditEnvironmentMessageHandler; this.AuditEnvironment.MessageHandler -= HostEnvironmentMessage; this.AuditEnvironment.MessageHandler += this.AuditEnvironmentMessage; } else { ssh_environment = null; this.AuditEnvironmentIntialised = false; throw new Exception("Failed to initialise SSH audit environment."); } } else if (this.AuditOptions.Keys.Contains("WinRmRemoteIp") || this.AuditOptions.Keys.Contains("WinRmRemoteHost")) { if (!this.AuditOptions.Keys.Contains("RemoteUser") || !this.AuditOptions.Keys.Contains("RemotePass")) { throw new Exception("A remote user and password must be specified."); } WinRmAuditEnvironment winrm; if (this.AuditOptions.Keys.Contains("WinRmRemoteIp")) { winrm = new WinRmAuditEnvironment(this.HostEnvironmentMessage, this.AuditOptions["WinRmRemoteIp"] as IPAddress, (string)this.AuditOptions["RemoteUser"], (SecureString)this.AuditOptions["RemotePass"], this.HostEnvironment); if (winrm.IsConnected) { this.AuditEnvironment = winrm; this.AuditEnvironmentIntialised = true; this.AuditEnvironmentMessage = AuditTarget_AuditEnvironmentMessageHandler; this.AuditEnvironment.MessageHandler -= HostEnvironmentMessage; this.AuditEnvironment.MessageHandler += this.AuditEnvironmentMessage; } else { winrm = null; this.AuditEnvironmentIntialised = false; throw new Exception("Failed to initialise WinRM audit environment."); } } else { winrm = new WinRmAuditEnvironment(this.HostEnvironmentMessage, (string)this.AuditOptions["WinRmRemoteHost"], (string)this.AuditOptions["RemoteUser"], (SecureString)this.AuditOptions["RemotePass"], this.HostEnvironment); } } else if (this.AuditOptions.Keys.Contains("RemoteUser") || this.AuditOptions.Keys.Contains("RemotePass")) { throw new Exception("A remote host name must be specified."); } else if (this.AuditOptions.ContainsKey("Dockerized")) { this.AuditEnvironmentMessage = AuditTarget_AuditEnvironmentMessageHandler; this.AuditEnvironment = new DockerizedLocalEnvironment(this.AuditEnvironmentMessage); this.AuditEnvironmentIntialised = true; } else if (this.AuditOptions.ContainsKey("GitHubRepoName")) { if (!this.AuditOptions.ContainsKey("GitHubRepoOwner") || !this.AuditOptions.ContainsKey("GitHubRepoBranch")) { throw new ArgumentException("A required audit option for the GitHub environment is missing."); } string user_api_token = string.Empty; if (this.AuditOptions.ContainsKey("GitHubToken")) { user_api_token = (string)this.AuditOptions["GitHubToken"]; } GitHubAuditEnvironment github_environment = new GitHubAuditEnvironment(this.HostEnvironmentMessage, user_api_token, (string)this.AuditOptions["GitHubRepoOwner"], (string)this.AuditOptions["GitHubRepoName"], (string)this.AuditOptions["GitHubRepoBranch"], this.HostEnvironment); if (github_environment.RepositoryInitialised) { this.AuditEnvironment = github_environment; this.AuditEnvironmentIntialised = true; this.AuditEnvironmentMessage = AuditTarget_AuditEnvironmentMessageHandler; this.AuditEnvironment.MessageHandler -= HostEnvironmentMessage; this.AuditEnvironment.MessageHandler += this.AuditEnvironmentMessage; } else { github_environment = null; this.AuditEnvironmentIntialised = false; throw new Exception("Failed to initialise GitHub audit environment."); } } else if (this.AuditOptions.ContainsKey("GitLabRepoName")) { string api_token = string.Empty; if (!this.AuditOptions.ContainsKey("GitLabRepoUrl") || !this.AuditOptions.ContainsKey("GitLabRepoName") || !this.AuditOptions.ContainsKey("GitLabRepoBranch")) { throw new ArgumentException("A required audit option for the GitLab environment is missing."); } GitLabAuditEnvironment GitLab_environment = new GitLabAuditEnvironment(this.HostEnvironmentMessage, api_token, (string)this.AuditOptions["GitLabRepoUrl"], (string)this.AuditOptions["GitLabRepoName"], (string)this.AuditOptions["GitLabRepoBranch"], this.HostEnvironment); if (GitLab_environment.RepositoryInitialised) { this.AuditEnvironment = GitLab_environment; this.AuditEnvironmentIntialised = true; this.AuditEnvironmentMessage = AuditTarget_AuditEnvironmentMessageHandler; this.AuditEnvironment.MessageHandler -= HostEnvironmentMessage; this.AuditEnvironment.MessageHandler += this.AuditEnvironmentMessage; } else { GitLab_environment = null; this.AuditEnvironmentIntialised = false; throw new Exception("Failed to initialise audit environment."); } } else { this.AuditEnvironmentMessage = AuditTarget_AuditEnvironmentMessageHandler; this.AuditEnvironment = new LocalEnvironment(this.AuditEnvironmentMessage); this.AuditEnvironmentIntialised = true; } if (this.AuditOptions.ContainsKey("Profile")) { AuditFileInfo pf = this.AuditEnvironment.ConstructFile((string)this.AuditOptions["Profile"]); if (pf.Exists) { this.AuditProfile = new AuditProfile(this.AuditEnvironment, pf); } else { this.AuditEnvironment.Warning("The profile file {0} does not exist. No audit profile will be used.", pf.FullName); } } if (this.AuditEnvironment is IOperatingSystemEnvironment) { this.AuditEnvironment.GetOSName(); this.AuditEnvironment.GetOSVersion(); if (this.AuditOptions.ContainsKey("OSName")) { this.AuditEnvironment.OSName = (string)this.AuditOptions["OSName"]; this.AuditEnvironment.Info("Overriding audit environment OS name to {0}.", this.AuditEnvironment.OSName); } if (this.AuditOptions.ContainsKey("OSVersion")) { this.AuditEnvironment.OSVersion = (string)this.AuditOptions["OSVersion"]; this.AuditEnvironment.Info("Overriding audit environment OS version to {0}.", this.AuditEnvironment.OSVersion); } } } #endregion #region Setup data source options if (this.AuditOptions.ContainsKey("NoCache")) { this.DataSourceOptions.Add("NoCache", true); } if (this.AuditOptions.ContainsKey("DeleteCache")) { this.DataSourceOptions.Add("DeleteCache", true); } if (this.AuditOptions.ContainsKey("HttpsProxy")) { DataSourceOptions.Add("HttpsProxy", (Uri)this.AuditOptions["HttpsProxy"]); } if (this.AuditOptions.ContainsKey("IgnoreHttpsCertErrors")) { this.DataSourceOptions.Add("IgnoreHttpsCertErrors", true); } if (this.AuditOptions.ContainsKey("ApiUser")) { this.DataSourceOptions.Add("ApiUser", this.AuditOptions["ApiUser"]); } else if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("API_USER"))) { this.DataSourceOptions.Add("ApiUser", Environment.GetEnvironmentVariable("API_USER")); } if (this.AuditOptions.ContainsKey("ApiToken")) { this.DataSourceOptions.Add("ApiToken", this.AuditOptions["ApiToken"]); } else if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("API_TOKEN"))) { this.DataSourceOptions.Add("ApiToken", Environment.GetEnvironmentVariable("API_TOKEN")); } if (this.AuditOptions.ContainsKey("WithOSSI")) { this.DataSources.Add(new OSSIndexApiv3DataSource(this, DataSourceOptions)); } if (this.AuditOptions.ContainsKey("WithVulners")) { this.DataSources.Add(new VulnersDataSource(this, DataSourceOptions)); } #endregion #region Setup audit profile if (this.AuditEnvironment.FileExists("devaudit.yml")) { this.AuditProfile = new AuditProfile(this.AuditEnvironment, this.AuditEnvironment.ConstructFile("devaudit.yml")); } #endregion }
public AuditTarget(Dictionary <string, object> audit_options, EventHandler <EnvironmentEventArgs> controller_message_handler = null) { if (ReferenceEquals(audit_options, null)) { throw new ArgumentNullException("audit_options"); } this.AuditOptions = audit_options; this.ControllerMessage = controller_message_handler; this.HostEnvironmentMessage = AuditTarget_HostEnvironmentMessageHandler; this.HostEnvironment = new LocalEnvironment(this.HostEnvironmentMessage); this.HostEnvironment.ScriptEnvironment.MessageHandler += this.AuditTarget_ScriptEnvironmentMessageHandler; if (this.AuditOptions.ContainsKey("Dockerized")) { this.HostEnvironment.IsDockerContainer = true; } this.HostEnvironmentInitialised = true; if (this.AuditOptions.Keys.Contains("DockerContainer")) { DockerAuditEnvironment docker_environment = new DockerAuditEnvironment(this.HostEnvironmentMessage, (string)this.AuditOptions["DockerContainer"], new OperatingSystem(PlatformID.Unix, new Version(0, 0)), this.HostEnvironment); if (string.IsNullOrEmpty(docker_environment.Container)) { this.AuditEnvironmentIntialised = false; throw new Exception("Failed to initialise audit environment."); } else if (!docker_environment.ContainerRunning) { this.AuditEnvironmentIntialised = false; throw new Exception("The Docker container is not currently running and DevAudit does not know how to run your container. Ensure your container is running before attempting to" + "audit it."); } else { this.AuditEnvironment = docker_environment; this.AuditEnvironmentIntialised = true; this.AuditEnvironmentMessage = AuditTarget_AuditEnvironmentMessageHandler; this.AuditEnvironment.MessageHandler -= HostEnvironmentMessage; this.AuditEnvironment.MessageHandler += this.AuditEnvironmentMessage; } } else if (this.AuditOptions.Keys.Contains("RemoteHost")) { string client; SshAuditEnvironment ssh_environment = null; if (this.HostEnvironment.OS.Platform == PlatformID.Win32NT) { client = this.AuditOptions.Keys.Contains("WindowsUsePlink") ? "plink" : this.AuditOptions.Keys.Contains("WindowsUsePlink") ? "openssh" : "ssh"; } else { client = "ssh"; } if (this.AuditOptions.Keys.Contains("RemoteUser") && this.AuditOptions.Keys.Contains("RemoteKeyFile")) { if (this.AuditOptions.Keys.Contains("RemoteKeyPassPhrase")) { ssh_environment = new SshAuditEnvironment(this.HostEnvironmentMessage, client, (string)this.AuditOptions["RemoteHost"], (int)this.AuditOptions["RemoteSshPort"], (string)this.AuditOptions["RemoteUser"], this.AuditOptions["RemoteKeyPassPhrase"], (string)this.AuditOptions["RemoteKeyFile"], new OperatingSystem(PlatformID.Unix, new Version(0, 0)), this.HostEnvironment); } else { ssh_environment = new SshAuditEnvironment(this.HostEnvironmentMessage, client, (string)this.AuditOptions["RemoteHost"], (int)this.AuditOptions["RemoteSshPort"], (string)this.AuditOptions["RemoteUser"], null, (string)this.AuditOptions["RemoteKeyFile"], new OperatingSystem(PlatformID.Unix, new Version(0, 0)), this.HostEnvironment); } } else if (this.AuditOptions.Keys.Contains("RemoteUser") && this.AuditOptions.Keys.Contains("RemotePass")) { ssh_environment = new SshAuditEnvironment(this.HostEnvironmentMessage, client, (string)this.AuditOptions["RemoteHost"], (int)this.AuditOptions["RemoteSshPort"], (string)this.AuditOptions["RemoteUser"], this.AuditOptions["RemotePass"], null, new OperatingSystem(PlatformID.Unix, new Version(0, 0)), this.HostEnvironment); } else { throw new Exception("Unknown remote host authentication options."); } if (ssh_environment.IsConnected) { this.AuditEnvironment = ssh_environment; this.AuditEnvironmentIntialised = true; this.AuditEnvironmentMessage = AuditTarget_AuditEnvironmentMessageHandler; this.AuditEnvironment.MessageHandler -= HostEnvironmentMessage; this.AuditEnvironment.MessageHandler += this.AuditEnvironmentMessage; } else { ssh_environment = null; this.AuditEnvironmentIntialised = false; throw new Exception("Failed to initialise SSH audit environment."); } } else if (this.AuditOptions.Keys.Contains("WinRmRemoteIp") || this.AuditOptions.Keys.Contains("WinRmRemoteHost")) { if (!this.AuditOptions.Keys.Contains("RemoteUser") || !this.AuditOptions.Keys.Contains("RemotePass")) { throw new Exception("A remote user and password must be specified."); } WinRmAuditEnvironment winrm; if (this.AuditOptions.Keys.Contains("WinRmRemoteIp")) { winrm = new WinRmAuditEnvironment(this.HostEnvironmentMessage, this.AuditOptions["WinRmRemoteIp"] as IPAddress, (string)this.AuditOptions["RemoteUser"], (SecureString)this.AuditOptions["RemotePass"], this.HostEnvironment); if (winrm.IsConnected) { this.AuditEnvironment = winrm; this.AuditEnvironmentIntialised = true; this.AuditEnvironmentMessage = AuditTarget_AuditEnvironmentMessageHandler; this.AuditEnvironment.MessageHandler -= HostEnvironmentMessage; this.AuditEnvironment.MessageHandler += this.AuditEnvironmentMessage; } else { winrm = null; this.AuditEnvironmentIntialised = false; throw new Exception("Failed to initialise WinRM audit environment."); } } else { winrm = new WinRmAuditEnvironment(this.HostEnvironmentMessage, (string)this.AuditOptions["WinRmRemoteHost"], (string)this.AuditOptions["RemoteUser"], (SecureString)this.AuditOptions["RemotePass"], this.HostEnvironment); } } else if (this.AuditOptions.Keys.Contains("RemoteUser") || this.AuditOptions.Keys.Contains("RemotePass")) { throw new Exception("A remote host name must be specified."); } else if (this.AuditOptions.ContainsKey("Dockerized")) { this.AuditEnvironmentMessage = AuditTarget_AuditEnvironmentMessageHandler; this.AuditEnvironment = new DockerizedLocalEnvironment(this.AuditEnvironmentMessage); this.AuditEnvironmentIntialised = true; } else if (this.AuditOptions.ContainsKey("GitHubRepoName")) { if (!this.AuditOptions.ContainsKey("GitHubRepoOwner") || !this.AuditOptions.ContainsKey("GitHubRepoBranch")) { throw new ArgumentException("A required audit option for the GitHub environment is missing."); } string user_api_token = string.Empty; if (this.AuditOptions.ContainsKey("GitHubToken")) { user_api_token = (string)this.AuditOptions["GitHubToken"]; } GitHubAuditEnvironment github_environment = new GitHubAuditEnvironment(this.HostEnvironmentMessage, user_api_token, (string)this.AuditOptions["GitHubRepoOwner"], (string)this.AuditOptions["GitHubRepoName"], (string)this.AuditOptions["GitHubRepoBranch"], this.HostEnvironment); if (github_environment.RepositoryInitialised) { this.AuditEnvironment = github_environment; this.AuditEnvironmentIntialised = true; this.AuditEnvironmentMessage = AuditTarget_AuditEnvironmentMessageHandler; this.AuditEnvironment.MessageHandler -= HostEnvironmentMessage; this.AuditEnvironment.MessageHandler += this.AuditEnvironmentMessage; } else { github_environment = null; this.AuditEnvironmentIntialised = false; throw new Exception("Failed to initialise GitHub audit environment."); } } else if (this.AuditOptions.ContainsKey("GitLabRepoName")) { string api_token = string.Empty; if (!this.AuditOptions.ContainsKey("GitLabRepoUrl") || !this.AuditOptions.ContainsKey("GitLabRepoName") || !this.AuditOptions.ContainsKey("GitLabRepoBranch")) { throw new ArgumentException("A required audit option for the GitLab environment is missing."); } GitLabAuditEnvironment GitLab_environment = new GitLabAuditEnvironment(this.HostEnvironmentMessage, api_token, (string)this.AuditOptions["GitLabRepoUrl"], (string)this.AuditOptions["GitLabRepoName"], (string)this.AuditOptions["GitLabRepoBranch"], this.HostEnvironment); if (GitLab_environment.RepositoryInitialised) { this.AuditEnvironment = GitLab_environment; this.AuditEnvironmentIntialised = true; this.AuditEnvironmentMessage = AuditTarget_AuditEnvironmentMessageHandler; this.AuditEnvironment.MessageHandler -= HostEnvironmentMessage; this.AuditEnvironment.MessageHandler += this.AuditEnvironmentMessage; } else { GitLab_environment = null; this.AuditEnvironmentIntialised = false; throw new Exception("Failed to initialise audit environment."); } } else { this.AuditEnvironmentMessage = AuditTarget_AuditEnvironmentMessageHandler; this.AuditEnvironment = new LocalEnvironment(this.AuditEnvironmentMessage); this.AuditEnvironmentIntialised = true; } if (this.AuditOptions.ContainsKey("Profile")) { AuditFileInfo pf = this.AuditEnvironment.ConstructFile((string)this.AuditOptions["Profile"]); if (pf.Exists) { this.AuditProfile = new AuditProfile(this.AuditEnvironment, pf); } else { this.AuditEnvironment.Warning("The profile file {0} does not exist. No audit profile will be used.", pf.FullName); } } }