From 68b150b8d34d46873d6fb991862367c90acbcb40 Mon Sep 17 00:00:00 2001 From: sagar608 Date: Thu, 11 Jun 2020 11:54:45 +0545 Subject: [PATCH] Added 404 page feature --- app/data/allocations-dao.js | 4 +- app/data/benefits-dao.js | 4 +- app/data/contributions-dao.js | 6 +- app/data/memos-dao.js | 4 +- app/data/profile-dao.js | 4 +- app/data/research-dao.js | 4 +- app/data/user-dao.js | 6 +- app/routes/allocations.js | 15 +- app/routes/benefits.js | 11 +- app/routes/contributions.js | 10 +- app/routes/error.js | 6 +- app/routes/index.js | 11 +- app/routes/memos.js | 6 +- app/routes/profile.js | 20 ++- app/routes/research.js | 20 +-- app/routes/session.js | 22 ++- app/views/404.html | 61 ++++++++ app/views/allocations.html | 2 +- app/views/layout.html | 2 +- app/views/login.html | 10 +- app/views/research.html | 4 +- app/views/tutorial/a1.html | 70 +++++----- app/views/tutorial/a2.html | 32 ++--- app/views/tutorial/a3.html | 4 +- app/views/tutorial/a4.html | 2 +- app/views/tutorial/a5.html | 28 ++-- app/views/tutorial/a7.html | 2 +- app/views/tutorial/a8.html | 2 +- app/views/tutorial/a9.html | 2 +- app/views/tutorial/ssrf.html | 2 +- config/config.js | 5 +- server.js | 6 +- test/e2e/integration/allocations_spec.js | 90 ++++++------ test/e2e/integration/benefits_spec.js | 104 +++++++------- test/e2e/integration/contributions_spec.js | 106 +++++++------- test/e2e/integration/dashboard_spec.js | 56 ++++---- test/e2e/integration/general_spec.js | 114 +++++++-------- test/e2e/integration/learn_spec.js | 24 ++-- test/e2e/integration/login_spec.js | 154 ++++++++++----------- test/e2e/integration/logout_spec.js | 48 +++---- test/e2e/integration/memos_spec.js | 94 ++++++------- test/e2e/integration/profile_spec.js | 112 +++++++-------- test/e2e/integration/research_spec.js | 80 +++++------ test/e2e/integration/signup_spec.js | 144 +++++++++---------- test/e2e/integration/tutorial_spec.js | 112 +++++++-------- test/e2e/plugins/index.js | 4 +- test/e2e/support/commands.js | 39 +++--- test/e2e/support/index.js | 1 - 48 files changed, 902 insertions(+), 767 deletions(-) create mode 100644 app/views/404.html diff --git a/app/data/allocations-dao.js b/app/data/allocations-dao.js index 26ecc387e..b89eefe87 100644 --- a/app/data/allocations-dao.js +++ b/app/data/allocations-dao.js @@ -1,7 +1,7 @@ const UserDAO = require("./user-dao").UserDAO; /* The AllocationsDAO must be constructed with a connected database object */ -const AllocationsDAO = function(db){ +const AllocationsDAO = function(db) { "use strict"; @@ -90,7 +90,7 @@ const AllocationsDAO = function(db){ let doneCounter = 0; const userAllocations = []; - allocations.forEach( alloc => { + allocations.forEach(alloc => { userDAO.getUserById(alloc.userId, (err, user) => { if (err) return callback(err, null); diff --git a/app/data/benefits-dao.js b/app/data/benefits-dao.js index 5e773e442..6eada694f 100644 --- a/app/data/benefits-dao.js +++ b/app/data/benefits-dao.js @@ -40,4 +40,6 @@ function BenefitsDAO(db) { }; } -module.exports = { BenefitsDAO }; +module.exports = { + BenefitsDAO +}; diff --git a/app/data/contributions-dao.js b/app/data/contributions-dao.js index 00041dac6..5863b6cf1 100644 --- a/app/data/contributions-dao.js +++ b/app/data/contributions-dao.js @@ -26,7 +26,7 @@ function ContributionsDAO(db) { }; contributionsDB.update({ - userId + userId }, contributions, { upsert: true @@ -83,4 +83,6 @@ function ContributionsDAO(db) { }; } -module.exports = { ContributionsDAO }; +module.exports = { + ContributionsDAO +}; diff --git a/app/data/memos-dao.js b/app/data/memos-dao.js index 434c935c2..c28a4c6c7 100644 --- a/app/data/memos-dao.js +++ b/app/data/memos-dao.js @@ -36,4 +36,6 @@ function MemosDAO(db) { } -module.exports = { MemosDAO }; +module.exports = { + MemosDAO +}; diff --git a/app/data/profile-dao.js b/app/data/profile-dao.js index 552e5df38..3d98bb276 100644 --- a/app/data/profile-dao.js +++ b/app/data/profile-dao.js @@ -110,4 +110,6 @@ function ProfileDAO(db) { }; } -module.exports = { ProfileDAO }; +module.exports = { + ProfileDAO +}; diff --git a/app/data/research-dao.js b/app/data/research-dao.js index 96e211fc4..1486a860c 100644 --- a/app/data/research-dao.js +++ b/app/data/research-dao.js @@ -24,4 +24,6 @@ function ResearchDAO(db) { } } -module.exports = { ResearchDAO }; +module.exports = { + ResearchDAO +}; diff --git a/app/data/user-dao.js b/app/data/user-dao.js index e88bdafb3..a39eca370 100644 --- a/app/data/user-dao.js +++ b/app/data/user-dao.js @@ -116,8 +116,10 @@ function UserDAO(db) { }, { new: true }, - (err, data) => err ? callback(err, null) : callback(null, data.value.seq)); + (err, data) => err ? callback(err, null) : callback(null, data.value.seq)); }; } -module.exports = { UserDAO }; +module.exports = { + UserDAO +}; diff --git a/app/routes/allocations.js b/app/routes/allocations.js index b45f1ab13..1f484dd67 100644 --- a/app/routes/allocations.js +++ b/app/routes/allocations.js @@ -1,6 +1,6 @@ const AllocationsDAO = require("../data/allocations-dao").AllocationsDAO; -function AllocationsHandler (db) { +function AllocationsHandler(db) { "use strict"; const allocationsDAO = new AllocationsDAO(db); @@ -10,12 +10,19 @@ function AllocationsHandler (db) { // Fix for A4 Insecure DOR - take user id from session instead of from URL param const { userId } = req.session; */ - const {userId} = req.params; - const { threshold } = req.query + const { + userId + } = req.params; + const { + threshold + } = req.query allocationsDAO.getByUserIdAndThreshold(userId, threshold, (err, allocations) => { if (err) return next(err); - return res.render("allocations", { userId, allocations }); + return res.render("allocations", { + userId, + allocations + }); }); }; } diff --git a/app/routes/benefits.js b/app/routes/benefits.js index 1e8189501..b6a541541 100644 --- a/app/routes/benefits.js +++ b/app/routes/benefits.js @@ -1,6 +1,8 @@ -const { BenefitsDAO } = require("../data/benefits-dao"); +const { + BenefitsDAO +} = require("../data/benefits-dao"); -function BenefitsHandler (db) { +function BenefitsHandler(db) { "use strict"; const benefitsDAO = new BenefitsDAO(db); @@ -21,7 +23,10 @@ function BenefitsHandler (db) { }; this.updateBenefits = (req, res, next) => { - const { userId, benefitStartDate } = req.body; + const { + userId, + benefitStartDate + } = req.body; benefitsDAO.updateBenefits(userId, benefitStartDate, (error) => { diff --git a/app/routes/contributions.js b/app/routes/contributions.js index 14327c9ed..3dbcffac9 100644 --- a/app/routes/contributions.js +++ b/app/routes/contributions.js @@ -1,13 +1,15 @@ const ContributionsDAO = require("../data/contributions-dao").ContributionsDAO; /* The ContributionsHandler must be constructed with a connected db */ -function ContributionsHandler (db) { +function ContributionsHandler(db) { "use strict"; const contributionsDAO = new ContributionsDAO(db); this.displayContributions = (req, res, next) => { - const { userId } = req.session; + const { + userId + } = req.session; contributionsDAO.getByUserId(userId, (error, contrib) => { if (error) return next(error); @@ -31,7 +33,9 @@ function ContributionsHandler (db) { const afterTax = parseInt(req.body.afterTax); const roth = parseInt(req.body.roth); */ - const { userId } = req.session; + const { + userId + } = req.session; //validate contributions const validations = [isNaN(preTax), isNaN(afterTax), isNaN(roth), preTax < 0, afterTax < 0, roth < 0] diff --git a/app/routes/error.js b/app/routes/error.js index 0df5fd867..1d56e7c9e 100644 --- a/app/routes/error.js +++ b/app/routes/error.js @@ -1,6 +1,6 @@ // Error handling middleware -const errorHandler = (err, req, res,next) => { +const errorHandler = (err, req, res, next) => { "use strict"; @@ -12,4 +12,6 @@ const errorHandler = (err, req, res,next) => { }); }; -module.exports = { errorHandler }; +module.exports = { + errorHandler +}; diff --git a/app/routes/index.js b/app/routes/index.js index 62ca639a8..966b6884d 100644 --- a/app/routes/index.js +++ b/app/routes/index.js @@ -76,15 +76,22 @@ const index = (app, db) => { app.get("/tutorial", (req, res) => { return res.render("tutorial/a1"); }); - + app.get("/tutorial/:page", (req, res) => { - const { page } = req.params + const { + page + } = req.params return res.render(`tutorial/${page}`); }); // Research Page app.get("/research", isLoggedIn, researchHandler.displayResearch); + //404 Page + app.get("*", (req, res) => { + return res.render("404"); + }); + // Error handling middleware app.use(ErrorHandler); }; diff --git a/app/routes/memos.js b/app/routes/memos.js index c70af0beb..72e0903cd 100644 --- a/app/routes/memos.js +++ b/app/routes/memos.js @@ -1,6 +1,6 @@ const MemosDAO = require("../data/memos-dao").MemosDAO; -function MemosHandler (db) { +function MemosHandler(db) { "use strict"; const memosDAO = new MemosDAO(db); @@ -15,7 +15,9 @@ function MemosHandler (db) { this.displayMemos = (req, res, next) => { - const { userId } = req.session; + const { + userId + } = req.session; memosDAO.getAllMemos((err, docs) => { if (err) return next(err); diff --git a/app/routes/profile.js b/app/routes/profile.js index 4282d55cb..16150a9f1 100644 --- a/app/routes/profile.js +++ b/app/routes/profile.js @@ -2,13 +2,15 @@ const ProfileDAO = require("../data/profile-dao").ProfileDAO; const ESAPI = require('node-esapi') /* The ProfileHandler must be constructed with a connected db */ -function ProfileHandler (db) { +function ProfileHandler(db) { "use strict"; const profile = new ProfileDAO(db); this.displayProfile = (req, res, next) => { - const { userId } = req.session; + const { + userId + } = req.session; @@ -31,7 +33,15 @@ function ProfileHandler (db) { this.handleProfileUpdate = (req, res, next) => { - const {firstName, lastName, ssn, dob, address, bankAcc, bankRouting} = req.body; + const { + firstName, + lastName, + ssn, + dob, + address, + bankAcc, + bankRouting + } = req.body; // Fix for Section: ReDoS attack // The following regexPattern that is used to validate the bankRouting number is insecure and vulnerable to @@ -58,7 +68,9 @@ function ProfileHandler (db) { }); } - const { userId } = req.session; + const { + userId + } = req.session; profile.updateUser( parseInt(userId), diff --git a/app/routes/research.js b/app/routes/research.js index 6923256cd..4c5f4cdcb 100644 --- a/app/routes/research.js +++ b/app/routes/research.js @@ -1,25 +1,27 @@ const ResearchDAO = require("../data/research-dao").ResearchDAO; const needle = require('needle'); -function ResearchHandler (db) { +function ResearchHandler(db) { "use strict"; const researchDAO = new ResearchDAO(db); this.displayResearch = (req, res) => { - + if (req.query.symbol) { - const url = req.query.url+req.query.symbol; + const url = req.query.url + req.query.symbol; return needle.get(url, (error, newResponse) => { if (!error && newResponse.statusCode == 200) - res.writeHead(200, {'Content-Type': 'text/html'}); - res.write('

The following is the stock information you requested.

\n\n'); - res.write('\n\n'); - res.write(newResponse.body); - return res.end(); + res.writeHead(200, { + 'Content-Type': 'text/html' + }); + res.write('

The following is the stock information you requested.

\n\n'); + res.write('\n\n'); + res.write(newResponse.body); + return res.end(); }); } - + return res.render("research"); }; diff --git a/app/routes/session.js b/app/routes/session.js index 64a4e2a61..f8a2c52bb 100644 --- a/app/routes/session.js +++ b/app/routes/session.js @@ -2,7 +2,7 @@ const UserDAO = require("../data/user-dao").UserDAO; const AllocationsDAO = require("../data/allocations-dao").AllocationsDAO; /* The SessionHandler must be constructed with a connected db */ -function SessionHandler (db) { +function SessionHandler(db) { "use strict"; const userDAO = new UserDAO(db); @@ -22,16 +22,16 @@ function SessionHandler (db) { this.isAdminUserMiddleware = (req, res, next) => { if (req.session.userId) { return userDAO.getUserById(req.session.userId, (err, user) => user && user.isAdmin ? next() : res.redirect("/login")); - } + } console.log("redirecting to login"); return res.redirect("/login"); - + }; this.isLoggedInMiddleware = (req, res, next) => { if (req.session.userId) { return next(); - } + } console.log("redirecting to login"); return res.redirect("/login"); }; @@ -45,7 +45,10 @@ function SessionHandler (db) { }; this.handleLoginRequest = (req, res, next) => { - const { userName, password } = req.body + const { + userName, + password + } = req.body userDAO.validateLogin(userName, password, (err, user) => { const errorMessage = "Invalid username and/or password"; const invalidUserNameErrorMessage = "Invalid username"; @@ -173,7 +176,14 @@ function SessionHandler (db) { this.handleSignup = (req, res, next) => { - const { email, userName, firstName, lastName, password, verify } = req.body; + const { + email, + userName, + firstName, + lastName, + password, + verify + } = req.body; // set these up in case we have an error case const errors = { diff --git a/app/views/404.html b/app/views/404.html new file mode 100644 index 000000000..de22e5778 --- /dev/null +++ b/app/views/404.html @@ -0,0 +1,61 @@ + + + + + + + + + 404 error + + + + +
+
+

Opps! Page not found.

+

404

+

We can't find the page you're looking for.

+ Home +
+
+ + + \ No newline at end of file diff --git a/app/views/allocations.html b/app/views/allocations.html index 3b28d546f..0d85be781 100644 --- a/app/views/allocations.html +++ b/app/views/allocations.html @@ -50,4 +50,4 @@

-{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/app/views/layout.html b/app/views/layout.html index 6206deb6c..c734b5221 100644 --- a/app/views/layout.html +++ b/app/views/layout.html @@ -128,4 +128,4 @@ - + \ No newline at end of file diff --git a/app/views/login.html b/app/views/login.html index b96266f1d..643af9439 100644 --- a/app/views/login.html +++ b/app/views/login.html @@ -87,10 +87,10 @@ RetireEasy -
+
Employee Retirement Savings Management -
-
+
+
@@ -158,9 +158,9 @@ document.cookie = "testcookie=1"; if (!document.cookie) return false; - + document.cookie = "testcookie=; expires=" + new Date(0).toUTCString(); - + } return true; diff --git a/app/views/research.html b/app/views/research.html index ddfc9a3eb..66e457e3a 100644 --- a/app/views/research.html +++ b/app/views/research.html @@ -14,7 +14,7 @@

- +

Enter Stock Symbol.

@@ -27,4 +27,4 @@

-{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/app/views/tutorial/a1.html b/app/views/tutorial/a1.html index 2587ce97e..5e40c2b64 100644 --- a/app/views/tutorial/a1.html +++ b/app/views/tutorial/a1.html @@ -93,7 +93,7 @@

Attack Mechanics

File System Access -
+

@@ -221,7 +221,7 @@

1. SQL Injection

If this statement is not prepared or properly handled when constructed, an attacker may be able to supply admin' --in the username field to access the admin user's account bypassing the condition that checks for the password. The resultant SQL query would looks like:

SELECT * FROM accounts WHERE username = 'admin' -- AND password = ''
-
+
2. NoSQL Injection

The equivalent of above query for NoSQL MongoDB database is:

db.accounts.find({username: username, password: password});
@@ -270,7 +270,7 @@
$where operator

-
+
NoSQL SSJS Injection

An attacker can send the following input for the @@ -347,38 +347,38 @@

1. Log Forging (CRLF)
console.log('Error: attempt to login with invalid user: ', userName);

When user input is unsanitized and the output mechanism is an ordinary terminal stdout facility then the application will be vulnerable to CRLF injection, where an attacker can create a malicious payload as follows: -

+                            
 curl http://localhost:4000/login -X POST --data 'userName=vyva%0aError: alex moldovan failed $1,000,000 transaction&password=Admin_123&_csrf='
                         
- Where the userName parameter is encoding in the request the LF symbol which will result in a new line to begin. Resulting log output will look as follows: -
+                            Where the userName parameter is encoding in the request the LF symbol which will result in a new line to begin. Resulting log output will look as follows:
+                            
 Error: attempt to login with invalid user:  vyva
 Error: alex moldovan failed $1,000,000 transaction
                         
-
-
2. Log Injection Escalation
-

- An attacker may craft malicious input in hope of an escalated attack where the target isn't the logs themselves, but rather the actual logging system. For example, if an application has a back-office web app that manages viewing and tracking the logs, then an attacker may send an XSS payload into the log, which may not result in log forging on the log itself, but when viewed by a system administrator on the log viewing web app then it may compromise it and result in XSS injection that if the logs app is vulnerable. -

+
+
2. Log Injection Escalation
+

+ An attacker may craft malicious input in hope of an escalated attack where the target isn't the logs themselves, but rather the actual logging system. For example, if an application has a back-office web app that manages viewing and tracking the logs, then an attacker may send an XSS payload into the log, which may not result in log forging on the log itself, but when viewed by a system administrator on the log viewing web app then it may compromise it and result in XSS injection that if the logs app is vulnerable. +

-
-
-

How Do I Prevent It?

-
-
+
+
+

How Do I Prevent It?

+
+
- As always when dealing with user input: -
    -
  • - Do not allow user input into logs -
  • -
  • - Encode to proper context, or sanitize user input -
  • -
+ As always when dealing with user input: +
    +
  • + Do not allow user input into logs +
  • +
  • + Encode to proper context, or sanitize user input +
  • +
- Encoding example: -
+                                    Encoding example:
+                                    
 // Step 1: Require a module that supports encoding
 var ESAPI = require('node-esapi');
 // - Step 2: Encode the user input that will be logged in the correct context
@@ -388,15 +388,15 @@ 

How Do I Prevent It?

console.log('Error: attempt to login with invalid user: %s', ESAPI.encoder().encodeForURL(userName));
- For the above Log Injection vulnerability, example and fix can be found at - routes/session.js + For the above Log Injection vulnerability, example and fix can be found at + routes/session.js +
+
- - - + - - -{% endblock %} + + + {% endblock %} \ No newline at end of file diff --git a/app/views/tutorial/a2.html b/app/views/tutorial/a2.html index 9202d8649..9efaa3889 100644 --- a/app/views/tutorial/a2.html +++ b/app/views/tutorial/a2.html @@ -131,8 +131,8 @@

1. Protecting user credentials

callback(invalidPasswordError, null); }
Note: The bcrypt module also provides asynchronous methods for creating and comparing hash. -
-
+
+

2. Session timeout and protecting cookies in transit

The insecure demo application does not contain any provision to timeout user session. The session stays active until user explicitly logs out.

@@ -166,22 +166,22 @@

2. Session timeout and protecting cookies in transit

});
Note: The example code uses MemoryStoreto manage session data, which is not designed for production environment, as it will leak memory, and will not scale past a single process. Use database based storage MongoStore or RedisStore for production. Alternatively, sessions can be managed using popular passport module. -
-
-

3. Session hijacking

+
+
+

3. Session hijacking

-

The insecure demo application does not regenerate a new session id upon user's login, therefore rendering a vulnerability of session hijacking if an attacker is able to somehow steal the cookie with the session id and use it. +

The insecure demo application does not regenerate a new session id upon user's login, therefore rendering a vulnerability of session hijacking if an attacker is able to somehow steal the cookie with the session id and use it. -

Upon login, a security best practice with regards to cookies session management would be to regenerate the session id so that if an id was already created for a user on an insecure medium (i.e: non-HTTPS website or otherwise), or if an attacker was able to get their hands on the cookie id before the user logged-in, then the old session id will render useless as the logged-in user with new privileges holds a new session id now. -

+

Upon login, a security best practice with regards to cookies session management would be to regenerate the session id so that if an id was already created for a user on an insecure medium (i.e: non-HTTPS website or otherwise), or if an attacker was able to get their hands on the cookie id before the user logged-in, then the old session id will render useless as the logged-in user with new privileges holds a new session id now. +

-

To secure the application:

-

1. Re-generate a new session id upon login (and best practice is to keep regenerating them -upon requests or at least upon sensitive actions like a user's password reset. +

To secure the application:

+

1. Re-generate a new session id upon login (and best practice is to keep regenerating them + upon requests or at least upon sensitive actions like a user's password reset. - Re-generate a session id as follows: - By wrapping the below code as a function callback for the method req.session.regenerate() -

+                                    Re-generate a session id as follows:
+                                    By wrapping the below code as a function callback for the method req.session.regenerate()
+                                    
 req.session.regenerate(function() {
 
   req.session.userId = user._id;
@@ -194,7 +194,7 @@ 

3. Session hijacking

})
-

+

@@ -307,4 +307,4 @@

Source Code Example

-{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/app/views/tutorial/a3.html b/app/views/tutorial/a3.html index b1dd5a04b..03900bba6 100644 --- a/app/views/tutorial/a3.html +++ b/app/views/tutorial/a3.html @@ -85,7 +85,7 @@

How Do I Prevent It?

UNTRUSTED DATA"> Except for alphanumeric characters, escape all characters with the HTML Entity &#xHH; format, including spaces. (HH = Hex Value) -
+
@@ -94,7 +94,7 @@

How Do I Prevent It?

UNTRUSTED DATA">clickme</a> Except for alphanumeric characters, escape all characters with ASCII values less than 256 with the HTML Entity &#xHH; format, including spaces. (HH = Hex Value) -
+
diff --git a/app/views/tutorial/a4.html b/app/views/tutorial/a4.html index 81292aeba..13b1ac4f6 100644 --- a/app/views/tutorial/a4.html +++ b/app/views/tutorial/a4.html @@ -78,4 +78,4 @@

Source Code Example

-{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/app/views/tutorial/a5.html b/app/views/tutorial/a5.html index 1bf72b5a2..a7f29f604 100644 --- a/app/views/tutorial/a5.html +++ b/app/views/tutorial/a5.html @@ -62,18 +62,18 @@

How Do I Prevent It?

Limit HTTP Request Body size by setting sensible size limits on each content type specific middleware ( urlencoded, json, multipart) instead of using aggregate limitmiddleware. Include only required middleware. For example if application doesn't need to support file uploads, do not include multipart middleware. -
  • - If using multipart middleware, have a strategy to clean up temporary files generated by it. These files are not garbage collected by default, and an attacker can fill disk with such temporary files -
  • -
  • - Vet npm packages used by the application -
  • -
  • - Lock versions of all npm packages used, for example using shrinkwarp, to have full control over when to install a new version of the package. -
  • -
  • - Set security specific HTTP headers -
  • +
  • + If using multipart middleware, have a strategy to clean up temporary files generated by it. These files are not garbage collected by default, and an attacker can fill disk with such temporary files +
  • +
  • + Vet npm packages used by the application +
  • +
  • + Lock versions of all npm packages used, for example using shrinkwarp, to have full control over when to install a new version of the package. +
  • +
  • + Set security specific HTTP headers +
  • @@ -87,7 +87,7 @@

    Source Code Example

    The default HTTP header x-powered-by can reveal implementation details to an attacker. It can be taken out by including this code in server.js -

       
    +                    
             app.disable("x-powered-by"); 
         

    @@ -129,4 +129,4 @@

    Source Code Example

    -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/app/views/tutorial/a7.html b/app/views/tutorial/a7.html index 124d42950..e211d3009 100644 --- a/app/views/tutorial/a7.html +++ b/app/views/tutorial/a7.html @@ -61,7 +61,7 @@

    Source Code Examples

    To implement isAdminmiddleware, check if isAdmin flag is set for the logged in user in database. -
    For example, here is middleware function that can be added to +
    For example, here is middleware function that can be added to routes\session.js:

    diff --git a/app/views/tutorial/a8.html b/app/views/tutorial/a8.html
    index ae1883bdf..2b56d669d 100644
    --- a/app/views/tutorial/a8.html
    +++ b/app/views/tutorial/a8.html
    @@ -100,4 +100,4 @@ 

    Source Code Example

    -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/app/views/tutorial/a9.html b/app/views/tutorial/a9.html index dceaf36da..7bb72e955 100644 --- a/app/views/tutorial/a9.html +++ b/app/views/tutorial/a9.html @@ -158,4 +158,4 @@

    Attack Mechanics

    -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/app/views/tutorial/ssrf.html b/app/views/tutorial/ssrf.html index 11caba305..b9db9322a 100644 --- a/app/views/tutorial/ssrf.html +++ b/app/views/tutorial/ssrf.html @@ -47,4 +47,4 @@

    How Do I Prevent It?

    -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/config/config.js b/config/config.js index 5b1d511d6..8701b80ed 100644 --- a/config/config.js +++ b/config/config.js @@ -6,7 +6,10 @@ const finalEnv = process.env.NODE_ENV || "development"; const allConf = require(path.resolve(__dirname + "/../config/env/all.js")) const envConf = require(path.resolve(__dirname + "/../config/env/" + finalEnv.toLowerCase() + ".js")) || {} -const config = { ...allConf, ...envConf } +const config = { + ...allConf, + ...envConf +} console.log(`Current Config: ${config}`) diff --git a/server.js b/server.js index a0147579f..cb141ec26 100644 --- a/server.js +++ b/server.js @@ -14,7 +14,11 @@ const marked = require("marked"); //const nosniff = require('dont-sniff-mimetype'); const app = express(); // Web framework to handle routing requests const routes = require("./app/routes"); -const { port, db, cookieSecret } = require("./config/config"); // Application config properties +const { + port, + db, + cookieSecret +} = require("./config/config"); // Application config properties /* // Fix for A6-Sensitive Data Exposure // Load keys for establishing secure HTTPS connection diff --git a/test/e2e/integration/allocations_spec.js b/test/e2e/integration/allocations_spec.js index 225d2c6f2..73ab5e078 100644 --- a/test/e2e/integration/allocations_spec.js +++ b/test/e2e/integration/allocations_spec.js @@ -1,50 +1,50 @@ /// describe('/allocations behaviour', () => { - before(() => { - cy.dbReset() - }) - - after(() => { - cy.dbReset() - }) - - afterEach(() => { - cy.visitPage('/logout') - }) - - it('Should redirect if the user has not logged in', () => { - cy.visitPage('/allocations/1') - cy.url().should('include', 'login') - }) - - it('Should be accesible for a logged user', () => { - cy.userSignIn() - cy.visitPage('/allocations/1') - cy.url().should('include', 'allocations') - }) - - it('Should be an input', () => { - cy.userSignIn() - cy.visitPage('/allocations/1') - cy.get('input[name="threshold"]') - }) - - it('Should redirect the user', () => { - const threshold = 2 - cy.userSignIn() - cy.visitPage('/allocations/1') - - cy.get('input[name="threshold"]') - .clear() - .type(threshold) - - cy.get('button[type="submit"]') - .click() - - cy.location().should((loc) => { - expect(loc.search).to.eq(`?threshold=${threshold}`) - expect(loc.pathname).to.eq('/allocations/1') + before(() => { + cy.dbReset() + }) + + after(() => { + cy.dbReset() + }) + + afterEach(() => { + cy.visitPage('/logout') + }) + + it('Should redirect if the user has not logged in', () => { + cy.visitPage('/allocations/1') + cy.url().should('include', 'login') + }) + + it('Should be accesible for a logged user', () => { + cy.userSignIn() + cy.visitPage('/allocations/1') + cy.url().should('include', 'allocations') + }) + + it('Should be an input', () => { + cy.userSignIn() + cy.visitPage('/allocations/1') + cy.get('input[name="threshold"]') + }) + + it('Should redirect the user', () => { + const threshold = 2 + cy.userSignIn() + cy.visitPage('/allocations/1') + + cy.get('input[name="threshold"]') + .clear() + .type(threshold) + + cy.get('button[type="submit"]') + .click() + + cy.location().should((loc) => { + expect(loc.search).to.eq(`?threshold=${threshold}`) + expect(loc.pathname).to.eq('/allocations/1') + }) }) - }) }) diff --git a/test/e2e/integration/benefits_spec.js b/test/e2e/integration/benefits_spec.js index b28f0d968..4ede56420 100644 --- a/test/e2e/integration/benefits_spec.js +++ b/test/e2e/integration/benefits_spec.js @@ -1,56 +1,56 @@ /// describe('/login behaviour', () => { - before(() => { - cy.dbReset() - }) - - after(() => { - cy.dbReset() - }) - - afterEach(() => { - cy.visitPage('/logout') - }) - - it('Should redirect if the user has not logged in', () => { - cy.visitPage('/benefits') - cy.url().should('include', 'login') - }) - - it('Should be accesible by default if the user is an admin', () => { - cy.adminSignIn() - cy.visitPage('/benefits') - cy.url().should('include', 'benefits') - }) - - it('Should be accesible if the user is not an admin', () => { - cy.userSignIn() - cy.visitPage('/benefits') - cy.url().should('include', 'benefits') - }) - - it('Should be a table with rows', () => { - cy.adminSignIn() - cy.visitPage('/benefits') - cy.get('table tr') - }) - - it('Should data in the table be modified', () => { - cy.adminSignIn() - cy.visitPage('/benefits') - cy.get('input[name="benefitStartDate"') - .first() - .type('2099-01-10') - - cy.get('button[type="submit"]') - .first() - .click() - - cy.url().should('include', 'benefits') - cy.get('input[name="benefitStartDate"') - .first() - .invoke('val') - .should('eq', '2099-01-10') - }) + before(() => { + cy.dbReset() + }) + + after(() => { + cy.dbReset() + }) + + afterEach(() => { + cy.visitPage('/logout') + }) + + it('Should redirect if the user has not logged in', () => { + cy.visitPage('/benefits') + cy.url().should('include', 'login') + }) + + it('Should be accesible by default if the user is an admin', () => { + cy.adminSignIn() + cy.visitPage('/benefits') + cy.url().should('include', 'benefits') + }) + + it('Should be accesible if the user is not an admin', () => { + cy.userSignIn() + cy.visitPage('/benefits') + cy.url().should('include', 'benefits') + }) + + it('Should be a table with rows', () => { + cy.adminSignIn() + cy.visitPage('/benefits') + cy.get('table tr') + }) + + it('Should data in the table be modified', () => { + cy.adminSignIn() + cy.visitPage('/benefits') + cy.get('input[name="benefitStartDate"') + .first() + .type('2099-01-10') + + cy.get('button[type="submit"]') + .first() + .click() + + cy.url().should('include', 'benefits') + cy.get('input[name="benefitStartDate"') + .first() + .invoke('val') + .should('eq', '2099-01-10') + }) }) diff --git a/test/e2e/integration/contributions_spec.js b/test/e2e/integration/contributions_spec.js index c00af2f87..e846b8847 100644 --- a/test/e2e/integration/contributions_spec.js +++ b/test/e2e/integration/contributions_spec.js @@ -1,57 +1,57 @@ /// describe('/contributions behaviour', () => { - before(() => { - cy.dbReset() - }) - - after(() => { - cy.dbReset() - }) - - afterEach(() => { - cy.visitPage('/logout') - }) - - it('Should redirect if the user has not logged in', () => { - cy.visitPage('/contributions') - cy.url().should('include', 'login') - }) - - it('Should be accesible for a logged user', () => { - cy.userSignIn() - cy.visitPage('/contributions') - cy.url().should('include', 'contributions') - }) - - it('Should be a table with several inputs', () => { - cy.userSignIn() - cy.visitPage('/contributions') - cy.get('table') - .find('input') - .should('have.length', 3) - }) - - it('Should input be modified', () => { - const value = '12' - cy.userSignIn() - cy.visitPage('/contributions') - cy.get('table') - .find('input') - .first() - .clear() - .type(value) - - cy.get('button[type="submit"]') - .click() - - cy.get('tbody > tr > td') - .eq(1) - .contains(`${value} %`) - - cy.get('.alert-success') - .should('be.visible') - - cy.url().should('include', 'contributions') - }) + before(() => { + cy.dbReset() + }) + + after(() => { + cy.dbReset() + }) + + afterEach(() => { + cy.visitPage('/logout') + }) + + it('Should redirect if the user has not logged in', () => { + cy.visitPage('/contributions') + cy.url().should('include', 'login') + }) + + it('Should be accesible for a logged user', () => { + cy.userSignIn() + cy.visitPage('/contributions') + cy.url().should('include', 'contributions') + }) + + it('Should be a table with several inputs', () => { + cy.userSignIn() + cy.visitPage('/contributions') + cy.get('table') + .find('input') + .should('have.length', 3) + }) + + it('Should input be modified', () => { + const value = '12' + cy.userSignIn() + cy.visitPage('/contributions') + cy.get('table') + .find('input') + .first() + .clear() + .type(value) + + cy.get('button[type="submit"]') + .click() + + cy.get('tbody > tr > td') + .eq(1) + .contains(`${value} %`) + + cy.get('.alert-success') + .should('be.visible') + + cy.url().should('include', 'contributions') + }) }) diff --git a/test/e2e/integration/dashboard_spec.js b/test/e2e/integration/dashboard_spec.js index 737aacf6e..ad6c6e762 100644 --- a/test/e2e/integration/dashboard_spec.js +++ b/test/e2e/integration/dashboard_spec.js @@ -1,36 +1,36 @@ /// describe('/dashboard behaviour', () => { - afterEach(() => { - cy.visitPage('/logout') - }) + afterEach(() => { + cy.visitPage('/logout') + }) - it('Should redirect if the user has not logged in', () => { - cy.visitPage('/dashboard') - cy.url().should('include', 'login') - }) + it('Should redirect if the user has not logged in', () => { + cy.visitPage('/dashboard') + cy.url().should('include', 'login') + }) - it('Should be accesible for a logged user', () => { - cy.userSignIn() - cy.visitPage('/dashboard') - cy.url().should('include', 'dashboard') - }) + it('Should be accesible for a logged user', () => { + cy.userSignIn() + cy.visitPage('/dashboard') + cy.url().should('include', 'dashboard') + }) - it('Should display information', () => { - cy.userSignIn() - cy.visitPage('/dashboard') - cy.url().should('include', 'dashboard') - cy.get('.panel') - .should('be.visible') - .should('have.length', 5) - }) + it('Should display information', () => { + cy.userSignIn() + cy.visitPage('/dashboard') + cy.url().should('include', 'dashboard') + cy.get('.panel') + .should('be.visible') + .should('have.length', 5) + }) - it('Should have a link to /contributions', () => { - cy.userSignIn() - cy.visitPage('/dashboard') - cy.url().should('include', 'dashboard') - cy.get('.panel a') - .first() - .should('have.attr', 'href', '/contributions') - }) + it('Should have a link to /contributions', () => { + cy.userSignIn() + cy.visitPage('/dashboard') + cy.url().should('include', 'dashboard') + cy.get('.panel a') + .first() + .should('have.attr', 'href', '/contributions') + }) }) diff --git a/test/e2e/integration/general_spec.js b/test/e2e/integration/general_spec.js index d211a90ce..7b7e872ec 100644 --- a/test/e2e/integration/general_spec.js +++ b/test/e2e/integration/general_spec.js @@ -1,73 +1,75 @@ /// describe('General behaviour', () => { - beforeEach(() => { - cy.adminSignIn() - cy.visitPage('/') - }) + beforeEach(() => { + cy.adminSignIn() + cy.visitPage('/') + }) - afterEach(() => { - cy.visitPage('/logout') - }) + afterEach(() => { + cy.visitPage('/logout') + }) - it('should have all the links in the side menu', () => { - cy.get('#dashboard-menu-link') - .should('be.visible') - .should('have.attr', 'href', '/') + it('should have all the links in the side menu', () => { + cy.get('#dashboard-menu-link') + .should('be.visible') + .should('have.attr', 'href', '/') - cy.get('#contributions-menu-link') - .should('be.visible') - .should('have.attr', 'href', '/contributions') + cy.get('#contributions-menu-link') + .should('be.visible') + .should('have.attr', 'href', '/contributions') - cy.get('#allocations-menu-link') - .should('be.visible') - .should('have.attr', 'href', '/allocations/1') + cy.get('#allocations-menu-link') + .should('be.visible') + .should('have.attr', 'href', '/allocations/1') - cy.get('#memos-menu-link') - .should('be.visible') - .should('have.attr', 'href', '/memos') + cy.get('#memos-menu-link') + .should('be.visible') + .should('have.attr', 'href', '/memos') - cy.get('#profile-menu-link') - .should('be.visible') - .should('have.attr', 'href', '/profile') + cy.get('#profile-menu-link') + .should('be.visible') + .should('have.attr', 'href', '/profile') - cy.get('#learn-menu-link') - .should('be.visible') - .should('have.attr', 'target', '_blank') - .should('have.attr', 'href', '/learn?url=https://www.khanacademy.org/economics-finance-domain/core-finance/investment-vehicles-tutorial/ira-401ks/v/traditional-iras') + cy.get('#learn-menu-link') + .should('be.visible') + .should('have.attr', 'target', '_blank') + .should('have.attr', 'href', '/learn?url=https://www.khanacademy.org/economics-finance-domain/core-finance/investment-vehicles-tutorial/ira-401ks/v/traditional-iras') - cy.get('#research-menu-link') - .should('be.visible') - .should('have.attr', 'href', '/research') + cy.get('#research-menu-link') + .should('be.visible') + .should('have.attr', 'href', '/research') - cy.get('#logout-menu-link') - .should('be.visible') - .should('have.attr', 'href', '/logout') - }) + cy.get('#logout-menu-link') + .should('be.visible') + .should('have.attr', 'href', '/logout') + }) - it('should have a profile menu', () => { - cy.get('.user-dropdown a') - .eq(0) - .invoke('text') - .should('eq', ' Node Goat Admin ') + it('should have a profile menu', () => { + cy.get('.user-dropdown a') + .eq(0) + .invoke('text') + .should('eq', ' Node Goat Admin ') - cy.get('.user-dropdown a') - .eq(1) - .should('have.attr', 'href', '/profile') - .invoke('text') - .should('eq', ' Profile') + cy.get('.user-dropdown a') + .eq(1) + .should('have.attr', 'href', '/profile') + .invoke('text') + .should('eq', ' Profile') - cy.get('.user-dropdown a') - .eq(2) - .should('have.attr', 'href', '/logout') - .invoke('text') - .should('eq', ' Log Out') - }) + cy.get('.user-dropdown a') + .eq(2) + .should('have.attr', 'href', '/logout') + .invoke('text') + .should('eq', ' Log Out') + }) - it('should manage 404', () => { - cy.visitPage('/invented', { failOnStatusCode: false }) - cy.get('body') - .invoke('text') - .should('eq', '\nCannot GET /invented\n\n\n') - }) + it('should manage 404', () => { + cy.visitPage('/invented', { + failOnStatusCode: false + }) + cy.get('body') + .invoke('text') + .should('eq', '\nCannot GET /invented\n\n\n') + }) }) diff --git a/test/e2e/integration/learn_spec.js b/test/e2e/integration/learn_spec.js index 3e15f6454..177fd3555 100644 --- a/test/e2e/integration/learn_spec.js +++ b/test/e2e/integration/learn_spec.js @@ -1,18 +1,18 @@ /// describe('/learn behaviour', () => { - afterEach(() => { - cy.visitPage('/logout') - }) + afterEach(() => { + cy.visitPage('/logout') + }) - it('Should redirect if the user has not logged in', () => { - cy.visitPage('/learn?url=/dashboard') - cy.url().should('include', 'login') - }) + it('Should redirect if the user has not logged in', () => { + cy.visitPage('/learn?url=/dashboard') + cy.url().should('include', 'login') + }) - it('Should be accesible for a logged user', () => { - cy.userSignIn() - cy.visitPage('/learn?url=/dashboard') - cy.url().should('include', 'dashboard') - }) + it('Should be accesible for a logged user', () => { + cy.userSignIn() + cy.visitPage('/learn?url=/dashboard') + cy.url().should('include', 'dashboard') + }) }) diff --git a/test/e2e/integration/login_spec.js b/test/e2e/integration/login_spec.js index 458525379..70353eeef 100644 --- a/test/e2e/integration/login_spec.js +++ b/test/e2e/integration/login_spec.js @@ -1,94 +1,94 @@ /// describe('/login behaviour', () => { - before(() => { - cy.dbReset() - }) - - after(() => { - cy.dbReset() - }) - - afterEach(() => { - cy.visitPage('/logout') - }) - - beforeEach(() => { - cy.visitPage('/login') - }) - - it('should have tutorial Guide link', () => { - cy.get("a[href='/tutorial']") - .should('have.attr', 'target', '_blank') - .and('be.visible') - }) - - it('Should open the tutorial in another tab', () => { - cy.get("a[href='/tutorial']").then(function ($a) { - const href = - $a.prop('href') - cy.visit(href) - cy.url().should('include', 'tutorial') + before(() => { + cy.dbReset() }) - }) - - it('should have admin user able to login', () => { - cy.fixture('users/admin.json').as('admin') - cy.get('@admin').then(admin => { - cy.get('#userName').type(admin.user) - cy.get('#password').type(admin.pass) - cy.get('[type="submit"]').click() - cy.url().should('include', 'benefits') + + after(() => { + cy.dbReset() + }) + + afterEach(() => { + cy.visitPage('/logout') + }) + + beforeEach(() => { + cy.visitPage('/login') + }) + + it('should have tutorial Guide link', () => { + cy.get("a[href='/tutorial']") + .should('have.attr', 'target', '_blank') + .and('be.visible') }) - }) - - it('should have non-admin user able to login', () => { - cy.fixture('users/user.json').as('user') - cy.get('@user').then(user => { - cy.get('#userName').type(user.user) - cy.get('#password').type(user.pass) - cy.get('[type="submit"]').click() - cy.url().should('include', 'dashboard') + + it('Should open the tutorial in another tab', () => { + cy.get("a[href='/tutorial']").then(function($a) { + const href = + $a.prop('href') + cy.visit(href) + cy.url().should('include', 'tutorial') + }) + }) + + it('should have admin user able to login', () => { + cy.fixture('users/admin.json').as('admin') + cy.get('@admin').then(admin => { + cy.get('#userName').type(admin.user) + cy.get('#password').type(admin.pass) + cy.get('[type="submit"]').click() + cy.url().should('include', 'benefits') + }) }) - }) - it('should reject wrong password', () => { - cy.fixture('users/user.json').as('user') - cy.get('@user').then(user => { - cy.get('#userName').type(user.user) - cy.get('#password').type('TO BE REJECTED') - cy.get('[type="submit"]').click() + it('should have non-admin user able to login', () => { + cy.fixture('users/user.json').as('user') + cy.get('@user').then(user => { + cy.get('#userName').type(user.user) + cy.get('#password').type(user.pass) + cy.get('[type="submit"]').click() + cy.url().should('include', 'dashboard') + }) + }) - cy.url().should('include', 'login') + it('should reject wrong password', () => { + cy.fixture('users/user.json').as('user') + cy.get('@user').then(user => { + cy.get('#userName').type(user.user) + cy.get('#password').type('TO BE REJECTED') + cy.get('[type="submit"]').click() - cy.get('.alert-danger') - .contains('Invalid password') - .and('be.visible') + cy.url().should('include', 'login') + + cy.get('.alert-danger') + .contains('Invalid password') + .and('be.visible') + }) }) - }) - it('should reject wrong username', () => { - cy.fixture('users/user.json').as('user') - cy.get('@user').then(user => { - cy.get('#userName').type('INVENTED') - cy.get('#password').type(user.pass) - cy.get('[type="submit"]').click() + it('should reject wrong username', () => { + cy.fixture('users/user.json').as('user') + cy.get('@user').then(user => { + cy.get('#userName').type('INVENTED') + cy.get('#password').type(user.pass) + cy.get('[type="submit"]').click() - cy.url().should('include', 'login') + cy.url().should('include', 'login') - cy.get('.alert-danger') - .contains('Invalid username') - .and('be.visible') + cy.get('.alert-danger') + .contains('Invalid username') + .and('be.visible') + }) }) - }) - it('should have new user/ sign up link', () => { - cy.get("a[href='/signup']") - .and('be.visible') - }) + it('should have new user/ sign up link', () => { + cy.get("a[href='/signup']") + .and('be.visible') + }) - it('Should redirect to the signup', () => { - cy.get("a[href='/signup']").click() - cy.url().should('include', 'signup') - }) + it('Should redirect to the signup', () => { + cy.get("a[href='/signup']").click() + cy.url().should('include', 'signup') + }) }) diff --git a/test/e2e/integration/logout_spec.js b/test/e2e/integration/logout_spec.js index b56bc9142..53c1f3755 100644 --- a/test/e2e/integration/logout_spec.js +++ b/test/e2e/integration/logout_spec.js @@ -1,32 +1,32 @@ /// describe('/logout behaviour', () => { - before(() => { - cy.dbReset() - }) + before(() => { + cy.dbReset() + }) - after(() => { - cy.dbReset() - }) + after(() => { + cy.dbReset() + }) - it('Should redirect if the user has not logged in', () => { - cy.visitPage('/logout') - cy.url().should('include', 'login') - }) + it('Should redirect if the user has not logged in', () => { + cy.visitPage('/logout') + cy.url().should('include', 'login') + }) - it('Should be working if the user is an admin', () => { - cy.adminSignIn() - cy.visitPage('/logout') - cy.url().should('include', 'login') - cy.visitPage('/dashboard') - cy.url().should('include', 'login') - }) + it('Should be working if the user is an admin', () => { + cy.adminSignIn() + cy.visitPage('/logout') + cy.url().should('include', 'login') + cy.visitPage('/dashboard') + cy.url().should('include', 'login') + }) - it('Should be working if the user is not an admin', () => { - cy.userSignIn() - cy.visitPage('/logout') - cy.url().should('include', 'login') - cy.visitPage('/dashboard') - cy.url().should('include', 'login') - }) + it('Should be working if the user is not an admin', () => { + cy.userSignIn() + cy.visitPage('/logout') + cy.url().should('include', 'login') + cy.visitPage('/dashboard') + cy.url().should('include', 'login') + }) }) diff --git a/test/e2e/integration/memos_spec.js b/test/e2e/integration/memos_spec.js index 0928d3d56..834343ece 100644 --- a/test/e2e/integration/memos_spec.js +++ b/test/e2e/integration/memos_spec.js @@ -1,51 +1,51 @@ /// describe('/memos behaviour', () => { - before(() => { - cy.dbReset() - }) - - after(() => { - cy.dbReset() - }) - - afterEach(() => { - cy.visitPage('/logout') - }) - - it('Should redirect if the user has not logged in', () => { - cy.visitPage('/memos') - cy.url().should('include', 'login') - }) - - it('Should be accesible for a logged user', () => { - cy.userSignIn() - cy.visitPage('/memos') - cy.url().should('include', 'memos') - }) - - it('Should exists a textarea', () => { - cy.userSignIn() - cy.visitPage('/memos') - cy.get('textarea[name="memo"]') - }) - - it('Should memo be generated', () => { - const text = 'Hello World!' - - cy.userSignIn() - cy.visitPage('/memos') - cy.get('textarea[name="memo"]') - .clear() - .type(text) - - cy.get('button[type="submit"]') - .click() - - cy.url().should('include', 'memos') - - cy.get('.panel-body > p') - .should('be.visible') - .contains(text) - }) + before(() => { + cy.dbReset() + }) + + after(() => { + cy.dbReset() + }) + + afterEach(() => { + cy.visitPage('/logout') + }) + + it('Should redirect if the user has not logged in', () => { + cy.visitPage('/memos') + cy.url().should('include', 'login') + }) + + it('Should be accesible for a logged user', () => { + cy.userSignIn() + cy.visitPage('/memos') + cy.url().should('include', 'memos') + }) + + it('Should exists a textarea', () => { + cy.userSignIn() + cy.visitPage('/memos') + cy.get('textarea[name="memo"]') + }) + + it('Should memo be generated', () => { + const text = 'Hello World!' + + cy.userSignIn() + cy.visitPage('/memos') + cy.get('textarea[name="memo"]') + .clear() + .type(text) + + cy.get('button[type="submit"]') + .click() + + cy.url().should('include', 'memos') + + cy.get('.panel-body > p') + .should('be.visible') + .contains(text) + }) }) diff --git a/test/e2e/integration/profile_spec.js b/test/e2e/integration/profile_spec.js index 42d0198f3..38c9099d2 100644 --- a/test/e2e/integration/profile_spec.js +++ b/test/e2e/integration/profile_spec.js @@ -1,72 +1,72 @@ /// describe('/profile behaviour', () => { - before(() => { - cy.dbReset() - }) + before(() => { + cy.dbReset() + }) - after(() => { - cy.dbReset() - }) + after(() => { + cy.dbReset() + }) - afterEach(() => { - cy.visitPage('/logout') - }) + afterEach(() => { + cy.visitPage('/logout') + }) - it('Should redirect if the user has not logged in', () => { - cy.visitPage('/profile') - cy.url().should('include', 'login') - }) + it('Should redirect if the user has not logged in', () => { + cy.visitPage('/profile') + cy.url().should('include', 'login') + }) - it('Should be accesible for logged user', () => { - cy.userSignIn() - cy.visitPage('/profile') - cy.url().should('include', 'profile') - }) + it('Should be accesible for logged user', () => { + cy.userSignIn() + cy.visitPage('/profile') + cy.url().should('include', 'profile') + }) - it('Should be a form with inputs', () => { - cy.userSignIn() - cy.visitPage('/profile') - cy.get('form[role="form"]') - .find('input') - .should('have.length', 8) - }) + it('Should be a form with inputs', () => { + cy.userSignIn() + cy.visitPage('/profile') + cy.get('form[role="form"]') + .find('input') + .should('have.length', 8) + }) - it('Should first name be modified', () => { - const newName = 'My new name!' - const bankRouting = '0198212#' - cy.userSignIn() - cy.visitPage('/profile') - cy.get('#firstName') - .clear() - .type(newName) + it('Should first name be modified', () => { + const newName = 'My new name!' + const bankRouting = '0198212#' + cy.userSignIn() + cy.visitPage('/profile') + cy.get('#firstName') + .clear() + .type(newName) - cy.get('#bankRouting') - .clear() - .type(bankRouting) + cy.get('#bankRouting') + .clear() + .type(bankRouting) - cy.get('button[type="submit"]') - .first() - .click() + cy.get('button[type="submit"]') + .first() + .click() - cy.url().should('include', 'profile') + cy.url().should('include', 'profile') - cy.get('.alert-success') - .should('be.visible') - // @TODO: Just commented for CI, this MUST be improved - /* - cy.get('#firstName') - .invoke('val') - .should('eq', newName) - */ - }) + cy.get('.alert-success') + .should('be.visible') + // @TODO: Just commented for CI, this MUST be improved + /* + cy.get('#firstName') + .invoke('val') + .should('eq', newName) + */ + }) - it('Google search this profile by name', () => { - cy.userSignIn() - cy.visitPage('/profile') + it('Google search this profile by name', () => { + cy.userSignIn() + cy.visitPage('/profile') - cy.get('form[role="form"] a') - .should('be.visible') - .should('have.attr', 'href') - }) + cy.get('form[role="form"] a') + .should('be.visible') + .should('have.attr', 'href') + }) }) diff --git a/test/e2e/integration/research_spec.js b/test/e2e/integration/research_spec.js index 841da0860..ef1d3c8a6 100644 --- a/test/e2e/integration/research_spec.js +++ b/test/e2e/integration/research_spec.js @@ -1,44 +1,44 @@ /// describe('/research behaviour', () => { - afterEach(() => { - cy.visitPage('/logout') - }) - - it('Should redirect if the user has not logged in', () => { - cy.visitPage('/research') - cy.url().should('include', 'login') - }) - - it('Should be accesible for a logged user', () => { - cy.userSignIn() - cy.visitPage('/research') - cy.url().should('include', 'research') - }) - - it('Should be a form with an input', () => { - cy.userSignIn() - cy.visitPage('/research') - cy.get('form[role="search"]') - .find('input') - }) - - it('Should have an input text as a valid stock symbol', () => { - const stockSymbol = 'AAPL' - cy.userSignIn() - cy.visitPage('/research') - cy.get('.form-control') - .clear() - .type(stockSymbol) - - cy.get('form') - .should('have.attr', 'action', '/research') - .invoke('attr', 'action', '/skip') - - cy.get('button[type="submit"]') - .first() - .click() - - cy.url().should('include', 'https%3A%2F%2Ffinance.yahoo.com%2Fquote%2F&symbol=AAPL') - }) + afterEach(() => { + cy.visitPage('/logout') + }) + + it('Should redirect if the user has not logged in', () => { + cy.visitPage('/research') + cy.url().should('include', 'login') + }) + + it('Should be accesible for a logged user', () => { + cy.userSignIn() + cy.visitPage('/research') + cy.url().should('include', 'research') + }) + + it('Should be a form with an input', () => { + cy.userSignIn() + cy.visitPage('/research') + cy.get('form[role="search"]') + .find('input') + }) + + it('Should have an input text as a valid stock symbol', () => { + const stockSymbol = 'AAPL' + cy.userSignIn() + cy.visitPage('/research') + cy.get('.form-control') + .clear() + .type(stockSymbol) + + cy.get('form') + .should('have.attr', 'action', '/research') + .invoke('attr', 'action', '/skip') + + cy.get('button[type="submit"]') + .first() + .click() + + cy.url().should('include', 'https%3A%2F%2Ffinance.yahoo.com%2Fquote%2F&symbol=AAPL') + }) }) diff --git a/test/e2e/integration/signup_spec.js b/test/e2e/integration/signup_spec.js index 1bbc1920a..4a9f7d291 100644 --- a/test/e2e/integration/signup_spec.js +++ b/test/e2e/integration/signup_spec.js @@ -1,78 +1,78 @@ /// describe('/signup behaviour', () => { - before(() => { - cy.dbReset() - }) - - after(() => { - cy.dbReset() - }) - - afterEach(() => { - cy.visitPage('/logout') - }) - - it('Should not redirect if the user has not logged in', () => { - cy.visitPage('/signup') - cy.url().should('include', 'signup') - }) - - it('Should not redirect if the user has logged in', () => { - cy.visitPage('/signup') - cy.url().should('include', 'signup') - }) - - it('Should be a form with inputs', () => { - cy.visitPage('/signup') - cy.get('form[role="form"]') - .find('input') - .should('have.length', 7) - }) - - it('Should new user be added to the system', () => { - cy.fixture('users/new_user.json').as('newUser') - cy.get('@newUser').then(newUser => { - cy.visitPage('/signup') - - cy.get('#userName') - .clear() - .type(newUser.user) - - cy.get('#firstName') - .clear() - .type(newUser.firstName) - - cy.get('#lastName') - .clear() - .type(newUser.lastName) - - cy.get('#password') - .clear() - .type(newUser.pass) - - cy.get('#verify') - .clear() - .type(newUser.pass) - - cy.get('button[type="submit"]') - .first() - .click() - - cy.get('.alert-danger').should('not.be.visible') - - cy.get('.breadcrumb > li') - .invoke('text') - .should('eq', ' Dashboard') + before(() => { + cy.dbReset() }) - }) - - it('Should new user be able to login in the system', () => { - cy.fixture('users/new_user.json').as('newUser') - cy.get('@newUser').then(newUser => { - cy.signIn(newUser.user, newUser.pass) - cy.visitPage('/dashboard') - cy.url().should('include', 'dashboard') + + after(() => { + cy.dbReset() + }) + + afterEach(() => { + cy.visitPage('/logout') + }) + + it('Should not redirect if the user has not logged in', () => { + cy.visitPage('/signup') + cy.url().should('include', 'signup') + }) + + it('Should not redirect if the user has logged in', () => { + cy.visitPage('/signup') + cy.url().should('include', 'signup') + }) + + it('Should be a form with inputs', () => { + cy.visitPage('/signup') + cy.get('form[role="form"]') + .find('input') + .should('have.length', 7) + }) + + it('Should new user be added to the system', () => { + cy.fixture('users/new_user.json').as('newUser') + cy.get('@newUser').then(newUser => { + cy.visitPage('/signup') + + cy.get('#userName') + .clear() + .type(newUser.user) + + cy.get('#firstName') + .clear() + .type(newUser.firstName) + + cy.get('#lastName') + .clear() + .type(newUser.lastName) + + cy.get('#password') + .clear() + .type(newUser.pass) + + cy.get('#verify') + .clear() + .type(newUser.pass) + + cy.get('button[type="submit"]') + .first() + .click() + + cy.get('.alert-danger').should('not.be.visible') + + cy.get('.breadcrumb > li') + .invoke('text') + .should('eq', ' Dashboard') + }) + }) + + it('Should new user be able to login in the system', () => { + cy.fixture('users/new_user.json').as('newUser') + cy.get('@newUser').then(newUser => { + cy.signIn(newUser.user, newUser.pass) + cy.visitPage('/dashboard') + cy.url().should('include', 'dashboard') + }) }) - }) }) diff --git a/test/e2e/integration/tutorial_spec.js b/test/e2e/integration/tutorial_spec.js index eac8f5c28..e0e0b77eb 100644 --- a/test/e2e/integration/tutorial_spec.js +++ b/test/e2e/integration/tutorial_spec.js @@ -1,71 +1,71 @@ /// describe('/tutorial behaviour', () => { - it('Should have all the links in the side nav', () => { - cy.visitPage('/tutorial') - cy.url().should('include', 'tutorial') - cy.get('.side-nav') - .should('be.visible') - .find('a') - .should('have.length', 12) - }) + it('Should have all the links in the side nav', () => { + cy.visitPage('/tutorial') + cy.url().should('include', 'tutorial') + cy.get('.side-nav') + .should('be.visible') + .find('a') + .should('have.length', 12) + }) - it('Should exists /tutorial/a1', () => { - cy.visitPage('/tutorial/a1') - cy.url().should('include', 'a1') - }) + it('Should exists /tutorial/a1', () => { + cy.visitPage('/tutorial/a1') + cy.url().should('include', 'a1') + }) - it('Should exists /tutorial/a2', () => { - cy.visitPage('/tutorial/a2') - cy.url().should('include', 'a2') - }) + it('Should exists /tutorial/a2', () => { + cy.visitPage('/tutorial/a2') + cy.url().should('include', 'a2') + }) - it('Should exists /tutorial/a3', () => { - cy.visitPage('/tutorial/a3') - cy.url().should('include', 'a3') - }) - it('Should exists /tutorial/a4', () => { - cy.visitPage('/tutorial/a4') - cy.url().should('include', 'a4') - }) + it('Should exists /tutorial/a3', () => { + cy.visitPage('/tutorial/a3') + cy.url().should('include', 'a3') + }) + it('Should exists /tutorial/a4', () => { + cy.visitPage('/tutorial/a4') + cy.url().should('include', 'a4') + }) - it('Should exists /tutorial/a5', () => { - cy.visitPage('/tutorial/a5') - cy.url().should('include', 'a5') - }) + it('Should exists /tutorial/a5', () => { + cy.visitPage('/tutorial/a5') + cy.url().should('include', 'a5') + }) - it('Should exists /tutorial/a6', () => { - cy.visitPage('/tutorial/a6') - cy.url().should('include', 'a6') - }) + it('Should exists /tutorial/a6', () => { + cy.visitPage('/tutorial/a6') + cy.url().should('include', 'a6') + }) - it('Should exists /tutorial/a7', () => { - cy.visitPage('/tutorial/a7') - cy.url().should('include', 'a7') - }) + it('Should exists /tutorial/a7', () => { + cy.visitPage('/tutorial/a7') + cy.url().should('include', 'a7') + }) - it('Should exists /tutorial/a8', () => { - cy.visitPage('/tutorial/a8') - cy.url().should('include', 'a8') - }) + it('Should exists /tutorial/a8', () => { + cy.visitPage('/tutorial/a8') + cy.url().should('include', 'a8') + }) - it('Should exists /tutorial/a9', () => { - cy.visitPage('/tutorial/a9') - cy.url().should('include', 'a9') - }) + it('Should exists /tutorial/a9', () => { + cy.visitPage('/tutorial/a9') + cy.url().should('include', 'a9') + }) - it('Should exists /tutorial/a10', () => { - cy.visitPage('/tutorial/a10') - cy.url().should('include', 'a10') - }) + it('Should exists /tutorial/a10', () => { + cy.visitPage('/tutorial/a10') + cy.url().should('include', 'a10') + }) - it('Should exists /tutorial/redos', () => { - cy.visitPage('/tutorial/redos') - cy.url().should('include', 'redos') - }) + it('Should exists /tutorial/redos', () => { + cy.visitPage('/tutorial/redos') + cy.url().should('include', 'redos') + }) - it('Should exists /tutorial/ssrf', () => { - cy.visitPage('/tutorial/ssrf') - cy.url().should('include', 'ssrf') - }) + it('Should exists /tutorial/ssrf', () => { + cy.visitPage('/tutorial/ssrf') + cy.url().should('include', 'ssrf') + }) }) diff --git a/test/e2e/plugins/index.js b/test/e2e/plugins/index.js index fd170fba6..19b9052ba 100644 --- a/test/e2e/plugins/index.js +++ b/test/e2e/plugins/index.js @@ -12,6 +12,6 @@ // the project's config changing) module.exports = (on, config) => { - // `on` is used to hook into various events Cypress emits - // `config` is the resolved Cypress config + // `on` is used to hook into various events Cypress emits + // `config` is the resolved Cypress config } diff --git a/test/e2e/support/commands.js b/test/e2e/support/commands.js index 42278a9f0..25e02dabd 100644 --- a/test/e2e/support/commands.js +++ b/test/e2e/support/commands.js @@ -1,33 +1,36 @@ -const { port, hostName } = require('../../../config/env/all') +const { + port, + hostName +} = require('../../../config/env/all') Cypress.Commands.add('signIn', (usr, pw) => { - cy.visit(`http://${hostName}:${port}/login`) - cy.get('#userName').type(usr) - cy.get('#password').type(pw) - cy.get('[type="submit"]').click() + cy.visit(`http://${hostName}:${port}/login`) + cy.get('#userName').type(usr) + cy.get('#password').type(pw) + cy.get('[type="submit"]').click() }) Cypress.Commands.add('adminSignIn', () => { - cy.fixture('users/admin.json').as('admin') - cy.get('@admin').then(admin => { - cy.signIn(admin.user, admin.pass) - }) + cy.fixture('users/admin.json').as('admin') + cy.get('@admin').then(admin => { + cy.signIn(admin.user, admin.pass) + }) }) Cypress.Commands.add('userSignIn', () => { - cy.fixture('users/user.json').as('user') - cy.get('@user').then(user => { - cy.signIn(user.user, user.pass) - }) + cy.fixture('users/user.json').as('user') + cy.get('@user').then(user => { + cy.signIn(user.user, user.pass) + }) }) Cypress.Commands.add('visitPage', (path = '/', config = {}) => { - cy.visit(`http://${hostName}:${port}${path}`, config) + cy.visit(`http://${hostName}:${port}${path}`, config) }) Cypress.Commands.add('dbReset', () => { - cy.exec('npm run db:seed', { - timeout: 6000, - failOnNonZeroExit: false - }) + cy.exec('npm run db:seed', { + timeout: 6000, + failOnNonZeroExit: false + }) }) diff --git a/test/e2e/support/index.js b/test/e2e/support/index.js index 292b920bb..d6189ff90 100644 --- a/test/e2e/support/index.js +++ b/test/e2e/support/index.js @@ -1,2 +1 @@ - require('./commands.js')