.Net 4.0 is encoding single quotes when I am using Attributes.Add to add client side events to my asp.net objects. In the previous versions this didn't happen.
for example :
<asp:Image runat="server" ID="imgTest" ImageUrl="~/DateControl/cal.gif" />
imgTest.Attributes.Add("onmouseover", "alert('Hello')");
When I view the client side output, I am getting
<img id="ctl00_MainContent_calFromTimeStamp1_imgTest" onmouseover="alert('Hello')" src="../DateControl/cal.gif" style="border-width:0px;" />
I found a workaround by creating a custom encoder : creating custom encoding routines but I don't want to stop the encoding for the whole website just because of this issue. Anybody got a workaround or an idea of how to fix this?
According to Microsoft you should not be adding JavaScript to HTML attributes using WebControl.Attributes.Add(), exactly because it will encode the attribute value:
You cannot add client-side script to a WebControl instance using the
Attributes collection. To add client-side script, use the ClientScript
property on the Page control.
Source
The advice is to use the Page.ClientScript.RegisterExpandoAttribute(string controlId, string attributeName, string attributeValue, bool encode) method. In your case it would look like this:
Page.ClientScript.RegisterExpandoAttribute(
imgTest.ClientID,
"onmouseover",
"alert('Hello')",
false /* Do not encode */
);
This will result in a piece of JavaScript in your page that sets the attribute client-side.
The best way to set event attributes in .NET is to call a single function :
imgTest.Attributes("message") = "Hello";
imgTest.Attributes("onmouseover") = "showMessage(this);"
And on your page, or registered script :
function showMessage(ctrl)
{
alert(ctrl.getAttribute('message'));
}
imgTest.Attributes.Add("onmouseover", "alert(\'Hello\')");
Thanks to Franzo's link, where the following answer is copied and pasted:
You can turn off attribute encoding by creating a class like this:
public class HtmlAttributeEncodingNot : System.Web.Util.HttpEncoder
{
protected override void HtmlAttributeEncode(string value, System.IO.TextWriter output)
{
output.Write(value);
}
}
and adding this to web.config under :
<httpRuntime encoderType="HtmlAttributeEncodingNot"/>
This gives me the control I need.
However, now we must worry that new controls may depend on the new
standard 4.0 behaviour and not encode single quotes, so it's still
imperfect, nay, worse than imperfect: security is even worse, because
we don't know what is going on where, so it's not a great workaround
really.
I think only Microsoft can fix this properly. Others have suggested
the need for an HtmlAttributeString class here: link If there
were such a class and Attributes.Add could take an object like this
for its value parameter then we would have the control that we need
again.
It's not recommended to turn off attribute encoding. If you try to prevent encoding by default, there are many strange behaviors occurring with your code in future and you have to pay price for bad practices.
.NET is always encoding any attributes to stop from injecting malicious script. So you should go by this default practice to protect your program.
You can use an escape character before any quote character :
Source :
this.Attributes.Add("onmouseover", string.Format("$(this).attr(\'src\',\'{0}\')",this.Page.ClientScript.GetWebResourceUrl(typeof(SwapImage), urlenabledkey)));
Render :
onmouseover="$(this).attr('src','/WebResource.axd?d=kHY3FE9nMsUOvDU-pPthg4KQvVrnXlcASyA7dFf6L
Related
I've been using Microsoft.AspNetCore.Mvc.ResponseCacheAttribute for the first time and have come across an issue that I would have solved previously using the VaryByCustom property in OutputCachein ASP.NET (and using public override string GetVaryByCustomString(HttpContext context, string s) in the global.asax).
This VaryByCustom caching seems to no longer exist in ASP.NET Core. Is there a built-in alternative that I'm missing here or will I need to implement this myself to achieve something similar?
From my understanding, you have two flexible options in ASP.NET core:
Use the VaryByHeader or VaryByQueryKeys if you're using the ResponseCacheAttribute.
When using Headers, you need to write the value to vary by as a header, which could be any arbitrary value (no need to expose data to the client):
Response.Headers.Add("X-My-Vary-Header", "this-is-variable");
In essence, this is all the VaryByCustomString ever did for you anyway. The way I see it, you're no longer forced to put this code in a specific method/file (e.g. global.asax).
Try the <cache> Tag Helper when caching in Razor.
Here you have a wide range of things to "vary" by: vary-by-header, vary-by-route, vary-by-user, and even a custom vary-by.
Have a look here and decide whether to use the attribute or the cache tag helper: https://learn.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/built-in/cache-tag-helper
Alright. I can get the HTML I need from the page (it's all in a DIV). That's not the problem. What I need to do is take the HTML and pass it, via a C# class into a controller.
I tried doing something like this with knockout/jQuery:
var Details = $("#Details").html();
console.log(Details);
DetailsPdf.DetailsMarkup = JSON.stringify(Details);
var jsonData = ko.toJS(Details);
ko.utils.postJson("/MyController/MyAction", DetailsPdf);
The knockout actually DOES get me the relevant HTML. But when I pass it to my class, I get an exception that reads:
A potentially dangerous Request.Form value was detected from the client.
Then it partially shows the HTML I was sending as a part of the exception. I can't even seem to pass in the entities themselves without getting that exception.
This is an app with certain company-mandated security features, so turning off validation is not an option.
I need the HTML, or at least a way to re-create it on the server in the C#.
I'm still fairly new to knockout. Does anyone have any suggestions here?
You should have mentioned that:
This is an app with certain company-mandated security features, so
turning off validation is not an option.
In your recent question Using iTextSharp with the knockout JavaScript framework?. I could have provided this answer there.
I'm not sure why decorating the controllers Action with [ValidateInput(false)] isn't working, but that answer's fully working source code is available. Whatever the reason, you should only need a few changes to workaround the issue:
(1) Base64 encode the HTML in your JavaScript:
ko.utils.postJson("/MyController/MyAction", window.btoa(DetailsPdf));
(2) Decode string in your MVC Controller:
[HttpPost]
public ActionResult Index(string xHtml)
{
xHtml = Encoding.UTF8.GetString(Convert.FromBase64String(xHtml));
(3) Deserialize that string to your model / entities with Json.Net or other.
Above steps have also been tested and verified working. ;)
You should be able to decorate your model (the one that the controller action is expecting) with the [AllowHtml] attribute on the property that has the HTML.
When you do that, MVC skips the validation for that property.
Here's a link to the documentation for more information.
Note Use this ONLY when you need to. It does open a vector for XSS if misused.
Edit: If for some reason, You can't use the [AllowHtml] attribute, you can turn off validating the request for that one action with [ValidateInput(false)].
Same rules apply. Use that very sparingly. This means none of security validations will run against that particular model in that particular action only.
Have a few custom content-types registered via
ContentTypeFilters.Register(contentType, StreamSerializer, StreamDeserializer);
and would like to restrict the display for routes on the metadata page. These content-types are only meant to be used with request dto's which are restricted to InternalNetworkAccess. Just looking to not clutter up the public facing metadata page with stuff that isn't necessary.
For the builtin contentTypes you would just add it under the RestrictAttribute. Is there a similar feature hidden somewhere else that isn't documented yet maybe for the custom types?
It looks like perhaps I could customize the MetadataFeature plugin and possibly restrict which request dto's get the content type and which don't. But I only just recently noticed that, and not sure how well that would turn out (also don't really know yet how to remove the MetadataFeature and safely replace with my own).
Essentially I only want this custom contentType visible on the metadata page for the requestDtos restricted to InternalNetworkAccess.
Any ideas?
Edit:
Also am still on ServiceStack v3, but still interested in possibilities for v4.
You can prevent your custom type from showing up in the metadata using.
If your content type is application/yourformat you would use:
SetConfig(new HostConfig {
IgnoreFormatsInMetadata = new HashSet<string>{ "yourformat" }
});
So I found that the initial class handling the metadata requests was ServiceStack.MetadataFeature an IPlugin. This actually controls both the layout of the underlying example request/response page (for each content-type) as well as the overall "/metadata" page.
From this small segment
private IHttpHandler GetHandlerForPathParts(String[] pathParts)
{
var pathController = string.Intern(pathParts[0].ToLower());
if (pathParts.Length == 1)
{
if (pathController == "metadata")
return new IndexMetadataHandler();
return null;
}
...
}
is where the handler for the actual "/metadata" page is sent off. You don't find the actual construction of the ContentTypes per request until you get down a little further, inside IndexMetadataHandler's parent class BaseSoapMetadataHandler in the method
protected override void RenderOperations(HtmlTextWriter writer, IHttpRequest httpReq, ServiceMetadata metadata)
An internal control is created (IndexOperationsControl) which has a method RenderRow, which is where all the magic occurs. Here you'll see some obvious checks for the "Operation" (which is another word for the Dto now) and ContentType like
if (this.MetadataConfig.IsVisible(this.HttpRequest, EndpointAttributesExtensions.ToFormat(config.Format), operation))
So all that needs to be done is create your own class of IndexOperationsControl and handle the config.Format in the RenderRow method. The config.Format is simply everything after the forward slash in the ContentType you registered, so if it was "application/x-my-type" the config.Format String will be "x-my-type". Operation is simply the class name of the RequestDto. Unfortunately because the class is marked internal it means you pretty much have to copy it completely instead of using inheritance. In order to keep a 1:1 likeness with how the pages are generated by default you'll also need a copy of the internal classes ListTemplate, TableTemplate, and XsdTypes (used in construction of IndexOperationsControl).
After this you simply need your own IndexMetadataHandler and overload RenderOperations (you can use inheritance for this one) to create your new IndexOperationsControl. Also we'll need our own MetadataFeature equivalent IPlugin but we'll need to copy it completely and modify GetHandlerForPathParts to return our new IndexMetadataHandler. The only other thing to do is remove MetadataFeature and add our own as a plugin.
// removing default metadata feature
Plugins.RemoveAll(x => x is MetadataFeature);
// add our custom one
Plugins.Add(new CustomMetadataFeature());
Voila, you can display custom ContentTypes exactly how you want per RequestDto.
I'm struggling with the following problem and I can't find an acceptable way to solve it.
My challenge: write out HTML comments just before the actual property value in a Razor view.
This is my (simplyfied) Viewmodel:
public class Article
{
public string Title {get;set;}
}
To write out this title I simply do this in my Razor view:
<h2>#Model.Title</h2>
Now I want to write out a html comment just before the actual title so the generated HTML looks like this (simplyfied):
<h2><!-- some parameters for a 3th party system --> This is my title</h2>
The HTML comment comes from an Attribute I applied to the 'Title' attribute. It's value is generated, so the attribute-value is added at runtime using the TypeDescriptor from the .NET framework.
Now I know I could achieve this by simply writing out all my properties using an HTML helper. Like this: #MyHelper.Write(m => m.Title)
But since potentially ALL my properties need this HTML comment I want to avoid the use of an HTML helper since it clutters the View and doesn't make the view look nice and (more) readable.
This is what I have tried:
Created a custom Razor base page (Inheriting from WebViewPage<TModel>). And overwriting it's 'Write' method.
This kind of works but the BIGGEST problem here is that I don't know which property is been written out at that moment. There is no way of getting the current property name in the 'Write' method. So now I dynamically search my Model to find a property with the value that's been written out and prepend the HTML comment from the attribute.
My question: is there another approach to accomplish what I want. As sais before: I want to avoid using an HTML helper to write out all my properties. (Think about loops, etc. It's just not nice).
Also, adding this HTML comment in my Controller is no option since:
it's not part of the actual value. Is a sort of metadata.
The HTML comment should be added to int's, double's and DateTime's. There is no way to adjust a double property to include a string. (Image a List<DateTime>. All date's need this HTML comment)
the HTML comment should be added based on a web.config setting. Yes or No. (The actual HTML comment is different for each value of a property)
I realize this question is rather long. Sorry for that. Any thoughts are appreciated.
You can use the existing #Html.Raw(Model.Title)
Alternatively you can use a display templates. Add a UIHintAttribute to the properties you wish to behave this way.
public class MyModel
{
[UIHint("Raw")]
public string MyString { get; set; }
}
Create a new display template called Raw.cshtml that accepts model of type string:
#model string
#Html.Raw(model)
Then in your view you can use:
#Html.DisplayFor(m => m.MyString)
This still requires that you use a helper (DisplayFor). This is a recommended practice that allows you to easily change the behavior of one or many fields with minimal code changes.
There are some posts on this, but not an answer to this specific question.
The server is returning this: "/Date(1304146800000)/"
I would like to not change the server-side code at all and instead parse the date that is included in the .Net generated JSON object. This doesn't seem that hard because it looks like it is almost there. Yet there doesn't seem to be a quick fix, at least in these forums.
From previous posts it sounds like this can be done using REGEX but REGEX and I are old enemies that coldly stare at each other across the bar.
Is this the only way? If so, can someone point me to a REGEX reference that is appropriate to this task?
Regards,
Guido
The link from Robert is good, but we should strive to answer the question here, not to just post links.
Here's a quick function that does what you need. http://jsfiddle.net/Aaa6r/
function deserializeDotNetDate(dateStr) {
var matches = /\/Date\((\d*)\)\//.exec(dateStr);
if(!matches) {
return null;
}
return new Date( parseInt( matches[1] ) );
}
deserializeDotNetDate("/Date(1304146800000)/");
Since you're using jQuery I've extended its $.parseJSON() functionality so it's able to do this conversion for you automatically and transparently.
It doesn't convert only .net dates but ISO dates as well. ISO dates are supported by native JSON converters in all major browsers but they work only one way because JSON spec doesn't support date data type.
Read all the details (don't want to copy blog post content here because it would be too much) in my blog post and get the code as well. The idea is still the same: change jQuery's default $.parseJSON() behaviour so it can detect .Net and ISO dates and converts them automatically when parsing JSON data. This way you don't have to traverse your parsed objects and convert dates manually.
How it's used?
$.parseJSON(yourJSONstring, true);
See the additional variable? This makes sure that all your existing code works as expected without any change. But if you do provide the additional parameter and set it to true it will detect dates and convert them accordingly.
Why is this solution better than manual conversion? (suggested by Juan)
Because you lower the risk of human factor of forgetting to convert some variable in your object tree (objects can be deep and wide)
Because your code is in development and if you change some server-side part that returns JSON to the client (rename variables, add new ones, remove existing etc.), you have to think of these manual conversions on the client side as well. If you do it automatically you don't have to think (or do anything) about it.
Two top reasons from the top of my head.
When overriding jQuery functionality feels wrong
When you don't want to actually override existing $.parseJSON() functionality you can minimally change the code and rename the extension to $.parseJSONwithdates() and then always use your own function when parsing JSON. But you may have a problem when you set your Ajax calls to dataType: "json" which automatically calls the original parser. If you use this setting you will have to override jQuery's existing functionality.
The good thing is also that you don't change the original jQuery library code file. You put this extension in a separate file and use it at your own will. Some pages may use it, others may not. But it's wise to use it everywhere otherwise you have the same problem of human factor with forgetting to include the extension. Just include your extension in some global Javascript file (or master page/template) you may be using.