bjowes / cypress-ntlm-auth

Windows authentication plugin for Cypress
MIT License
55 stars 10 forks source link

Replaces Custom status messages with their defaults #249

Closed FelixWhitefield closed 6 months ago

FelixWhitefield commented 6 months ago

In the app I am trying to test with this plugin, we use the HTTP status message for sending information to the client.

When running cypress with cypress-ntlm the status message gets converted to the default for the status code. e.g, 403 will have the status message 'Forbidden' in cypress even if it is set to a custom value.

I have tested this with a simple node server - and have found that cy.visit() and any requests made by the browser have their custom status message stripped when running in cypress-ntlm open

It seems like cypress may have fixed this issue in 10.2 (https://github.com/cypress-io/cypress/issues/16973) but when using ntlm-auth it seems to not work. (Also, that bug fix appears to have not fixed cy.request() with custom statuses)

Example I put a button on a page, which made a request to the server that returned a custom status. When running in cypress open using npx cypress-ntlm open I get this response when I manually click on the button: image

When I click that same button but using cypress with npx cypress open I get this response: image

bjowes commented 6 months ago

Hi @FelixWhitefield

There was actually a similar issue some time back (#175) and it was fixed in this plugin. There is still a unit test for it that passes. In that case, the custom status message was still lost before it came through cypress, and an issue was filed for it. It seems to have been resolved in 10.2 as you mentioned.

I extended my own e2e tests for this scenario and I can receive the custom status message successfully, even when going through the plugin. As you noted, cy.request seems to discard the custom status message, but for requests that are internal on the page it works. I use jQuery $.ajax() on the test page, for reference.

Tested with Cypress v13.6.3, Electron v114.

Please add some more details to your case. Is the request url, http://localhost:3000/api configured for NTLM authentication in the plugin, or is it just "passing through" the proxy?

FelixWhitefield commented 6 months ago

Hi @bjowes

Thank you for the response! Hopefully I can provide some more information.

For reference, I am using Cypress v13.7.1, Cypress-ntlm-auth v4.1.3 and Electron v118.

I initially saw the issue when setting the ReasonPhrase in an ASP.NET Framework API. Edit: Sometimes the issue didn't happen, but that seemed to only occur when the header www-authenticate was present on the response and then the custom status message would be returned. Otherwise, if that header wasn't present, the custom status message would not be returned and it would be the default HTTP status message instead.

http://losthost:3000/api is not configured for NTLM authentication, it is a very simple node server which I made to recreate the issue. It is made up of the following (slightly different from the server I used in the initial post):

// index.js
const http = require('http');
const url = require('url');
const fs = require('fs');
const path = require('path');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
    const { pathname } = url.parse(req.url);

    if (pathname === '/api') {
        res.statusCode = 200;
        res.statusMessage = 'Custom Message';
        res.end('API Body');
    } else {
        // serve index.html
        fs.readFile(path.join(__dirname, '/index.html'), (err, data) => {
            if (err) {
                res.statusCode = 404;
                res.statusMessage = 'Not Found';
                res.end('Not Found');
            } else {
                res.statusCode = 200;
                res.setHeader('Content-Type', 'text/html');
                res.end(data);
            }
        });
    }
})

server.listen(port, hostname, () => {
    console.log(`Server running at http://${hostname}:${port}`)
});
<!-- index.html -->
<button onclick="sendReq()">Click Me!</button>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script>
    function sendReq() {
       // Initially I used fetch('http://localhost:3000/api') and the same issue occured
        $.ajax({
            url: 'http://localhost:3000/api',
            type: 'GET',
        });
    }
</script>

And the spec file which fails when running cypress through npx cypress-ntml open is the following:

it('custom status message test', () => {
    // test fails when running in cypress-ntlm open with and without these lines
    //cy.ntlmReset();
    //cy.ntlmSso(['localhost'])

    cy.intercept('**/api').as('apiCall');
    cy.visit('http://localhost:3000').get('button').click();

    cy.wait('@apiCall').its('response.statusMessage').should('eq', 'Custom Message');
})

When running with npx cypress-ntlm open this is the result of the test - with further manual clicks of the button also returning the status message 'OK'. image

When running with npx cypress open this is the result of the test - with further manual clicks of the button also returning the status message 'Custom Message'. image

I have tried this running on two different computers (my personal and work device) and the behaviour seems consistent across them.

Hopefully that helps, if you need any more information I'll be happy to provide it.

bjowes commented 6 months ago

Finding so far

FelixWhitefield commented 6 months ago

I've done some more testing on the main application I'm testing

Currently I am working around it by checking that the error is displayed instead of checking the text content is correct.

bjowes commented 6 months ago

Thanks, the fact that timing was a factor was an important clue. The custom message is properly passed when a NTLM handshake is completed, but if an authenticated connection is already established, that part of the code was not used. In fact, a large part of the proxy implementation is from another library (node-http-mitm-proxy), and apparently that library does not support the custom message. I already have my own fork of the library so I can patch it, but it will take some time to process everything. Something you could try out - would be great to know if this covers your scenarios before implementing:

in your project, go to node_modules/@bjowes/httm-mitm-proxy/lib/proxy.js, find this line

ctx.proxyToClientResponse.writeHead(ctx.serverToProxyResponse.statusCode, Proxy.filterAndCanonizeHeaders(ctx.serverToProxyResponse.headers));```
change it to
``` js
ctx.proxyToClientResponse.writeHead(ctx.serverToProxyResponse.statusCode, ctx.serverToProxyResponse.statusMessage, Proxy.filterAndCanonizeHeaders(ctx.serverToProxyResponse.headers));
FelixWhitefield commented 6 months ago

Changing that line fixes it! I've rerun the tests a few times with that change and they've all been showing the correct custom status message. When I change that line back it starts misbehaving again so it definitely appears to be that line.

Thank you for your help on this

bjowes commented 6 months ago

I just released v4.1.4 where this patch is included

FelixWhitefield commented 6 months ago

Hi that seems to have worked, however I did have some problems with it installing the older version of '@bjowes/http-mitm-proxy' (0.9.4). Does the package.json for this package need updating to make sure it grabs the newer version of '@bjowes/http-mitm-proxy'?

bjowes commented 6 months ago

You are quite right. For some reason the update did not go into the package.json. Thanks for reporting, will re-release it later.

bjowes commented 6 months ago

Released 4.1.5 to fix this.