Skip to content

Do not use FormattedModelValue in password editor template #136

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 22 additions & 8 deletions src/System.Web.Mvc/Html/DefaultEditorTemplates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Globalization;
using System.Linq;
using System.Text;
using System.Web.Configuration;
using System.Web.Mvc.Properties;
using System.Web.Routing;
using System.Web.UI.WebControls;
Expand All @@ -18,6 +19,7 @@ namespace System.Web.Mvc.Html
internal static class DefaultEditorTemplates
{
private const string HtmlAttributeKey = "htmlAttributes";
private const string UsePasswordValue = "Switch.System.Web.Mvc.UsePasswordValue";

internal static string BooleanTemplate(HtmlHelper html)
{
Expand Down Expand Up @@ -149,11 +151,11 @@ internal static string HiddenInputTemplate(HtmlHelper html)

MvcHtmlString hiddenResult;

if (htmlAttributesDict != null)
if (htmlAttributesDict != null)
{
hiddenResult = html.Hidden(String.Empty, model, htmlAttributesDict);
}
else
}
else
{
hiddenResult = html.Hidden(String.Empty, model, htmlAttributesObject);
}
Expand All @@ -178,9 +180,9 @@ private static IDictionary<string, object> CreateHtmlAttributes(HtmlHelper html,
{
return MergeHtmlAttributes(htmlAttributesObject, className, inputType);
}

var htmlAttributes = new Dictionary<string, object>()
{
{
{ "class", className }
};
if (inputType != null)
Expand Down Expand Up @@ -274,9 +276,21 @@ internal static string ObjectTemplate(HtmlHelper html, TemplateHelpers.TemplateH

internal static string PasswordTemplate(HtmlHelper html)
{
return html.Password(String.Empty,
html.ViewContext.ViewData.TemplateInfo.FormattedModelValue,
CreateHtmlAttributes(html, "text-box single-line password")).ToHtmlString();
object value = null;
var usePasswordStrings = WebConfigurationManager.AppSettings.GetValues(UsePasswordValue);
bool usePasswordValue;
if (usePasswordStrings != null &&
usePasswordStrings.Length > 0 &&
bool.TryParse(usePasswordStrings[0], out usePasswordValue) &&
usePasswordValue)
{
value = html.ViewContext.ViewData.TemplateInfo.FormattedModelValue;
}

return html.Password(
name: String.Empty,
value: value,
htmlAttributes: CreateHtmlAttributes(html, "text-box single-line password")).ToHtmlString();
}

private static bool ShouldShow(ModelMetadata metadata, TemplateInfo templateInfo)
Expand Down
57 changes: 53 additions & 4 deletions test/System.Web.Mvc.Test/Html/Test/DefaultEditorTemplatesTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -731,14 +731,63 @@ public void ObjectTemplateDisplaysSimpleDisplayTextWithNonNullModelTemplateDepth
public void PasswordTemplateTests()
{
Assert.Equal(
"<input class=\"text-box single-line password\" id=\"FieldPrefix\" name=\"FieldPrefix\" type=\"password\" value=\"Value\" />",
"<input class=\"text-box single-line password\" id=\"FieldPrefix\" name=\"FieldPrefix\" type=\"password\" />",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test (well, two tests in one) and the two theories using the data set below set Model and FormattedModelValue. I left similar settings in PasswordTemplate_ReturnsInputElement_IgnoresValues() due to its narrower intent.

DefaultEditorTemplates.PasswordTemplate(MakeHtmlHelper<string>("Value")));

Assert.Equal(
"<input class=\"text-box single-line password\" id=\"FieldPrefix\" name=\"FieldPrefix\" type=\"password\" value=\"&lt;script>alert(&#39;XSS!&#39;)&lt;/script>\" />",
"<input class=\"text-box single-line password\" id=\"FieldPrefix\" name=\"FieldPrefix\" type=\"password\" />",
DefaultEditorTemplates.PasswordTemplate(MakeHtmlHelper<string>("<script>alert('XSS!')</script>")));
}

[Fact]
public void PasswordTemplate_ReturnsInputElement_IgnoresValues()
{
// Arrange
var expected = "<input class=\"text-box single-line password\" id=\"FieldPrefix\" name=\"FieldPrefix\" " +
"type=\"password\" />";

// Template ignores Model and FormattedModelValue.
var helper = MakeHtmlHelper<string>(model: "Model string", formattedModelValue: "Formatted string");

var viewData = helper.ViewData;
var templateInfo = viewData.TemplateInfo;
templateInfo.HtmlFieldPrefix = "FieldPrefix";

// Template ignores ModelState and ViewData.
var valueProviderResult = new ValueProviderResult(
"Raw model string",
"Attempted model string",
CultureInfo.InvariantCulture);
viewData.ModelState.SetModelValue("FieldPrefix", valueProviderResult);
viewData["FieldPrefix"] = "ViewData string";

// Act
var result = DefaultEditorTemplates.PasswordTemplate(helper);

// Assert
Assert.Equal(expected, result);
}

[Fact]
public void PasswordTemplate_ReturnsInputElement_UsesHtmlAttributes()
{
// Arrange
var expected = "<input class=\"super text-box single-line password\" id=\"FieldPrefix\" " +
"name=\"FieldPrefix\" type=\"password\" value=\"Html attributes string\" />";
var helper = MakeHtmlHelper<string>(model: null);
var viewData = helper.ViewData;
var templateInfo = viewData.TemplateInfo;
templateInfo.HtmlFieldPrefix = "FieldPrefix";

viewData["htmlAttributes"] = new { @class = "super", value = "Html attributes string" };

// Act
var result = DefaultEditorTemplates.PasswordTemplate(helper);

// Assert
Assert.Equal(expected, result);
}

public static TheoryDataSet<object, string> PasswordTemplateHtmlAttributeData
{
get
Expand All @@ -747,11 +796,11 @@ public static TheoryDataSet<object, string> PasswordTemplateHtmlAttributeData
{
{
new { @class = "form-control" },
"<input class=\"form-control text-box single-line password\" id=\"FieldPrefix\" name=\"FieldPrefix\" type=\"password\" value=\"Value\" />"
"<input class=\"form-control text-box single-line password\" id=\"FieldPrefix\" name=\"FieldPrefix\" type=\"password\" />"
},
{
new { @class = "form-control", custom = "foo" },
"<input class=\"form-control text-box single-line password\" custom=\"foo\" id=\"FieldPrefix\" name=\"FieldPrefix\" type=\"password\" value=\"Value\" />"
"<input class=\"form-control text-box single-line password\" custom=\"foo\" id=\"FieldPrefix\" name=\"FieldPrefix\" type=\"password\" />"
}
};
}
Expand Down