private void ProcessCSPReport(Session session) { string requestBody = session.GetRequestBodyAsString(); if (requestBody.Length > 0) { try { CSPReport cspReport = CSPReport.Parse(requestBody); if (cspReport.cspReport != null && cspReport.cspReport.documentUri != null) { logger.Log("Got report for " + cspReport.cspReport.documentUri + " via " + session.fullUrl); } logger.Log("Adding " + cspReport.ToString()); collector.Add(cspReport, session.PathAndQuery == "/unsafe-eval" ? CSPRuleCollector.InterpretBlank.UnsafeEval : CSPRuleCollector.InterpretBlank.UnsafeInline); logger.Log("Total " + collector.ToString()); } catch (Exception exception) { logger.Log("Invalid CSP - " + exception); } } }
public void Add(CSPReport cspReport, InterpretBlank blankIs) { if (!(cspReport.cspReport.blockedUri == null || cspReport.cspReport.documentUri == null || (cspReport.cspReport.violatedDirective == null && cspReport.cspReport.effectiveDirective == null))) { string documentUri = cspReport.cspReport.documentUri; string documentUriOrigin = UriOrigin(documentUri); string directive = cspReport.cspReport.effectiveDirective == null ? cspReport.cspReport.violatedDirective : cspReport.cspReport.effectiveDirective; string blockedUri = cspReport.cspReport.blockedUri; if (blockedUri.Trim().Length == 0) { // How to handle unsafe-eval? Might require a different report-uri and rule set. blockedUri = blankIs == InterpretBlank.UnsafeInline ? "'unsafe-inline'" : "'unsafe-eval'"; } else if (blockedUri.IndexOf(":") >= 0) { blockedUri = UriWrtDocumentUri(UriOrigin(blockedUri), documentUriOrigin); } else if (blockedUri == "self") // Firefox can return self as the blocked-uri. { blockedUri = "'self'"; } else { // Report can give out schemes with no delimiters or anything else. blockedUri = blockedUri + ":"; } // directive may be script-src or script-src none. We want just the first part. directive = directive.Split(' ')[0]; cacheLock.EnterWriteLock(); try { if (!rules.Keys.Contains(documentUri)) { rules.Add(documentUri, new Dictionary <string, HashSet <string> >()); } if (!rules[documentUri].Keys.Contains(directive)) { rules[documentUri].Add(directive, new HashSet <string>()); } rules[documentUri][directive].Add(blockedUri); } finally { cacheLock.ExitWriteLock(); } OnRuleAddedOrModified.Invoke(documentUri, Get(documentUri)); } else { FiddlerExtension.Log("FiddlerCSP: Invalid cspreport: " + cspReport); } }
public void AutoTamperRequestBefore(Session session) { if (!Settings.enabled) { return; } if (!session.HostnameIs(reportHost) || session.isFTP) { return; } // TODO: We should offer an option to hide the reports from Fiddler; change "ui-strikeout" to "ui-hide" in the next line session["ui-strikeout"] = "CSPReportGenerator"; if (session.HTTPMethodIs("CONNECT")) { session["x-replywithtunnel"] = "CSPReportGenerator"; return; } session.utilCreateResponseAndBypassServer(); session.oResponse.headers.Add("Content-Type", "text/html"); session.ResponseBody = Encoding.UTF8.GetBytes("<!doctype html><HTML><BODY><H1>Report received. Thanks. You're the best.</H1></BODY></HTML>"); string requestBody = session.GetRequestBodyAsString(); if (requestBody.Length > 0) { try { CSPReport cspReport = CSPReport.Parse(requestBody); if (cspReport.cspReport != null && cspReport.cspReport.documentUri != null) { logger.Log("Got report for " + cspReport.cspReport.documentUri + " via " + session.fullUrl); } logger.Log("Adding " + cspReport.ToString()); collector.Add(cspReport, session.PathAndQuery == "/unsafe-eval" ? CSPRuleCollector.InterpretBlank.UnsafeEval : CSPRuleCollector.InterpretBlank.UnsafeInline); logger.Log("Total " + collector.ToString()); } catch (Exception exception) { logger.Log("Invalid CSP - " + exception); } } }
public void AutoTamperRequestBefore(Session session) { if (!session.isTunnel && !session.isFTP) { bool handled = false; if (session.oRequest.host == reportHost && Settings.enabled) { // Not sure the best way to handle these report URI requests. They are real requests from the browser but // only generated because of this extension. Not sure if should be hidden from Fiddler's view or marked specially. session.utilCreateResponseAndBypassServer(); session.oResponse.headers.Add("Content-Type", "text/html"); session.ResponseBody = Encoding.UTF8.GetBytes("<!doctype html><HTML><BODY><H1>Report received. Thanks. You're the best.</H1></BODY></HTML>"); string requestBody = session.GetRequestBodyAsString(); if (requestBody != null && requestBody.Length > 0) { CSPReport cspReport = CSPReport.TryParse(requestBody); if (cspReport != null && cspReport.cspReport != null && cspReport.cspReport.documentUri != null) { Log("Got report for " + cspReport.cspReport.documentUri); } Log("Adding " + cspReport.ToString()); collector.Add(cspReport, session.PathAndQuery == "/unsafe-eval" ? CSPRuleCollector.InterpretBlank.UnsafeEval : CSPRuleCollector.InterpretBlank.UnsafeInline); Log("Total " + collector.ToString()); handled = true; } } if (!handled) { session.bBufferResponse = true; } } }
public void Add(CSPReport cspReport, InterpretBlank blankIs) { if (cspReport.cspReport.blockedUri == null) { logger.Log("Invalid CSP Report - missing blocked-uri property."); } else if (cspReport.cspReport.documentUri == null) { logger.Log("Invalid CSP Report - missing document-uri property."); } else if (cspReport.cspReport.violatedDirective == null && cspReport.cspReport.effectiveDirective == null) { logger.Log("Invalid CSP Report - missing violated-directive and effective-directive properties."); } else { string documentUri = cspReport.cspReport.documentUri; string documentUriOrigin = UriOrigin(documentUri); string directive = cspReport.cspReport.effectiveDirective == null ? cspReport.cspReport.violatedDirective : cspReport.cspReport.effectiveDirective; string blockedUri = cspReport.cspReport.blockedUri; if (blockedUri.Trim().Length == 0) { // A blank blocked-uri indicates either unsafe-inline or unsafe-eval. The caller tells us // which it is. blockedUri = blankIs == InterpretBlank.UnsafeInline ? "'unsafe-inline'" : "'unsafe-eval'"; } else if (blockedUri.IndexOf(":") >= 0) // If there's a colon, assume its a URI. { blockedUri = UriWrtDocumentUri(UriOrigin(blockedUri), documentUriOrigin); } else if (blockedUri == "self") // Firefox can return self as the blocked-uri. { blockedUri = "'self'"; } else // Lastly CSP reports may contain schemes with no delimiters just the scheme name. { blockedUri = blockedUri + ":"; } // Directive might be something like script-src or script-src none. We want just the first part. directive = directive.Split(' ')[0]; cacheLock.EnterWriteLock(); try { if (!rules.Keys.Contains(documentUri)) { rules.Add(documentUri, new Dictionary<string, HashSet<string>>()); } if (!rules[documentUri].Keys.Contains(directive)) { rules[documentUri].Add(directive, new HashSet<string>()); } rules[documentUri][directive].Add(blockedUri); } finally { cacheLock.ExitWriteLock(); } if (OnRuleAddedOrModified != null) { OnRuleAddedOrModified.Invoke(documentUri, Get(documentUri)); } } }
public void Add(CSPReport cspReport, InterpretBlank blankIs) { if (cspReport.cspReport.blockedUri == null) { logger.Log("Invalid CSP Report - missing blocked-uri property."); } else if (cspReport.cspReport.documentUri == null) { logger.Log("Invalid CSP Report - missing document-uri property."); } else if (cspReport.cspReport.violatedDirective == null && cspReport.cspReport.effectiveDirective == null) { logger.Log("Invalid CSP Report - missing violated-directive and effective-directive properties."); } else { string documentUri = cspReport.cspReport.documentUri; string documentUriOrigin = UriOrigin(documentUri); string directive = cspReport.cspReport.effectiveDirective == null ? cspReport.cspReport.violatedDirective : cspReport.cspReport.effectiveDirective; string blockedUri = cspReport.cspReport.blockedUri; if (blockedUri.Trim().Length == 0) { // A blank blocked-uri indicates either unsafe-inline or unsafe-eval. The caller tells us // which it is. blockedUri = blankIs == InterpretBlank.UnsafeInline ? "'unsafe-inline'" : "'unsafe-eval'"; } else if (blockedUri.IndexOf(":") >= 0) // If there's a colon, assume its a URI. { blockedUri = UriWrtDocumentUri(UriOrigin(blockedUri), documentUriOrigin); } else if (blockedUri == "self") // Firefox can return self as the blocked-uri. { blockedUri = "'self'"; } else // Lastly CSP reports may contain schemes with no delimiters just the scheme name. { blockedUri = blockedUri + ":"; } // Directive might be something like script-src or script-src none. We want just the first part. directive = directive.Split(' ')[0]; cacheLock.EnterWriteLock(); try { if (!rules.Keys.Contains(documentUri)) { rules.Add(documentUri, new Dictionary <string, HashSet <string> >()); } if (!rules[documentUri].Keys.Contains(directive)) { rules[documentUri].Add(directive, new HashSet <string>()); } rules[documentUri][directive].Add(blockedUri); } finally { cacheLock.ExitWriteLock(); } if (OnRuleAddedOrModified != null) { OnRuleAddedOrModified.Invoke(documentUri, Get(documentUri)); } } }
private void ValidateCSPReportSet( string documentUri, string expectedCsp, string[] cspReportsUnsafeInline, string[] cspReportsUnsafeEval) { CSPRuleCollector collector = new CSPRuleCollector(logger); foreach (var cspReport in cspReportsUnsafeInline.Select(cspReportAsString => CSPReport.Parse(cspReportAsString))) { collector.Add(cspReport, CSPRuleCollector.InterpretBlank.UnsafeInline); } foreach (var cspReport in cspReportsUnsafeEval.Select(cspReportAsString => CSPReport.Parse(cspReportAsString))) { collector.Add(cspReport, CSPRuleCollector.InterpretBlank.UnsafeEval); } Assert.AreEqual(collector.Get(documentUri), expectedCsp); }