Skip to content

Conversation

@Kosztyk
Copy link

@Kosztyk Kosztyk commented Dec 2, 2025

new function will scan every file uploaded and if an infected file is found the action will be aborted

Screenshot 2025-12-02 at 10 46 18

for this 2 new containers are needed, CLAMAV and CLAMAV API

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
- CLAMD_PORT=3310
# max allowed file size (here: 250 MB)
- APP_MAX_FILE_SIZE=262144000
ports:
# outside:inside
- "3000:3000"

clamav:
image: clamav/clamav:latest
container_name: clamav
restart: unless-stopped
ports:
- "3310:3310"
environment:
- CLAMAV_NO_FRESHCLAMD=false

In ConvertX compose file we need a new field in environment - CLAMAV_URL=http://clam_av_api:3000/api/v1/scan


Summary by cubic

Add antivirus scanning for file uploads using ClamAV via a REST API; infected files are blocked before saving, the UI warns the user, and scanning can be toggled at runtime. Also adds a simple light/dark theme toggle and admin user management.

  • New Features

    • Scan each uploaded file via CLAMAV_URL before writing to disk.
    • If malware is found, return infected=true with virus details; infected files are not saved and conversion is aborted.
    • Frontend shows an alert, removes the file row, and keeps Convert disabled until only clean files remain.
    • Added logging; if ClamAV is unreachable, uploads proceed and are logged as a fail-open.
    • New /api/antivirus to get/set AV status; scanning can be enabled/disabled at runtime.
    • Added light/dark theme toggle with saved preference and a small theme-init script.
    • User roles and admin tools: first user becomes admin; admins can add/edit/delete users from Account.
  • Migration

    • Add clamav-rest-api and clamav services to docker compose.
    • Set CLAMAV_URL to the REST API endpoint (e.g., http://clamav-rest-api:3000/api/v1/scan) in the app service.
    • Configure clamav-rest-api to point to the clamav daemon (CLAMD_IP and CLAMD_PORT=3310).
    • App external port changed to 8080.
    • Optional: set ANTIVIRUS_ENABLED_DEFAULT=false to start with scanning disabled.

Written for commit 1cdbd9e. Summary will update automatically on new commits.

added clav av scanning capabilities
added clamav scanning capabilities
added clamav capabilities
Added ClamAV REST API service and updated ports.
@Kosztyk Kosztyk changed the title Adding antifirus scanning capabilities with ClamAV via API calls Adding antivirus scanning capabilities with ClamAV via API calls Dec 2, 2025
Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 4 files

Prompt for AI agents (all 2 issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="compose.yaml">

<violation number="1" location="compose.yaml:19">
P1: Convertx injects `CLAMAV_URL` pointing 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.</violation>

<violation number="2" location="compose.yaml:32">
P1: `CLAMD_IP` is left as the placeholder `CLAMAV_server_IP` even though the ClamAV daemon service defined below is named `clamav`. The REST API container will fail to connect to the daemon, so scans will always fail.</violation>
</file>

Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Ask questions if you need clarification on any suggestion

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

# field name expected in the multipart form
- APP_FORM_KEY=FILES
# talk to your existing ClamAV daemon
- CLAMD_IP=CLAMAV_server_IP
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: CLAMD_IP is left as the placeholder CLAMAV_server_IP even though the ClamAV daemon service defined below is named clamav. The REST API container will fail to connect to the daemon, so scans will always fail.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At compose.yaml, line 32:

<comment>`CLAMD_IP` is left as the placeholder `CLAMAV_server_IP` even though the ClamAV daemon service defined below is named `clamav`. The REST API container will fail to connect to the daemon, so scans will always fail.</comment>

<file context>
@@ -16,5 +16,32 @@ services:
+      # field name expected in the multipart form
+      - APP_FORM_KEY=FILES
+      # talk to your existing ClamAV daemon
+      - CLAMD_IP=CLAMAV_server_IP
+      - CLAMD_PORT=3310
+      # max allowed file size (here: 250 MB)
</file context>
Suggested change
- CLAMD_IP=CLAMAV_server_IP
- CLAMD_IP=clamav
Fix with Cubic

# - 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
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Convertx injects CLAMAV_URL pointing 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
Check if this issue is valid — if so, understand the root cause and fix it. At compose.yaml, line 19:

<comment>Convertx injects `CLAMAV_URL` pointing 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.</comment>

<file context>
@@ -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
     ports:
-      - 3000:3000
</file context>
Suggested change
- CLAMAV_URL=http://clam_av_api:3000/api/v1/scan
+ - CLAMAV_URL=http://clamav-rest-api:3000/api/v1/scan
Fix with Cubic

@C4illin
Copy link
Owner

C4illin commented Dec 3, 2025

Nice idea! I would like this function to be behind a toggle. There isn't really that many things that could be infected since most conversions are images of different kinds.

@Kosztyk
Copy link
Author

Kosztyk commented Dec 3, 2025

Hi,

that will be also a good option, if you can help implementing it will be amassing, unfortunately something like that is over my skills.

Thank you

@C4illin
Copy link
Owner

C4illin commented Dec 3, 2025

I have quite a lot to do at the moment sorry, but the toggle is just implementing an environment variable. Take a look at how the other ones are done if you want :)

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 6 files

Prompt for AI agents (all 3 issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="compose.yaml">

<violation number="1" location="compose.yaml:19">
P1: Service hostname mismatch: `clam_av_api` should be `clamav-rest-api` to match the actual service name defined in this compose file. Docker Compose uses service names for internal DNS resolution.</violation>

<violation number="2" location="compose.yaml:32">
P1: Placeholder value not configured: `CLAMAV_server_IP` should be `clamav` to reference the ClamAV daemon service defined in this compose file. Docker Compose provides DNS resolution using service names.</violation>

<violation number="3" location="compose.yaml:40">
P0: YAML indentation error: The `clamav` service has 3 spaces of indentation instead of 2, which will cause a YAML parsing error and prevent the compose file from being loaded.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

# field name expected in the multipart form
- APP_FORM_KEY=FILES
# talk to your existing ClamAV daemon
- CLAMD_IP=CLAMAV_server_IP
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Placeholder value not configured: CLAMAV_server_IP should be clamav to reference the ClamAV daemon service defined in this compose file. Docker Compose provides DNS resolution using service names.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At compose.yaml, line 32:

<comment>Placeholder value not configured: `CLAMAV_server_IP` should be `clamav` to reference the ClamAV daemon service defined in this compose file. Docker Compose provides DNS resolution using service names.</comment>

<file context>
@@ -16,5 +16,32 @@ services:
+      # field name expected in the multipart form
+      - APP_FORM_KEY=FILES
+      # talk to your existing ClamAV daemon
+      - CLAMD_IP=CLAMAV_server_IP
+      - CLAMD_PORT=3310
+      # max allowed file size (here: 250 MB)
</file context>
Suggested change
- CLAMD_IP=CLAMAV_server_IP
- CLAMD_IP=clamav
Fix with Cubic

# - 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
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Service hostname mismatch: clam_av_api should be clamav-rest-api to match the actual service name defined in this compose file. Docker Compose uses service names for internal DNS resolution.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At compose.yaml, line 19:

<comment>Service hostname mismatch: `clam_av_api` should be `clamav-rest-api` to match the actual service name defined in this compose file. Docker Compose uses service names for internal DNS resolution.</comment>

<file context>
@@ -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
     ports:
-      - 3000:3000
</file context>
Suggested change
- CLAMAV_URL=http://clam_av_api:3000/api/v1/scan
- CLAMAV_URL=http://clamav-rest-api:3000/api/v1/scan
Fix with Cubic

# outside:inside
- "3000:3000"

clamav:
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0: YAML indentation error: The clamav service has 3 spaces of indentation instead of 2, which will cause a YAML parsing error and prevent the compose file from being loaded.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At compose.yaml, line 40:

<comment>YAML indentation error: The `clamav` service has 3 spaces of indentation instead of 2, which will cause a YAML parsing error and prevent the compose file from being loaded.</comment>

<file context>
@@ -16,5 +16,32 @@ services:
+      # outside:inside
+      - &quot;3000:3000&quot;
+
+   clamav:
+    image: clamav/clamav:latest
+    container_name: clamav
</file context>
Suggested change
clamav:
clamav:
Fix with Cubic

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found across 5 files

Prompt for AI agents (all 4 issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="src/pages/upload.tsx">

<violation number="1" location="src/pages/upload.tsx:163">
P2: File overwrite risk when multiple files sanitize to the same name. If two files have identical sanitized names (or invalid names defaulting to &quot;file&quot;), later files silently overwrite earlier ones. Consider appending a suffix or checking for existing files.</violation>
</file>

<file name="compose.yaml">

<violation number="1" location="compose.yaml:19">
P1: Hostname mismatch: `clam_av_api` should be `clamav-rest-api` to match the service name. Docker Compose uses service names as DNS hostnames within the network.</violation>

<violation number="2" location="compose.yaml:32">
P1: Placeholder value: `CLAMAV_server_IP` should be `clamav` to reference the ClamAV daemon service defined in this compose file.</violation>

<violation number="3" location="compose.yaml:40">
P0: YAML syntax error: The `clamav:` service has 3 spaces indentation instead of 2 spaces like other services. This will cause a parsing error.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR


for (const file of files) {
const originalName = (file as any).name ?? "upload";
const sanitizedFileName = sanitize(originalName) || "file";
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: File overwrite risk when multiple files sanitize to the same name. If two files have identical sanitized names (or invalid names defaulting to "file"), later files silently overwrite earlier ones. Consider appending a suffix or checking for existing files.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pages/upload.tsx, line 163:

<comment>File overwrite risk when multiple files sanitize to the same name. If two files have identical sanitized names (or invalid names defaulting to &quot;file&quot;), later files silently overwrite earlier ones. Consider appending a suffix or checking for existing files.</comment>

<file context>
@@ -1,42 +1,219 @@
+
+        for (const file of files) {
+          const originalName = (file as any).name ?? &quot;upload&quot;;
+          const sanitizedFileName = sanitize(originalName) || &quot;file&quot;;
+          console.log(&quot;[Upload] Handling file:&quot;, originalName, &quot;=&gt; sanitized:&quot;, sanitizedFileName);
+
</file context>
Fix with Cubic

# field name expected in the multipart form
- APP_FORM_KEY=FILES
# talk to your existing ClamAV daemon
- CLAMD_IP=CLAMAV_server_IP
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Placeholder value: CLAMAV_server_IP should be clamav to reference the ClamAV daemon service defined in this compose file.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At compose.yaml, line 32:

<comment>Placeholder value: `CLAMAV_server_IP` should be `clamav` to reference the ClamAV daemon service defined in this compose file.</comment>

<file context>
@@ -16,5 +16,32 @@ services:
+      # field name expected in the multipart form
+      - APP_FORM_KEY=FILES
+      # talk to your existing ClamAV daemon
+      - CLAMD_IP=CLAMAV_server_IP
+      - CLAMD_PORT=3310
+      # max allowed file size (here: 250 MB)
</file context>
Suggested change
- CLAMD_IP=CLAMAV_server_IP
- CLAMD_IP=clamav
Fix with Cubic

# - 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
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Hostname mismatch: clam_av_api should be clamav-rest-api to match the service name. Docker Compose uses service names as DNS hostnames within the network.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At compose.yaml, line 19:

<comment>Hostname mismatch: `clam_av_api` should be `clamav-rest-api` to match the service name. Docker Compose uses service names as DNS hostnames within the network.</comment>

<file context>
@@ -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
     ports:
-      - 3000:3000
</file context>
Suggested change
- CLAMAV_URL=http://clam_av_api:3000/api/v1/scan
- CLAMAV_URL=http://clamav-rest-api:3000/api/v1/scan
Fix with Cubic

# outside:inside
- "3000:3000"

clamav:
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0: YAML syntax error: The clamav: service has 3 spaces indentation instead of 2 spaces like other services. This will cause a parsing error.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At compose.yaml, line 40:

<comment>YAML syntax error: The `clamav:` service has 3 spaces indentation instead of 2 spaces like other services. This will cause a parsing error.</comment>

<file context>
@@ -16,5 +16,32 @@ services:
+      # outside:inside
+      - &quot;3000:3000&quot;
+
+   clamav:
+    image: clamav/clamav:latest
+    container_name: clamav
</file context>
Suggested change
clamav:
clamav:
Fix with Cubic

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 issues found across 7 files

Prompt for AI agents (all 5 issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="compose.yaml">

<violation number="1" location="compose.yaml:19">
P1: Service name mismatch: `clam_av_api` doesn&#39;t match the defined service name `clamav-rest-api`. Docker Compose uses service names for DNS resolution, so this URL will fail to resolve.</violation>

<violation number="2" location="compose.yaml:32">
P1: Placeholder value `CLAMAV_server_IP` needs to be replaced with the actual ClamAV service name. Since the `clamav` service is in the same compose file, use `clamav` as the hostname.</violation>

<violation number="3" location="compose.yaml:40">
P0: YAML indentation error: `clamav:` has 3 spaces instead of 2. This will cause a YAML parsing error and prevent Docker Compose from running.</violation>
</file>

<file name="public/script.js">

<violation number="1" location="public/script.js:277">
P2: Failed upload doesn&#39;t remove file from `fileNames` array or UI. This leaves inconsistent state where user could attempt to convert a file that failed to upload.</violation>
</file>

<file name="src/pages/antivirus.tsx">

<violation number="1" location="src/pages/antivirus.tsx:22">
P1: Security: POST `/api/antivirus` endpoint lacks authentication. This allows unauthenticated users to disable antivirus scanning. Add `user` to the handler context to require authentication, similar to other protected endpoints like `deleteFile.tsx`.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

# field name expected in the multipart form
- APP_FORM_KEY=FILES
# talk to your existing ClamAV daemon
- CLAMD_IP=CLAMAV_server_IP
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Placeholder value CLAMAV_server_IP needs to be replaced with the actual ClamAV service name. Since the clamav service is in the same compose file, use clamav as the hostname.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At compose.yaml, line 32:

<comment>Placeholder value `CLAMAV_server_IP` needs to be replaced with the actual ClamAV service name. Since the `clamav` service is in the same compose file, use `clamav` as the hostname.</comment>

<file context>
@@ -16,5 +16,32 @@ services:
+      # field name expected in the multipart form
+      - APP_FORM_KEY=FILES
+      # talk to your existing ClamAV daemon
+      - CLAMD_IP=CLAMAV_server_IP
+      - CLAMD_PORT=3310
+      # max allowed file size (here: 250 MB)
</file context>
Suggested change
- CLAMD_IP=CLAMAV_server_IP
- CLAMD_IP=clamav
Fix with Cubic

# - 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
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Service name mismatch: clam_av_api doesn't match the defined service name clamav-rest-api. Docker Compose uses service names for DNS resolution, so this URL will fail to resolve.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At compose.yaml, line 19:

<comment>Service name mismatch: `clam_av_api` doesn&#39;t match the defined service name `clamav-rest-api`. Docker Compose uses service names for DNS resolution, so this URL will fail to resolve.</comment>

<file context>
@@ -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
     ports:
-      - 3000:3000
</file context>
Suggested change
- CLAMAV_URL=http://clam_av_api:3000/api/v1/scan
- CLAMAV_URL=http://clamav-rest-api:3000/api/v1/scan
Fix with Cubic

# outside:inside
- "3000:3000"

clamav:
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0: YAML indentation error: clamav: has 3 spaces instead of 2. This will cause a YAML parsing error and prevent Docker Compose from running.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At compose.yaml, line 40:

<comment>YAML indentation error: `clamav:` has 3 spaces instead of 2. This will cause a YAML parsing error and prevent Docker Compose from running.</comment>

<file context>
@@ -16,5 +16,32 @@ services:
+      # outside:inside
+      - &quot;3000:3000&quot;
+
+   clamav:
+    image: clamav/clamav:latest
+    container_name: clamav
</file context>
Suggested change
clamav:
clamav:
Fix with Cubic

}

// Generic HTTP error
if (xhr.status !== 200) {
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Failed upload doesn't remove file from fileNames array or UI. This leaves inconsistent state where user could attempt to convert a file that failed to upload.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At public/script.js, line 277:

<comment>Failed upload doesn&#39;t remove file from `fileNames` array or UI. This leaves inconsistent state where user could attempt to convert a file that failed to upload.</comment>

<file context>
@@ -209,20 +202,104 @@ const uploadFile = (file) =&gt; {
+    }
+
+    // Generic HTTP error
+    if (xhr.status !== 200) {
+      console.error(&quot;Upload failed:&quot;, xhr.status, xhr.responseText);
+      alert(&quot;Upload failed. Please try again.&quot;);
</file context>
Fix with Cubic

// Update AV status
.post(
"/api/antivirus",
({ body }) => {
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Security: POST /api/antivirus endpoint lacks authentication. This allows unauthenticated users to disable antivirus scanning. Add user to the handler context to require authentication, similar to other protected endpoints like deleteFile.tsx.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pages/antivirus.tsx, line 22:

<comment>Security: POST `/api/antivirus` endpoint lacks authentication. This allows unauthenticated users to disable antivirus scanning. Add `user` to the handler context to require authentication, similar to other protected endpoints like `deleteFile.tsx`.</comment>

<file context>
@@ -0,0 +1,45 @@
+  // Update AV status
+  .post(
+    &quot;/api/antivirus&quot;,
+    ({ body }) =&gt; {
+      const { enabled } = body;
+
</file context>

✅ Addressed in 3420464

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 issues found across 7 files

Prompt for AI agents (all 5 issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="public/script.js">

<violation number="1" location="public/script.js:463">
P2: Generic HTTP error handler doesn&#39;t remove the file from `fileNames` array or remove the row from the DOM. This leaves orphaned file entries in the UI that weren&#39;t actually uploaded.</violation>
</file>

<file name="compose.yaml">

<violation number="1" location="compose.yaml:19">
P1: Hostname mismatch: `clam_av_api` does not match the service name `clamav-rest-api`. Docker Compose uses service names as DNS hostnames, so this URL will fail to resolve.</violation>

<violation number="2" location="compose.yaml:32">
P1: Placeholder value not replaced: `CLAMAV_server_IP` should be `clamav` to reference the ClamAV daemon service defined in the same compose file.</violation>

<violation number="3" location="compose.yaml:40">
P0: YAML indentation error: This line has 3 spaces instead of 2, which breaks YAML parsing. Service definitions must be consistently indented.</violation>
</file>

<file name="src/pages/antivirus.tsx">

<violation number="1" location="src/pages/antivirus.tsx:14">
P0: The new /api/antivirus GET/POST endpoints are exposed without `{ auth: true }`, so unauthenticated callers can enable or disable the global scanning state used by the upload pipeline.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

convertButton.disabled = false;
convertButton.textContent = "Upload failed";

const progressbar = file.htmlRow.getElementsByTagName("progress");
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Generic HTTP error handler doesn't remove the file from fileNames array or remove the row from the DOM. This leaves orphaned file entries in the UI that weren't actually uploaded.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At public/script.js, line 463:

<comment>Generic HTTP error handler doesn&#39;t remove the file from `fileNames` array or remove the row from the DOM. This leaves orphaned file entries in the UI that weren&#39;t actually uploaded.</comment>

<file context>
@@ -209,20 +382,104 @@ const uploadFile = (file) =&gt; {
+      convertButton.disabled = false;
+      convertButton.textContent = &quot;Upload failed&quot;;
+
+      const progressbar = file.htmlRow.getElementsByTagName(&quot;progress&quot;);
+      if (progressbar[0]?.parentElement) {
+        progressbar[0].parentElement.remove();
</file context>
Suggested change
const progressbar = file.htmlRow.getElementsByTagName("progress");
// 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);
}
const progressbar = file.htmlRow?.getElementsByTagName("progress");
Fix with Cubic

# field name expected in the multipart form
- APP_FORM_KEY=FILES
# talk to your existing ClamAV daemon
- CLAMD_IP=CLAMAV_server_IP
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Placeholder value not replaced: CLAMAV_server_IP should be clamav to reference the ClamAV daemon service defined in the same compose file.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At compose.yaml, line 32:

<comment>Placeholder value not replaced: `CLAMAV_server_IP` should be `clamav` to reference the ClamAV daemon service defined in the same compose file.</comment>

<file context>
@@ -16,5 +16,32 @@ services:
+      # field name expected in the multipart form
+      - APP_FORM_KEY=FILES
+      # talk to your existing ClamAV daemon
+      - CLAMD_IP=CLAMAV_server_IP
+      - CLAMD_PORT=3310
+      # max allowed file size (here: 250 MB)
</file context>
Suggested change
- CLAMD_IP=CLAMAV_server_IP
- CLAMD_IP=clamav
Fix with Cubic

# - 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
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Hostname mismatch: clam_av_api does not match the service name clamav-rest-api. Docker Compose uses service names as DNS hostnames, so this URL will fail to resolve.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At compose.yaml, line 19:

<comment>Hostname mismatch: `clam_av_api` does not match the service name `clamav-rest-api`. Docker Compose uses service names as DNS hostnames, so this URL will fail to resolve.</comment>

<file context>
@@ -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
     ports:
-      - 3000:3000
</file context>
Suggested change
- CLAMAV_URL=http://clam_av_api:3000/api/v1/scan
- CLAMAV_URL=http://clamav-rest-api:3000/api/v1/scan
Fix with Cubic

# outside:inside
- "3000:3000"

clamav:
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 4, 2025

Choose a reason for hiding this comment

The 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
Check if this issue is valid — if so, understand the root cause and fix it. At compose.yaml, line 40:

<comment>YAML indentation error: This line has 3 spaces instead of 2, which breaks YAML parsing. Service definitions must be consistently indented.</comment>

<file context>
@@ -16,5 +16,32 @@ services:
+      # outside:inside
+      - &quot;3000:3000&quot;
+
+   clamav:
+    image: clamav/clamav:latest
+    container_name: clamav
</file context>
Suggested change
clamav:
clamav:
Fix with Cubic

export const antivirus = new Elysia()
.use(userService)
// Get current AV status
.get("/api/antivirus", () => {
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0: The new /api/antivirus GET/POST endpoints are exposed without { auth: true }, so unauthenticated callers can enable or disable the global scanning state used by the upload pipeline.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pages/antivirus.tsx, line 14:

<comment>The new /api/antivirus GET/POST endpoints are exposed without `{ auth: true }`, so unauthenticated callers can enable or disable the global scanning state used by the upload pipeline.</comment>

<file context>
@@ -0,0 +1,45 @@
+export const antivirus = new Elysia()
+  .use(userService)
+  // Get current AV status
+  .get(&quot;/api/antivirus&quot;, () =&gt; {
+    const available = isAntivirusAvailable();
+    const enabled = isAntivirusEnabled();
</file context>

✅ Addressed in 3420464

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 issues found across 7 files

Prompt for AI agents (all 5 issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="src/pages/antivirus.tsx">

<violation number="1" location="src/pages/antivirus.tsx:39">
P1: Missing authentication on POST `/api/antivirus` endpoint. This allows any user to disable antivirus scanning without being logged in, which is a security vulnerability. Add `auth: true` to the endpoint configuration like other sensitive endpoints in this codebase.</violation>
</file>

<file name="compose.yaml">

<violation number="1" location="compose.yaml:19">
P1: Service name mismatch: `CLAMAV_URL` references `clam_av_api` but the service is named `clamav-rest-api`. The antivirus scanning will fail to connect. Use `http://clamav-rest-api:3000/api/v1/scan` instead.</violation>

<violation number="2" location="compose.yaml:32">
P1: Placeholder value not replaced: `CLAMD_IP=CLAMAV_server_IP` should be `CLAMD_IP=clamav` to reference the clamav service defined in this compose file. The REST API won&#39;t be able to connect to the ClamAV daemon.</violation>

<violation number="3" location="compose.yaml:40">
P0: YAML indentation error: `clamav` service has 3 spaces instead of 2. This will cause YAML parsing to fail or produce unexpected structure.</violation>
</file>

<file name="src/pages/upload.tsx">

<violation number="1" location="src/pages/upload.tsx:180">
P2: When `sanitize()` returns an empty string, the fallback to &quot;file&quot; could cause filename collisions. If multiple files in the same batch have invalid names, they will all write to the same path, with each overwriting the previous. Consider appending a unique suffix (e.g., index or timestamp) to prevent data loss.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

Comment on lines +39 to +43
},
{
body: t.Object({
enabled: t.Boolean(),
}),
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Missing authentication on POST /api/antivirus endpoint. This allows any user to disable antivirus scanning without being logged in, which is a security vulnerability. Add auth: true to the endpoint configuration like other sensitive endpoints in this codebase.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pages/antivirus.tsx, line 39:

<comment>Missing authentication on POST `/api/antivirus` endpoint. This allows any user to disable antivirus scanning without being logged in, which is a security vulnerability. Add `auth: true` to the endpoint configuration like other sensitive endpoints in this codebase.</comment>

<file context>
@@ -0,0 +1,45 @@
+        available: true,
+        enabled: isAntivirusEnabled(),
+      };
+    },
+    {
+      body: t.Object({
</file context>
Suggested change
},
{
body: t.Object({
enabled: t.Boolean(),
}),
},
{
body: t.Object({
enabled: t.Boolean(),
}),
auth: true,
},

✅ Addressed in 3420464

# field name expected in the multipart form
- APP_FORM_KEY=FILES
# talk to your existing ClamAV daemon
- CLAMD_IP=CLAMAV_server_IP
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Placeholder value not replaced: CLAMD_IP=CLAMAV_server_IP should be CLAMD_IP=clamav to reference the clamav service defined in this compose file. The REST API won't be able to connect to the ClamAV daemon.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At compose.yaml, line 32:

<comment>Placeholder value not replaced: `CLAMD_IP=CLAMAV_server_IP` should be `CLAMD_IP=clamav` to reference the clamav service defined in this compose file. The REST API won&#39;t be able to connect to the ClamAV daemon.</comment>

<file context>
@@ -16,5 +16,32 @@ services:
+      # field name expected in the multipart form
+      - APP_FORM_KEY=FILES
+      # talk to your existing ClamAV daemon
+      - CLAMD_IP=CLAMAV_server_IP
+      - CLAMD_PORT=3310
+      # max allowed file size (here: 250 MB)
</file context>
Suggested change
- CLAMD_IP=CLAMAV_server_IP
- CLAMD_IP=clamav
Fix with Cubic

# - 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
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Service name mismatch: CLAMAV_URL references clam_av_api but the service is named clamav-rest-api. The antivirus scanning will fail to connect. Use http://clamav-rest-api:3000/api/v1/scan instead.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At compose.yaml, line 19:

<comment>Service name mismatch: `CLAMAV_URL` references `clam_av_api` but the service is named `clamav-rest-api`. The antivirus scanning will fail to connect. Use `http://clamav-rest-api:3000/api/v1/scan` instead.</comment>

<file context>
@@ -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
     ports:
-      - 3000:3000
</file context>
Suggested change
- CLAMAV_URL=http://clam_av_api:3000/api/v1/scan
- CLAMAV_URL=http://clamav-rest-api:3000/api/v1/scan
Fix with Cubic

# outside:inside
- "3000:3000"

clamav:
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0: YAML indentation error: clamav service has 3 spaces instead of 2. This will cause YAML parsing to fail or produce unexpected structure.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At compose.yaml, line 40:

<comment>YAML indentation error: `clamav` service has 3 spaces instead of 2. This will cause YAML parsing to fail or produce unexpected structure.</comment>

<file context>
@@ -16,5 +16,32 @@ services:
+      # outside:inside
+      - &quot;3000:3000&quot;
+
+   clamav:
+    image: clamav/clamav:latest
+    container_name: clamav
</file context>
Suggested change
clamav:
clamav:
Fix with Cubic


for (const file of files) {
const originalName = (file as any).name ?? "upload";
const sanitizedFileName = sanitize(originalName) || "file";
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When sanitize() returns an empty string, the fallback to "file" could cause filename collisions. If multiple files in the same batch have invalid names, they will all write to the same path, with each overwriting the previous. Consider appending a unique suffix (e.g., index or timestamp) to prevent data loss.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pages/upload.tsx, line 180:

<comment>When `sanitize()` returns an empty string, the fallback to &quot;file&quot; could cause filename collisions. If multiple files in the same batch have invalid names, they will all write to the same path, with each overwriting the previous. Consider appending a unique suffix (e.g., index or timestamp) to prevent data loss.</comment>

<file context>
@@ -1,42 +1,240 @@
+
+        for (const file of files) {
+          const originalName = (file as any).name ?? &quot;upload&quot;;
+          const sanitizedFileName = sanitize(originalName) || &quot;file&quot;;
+          console.log(
+            &quot;[Upload] Handling file:&quot;,
</file context>
Suggested change
const sanitizedFileName = sanitize(originalName) || "file";
const baseSanitized = sanitize(originalName);
const sanitizedFileName = baseSanitized || `file_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
Fix with Cubic

@Kosztyk
Copy link
Author

Kosztyk commented Dec 4, 2025

HI,

Following your suggestion i added the toggle
Screenshot 2025-12-04 at 09 59 56

If a Clam av server is not setup in docker-compose.yml using - CLAMAV_URL=http://clam_av_api:3000/api/v1/scan the toggle will be by inactive and deactivated and uploads will follow like normal.

Screenshot 2025-12-04 at 16 34 56

@Kosztyk
Copy link
Author

Kosztyk commented Dec 4, 2025

Also added a dark theme with a switch toggle
Screenshot 2025-12-04 at 14 14 20

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 5 files (changes from recent commits).

Prompt for AI agents (all 1 issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="src/components/base.tsx">

<violation number="1" location="src/components/base.tsx:43">
P2: Using `defer` on theme-init.js contradicts its purpose of applying the theme &quot;*before* the page renders&quot;. With `defer`, the script runs after HTML parsing, causing a flash of the wrong theme (FOUC). Remove `defer` so the script blocks rendering briefly, or consider inlining the script.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

<link rel="manifest" href={`${webroot}/site.webmanifest`} />

{/* NEW: global theme initializer – runs on *all* pages */}
<script src={`${webroot}/theme-init.js`} defer></script>
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Using defer on theme-init.js contradicts its purpose of applying the theme "before the page renders". With defer, the script runs after HTML parsing, causing a flash of the wrong theme (FOUC). Remove defer so the script blocks rendering briefly, or consider inlining the script.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/components/base.tsx, line 43:

<comment>Using `defer` on theme-init.js contradicts its purpose of applying the theme &quot;*before* the page renders&quot;. With `defer`, the script runs after HTML parsing, causing a flash of the wrong theme (FOUC). Remove `defer` so the script blocks rendering briefly, or consider inlining the script.</comment>

<file context>
@@ -15,13 +15,36 @@ export const BaseHtml = ({
       &lt;link rel=&quot;manifest&quot; href={`${webroot}/site.webmanifest`} /&gt;
+
+      {/* NEW: global theme initializer – runs on *all* pages */}
+      &lt;script src={`${webroot}/theme-init.js`} defer&gt;&lt;/script&gt;
     &lt;/head&gt;
-    &lt;body class={`flex min-h-screen w-full flex-col bg-neutral-900 text-neutral-200`}&gt;
</file context>
Suggested change
<script src={`${webroot}/theme-init.js`} defer></script>
<script src={`${webroot}/theme-init.js`}></script>

✅ Addressed in 6fe7899

@Kosztyk
Copy link
Author

Kosztyk commented Dec 4, 2025

fixed the issues highlighted by cubic

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 2 files (changes from recent commits).

Prompt for AI agents (all 2 issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="src/pages/user.tsx">

<violation number="1" location="src/pages/user.tsx:947">
P2: These cascading delete operations should be wrapped in a transaction to ensure atomicity. If one query fails, the database could be left in an inconsistent state with orphaned records.</violation>
</file>

<file name="src/pages/root.tsx">

<violation number="1" location="src/pages/root.tsx:118">
P2: Unused variable `storedJobs`. This database query is executed but its result is never used, causing unnecessary database overhead on every page load.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR


const converters = await getAllTargets();

const storedJobs = db.query(
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Unused variable storedJobs. This database query is executed but its result is never used, causing unnecessary database overhead on every page load.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pages/root.tsx, line 118:

<comment>Unused variable `storedJobs`. This database query is executed but its result is never used, causing unnecessary database overhead on every page load.</comment>

<file context>
@@ -82,27 +93,31 @@ export const root = new Elysia().use(userService).get(
+
+    const converters = await getAllTargets();
+
+    const storedJobs = db.query(
+      &quot;SELECT jobs.*, users.email FROM jobs INNER JOIN users ON jobs.user_id = users.id ORDER BY jobs.date_created DESC&quot;,
+    ).all() as (User &amp; { date_created: string; num_files: number; status: string })[];
</file context>

✅ Addressed in 1cdbd9e

@Kosztyk
Copy link
Author

Kosztyk commented Dec 4, 2025

Added User management function:

  • add user;
  • delete
  • update
    First user created after application installation will be administrator by default, the rest of the users can be admin users or normal users.
Screenshot 2025-12-04 at 21 01 37

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants