diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml new file mode 100644 index 0000000..43f3b5a --- /dev/null +++ b/.github/workflows/cypress.yml @@ -0,0 +1,23 @@ +name: Cypress Tests +on: [push] +jobs: + cypress-run: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + containers: [1, 2] + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Start services with Docker Compose + run: docker-compose up --build -d + - name: Cypress run + uses: cypress-io/github-action@v6 + with: + wait-on: 'http://localhost:3000' + record: true + parallel: true + env: + CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0e132c3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +cypress/reports +cypress/screenshots \ No newline at end of file diff --git a/Dockerfile.app b/Dockerfile.app new file mode 100644 index 0000000..81152f0 --- /dev/null +++ b/Dockerfile.app @@ -0,0 +1,13 @@ +FROM node:latest + +WORKDIR /usr/src/app + +COPY package*.json ./ + +RUN npm install + +COPY . . + +EXPOSE 3000 + +CMD ["node", "server.js"] \ No newline at end of file diff --git a/Dockerfile.db b/Dockerfile.db new file mode 100644 index 0000000..1257047 --- /dev/null +++ b/Dockerfile.db @@ -0,0 +1,8 @@ +FROM mysql:latest + +ENV MYSQL_DATABASE blog_cypress +ENV MYSQL_ROOT_PASSWORD rootpassword + +COPY ./init.sql /docker-entrypoint-initdb.d/ + +EXPOSE 3306 \ No newline at end of file diff --git a/Dockerfile.jenkins b/Dockerfile.jenkins new file mode 100644 index 0000000..fe24f3b --- /dev/null +++ b/Dockerfile.jenkins @@ -0,0 +1,30 @@ +FROM jenkins/jenkins:lts + +USER root + +RUN apt-get update && apt-get install -y \ + sudo \ + libgtk2.0-0 \ + libgtk-3-0 \ + libgbm-dev \ + libnotify-dev \ + libnss3 \ + libxss1 \ + libasound2 \ + libxtst6 \ + xauth \ + xvfb \ + && rm -rf /var/lib/apt/lists/* + +RUN curl -fsSL https://get.docker.com -o get-docker.sh \ + && sh get-docker.sh \ + && rm get-docker.sh + +ARG DOCKER_GID=1001 + +RUN if getent group docker ; then groupmod -g ${DOCKER_GID} docker; else groupadd -g ${DOCKER_GID} docker; fi \ + && usermod -aG docker jenkins + +RUN echo "jenkins ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + +USER jenkins diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..b59309f --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,25 @@ +pipeline { + agent { + docker { + image 'cypress/base:20.9.0' + } + } + + stages + { + stage('Clone repository') { + steps { + script { + sh 'git clone https://github.com/fannyv/vulnerabilities_cypress.git' + sh 'cd vulnerabilities_cypress && git checkout master' + } + } + } + stage('build and test') { + steps { + sh 'npm install' + sh 'npm run test:report' + } + } + } +} \ No newline at end of file diff --git a/README.md b/README.md index 06eee54..71ec070 100644 --- a/README.md +++ b/README.md @@ -1 +1,7 @@ -# Livre-Testez-votre-application-web-avec-Cypress \ No newline at end of file +# Livre-Testez-votre-application-web-avec-Cypress + +Pour lancer le projet, exécutez `docker-compose up` cette commande démarre les services définis dans le fichier `docker-compose.yml`. Si les conteneurs n'existent pas, ils seront crées. Si les conteneurs existent déjà, ils seront démarrés. + +Vous pouvez y ajouter `--build`, cette option force la reconstruction des images pour les services avant de les démarrer. Cela garantit que toutes les modifications récentes apportées aux Dockerfiles seront prises en compte. + +Si vous avez besoin de debugguer, vous pouvez utiliser : docker-compose up --build > build_output.log 2>&1 qui vous donnera la sortie dans le fichier build_output.log et vous pourrez ainsi voir les erreurs \ No newline at end of file diff --git a/backend/authentification.js b/backend/authentification.js new file mode 100644 index 0000000..4741939 --- /dev/null +++ b/backend/authentification.js @@ -0,0 +1,48 @@ +var users = [ + { username: "user1", password: "pass1" }, + { username: "user2", password: "pass2" } +]; + +var expectedTOTP = "123456"; + +function authenticate(username, password) { + const user = users.find(u => u.username === username && u.password === password); + + if (user) { + document.getElementById('loginForm').style.display = 'none'; + document.getElementById('totpForm').style.display = 'block'; + + fetch('/mfa/send-code', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ email: user.email }), + }) + .then(response => { + if (!response.ok) { + throw new Error('Erreur lors de l\'envoi du code MFA'); + } + console.log('Code MFA envoyé avec succès'); + }) + .catch(error => { + console.error('Erreur:', error); + }); + } else { + var x = document.getElementById("snackbar"); + x.className = "show"; + setTimeout(function () { x.className = x.className.replace("show", ""); }, 3000); + } +} + + +function validateTOTP(mfaCode) { + if (mfaCode === expectedTOTP) { + window.location.href = './index.html'; + } else { + var x = document.getElementById("snackbar-mfa"); + document.getElementById("snackbar-mfa").style.display = 'block' + x.className = "show"; + setTimeout(function () { x.className = x.className.replace("show", ""); }, 3000); + } +} diff --git a/cypress.config.js b/cypress.config.js new file mode 100644 index 0000000..a134926 --- /dev/null +++ b/cypress.config.js @@ -0,0 +1,48 @@ +const { defineConfig } = require("cypress"); +const ms = require('smtp-tester') +const { lighthouse, prepareAudit } = require('cypress-audit'); + +module.exports = defineConfig({ + projectId: "", + env: { + login_url: "/login", + api_url: "http://localhost:3000", + api_key: "secret_api_key" + }, + e2e: { + experimentalStudio: true, + specPattern: 'cypress/e2e/**/*.*', + proxyServer: 'http://localhost:8090', + reporter: 'mochawesome', + reporterOptions: { + reportDir: 'cypress/reports', + overwrite: false, + html: true, + json: true + }, + setupNodeEvents(on, config) { + const port = 7777; + const mailServer = ms.init(port); + let emails = []; + + mailServer.bind((addr, id, email) => { + console.log('--- email ---'); + console.log(addr, id, email); + emails.push(email.html || email.body); + }); + + on('before:browser:launch', (browser = {}, launchOptions) => { + prepareAudit(launchOptions); + }); + + on('task', { + lighthouse: lighthouse(), + getEmails() { + const capturedEmails = [...emails]; + emails = []; + return capturedEmails; + }, + }); + }, + }, +}); diff --git a/cypress/e2e/1-getting-started/todo.cy.js b/cypress/e2e/1-getting-started/todo.cy.js new file mode 100644 index 0000000..4768ff9 --- /dev/null +++ b/cypress/e2e/1-getting-started/todo.cy.js @@ -0,0 +1,143 @@ +/// + +// Welcome to Cypress! +// +// This spec file contains a variety of sample tests +// for a todo list app that are designed to demonstrate +// the power of writing tests in Cypress. +// +// To learn more about how Cypress works and +// what makes it such an awesome testing tool, +// please read our getting started guide: +// https://on.cypress.io/introduction-to-cypress + +describe('example to-do app', () => { + beforeEach(() => { + // Cypress starts out with a blank slate for each test + // so we must tell it to visit our website with the `cy.visit()` command. + // Since we want to visit the same URL at the start of all our tests, + // we include it in our beforeEach function so that it runs before each test + cy.visit('https://example.cypress.io/todo') + }) + + it('displays two todo items by default', () => { + // We use the `cy.get()` command to get all elements that match the selector. + // Then, we use `should` to assert that there are two matched items, + // which are the two default items. + cy.get('.todo-list li').should('have.length', 2) + + // We can go even further and check that the default todos each contain + // the correct text. We use the `first` and `last` functions + // to get just the first and last matched elements individually, + // and then perform an assertion with `should`. + cy.get('.todo-list li').first().should('have.text', 'Pay electric bill') + cy.get('.todo-list li').last().should('have.text', 'Walk the dog') + }) + + it('can add new todo items', () => { + // We'll store our item text in a variable so we can reuse it + const newItem = 'Feed the cat' + + // Let's get the input element and use the `type` command to + // input our new list item. After typing the content of our item, + // we need to type the enter key as well in order to submit the input. + // This input has a data-test attribute so we'll use that to select the + // element in accordance with best practices: + // https://on.cypress.io/selecting-elements + cy.get('[data-test=new-todo]').type(`${newItem}{enter}`) + + // Now that we've typed our new item, let's check that it actually was added to the list. + // Since it's the newest item, it should exist as the last element in the list. + // In addition, with the two default items, we should have a total of 3 elements in the list. + // Since assertions yield the element that was asserted on, + // we can chain both of these assertions together into a single statement. + cy.get('.todo-list li') + .should('have.length', 3) + .last() + .should('have.text', newItem) + }) + + it('can check off an item as completed', () => { + // In addition to using the `get` command to get an element by selector, + // we can also use the `contains` command to get an element by its contents. + // However, this will yield the