diff --git a/README.md b/README.md index 2b540e3..6c28526 100755 --- a/README.md +++ b/README.md @@ -15,13 +15,14 @@ Run and Eventarc. ## Changes -* 2019-09-01 Initial version -* 2020-10-05 Fixes for ClamAV OOM -* 2021-10-14 Use Cloud Run and EventArc instead of Cloud Functions/App Engine -* 2021-10-22 Improve resiliency, Use streaming reads (no temp disk required), +* 2019-09-01 Initial version +* 2020-10-05 Fixes for ClamAV OOM +* 2021-10-14 Use Cloud Run and EventArc instead of Cloud Functions/App Engine +* 2021-10-22 Improve resiliency, Use streaming reads (no temp disk required), improve logging, and handles files in subdirectories -* 2021-11-08 Add support for scanning multiple buckets, improve error - handling to prevent infinite retries, +* 2021-11-08 Add support for scanning multiple buckets, improve error + handling to prevent infinite retries +* 2021-11-22 Remove requirement for Project Viewer permissions. ## License diff --git a/cloudrun-malware-scanner/package.json b/cloudrun-malware-scanner/package.json index 7f82606..130d040 100644 --- a/cloudrun-malware-scanner/package.json +++ b/cloudrun-malware-scanner/package.json @@ -1,6 +1,6 @@ { "name": "gcs-malware-scanner", - "version": "1.2.0", + "version": "1.3.0", "description": "Service to scan GCS documents for the malware and move the analyzed documents to appropriate buckets", "main": "index.js", "scripts": { diff --git a/cloudrun-malware-scanner/server.js b/cloudrun-malware-scanner/server.js index 3f40e7d..5b5dd5c 100644 --- a/cloudrun-malware-scanner/server.js +++ b/cloudrun-malware-scanner/server.js @@ -32,7 +32,7 @@ const MAX_FILE_SIZE = 5000000000; // 5GiB * Configuration object. * * Values are read from the environment variable CONFIG_FILE (which specifies a - * JSON file to read the config from) or single-bucket config variables: + * JSON file to read the config from) or single-bucket config variables: * UNSCANNED_BUCKET, CLEAN_BUCKET and QUARANTINED_BUCKET. * See {@link readAndVerifyConfig}. * @@ -273,24 +273,40 @@ async function readAndVerifyConfig() { throw new Error('No buckets configured'); } - logger.info("BUCKET_CONFIG: "+JSON.stringify(BUCKET_CONFIG, null, 2)); + logger.info('BUCKET_CONFIG: '+JSON.stringify(BUCKET_CONFIG, null, 2)); // Check buckets are specified and exist. let success = true; for (let x = 0; x < BUCKET_CONFIG.buckets.length; x++) { - for (const bucket of ['unscanned', 'clean', 'quarantined']) { - if (!BUCKET_CONFIG.buckets[x][bucket]) { - logger.fatal( - `Error in bucket config #${x}: no "${bucket}" bucket defined`); + const config = BUCKET_CONFIG.buckets[x]; + for (const bucketType of ['unscanned', 'clean', 'quarantined']) { + const bucketName = config[bucketType]; + if (!bucketName) { + logger.fatal(`Error in bucket config[${x}]: no "${ + bucketType}" bucket defined`); success = false; } - // Check for bucket existence - if (!(await storage.bucket(BUCKET_CONFIG.buckets[x][bucket]).exists())) { - logger.fatal(`Error in bucket config[${x}]: "${bucket}" bucket: ${ - BUCKET_CONFIG.buckets[x][bucket]} does not exist`); + // Check for bucket existence by listing files in bucket, will throw + // an exception if the bucket is not readable. + // This is used in place of Bucket.exists() to avoid the need for + // Project/viewer permission. + try { + await storage.bucket(bucketName).getFiles( + {maxResults: 1, prefix: 'zzz', autoPaginate: false}); + } catch (e) { + logger.fatal(`Error in bucket config[${x}]: cannot view files in "${ + bucketType}" bucket: ${bucketName} : ${e.message}`); + logger.debug({err: e}); success = false; } } + if(config.unscanned === config.clean + || config.unscanned === config.quarantined + || config.clean === config.quarantined) { + logger.fatal( + `Error in bucket config[${x}]: bucket names are not unique`); + success = false; + } } if (!success) { throw new Error('Invalid configuration');