Build Cookie Scanning Extension

Below is the code for the Fiddler Classic Privacy Scanner add-on.

    using System;
    using System.Collections;
    using System.Globalization;
    using System.Collections.Generic;
    using System.Windows.Forms;
    using System.Text;
    using Fiddler;
    using System.IO;
    using System.Diagnostics;
    using Microsoft.Win32;
    using System.Reflection;
    using System.Text.RegularExpressions;

    [assembly: Fiddler.RequiredVersion("2.3.9.0")]
    [assembly: AssemblyVersion("1.0.1.0")]
    [assembly: AssemblyTitle("PrivacyScanner")]
    [assembly: AssemblyDescription("Scans for Cookies and P3P")]
    [assembly: AssemblyCompany("Eric Lawrence")]
    [assembly: AssemblyProduct("PrivacyScanner")]

    public class TagCookies : IAutoTamper2
    {
        private bool bEnabled = false;
        private bool bEnforceP3PValidity = false;
        private bool bCreatedColumn = false;
        private System.Windows.Forms.MenuItem miEnabled;
        private System.Windows.Forms.MenuItem miEnforceP3PValidity;
        private System.Windows.Forms.MenuItem mnuCookieTag;

        public void OnLoad()
        {
            /*
     * NB: You might not get called here until ~after~ one of the AutoTamper methods was called.
     * This is okay for us, because we created our mnuContentBlock in the constructor and its simply not
     * visible anywhere until this method is called and we merge it onto the Fiddler Classic Main menu.
     */
            FiddlerApplication.UI.mnuMain.MenuItems.Add(mnuCookieTag);
        }

        public void OnBeforeUnload() {  /*noop*/   }

        private void InitializeMenu()
        {
            this.miEnabled = new System.Windows.Forms.MenuItem("&Enabled");
            this.miEnforceP3PValidity = new System.Windows.Forms.MenuItem("&Rename P3P header if invalid");

            this.miEnabled.Index = 0;
            this.miEnforceP3PValidity.Index = 1;

            this.mnuCookieTag = new System.Windows.Forms.MenuItem();
            this.mnuCookieTag.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { this.miEnabled, this.miEnforceP3PValidity });
            this.mnuCookieTag.Text = "Privacy";

            this.miEnabled.Click += new System.EventHandler(this.miEnabled_Click);
            this.miEnabled.Checked = bEnabled;

            this.miEnforceP3PValidity.Click += new System.EventHandler(this.miEnforceP3PValidity_Click);
            this.miEnforceP3PValidity.Checked = bEnforceP3PValidity;
        }

        public void miEnabled_Click(object sender, EventArgs e)
        {
            miEnabled.Checked = !miEnabled.Checked;
            bEnabled = miEnabled.Checked;
            this.miEnforceP3PValidity.Enabled = bEnabled;
            if (bEnabled) { EnsureColumn(); }
            FiddlerApplication.Prefs.SetBoolPref("extensions.tagcookies.enabled", bEnabled);
        }

        public void miEnforceP3PValidity_Click(object sender, EventArgs e)
        {
            miEnforceP3PValidity.Checked = !miEnforceP3PValidity.Checked;
            bEnforceP3PValidity = miEnforceP3PValidity.Checked;
            FiddlerApplication.Prefs.SetBoolPref("extensions.tagcookies.EnforceP3PValidity", bEnforceP3PValidity);
        }

        private void EnsureColumn()
        {
            if (bCreatedColumn) return;

            FiddlerApplication.UI.lvSessions.AddBoundColumn("Privacy Info", 1, 120, "X-Privacy");

            bCreatedColumn = true;
        }

        public TagCookies()
        {
            this.bEnabled = FiddlerApplication.Prefs.GetBoolPref("extensions.tagcookies.enabled", false);
            this.bEnforceP3PValidity = FiddlerApplication.Prefs.GetBoolPref("extensions.tagcookies.EnforceP3PValidity", true);
            InitializeMenu();

            if (bEnabled) { EnsureColumn(); } else { this.miEnforceP3PValidity.Enabled = false; }
        }

        private void SetP3PStateFromHeader(string sValue, ref P3PState oP3PState)
        {
            if (string.IsNullOrEmpty(sValue))
            {
                return;
            }

            string sUnsatCat = String.Empty;
            string sUnsatPurpose = String.Empty;
            sValue = sValue.Replace('\'', '"');

            string sCP = null;

            Regex r = new Regex("CP\\s?=\\s?[\"]?(?<TokenValue>[^\";]*)");
            Match m = r.Match(sValue);
            if (m.Success && (null != m.Groups["TokenValue"]))
            {
                sCP = m.Groups["TokenValue"].Value;
            }

            if (String.IsNullOrEmpty(sCP))
            {
                return;
            }

            // Okay, we've got a compact policy token.

            oP3PState = P3PState.P3POk;
            string[] sTokens = sCP.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

            foreach (string sToken in sTokens)
            {
                // Reject clearly invalid tokens...
                if ((sToken.Length < 3) || (sToken.Length > 4))
                {
                    oP3PState = P3PState.P3PMalformed;
                    return;
                }

                if (",PHY,ONL,GOV,FIN,".IndexOf("," + sToken + ",", StringComparison.OrdinalIgnoreCase) > -1)
                {
                    sUnsatCat += (sToken + " ");
                    continue;
                }

                if (",SAM,OTR,UNR,PUB,IVA,IVD,CON,TEL,OTP,".IndexOf("," + sToken + ",", StringComparison.OrdinalIgnoreCase) > -1)
                {
                    sUnsatPurpose += (sToken + " ");
                    continue;
                }

                // TODO: Look up the token in the complete collection and check validity
            }

            // If a cookie contains an unsatisfactory purpose and an unsatisfactory category, mark it
            // https://msdn.microsoft.com/en-us/library/ie/ms537343(v=vs.85).aspx#unsatisfactory_cookies
            if ((sUnsatCat.Length > 0) && (sUnsatPurpose.Length > 0))
            {
                if (oP3PState == P3PState.P3POk)
                {
                    oP3PState = P3PState.P3PUnsatisfactory;
                }
            }
        }

        private enum P3PState
        {
            NoCookies,
            NoP3PAndSetsCookies,
            P3POk,
            P3PUnsatisfactory,
            P3PMalformed
        }

        public void OnPeekAtResponseHeaders(Session oSession) 
        {
            if (!bEnabled) return;

            P3PState oP3PState = P3PState.NoCookies;

            if (!oSession.oResponse.headers.Exists("Set-Cookie"))
            {
                return;
            }

            oP3PState = P3PState.NoP3PAndSetsCookies;

            if (oSession.oResponse.headers.Exists("P3P"))
            {
                SetP3PStateFromHeader(oSession.oResponse.headers["P3P"], ref oP3PState);
            }

            switch (oP3PState)
            {
                case P3PState.P3POk:
                    oSession["ui-backcolor"] = "#ACDC85";
                    oSession["X-Privacy"] = "Sets cookies & P3P";
                    break;

                case P3PState.NoP3PAndSetsCookies:
                    oSession["ui-backcolor"] = "#FAFDA4";
                    oSession["X-Privacy"] = "Sets cookies without P3P";
                    break;

                case P3PState.P3PUnsatisfactory:
                    oSession["ui-backcolor"] = "#EC921A";
                    oSession["X-Privacy"] = "Sets cookies; P3P unsatisfactory for 3rd-party use";
                    break;

                case P3PState.P3PMalformed:
                    oSession["ui-backcolor"] = "#E90A05";
                    if (bEnforceP3PValidity)
                    {
                        oSession.oResponse.headers["MALFORMED-P3P"] = oSession.oResponse.headers["P3P"];
                        oSession["X-Privacy"] = "MALFORMED P3P: " + oSession.oResponse.headers["P3P"];
                        oSession.oResponse.headers.Remove("P3P");
                    }
                    break;
            }
        }
        public void AutoTamperRequestBefore(Session oSession) { }
        public void AutoTamperRequestAfter(Session oSession){ /*noop*/ }
        public void AutoTamperResponseAfter(Session oSession) {/*noop*/}
        public void AutoTamperResponseBefore(Session oSession) { /*noop*/ }
        public void OnBeforeReturningError(Session oSession) {/*noop*/}
    }

See Also

In this article