Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 10 years ago.
Improve this question
I'm investigating a potentially large incident of advertising fraud and in following the bread crumbs have come across a page that calls a javascript aptly named "Google_Analytics.js", which it clearly is not.
I've put the raw code to the file here: http://pastebin.com/5YqzCVbB
I am not skilled enough with javascript to have seen something like this before or to know what it is designed to do.
Is there anyone here who can help?
Thanks in advance for any assistance!
D
Well, it's not Google Analytics, but it looks like it's purposely trying to spoof GA into thinking something's being clicked more than it should be or something.
What is in this? Well, there's a bunch of code related to creating a Flash object. And a big whitelist, presumably of pages this should affect. And there's this:
if (follow.length > 0) {
rand = randomFromTo(100, 700);
rand2 = randomFromTo(1, 6);
if (rand == 666) {
$('<iframe />', {
name: 'follow_analytics',
id: 'follow_analytics',
style: 'width:2px;height:2px;display:none;',
width: '2',
height: '2',
src: follow[0]
}).appendTo('body');
if (trk_value[$.inArray(1 * (follow[0].match(/cid=([\d]*)/)[1]), tracking)]) {
if (rand2 == 1) {
$('<iframe />', {
name: 'trk_analytics',
id: 'trk_analytics',
style: 'width:2px;height:2px;display:none;',
width: '2',
height: '2',
src: 'tracking.html?' + trk_value[$.inArray(1 * (follow[0].match(/cid=([\d]*)/)[1]), tracking)]
}).appendTo('body');
}
}
};
}
This looks like it's meant to artificially inflate numbers just a little bit: one part in 600. And one in 6 of those should be tracked or something. But I don't know a lot about Google Analytics so I can't help you beyond that.
The short answer is, it attempts to find a URL on the page from one of various sources (global Javascript variable, Flash params, and iframe src). If it finds said URL, and the correct random number is chosen, (666, randomly chosen between 100 and 700), then it creates, on the page, an iframe of that URL.
Then, it looks for a "CID" in the URL that was found in the first of two static lists of numeric values. If the CID is found in that list, it gets the corresponding numeric value from the other list. Then, if a second correct random number is chosen, (1, randomly chosen between 1 and 6), it will attach a second iframe. The URL of that iframe will be tracking.html?x, where x is the numeric value found from the translation of the CID.
Neither iframe is intended to be seen.
That's the short answer, but if you need more details, see below. I really hope you can get some value out of this, because I don't know enough about ads and ad behaviour to understand what it's really trying to accomplish.
First, it tries to create a function, but that appears to be a decoy, as all it does is create a string that's supposed to look like code. Also, it never gets called.
var analytics_obj=function(){
var b='undefined",Q="object",n="Shockwave Flash",p="ShockwaveFlash.ShockwaveFlash",P="application/x-shockwave-flash",m="SWFObjectExprInst",j=window,K=document,T=navigator,o=[],N=[],i=[],d=[],J,Z=null,M=null,l=null,e=false,A=false;var h=function(){var v=typeof K.getElementById!=b&&typeof K.getElementsByTagName!=b&&typeof K.createElement!=b,AC=[0,0,0],x=null;if(typeof T.plugins!=b&&typeof T.plugins[n]==Q){x=T.plugins[n].description;if(x&&!(typeof T.mimeTypes!=b&&T.mimeTypes[P]&&!T.mimeTypes[P].enabledPlugin)){x=x.replace(/^.*\s+(\S+\s+\S+$)/,"$1");AC[0]=parseInt(x.replace(/^(.*)\..*$/,"$1"),10);AC[1]=parseInt(x.replace(/^.*\.(.*)\s.*$/,"$1"),10);AC[2]=/r/.test(x)?parseInt(x.replace(/^.*r(.*)$/,"$1"),10)}}else{if(typeof j.ActiveXObject!=b){var y=null,AB=false;try{y=new ActiveXObject(p+".7")}catch(t){try{y=new ActiveXObject(p+".6");AC=[6,0,21];y.AllowScriptAccess="always"}catch(t){if(AC[0]==6){AB=true}}if(!AB){try{y=new ActiveXObject(p)}catch(t){}}}if(!AB&&y){try{x=y.GetVariable("$version");if(x){x=x.split(" ")[1].split(",");AC=[parseInt(x[0],10),parseInt(x[1],10),parseInt(x[2],10)]}}catch(t){}}}}var AD=T.userAgent.toLowerCase(),r=T.platform.toLowerCase(),AA=/webkit/.test(AD)?parseFloat(AD.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1"))false,q=false,z=r?/win/.test(r),w=r?/mac/.test(r)/mac/.test(AD);}'
};
Note that var b= starts with a single quote, and the line ends with a single quote. Nowhere in the middle is there any other single quote, so it is just one long string meant to look like code. Unless it's meant to be run through eval() (and I don't see an eval() anywhere in this script), it's just a decoy.
Second, it creates a function that's a simple shorthand for getting a random number.
Third is the meatiest part. It tries to capture a URL. First it tries a variable called click_url2, which is not declared here, so it must be trying to rip it off from another script.
Failing that, it tries to find a flash movie on the page that has clickTag= or click= in its FlashVars or URL, and capture the value after the =.
Failing that, it looks for an iframe that has an src with the following text in it: click= or CPSC#=, followed by http://media.fastclick.net/w/click.here and forced_click=, and captures all of that text starting with http, and ending at forced_click=. It appends this with the URL: http://cc.openxads.org/clothedcanines.com/popped_content.html.
This behaviour is below:
try {
follow.push(click_url2);
} catch(e){
try {
follow.push(unescape($('object param[name="flashvars"]').attr('value').match(/clickTag=([^&]*)/)[1]));
} catch(e){
try {
follow.push(unescape($('object param[name="movie"]').attr('value').match(/clickTag=([^&]*)/)[1]));
} catch(e){
try {
follow.push(unescape($('object param[name="FlashVars"]').attr('value').match(/clickTag=([^&]*)/)[1]));
} catch(e){
try {
follow.push(unescape($('embed').attr('flashvars').match(/clickTag=([^&]*)/)[1]));
} catch(e){
try {
follow.push(unescape($('embed').attr('flashvars').match(/click=([^&]*)/)[1]));
} catch(e){
try {
follow.push(unescape($('iframe').attr('src').match(/click=(http:\/\/media\.fastclick\.net\/w\/click\.here.*forced_click=)/)[1])+"http%3A%2F%2Fcc.openxads.org%2Fclothedcanines.com%2Fpopped_content.html");
} catch(e) {
try {
follow.push(unescape($('iframe').attr('src').match(/CPSC#=(http:\/\/media\.fastclick\.net\/w\/click\.here.*forced_click=)/)[1])+"http%3A%2F%2Fcc.openxads.org%2Fclothedcanines.com%2Fpopped_content.html");
} catch(e) {};
};
};
};
};
};
};
};
After that, it declares two very long arrays of numbers. I have no clue as to what they are, but based on later behaviour, they seem to be parallel.
If any of the above conditions (the variable, flash, or iframe) were found, it continues. If not it fails.
It will at this point choose two random numbers, the first between 100 and 700, and the second between 1 and 6.
If the first number chosen is 666, it will create an iframe on the page, very small and hidden, not intended to be viewed. That iframe's src will be equal to whatever URL it managed to capture above.
if(rand==666){
$('<iframe />', {
name:'follow_analytics',id:'follow_analytics',style:'width:2px;height:2px;display:none;',width:'2',height:'2',src:follow[0]
}).appendTo('body');
Then (still if the first number is 666), comes the tricky part. It will try to find a cid= in the URL it captured earlier, and capture the (expectedly numeric) value of it. This numeric value will be checked against one of the arrays declared earlier. If found, it will get the value from the other array at the same index as the value it found. Essentially, this is just a translation table, changing the so-called CID into a different but corresponding numeric value.
So if that one matched, AND if the second random number (between 1 and 6) is 1, only then will it attach a second iframe. The second iframe's src will be tracking.html?, appended by the numeric value it got from the second array, whose value was gotten by matching it from the first array.
if(trk_value[$.inArray(1*(follow[0].match(/cid=([\d]*)/)[1]),tracking)]){
if(rand2==1){
$('<iframe />', {
name:'trk_analytics',id:'trk_analytics',style:'width:2px;height:2px;display:none;',width:'2',height:'2',
src:'tracking.html?'+trk_value[$.inArray(1*(follow[0].match(/cid=([\d]*)/)[1]), tracking)]
}).appendTo('body');
}
}
Related
This question already has answers here:
Template literal inside of the RegEx
(2 answers)
Closed 1 year ago.
hi I am coding an online shop ,
currently working on add to cart functionality,
like to store product ids and their quantity in a cookie like this id1:qt1,id2:qt2...
like to check if a product is already is in cart , looks like my regular expr doesn't work
const reg = new RegExp(`${product_id}:\d+`);
if (!reg.test(cart_cookie)){
const values = cart_cookie.split(',');
values.push(`${product_id}:1`);
setCookie('cart', values.join(','), 7);
}
else {
console.log('already in cart.')
}
Yep, your regex is off :)
It stumped me for a second, but its clear when you check what your "regex" string actually evaluates to: /product_id:d+/. This is because the string you're passing in sees the \d as a literal d. To fix this, just throw another \ in there so the original \ is whats being escaped. Before, you were matching things like "apple:ddddddd".
Once you do that, your code DOES seem to work, but maybe just not like you expect it to?
I've put your code into a function, since you would -- presumably -- be calling this every time you want to add an item to the cart, and added a console.log statement to show the end value of the "cookie."
// Just as a stand in for actual values
let cart_cookie = 'apples:2,oranges:3,bananas:1';
function addCartItem(product_id) {
const reg = new RegExp(`${product_id}:\\d+`);
console.log(reg)
if (!reg.test(cart_cookie)){
const values = cart_cookie.split(',');
values.push(`${product_id}:1`);
console.log('cart_cookie is now ', values.join(','));
setCookie('cart', values.join(','), 7);
}
else {
console.log('already in cart.')
}
}
addCartItem('apples');
// already in cart.
addCartItem('kiwis');
// cart_cookie is now apples:2,oranges:3,bananas:1,kiwis:1
Its fixed! 🥳 But...
I'm not quite sure what your product ids look like, but if they contain special characters (e.g. periods, question marks, etc.) it'll cause some issues with how your regex performs. I doubt you have things like question marks in it, but something like this illustrates my point:
let cart_cookie = '123.15:3,2.5.141:1';
/* ... */
addCartItem('1.3.15');
// already in cart.
I know, its a rather unlikely scenario -- and it might not even apply to you if you know your product ids won't contain anything tricky -- but if you're goal is to build an online shop, you'll probably want to cover all your bases.
Even fixing that, this still has a potential issue, as this will only let you add a quantity of 1 to an item thats not already in the cart, with no ability to increment it after that. Not sure if thats what you're going for.
This veers off of the main question you posed, but a potentially better solution might be using the browser's localstorage (or sessionstorage) to keep track of the cart. This would allow you to use more familiar data structures to store this info, rather than a string that you have to pray you're parsing correctly.
More info on that here: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
you can use just array method instead you don't need for regex for that,
for example..
const values = cart_cookie.split(',');
values.foreach(value => {
if(value != product_id){
values.push(`${product_id}:1`);
setCookie('cart', values.join(','), 7);
}
});
A function in my WP plugin has just randomly (as far as I can tell) stopped working.
Here's the code in question:
window.send_to_editor = function(html) {
var classes = jQuery('img',html).attr('class');
var items = classes.split(" ");
... more stuff here
}
I've confirmed that the html variable is indeed an img html tag. Here's what firebug shows when I do a console.log of the object (console.log(jQuery('img',html));):
Object[]
context -> undefined
jquery -> "1.11.2"
length -> 0
prevObject -> Object[img.alignnone.size-full.wp-image-1234 name.jpg]
And the error it shows is classes is undefined.
I figure there's something wrong with the object I get, but this used to work recently and I'm not aware of any changes in the site that could have caused this.
I'd appreciate any input on this.
EDIT:
More info. This happens with two plugins which are supposed to be unrelated (made by different people). It happens when, after uploading an image to the server (or selecting a previously uploaded picture) you try to insert it into the post.
As I said before this error has appeared out of nowhere, it was working as intended a couple days ago. The only thing I can think of that has changed since then is the domain name, but I can't see how that could be related.
The jQuery selector always returns a jQuery object, but when the length is 0 then no elements were found matching the selector that you provided. In your example you've confirmed that nothing is selected as the length of the jQuery object is 0. Perform a check whether an element was selected like this:
var $els = jQuery('img',html),
classes;
if ($els.length) {
classes = $els.attr("class");
}
Keep in mind that your DOM query is limited by what you pass in as the html parameter. If you simply want to find the images on the page do: var $els = jQuery('img');
I finally managed to fix this; the key was parsing the html string variable into proper HTML, using jQuery.parseHTML(). Thanks to everyone who helped!
There is a page I can access that contains a bunch of links like this:
<a href="#" onclick="navigate(___VIEW_RAID_2, {raid_inst_id:556816});return false;">
The number after the raid_inst_id: is always going to be different and there will be multiples on the same page all with different numbers. I'm trying to put together a javascript that will scrape the page for these links, put them in an array and then cycle through clicking them.
Ideally, an alert causing a pause between onclicks would be helpful. I've been unsuccessful so far even trying to gather the numbers and just echoing them out let alone manipulating them.
Any hints or help would be greatly appreciated!
Below is a function I tried putting together just to see if I could capture some of the onclick values for further processing but, this produces nothing...
function closeraids(){
x=document.getElementsByTagName('a');
for(i=0;i<x.length;i++)
{
attnode=x.item(i).getAttributeNode('onclick');
alert("OnClick events are: " + attnode);
}
}
Wow - 4 months later and the same problem still exists. I decided to look into this again only to find my own posted question in my Google search! Does anyone have any thoughts on what could be done here? The function I'm trying to provide will be part of a Chrome extension I already provide to users. It uses a combination of a .js file I host on my webserver and injected html content.
Any help would be appreciated!
Had some fun while making this jsfiddle: http://jsfiddle.net/Ralt/ttkGG/
Mostly because I went onto using almost fully functional style... but well. Onto your question.
I'm using getAttribute('onclick') to get the string in there. It shows something like:
"navigate(___VIEW_RAID_2, {raid_inst_id:553516});return false;"
So I just built the necessary regex to match it, and capture the number after raid_inst_id:
var re = /navigate\(___VIEW_RAID_2, {raid_inst_id:(\d+)}\);return false;/;
It's mostly rewriting the string by escaping the parentheses and putting (\d+) where you want to capture the number. (\d+ is matching a number, () is capturing the matched string.)
Using match(), I can simply get the captured string as the last element. So, rewriting the code in old IE way:
var links = document.getElementsByTagName('a'),
re = /navigate\(___VIEW_RAID_2, {raid_inst_id:(\d+)}\);return false;/;
for (var i = 0, l = links.length; i < l; i++) {
var attribute = links[i].getAttribute('onclick'),
nb;
if (nb = attribute.match(re)) {
alert(nb.pop());
}
}
Note - I do not have access to a database and cannot use PHP, thus is why I'm asking for help. My hands are tied and I can only do through html/javascript/etc.
I'm creating a online quiz that has 10 questions (1 question per page), but only one question determines the outcome to 4 different final locations. Question 3 (page 3) is the determining question, but I need to "record" the radio button selection, so the last question (page 10) determines the URL for the submit button, which in hand redirects the user.
All the other questions are vanity questions, as question 3 is the big determining factor.
I was thinking that the answer on question 3 (page 3) creates a cookie with 1 of 4 values (based off of the radio button selection), then on the last question (page 10) the cookie is read and the value determines the url, which then writes in the URL redirect code for the submit button. This is just theory now, as I'm not sure if this is doable.
Any advice would be greatly appreciated.
I can see at least 2 more ways to do it:
Use an iFrame:
Keep a hidden input outside an iFrame. This input records the answers. An iFrame script must append the answer (comma-separated or whatever) to the hidden input in the parent when a question is submitted. A new page gets loaded into the iFrame after that. When the last question is encountered, the iFrame script can update and read the parent input, then determine which url to load, and then load the url into the parent document location.
Use jQuery Ajax: EDITED to remove hidden input which isn't needed here:
Using jQuery Ajax you can load the next question into a main div, and also retain the values of the answers you want in the jQuery script.
EDIT: Here is another way, using CSS and javascript/jQuery:
Load all the questions into ONE page. But only the current question has style='display:block;', the others must have style='display:none'. When a question is answered, use javascript or jQuery to record the answers in a variable. In your case it seems you are only interested in one answer, so it's even easier. Then use your script to change the display attributes depending on which question should show. Finally, if it is the last question which is submitted, you can determine the url and load the next page.
HOW TO GET THE PARAMETER FROM A QUERY STRING:
function getQuery(key_str) {
// return value of key_str variables query string of url
// Example: url = "index.html?answer=5"; if key_str = "answer" then it returns "5"
if(window.location.search) {
var query = window.location.search.substr(1);
var pairs = query.split("&");
for(var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split("=");
if(unescape(pair[0]) == key_str) return unescape(pair[1]);
}
return null;
}
}
function go() {
var answer = getQuery(answer);
var durl = "http://www.defaulturl.com";
switch (answer) {
case '1' : url = "http://www.url1.com"; break;
case '2' : url = "http://www.url2.com"; break;
case '3' : url = "http://www.url3.com"; break;
default : url = durl;
}
window.location.href = url;
return false;
}
<input type="button" value="Submit" name="butt" onclick="return go();">
Why don't you pass the relevant answer as a GET ? You can easily read the query string in Javascript.
Your cookie method would work fine. Also doable with HTML5 local storage. But I have to ask... if you're not recording the output of the other 9 questions anywhere, what's the point of asking them? :)
I would do everything I could to avoid cookies and just keep the quiz on one page... I know you're saying you don't have backend access, but I just wanted to throw that out there.
You can make the user feel like it's really 10 "pages," but your JS would be swapping "pages" and storing the form values in one page load. Think about a DOM tabbed element with 10 tabs.
Totally agree with Alex that HTML5 local storage would work, too.
I am writing a greasemonkey script. Recently i had this same problem twice and i have no idea why is this happening.
function colli(){
.....
var oPriorityMass = bynID('massadderPriority');//my own document.getElementById() function
var aPriorities = [];
if (oPriorityMass) {
for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) {
var sCollNumber = oPriorityMass.childNodes[cEntry].getAttribute('coll');
if (bynID('adder' + sCollNumber + '_check').checked)
aPriorities.push(parseInt(sCollNumber));
}
}
.....
}
So the mystery of this is, one day i had oPriorityMass named as oPririoty. It was working fine, but the whole function was not yet complete and i started working on another functions for my script. These functions have no connection with each other.
Few days later i decided to go back to my function in the above example and finish it. I ran a test on it without modifying anything and got an error in the firefox's (4) javascript error console saying that oPriority.chilNodes[cEntry] is undefined. NOTE, few days back i have tested it exactly the same way and there was no such problem at all.
Ok, so, i decided to rename oPriority to oPriorityMass. Magically, problem got solved.
At first i thought, maybe there was some conflict of 2 objects, with the same name being used in different functions, which somehow continued to live even outside of function scope. My script is currently over 6000 lines big, but i did a search and found out that oPriority was not mentioned anywhere else but in this exact function.
Can somebody tell me, how and why is this happening? I mentioned same thing happened twice now and they happened in different functions, but the same problem node.childNodes[c] is undefined yet node is not null and node.childNodes.length show correct child count.
What is going on? How do i avoid such problems?
Thank you
EDIT: The error given by error console is
Error: uncaught exception: TypeError: oPriorityMass.childNodes[cEntry] is undefined
In response to Brocks comment:
GM_log(oPriorityMass.childNodes[cEntry]) returns undefined as a message. So node.childNodes[c] is the thing that is undefined in general.
My script creates a div window. Later, the above function uses elements in this div. Elements do have unique IDs and i am 100% sure the original site don't know about them.
My script has a start/stop button to run one or the other function when i need to.
I have been refreshing the page and running my script function now. I have noticed that sometimes (but not always) script will fail with the described error on the first run, however, if i run it again (without refreshing the page) it starts working.
The page has a javascript that modifies it. It changes some of it's element widths so it changes when the browser is resized. But i know it has no effect on my div as it is left unchanged when i resize browser.
EDIT2:
function bynID(sID) {
return top.document.getElementById(ns(sID));
}
function ns(sText) {
return g_sScriptName + '_' + sText;
}
ns function just adds the script name in front of the ID. I use it when creating HTML element so my elements never have the same id as the web page. So bynID() is simple function that saves some typing time when i need to get element by ID.
I have modified my colli() function to include check
if (oPriorityMass) {
if (!oPriorityMass.childNodes[0]) {
GM_log('Retrying');
setTimeout(loadPage,2000);
return;
}
for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) {
var sCollNumber = oPriorityMass.childNodes[cEntry].getAttribute('coll');
if (bynID('adder' + sCollNumber + '_check').checked)
aPriorities.push(parseInt(sCollNumber));
}
}
The loadPage function does 1 AJAX call, then i run few XPATH queries on it, but the actual contents are never appended/shown on the page, just kept inside document.createElement('div'), then this function calls colli(). So now, as i have modified my function, i checked the error console and saw that it may take up to 5 tries for it to start working correctly. 5 x 2seconds, thats 10 seconds. It is never 5 retries always, may vary There's got to be something else going on?
In Firefox, childNodes can include #text nodes. You should check to make sure that childNodes[cEntry] has nodeType == 1 or has a getAttribute method before trying to call it. e.g.
<div id="d0">
</div>
<div id="d1"></div>
In the above in Firefox and similar browsers (i.e. based on Gecko and WebKit based browsers like Safari), d0 has one child node, a text node, and d1 has no child nodes.
So I would do something like:
var sCollNumber, el0, el1;
if (oPriorityMass) {
for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) {
el0 = oPriorityMass.childNodes[cEntry];
// Make sure have an HTMLElement that will
// have a getAttribute method
if (el0.nodeType == 1) {
sCollNumber = el0.getAttribute('coll');
el1 = bynID('adder' + sCollNumber + '_check');
// Make sure el1 is not falsey before attempting to
// access properties
if (el1 && el1.checked)
// Never call parseInt on strings without a radix
// Or use some other method to convert to Number
aPriorities.push(parseInt(sCollNumber, 10));
}
}
Given that sCollNumber seems like it is a string integer (just guessing but it seems likely), you can also use:
Number(sCollNumber)
or
+sCollNumber
whichever suits and is more maintainable.
So, according to your last edit, it now works, with the delay, right?
But when I suggested the delay it was not meant to do (even more?) ajax calls while waiting!!
NOT:
if (!oPriorityMass.childNodes[0]) {
GM_log('Retrying');
setTimeout(loadPage,2000);
return;
More like:
setTimeout (colli, 2000);
So the ajax and the other stuff that loadPage does could explain the excessive delay.
The random behavior could be caused by:
return top.document.getElementById(ns(sID));
This will cause erratic behavior if any frames or iframes are present, and you do not block operation on frames. (If you do block such operation then top is redundant and unnecessary.)
GM does not operate correctly in such cases -- depending on what the script does -- often seeming to "switch" from top scope to frame scope or vice versa.
So, it's probably best to change that to:
return document.getElementById (ns (sID) );
And make sure you have:
if (window.top != window.self) //-- Don't run on frames or iframes
return;
as the top lines of code.
Beyond that, it's near impossible to see the problem, because of insufficient information.
Either boil the problem into a Complete, Self Contained, Recipe for duplicating the failure.
OR, post or link to the Complete, Unedited, Script.