-
-
Notifications
You must be signed in to change notification settings - Fork 905
Adding antivirus scanning capabilities with ClamAV via API calls and Add User fucntion #454
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 7 commits
4d8652d
a30e543
e67c1b3
4c28300
4697b15
718f87e
179abac
6b2c737
48d1ce5
f2f48c9
d9bb395
dff8926
e629fca
5519374
6fe7899
d4be44c
3420464
8ec2a22
55fa5ae
4fd9862
1cdbd9e
60d4a48
97ddf01
96e7123
5083935
3df62fc
09e0e84
9c30944
61894a7
da67096
5e19e84
2d5c8a0
e4624a8
3bcb511
dbd1805
86e67d4
8eda62f
2d954a4
a5fbe90
ddb7292
928655c
58fafe8
089bc8e
1c8e084
89c5b04
9cd47ce
5b25ed8
5a0f024
f3a40ea
51e4911
2075e51
fdb0c0d
2b7491a
3e03af3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -16,5 +16,32 @@ services: | |||||||||||||||||||||||||
| # - HIDE_HISTORY=true # hides the history tab in the web interface, defaults to false | ||||||||||||||||||||||||||
| - TZ=Europe/Stockholm # set your timezone, defaults to UTC | ||||||||||||||||||||||||||
| # - UNAUTHENTICATED_USER_SHARING=true # for use with ALLOW_UNAUTHENTICATED=true to share history with all unauthenticated users / devices | ||||||||||||||||||||||||||
| - CLAMAV_URL=http://clam_av_api:3000/api/v1/scan | ||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Service hostname mismatch: Prompt for AI agents
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Hostname mismatch: Prompt for AI agents
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Service name mismatch: Prompt for AI agents
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Hostname mismatch: Prompt for AI agents
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Service name mismatch: Prompt for AI agents
Suggested change
|
||||||||||||||||||||||||||
| ports: | ||||||||||||||||||||||||||
| - 3000:3000 | ||||||||||||||||||||||||||
| - 8080:3000 | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| clamav-rest-api: | ||||||||||||||||||||||||||
| image: benzino77/clamav-rest-api:latest | ||||||||||||||||||||||||||
| container_name: clamav-rest-api | ||||||||||||||||||||||||||
| restart: unless-stopped | ||||||||||||||||||||||||||
| environment: | ||||||||||||||||||||||||||
| - NODE_ENV=production | ||||||||||||||||||||||||||
| # field name expected in the multipart form | ||||||||||||||||||||||||||
| - APP_FORM_KEY=FILES | ||||||||||||||||||||||||||
| # talk to your existing ClamAV daemon | ||||||||||||||||||||||||||
| - CLAMD_IP=CLAMAV_server_IP | ||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Prompt for AI agents
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Placeholder value not configured: Prompt for AI agents
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Placeholder value: Prompt for AI agents
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Placeholder value Prompt for AI agents
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Placeholder value not replaced: Prompt for AI agents
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Placeholder value not replaced: Prompt for AI agents
Suggested change
|
||||||||||||||||||||||||||
| - CLAMD_PORT=3310 | ||||||||||||||||||||||||||
| # max allowed file size (here: 250 MB) | ||||||||||||||||||||||||||
| - APP_MAX_FILE_SIZE=262144000 | ||||||||||||||||||||||||||
| ports: | ||||||||||||||||||||||||||
| # outside:inside | ||||||||||||||||||||||||||
| - "3000:3000" | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| clamav: | ||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P0: YAML indentation error: The Prompt for AI agents
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P0: YAML syntax error: The Prompt for AI agents
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P0: YAML indentation error: Prompt for AI agents
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P0: YAML indentation error: This line has 3 spaces instead of 2, which breaks YAML parsing. Service definitions must be consistently indented. Prompt for AI agents
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P0: YAML indentation error: Prompt for AI agents
Suggested change
|
||||||||||||||||||||||||||
| image: clamav/clamav:latest | ||||||||||||||||||||||||||
| container_name: clamav | ||||||||||||||||||||||||||
| restart: unless-stopped | ||||||||||||||||||||||||||
| ports: | ||||||||||||||||||||||||||
| - "3310:3310" | ||||||||||||||||||||||||||
| environment: | ||||||||||||||||||||||||||
| - CLAMAV_NO_FRESHCLAMD=false | ||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -33,7 +33,6 @@ dropZone.addEventListener("drop", (e) => { | |||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Extracted handleFile function for reusability in drag-and-drop and file input | ||||||||||||||||||||||||||||
| function handleFile(file) { | ||||||||||||||||||||||||||||
| const fileList = document.querySelector("#file-list"); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
|
@@ -128,14 +127,11 @@ const updateSearchBar = () => { | |||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| convertToInput.addEventListener("search", () => { | ||||||||||||||||||||||||||||
| // when the user clears the search bar using the 'x' button | ||||||||||||||||||||||||||||
| convertButton.disabled = true; | ||||||||||||||||||||||||||||
| formatSelected = false; | ||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| convertToInput.addEventListener("blur", (e) => { | ||||||||||||||||||||||||||||
| // Keep the popup open even when clicking on a target button | ||||||||||||||||||||||||||||
| // for a split second to allow the click to go through | ||||||||||||||||||||||||||||
| if (e?.relatedTarget?.classList?.contains("target")) { | ||||||||||||||||||||||||||||
| convertToPopup.classList.add("hidden"); | ||||||||||||||||||||||||||||
| convertToPopup.classList.remove("flex"); | ||||||||||||||||||||||||||||
|
|
@@ -152,7 +148,6 @@ const updateSearchBar = () => { | |||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Add a 'change' event listener to the file input element | ||||||||||||||||||||||||||||
| fileInput.addEventListener("change", (e) => { | ||||||||||||||||||||||||||||
| const files = e.target.files; | ||||||||||||||||||||||||||||
| for (const file of files) { | ||||||||||||||||||||||||||||
|
|
@@ -165,21 +160,19 @@ const setTitle = () => { | |||||||||||||||||||||||||||
| title.textContent = `Convert ${fileType ? `.${fileType}` : ""}`; | ||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Add a onclick for the delete button | ||||||||||||||||||||||||||||
| // eslint-disable-next-line @typescript-eslint/no-unused-vars | ||||||||||||||||||||||||||||
| const deleteRow = (target) => { | ||||||||||||||||||||||||||||
| const filename = target.parentElement.parentElement.children[0].textContent; | ||||||||||||||||||||||||||||
| const row = target.parentElement.parentElement; | ||||||||||||||||||||||||||||
| row.remove(); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // remove from fileNames | ||||||||||||||||||||||||||||
| const index = fileNames.indexOf(filename); | ||||||||||||||||||||||||||||
| fileNames.splice(index, 1); | ||||||||||||||||||||||||||||
| if (index !== -1) { | ||||||||||||||||||||||||||||
| fileNames.splice(index, 1); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // reset fileInput | ||||||||||||||||||||||||||||
| fileInput.value = ""; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // if fileNames is empty, reset fileType | ||||||||||||||||||||||||||||
| if (fileNames.length === 0) { | ||||||||||||||||||||||||||||
| fileType = null; | ||||||||||||||||||||||||||||
| fileInput.removeAttribute("accept"); | ||||||||||||||||||||||||||||
|
|
@@ -209,20 +202,104 @@ const uploadFile = (file) => { | |||||||||||||||||||||||||||
| xhr.open("POST", `${webroot}/upload`, true); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| xhr.onload = () => { | ||||||||||||||||||||||||||||
| let data = JSON.parse(xhr.responseText); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| pendingFiles -= 1; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // 🔍 1) Log exactly what the browser got | ||||||||||||||||||||||||||||
| console.log("Upload raw response:", xhr.status, xhr.responseText); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| let data = {}; | ||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||
| data = JSON.parse(xhr.responseText || "{}"); | ||||||||||||||||||||||||||||
| } catch (e) { | ||||||||||||||||||||||||||||
| console.error("Failed to parse upload response:", e, xhr.responseText); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // 🔍 2) Compute an "infected" flag as robustly as possible | ||||||||||||||||||||||||||||
| const isInfected = | ||||||||||||||||||||||||||||
| (typeof data === "object" && | ||||||||||||||||||||||||||||
| data !== null && | ||||||||||||||||||||||||||||
| (data.infected === true || | ||||||||||||||||||||||||||||
| data.infected === "true" || | ||||||||||||||||||||||||||||
| (typeof data.message === "string" && | ||||||||||||||||||||||||||||
| data.message.toLowerCase().includes("infected file found")))) || | ||||||||||||||||||||||||||||
| (typeof xhr.responseText === "string" && | ||||||||||||||||||||||||||||
| xhr.responseText.toLowerCase().includes("infected file found")); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // 🔴 3) If backend reports infection, show popup and stop | ||||||||||||||||||||||||||||
| if (xhr.status >= 200 && xhr.status < 300 && isInfected) { | ||||||||||||||||||||||||||||
| const infectedFiles = data.infectedFiles || []; | ||||||||||||||||||||||||||||
| const details = infectedFiles | ||||||||||||||||||||||||||||
| .map((f) => | ||||||||||||||||||||||||||||
| `${f.name}: ${ | ||||||||||||||||||||||||||||
| Array.isArray(f.viruses) && f.viruses.length | ||||||||||||||||||||||||||||
| ? f.viruses.join(", ") | ||||||||||||||||||||||||||||
| : "malware detected" | ||||||||||||||||||||||||||||
| }`, | ||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||
| .join("\n"); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| alert( | ||||||||||||||||||||||||||||
| "⚠️ Infected file found. Conversion will be aborted.\n\n" + | ||||||||||||||||||||||||||||
| (details ? "Details:\n" + details : ""), | ||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Remove row for this file | ||||||||||||||||||||||||||||
| if (file.htmlRow && file.htmlRow.remove) { | ||||||||||||||||||||||||||||
| file.htmlRow.remove(); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Remove from internal list | ||||||||||||||||||||||||||||
| const idx = fileNames.indexOf(file.name); | ||||||||||||||||||||||||||||
| if (idx !== -1) { | ||||||||||||||||||||||||||||
| fileNames.splice(idx, 1); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if (fileNames.length === 0) { | ||||||||||||||||||||||||||||
| fileType = null; | ||||||||||||||||||||||||||||
| fileInput.removeAttribute("accept"); | ||||||||||||||||||||||||||||
| setTitle(); | ||||||||||||||||||||||||||||
| convertButton.disabled = true; | ||||||||||||||||||||||||||||
| } else if (pendingFiles === 0 && formatSelected) { | ||||||||||||||||||||||||||||
| convertButton.disabled = false; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| convertButton.textContent = "Convert"; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const progressbar = file.htmlRow?.getElementsByTagName("progress"); | ||||||||||||||||||||||||||||
| if (progressbar && progressbar[0]?.parentElement) { | ||||||||||||||||||||||||||||
| progressbar[0].parentElement.remove(); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Generic HTTP error | ||||||||||||||||||||||||||||
| if (xhr.status !== 200) { | ||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Failed upload doesn't remove file from Prompt for AI agents |
||||||||||||||||||||||||||||
| console.error("Upload failed:", xhr.status, xhr.responseText); | ||||||||||||||||||||||||||||
| alert("Upload failed. Please try again."); | ||||||||||||||||||||||||||||
| convertButton.disabled = false; | ||||||||||||||||||||||||||||
| convertButton.textContent = "Upload failed"; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const progressbar = file.htmlRow.getElementsByTagName("progress"); | ||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Generic HTTP error handler doesn't remove the file from Prompt for AI agents
Suggested change
|
||||||||||||||||||||||||||||
| if (progressbar[0]?.parentElement) { | ||||||||||||||||||||||||||||
| progressbar[0].parentElement.remove(); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Clean upload | ||||||||||||||||||||||||||||
| if (pendingFiles === 0) { | ||||||||||||||||||||||||||||
| if (formatSelected) { | ||||||||||||||||||||||||||||
| if (formatSelected && fileNames.length > 0) { | ||||||||||||||||||||||||||||
| convertButton.disabled = false; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| convertButton.textContent = "Convert"; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| //Remove the progress bar when upload is done | ||||||||||||||||||||||||||||
| let progressbar = file.htmlRow.getElementsByTagName("progress"); | ||||||||||||||||||||||||||||
| progressbar[0].parentElement.remove(); | ||||||||||||||||||||||||||||
| console.log(data); | ||||||||||||||||||||||||||||
| const progressbar = file.htmlRow.getElementsByTagName("progress"); | ||||||||||||||||||||||||||||
| if (progressbar[0]?.parentElement) { | ||||||||||||||||||||||||||||
| progressbar[0].parentElement.remove(); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| console.log("Upload parsed response:", data); | ||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| xhr.upload.onprogress = (e) => { | ||||||||||||||||||||||||||||
|
|
@@ -231,11 +308,16 @@ const uploadFile = (file) => { | |||||||||||||||||||||||||||
| console.log(`upload progress (${file.name}):`, (100 * sent) / total); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| let progressbar = file.htmlRow.getElementsByTagName("progress"); | ||||||||||||||||||||||||||||
| progressbar[0].value = (100 * sent) / total; | ||||||||||||||||||||||||||||
| if (progressbar[0]) { | ||||||||||||||||||||||||||||
| progressbar[0].value = (100 * sent) / total; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| xhr.onerror = (e) => { | ||||||||||||||||||||||||||||
| console.log(e); | ||||||||||||||||||||||||||||
| console.log("XHR error:", e); | ||||||||||||||||||||||||||||
| alert("Upload failed due to a network error."); | ||||||||||||||||||||||||||||
| convertButton.disabled = false; | ||||||||||||||||||||||||||||
| convertButton.textContent = "Upload failed"; | ||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| xhr.send(formData); | ||||||||||||||||||||||||||||
|
|
@@ -249,3 +331,4 @@ formConvert.addEventListener("submit", () => { | |||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| updateSearchBar(); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| // src/helpers/avToggle.ts | ||
|
|
||
| import { | ||
| ANTIVIRUS_ENABLED_DEFAULT, | ||
| CLAMAV_CONFIGURED, | ||
| } from "./env"; | ||
|
|
||
| let antivirusEnabled = ANTIVIRUS_ENABLED_DEFAULT; | ||
|
|
||
| /** | ||
| * Is ClamAV configured at all (CLAMAV_URL set)? | ||
| */ | ||
| export function isAntivirusAvailable(): boolean { | ||
| return CLAMAV_CONFIGURED; | ||
| } | ||
|
|
||
| /** | ||
| * Is antivirus scanning currently enabled (and available)? | ||
| */ | ||
| export function isAntivirusEnabled(): boolean { | ||
| return CLAMAV_CONFIGURED && antivirusEnabled; | ||
| } | ||
|
|
||
| /** | ||
| * Change current antivirus enabled/disabled state. | ||
| * If CLAMAV is not configured, this is effectively a no-op and remains false. | ||
| */ | ||
| export function setAntivirusEnabled(enabled: boolean): void { | ||
| if (!CLAMAV_CONFIGURED) { | ||
| antivirusEnabled = false; | ||
| return; | ||
| } | ||
| antivirusEnabled = enabled; | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,45 @@ | ||||||||||||||||||||||||||
| // src/pages/antivirus.tsx | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| import { Elysia, t } from "elysia"; | ||||||||||||||||||||||||||
| import { userService } from "./user"; | ||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||
| isAntivirusAvailable, | ||||||||||||||||||||||||||
| isAntivirusEnabled, | ||||||||||||||||||||||||||
| setAntivirusEnabled, | ||||||||||||||||||||||||||
| } from "../helpers/avToggle"; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| export const antivirus = new Elysia() | ||||||||||||||||||||||||||
| .use(userService) | ||||||||||||||||||||||||||
| // Get current AV status | ||||||||||||||||||||||||||
| .get("/api/antivirus", () => { | ||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P0: The new /api/antivirus GET/POST endpoints are exposed without Prompt for AI agents✅ Addressed in |
||||||||||||||||||||||||||
| const available = isAntivirusAvailable(); | ||||||||||||||||||||||||||
| const enabled = isAntivirusEnabled(); | ||||||||||||||||||||||||||
| return { available, enabled }; | ||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||
| // Update AV status | ||||||||||||||||||||||||||
| .post( | ||||||||||||||||||||||||||
| "/api/antivirus", | ||||||||||||||||||||||||||
| ({ body }) => { | ||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Security: POST Prompt for AI agents✅ Addressed in |
||||||||||||||||||||||||||
| const { enabled } = body; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if (!isAntivirusAvailable()) { | ||||||||||||||||||||||||||
| // CLAMAV_URL missing: force disabled and report unavailable | ||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||
| available: false, | ||||||||||||||||||||||||||
| enabled: false, | ||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| setAntivirusEnabled(Boolean(enabled)); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||
| available: true, | ||||||||||||||||||||||||||
| enabled: isAntivirusEnabled(), | ||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| body: t.Object({ | ||||||||||||||||||||||||||
| enabled: t.Boolean(), | ||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||
|
Comment on lines
+88
to
+92
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Missing authentication on POST Prompt for AI agents
Suggested change
✅ Addressed in |
||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P1: Convertx injects
CLAMAV_URLpointing to a non-existent Docker hostname (clam_av_api), so every server-side ClamAV fetch fails and uploads are never actually scanned, bypassing the antivirus workflow.Prompt for AI agents