private XmlDocument GetXmlReport(WatcherResultCollection resultslist) { XmlDocument doc = new XmlDocument(); XmlDeclaration xmlDec; XmlElement root = doc.DocumentElement; XmlElement issue = null; XmlElement level = null; XmlElement url = null; XmlElement type = null; XmlElement desc = null; // Get current Watcher version Version currentver = new UpdateManager().CurrentVersionEngine; // Create the very beginning Xml Declaration xmlDec = doc.CreateXmlDeclaration("1.0", "utf-8", null); root = doc.CreateElement("watcher"); root.SetAttribute("version", currentver.ToString()); root.SetAttribute("date", DateTime.Today.ToLongDateString()); root.SetAttribute("originDomain", WatcherEngine.Configuration.OriginDomain.ToString()); root.SetAttribute("trustedDomains", WatcherEngine.Configuration.GetTrustedDomainsAsString()); root.SetAttribute("enabledChecks", WatcherEngine.CheckManager.GetEnabledChecksAsString()); doc.AppendChild(root); doc.InsertBefore(xmlDec, root); foreach (WatcherResult item in resultslist) { WatcherEngine.ProgressDialog.labelOperation.Text = "Saving Finding: " + item.Title; WatcherEngine.ProgressDialog.UpdateProgress(); issue = doc.CreateElement("issue"); level = doc.CreateElement("level"); level.InnerText = item.Severity.ToString(); url = doc.CreateElement("url"); url.InnerText = item.URL; type = doc.CreateElement("type"); type.InnerText = item.Title; desc = doc.CreateElement("description"); desc.InnerText = item.Description; issue.AppendChild(type); issue.AppendChild(level); issue.AppendChild(url); issue.AppendChild(desc); root.AppendChild(issue); } return(doc); }
/// <summary> /// This method is the delegate called when an export is to be handled using a plugin. /// </summary> /// <returns>True if the operation succeeded, False otherwise.</returns> private void ExportResults(WatcherOutputPlugin plugin, WatcherResultCollection results, String filename) { // Perform the Save operation. Not every save operation will return a // stream; therefore, a null return value is not necessarily an error. // // TODO: This class is a candidate for redesign as it handles exports with different semantics (e.g., TFS) Stream outputStream = plugin.SaveResult(results); if (outputStream != null) { using (FileStream fs = new FileStream(filename, FileMode.Create)) { Utility.ReadWriteStream(outputStream, fs); } } }
public override Stream SaveResult(WatcherResultCollection resultslist) { // Reset the bar's progress position WatcherEngine.ProgressDialog.ProgressValue = 0; WatcherEngine.ProgressDialog.MaximumRange = resultslist.Count; WatcherEngine.ProgressDialog.MinimumRange = 0; WatcherEngine.ProgressDialog.Increment = 1; WatcherEngine.ProgressDialog.BodyText = "Exporting results to XML:"; MemoryStream s = new MemoryStream(); XmlDocument doc = GetXmlReport(resultslist); if (doc != null) { doc.Save(s); return(s); } return(null); }
/// <summary> /// This is an event handler called when the user clicks the Export button. /// </summary> private void FileSaveButton_Click(object sender, EventArgs e) { // If no export method is selected, bail. if (cbExportMethod.SelectedItem == null) { MessageBox.Show("No export method selected. Please select an export method.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } // Gather the selected results WatcherResultCollection selectedResults = new WatcherResultCollection(); foreach (AlertListViewItem item in alertListView.Items) { WatcherResult result = new WatcherResult(); result.Description = item.Description; result.Severity = item.Severity; result.Title = item.TypeX; result.URL = item.URL; selectedResults.Add(result); } // The export method refers to an instance of the output plugin WatcherOutputPlugin outputPlugin = cbExportMethod.SelectedItem as WatcherOutputPlugin; if (outputPlugin == null) { Trace.TraceError("WatcherResultsControl: Invalid export method selected."); MessageBox.Show("Unable to use the selected export method.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } String filename = String.Empty; String filter = String.Empty; // If this plugin is a file-based export, open a file save dialog and allow // the user to select the output filename. Use the default values provided // by the plugin. // // TODO: Plugin should be interface-based or provide a property to tell whether it is a file-based export or not if (outputPlugin.GetExportDefaults(ref filename, ref filter)) { SaveFileDialog dlg = new SaveFileDialog(); dlg.FileName = filename; dlg.Filter = filter; dlg.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); dlg.FilterIndex = 1; dlg.RestoreDirectory = true; // Display the dialog, and if canceled, abort. if (dlg.ShowDialog() != DialogResult.OK) { Trace.TraceInformation("User canceled export."); return; } // Update the filename selected by the user filename = dlg.FileName; } // This is the export progress dialog shown during the export process // CHANGE - potentially breaking change, removing progress dialog // which seems to be dreadfully slowing down real work. WatcherEngine.ProgressDialog.Show(); WatcherEngine.ProgressDialog.Title = "Export Data"; // Perform the export asynchronously ExportResultsCallback callback = new ExportResultsCallback(ExportResults); callback.BeginInvoke(outputPlugin, selectedResults, filename, // This is the anonymous callback method delegate(IAsyncResult ar) { try { // Tidy up after the export ExportResultsCallback _callback = (ExportResultsCallback)ar.AsyncState; _callback.EndInvoke(ar); // NOTE: This will throw any unhandled exceptions that happened during the call } catch (WatcherException ex) { Trace.TraceError("Exception: {0}", ex.Message); if (ex.Data.Contains("UserAbortedOperation") == false) { // Only show a message box if the user hasn't already seen one (i.e., they've elected to abort the operation) MessageBox.Show(this, String.Format("{0}", ex.Message), "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); } return; } finally { // CHANGE - see above breaking change, removing progress dialog. WatcherEngine.ProgressDialog.Hide(); } MessageBox.Show(this, "Items successfully exported.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); }, // Pass the callback as user data so we can call EndInvoke() when the operation completes // to retrieve the result. callback); }
private XmlDocument GetHtmlReport(WatcherResultCollection resultslist) { doc = new XmlDocument(); XmlDocumentType doctype; // The HTML element XmlElement root = doc.DocumentElement; // The HEAD element XmlElement head = null; XmlElement title = null; // The meta tag element XmlElement meta = null; // The style element XmlElement style = null; XmlElement script = null; XmlElement toggleHidden = null; // The body element XmlElement body = null; XmlElement level = null; XmlElement url = null; XmlElement typex = null; XmlElement desc = doc.DocumentElement; XmlElement divResults = doc.DocumentElement; XmlElement divTOC = doc.DocumentElement; XmlDocumentFragment metaIE = null; string css = @" body { font-family: 'Arial'; font-size: 12pt; } .issue { margin-left: 30px; display: block; } .type { color: #FF0000; font-size: 15pt; display: block; } .level:before { font-weight: bolder; font-variant:small-caps; content: 'severity: '; } .level { display: block; } pre { visibility: hidden; display: none; } .description { width: 80%; margin-bottom: 30pt; margin-left: 30pt; white-space: pre; } .toggler { font-weight: normal; font-style:italic; } .toggler:before { font-style:normal; font-weight: bolder; font-variant:small-caps; content: 'description: '; text-decoration: none; } .url { display: block; } .url:before { font-weight: bolder; font-variant:small-caps; content: 'url: '; text-decoration: none; } "; string js = @" function toggleVisibility(id){ var elem = document.getElementById(id); if (elem.style.visibility=='hidden'){ elem.style.visibility='visible'; elem.style.display = 'block'; } else { elem.style.visibility='hidden'; elem.style.display = 'none'; } } "; string introduction = @"<h1>Watcher Security Report</h1> <p>The following issues were automatically identified by the <a href='http://www.casaba.com'>Watcher security plugin</a> for <a href='http://www.fiddler2.com'>Fiddler</a>.</p>"; doctype = doc.CreateDocumentType("html", "-//W3C//DTD XHTML 1.0 Transitional//EN", null, null); doc.AppendChild(doctype); root = doc.CreateElement("html"); doc.AppendChild(root); head = doc.CreateElement("head"); root.AppendChild(head); // Add a title title = doc.CreateElement("title"); title.InnerText = "Watcher Security Report"; head.AppendChild(title); // Setup the meta tag meta = doc.CreateElement("meta"); meta.SetAttribute("http-equiv", "Content-Type"); meta.SetAttribute("content", "text/html; charset=utf-8"); head.AppendChild(meta); metaIE = doc.CreateDocumentFragment(); metaIE.InnerXml = "<meta http-equiv='x-ua-compatible' content='IE=8' />"; head.AppendChild(metaIE); // Setup the style tag style = doc.CreateElement("style"); style.SetAttribute("type", "text/css"); style.InnerXml = css; head.AppendChild(style); // Setup script tag script = doc.CreateElement("script"); script.SetAttribute("language", "JavaScript"); script.InnerXml = js;; head.AppendChild(script); // Create the body tag body = doc.CreateElement("body"); body.InnerXml = introduction; // Insert the body after the head root.InsertAfter(body, head); // Create a TOC and intro divTOC = doc.CreateElement("div"); divTOC.SetAttribute("class", "TOC"); divTOC.SetAttribute("id", "TOC"); // Start the ordered list string contents = "<ol>"; // Create div to hold selectedResults divResults = doc.CreateElement("div"); divResults.SetAttribute("class", "selectedResults"); divResults.SetAttribute("id", "selectedResults"); int x = 0; foreach (WatcherResult item in resultslist) { WatcherEngine.ProgressDialog.labelOperation.Text = "Saving Finding: " + item.Title; WatcherEngine.ProgressDialog.UpdateProgress(); string resultTitle = System.Security.SecurityElement.Escape(item.Title); level = doc.CreateElement("span"); level.SetAttribute("class", "level"); // Safe from XSS, we control this and its treated as text. level.InnerText = item.Severity.ToString(); url = doc.CreateElement("span"); url.SetAttribute("class", "url"); // Use InnertText for URL to make it safe from XSS url.InnerText = item.URL; typex = doc.CreateElement("span"); typex.SetAttribute("class", "type"); // Safe from XSS? We don't control all of the titles - at least not the body regex one. typex.InnerXml = "<a name='content-" + x + "'>" + resultTitle + "</a>"; // Update the string holding the TOC // Link to anchor contents = String.Concat(contents, String.Format("<li><a href='#content-{0}' class='list'>{1}</a></li>\r\n", x, resultTitle)); toggleHidden = doc.CreateElement("span"); toggleHidden.SetAttribute("class", "toggler"); toggleHidden.InnerXml = "<input type=\"button\" value=\"show/hide\" onclick=\"toggleVisibility('description-" + x + "');\" />"; desc = doc.CreateElement("pre"); desc.SetAttribute("class", "description"); desc.SetAttribute("id", "description-" + x); desc.SetAttribute("style", "visibility:hidden;display:none"); // Safe from XSS, we control this and its treated as text. desc.InnerText = item.Description; divResults.AppendChild(typex); divResults.AppendChild(level); divResults.AppendChild(url); divResults.AppendChild(toggleHidden); divResults.AppendChild(desc); x++; } // Append the TOC to the body // End the ordered list contents = String.Concat(contents, "</ol>"); divTOC.InnerXml = contents; body.AppendChild(divTOC); // Append the selectedResults to the body body.AppendChild(divResults); return(doc); }
//Standard Compliance Handling? /// <summary> /// This method Takes a single result and processes for output. /// </summary> public abstract Stream SaveResult(WatcherResultCollection result);
/// <summary> /// This method saves the collection of Watcher findings to the Team Foundation Server specified in the UI. /// </summary> /// <param name="watcherResults">Collection of Watcher findings to export.</param> /// <returns>This method always returns null for Team Foundation Server exports.</returns> public override Stream SaveResult(WatcherResultCollection watcherResults) { Trace.TraceInformation("Exporting {0} items...", watcherResults.Count); // Create an instance of the Watcher Result to Team Foundation Work Item adapter _adapter = TeamFoundationAdapter.Create(_configurationPanel.servername, _configurationPanel.projectname); Trace.TraceInformation("Connecting to Team Foundation Server..."); WatcherEngine.ProgressDialog.UpdateProgress("Connecting to Team Foundation Server..."); // Attempt to connect to the Team Foundation Server specified in the configuration panel try { _adapter.Connect(); } catch (WatcherException ex) { Trace.TraceError("Unable to connect to Team Foundation Server \"{0}\": {1}", _adapter.ServerName, ex.Message); throw; } Trace.TraceInformation("Opening the project..."); WatcherEngine.ProgressDialog.UpdateProgress("Opening the project..."); // Attempt to open the Team Foundation Project specified in the configuration panel try { _adapter.OpenProject(); } catch (WatcherException ex) { Trace.TraceError("Unable to connect to Team Foundation Server \"{0}\": {1}", _adapter.ServerName, ex.Message); throw; } // TODO: collect results and throw with error // Enumerate and export the results for (int ndx = 0; ndx < watcherResults.Count;) { // Reset the progress bar for each finding exported WatcherEngine.ProgressDialog.ProgressValue = 10; // Determine the current finding WatcherResult watcherResult = watcherResults[ndx]; // Export the current finding try { ExportResult(watcherResult); } catch (WatcherException ex) { DialogResult dlgResult = MessageBox.Show(ex.Message, "Error", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Warning); switch (dlgResult) { case DialogResult.Abort: Trace.TraceInformation("User has aborted the export."); WatcherException e = new WatcherException(ex.Message); e.Data["UserAbortedOperation"] = true; // Prevent an additional dialog from appearing when the exception is throw e; // rethrown to abort the operation. case DialogResult.Retry: Trace.TraceInformation("User is attempting to retry the export of item \"{0}\".", watcherResult.Title); continue; case DialogResult.Ignore: break; } } // Continue with the next finding ++ndx; } // TODO: Make the SaveResult return type more generic. Returning null here is // a kludge since output plugins were originally intended to return a Stream. return(null); }