Protecting sensitive data in Cypress testing

Testing is essential in software development because it ensures that the end user will benefit from a high-quality solution that works as intended. An application might not work correctly without proper testing and could damage the user’s system. One of the most popular testing tools is Cypress, which helps tech specialists identify and fix web application issues before being released.

When testing with Cypress, sensitive data must be securely processed to avoid software malfunctioning and vulnerabilities. Our colleague, Vlad R., shows the best practices that will help you protect sensitive data when testing in Cypress. Vlad is a passionate software tester of AROBS’s custom enterprise software development team and by giving examples, he shows you how to properly handle test data.


The real issue

 

Since a lot of cyber attacks start with a leaked secret, one of the most wanted digital assets for cybercriminals nowadays are exposed sensitive data and according to the latest Secret Sprawl report published by GitGuardian in 2022, these hardcoded secrets have never been easier to find.

The number of secrets that have been detected in 2021 through scanning GitHub commits has increased two times compared to 2020. On average, 3 commits out of 1,000 exposed at least one secret. We can therefore conclude that secrets sprawling is a constantly growing problem in corporate repositories, so protecting your organization’s data is now more important than ever.  


What is a secret? Handling data in Cypress

 

Very often an overlooked aspect of implementing solutions for automated software testing is how test data is handled. This article will explore ways of addressing that in Cypress, one of the most used JavaScript End to End Testing Framework. But before we dive deep into ways of protecting our organization’s data while using this popular framework, an obvious question needs to be answered first: what are these secrets actually?

In the context of software development, a secret can be any sensitive data that we aim to keep private, from API keys, security certificates, to authentication credentials like usernames and passwords and these last two are the ones we will be focusing on, although some concepts will apply to other secrets as well.  


How not to

 

Let’s take a simple and very common example: A password protected web-application that requires you to enter the email of a user account and a password to see your personal profile. Testing the login functionality in Cypress could look like this: 

 

				
					it('Logs in', () => {  
 cy.visit('/login')   
 cy.get('[name=email]').type('myemailaddress@example.com')  
 cy.get('[name=password]').type('MySecret%Password')
 cy.get('[type=Submit]').click() 
 cy.contains('Your profile').should('be.visible') 
 cy.url().should('eq', 'https://mytestapp/account/profile')  
}) 
				
			

 

The test would pass, but it would both expose our user credentials hardcoded in the test file, a bad practice to be avoided, while at the same time show the credentials as plain text during type command in the Cypress Test Runner.


How to protect sensitive data when testing in Cypress

 

The first thing that we must do is to remove the hardcoded email and password from the test file and pass the credentials as environment variables. We can pass environment variables as options when using the CLI tool and use the  ‐‐env argument for  cypress run, with multiple values being separated by commas. From the command line or from CI, our command to run the test below would look like this: 

 

				
					cypress run --env email=myemailaddress@example.com,password=MySecret%Password  
				
			
				
					it('logs in using env variables', () => {  
 const email = Cypress.env('email')  
 const password = Cypress.env('password')  
 cy.visit('/login')   
 cy.get('[name=email]').type(email)  
 cy.get('[name=password]').type(password)
 cy.get('[type=Submit]').click()
 cy.contains('Your profile').should('be.visible')
 cy.url().should('eq', 'https://mytestapp/account/profile')
}) 
				
			
  The benefit of this approach is that it does not require any changes to files or configuration. It is clear where environment variables come from and allows for dynamic values between different machines. But there are also downsides that need to be considered. It is tedious to write the ‐‐env options everywhere we use Cypress and if we want to save test run scripts in our package.json configuration file for quick access, then our valued secrets like user credentials would still end up in our repository.  Fortunately, there are different paths that we can take in order to mitigate such risks and my favourite one is the.as-a utility tool installed via npm. This Cypress dedicated tool will give us the ability to load groups of environment variables conveniently and cross-platform in a configuration file called .as-a.ini that we would place in our user’s home directory. To better separate the secrets from the user home folder, we can place the .as-a.ini file into a subfolder ~/.as-a/.as-a.ini. In case we need to place this file in the project’s folder, we have to make sure to git ignore the file in order to avoid accidentally committing this to the repository. Once ready, we just have to populate the file with environment variables grouped as below:   
				
					/.as-a/.as-a.ini
[stageUser]  
CYPRESS_password=MySecret%Password  
CYPRESS_email=myemailaddress@example.com  

[prodUser]  
CYPRESS_password=MySecret%Password2
CYPRESS_email=myemailaddressProd@example.com 
				
			

 

When running Cypress tests from our terminal, we can then use commands such as the following examples and all variables in the respective blocks will be injected for the duration of these commands.

 

				
					npx as-a stageUser cypress open
npx as-a prodUser cypress open
				
			


Protecting information in Cypress tests - CI setup

 

Secrets need to be hidden also in Cypress tests on CI setup and we can take CircleCI as an example for this article. It allows us to add private keys or secrets as environment variables for use throughout our project, whereas if we need to share these across projects, then CircleCI security contexts are the recommended way to go. Once a security context is created, we can use the context key in the workflows section of a project’s .circleci/config.yml  file to give any job access to the environment variables associated with the context.  

Last but not least, we need to hide our secrets from any test artifacts like videos or screenshots. The Cypress type command will reveal the typed in value, which in our case would be a password or an email, so we can solve this simply by adding a {log: false} option to this command. Once using this option, the Command Log will no longer reveal the secrets value in videos or screenshots saved from our test runs or when running our tests in the Test Runner.

 

				
					cy.get('[name=email]').type(email, {log:false})
cy.get('[name=password]').type(password, {log:false})
				
			

 

Assertions like  should(have.value, …) also print the value in the Command Log if they are chained at the end of a.type command. This would often be used to confirm right away the value of the input element. 

 

				
					cy.get('[name=password]')
  .type(password, {log: false})
  .should('have.value', password)
				
			

 

Such chained assertion would reveal our secrets so in order to avoid this we need to throw our own error by using a should(callback) assertion form. 

 

				
					cy.get('[name=password]')  
   .type(password, { log: false })  
   .should(el$ => {  
   if (el$.val() !== password) {  
     throw new Error('The value of your typed password is different')  
   }  
 })  
				
			

 

Implementing your Cypress tests following approaches like the ones above will give you a more robust testing framework that takes into consideration the privacy of your project and the security of your organization as a whole 


Conclusion

 

Competitive software development companies understand these risks and make sure their engineers are always using best practices. Following best practices is not always enough and we as humans are still prone to mistakes, therefore solutions that automate secret detection by constantly scanning public and private repositories are also considered to be a highly recommended additional layer of security.  

In a world that becomes more and more digital every day, malicious individuals are always waiting around the corner for an opportunity to take advantage of our vulnerabilities, but being aware of the danger, having the right mindset and implementing a proper set of measures, we are making sure we do not become their next easy target. 

// If you’re looking for a software services partner to help your business adapt to change, leave us a message
Looking for a digital upgrade?
// our recent news

Read our recent blog posts