diff --git a/.chachalog/BBLVnBTa.md b/.chachalog/BBLVnBTa.md new file mode 100644 index 0000000..53d1849 --- /dev/null +++ b/.chachalog/BBLVnBTa.md @@ -0,0 +1,6 @@ +--- +# Allowed version bumps: patch, minor, major +"@jahia/cypress": patch +--- + +Fixed the spec/test log markers so they can no longer promote the visitor's own session to root and leak an authenticated view into the command that runs right after. (#253) diff --git a/src/support/jahiaLog.ts b/src/support/jahiaLog.ts index 6bee0fc..58b3864 100644 --- a/src/support/jahiaLog.ts +++ b/src/support/jahiaLog.ts @@ -19,6 +19,40 @@ const specMarker = (action: string, name: string): string => `${delimeters.spec} */ const testMarker = (action: string, title: string): string => `${delimeters.test} ${action} ${title} ${delimeters.test}`; +/** + * Runs `fn` on a disposable cookie jar, then restores the jar `fn` actually started with. + * + * `executeGroovy` authenticates with root's HTTP Basic credentials, but it's still a + * `cy.request()` — Cypress attaches it to the browser's current cookies like any other request to + * the app's origin. A servlet container commonly binds Basic-Auth to whatever session a request + * already carries rather than opening an isolated one, so if the visitor's own session cookie + * rides along, that session's identity gets mutated server-side to root. Reinstating the same + * cookie *value* afterwards does not undo that — the server still recognizes that exact session id + * as root's. Clearing the jar before `fn()` denies it any existing session to mutate, so it always + * gets a fresh one of its own; clearing again after discards that one instead of leaving it behind + * for the next command; restoring the original snapshot then returns exactly the session (or lack + * of one) that was in place beforehand, including one a suite legitimately established earlier + * (e.g. via `loginAndStoreSession`'s `cy.session()`). + * @param {() => void} fn the action to run in isolation from the surrounding cookie jar + */ +const withPreservedCookies = (fn: () => void): void => { + cy.getCookies({log: false}).then(cookies => { + cy.clearCookies({log: false}); + fn(); + cy.clearCookies({log: false}); + cookies.forEach(cookie => { + cy.setCookie(cookie.name, cookie.value, { + path: cookie.path, + domain: cookie.domain, + secure: cookie.secure, + httpOnly: cookie.httpOnly, + sameSite: cookie.sameSite, + log: false + }); + }); + }); +}; + /** * Enables logging markers for the start and end of test suites and individual tests. * This function sets up hooks to log messages before and after each test and suite execution. @@ -26,19 +60,19 @@ const testMarker = (action: string, title: string): string => `${delimeters.test */ const enableSpecsMarker = (): void => { before(() => { - cy.executeGroovy(loggerScript, {MESSAGE: specMarker('[BEGIN SPEC]', Cypress.spec.name)}); + withPreservedCookies(() => cy.executeGroovy(loggerScript, {MESSAGE: specMarker('[BEGIN SPEC]', Cypress.spec.name)})); }); beforeEach(function () { - cy.executeGroovy(loggerScript, {MESSAGE: testMarker('[BEGIN TEST]', this.currentTest!.title)}); + withPreservedCookies(() => cy.executeGroovy(loggerScript, {MESSAGE: testMarker('[BEGIN TEST]', this.currentTest!.title)})); }); afterEach(function () { - cy.executeGroovy(loggerScript, {MESSAGE: testMarker('[END TEST]', this.currentTest!.title)}); + withPreservedCookies(() => cy.executeGroovy(loggerScript, {MESSAGE: testMarker('[END TEST]', this.currentTest!.title)})); }); after(() => { - cy.executeGroovy(loggerScript, {MESSAGE: specMarker('[END SPEC]', Cypress.spec.name)}); + withPreservedCookies(() => cy.executeGroovy(loggerScript, {MESSAGE: specMarker('[END SPEC]', Cypress.spec.name)})); }); };