Skip to main content
author avatar
Dan RealePrincipal QA Engineer
author avatar
Steven SeeseSenior QA Engineer

*Views, thoughts, and opinions expressed in this post belong solely to the author, and not necessarily to SemanticBits.

On every project we have worked on, there has been a feature in our applications for file uploads. These files typically contain either patient health data or provider data. As part of our team’s regression tests, we ensure that the user can upload a file and that the system processes that file the way it should. Part of the file upload process includes a virus scanner that checks the file submitted by the user. If by some chance a virus is found, the virus scanner tool handles the file and discards it or quarantines it and does not let the system process the file.

So, how would we go about testing a scenario to make sure the virus scanner is behaving normally? Simple, just upload a file that contains a virus and make sure the file does not get processed. In theory this is a very simple test. But what if you have no idea how to upload a file with a virus? Where can you find a virus that won’t do damage to your computer?

The European Institute for Computer Antivirus Research (EICAR) is an organization that can help us with some of that.

There are example viruses in the form of a .txt or .zip file that can be downloaded and used for our exact use case. We tried to download the file from the EICAR site but our corporate antivirus detected these files and discarded them before we ever had a chance to use it. No problem. We will just create a .txt file with the test string on their site `X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*

While trying that, the corporate antivirus automatically deleted the file we tried to create. It detected the test virus and removed it from my machine. So, how are we supposed to test these uploads of a virus file to see that our system under test handles them properly?

Here comes Docker to the rescue.

We are not going to give a full Docker tutorial, as that is not the purpose of this post. But it is a great tool that makes development and testing more streamlined than ever before. For our virus scan test, Docker will work perfectly because it is an isolated space where there is a Linux OS. More importantly, it does not contain antivirus software, and our corporate antivirus does not have access inside that container. That is the beauty of Docker. We’re not going to get into mounting and volumes, because that is out of scope. But if you decided to use volumes in your Docker setup, you might run into a problem where your corporate antivirus picks up the test virus file. For this example, we are not going to use a volume.

To get started, we are going to write a small script in Javascript that will upload our file to a live server that is running our application. We could follow this same flow that will be documented below against a local running environment. We would just need to change the URL in our test.

Create a new folder on your computer for this project: `mdkir virustestproject`

Assuming you have NodeJS installed in some capacity: `npm init -y`

Next, we will install some of the dependencies we need: `npm install --save-dev axios form-data`

Create a file at the root of your project named: `test.js`

Add these contents:

const axios = require("axios");
const FormData = require("form-data");
const fs = require("fs");
const args = process.argv.slice(2);
const virusFilePath = args[0];


async function uploadFile(file) {
 console.log(file);
 var data = new FormData();
 data.append("assessmentZip", fs.createReadStream(file));
 var config = {
   method: "post",
   url: `https://exampleapp.com/api/upload`,
   headers: {
     ...data.getHeaders(),
   },
   data: data,
 };
 try {
   const response = await axios(config);
   console.log(response.data);
 } catch (error) {
   console.log(error);
 }
}


async () => {
 await uploadFile(virusFilePath);
};

This is the script that will upload our virus file to the application. Since we have an API that we can use directly, we use Axios and FormData to attach the file and send it to the server via an http request. If you want to test this script with a non-virus file, you can certainly do that. We can run our test with this command in our terminal window: `node test.js “filepathwithofnonvirusfile”. For our example, we also added a test script in our package.json file. This will shorten the command once we have it working in Docker: `"test": "node test.js \"/usr/src/app/virustest.zip\""`. But this is optional.

Now that we have our script working, we will add the Docker files we need.

Create a file at the root of your project `Dockerfile` with these contents:

FROM node:16
USER root


RUN echo  " node version:    $(node -v) \n" \
"npm version:     $(npm -v) \n"


RUN apt-get update -y && \
apt-get install -y \
zip


ENV TERM xterm
ENV npm_config_loglevel warn
ENV npm_config_unsafe_perm true


WORKDIR /usr/src/app

COPY package*.json ./

RUN npm install

COPY . .

ENTRYPOINT [ "/bin/bash" ]

In a nutshell, this Docker file is going to build a Docker image that contains all of our source code (test script that will upload a file) and install the .zip utility. In our case, we needed to upload a .zip file to my application under test. If you don’t have that need, then you can omit this from the Docker file or just leave it. It won’t get in the way.

Since we are hosting these files in a Git repo, we have included a .gitignore file as well as a .dockerignore file.

`.gitinore`  contents:

node_modules

`.dockerignore` contents:

node_modules

npm-debug.log

Dockerfile

.dockerignore

Finally, we will run our test. Here are the steps to follow below in your command line. 

docker build -t blogpost (creates Docker image)

docker run -it --rm --name blogpostcontainer blogpost (starts the container in interactive mode)

You are now inside the Docker container echo'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' > virustext.txt  (creates the test virus file inside the Docker container. This is what we cannot do on our corporate computers that have antivirus installed.)

zip -r -X virustest.zip virustext.txt (creates a .zip file with the infected virus test file, but you can omit this step if you don’t need a .zip file.)

node test.js "/usr/src/app/virustest.zip" (runs your test or script or `npm test` if you added the script inside of your package.json earlier. This is the same test we ran above to check that our regular file would be uploaded. Instead, we are now using our test virus file created via Docker.)

exit  (exits from Docker container and removes the container)

As you can see, we are able to use Docker to create the test virus file, avoid removal, and successfully upload it to our application for testing.

This Git repo has a working example of everything explained above, so you can use it as your starter template for virus file testing.

Bonus

We are going to give you a simple playwright script that can be used as well. This is a simple playwright script that performs a file upload through our application’s user interface. You will need to install the playwright dependency `npm install playwright mocha --save-dev`. Create a new folder called “tests” at the root of the project. Inside that folder, create a new file and call it ui_test.spec.js.  In package.json, add a script with the following: `"test:ui": "mocha ./tests/**.spec.js --timeout=60000"

In this example, we are taking a slightly different approach and will create the EICAR test virus file directly in our test spec as opposed to as a separate process. This will allow us to repeat this test easily for our UI automated regression. After that file is created, we go to the page, “upload” the file by setting the test virus file as our input file. Our application automatically takes the file and processes it. If a virus is found, we expect to see a “File is infected” message and there should be nothing in our results table. We add a secondary check in case the file is found as a virus but the results table contains that file and treats it as a valid file.

const { chromium } = require("playwright");
const fs = require("fs");


const fileDir = "eicar.txt";


const baseUrl = "http:example.com";


describe("Virus file upload", () => {
 let browser;
 let page;
 before(async () => {
   browser = await chromium.launch({
     channel: "chrome",
     headless: false,
     args: ["--ignore-certificate-errors", "--no-sandbox"],
   });
   page = await browser.newPage();
   await generateEICARTestFile(fileDir);
   await page.goto(baseUrl);
 });
 after(async () => {
   await browser.close();
 });
 it('upload virus infected file and verify "File is infected" error', async () => {
   await page.setInputFiles(
     "input[type=file]",
     fileDir
   );
   // check the app finds the virus
   const fileInfectLocation = '[data-testid="error"]';
   const fileInfect = "File is infected";
   await verifyTextOnPage(fileInfectLocation, fileInfect, page);
   // secondary check just in case our application virus scanner failed to catch the virus
   const TableCount = '[data-testid="table-count"]';
   const NoTableCount = "No";
   await verifyTextOnPage(TableCount, NoTableCount, page);
 });
});


async function generateEICARTestFile(dir) {
 await fs.writeFile(
   `${dir}`,
   "X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*",
   (err) => {
     if (err) {
       console.error(err);
     }
   }
 );
}


async function verifyTextOnPage(el, result, page) {
 const element = await page.$(el);
 const text = await page.evaluate((element) => element.textContent, element);
 expect(text.trim()).to.eql(result);
}

Here is a sample docker file that can be used to run the playwright tests. We will name this one Dockerfile.ui at the root of our project:

FROM mcr.microsoft.com/playwright:focal


WORKDIR /usr/src/app
COPY package*.json ./


RUN apt-get update && \
   apt-get install -y gnupg wget curl unzip --no-install-recommends && \
   wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - && \
   echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list && \
   apt-get update -y && \
   apt-get install -y google-chrome-stable && \
   CHROMEVER=$(google-chrome --product-version | grep -o "[^\.]*\.[^\.]*\.[^\.]*") && \
   DRIVERVER=$(curl -s "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_$CHROMEVER") && \
   wget -q --continue -P /chromedriver "http://chromedriver.storage.googleapis.com/$DRIVERVER/chromedriver_linux64.zip" && \
   unzip /chromedriver/chromedriver* -d /chromedriver


RUN npm install


COPY . .

docker build -f Dockerfile.ui -t blogpostui (creates Docker image)

docker run -it --rm --name blogpostcontainerui blogpostui (starts the container in interactive mode)

`npm test:ui`  (runs your tests that you added inside of your package.json earlier)

exit  (exits from Docker container and removes the container)

As you can see, we can group Docker with a myriad of testing frameworks and tools to test out certain scenarios that might not be appropriate on our corporate machines. If we were to really infect the Docker container with a virus, it would take one command to blow that container away and start fresh. It’s been a very handy tool for us in QA. We welcome other QA professionals and developers to explore other areas where Docker makes sense in their testing journey.