Securely Injecting Secrets and Passwords into Cycle

Securely Injecting Secrets and Passwords into Cycle

Handling secrets, like passwords used to login to the the system under test, safely and securely is an important part of maintaining and sharing your tests. Hard-coding passwords in your features in plain text makes them readable to anyone who you share your features with. Storing them in plain text in other shared files in your project makes them only marginally less accessible to everyone you share your project with and it is still far from secure. In this article, we’ll discuss two approaches to more safely and securely handle passwords in your Cycle test suites.

Utilizing Config Files

Utilizing config file steps is the most flexible way to handle secrets in your Cycle test suites. If you are not familiar with the config file steps, we have several examples to help get you started in the Intro to Config Files public repository on GitHub. The advantages of utilizing a config file to handle your secrets include:

  • You can use them to store more than just secrets, allowing you to inject other values to parameterize your tests.

  • You can inject secrets from a secure third party secret manager, either when running Cycle locally or running Cycle in a CI/CD pipeline. However, you also have the option of overriding passwords locally if you do not have access to a third party secret manager.

  • Config files allow you to include comments in the file, so you can add any documentation you would like to the config file, allowing you to document the required data.


The disadvantages of utilizing a config file include:
  • You must set the variable names in the variable blacklist to make sure they’re not exposed in the output panel or reports.

  • If you are not already using steps that assign variables from config files, you will need to make some minor updates to your features.


Let’s consider the following example config file.  We’ll name it “default.conf” and add it to a directory named “conf” in our Cycle project.

  1. user = "super"
  2. user = ${?WMS_USER}
  3. password = ${?WMS_PASS}
  4. wh_id = "WMD1"
  5. wh_id = ${?WH_ID}

If we ran the following step:

  1. Given I assign values from config file "conf/default.conf" to variables

Then it will create cycle variables named user, password, and wh_id.  The user variable will have a value of “super” unless it was overridden by an environment variable named “WMS_USER”.  Likewise, the wh_id variable will have a value of “WMD1” unless it was overridden by an environment variable named “WH_ID”. For the password variable, there is no default, since it would have had to have been hard-coded in the config file, which we’re trying to avoid. 


So our two options are:
  1. Inject the password value into Cycle as an environment variable (the details of how to do this will be covered later in this article).  This is how you would inject the values when running in a CI/CD pipeline, and is one option when running Cycle locally.

  2. Override the value in a local.conf file, which is not committed to a git repository (the .gitignore file in your project would include local.conf, so no files named local.conf would be committed and would instead remain local to your machine). This would only be a valid option when running Cycle locally.


Option 2 would involve creating a second config file in the “conf” directory named “local.conf”.  In this file, we include all the values from default.conf in the first line. We then override the value of password and assign it a value in the second line. 

  1. include "default"
  2. password = WELCOME1

Then if we updated our feature to run the following steps:

  1. If I assign values from config file "conf/local.conf" to variables
  2. ElsIf I assign values from config file "conf/default.conf" to variables
  3. EndIf

When running Cycle locally the password variable would have a value of “WELCOME1”. When running Cycle in a CI/CD pipeline, there would be no local.conf file, so the value would have to be injected as an environment variable.

Updating Existing Stored Credentials

The other option to handle secrets in Cycle would be to set the password in a stored credential to reference an environment variable using the same syntax that you would use in a config file, e.g., ${?WMS_PASS}. When you enter a password for a stored credential in the settings panel, it is stored in plain text in the .cycuser file. Updating the credential to refer to an environment variable, it is now the environment variable name that’s stored in the .cycuser file, rather than the actual password.


The advantages of using a stored credential are:

  • Since they are not variables, you don’t have to worry about blacklisting any variables to prevent the values from showing up in the output panel or reports.

  • If you are already using steps in your features that reference stored credentials, you don’t need to make any changes to those features.


The disadvantage of using a stored credential are:
  • You can only inject credentials, not any other values you may refer to in the rest of your test suite.

  • There is no equivalent option of utilizing a local.conf file, so you have to utilize a .bat file to launch Cycle so you can inject the values at startup when running locally.

  • If you needed to update the value, you would have to relaunch Cycle to inject the updated value at application launch.

Injecting Environment Variables into Cycle

All popular CI/CD pipelines have mechanisms to securely store secrets and inject them as environment variables into the applications running in the pipeline. We’ll provide some information on how to set that up in Azure DevOps and Jenkins. Additionally, we’ll provide a few examples of utilizing batch files to launch Cycle locally with injected environment variables.

Azure DevOps

In Azure DevOps, there is an AzureKeyValue@1 task that will make all or a subset of the secrets stored in KeyVault available to be injected as environment variables. In the following example, a KeyVault entry named “wmspass” is referenced in the pipeline. An environment variable named “WMS_PASS” is then set to that value in the step that executes Cycle.

  1. stages:
  2. - stage: Run_Web_Tests
  3.  jobs:
  4.  - job:
  5.    steps:
  6.    - task: AzureKeyVault@1
  7.      inputs:
  8.        azureSubscription: 'WMS - Development'
  9.        KeyVaultName: 'WMS-KV'
  10.        SecretsFilter: 'wmspass'
  11.        RunAsPreJob: true
  12.    - script: |
  13.        ./cyclecli.exe -p myproj picking.feature 
  14.      workingDirectory: test/
  15.      env:
  16.        WMS_PASS: $(wmspass)

Jenkins

There are numerous references online describing how to make secrets available to your jobs as environment variables. A few references are included below.

  1. https://devops.com/how-to-securely-manage-secrets-within-jenkins/
  2. https://secrethub.io/docs/guides/jenkins/
  3. https://docs.cloudbees.com/docs/cloudbees-ci/latest/cloud-secure-guide/injecting-secrets

Utilizing Batch Files Locally

Creating a batch file locally to inject a secret into Cycle could be as easy as creating a batch file that sets the environment variables prior to launching Cycle. While this approach is pretty simple, it is important that this file is not stored anywhere where it would be shared with other people, such as committing it to a git repository. Otherwise, you would still have secrets stored in plain text easily readable by anyone, just in a different location. A sample batch file is as follows.

  1. set WMS_PASS=WELCOME1
  2. START C:\"Program Files (x86)"\CycleLabs\Cycle\cycle.exe

If you use Azure KeyVault, you can utilize Azure CLI in a batch file to inject the secrets into Cycle. Below is an example using a KeyVault named “WMS-KV”, utilizing the same “wmspass” KeyVault entry mentioned above.

  1. @echo off
  2. FOR /F %%G IN ('az keyvault secret show --vault-name WMS-KV --name wmspass --query value --output tsv') DO set WMS_PASS=%%G
  3. START C:\"Program Files (x86)"\CycleLabs\Cycle\cycle.exe

A variety of third party solutions exist that have CLI components, so you can store passwords securely locally and inject them into applications as environment variables. Below is an example using 1Password, where your 1Password vault name is “wms”, the item name is “credentials”, and the field name is “password”. More documentation is available at https://developer.1password.com/docs/cli/secrets-environment-variables/.

  1. export WMS_PASS=op://wms/credentials/password
  2. START C:\"Program Files (x86)"\CycleLabs\Cycle\cycle.exe

Many other third party secret managers are available to provide equivalent functionality as these examples to safely and securely inject values into your Cycle test suite. If you are using a tool that has not been covered above and would like to contribute an example or need assistance in getting a sample set up for your needs, we’d love to hear from you.