website/docs/Security.md
WebdriverIO has the security aspect in mind when providing solutions. Below are some ways to better secure your test.
:::info
Even for test data, it is essential to ask whether, in the wrong hands, a malicious person could retrieve information or use those resources with malicious intent.
:::
If you are using sensitive data during your test, it is essential to ensure that they are not visible to everyone, such as in logs. Also, when using a cloud provider, private keys are often involved. This information must be masked from logs, reporters, and other touchpoints. The following provides some masking solutions to run tests without exposing those values.
The commands addValue and setValue support a boolean mask value to mask in logs, as well as reporters. Moreover, other tools, such as performance tools and third-party tools, will also receive the mask version, enhancing security.
For example, if you are using a real production user and need to enter a password that you want to mask, then it is now possible with the following:
async enterPassword(userPassword) {
const passwordInputElement = $('Password');
// Get focus
await passwordInputElement.click();
await passwordInputElement.setValue(userPassword, { mask: true });
}
The above will hide the text value from WDIO logs as the following:
Logs example:
INFO webdriver: DATA { text: "**MASKED**" }
Reporters, such as Allure reporters, and third-party tools like Percy from BrowserStack will also handle the masked version. Paired with the proper Appium version, the Appium Logs will also be exempt from your sensitive data.
:::info
Limitations:
getValue command is not supported. Moreover, if used on the same element, it can expose the value intended to be masked when using addValue or setValue.Minimum required version:
:::
Using the maskingPatterns configuration, we can mask sensitive information from WDIO logs. However, Appium logs are not covered.
For example, if you are using a Cloud provider and use the info level, then most certainly you will "leak" the user's key as shown below:
INFO @wdio/local-runner: Start worker 0-0 with arg: ./wdio.conf.ts --user=cloud_user --key=myCloudSecretExposedKey --spec myTest.test.ts
To counter that we can pass the the regular expression '--key=([^ ]*)' and now in the logs you will see
INFO @wdio/local-runner: Start worker 0-0 with arg: ./wdio.conf.ts --user=cloud_user --key=**MASKED** --spec myTest.test.ts
You can achieve the above by providing the regular expression to the maskingPatterns field of the configuration.
export const config: WebdriverIO.Config = {
specs: [...],
capabilities: [{...}],
services: ['lighthouse'],
/**
* test configurations
*/
logLevel: 'info',
maskingPatterns: '/--key=([^ ]*)/',
framework: 'mocha',
outputDir: __dirname,
reporters: ['spec'],
mochaOpts: {
ui: 'bdd',
timeout: 60000
}
}
:::info
Minimum required version:
:::
Another way to block the logging of sensitive data is to lower or silence the log level or disable the logger. It can achieved as follow:
import logger from '@wdio/logger';
/**
* Set the logger level of the WDIO logger to 'silent' before *running a promise, which helps hide sensitive information in the logs.
*/
export const withSilentLogger = async <T>(promise: () => Promise<T>): Promise<T> => {
const webdriverLogLevel = driver.options.logLevel ?? 'error';
try {
logger.setLevel('webdriver', 'silent');
return await promise();
} finally {
logger.setLevel('webdriver', webdriverLogLevel);
}
};
Appium offers its masking solution; see Log filter
@mask@ and use it as a regular expressionUsing the @mask@ example previously mentioned, we can use the following JSON file named appiumMaskLogFilters.json
[
{
"pattern": "@mask@(.*)",
"flags": "s",
"replacer": "**MASKED**"
},
{
"pattern": "\\[(\\\"@\\\",\\\"m\\\",\\\"a\\\",\\\"s\\\",\\\"k\\\",\\\"@\\\",\\S+)\\]",
"flags": "s",
"replacer": "[*,*,M,A,S,K,E,D,*,*]"
}
]
Then pass the JSON file name to the logFilters field into the appium service config:
import { AppiumServerArguments, AppiumServiceConfig } from '@wdio/appium-service';
import { ServiceEntry } from '@wdio/types/build/Services';
const appium = [
'appium',
{
args: {
log: './logs/appium.log',
logFilters: './appiumMaskLogFilters.json',
} satisfies AppiumServerArguments,
} satisfies AppiumServiceConfig,
] satisfies ServiceEntry;
BrowserStack also offer some level of masking to hide some data; see hide sensitive data