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);
                }
            }
        }
Example #2
0
        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);
                }
            }
        }
Example #4
0
        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));
                }
            }
        }
Example #6
0
        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));
                }
            }
        }
Example #7
0
        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);
        }