From 061496c4044717d1d36041c7bf5387fa4ee14d3b Mon Sep 17 00:00:00 2001 From: sanford schaffer Date: Wed, 8 Apr 2026 16:31:15 -0400 Subject: [PATCH 1/3] Add ClamAV rejection placeholder and update attachment response structure --- .../adapters.observations.controllers.web.ts | 31 ++++++++++++++---- service/src/assets/SecError.png | Bin 0 -> 44454 bytes 2 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 service/src/assets/SecError.png diff --git a/service/src/adapters/observations/adapters.observations.controllers.web.ts b/service/src/adapters/observations/adapters.observations.controllers.web.ts index b5a6c91ed..7050e65cc 100644 --- a/service/src/adapters/observations/adapters.observations.controllers.web.ts +++ b/service/src/adapters/observations/adapters.observations.controllers.web.ts @@ -1,5 +1,8 @@ /* eslint-disable @typescript-eslint/explicit-function-return-type */ +import fs from 'fs' +import path from 'path' + import { scanAttachmentWithClamAV } from './adapters.attachments.clamav' import { Readable } from 'stream' import express from 'express' @@ -125,11 +128,23 @@ async function handleFileUpload( const originalBuffer = await readStreamToBuffer(fileStream) const finalBuffer = await scanFileIfNeeded(originalBuffer, info.filename, uploadErrors) if (!finalBuffer) { - attachmentsJson.push({ - name: info.filename, - rejected: true, - error: uploadErrors.find(e => e.file === info.filename)?.error || 'Rejected by ClamAV' - }) + const secErrorPath = path.join(__dirname, '..', '..', '..', 'src', 'assets', 'SecError.png') + const placeholderBuffer = await readStreamToBuffer(fs.createReadStream(secErrorPath)) + await storeAttachment(placeholderBuffer, { filename: info.filename, mimeType: info.mimeType, encoding: info.encoding }, req, createAppRequest, app, attachmentsJson, uploadErrors) + const stored = attachmentsJson[attachmentsJson.length - 1] + if (stored) { + attachmentsJson[attachmentsJson.length - 1] = { + ...stored, + contentStored: false, + error: "File failed security scan", + failures: [ + { + file: info.filename, + error: "File failed security scan" + } + ] + } + } console.log(`[REJECT] File ${info.filename} rejected by ClamAV, skipping storage`) return } @@ -215,8 +230,12 @@ export function ObservationRoutes( req.pipe(bb) }) + const rejected = attachmentsJson.find(a => a.error) + if (rejected) { + return res.status(200).json(rejected) + } return res.status(200).json({ - successes: attachmentsJson.filter(a => !a.rejected), + successes: attachmentsJson, failures: uploadErrors, message: uploadErrors.length > 0 ? 'Some files failed to upload due to scanning errors.' : 'All files uploaded successfully.' }) diff --git a/service/src/assets/SecError.png b/service/src/assets/SecError.png new file mode 100644 index 0000000000000000000000000000000000000000..a3de09499ae61a9d4d5d538d90032deb4ca9a3ff GIT binary patch literal 44454 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

9sFibn$N2jD0Hk@J=awv1M;5!Qu+AHSH%>vKj4J zNC5dW>8djnu5S_>D0H@Rnf9pe;edp_K?Pa;Su^GJT=`dXxDK_a`#}I$u&10#arSg@ zMKh52O3|Q9A^N$xIoIirDUBAI0iQWbqr6->pFz5_tj_>uLLDH{=IBGHtP1dMvDEwd zG+G7Gmf~g2=UC$9c`}h`C&f6DO9x^>OmWu{JxaJad zWjfe`R&6(1){WtPs_-D8$CTrI5c|@WKVv~wth*fi;+$O2{UX(S$fH{!%QQ-vzcwiuwyElN}P_alxDzZ z=qST4Q=(6^OgrMKPqXs4P?F9)nd}n(Was;dXd=gCO$N)eN%ufm-tLZ3%@Plj%*TQRJiz zf^Z?q=XxebKr`7D#-4EqMy4$c8~#JA&Q$Njrgn_obv05!=CobbJzU6e_o8=l#$y(p zvI30uwfx!-XL-2KtGF&n)D?@eNfslEWC#gH-q=r=mFu&1>>~sPUE+t{L@V5sn%P}K z0K67gstiGnmY9QXIK$19#W1nWoFry~itW1d_`{GV@1mpx+7?!@99qXerUlv0xE;f{ z?k{)T=y)yfUwpgkq;}iQVz!V_n=UeapB=(?57X1%lq-)rj%Q4jtS^FN$;wHmVaCDVuw&g zqewW&)x6{d$|WF-Ww6?wVGkGvO^6ZMMs@oxj?DI;;}T=Sybuf_W+h0d5escn5dZprLtl@iCw{hD;C8ucAKJR zqIFsAkpMo4>{n`uO8^cP?U2hFypx6TqZ3^3rkC@&vCSEV5^fdeTtu6*OIZDVb2ij+n}AiM}jf`h23c*IRqoH4GOQS=Je|RtR=_!}7&z?Sr3E?IPh?D4=DWB^h-nR79IfMPqN-*M-Jq4ahPErMCB zW)iKJ<{qBJ&n~u3z36t!B^nA3Fo)MJ4o|DuUlufZb|Go0iKB@4(ca>0GQdEID;j8Ro;Zw^DNQNO{}($Vtn( z{Ye0$L<^sCSAQd<(9!rDEY|{_P^|mfd;cAr$2i)3jkUO1>$UZOG#9zKc+lPR@aoBp z1MUxOH?NV6?(5VYljR3}t2^e=n5hUbjA)P6Aw3IRPY2G%MI}Q2GAEYNB!rXg1?}y7 z@EG0bo-Ea=ukCC`4z-8Foj52o84p>6qo|7-7wu{;(flZAv67I{N=Y2@oopRS>QtTC zhFzMQFF1M(a6CA#a>8_Gvt7UF@mzU|uQ?|T-Hb2YGS)nl;PXfG6Bs!R)s<7IKCqz) z_)!ngj`x}T?$gr~@ScZ%-vuzTrrBexM%ej~4R*BLChly*b=k=;c0^ja6mRk+uc)Je zZ{}<-re5QTzF|iDQV@U$!d4SG+Lv<-de`vYHsBMAZMjA^^*F5Txm)pr*} zzRgO3k#uZ{eUybAAIYuatxj%m%P#8B5Qb*1KVA5us$vgx}Hr;01+6xEvrtvmE8Ru8?$uDMNznb6z1=%7_x|BJ6zF4#Koa# zlGt%&6ql8N+R4#>KgAg+gOHptN3F=LmS0i9%NG1}r#R)(wCRNi?M|)po9K*(UBgO2 zU7`;J-Cd*P27v-H+O|>yP8Ms5sPt;#t13yV5C%Hwc*WtRt;@-$S6PWB1(i8@)&p(2 znRySBm>(rvNmU{5E!RDFAg|5(#eps`vC5nqkF^HgaIc1n;AitMJTvL# zf*bUZJ4{;jzok=J{rZBRUhbkYOg)ALvz_9mKJqbvTe4Z%nDMbQkP29)UsK->BycnD zV_&^85Zr5{A)u4lK$cDuJ3WpjXIk$b;#sxPRveDqa%+q^RAKo~7!$$+kA;+*+%0D# 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 literal 0 HcmV?d00001 From 7813afef95507057c6a236262597ae0e89dc72e6 Mon Sep 17 00:00:00 2001 From: sanford schaffer Date: Thu, 9 Apr 2026 14:17:18 -0400 Subject: [PATCH 2/3] adding a conditional for local verses test-mage space instances re path of sec-error-png canned file.. --- .../observations/adapters.observations.controllers.web.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/service/src/adapters/observations/adapters.observations.controllers.web.ts b/service/src/adapters/observations/adapters.observations.controllers.web.ts index 7050e65cc..08a16f626 100644 --- a/service/src/adapters/observations/adapters.observations.controllers.web.ts +++ b/service/src/adapters/observations/adapters.observations.controllers.web.ts @@ -128,7 +128,9 @@ async function handleFileUpload( const originalBuffer = await readStreamToBuffer(fileStream) const finalBuffer = await scanFileIfNeeded(originalBuffer, info.filename, uploadErrors) if (!finalBuffer) { - const secErrorPath = path.join(__dirname, '..', '..', '..', 'src', 'assets', 'SecError.png') + const secErrorPath = process.env.NODE_ENV === 'production' + ? path.join(__dirname, '..', 'assets', 'SecError.png') + : path.join(__dirname, '..', '..', '..', 'src', 'assets', 'SecError.png') const placeholderBuffer = await readStreamToBuffer(fs.createReadStream(secErrorPath)) await storeAttachment(placeholderBuffer, { filename: info.filename, mimeType: info.mimeType, encoding: info.encoding }, req, createAppRequest, app, attachmentsJson, uploadErrors) const stored = attachmentsJson[attachmentsJson.length - 1] From f433d9d94c1af3235f06f2c5f4e910ce410d6840 Mon Sep 17 00:00:00 2001 From: sanford schaffer Date: Tue, 14 Apr 2026 11:39:58 -0400 Subject: [PATCH 3/3] adding test-mage changes to allow testing in test space.. --- .../src/adapters/observations/adapters.attachments.clamav.ts | 3 ++- .../observations/adapters.observations.controllers.web.ts | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/service/src/adapters/observations/adapters.attachments.clamav.ts b/service/src/adapters/observations/adapters.attachments.clamav.ts index 61d5baf4a..188f7800d 100644 --- a/service/src/adapters/observations/adapters.attachments.clamav.ts +++ b/service/src/adapters/observations/adapters.attachments.clamav.ts @@ -1,7 +1,8 @@ import { Readable, PassThrough } from 'stream'; import net from 'net'; -const CLAMAV_HOST = process.env.CLAMAV_HOST || 'localhost'; +// LOCAL: const CLAMAV_HOST = process.env.CLAMAV_HOST || 'localhost'; +const CLAMAV_HOST = 'clamav.clamav.svc.cluster.local'; const CLAMAV_PORT = Number(process.env.CLAMAV_PORT) || 3310; const CLAMAV_TIMEOUT_MS = 60_000; const CLAMAV_RETRIES = 3; diff --git a/service/src/adapters/observations/adapters.observations.controllers.web.ts b/service/src/adapters/observations/adapters.observations.controllers.web.ts index 08a16f626..98441f0d7 100644 --- a/service/src/adapters/observations/adapters.observations.controllers.web.ts +++ b/service/src/adapters/observations/adapters.observations.controllers.web.ts @@ -62,7 +62,7 @@ async function scanFileIfNeeded( filename: string, uploadErrors: any[] ): Promise { - if (!process.env.CLAM_AV_URL && !process.env.CLAMAV_HOST) return buffer + // if (!process.env.CLAM_AV_URL && !process.env.CLAMAV_HOST) return buffer try { const result = await scanAttachmentWithClamAV(Readable.from(buffer)) @@ -130,7 +130,8 @@ async function handleFileUpload( if (!finalBuffer) { const secErrorPath = process.env.NODE_ENV === 'production' ? path.join(__dirname, '..', 'assets', 'SecError.png') - : path.join(__dirname, '..', '..', '..', 'src', 'assets', 'SecError.png') + : path.join(__dirname, '..', '..', '..', 'lib', 'assets', 'SecError.png') + //LOCAL : path.join(__dirname, '..', '..', '..', 'dir', 'assets', 'SecError.png') const placeholderBuffer = await readStreamToBuffer(fs.createReadStream(secErrorPath)) await storeAttachment(placeholderBuffer, { filename: info.filename, mimeType: info.mimeType, encoding: info.encoding }, req, createAppRequest, app, attachmentsJson, uploadErrors) const stored = attachmentsJson[attachmentsJson.length - 1]