Monday, January 23, 2017

Sitecore WFFM viewstate validation error

A fresh install of web forms for marketers had been deployed to a scaled 7.5 instance of Sitecore. The following error message was reported by a user:
Validation of the viewstate MAC failed. If this application is hosted by a Web Farm or cluster, ensure that <machineKey> configuration specifies the same ValidationKey and validation algorithm. AutoGenerate cannot be used in a cluster.
Looking into the web.config files the machineKey element was completely missing. The Sitecore scaling guide had the following to say:
In a single instance environment, you shouldn’t change the configuration.

If the environment contains two or more instances:

In the web.config file, set the validationKey and the decryptionKey attributes of the /configuration/system.web/machineKey element to a non-auto generated value. Ensure that the values are identical for all the instances within the environment and that the IsolateApps modifier is not present in either value.
Therefore a machine key generator can be used to generate a valid machine key. An example would be:
<machineKey validationKey='7652D32FB7D858DC44FA409A88FDB9AC8E9811C4F2F4174F138A7901F53CF8E759551BA8EBCD923B5FFEE87737BDA8DA18337BDE51E7C89934B4B1D4F3C84F01' decryptionKey='8158C85A02EADC24CDD400E2E71D2D77B6B3A14E4EABD041'   validation='SHA1'/>
Take note that IsolateApps is not present after either of the keys and that a key is set rather than AutoGenerate.

Wednesday, January 18, 2017

Sitecore WFFM field contains content that may present a security risk

Users of a web forms for marketers form in production started raising issues with validation on the form which was returning the following message:
The [field name] field contains content that may present a security risk. Please enter appropriate information
This is traced back to the Assess Security Risk, Form Verification action. Effectively what this piece of code is doing is checking to ensure that fields in a WFFM form do not contain any of the following characters: "<", ">" and "&".


The users of the form where using and ampersand ("&") character - and this was required for form functionality.

There are two potential solutions to fix this issue on the form.

Removing the Assess Security Risk action

  1. In the content editor navigate to the web form
  2. In the view options (top ribbon) turn "Raw Values" on
  3. In the submit section, locate the "Check Actions" field
  4. Remove the following section from the field's XML value
  5. Disable the "Raw Values" view option 
<li id="{2D5B5061-747A-4477-BD41-E746EAFEB231}" unicid="89F18F7C96F4469A9470057CE421A115"><parameters /></li> 
Effectively this clears out the "Check Actions" node of the XML, however if you have added custom actions you will need to ensure the correct one is removed.

Modifying the Assess Security Risk code

  1. Create your own version of the Assess Security Risk code, the example below removed the check for the ampersand ("&")
  2. Navigate to the action's configuration - /sitecore/system/Modules/Web Forms for Marketers/Settings/Actions/Form Verification/Assess Security Risk
  3. Update the assembly field to that of your custom DLL
  4. Update the class field to that of your custom class
  5. Publish the item
using Sitecore.Data;
using Sitecore.Form.Core.Configuration;
using Sitecore.Form.Core.Controls.Data;
using Sitecore.Form.Core.Submit;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace MyProject.WFFM.Validators
{
    public class AssessSecurityRisk : BaseCheckAction
    {
        public string FailedMessage { get; set; }

        public override void Execute(ID formid, IEnumerable<ControlResult> fields)
        {
            List<string> stringList = new List<string>();
            foreach (ControlResult field in fields)
            {
                if (field.Value != null)
                {
                    string str = HttpUtility.HtmlDecode(field.Value.ToString());
                    if (str.StartsWith("<item>"))
                        str = str.Replace("<item>", string.Empty).Replace("</item>", string.Empty);
                    if (str.IndexOfAny(new char[2] { '<', '>' }) >= 0)
                        stringList.Add(field.FieldName);
                }
            }
            if (stringList.Count > 0)
                throw new Exception(string.Format(this.FailedMessage ?? (stringList.Count == 1 ? ResourceManager.Localize("VALIDATE_INPUT_FAILED") : ResourceManager.Localize("VALIDATE_INPUT_FAILED_MULTIPLE")), (object)string.Join(", ", stringList.ToArray())));
        }

        public override ActionState QueryState(ActionContext context)
        {
            return ActionState.DisabledSingleCall;
        }
    }
}

Thursday, January 12, 2017

Sitecore WFFM 2.5 customizing field output HTML with web forms

Working on a Sitecore 7.5 instance, along with web forms for marketers 2.5, I came across a requirement to implement Bootstrap styling across the form fields. Because the site wasn't running on MVC it wasn't as simple as editing the views for each field type (found in "/Website/Views/Form/EditorTemplates").

Instead I needed to create my own version of each field type I was editing and change how the field was output. For example with the single line of text field, it is defined in Sitecore under: /sitecore/system/Modules/Web Forms for Marketers/Settings/Field Types/Simple Types. As you can see it then references a DLL and Class:


This class inside the DLL looks like:
using Sitecore.Form.Core.Attributes;
using Sitecore.Form.Core.Visual;
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Sitecore.Form.Web.UI.Controls
{
  [Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof (IDesigner))]
  public class SingleLineText : RegexInputControl
  {
    private static readonly string baseCssClassName = "scfSingleLineTextBorder";

    [VisualCategory("Validation")]
    [VisualProperty("Maximum Length:", 2000)]
    [DefaultValue(256)]
    public int MaxLength
    {
      get
      {
        return this.textbox.MaxLength;
      }
      set
      {
        this.textbox.MaxLength = value;
      }
    }

    [VisualProperty("Minimum Length:", 1000)]
    [DefaultValue(0)]
    [VisualCategory("Validation")]
    public int MinLength { get; set; }

    [DefaultValue("scfSingleLineTextBorder")]
    [VisualProperty("CSS Class:", 600)]
    [VisualFieldType(typeof (CssClassField))]
    public new string CssClass
    {
      get
      {
        return base.CssClass;
      }
      set
      {
        base.CssClass = value;
      }
    }

    public SingleLineText(HtmlTextWriterTag tag)
      : base(tag)
    {
      this.MaxLength = 256;
      this.MinLength = 0;
      this.CssClass = SingleLineText.baseCssClassName;
    }

    public SingleLineText()
      : this(HtmlTextWriterTag.Div)
    {
    }

    protected override void OnInit(EventArgs e)
    {
      this.textbox.CssClass = "scfSingleLineTextBox";
      this.help.CssClass = "scfSingleLineTextUsefulInfo";
      this.generalPanel.CssClass = "scfSingleLineGeneralPanel";
      this.title.CssClass = "scfSingleLineTextLabel";
      this.textbox.TextMode = TextBoxMode.SingleLine;
      this.Controls.AddAt(0, (Control) this.generalPanel);
      this.Controls.AddAt(0, (Control) this.title);
      this.generalPanel.Controls.AddAt(0, (Control) this.help);
      this.generalPanel.Controls.AddAt(0, (Control) this.textbox);
    }
  }
}
The OnInit method is then where the field it actually output, and this is where changes can be made to affect the output of the field (CSS classes and div nesting). The following example is a replacement class which outputs single line text like a bootstrap field.
using Sitecore.Form.Core.Attributes;
using Sitecore.Form.Core.Visual;
using Sitecore.Form.Web.UI.Controls;
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace MyProject.WFFM.Controls
{
    [Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof(IDesigner))]
    public class SingleLineText : RegexInputControl
    {
        private static readonly string baseCssClassName = "form-group";

        [VisualCategory("Validation")]
        [VisualProperty("Maximum Length:", 2000)]
        [DefaultValue(256)]
        public int MaxLength
        {
            get
            {
                return this.textbox.MaxLength;
            }
            set
            {
                this.textbox.MaxLength = value;
            }
        }

        [VisualProperty("Minimum Length:", 1000)]
        [DefaultValue(0)]
        [VisualCategory("Validation")]
        public int MinLength { get; set; }

        [DefaultValue("form-group")]
        [VisualProperty("CSS Class:", 600)]
        [VisualFieldType(typeof(CssClassField))]
        public new string CssClass
        {
            get
            {
                return base.CssClass;
            }
            set
            {
                base.CssClass = value;
            }
        }

        public SingleLineText(HtmlTextWriterTag tag)
          : base(tag)
        {
            this.MaxLength = 256;
            this.MinLength = 0;
            this.CssClass = SingleLineText.baseCssClassName;
        }

        public SingleLineText()
          : this(HtmlTextWriterTag.Div)
        {
        }

        protected Panel titlePanel;

        protected override void OnInit(EventArgs e)
        {
            this.textbox.CssClass = "scfSingleLineTextBox";
            this.help.CssClass = "scfSingleLineTextUsefulInfo";
            this.generalPanel.CssClass = "field-responsive";
            this.title.CssClass = "control-label";
            this.textbox.TextMode = TextBoxMode.SingleLine;
            this.Controls.AddAt(0, (Control)this.generalPanel);
            this.titlePanel = new Panel();
            this.titlePanel.CssClass = "label-responsive";
            this.titlePanel.Controls.AddAt(0, (Control)this.title);
            this.Controls.AddAt(0, (Control)this.titlePanel);
            this.generalPanel.Controls.AddAt(0, (Control)this.help);
            this.generalPanel.Controls.AddAt(0, (Control)this.textbox);
        }
    }
}
This might not work for all cases, but serves as a base for how to edit the output of fields inside WFFM (in a web forms site).