I developed a web application and deployed into the server and my security team come up with the below security remidiation issue.
Reflected HTML Parameter Pollution (HPP) is an injection weakness vulnerability that occurs when an attacker can inject a delimiter and change the parameters of a URL generated by an application. The consequences of the attack depend upon the functionality of the application, but may include accessing and potentially exploiting uncontrollable variables, conducting other attacks such as Cross-Site Request Forgery, or altering application behavior in an unintended manner. Recommendations include using strict validation inputs to ensure that the encoded parameter delimiter “%26” is handled properly by the server, and using URL encoding whenever user-supplied content is contained within links or other forms of output generated by the application.
Can any one have the idea about how to prevent HTML parameter pollution in asp.net
here is the script code in the webpage
<script type="text/javascript" language="javascript">
document.onclick = doNavigationCheck ;
var srNumberFinal="";
function OpenDetailsWindow(srNumber)
{
window.open("xxx.aspx?SRNumber="+srNumber+ "","","minimize=no,maximize=no,scrollbars=yes,status=no,toolbar=no,menubar=no,location=no,width=800,directories=no,resizable=yes,titlebar=no");
}
function OpenPrintWindow()
{
var querystrActivityId = "<%=Request.QueryString["activityId"]%>";
if(querystrActivityId != "")
{
var url = "abc.aspx?id=" + "<%=Request.QueryString["id"]%>" + "&activityId=" + querystrActivityId + "";
}
else
{
var hdrActivityId = document.getElementById('<%=uxHdnHdrActivityId.ClientID%>').value;
var url = "PrintServiceRequestDetail.aspx?id=" + "<%=Request.QueryString["id"]%>" + "&activityId=" + hdrActivityId + "";
}
childWinReference=window.open(url, "ChildWin","minimize=yes,maximize=yes,scrollbars=yes,status=yes,toolbar=no,menubar=yes,location=no,directories=no,resizable=yes,copyhistory=no");
childWinReference.focus();
}
function NavigateSRCopy(srNumber)
{
srNumberFinal = srNumber;
if (srNumber != "undefined" && srNumber != null && srNumber != "")
{
new Ajax.Request('<%= (Request.ApplicationPath != "/") ? Request.ApplicationPath : string.Empty %>/xxx/AutoCompleteService.asmx/CheckFormID'
, { method: 'post', postBody: 'srNumber=' + srNumber, onComplete: SearchResponse });
}
}
function SearchResponse(xmlResponse)
{
var xmlDoc;
try //Internet Explorer
{
xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async="false";
xmlDoc.loadXML(xmlResponse.responseText);
}
catch(e)
{
try // Firefox, Mozilla, Opera, etc.
{
parser=new DOMParser();
xmlDoc=parser.parseFromString(xmlResponse.responseText,"text/xml");
}
catch(e)
{
alert(e.message);
return;
}
}
if(xmlDoc.getElementsByTagName("string")[0].childNodes[0] != null)
{
formID = xmlDoc.getElementsByTagName("string")[0].childNodes[0].nodeValue;
}
else
{
formID = null;
}
if(formID != null && formID != "")
{
window.location.href = '/CustomerSupportRequest/CreateServiceRequest.aspx?id=' + formID + '&TemplateSR=' + srNumberFinal + '&Frompage=CopySR';
return true;
}
else
{
alert("This Service Request cannot be copied because it meets at least one of these conditions: \t\t\n\n * It was created prior to 10/15/2008 \n * It was auto generated as part of the Report Requeue Process \n * It was auto generated as part of the ERA Requeue Process \n * It was not created online");
}
}
function UpdateChildCases()
{
var modalPopup = $find('modalParentChildComments');
modalPopup.show();
}
function HideParentChildPopup()
{
var modalPopup = $find('modalParentChildComments');
modalPopup.hide();
return false;
}
function HideErrorSRNumsPopup()
{
var modalPopup = $find('modalParentErrorSRNumDisplay');
modalPopup.hide();
return false;
}
function HideRetrySRNumsPopup()
{
var modalPopup = $find('modalRetrySRNumDisplay');
modalPopup.hide();
return false;
}
function RemoveParent_ChildFlag(type)
{
var childCases = document.getElementById("<%=uxHdnChildCases.ClientID %>");
var msg = "";
var btn;
if(type == "Child")
{
if(childCases.value.indexOf(',') != -1)
msg = "Are you sure you want to remove the Child flag from this Service Request?";
else
msg = "This is the only child associated to the parent case. Removing the child flag will also remove the parent flag from the associated case. Choose OK to remove the flags, or Cancel to close this dialog";
btn = document.getElementById('<%=uxRemoveChildFlag.ClientID%>');
}
else
{
msg = "Removing the parent flag from this case will also remove the child flag from all associated cases. Are you sure you want to remove the Parent flag from this Service Request?";
btn = document.getElementById('<%=uxRemoveParentFlag.ClientID%>');
}
if(btn)
{
if(!confirm(msg))
{
return false;
}
else
{
btn.click();
}
}
}
function limitTextForParentChildComments()
{
var objLblCharCount = document.getElementById('uxLblPCCharCount');
var objTxtComments = document.getElementById('<%=txtParentComment.ClientID%>');
if (objTxtComments.value.length > 1500)
{
objTxtComments.value = objTxtComments.value.substring(0, 1500);
}
else
{
objLblCharCount.innerHTML = 1500 - objTxtComments.value.length + " ";
}
setTimeout("limitTextForParentChildComments()",50);
}
function ValidateInputs()
{
var lblErrorMessage = document.getElementById('<%=lblCommentErrorTxt.ClientID%>');
var objTxtComments = document.getElementById('<%=txtParentComment.ClientID%>');
if(objTxtComments.value.trim() == "")
{
lblErrorMessage.style.display = "block";
return false;
}
}
</script>
As per OWASP Testing for HTTP Parameter pollution, ASP.NET is not vulnerable to HPP because ASP.NET will return all occurrences of a query string value concatenated with a comma (e.g. color=red&color=blue gives color=red,blue).
See here for an example explanation.
That said, your code appears to be vulnerable to XSS instead:
var querystrActivityId = "<%=Request.QueryString["activityId"]%>";
If the query string parameter activityId="; alert('xss');" (URL encoded of course), then an alert box will trigger on your application because this code will be generated in your script tag.
var querystrActivityId = ""; alert('xss');"";
Related
I am building an add-on for google docs (Just for practice) that will act like email. I already incorporated sending, receiving, deleting, and viewing messages. I added the code needed for a UI modal dialog, but one of the functions is only returning undfined. I tested this function in the code.gs file, and it worked perfectly. Here is a section of code.gs:
function onInstall() {
var html = HtmlService.createHtmlOutputFromFile('Startup').setWidth(350).setHeight(170);
DocumentApp.getUi().showModalDialog(html, 'New account:');
}
function testCheck() {
var ui = DocumentApp.getUi();
ui.alert(checkUsername(ui.prompt('').getResponseText(), ui.prompt('').getResponseText()));
}
function checkUsername(un, em) {
var i; var a; var is;
var props = PropertiesService.getScriptProperties();
if (props.getProperty(un) == null) {
is = true;
} else {
return 'This username is taken!';
}
if (em.length == 0) {
return true;
} else {
var len = (em.match(/#/g) || []).length;
if (len == 1) {
if (props.getProperty(em) != null) {
return 'Someone has already registered this email address as ' + props.getProperty(em);
} else {
return true;
}
} else {
if (em.indexOf(', ') != -1) {
em = em.split(', ');
} else if (em.indexOf('; ') != -1) {
em = em.split('; ');
} else if (em.indexOf(' + ') != -1) {
em = em.split(' + ');
} else if (em.indexOf(';') != -1) {
em = em.split(';');
} else if (em.indexOf(',') != -1) {
em = em.split(',');
} else if (em.indexOf('+') != -1) {
em = em.split('+');
} else if (em.indexOf(' ') != -1) {
em = em.split(' ');
} else {
return 'Please separate your email addresses with a comma, space, or semicolon.';
}
for (i = 0; i < em.length; i++) {
a = em[i];
if (props.getProperty(a) != null) {
return 'Someone has already registered ' + a + ' as ' + props.getProperty(a);
}
}
return true;
}
}
}
Here is the html file:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
</head>
<body>
Username:<br>
<input type='text' id='user' style='width:350px' maxlength='12'/><br>
Other email addresses:<br>
<textarea id='extras' style='width:350px' rows='2'></textarea><br>
<span class='error' id='err'></span><br>
<button class='action' onClick='check()'>Next</button>
<button onclick='group()'>Groups</button><br>
<script>
function check() {
var un = document.getElementById('user').value;
var em = document.getElementById('extras').value;
var fail = document.getElementById('err');
var is = google.script.run.checkUsername(un, em);
if (typeof is == 'string') {
fail.innerHTML = is;
} else {
google.script.host.close();
google.script.run.setAccount(un, em);
}
}
function group() {
var un = document.getElementById('user').value;
var em = document.getElementById('extras').value;
var is = google.script.run.checkUsername(un, em);
if (typeof is == 'boolean') {
setGroupAddress(un, em);
} else {
document.getElementById('err').innerHtml = is;
}
}
</script>
</body>
</html>
Update: I completely retyped the functions, but the program continues to return undefined. All inputs are the correct values, and the function returns information correctly in a ui.alert() box.
I figured it out after completely reading the Google Apps Script Documentation. The google.script.run.function() API does not return a value. In order to fetch data from a script, you must have the script generate raw HTML, and create a dialog with an HTML string.
Due to security considerations, scripts cannot directly return HTML to a browser. Instead, they must sanitize it so that it cannot perform malicious actions. You can return sanitized HTML using the createHtmlOutput API
function doGet() {
return HtmlService.createHtmlOutput('<b>Hello, world!</b>');
}
The code in the HtmlOutput can include embedded JavaScript and CSS. (This is standard client-side JavaScript that manipulates the DOM, not Apps Script). All of this content is sanitized using Google Caja, which applies some limitations to your client-side code. For more information, see the guide to restrictions in HTML service.
https://developers.google.com/apps-script/reference/html/html-output#
My JavaScript inside the head tag:
<script type="text/javascript">
function over1() {
var img1 = document.getElementById("1").src;
document.getElementById("big").src = img1;
}
function out() {
document.getElementById("big").src = "http://icons.iconarchive.com/icons/paomedia/small-n-flat/1024/shop-icon.png";
}
function london() {
var city = document.getElementById("city").value;
var check = city.toLowerCase();
var province = document.getElementById("province").value;
if (check == "london" && province == "ON") {
alert("Visit our company travel store at Masonville Mall!");
}
}
function checkinput() {
var email = document.contest.email.value;
var emailcheck = email.search("#");
if (!document.contest.name.value) {
alert("Enter a name!")
} else {
alert("Thank You " + document.contest.name.value + " " + document.contest.lastname.value + " For Entering The Contest!")
window.open(calculator.html,'_blank');
}
}
</script>
I have the simple JavaScript inside the HTML file, but Chrome won't read it. In Inspector View, it throws ReferenceErrors for all my functions. Please help.
Why do you say that, those are all functions, nothing is invoked from these functions. call the functions and see if they are invoked correctly or not
checkinput();
over1();
/* the rest of them */
I need to fire piece of jQuery code only if it is home page.
URL probability are
http://www.example.com
http://www.example.com/
http://www.example.com/default.aspx
How can i run code if it is any of the above url i can use
var currenturl = window.location
but then i have to change this every time i move my code to server as on local host my url is like
http://localhost:90/virtualDir/default.aspx
in asp.net we can get the it using various
HttpContext.Current.Request.Url.AbsolutePath
or
HttpContext.Current.Request.ApplicationPath
I am not sure what are the equivalent in jQuery
reference of asp.net example
UPDATE:
I have taken a simple approach as i could not find other easy way of doing it
var _href = $(location).attr('href').toLowerCase()
var _option1 = 'http://localhost:51407/virtualDir/Default.aspx';
var _option2 = 'http://www.example.com/Default.aspx';
var _option3 = 'http://www.example.com/';
if (_href == _option1.toLowerCase() || _href == _option2.toLowerCase() || _href == _option3.toLowerCase()) {
$(".bar-height").css("min-height", "689px");
// alert('aa');
}
else
{ //alert('bb'); }
Could you only include the script on the page where it's needed? i.e. only use <script type="text/javascript" src="homepage.js"></script> from default.aspx ?
If not, then, as dfsq said - use window.location.pathname .
var page = window.location.pathname;
if(page == '/' || page == '/default.aspx'){
// -- do stuff
}
You could just get the part after the last slash, to account for folder differences...
var page = window.location.toString();
page = page.substring(page.lastIndexOf('/'));
... but this would be true for both example.com/default.aspx and example.com/folder1/default.aspx.
Remember, this Javascript is client-side, so there's no equivalent to the C# example you linked.
You could use my approch to know exactly the page (also with urlrouting) to use it in javascript:
I use the body id to identify the page.
javascript code:
$(document).ready(function () {
if (document.body.id.indexOf('defaultPage') == 0) {
/*do something*/
}
});
Asp.net code:
in masterpage or page (aspx):
...
<body id="<%=BodyId %>">
...
code behind:
private string _bodyId;
public string BodyId
{
get
{
if (string.IsNullOrWhiteSpace(_bodyId))
{
var path = GetRealPagePath().TrimStart('/','~');
int index = path.LastIndexOf('.');
if (index > -1)
{
path = path.Substring(0, index);
}
_bodyId = path.Replace("/", "_").ToLower();
}
return string.Concat(_bodyId,"Page");
}
}
public string GetRealPagePath()
{
string rtn = Request.Path;
if (Page.RouteData != null && Page.RouteData.RouteHandler!= null)
{
try
{
if (Page.RouteData.RouteHandler.GetType() == typeof(PageRouteHandler))
{
rtn=((PageRouteHandler)Page.RouteData.RouteHandler).VirtualPath;
}
else
{
rtn = Page.Request.AppRelativeCurrentExecutionFilePath;
}
}
catch (Exception ex)
{
Logger.Error(string.Format("GetRealPagePath() Request.Path:{0} Page.Request.AppRelativeCurrentExecutionFilePath:{1}", Request.Path, rtn), ex);
}
}
return rtn;
}
I'm having an issue where my call to $.ajax is completing successfully and returning content with a response of 200OK as reported by firebug, but the success,complete and error callbacks do not execute. This is only happening in firefox, in chrome it works fine (i am running firefox22).
$.ajax(site_url+url+'/fetch_salt',{type:'POST',data:data,success:check_salt});
var group = '';
function check_salt(d)
{
console.log(d);
The actual response for the request as reported by firebug is:
choose_login:{"admin":"Admin Zone"}
And response type:
Content-Type text/html
I have tried forcing settings like dataType and contentType in case jquery is assuming json or something and I have tried anonymous functions for the error, success and complete callbacks, but nothing works.
Am posting full function code, just in case its some kind of syntax error quirk:
function prep_login_form(elem,url,challenge)
{
function show_error(msg)
{
$(elem).find('.ecms-error-for-password .ecms-error-text').html(msg).closest('.ecms-error-container').removeClass('ecms-error-hidden');
}
function submit()
{
var data = {email:$(elem).find('input[name="email"]').val()};
data[csfr_token_name] = csfr_hash;
$.ajax({type:'POST',url:site_url+url+'/attempt_login',data:data,success:check_salt});
var group = '';
function check_salt(d)
{
console.log(d);
if (d=='no_email')
{
show_error('Invalid Email address');
}
else if (d=='account_disabled')
{
show_error('This account has been disabled, please contact your administrator');
}
else if (d.substr(0,12)=='choose_login')
{
var cl;
eval('cl = '+d.substr(13));
var cou = 0;
for (p in cl)
{
cou++;
}
if (cou==1)
{
group = p;
var mydata = $.extend(data,{group:p});
$.ajax(site_url+url+'/fetch_salt',{type:'POST',data:mydata,success:check_salt})
}
else
{
var str = '<div class="login-selection-popup"><p>We have detected that your email address is linked to more than one account.<br />Please select which zone you would like to login to.</p><ul class="choose-login-popup">';
for (p in cl)
{
str+='<li><a rel="'+p+'">'+cl[p]+'</a></li>';
}
str+='</ul></div>';
open_modal({heading:'Choose Account',content:str,buttons:function(close_modal)
{
$(this).find('.choose-login-popup').on('click','a',function()
{
group = $(this).attr('rel');
var mydata = $.extend(data,{group:$(this).attr('rel')});
$.ajax(site_url+url+'/fetch_salt',{type:'POST',data:mydata,success:check_salt})
close_modal();
});
}});
}
}
else
{
var salt = d;
var pw = $(elem).find('input[name="password"]').val();
data.password = hex_md5(challenge+hex_md5(salt+pw));
data.group = group;
$.ajax(site_url+url+'/attempt_login',{type:'POST',data:data,success:function(d)
{
if (d=='no_email')
{
show_error('Invalid username or password');//Invalid Email address
}
else if (d=='account_disabled')
{
show_error('This account has been disabled, please contact your administrator');
}
else if (d=='invalid_login')
{
show_error('Invalid username or password');//Email or Password did not match
}
else
{
window.location.href = d;
}
}});
}
}
}
$(elem).on('keyup','input',function(e)
{
if (e.keyCode=='13')
{
submit();
}
});
$(elem).find('.login-submit').on('click',function()
{
submit();
});
}
Sorry for all the trouble guys I recently had addware on my PC and battled to get rid of it. I think that it had damaged/hijacked my firefox. After re-installing firefox the problem has gone away, the callbacks now execute.
hi i'm trying to build an rss reader using javascript. everything is up and running except the hyperlinks. I need to pass a variable that will hold the url for each list item. any advice would be appreciated. thanks.
xml ---------------------------
<?xml version="1.0"?>
<rss version="2.0">
<channel>
<title>CNN RSS Feed</title>
<link>http://rss.cnn.com/rss/cnn_world.rss</link>
<description>Feeds from Army Public Affairs</description>
<pubDate>Tue, 11 May 2010 22:04:03 GMT</pubDate>
<language>en-us</language>
<item>
<title>U.S. ambassador to mark Hiroshima</title>
<link>http://www.cnn.com/2010/WORLD/asiapcf/08/05/japan.us.hiroshima.presence/index.html?eref=rss_world&utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+rss%2Fcnn_world+%28RSS%3A+World%29</link>
<pubDate>June 24, 2010</pubDate>
<source url="http://rss.cnn.com/rss/cnn_world.rss">CNN</source>
</item>
<item>
<title>Study: Nearly 1.3 million Mexicans in capital don't have running water</title>
<link>http://www.cnn.com/2010/WORLD/americas/08/04/mexico.water.supply/index.html?eref=rss_world&utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+rss%2Fcnn_world+%28RSS%3A+World%29</link>
<pubDate>13 July 2010</pubDate>
<source url="http://rss.cnn.com/rss/cnn_world.rss">CNN</source>
</item>
</channel>
</rss>
//JavaScript File
/OBJECTS
//objects inside the RSS2Item object
function RSS2Enclosure(encElement)
{
if (encElement == null)
{
this.url = null;
this.length = null;
this.type = null;
}
else
{
this.url = encElement.getAttribute("url");
this.length = encElement.getAttribute("length");
this.type = encElement.getAttribute("type");
}
}
function RSS2Guid(guidElement)
{
if (guidElement == null)
{
this.isPermaLink = null;
this.value = null;
}
else
{
this.isPermaLink = guidElement.getAttribute("isPermaLink");
this.value = guidElement.childNodes[0].nodeValue;
}
}
function RSS2Source(souElement)
{
if (souElement == null)
{
this.url = null;
this.value = null;
}
else
{
this.url = souElement.getAttribute("url");
this.value = souElement.childNodes[0].nodeValue;
}
}
//object containing the RSS 2.0 item
function RSS2Item(itemxml)
{
//required
this.title;
this.link;
this.description;
//optional vars
this.author;
this.comments;
this.pubDate;
//optional objects
this.category;
this.enclosure;
this.guid;
this.source;
var properties = new Array("title", "link", "description", "author", "comments", "pubDate");
var tmpElement = null;
for (var i=0; i<properties.length; i++)
{
tmpElement = itemxml.getElementsByTagName(properties[i])[0];
if (tmpElement != null)
eval("this."+properties[i]+"=tmpElement.childNodes[0].nodeValue");
}
this.category = new RSS2Category(itemxml.getElementsByTagName("category")[0]);
this.enclosure = new RSS2Enclosure(itemxml.getElementsByTagName("enclosure")[0]);
this.guid = new RSS2Guid(itemxml.getElementsByTagName("guid")[0]);
this.source = new RSS2Source(itemxml.getElementsByTagName("source")[0]);
}
//objects inside the RSS2Channel object
function RSS2Category(catElement)
{
if (catElement == null)
{
this.domain = null;
this.value = null;
}
else
{
this.domain = catElement.getAttribute("domain");
this.value = catElement.childNodes[0].nodeValue;
}
}
//object containing RSS image tag info
function RSS2Image(imgElement)
{
if (imgElement == null)
{
this.url = null;
this.link = null;
this.width = null;
this.height = null;
this.description = null;
}
else
{
imgAttribs = new Array("url","title","link","width","height","description");
for (var i=0; i<imgAttribs.length; i++)
if (imgElement.getAttribute(imgAttribs[i]) != null)
eval("this."+imgAttribs[i]+"=imgElement.getAttribute("+imgAttribs[i]+")");
}
}
//object containing the parsed RSS 2.0 channel
function RSS2Channel(rssxml)
{
//required
this.title;
this.link;
this.description;
//array of RSS2Item objects
this.items = new Array();
//optional vars
this.language;
this.copyright;
this.managingEditor;
this.webMaster;
this.pubDate;
this.lastBuildDate;
this.generator;
this.docs;
this.ttl;
this.rating;
//optional objects
this.category;
this.image;
var chanElement = rssxml.getElementsByTagName("channel")[0];
var itemElements = rssxml.getElementsByTagName("item");
for (var i=0; i<itemElements.length; i++)
{
Item = new RSS2Item(itemElements[i]);
this.items.push(Item);
//chanElement.removeChild(itemElements[i]);
}
var properties = new Array("title", "link", "description", "language", "copyright", "managingEditor", "webMaster", "pubDate", "lastBuildDate", "generator", "docs", "ttl", "rating");
var tmpElement = null;
for (var i=0; i<properties.length; i++)
{
tmpElement = chanElement.getElementsByTagName(properties[i])[0];
if (tmpElement!= null)
eval("this."+properties[i]+"=tmpElement.childNodes[0].nodeValue");
}
this.category = new RSS2Category(chanElement.getElementsByTagName("category")[0]);
this.image = new RSS2Image(chanElement.getElementsByTagName("image")[0]);
}
//PROCESSES
//uses xmlhttpreq to get the raw rss xml
function getRSS()
{
//call the right constructor for the browser being used
if (window.ActiveXObject)
xhr = new ActiveXObject("Microsoft.XMLHTTP");
else if (window.XMLHttpRequest)
xhr = new XMLHttpRequest();
else
alert("not supported");
//prepare the xmlhttprequest object
xhr.open("GET",document.rssform.rssurl.value,true);
xhr.setRequestHeader("Cache-Control", "no-cache");
xhr.setRequestHeader("Pragma", "no-cache");
xhr.onreadystatechange = function() {
if (xhr.readyState == 4)
{
if (xhr.status == 200)
{
if (xhr.responseText != null)
processRSS(xhr.responseXML);
else
{
alert("Failed to receive RSS file from the server - file not found.");
return false;
}
}
else
alert("Error code " + xhr.status + " received: " + xhr.statusText);
}
}
//send the request
xhr.send(null);
}
//processes the received rss xml
function processRSS(rssxml)
{
RSS = new RSS2Channel(rssxml);
showRSS(RSS);
}
//shows the RSS content in the browser
function showRSS(RSS)
{
//default values for html tags used
var imageTag = "<img id='chan_image'";
var startItemTag = "<div id='item'>";
var startTitle = "<div id='item_title'>";
var startLink = "<div id='item_link'>";
var startDescription = "<div id='item_description'>";
var endTag = "</div>";
//populate channel data
var properties = new Array("title","link","description","pubDate","copyright");
for (var i=0; i<properties.length; i++)
{
eval("document.getElementById('chan_"+properties[i]+"').innerHTML = ''");
curProp = eval("RSS."+properties[i]);
if (curProp != null)
eval("document.getElementById('chan_"+properties[i]+"').innerHTML = curProp");
}
//show the image
document.getElementById("chan_image_link").innerHTML = "";
if (RSS.image.src != null)
{
document.getElementById("chan_image_link").href = RSS.image.link;
document.getElementById("chan_image_link").innerHTML = imageTag
+" alt='"+RSS.image.description
+"' width='"+RSS.image.width
+"' height='"+RSS.image.height
+"' src='"+RSS.image.url
+"' "+"/>";
}
//populate the items
document.getElementById("chan_items").innerHTML = "";
for (var i=0; i<RSS.items.length; i++)
{
item_html = startItemTag;
item_html += (RSS.items[i].title == null) ? "" : startTitle + RSS.items[i].title + endTag;
item_html += (RSS.items[i].link == null) ? "" : startLink + RSS.items[i].link + endTag;
item_html += (RSS.items[i].description == null) ? "" : startDescription + RSS.items[i].description + endTag;
item_html += endTag;
document.getElementById("chan_items").innerHTML += item_html;
}
//we're done
//document.getElementById("chan").style.visibility = "visible";
return true;
}
var xhr;
<!-- html file -->
<html>
<head>
<script language="javascript" src="rssajax.js"></script>
<style type="text/css">
#chan_items { margin: 20px; }
#chan_items #item { margin-bottom: 10px; }
#chan_items #item #item_title {
font-weight: bold;
}
</style>
</head>
<body onload="getRSS()">
<form name="rssform">
<input name="rssurl" type="hidden" value="ChapRSS.xml">
</form>
<script language="javascript" src="rssajax.js"></script>
<div class="rss" id="chan">
<div id="chan_title"></div>
<div id="chan_description"></div>
<div id="chan_image_link"></div>
<div id="chan_pubDate"></div>
<div id="chan_copyright"></div>
</div>
</body>
</html>
<link>http://www.cnn.com/...?eref=rss_world&utm_source=...</link>
That is not well-formed XML, and hence not RSS. You must escape all literal ampersand symbols to &.
(It's not valid in HTML either. When you put a & in an href="..." attribute you must also escape it to &. The difference is browsers typically correct your mistake for you when they can; XML parsers won't.)
document.rssform.rssurl.value
Adding an ID on the <input> and using document.getElementById is less ambiguous than the old-school form collection access. Either way, that's a rather roundabout way of getting a value into script. Why not lose the form and simple pass the RSS filename as an argument into getRSS()?
this.title;
That doesn't do anything at all. None of the places you refer to a property like this have any effect; you are not creating members by doing this.
var properties = new Array("title", "link", ...
In general avoid the new Array constructor. The array literal syntax (var properties= ['title', 'link, ...]; is easier to read and doesn't have the constructor's unexpected behaviour for a single argument.
eval("this."+properties[i]+"=tmpElement.childNodes[0].nodeValue");
eval is evil. Never use it.
You can use square-bracket notation to access a property with a dynamic name. a.b is the same as a['b'], so:
this[properties[i]]= tmpElement.childNodes[0].nodeValue;
...
imgAttribs = new Array("url","title", ...
You haven't declared var imgAttribs so that's an accidental global. Same with Item in RSS2Channel. (Why the capital letter?)
eval("this."+imgAttribs[i]+"=imgElement.getAttribute("+imgAttribs[i]+")");
That won't work due to lack of quotes on the attribute name. You'll be getting getAttribute(url), and there's no variable called url -> error. Again, use square bracket property access to set the attribute and not eval.
eval("document.getElementById('chan_"+properties[i]+"').innerHTML = ''");
getElementById('chan_'+properties[i]) is fine, there is no point in doing that in an eval.
xhr.setRequestHeader("Cache-Control", "no-cache");
xhr.setRequestHeader("Pragma", "no-cache");
Cache-Control and Pragma are typically HTTP response fields. They will not have the effect you expect in an HTTP request. If you want to ensure no caching occurs from the client side, use a ‘cachebuster’ method such as adding a random number or timestamp to the URL's query string.
innerHTML = curProp
Danger. Values you have fetched are arbitrary text strings and may contain HTML-special characters like < and &. If you write such strings to an element's innerHTML, you are likely to get broken results, and if they include third-party content you have just given yourself a cross-site-scripting security hole.
You can use textContent=... to set the content of an element without having to worry about HTML-escaping, however you then need to detect whether it's supported and fall back to IE's non-standard innerText property if it's not. A way that works on all browsers is to document.createTextNode(curProp) and append that text node to the element.
innerHTML= imageTag+" alt='"+RSS.image.description+ ...
You've got exactly the same problem with HTML-escaping here: if eg. the description contains <script>, you're in trouble. You can write an HTML-encoder, eg.:
function encodeHTML(s) {
return s.replace(/&/g, '&').replace(/</g, '<').replace(/"/g, '"').replace(/'/g, ''');
}
innerHTML= imageTag+' alt="'+encodeHTML(RSS.image.description)+ ...
But really, creating HTML from bits of string totally sucks. Use DOM methods instead:
var img= document.createElement('img');
img.src= RSS.image.url;
img.title= RSS.image.description;
img.width= RSS.image.width;
img.height= RSS.image.height;
document.getElementById('chan_image_link').appendChild(img);