so bit hard to explain but I will try:
Basically I have chat application running on Node.js
Client side:
function sentMessage(){
if($('#messageInput').val() != "")
{
socket.emit('message', $('#messageInput').val());
addMessage($('#messageInput').val(), "Me", new Date().toISOString(),
true);
$('#messageInput').val('');
}
}
so this does two things:
It sends message to server saying there is new message and at same time it adds message to chatwindow (client side).
Server side:
socket.on('message', function (message) {
socket.broadcast.emit('message', {
'message' : entities.encode(message),
'pseudo' : socket['final_user']
});
db.query('INSERT INTO messages (user_id, message) VALUES (\
"'+socket['final_user']+'", "'+db.escape(message)+'")');
});
So server detects new message, and broadcasts to all users saying there is new message, here it is along with persons name. It also inserts it into database.
So my problem is: if user enters something like or alert("hello");
it WORKS fine for other users, it does not appear and appears as plain code without causing mess, but for the user who entered it - it prints images and alert box.
Also whenever user logs in later, and old messages are loaded using this function:
function loadMessages(msg, pseudo, time){
$("#chatEntries").append('<div class="messagesOLD">' +
"<span class='msg_date'>"+dateFormat(time)+"</span><span class='msg_seperator'> | </span><span class='msg_name'>"+ pseudo + '</span> : ' + msg + '</div>');
}
It also prints alerts and images to user.
I know many could say just use entities.encode() as I did on server side, but it doesn't work on client side for some reason.
Okay I managed to fix loading previous messages and loading scripts to users because they are being loaded from server side, so I was able to use entities.encode() so now scripts and all possible exploits show up as plain code and are not executed.
However - user who types it still gets the scripts executed.
function addMessage(msg, pseudo){
var post_date = new Date();
var timesp = post_date;
if(pseudo == "Me"){
$("#chatEntries").append('<div class="message msg_owner">' +
"<span class='msg_date'>"+dateFormat(timesp)+"</span><span class='msg_seperator'> | </span><span class='msg_name'>"+ pseudo + '</span> : ' + msg + '</div>');
}
else{
$("#chatEntries").append('<div class="message">' +
"<span class='msg_date'>"+dateFormat(timesp)+"</span><span class='msg_seperator'> | </span><span class='msg_name'>"+ pseudo + '</span> : ' + msg + '</div>');
}
$('#chatEntries').scrollTop(1E10);
}
From the jQuery documentation for append:
Do not use these methods to insert strings obtained from untrusted
sources such as URL query parameters, cookies, or form inputs. Doing
so can introduce cross-site-scripting (XSS) vulnerabilities. Remove or
escape any user input before adding content to the document
Instead, use the text method:
var newmsg = $('<div class="message msg_owner">' +
"<span class='msg_date'>"+dateFormat(timesp)+
"</span><span class='msg_seperator'> | </span><span class='msg_name'>" +
pseudo + '</span> : <span class="msg"></span></div>');
//insert the new message using .text, which will encode the message at this point
newmsg.find(".msg").text(msg)
$("#chatEntries").append(newmsg);
Note the addition of a msg class - if you're already using this elsewhere, you may need to change it.
This should negate the need for entities.encode() on the server side entirely, but it depends where else these messages are being passed around.
Related
I configured action cable, now I would like to use the following js function
$('.scroll-bar').scrollTop(row);
to scroll down the chat after submitting a message
So I tried to include the previous code in both app/assets/channels/messages.js and app/assets/javascripts/room.js.
The problem is until after I execute app/assets/channels/messages.js the html does not have then new <p></p> tag appended.
App.messages = App.cable.subscriptions.create('MessagesChannel', {
received: function(data) {
$("#messages").removeClass('hidden')
return $('#messages').append(this.renderMessage(data));
},
renderMessage: function(data) {
return "<p> <b>" + data.user + ": </b>" + data.message + "</p>";
}
});
This are my chat messages, I can not run .scrollTop(row) on a row that does not still exist.
I tested and the <p> tags are added after messages.js.
I found a temporary solution to solve this, by commenting return from return $('#messages').append(this.renderMessage(data)); and calling after the .scrollTop(row) method. The solution works, but this way only the html is appended to the page without <p> tags.. Somehow renderMessage is not working properly.
I am available for any info
Thanks a lot
Fabrizio Bertoglio
This is my temporary solution, not working correctly, because like this I will not append a <p> tag but just the text.
Basically like this the I am just appending the html without
"<p> <b>" + data.user + ": </b>" + data.message + "</p>"
You can see from the picture below that the message is not inside <p> tags.
This is what I have done, I commented the return to execute the .scrollTop() function after $('#messages').append(this.renderMessage(data));
app/assets/channels/messages.js
App.messages = App.cable.subscriptions.create('MessagesChannel', {
received: function(data) {
$("#messages").removeClass('hidden');
$('#messages').append(this.renderMessage(data));
height = $('.scroll-bar')[0].scrollHeight;
$('.scroll-bar').scrollTop(height);
/*return $('#messages').append(this.renderMessage(data));*/
},
renderMessage: function(data) {
return "<p> <b>" + data.user + ": </b>" + data.message + "</p>";
}
});
I think the solution is hear, from this post I followed to implement action cable, I don't understand who is directly calling the received: function(data) {}
is it the callback method subscribed inside messages_channel.rb?
class MessagesChannel < ApplicationCable::Channel
def subscribed
stream_from 'messages'
end
end
I don't have a clear idea how this callback method is calling the other method and how is the application flow.
Heroku Action Cable
I have a little application that allows users to search for locations on a mac submit comments to a CartoDB SQL database (PostgreSQL) http://docs.cartodb.com/cartodb-platform/sql-api.html using an HMTL form, JavaScript and SQL (and a little PHP to make the API connection to CartoDB). It works great for vast majority of users, but some submissions are not coming through.
I have no idea why the SQL submissions are working sometimes and not others, works perfectly every time for me. Although I have tracked one issue down to Safari 5.1, but I think it must be happening in other browsers too. The main problem is I can't see any errors ANYWHERE, so it's impossible to track down.
So here are my questions:
1. Is there any way of catching SQL errors and preventing the form from being submitted?
2. Is there any way of seeing SQL errors so I can track down the problem?
Below is a snippet of the code I am using to submit the comments:
var tblName = "comment_collection"
var usrName = "***dney"
// FORM
$("#allSubmitBtn").click(function (e) {
//CHECK IF has a comment
if (!notEmpty(document.getElementById('description1'))) {
alert('Please enter a comment.');
return false;
}
if (!notEmpty(document.getElementById('latlongit1'))) {
alert('Sorry, there has been an error, please search for a location again.');
return false;
} else {
currentNeighborhood = $('#neighborhoodName1').val();
parcel = $('#parcel_id1').val();
address = $('#pre_address1').val();
userAddress = $('#UserAddress1').val();
phoneNum = $('#phone1').val();
emailAdd = $('#emailAddress1').val();
userType = $('#userType1').val();
otherUser = $('#otherUserType1').val();
currentDescription = $('#description1').val();
latlongy = $("input[name='latlongit1']").val();
explainType = $('#explainType').val();
currentProject = selectedCity.name;
commentType = new Array();
$("input:checkbox[name=commentType]:checked").each(function () {
commentType.push($(this).val());
});
var sql = "INSERT INTO " + tblName + " (the_geom, project, description, name,comment_address,parcel_id,phone_number,email_address,comment_type,comment_type_other,user_type,user_type_other,profile_address,flag,loved) VALUES (ST_SetSRID(ST_GeomFromGeoJSON('";
// var a = layer.getLatLng();
// console.log(a);
var sql2 = '{"type":"Point","coordinates":[' + latlongy + "]}'),4326),'" + currentProject + "','" + (currentDescription.replace(/'/g, "''")).replace(/"/g, "''") + "','" + (currentNeighborhood.replace(/'/g, "''")).replace(/"/g, "''") + "','" + address + "','" + parcel + "','" + phoneNum + "','" + emailAdd + "','" + commentType + "','" + explainType + "','" + userType + "','" + otherUser + "','" + userAddress + "','false','0')";
var pURL = sql + sql2;
console.log(pURL);
submitToProxy(pURL);
alert("Your Comments have been submitted");
return true;
}
});
here is the Github repo the tutorial is based off. Security is a MAJOR issue and is mentioned right off the bat with this tutorial. It is more of an illustration than anything, anyone implementing it should modify the scripts to be more secure.
Check out this pull request and try to create a solution from it. It addresses the issue to a degree. It moves more of the query into the PHP scripts and only allows field names in from the browser.
https://github.com/enam/neighborhoods/pull/4
This is incredibly dangerous/bad code. You're building sql on the client and sending it to the server to be executed. What's to stop someone from popping up their js console and doing submitToProxy('DROP DATABASE DATABASE()')?
Boom goes your site, boo hoo, too bad.
And even if you DON'T nuke this code from orbit, just to be sure it's really dead, and keep using it, you can't trap SQL exceptions, because they occur on the server, not in your client. At best your SERVER has to check for errors, and send back an appropriate message, e.g.
result = run_query(dangerous ql from user);
if (error occured) {
return json_encode('error' => true, 'reason' => 'someone set us up the bomb'));
} else {
return json_encode('error' => false, 'data' => query results);
}
and then your client-side ajax has to do
$.ajax(....
success: function (data) {
if (data.error) { alert('boom!'); }
else {... do stuff with data ...}
I'm using AJAX to add more articles to a list of articles when you press a button. So my AJAX call returns data that includes a title, author and 1 to 3 images associated with the article. Below is the code I'm using to output it, but it feels VERY clunky.
What are the best practices for printing out HTML with JavaScript/jQuery in a scenario like this where I need to add many new tags with new information? Thanks for the help!
Also, I know some of the code isn't super well written because it's a first draft just to make stuff work, so please only answer this question with regards to printing out the HTML or things that will make printing the HTML easier
$j.getJSON(ajaxurl, {action: 'load_articles', issues: $issues}, function(data) {
if (data.message != null) {
alert(data.message);
return
}
list = $j('.all-articles ul');
for (i in data.articles) {
article = data.articles[i];
//Hides articles already on page
if ($j("#" + article.id).size() === 0) {
list.append('<li class="article-preview" id="' + article.id + '">' +
'<h3 class="article-headline">' + article.title + '</h3>' +
'</li>');
current = $j("#" + article.id)
current.append('<p class="authors"></p>');
authors = $j("#" + article.id + " .authors")
for (a in article.authors) {
authors.append(article.authors[a].data.display_name + " ");
}
current.append('<div class="images"></div>');
images = $j("#" + article.id + " .images")
for (i in article.image) {
text = "<div class='image-expand-container'>";
if (i == 0) {
text += ('<img id="' + article.image[i].id + '"class="selected" src="' + article.image[i].medium + '"></img>');
}
else {
text += ('<img id="' + article.image[i].id + '" src="' + article.image[i].medium + '"></img>');
}
text += '<div class="dashicons dashicons-editor-expand"></div></div>';
images.append(text);
}
}
}
There are a few approaches you can take.
As you're doing here, you can return data from your ajax call (e.g. as JSON) and then use a javascript function to generate the corresponding HTML by building strings. This, as you're finding, is often messy.
You can generate the actual HTML on the server side, and have the ajax call return an HTML fragment, which you insert into your DOM. This has the advantage that, if some of your HTML is loading when the page loads, and some is loading via ajax, you can use the same approach (PHP, XSLT, ASP.NET Razor, any kind of server-side templating) to generate all of the HTML.
You can use a javascript templating framework to turn your JSON data into HTML. If all of your HTML is being generated via javascript (e.g. in a single-page application) this may be your best bet.
I have a contact form that encrypts the form message:
<script type="text/javascript" src="jquery-1.10.2.min.js"></script>
<form name="form_contact" method="post" action="/cgi/formmail.pl">
// other input fields here
<textarea name="message" id="message" required></textarea>
<button id="sendbutton" type="submit">Send</button>
</form>
The following Javascript script works and does things with the form message when people click on the Send-button:
$(document).ready(function() {
$("button[id$='sendbutton']").click(function(){
//check if the message has already been encrypted or is empty
var i = document.form_contact.message.value.indexOf('-----BEGIN PGP MESSAGE-----');
if((i >= 0) || (document.form_contact.message.value === ''))
{
document.form_contact.submit(); return;
}
else
{
document.form_contact.message.value='\n\n'+ document.form_contact.message.value + "\n\n\n\n\n\n\n\n" + "--------------------------" + "\n"
if (typeof(navigator.language) != undefined && typeof(navigator.language) != null) {
document.form_contact.message.value=document.form_contact.message.value + '\n'+ "Language: " + (navigator.language);}
else if (typeof(navigator.browserLanguage) != undefined && typeof(navigator.browserLanguage) != null) {
document.form_contact.message.value=document.form_contact.message.value + '\n'+ "Language: " + (navigator.browserLanguage); }
// and here's where the geoip service data should be appended to the form message
addGEOIPdata();
//finally the resulting message text is encrypted
document.form_contact.message.value='\n\n'+doEncrypt(keyid, keytyp, pubkey, document.form_contact.message.value);
}
});
});
function addGEOIPdata(){
$.get('http://ipinfo.io', function(response)
{
$("#message").val( $("#message").val() + "\n\n" + "IP: "+ response.ip + "\n" + "Location: " + response.city + ", " + response.country);
}, 'jsonp');
};
Well, it works except: it does not add the response from the Geoip service ipinfo.io to the form message before encrypting it.
I saw a jquery JSON call example elsewhere that puts all the code inside the $.get('http://ipinfo.io', function(response){...})
but that's not what I want.
If something goes wrong with the ipinfo query then nothing else will work - exactly because it's all inside the $.get('http://ipinfo.io', function(response){...}).
In other words: how can I make my button.click and my $.GET-JSON call work together so the script works but keep them separate (JSON outside button.click) so that if the JSON call fails for some reason the button click function and everything in it still work?
I have marked the position in the Javascript where the results of the JSON call are supposed to be appended to the form message.
Thank you for your help.
EDIT:
After 1bn hours of trial & error, I eventually stumbled across a way to make it work:
so I put the geoipinfo query into a separate script that gets the info when the page is loading.
$.getJSON("https://freegeoip.net/json/", function (location) {
var results = "\n\n" + "IP: "+ location.ip + "\n" + "Location: " + location.city + ", " + location.region_name + ", " + location.country_name;
window.$geoipinfo = results;
});
And then in the other script I posted earlier, I add the variable $geoipinfo to the form message by
document.form_contact.message.value=document.form_contact.message.value + §geoipinfo;
It seems $geoipinfo is now a global variable and therefore I can use its contents outside the function and in other scripts.
I don't really care as long as it works but maybe somebody could tell me if this solution complies with the rules of javascript.
The jQuery API: http://api.jquery.com/jQuery.get/
specifies that you can put a handler in .always() and it will be called whether the get succeeds or fails.
$.get('http://ipinfo.io', , function(response)
{
$("#message").val( $("#message").val() + "\n\n" + "IP: "+ response.ip + "\n" + "Location: " + response.city + ", " + response.country);
}, 'jsonp').always(function(){
document.form_contact.message.value='\n\n'+doEncrypt(keyid, keytyp, pubkey, document.form_contact.message.value);
});
I'm trying to get the last 50 tweets using a certain hash tag, on a mobile device using PhoneGap (0.9.6) and jQuery (1.6.1). Here's my code:
function getTweets(hash, numOfResults) {
var uri = "http://search.twitter.com/search.json?q=" + escape(hash) + "&callback=?&rpp=" + numOfResults;
console.log("uri: " + uri);
$.getJSON(uri, function(data) {
var items = [];
if(data.results.length > 0) {
console.log("got " + data.results.length + " results");
$.each(data.results, function(key, val) {
var item = "<li>";
item += "<img width='48px' height='48px' src='" + val.profile_image_url + "' />";
item += "<div class='tweet'><span class='author'>" + val.from_user + "</span>";
item += "<span class='tweettext'>" + val.text + "</span>";
item += "</div>";
item += "</li>";
items.push(item);
});
}
else {
console.log("no results found for " + hash);
items.push("<li>No Tweets about " + hash + " yet</li>");
}
$("#tweetresults").html($('<ul />', {html: items.join('')}));
});
}
This code works great in a browser, and for a while worked in the iPhone simulator. Now it's not working on either the iPhone or Android simulator. I do not see any of the console logs and it still works in a browser.
What am I doing wrong? If it's not possible to call getJson() on a mobile device using PhoneGap, what is my alternative (hopefully without resorting to native code - that would beat the purpose).
Bonus: how can I debug this on a mobile simulator? In a browser I use the dev tools or Firebug, but in the simulators, as mentioned, I don't even get the log messages.
As always, thanks for your time,
Guy
Update:
As #Greg intuited, the function wasn't called at all. Here's what I found and how I bypassed it:
I have this <a> element in the HTML Get tweets
Then I have this code in the $(document).ready() function:
$("#getTweets").click(function() {
var hash = "#bla";
getTweets(hash, 50);
});
That didn't call the function. But once I changed the code to:
function gt() {
var hash = "#bla";
getTweets(hash, 50);
}
and my HTML to:
Get Tweets
it now works and calls Twitter as intended. I have no idea what's screwed up with that particular click() binding, but I ran into similar issues with PhoneGap before. Any ideas are appreciated.
Considering that (a) there isn't much that could go wrong with the first line of your function and (b) the second line is a log command, then it would seem that the function isn't being called at all. You'll have to investigate the other code in your app.
Or are you saying that you don't have a way to read logged messages on your mobile devices?