Skip to content

Disallow MP3 Full Server Cleanse, improve metadata-wipe handling, and pin Node runtime#19

Merged
ChrisAdamsdevelopment merged 1 commit into
mainfrom
codex/fix-mp3-server-cleanse-behavior
May 12, 2026
Merged

Disallow MP3 Full Server Cleanse, improve metadata-wipe handling, and pin Node runtime#19
ChrisAdamsdevelopment merged 1 commit into
mainfrom
codex/fix-mp3-server-cleanse-behavior

Conversation

@ChrisAdamsdevelopment
Copy link
Copy Markdown
Owner

@ChrisAdamsdevelopment ChrisAdamsdevelopment commented May 12, 2026

Motivation

  • Prevent server-side processing of MP3 files which are intentionally handled in-browser and can fail metadata rewriting on the server.
  • Fail fast and surface clearer detail messages to the client when a format is unsupported or metadata wipe fails.
  • Document and pin a compatible Node runtime due to native compilation issues with newer Node releases (notably better-sqlite3).

Description

  • Add early MP3 detection in /api/process and return HTTP 422 with error and detail explaining that MP3s must use Quick Cleanse (Browser), while allowing MP4/M4A/WAV/FLAC for Full Server Cleanse, and remove the uploaded temp file when rejecting.
  • Change the metadata wipe flow to use the exiftool.write path and, on failure, clean up uploaded files and return HTTP 422 indicating the format is unsupported for server-side cleanse.
  • Surface server detail fields on client errors by falling back to errBody.detail in app.tsx error handling.
  • Pin Node engine compatibility to >=18 <23, add .node-version set to 20.20.2, and add README guidance about using Node 20.20.2 for native deployments and that Node 24 is unsupported for native installs.
  • Update README and docs/manual-qa-checklist.md to reflect the new MP3 rejection behavior and clarify usage-meter semantics; update package.json and package-lock.json engine fields accordingly.

Testing

  • Run project build with npm run build to compile TypeScript and build the frontend, and the build completed successfully.

Codex Task

Summary by Sourcery

Disallow server-side MP3 full cleanse, tighten server metadata-wipe handling, and pin Node runtime compatibility.

New Features:

  • Reject MP3 uploads for Full Server Cleanse at /api/process with HTTP 422 and guidance to use browser-based Quick Cleanse.

Bug Fixes:

  • Fail metadata wipe gracefully by cleaning up temporary files and returning HTTP 422 when server-side cleanse is unsupported for a file format.
  • Expose server-provided error detail messages in client-side error handling to improve surfaced feedback.

Enhancements:

  • Clarify usage meter semantics so counters only increment after successfully delivered cleansed files.

Build:

  • Constrain Node engine support to versions >=18 <23 in package configuration and add a .node-version file pinning Node 20.20.2 for native deployments.

Documentation:

  • Document the new MP3 rejection behavior, supported formats for Full Server Cleanse, and recommended Node runtime versions in README and manual QA checklist.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented May 12, 2026

Reviewer's Guide

Implements server-side rejection of MP3 files for Full Server Cleanse, hardens the metadata wipe flow by using exiftool.write with proper cleanup and 422 responses on failure, surfaces server-provided detail messages to the client, and documents/pins a compatible Node runtime and new behavior in docs and engine fields.

Sequence diagram for updated /api/process MP3 handling and metadata wipe errors

sequenceDiagram
  actor User
  participant BrowserApp
  participant ApiServer
  participant exiftool
  participant fs

  User->>BrowserApp: Submit Full Server Cleanse
  BrowserApp->>ApiServer: POST /api/process (file)

  alt [file is MP3]
    ApiServer->>fs: remove(inputPath)
    ApiServer-->>BrowserApp: 422 { error, detail }
    BrowserApp->>BrowserApp: throw new Error(errBody.detail || errBody.error)
  else [non-MP3, supported]
    ApiServer->>exiftool: read(outputPath)
    ApiServer->>exiftool: write(outputPath, {}, wipeArgs)
    ApiServer-->>BrowserApp: 200 cleansed file
  else [metadata wipe fails]
    ApiServer->>fs: remove(inputPath)
    ApiServer->>fs: remove(outputPath)
    ApiServer-->>BrowserApp: 422 { error, detail }
    BrowserApp->>BrowserApp: throw new Error(errBody.detail || errBody.error)
  end
Loading

File-Level Changes

Change Details Files
Disallow MP3 files from being processed by the Full Server Cleanse endpoint and ensure temporary files are cleaned up on rejection.
  • Derive inputPath, original filename extension, and MIME type from the uploaded file.
  • Detect MP3 uploads based on extension or audio/mpeg MIME type.
  • On MP3 detection, remove the uploaded temp file and return HTTP 422 with an error and detail message instructing users to use Quick Cleanse (Browser) and listing supported formats.
server.js
Harden server-side metadata wipe by using exiftool.write as the primary path, handling failures, and cleaning up files with clear client-facing errors.
  • Switch the nuclear metadata wipe to use exiftool.write with -all=, -XMP:all=, -IPTC:all=, and -overwrite_original.
  • On wipe failure, log a warning, delete both input and output files, and return HTTP 422 indicating server cleanse is unsupported for this format with guidance to use Quick Cleanse or alternative formats.
  • Refactor to reuse inputPath computed earlier instead of re-declaring it near outputPath creation.
server.js
Improve client error reporting by preferring server-provided detail messages when surfacing errors from /api/process.
  • Update the app-side fetch error handling to construct Error messages from response.detail, falling back to response.error and then a generic HTTP status message.
app.tsx
Pin and document supported Node runtime versions for native deployments and engines.
  • Constrain package.json engines.node to >=18 <23, and sync the same constraint in package-lock.json.
  • Add a .node-version file set to 20.20.2 for tooling consistency.
  • Document recommended Node 20.20.2 runtime, the supported engines range, and the lack of Node 24 support due to better-sqlite3 compilation issues.
package.json
package-lock.json
.node-version
README.md
Update documentation and QA checklist to describe new MP3 rejection behavior and clarify usage meter semantics.
  • Clarify that Quick Cleanse MP3 metadata writing is browser-only and that Full Server Cleanse handles only supported non-MP3 formats, rejecting MP3 with HTTP 422 and guidance.
  • Add manual QA steps for verifying MP3 rejection responses and non-incrementing usage counters on rejection.
  • Clarify that usage meters increment only after successfully delivered files.
README.md
docs/manual-qa-checklist.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@ChrisAdamsdevelopment ChrisAdamsdevelopment merged commit 30e4c37 into main May 12, 2026
2 of 5 checks passed
Copy link
Copy Markdown

@sourcery-ai sourcery-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.

Hey - I've found 2 issues, and left some high level feedback:

  • The MP3 detection in /api/process only checks for audio/mpeg and .mp3, so consider broadening this (e.g., handling audio/mp3 or other common variants) to avoid accidentally allowing MP3s through under alternate MIME types.
  • When handling the metadata wipe failure, you only log wipeErr.message; logging or capturing the full error (e.g., wipeErr.stack) would make debugging unexpected exiftool failures easier.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The MP3 detection in `/api/process` only checks for `audio/mpeg` and `.mp3`, so consider broadening this (e.g., handling `audio/mp3` or other common variants) to avoid accidentally allowing MP3s through under alternate MIME types.
- When handling the metadata wipe failure, you only log `wipeErr.message`; logging or capturing the full error (e.g., `wipeErr.stack`) would make debugging unexpected exiftool failures easier.

## Individual Comments

### Comment 1
<location path="server.js" line_range="455-458" />
<code_context>

   const userId = req.user.sub;
+  const inputPath = req.file.path;
+  const originalName = req.file.originalname || '';
+  const ext = path.extname(originalName).toLowerCase() || '.mp3';
+  const mime = (req.file.mimetype || '').toLowerCase();
+  const isMp3 = ext === '.mp3' || mime === 'audio/mpeg';
+
+  if (isMp3) {
</code_context>
<issue_to_address>
**issue (bug_risk):** Defaulting unknown extensions to `.mp3` risks misclassification and mismatched output extensions.

When `originalname` is empty or has no extension, `path.extname` returns `''`, so `ext` becomes `.mp3`. For non‑MP3 uploads without a usable filename, this can result in `isMp3 === false` (based on MIME) but an `.mp3` output extension, and in other supported formats being rejected because they’re treated as `.mp3`. Consider deriving `ext` from `mimetype` when `originalname` is unusable (e.g., `audio/wav``.wav`, `audio/flac``.flac`), and only then falling back to a neutral extension rather than `.mp3`, to avoid reintroducing MP3‑specific assumptions.
</issue_to_address>

### Comment 2
<location path="docs/manual-qa-checklist.md" line_range="103" />
<code_context>
+1. Run server cleanse on a supported non-MP3 file.
 2. Verify `/api/process` returns downloadable file response.
-3. Verify usage meter/counter updates.
+3. Verify usage meter/counter updates only after successful delivered files.
 4. Verify forensic/report information appears when present.
-5. Force or simulate `401` from protected endpoint.
</code_context>
<issue_to_address>
**nitpick (typo):** Rephrase "successful delivered files" to improve grammar and clarity.

Consider rephrasing to something like "updates only after files are successfully delivered" or "updates only after successful file delivery" for clearer, grammatical wording.

```suggestion
3. Verify usage meter/counter updates only after files are successfully delivered.
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread server.js
Comment on lines +455 to +458
const originalName = req.file.originalname || '';
const ext = path.extname(originalName).toLowerCase() || '.mp3';
const mime = (req.file.mimetype || '').toLowerCase();
const isMp3 = ext === '.mp3' || mime === 'audio/mpeg';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): Defaulting unknown extensions to .mp3 risks misclassification and mismatched output extensions.

When originalname is empty or has no extension, path.extname returns '', so ext becomes .mp3. For non‑MP3 uploads without a usable filename, this can result in isMp3 === false (based on MIME) but an .mp3 output extension, and in other supported formats being rejected because they’re treated as .mp3. Consider deriving ext from mimetype when originalname is unusable (e.g., audio/wav.wav, audio/flac.flac), and only then falling back to a neutral extension rather than .mp3, to avoid reintroducing MP3‑specific assumptions.

1. Run server cleanse on a supported non-MP3 file.
2. Verify `/api/process` returns downloadable file response.
3. Verify usage meter/counter updates.
3. Verify usage meter/counter updates only after successful delivered files.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nitpick (typo): Rephrase "successful delivered files" to improve grammar and clarity.

Consider rephrasing to something like "updates only after files are successfully delivered" or "updates only after successful file delivery" for clearer, grammatical wording.

Suggested change
3. Verify usage meter/counter updates only after successful delivered files.
3. Verify usage meter/counter updates only after files are successfully delivered.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant