Skip to content

Commit

Permalink
feat(test): add unit tests for scanner and config
Browse files Browse the repository at this point in the history
  • Loading branch information
nielm committed Dec 4, 2024
1 parent 05ad649 commit 2e647b2
Show file tree
Hide file tree
Showing 15 changed files with 1,094 additions and 49 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/codehealth.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ jobs:
working-directory: cloudrun-malware-scanner/
run: npm run typecheck

- name: NPM test
working-directory: cloudrun-malware-scanner/
run: npm test

- name: NPM Audit
working-directory: cloudrun-malware-scanner/
run: npm audit
Expand Down
3 changes: 2 additions & 1 deletion cloudrun-malware-scanner/.husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
echo "Running cloudrun-malware-scanner/.husky/pre-commit checks. Use -n/--no-verify to skip"

cd cloudrun-malware-scanner
cd cloudrun-malware-scanner || exit 1
npm run eslint
npm run check-format
npm run typecheck
npm run terraform-validate
npm test
npm audit
61 changes: 34 additions & 27 deletions cloudrun-malware-scanner/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@
* limitations under the License.
*/

const {Storage} = require('@google-cloud/storage');
const {logger} = require('./logger.js');
const pkgJson = require('./package.json');
const {readFileSync} = require('node:fs');

/** @typedef {import('@google-cloud/storage').Storage} Storage */

/**
* @typedef {{
* unscanned: string,
Expand All @@ -41,23 +42,20 @@ const BUCKET_TYPES = ['unscanned', 'clean', 'quarantined'];
* fileExclusionPatterns?: Array<string | Array<string>>,
* fileExclusionRegexps: Array<RegExp>,
* ignoreZeroLengthFiles: boolean,
* comments?: string
* comments?: string | string[]
* }} Config
*/

const storage = new Storage({
userAgent: `cloud-solutions/${pkgJson.name}-usage-v${pkgJson.version}`,
});

/**
* Read configuration from JSON configuration file, verify
* Read configuration from JSON configuration file, parse, verify
* and return a Config object
*
* @async
* @param {string} configFile
* @param {Storage} storage
* @return {Promise<Config>}
*/
async function readAndVerifyConfig(configFile) {
async function readAndVerifyConfig(configFile, storage) {
logger.info(`Using configuration file: ${configFile}`);
let configText;
try {
Expand All @@ -71,7 +69,7 @@ async function readAndVerifyConfig(configFile) {
throw err;
}
try {
return verifyConfig(configText);
return validateConfig(parseConfig(configText), storage);
} catch (e) {
const err = /** @type {Error} */ (e);
logger.fatal(
Expand All @@ -83,24 +81,31 @@ async function readAndVerifyConfig(configFile) {
}

/**
* Read configuration from JSON configuration file, verify
* and return a Config object
*
* @async
* @param {string} configText
* @return {Promise<Config>}
* @returns {Config}
*/
async function verifyConfig(configText) {
function parseConfig(configText) {
/** @type {Config} */
let config;

try {
config = JSON.parse(configText);
} catch (e) {
const err = /** @type {Error} */ (e);
throw new Error(`Failed to parse configuration as JSON: ${err.message}`);
throw new Error(`Failed to parse configuration as JSON: ${e}`);
}
return config;
}

/**
* Read configuration from JSON configuration file, verify
* and return a Config object
*
* @async
* @param {any} config
* @param {Storage} storage
* @return {Promise<Config>}
*/
async function validateConfig(config, storage) {
delete config.comments;

if (config.buckets.length === 0) {
Expand All @@ -119,6 +124,7 @@ async function verifyConfig(configText) {
!(await checkBucketExists(
bucketDefs[bucketType],
`config.buckets[${x}].${bucketType}`,
storage,
))
) {
success = false;
Expand All @@ -130,13 +136,14 @@ async function verifyConfig(configText) {
bucketDefs.clean === bucketDefs.quarantined
) {
logger.fatal(`Config Error: buckets[${x}]: bucket names are not unique`);
// success = false;
success = false;
}
}
if (
!(await checkBucketExists(
config.ClamCvdMirrorBucket,
'ClamCvdMirrorBucket',
storage,
))
) {
success = false;
Expand All @@ -152,12 +159,9 @@ async function verifyConfig(configText) {
success = false;
}

// Validate fileExclusionPatterns
// Validate fileExclusionPatterns[] and convert to fileExclusionRegexps[]
config.fileExclusionRegexps = [];
if (config.fileExclusionPatterns == null) {
// not specified.
config.fileExclusionPatterns = [];
} else {
if (config.fileExclusionPatterns != null) {
if (!(config.fileExclusionPatterns instanceof Array)) {
logger.fatal(
`Config Error: fileExclusionPatterns must be an array of Strings`,
Expand Down Expand Up @@ -213,21 +217,23 @@ async function verifyConfig(configText) {
}
}
}
delete config.fileExclusionPatterns;

if (!success) {
throw new Error('Invalid configuration');
}
return config;
return Object.freeze(config);
}

/**
* Check that given bucket exists. Returns true on success
*
* @param {string} bucketName
* @param {string} configName
* @param {Storage} storage
* @return {Promise<boolean>}
*/
async function checkBucketExists(bucketName, configName) {
async function checkBucketExists(bucketName, configName, storage) {
if (!bucketName) {
logger.fatal(`Config Error: no "${configName}" bucket defined`);
return false;
Expand All @@ -253,6 +259,7 @@ async function checkBucketExists(bucketName, configName) {
module.exports = {
readAndVerifyConfig,
TEST_ONLY: {
storage,
checkBucketExists,
validateConfig,
},
};
13 changes: 11 additions & 2 deletions cloudrun-malware-scanner/eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@ import globals from "globals";
import pluginJs from "@eslint/js";

export default [
{ files: ["**/*.js"], languageOptions: { sourceType: "commonjs" } },
{ languageOptions: { globals: globals.browser } },
{
files: ["*.js"],
languageOptions: { sourceType: "commonjs", globals: globals.node },
},
{
files: ["spec/*.js"],
languageOptions: {
sourceType: "commonjs",
globals: [globals.jasmine, globals.node],
},
},
pluginJs.configs.recommended,
];
8 changes: 7 additions & 1 deletion cloudrun-malware-scanner/gcs-proxy-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const {GoogleAuth} = require('google-auth-library');
const {logger} = require('./logger.js');
const {readAndVerifyConfig} = require('./config.js');
const httpProxy = require('http-proxy');
const {Storage} = require('@google-cloud/storage');
const pkgJson = require('./package.json');

/** @typedef {import('./config.js').Config} Config */
/** @typedef {import('http').IncomingMessage} IncomingMessage */
Expand Down Expand Up @@ -159,8 +161,12 @@ async function run() {
configFile = './config.json';
}

const storage = new Storage({
userAgent: `cloud-solutions/${pkgJson.name}-usage-v${pkgJson.version}`,
});

/** @type {Config} */
const config = await readAndVerifyConfig(configFile);
const config = await readAndVerifyConfig(configFile, storage);

clamCvdMirrorBucket = config.ClamCvdMirrorBucket;

Expand Down
2 changes: 1 addition & 1 deletion cloudrun-malware-scanner/jsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
"strict": true,
"resolveJsonModule": true
},
"include": ["*.js"]
"include": ["*.js", "spec/*.js"]
}
Loading

0 comments on commit 2e647b2

Please sign in to comment.