set-cookie overwrite condition - google-chrome

I was trying to determine the exact condition when browsers overwrite existing cookies, and found this pseudocode on the cookie specs document.
https://httpwg.org/specs/rfc6265.html#storage-model
Under #storage-model at step 11
11. If the cookie store contains a cookie with the same name, domain, and path as the newly created cookie:
1. Let old-cookie be the existing cookie with the same name, domain, and path as the newly created cookie. (Notice that this algorithm maintains the invariant that there is at most one such cookie.)
2. If the newly created cookie was received from a "non-HTTP" API and the old-cookie's http-only-flag is set, abort these steps and ignore the newly created cookie entirely.
3. Update the creation-time of the newly created cookie to match the creation-time of the old-cookie.
4. Remove the old-cookie from the cookie store.
12. Insert the newly created cookie into the cookie store.
If my understanding is correct, browsers should treat cookies with blank Domain attribute as different from one with Domain defined.
However, when I ran experiments on chrome and firefox, I found out that this was not the case. Browsers only seem to recognize cookies as different when Path attribute is non blank and not /
Here is the code that I used for the experiment. I used express but I am directly setting the set-cookie header, so there should be no library-specific behavior.
index.mjs
import express from "express";
const app = express()
app.get("/",(req,res)=>{
res.send("front page");
});
const inspect = function(req,res){
res.send(req.headers.cookie);
};
app.get("/inspect",inspect);
app.get("/subdir/inspect",inspect);
app.get("/bar1",(req,res)=>{
res.append("Set-Cookie", "foo=bar1;")
res.send("set foo to bar1");
});
app.get("/bar2",(req,res)=>{
res.append("Set-Cookie", "foo=bar2;")
res.send("set foo to bar2");
});
app.get("/bar3",(req,res)=>{
res.append("Set-Cookie", "foo=bar3; Domain=localhost; Path=/;");
res.send("set foo to bar3 with attrs");
});
//bar1, bar2, and bar3 gets treated the same
app.get("/bar4",(req,res)=>{
res.append("Set-Cookie", "foo=bar4; Domain=localhost; Path=/subdir;");
res.send("set foo to bar4 with path /subdir");
});
//bar4 gets treated differently as expected
app.get("/clear",(req,res)=>{
res.append("Set-Cookie", "foo=bar1; expires=Thu, Jan 01 1970 00:00:00 UTC;");
res.append("Set-Cookie", "foo=bar2; expires=Thu, Jan 01 1970 00:00:00 UTC;");
res.append("Set-Cookie", "foo=bar3; Domain=localhost; Path=/; expires=Thu, Jan 01 1970 00:00:00 UTC;");
res.append("Set-Cookie", "foo=bar4; Domain=localhost; Path=/subdir; expires=Thu, Jan 01 1970 00:00:00 UTC;");
res.send("cleared all cookies");
});
app.listen(3020);
on console at localhost:3020/
console.log(await (await fetch("/clear")).text());
console.log(await (await fetch("/bar1")).text());
console.log(await (await fetch("/subdir/inspect")).text());
console.log(await (await fetch("/bar2")).text());
console.log(await (await fetch("/subdir/inspect")).text());
console.log(await (await fetch("/bar3")).text());
console.log(await (await fetch("/subdir/inspect")).text());
console.log(await (await fetch("/bar4")).text());
console.log(await (await fetch("/subdir/inspect")).text());
console output
cleared all cookies
set foo to bar1
foo=bar1
set foo to bar2
foo=bar2
set foo to bar3 with attrs
foo=bar3
set foo to bar4 with path /subdir
foo=bar4; foo=bar3
Why does this happen? Is this specific to localhost (but in that case, what about blank vs / Path)? Also how reliable is this behavior?
Edit:
I found out that empty or invalid Path field gets set to "/". That answers the path part of this question
The exact algorithm is written under 5.1.4. Paths and Path-Match on the previously mentioned RFC document

Related

setValue() / addValue() type into adress bar instead of selected element

I'm using WebdriverIO + devtools:puppeteer + cucumber + Firefox Nightly.
When using setValue() / addValue(), the first letter of my input is typed into address bar, instead of selected element. The issue for same tests doesn't appear for mse or chrome browsers.
Issue:
After this, nothing happens until function timeouts
INFO devtools: COMMAND navigateTo("https://google.com/")
INFO devtools: RESULT null
INFO devtools: COMMAND findElement("css selector", "input[type=text]")
INFO devtools: RESULT { 'element-6066-11e4-a52e-4f735466cecf': 'ELEMENT-1' }
INFO devtools: COMMAND elementClear("ELEMENT-1")
INFO devtools: RESULT null
INFO devtools: COMMAND elementSendKeys("ELEMENT-1", "hello world")
Code examples:
Test:
Scenario: Try google
When I open "google.com"
Then I type "hello world" into "input[type=text]"
Steps:
When('I open {string}', async function (URL) {
await browser.url(`https://${URL}`);
});
Then('I type {string} into {string}', async function (input, selector) {
await $(selector).setValue(input);
});
Although there is a walkaround for some URLS with clicking on the element before using setValue(), this doesn't work for some cases (e.g. when redirecting from pre-login page to login page with pretyped-in login, I could not click + setValue for password field).
Hope anyone knows how this could be solved or walked around for all cases. Thanks.
[UPD]
#AnthumChris
as I'm using built-in puppeteer, page is not defined by default
Instead I tried:
const puppeteerBrowser = await browser.getPuppeteer()
const pages = await puppeteerBrowser.pages()
const page = await pages[0]
await (await page.waitForSelector('input[type=text]')).type('hello')
It worked for chrome and mse again, but failed for ffox nightly.
After opening in browser requested URL (google.com), I've received next error:
Error in "21: Then I type "hello world" into "input[type=text]""
TypeError [ERR_INVALID_URL]: Invalid URL: http://localhost:localhost:64619`
[UPD]
I've changed browserURL: 'http://localhost:${rdPort}' to browserURL: 'http://${rdPort}' in a ...\node_modules\webdriverio\build\commands\browser\getPuppeteer.js file
so I at least could connect to puppeteer.pages object, but there's still a problem on await (await page.waitForSelector('input[type=text]')).type('hello') action:
ProtocolError: Protocol error (DOM.resolveNode): Node with given id does not belong to the document resolveNode#chrome://remote/content/cdp/domains/content/DOM.jsm:245:15
execute#chrome://remote/content/cdp/domains/DomainCache.jsm:101:25
receiveMessage#chrome://remote/content/cdp/sessions/ContentProcessSession.jsm:84:45
Try awaiting the <input> and typing directly into it:
await (await page.waitForSelector('input[type=text]')).type('hello')

Unable to locate an element with puppeteer

I'm trying to do a basic search on FB marketplace with puppeteer(and it was working for me before) but fails recently.
The whole thing fails when it gets to "location" link on marketplace page. to change the location i need to click on it, but puppeteer Errors out saying:
Error: Node is either not visible or not an HTMLElement
If i try to get the boundingBox of the element it returns null
const browser = await puppeteer.launch();
const page = await browser.newPage();
const resp = await page.goto('https://www.facebook.com/marketplace', { waitUntil: 'networkidle2' })
const withinLink = await page.waitForXPath('//span[contains(.,"Within")]', { timeout: 4000 })
console.log(await withinLink.boundingBox()) //returns null
await withinLink.click() //errors out
If i take a screenshot of the page right before i locate an element it is clearly there and i am able to locate in in chrome console using the same xPath manually.
It just doesn't seem to work in puppeteer
Something clearly changed on FB. Maybe they started to use some AI technology to detect scraping?
I don't think facebook changed in headless browser detection lately, but it seems you haven't taken into account that const withinLink = await page.waitForXPath('//span[contains(.,"Within")]', { timeout: 4000 }) returns an array, even if there is only one matching elment to contains(.,"Within").
That should work if you add [0] index to the elementHandles:
const withinLink = await page.waitForXPath('//span[contains(.,"Within")]')
console.log(await withinLink[0].boundingBox())
await withinLink[0].click()
Note: Timeout is not mandatory in waitForXPath, but I'd suggest to rather use domcontentloaded instead of networkidle2 in page.goto if you don't need all analytics/tracking events to achive the desired results, it just slows down your script execution.
Note 2: Honestly, I don't have such element on my fb platform, maybe it is market dependent. But it works with any other XPath selectors with specific content.

Enter incorrect password in Chrome, login dialog no longer popups

Set the application with webapi in IIS as window authentication
Using ajax to access this webapi
The system popups the login dialog
I enter domain account and wrong password
The strange thing is that the login dialog no longer popup, even I close and reopen it. Why chrome does not give me a chance to correct the wrong password? But IE could work.
I find the chrome contains a cookie with empty, without key and value when I debug it. If I delete it (by Chrome -- setting -- Privacy and security -- Clear browsing data), then it works again.
So, my question is how to delete this cookie by JS? or any other idea? It will be greatly appreciate.
You could set below browser setting to ask always login credential:
Open the Internet Explorer and click the settings icon, and then select the ‘Internet Options’.
Select the ‘Security’ tab in the pop-up window.
Choose the “Local intranet” and choose the “Custom level
Prompt Settings
Select “Prompt for user name and password” under “Logon” for the
Internet Explorer to prompt for getting the credentials from the user.
Select “Automatic logon with current user name and password” for the
Internet Explorer to automatically log on as the currently logged
user
Restart the Internet Explorer to apply the prompt settings.
for chrome:
Click the Chrome menu three dots in the toolbar and choose Settings.
Click Passwords.
Turn off "Auto Sign-in"
if you want to use js then you could try below code:
function deleteAllCookies() {
var cookies = document.cookie.split(";");
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i];
var eqPos = cookie.indexOf("=");
var name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie;
document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT";
}
}
OR
function removeCookies() {
var res = document.cookie;
var multiple = res.split(";");
for(var i = 0; i < multiple.length; i++) {
var key = multiple[i].split("=");
document.cookie = key[0]+" =; expires = Thu, 01 Jan 1970 00:00:00 UTC";
}
}

how to use xhr.overrideMimeType in Chrome / IE Edge?

I have an issue with sending a file (part of a request in form data format).
The issue seems coming from the fact that only in Chrome for Linux the file (which is CVS file, with .csv extension and basically just text) is sent with mimetype (Content-type in request body) Content-Type: application/octet-stream
So, I am trying to override the mimetype to match the same sent by Chrome on Linux which is text/csv.
However the mimetype is apparently not overriden and still send as octet-stream.
My code:
let xhr = new XMLHttpRequest();
let form = new FormData();
form.append('file', file, file.name); // the file is loaded correctly
form.append('payload', JSON.stringify(data)); // data is just a JSON object
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
// we arrive here both on Debian and Windows 10
}
}
xhr.upload.onerror = function() { .... } // no error
xhr.open('POST', 'http://<my_url>', true);
console.log(file.type);
xhr.overrideMimeType("text/csv");
xhr.send(form);
A couple of notes:
console.log(file.type) actually prints "text-csv" but only in Chrome for Linux (Debian specifically). in the other cases (any other browser or platform) nothing is printed
given the previous point, it seems clear to me for some reason any other browser / platform can't recognize the file type, so the file is sent as octet-stream (general binary file)
xhr.overrideMimeType changes the MIME-type of the response, not the request.
I you want to change the MIME-type of the file, just create a new Blob with an explicit file type:
var blob = new Blob([blob], {type: 'text/csv'});
form.append('file', blob, file.name);
The above changes the MIME-type of the file in the uploaded form to "text/csv", as desired.
PS. If you literally want to change the MIME-type of the whole request (instead of just the file), use xhr.setRequestHeader('Content-Type', 'custom MIME type here');. (This only makes sense if you are sending a non-standard or custom data in the xhr.send method).

Twitter Typeahead Bloodhound longer-url request not sent

I am using django with bloodhound in prefetch mode like in the official example.
jQuery(document).ready(function() {
var countries = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.whitespace,
queryTokenizer: Bloodhound.tokenizers.whitespace,
// url points to a json file that contains an array of country names, see
// https://github.com/twitter/typeahead.js/blob/gh-pages/data/countries.json
prefetch: '../static/data/countries.json'
});
// passing in `null` for the `options` arguments will result in the default
// options being used
var typeahead_elem = $('.typeahead');
typeahead_elem.typeahead({
name: 'countries',
source: countries
});
})
The difference with the official example is the url. I use '../static/data/countries.json'
But I know by checking the server and using firebug network tab that no request is sent.
WHEREAS (and now it becomes strange) if I use '../data/countries.json'. Then the request is sent and becomes a 404. But with firebug I can edit it and resend. And putting '../static/data/countries.json' works, it comes back code 304.
Why is my original '../static/data/countries.json' not sent ??
Btw, why is 'countries.initialize();' not needed ?