From b5d311c00c3e92e7fb4e198c46126ead9b32e804 Mon Sep 17 00:00:00 2001 From: Tobias Wolf Date: Mon, 15 Aug 2022 21:09:46 +0200 Subject: [PATCH 1/2] Add support to validate attachment uploads by an external program --- .devcontainer/Dockerfile | 1 + Dockerfile | 1 + docker-compose.yml | 1 + models/attachments.js | 99 ++++++++++++++++++++-------- releases/virtualbox/start-wekan.sh | 1 + snap-src/bin/config | 6 +- snap-src/bin/wekan-help | 6 ++ start-wekan.sh | 1 + torodb-postgresql/docker-compose.yml | 1 + 9 files changed, 90 insertions(+), 27 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 263a05c39..3a44119f9 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -30,6 +30,7 @@ ENV \ RICHER_CARD_COMMENT_EDITOR=false \ CARD_OPENED_WEBHOOK_ENABLED=false \ ATTACHMENTS_STORE_PATH="" \ + ATTACHMENTS_UPLOAD_EXTERNAL_PROGRAM="" \ ATTACHMENTS_UPLOAD_MIME_TYPES="" \ ATTACHMENTS_UPLOAD_MAX_SIZE=0 \ MAX_IMAGE_PIXEL="" \ diff --git a/Dockerfile b/Dockerfile index 9020110af..921ac3ee9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,6 +33,7 @@ ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build- ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD=60 \ ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW=15 \ ACCOUNTS_COMMON_LOGIN_EXPIRATION_IN_DAYS=90 \ + ATTACHMENTS_UPLOAD_EXTERNAL_PROGRAM="" \ ATTACHMENTS_UPLOAD_MIME_TYPES="" \ ATTACHMENTS_UPLOAD_MAX_SIZE=0 \ RICHER_CARD_COMMENT_EDITOR=false \ diff --git a/docker-compose.yml b/docker-compose.yml index 662bb593f..167c64c93 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -267,6 +267,7 @@ services: - CARD_OPENED_WEBHOOK_ENABLED=false #--------------------------------------------------------------- # ==== Allow configuration to validate uploaded attachments ==== + #-ATTACHMENTS_UPLOAD_EXTERNAL_PROGRAM=/usr/local/bin/avscan {file} #-ATTACHMENTS_UPLOAD_MIME_TYPES=image/*,text/* #-ATTACHMENTS_UPLOAD_MAX_SIZE=5000000 #--------------------------------------------------------------- diff --git a/models/attachments.js b/models/attachments.js index f1368121f..8cba250ab 100644 --- a/models/attachments.js +++ b/models/attachments.js @@ -1,5 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { FilesCollection } from 'meteor/ostrio:files'; +import { exec } from 'node:child_process'; +import { promisify } from 'node:util'; import { createBucket } from './lib/grid/createBucket'; import fs from 'fs'; import FileType from 'file-type'; @@ -7,12 +9,15 @@ import path from 'path'; import { AttachmentStoreStrategyFilesystem, AttachmentStoreStrategyGridFs} from '/models/lib/attachmentStoreStrategy'; import FileStoreStrategyFactory, {moveToStorage, rename, STORAGE_NAME_FILESYSTEM, STORAGE_NAME_GRIDFS} from '/models/lib/fileStoreStrategy'; +let asyncExec; +let attachmentUploadExternalProgram; let attachmentUploadMimeTypes = []; let attachmentUploadSize = 0; let attachmentBucket; let storagePath; if (Meteor.isServer) { + asyncExec = promisify(exec); attachmentBucket = createBucket('attachments'); if (process.env.ATTACHMENTS_UPLOAD_MIME_TYPES) { @@ -28,6 +33,14 @@ if (Meteor.isServer) { } } + if (process.env.ATTACHMENTS_UPLOAD_EXTERNAL_PROGRAM) { + attachmentUploadExternalProgram = process.env.ATTACHMENTS_UPLOAD_EXTERNAL_PROGRAM; + + if (!attachmentUploadExternalProgram.includes("{file}")) { + attachmentUploadExternalProgram = undefined; + } + } + storagePath = path.join(process.env.WRITABLE_PATH, 'attachments'); } @@ -56,26 +69,6 @@ Attachments = new FilesCollection({ return ret; }, onAfterUpload(fileObj) { - let isValid = true; - - if (attachmentUploadMimeTypes.length) { - const mimeTypeResult = Promise.await(FileType.fromFile(fileObj.path)); - - const mimeType = (mimeTypeResult ? mimeTypeResult.mime : fileObj.type); - const baseMimeType = mimeType.split('/', 1)[0]; - - isValid = attachmentUploadMimeTypes.includes(mimeType) || attachmentUploadMimeTypes.includes(baseMimeType + '/*') || attachmentUploadMimeTypes.includes('*'); - - if (!isValid) { - console.log("Validation of uploaded file failed: file " + fileObj.path + " - mimetype " + mimeType); - } - } - - if (attachmentUploadSize && fileObj.size > attachmentUploadSize) { - console.log("Validation of uploaded file failed: file " + fileObj.path + " - size " + fileObj.size); - isValid = false; - } - // current storage is the filesystem, update object and database Object.keys(fileObj.versions).forEach(versionName => { fileObj.versions[versionName].storage = STORAGE_NAME_FILESYSTEM; @@ -83,12 +76,8 @@ Attachments = new FilesCollection({ Attachments.update({ _id: fileObj._id }, { $set: { "versions" : fileObj.versions } }); - if (isValid) { - let storage = fileObj.meta.copyStorage || STORAGE_NAME_GRIDFS; - moveToStorage(fileObj, storage, fileStoreStrategyFactory); - } else { - this.remove(fileObj._id); - } + let storageDestination = fileObj.meta.copyStorage || STORAGE_NAME_GRIDFS; + Meteor.defer(() => Meteor.call('validateAttachmentAndMoveToStorage', fileObj._id, storageDestination)); }, interceptDownload(http, fileObj, versionName) { const ret = fileStoreStrategyFactory.getFileStrategy(fileObj, versionName).interceptDownload(http, this.cacheControl); @@ -148,6 +137,64 @@ if (Meteor.isServer) { const fileObj = Attachments.findOne({_id: fileObjId}); rename(fileObj, newName, fileStoreStrategyFactory); }, + validateAttachment(fileObjId) { + check(fileObjId, String); + + const fileObj = Attachments.findOne({_id: fileObjId}); + let isValid = true; + + if (attachmentUploadMimeTypes.length) { + const mimeTypeResult = Promise.await(FileType.fromFile(fileObj.path)); + + const mimeType = (mimeTypeResult ? mimeTypeResult.mime : fileObj.type); + const baseMimeType = mimeType.split('/', 1)[0]; + + isValid = attachmentUploadMimeTypes.includes(mimeType) || attachmentUploadMimeTypes.includes(baseMimeType + '/*') || attachmentUploadMimeTypes.includes('*'); + + if (!isValid) { + console.log("Validation of uploaded file failed: file " + fileObj.path + " - mimetype " + mimeType); + } + } + + if (attachmentUploadSize && fileObj.size > attachmentUploadSize) { + console.log("Validation of uploaded file failed: file " + fileObj.path + " - size " + fileObj.size); + isValid = false; + } + + if (isValid && attachmentUploadExternalProgram) { + let args = { ...attachmentUploadExternalProgram.args }; + + for (let key in args) { + if (args[key] == "%file") { + args[key] = fileObj.path; + } + } + + Promise.await(asyncExec(attachmentUploadExternalProgram.replace("{file}", '"' + fileObj.path + '"'))); + isValid = fs.existsSync(fileObj.path); + + if (!isValid) { + console.log("Validation of uploaded file failed: file " + fileObj.path + " has been deleted externally"); + } + } + + if (!isValid) { + Attachments.remove(fileObjId); + } + }, + validateAttachmentAndMoveToStorage(fileObjId, storageDestination) { + check(fileObjId, String); + check(storageDestination, String); + + Meteor.call('validateAttachment', fileObjId); + + const fileObj = Attachments.findOne({_id: fileObjId}); + + if (fileObj) { + console.debug("Validation of uploaded file completed: file " + fileObj.path + " - storage destination " + storageDestination); + Meteor.defer(() => Meteor.call('moveAttachmentToStorage', fileObjId, storageDestination)); + } + }, }); Meteor.startup(() => { diff --git a/releases/virtualbox/start-wekan.sh b/releases/virtualbox/start-wekan.sh index e8f3c8743..d8a764ebf 100755 --- a/releases/virtualbox/start-wekan.sh +++ b/releases/virtualbox/start-wekan.sh @@ -49,6 +49,7 @@ # - ACCOUNTS_COMMON_LOGIN_EXPIRATION_IN_DAYS=90 #--------------------------------------------------------------- # ==== Allow configuration to validate uploaded attachments ==== + #export ATTACHMENTS_UPLOAD_EXTERNAL_PROGRAM="/usr/local/bin/avscan {file}" #export ATTACHMENTS_UPLOAD_MIME_TYPES="image/*,text/*" #export ATTACHMENTS_UPLOAD_MAX_SIZE=5000000 #--------------------------------------------------------------- diff --git a/snap-src/bin/config b/snap-src/bin/config index 291472f12..76e91660d 100755 --- a/snap-src/bin/config +++ b/snap-src/bin/config @@ -3,7 +3,7 @@ # All supported keys are defined here together with descriptions and default values # list of supported keys -keys="DEBUG MONGO_LOG_DESTINATION MONGO_URL MONGODB_BIND_UNIX_SOCKET MONGO_URL MONGODB_BIND_IP MONGODB_PORT MAIL_URL MAIL_FROM MAIL_SERVICE MAIL_SERVICE_USER MAIL_SERVICE_PASSWORD ROOT_URL PORT DISABLE_MONGODB CADDY_ENABLED CADDY_BIND_PORT WITH_API RICHER_CARD_COMMENT_EDITOR CARD_OPENED_WEBHOOK_ENABLED ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW ACCOUNTS_COMMON_LOGIN_EXPIRATION_IN_DAYS ATTACHMENTS_UPLOAD_MIME_TYPES ATTACHMENTS_UPLOAD_MAX_SIZE MAX_IMAGE_PIXEL IMAGE_COMPRESS_RATIO BIGEVENTS_PATTERN NOTIFICATION_TRAY_AFTER_READ_DAYS_BEFORE_REMOVE NOTIFY_DUE_DAYS_BEFORE_AND_AFTER NOTIFY_DUE_AT_HOUR_OF_DAY EMAIL_NOTIFICATION_TIMEOUT CORS CORS_ALLOW_HEADERS CORS_EXPOSE_HEADERS MATOMO_ADDRESS MATOMO_SITE_ID MATOMO_DO_NOT_TRACK MATOMO_WITH_USERNAME BROWSER_POLICY_ENABLED TRUSTED_URL WEBHOOKS_ATTRIBUTES OAUTH2_ENABLED OIDC_REDIRECTION_ENABLED OAUTH2_CA_CERT OAUTH2_LOGIN_STYLE OAUTH2_CLIENT_ID OAUTH2_SECRET OAUTH2_SERVER_URL OAUTH2_AUTH_ENDPOINT OAUTH2_USERINFO_ENDPOINT OAUTH2_TOKEN_ENDPOINT OAUTH2_ID_MAP OAUTH2_USERNAME_MAP OAUTH2_FULLNAME_MAP OAUTH2_ID_TOKEN_WHITELIST_FIELDS OAUTH2_EMAIL_MAP OAUTH2_REQUEST_PERMISSIONS OAUTH2_ADFS_ENABLED LDAP_ENABLE LDAP_PORT LDAP_HOST LDAP_AD_SIMPLE_AUTH LDAP_BASEDN LDAP_LOGIN_FALLBACK LDAP_RECONNECT LDAP_TIMEOUT LDAP_IDLE_TIMEOUT LDAP_CONNECT_TIMEOUT LDAP_AUTHENTIFICATION LDAP_AUTHENTIFICATION_USERDN LDAP_AUTHENTIFICATION_PASSWORD LDAP_LOG_ENABLED LDAP_BACKGROUND_SYNC LDAP_BACKGROUND_SYNC_INTERVAL LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS LDAP_ENCRYPTION LDAP_CA_CERT LDAP_REJECT_UNAUTHORIZED LDAP_USER_AUTHENTICATION LDAP_USER_AUTHENTICATION_FIELD LDAP_USER_SEARCH_FILTER LDAP_USER_SEARCH_SCOPE LDAP_USER_SEARCH_FIELD LDAP_SEARCH_PAGE_SIZE LDAP_SEARCH_SIZE_LIMIT LDAP_GROUP_FILTER_ENABLE LDAP_GROUP_FILTER_OBJECTCLASS LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT LDAP_GROUP_FILTER_GROUP_NAME LDAP_UNIQUE_IDENTIFIER_FIELD LDAP_UTF8_NAMES_SLUGIFY LDAP_USERNAME_FIELD LDAP_FULLNAME_FIELD LDAP_MERGE_EXISTING_USERS LDAP_SYNC_USER_DATA LDAP_SYNC_USER_DATA_FIELDMAP LDAP_SYNC_GROUP_ROLES LDAP_DEFAULT_DOMAIN LDAP_EMAIL_MATCH_ENABLE LDAP_EMAIL_MATCH_REQUIRE LDAP_EMAIL_MATCH_VERIFIED LDAP_EMAIL_FIELD LDAP_SYNC_ADMIN_STATUS LDAP_SYNC_ADMIN_GROUPS HEADER_LOGIN_ID HEADER_LOGIN_FIRSTNAME HEADER_LOGIN_LASTNAME HEADER_LOGIN_EMAIL LOGOUT_WITH_TIMER LOGOUT_IN LOGOUT_ON_HOURS LOGOUT_ON_MINUTES DEFAULT_AUTHENTICATION_METHOD PASSWORD_LOGIN_ENABLED CAS_ENABLED CAS_BASE_URL CAS_LOGIN_URL CAS_VALIDATE_URL SAML_ENABLED SAML_PROVIDER SAML_ENTRYPOINT SAML_ISSUER SAML_CERT SAML_IDPSLO_REDIRECTURL SAML_PRIVATE_KEYFILE SAML_PUBLIC_CERTFILE SAML_IDENTIFIER_FORMAT SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE SAML_ATTRIBUTES ORACLE_OIM_ENABLED RESULTS_PER_PAGE WAIT_SPINNER NODE_OPTIONS" +keys="DEBUG MONGO_LOG_DESTINATION MONGO_URL MONGODB_BIND_UNIX_SOCKET MONGO_URL MONGODB_BIND_IP MONGODB_PORT MAIL_URL MAIL_FROM MAIL_SERVICE MAIL_SERVICE_USER MAIL_SERVICE_PASSWORD ROOT_URL PORT DISABLE_MONGODB CADDY_ENABLED CADDY_BIND_PORT WITH_API RICHER_CARD_COMMENT_EDITOR CARD_OPENED_WEBHOOK_ENABLED ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW ACCOUNTS_COMMON_LOGIN_EXPIRATION_IN_DAYS ATTACHMENTS_UPLOAD_EXTERNAL_PROGRAM ATTACHMENTS_UPLOAD_MIME_TYPES ATTACHMENTS_UPLOAD_MAX_SIZE MAX_IMAGE_PIXEL IMAGE_COMPRESS_RATIO BIGEVENTS_PATTERN NOTIFICATION_TRAY_AFTER_READ_DAYS_BEFORE_REMOVE NOTIFY_DUE_DAYS_BEFORE_AND_AFTER NOTIFY_DUE_AT_HOUR_OF_DAY EMAIL_NOTIFICATION_TIMEOUT CORS CORS_ALLOW_HEADERS CORS_EXPOSE_HEADERS MATOMO_ADDRESS MATOMO_SITE_ID MATOMO_DO_NOT_TRACK MATOMO_WITH_USERNAME BROWSER_POLICY_ENABLED TRUSTED_URL WEBHOOKS_ATTRIBUTES OAUTH2_ENABLED OIDC_REDIRECTION_ENABLED OAUTH2_CA_CERT OAUTH2_LOGIN_STYLE OAUTH2_CLIENT_ID OAUTH2_SECRET OAUTH2_SERVER_URL OAUTH2_AUTH_ENDPOINT OAUTH2_USERINFO_ENDPOINT OAUTH2_TOKEN_ENDPOINT OAUTH2_ID_MAP OAUTH2_USERNAME_MAP OAUTH2_FULLNAME_MAP OAUTH2_ID_TOKEN_WHITELIST_FIELDS OAUTH2_EMAIL_MAP OAUTH2_REQUEST_PERMISSIONS OAUTH2_ADFS_ENABLED LDAP_ENABLE LDAP_PORT LDAP_HOST LDAP_AD_SIMPLE_AUTH LDAP_BASEDN LDAP_LOGIN_FALLBACK LDAP_RECONNECT LDAP_TIMEOUT LDAP_IDLE_TIMEOUT LDAP_CONNECT_TIMEOUT LDAP_AUTHENTIFICATION LDAP_AUTHENTIFICATION_USERDN LDAP_AUTHENTIFICATION_PASSWORD LDAP_LOG_ENABLED LDAP_BACKGROUND_SYNC LDAP_BACKGROUND_SYNC_INTERVAL LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS LDAP_ENCRYPTION LDAP_CA_CERT LDAP_REJECT_UNAUTHORIZED LDAP_USER_AUTHENTICATION LDAP_USER_AUTHENTICATION_FIELD LDAP_USER_SEARCH_FILTER LDAP_USER_SEARCH_SCOPE LDAP_USER_SEARCH_FIELD LDAP_SEARCH_PAGE_SIZE LDAP_SEARCH_SIZE_LIMIT LDAP_GROUP_FILTER_ENABLE LDAP_GROUP_FILTER_OBJECTCLASS LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT LDAP_GROUP_FILTER_GROUP_NAME LDAP_UNIQUE_IDENTIFIER_FIELD LDAP_UTF8_NAMES_SLUGIFY LDAP_USERNAME_FIELD LDAP_FULLNAME_FIELD LDAP_MERGE_EXISTING_USERS LDAP_SYNC_USER_DATA LDAP_SYNC_USER_DATA_FIELDMAP LDAP_SYNC_GROUP_ROLES LDAP_DEFAULT_DOMAIN LDAP_EMAIL_MATCH_ENABLE LDAP_EMAIL_MATCH_REQUIRE LDAP_EMAIL_MATCH_VERIFIED LDAP_EMAIL_FIELD LDAP_SYNC_ADMIN_STATUS LDAP_SYNC_ADMIN_GROUPS HEADER_LOGIN_ID HEADER_LOGIN_FIRSTNAME HEADER_LOGIN_LASTNAME HEADER_LOGIN_EMAIL LOGOUT_WITH_TIMER LOGOUT_IN LOGOUT_ON_HOURS LOGOUT_ON_MINUTES DEFAULT_AUTHENTICATION_METHOD PASSWORD_LOGIN_ENABLED CAS_ENABLED CAS_BASE_URL CAS_LOGIN_URL CAS_VALIDATE_URL SAML_ENABLED SAML_PROVIDER SAML_ENTRYPOINT SAML_ISSUER SAML_CERT SAML_IDPSLO_REDIRECTURL SAML_PRIVATE_KEYFILE SAML_PUBLIC_CERTFILE SAML_IDENTIFIER_FORMAT SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE SAML_ATTRIBUTES ORACLE_OIM_ENABLED RESULTS_PER_PAGE WAIT_SPINNER NODE_OPTIONS" #DESCRIPTION_WRITABLE_PATH="Writable path. Default: $SNAP_COMMON/files" #DEFAULT_WRITABLE_PATH="$SNAP_COMMON/files" @@ -129,6 +129,10 @@ DESCRIPTION_ACCOUNTS_COMMON_LOGIN_EXPIRATION_IN_DAYS="Accounts common login expi DEFAULT_ACCOUNTS_COMMON_LOGIN_EXPIRATION_IN_DAYS="90" KEY_ACCOUNTS_COMMON_LOGIN_EXPIRATION_IN_DAYS="accounts-common-login-expiration-in-days" +DESCRIPTION_ATTACHMENTS_UPLOAD_EXTERNAL_PROGRAM="Attachments upload validation by an external program. {file} is replaced by the uploaded file. Example: /usr/local/bin/avscan {file}" +DEFAULT_ATTACHMENTS_UPLOAD_EXTERNAL_PROGRAM="" +KEY_ATTACHMENTS_UPLOAD_EXTERNAL_PROGRAM="attachments-upload-external-program" + DESCRIPTION_ATTACHMENTS_UPLOAD_MIME_TYPES="Attachments upload mime types. Example: image/*,text/*" DEFAULT_ATTACHMENTS_UPLOAD_MIME_TYPES="" KEY_ATTACHMENTS_UPLOAD_MIME_TYPES="attachments-upload-mime-types" diff --git a/snap-src/bin/wekan-help b/snap-src/bin/wekan-help index bc58ad43c..4586baedb 100755 --- a/snap-src/bin/wekan-help +++ b/snap-src/bin/wekan-help @@ -125,6 +125,12 @@ echo -e "\t$ snap set $SNAP_NAME card-opened-webhook-enabled='true'" echo -e "Disable, default:" echo -e "\t$ snap unset $SNAP_NAME card-opened-webhook-enabled" echo -e "\n" +echo -e "Attachments upload validation by an external program. {file} is replaced by the uploaded file. Example: /usr/local/bin/avscan {file}" +echo -e "Enable:" +echo -e "\t$ snap set $SNAP_NAME attachments-upload-external-program='/usr/local/bin/avscan {file}'" +echo -e "Disable, default:" +echo -e "\t$ snap unset $SNAP_NAME attachments-upload-external-program" +echo -e "\n" echo -e "Attachments upload mime types. Example: image/*,text/*" echo -e "Enable:" echo -e "\t$ snap set $SNAP_NAME attachments-upload-mime-types='image/*,text/*'" diff --git a/start-wekan.sh b/start-wekan.sh index 0840f94c4..d41995a04 100755 --- a/start-wekan.sh +++ b/start-wekan.sh @@ -54,6 +54,7 @@ # - ACCOUNTS_COMMON_LOGIN_EXPIRATION_IN_DAYS=90 #--------------------------------------------------------------- # ==== Allow configuration to validate uploaded attachments ==== + #export ATTACHMENTS_UPLOAD_EXTERNAL_PROGRAM="/usr/local/bin/avscan {file}" #export ATTACHMENTS_UPLOAD_MIME_TYPES="image/*,text/*" #export ATTACHMENTS_UPLOAD_MAX_SIZE=5000000 #--------------------------------------------------------------- diff --git a/torodb-postgresql/docker-compose.yml b/torodb-postgresql/docker-compose.yml index 5dc57b1f0..0d2d5a2d8 100644 --- a/torodb-postgresql/docker-compose.yml +++ b/torodb-postgresql/docker-compose.yml @@ -277,6 +277,7 @@ services: - CARD_OPENED_WEBHOOK_ENABLED=false #--------------------------------------------------------------- # ==== Allow configuration to validate uploaded attachments ==== + #-ATTACHMENTS_UPLOAD_EXTERNAL_PROGRAM=/usr/local/bin/avscan {file} #-ATTACHMENTS_UPLOAD_MIME_TYPES=image/*,text/* #-ATTACHMENTS_UPLOAD_MAX_SIZE=5000000 #--------------------------------------------------------------- From e6b705be26987eb700ec8cd07a822bbb5f014f89 Mon Sep 17 00:00:00 2001 From: Tobias Wolf Date: Tue, 16 Aug 2022 12:01:25 +0200 Subject: [PATCH 2/2] Remove leftover unused arguments parsing for external execution --- models/attachments.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/models/attachments.js b/models/attachments.js index 8cba250ab..fe89e83e1 100644 --- a/models/attachments.js +++ b/models/attachments.js @@ -162,14 +162,6 @@ if (Meteor.isServer) { } if (isValid && attachmentUploadExternalProgram) { - let args = { ...attachmentUploadExternalProgram.args }; - - for (let key in args) { - if (args[key] == "%file") { - args[key] = fileObj.path; - } - } - Promise.await(asyncExec(attachmentUploadExternalProgram.replace("{file}", '"' + fileObj.path + '"'))); isValid = fs.existsSync(fileObj.path);