Skip to content

refactor(file): improve path handling and validation#82

Open
balazs-szucs wants to merge 2 commits intogrimmory-tools:developfrom
balazs-szucs:uncontrolled-paths
Open

refactor(file): improve path handling and validation#82
balazs-szucs wants to merge 2 commits intogrimmory-tools:developfrom
balazs-szucs:uncontrolled-paths

Conversation

@balazs-szucs
Copy link
Member

@balazs-szucs balazs-szucs commented Mar 20, 2026

📝 Description

This pull request focuses on improving file path validation and security throughout the codebase, especially to prevent directory traversal attacks and ensure safe file operations. It also includes some bug fixes and minor improvements to the OPDS feed and related services.

These changes collectively harden the application against path traversal vulnerabilities.

Required for develop and main. Your PR title must use Conventional Commit format because maintainers squash-merge with the PR title and stable releases are computed from commit history. Example: fix(reader): prevent blank pages on chapter jump

Linked Issue: Fixes #

Required. Every PR must reference an approved issue. If no issue exists, open one and wait for maintainer approval before submitting a PR. Unsolicited PRs without a linked issue will be closed.

🏷️ Type of Change

  • Bug fix
  • New feature
  • Enhancement to existing feature
  • Refactor (no behavior change)
  • Breaking change (existing functionality affected)
  • Documentation update

🔧 Changes

🧪 Testing (MANDATORY)

PRs without this section filled out will be closed. "Tests pass" or "Tested locally" is not sufficient. You must provide specifics.

Manual testing steps you performed:

Regression testing:

Edge cases covered:

Test output:

Backend test output (./gradlew test)
PASTE OUTPUT HERE
Frontend test output (ng test)
PASTE OUTPUT HERE

📸 Screen Recording / Screenshots (MANDATORY)

Every PR must include a screen recording or screenshots showing the change working end-to-end in a running local instance (both backend and frontend). This means you must have actually built, run, and tested the code yourself. PRs without visual proof will be closed without review.


✅ Pre-Submission Checklist

All boxes must be checked before requesting review. Incomplete PRs will be closed without review. No exceptions.

  • This PR is linked to an approved issue
  • Code follows project backend and frontend conventions
  • Branch is up to date with develop (merge conflicts resolved)
  • I ran the full stack locally (backend + frontend + database) and verified the change works
  • Automated tests added or updated to cover changes (backend and frontend)
  • All tests pass locally and output is pasted above
  • Screen recording or screenshots are attached above proving the change works
  • PR is a single focused change (one bug fix OR one feature, not multiple unrelated changes)
  • PR is reasonably scoped (PRs over 1000+ changed lines will be closed, split into smaller PRs)
  • No unsolicited refactors, cleanups, or "improvements" are bundled in
  • Flyway migration versioning is correct (if schema was modified)
  • Required documentation updates are included in this repo or the current Grimmory docs surface (if user-facing changes)

🤖 AI-Assisted Contributions

If any part of this PR was generated or assisted by AI tools (Copilot, Claude, ChatGPT, etc.), all items below are mandatory. You are fully responsible for every line you submit. "The AI wrote it" is not an excuse, and AI-generated PRs that clearly haven't been reviewed are the #1 reason PRs get closed.

  • I have read and understand every line of this PR and can explain any part of it during review
  • I personally ran the code and verified it works (not just trusted the AI's output)
  • PR is scoped to a single logical change, not a dump of everything the AI suggested
  • Tests validate actual behavior, not just coverage (AI-generated tests often assert nothing meaningful)
  • No dead code, placeholder comments, TODOs, or unused scaffolding left behind by AI
  • I did not submit refactors, style changes, or "improvements" the AI suggested beyond the scope of the issue

💬 Additional Context (optional)

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Fixed OPDS feed encoding to ensure proper UTF-8 character handling across clients.
    • Improved XML escaping in catalog and recent book feeds.
  • Improvements

    • Enhanced file path handling for book uploads and downloads.
    • Optimized OPDS query parameter processing for better feed generation.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 20, 2026

📝 Walkthrough

Walkthrough

This PR introduces systematic path traversal protection and input validation across file operations. New FileUtils utility methods enforce path normalization, parent-directory traversal detection, and base-path containment checks, integrated into file upload, download, fingerprinting, and migration services. OPDS endpoints now return UTF-8 encoded bytes with improved feed service query handling and XML escaping.

Changes

Cohort / File(s) Summary
Path Validation Utilities
booklore-api/src/main/java/org/booklore/util/FileUtils.java
Added four new public methods: normalizeAbsolutePath, containsParentTraversal, requirePathWithinBase, and resolvePathWithinBase. These utilities enforce absolute path normalization, detect .. segments, and validate that candidate paths remain within specified base directories, with additional filesystem-anchored validation for real path resolution.
File Operation Safety
booklore-api/src/main/java/org/booklore/service/book/BookDownloadService.java, booklore-api/src/main/java/org/booklore/service/file/FileFingerprint.java, booklore-api/src/main/java/org/booklore/service/file/PathService.java
Integrated path validation into file access operations. BookDownloadService now derives and constrains paths using requirePathWithinBase. FileFingerprint added private validators to normalize and check paths before filesystem operations. PathService added null/blank checks, InvalidPathException handling, and parent-traversal detection in getFoldersAtPath.
Upload Path Safety
booklore-api/src/main/java/org/booklore/service/upload/FileUploadService.java
Comprehensive refactoring to enforce root-constrained path resolution. Upload destinations now use resolvePathWithinRoot against normalized roots; validation and moves operate on safe paths via requirePathWithinRoot. Relative paths derived via toSafeRelativePath prevent absolute paths and parent traversal. Temp cleanup now constrains paths and handles suspicious issues with warnings.
Migration File Safety
booklore-api/src/main/java/org/booklore/service/migration/migrations/MoveIconsToDataFolderMigration.java
Added filename validation to reject suspicious names containing path separators (/, \) or parent references (..). File resolution now constrains output paths to remain within the normalized target directory using guarded resolution; files that would escape the base are skipped with a warning.
OPDS Response & Feed Handling
booklore-api/src/main/java/org/booklore/controller/OpdsController.java, booklore-api/src/main/java/org/booklore/service/opds/OpdsFeedService.java
OpdsController endpoints (getCatalog, getRecentBooks) changed return type from ResponseEntity<String> to ResponseEntity<byte[]>, converting feeds to UTF-8 encoded bytes. OpdsFeedService added PAGINATION_QUERY_WHITELIST, URL-encodes pagination parameters, XML-escapes feed IDs using StringEscapeUtils.escapeXml11 instead of manual character replacement.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 Paths are now safe, no traversal allowed!
With FileUtils bright, our code's standing proud.
Validation and guards at each service's door,
UTF-8 bytes and escaping galore—
Security hops make this bundle divine!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is incomplete: mandatory sections for linked issue, type of change, specific changes list, testing details, and screenshots are not filled out. Complete all mandatory sections: provide a specific linked issue number, select change type, list specific modifications, document testing steps with output, and attach screen recordings/screenshots.
Docstring Coverage ⚠️ Warning Docstring coverage is 2.63% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'refactor(file): improve path handling and validation' follows Conventional Commit format, is concise, and accurately describes the main change across the codebase.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
booklore-api/src/main/java/org/booklore/service/book/BookDownloadService.java (1)

107-107: ⚠️ Potential issue | 🟡 Minor

Inconsistent path validation: downloadBookFile lacks containment check.

Unlike downloadBook (lines 61-62) and downloadKoboBook (lines 261-263) which use FileUtils.requirePathWithinBase, downloadBookFile only normalizes the path without validating it stays within the library root. For consistency and defense-in-depth, consider adding the same containment check.

🛡️ Suggested fix for consistency
+            BookEntity bookEntity = bookFileEntity.getBook();
+            Path libraryRoot = Path.of(bookEntity.getLibraryPath().getPath());
-            Path file = bookFileEntity.getFullFilePath().toAbsolutePath().normalize();
+            Path file = FileUtils.requirePathWithinBase(bookFileEntity.getFullFilePath(), libraryRoot);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@booklore-api/src/main/java/org/booklore/service/book/BookDownloadService.java`
at line 107, The downloadBookFile method currently only normalizes the path
returned by bookFileEntity.getFullFilePath() without verifying it remains inside
the library root; update downloadBookFile to call
FileUtils.requirePathWithinBase (same pattern used in downloadBook and
downloadKoboBook) after normalization to enforce containment within the library
root and throw/handle the same error if the check fails so path validation is
consistent across methods.
🧹 Nitpick comments (3)
booklore-api/src/main/java/org/booklore/util/FileUtils.java (1)

34-44: Consider consistent null handling across path utilities.

containsParentTraversal returns false for null input (line 36), while normalizeAbsolutePath throws IllegalArgumentException. This inconsistency could mask null inputs in call sites that check traversal before normalization.

♻️ Optional: throw for null to align with normalizeAbsolutePath
     public boolean containsParentTraversal(Path path) {
         if (path == null) {
-            return false;
+            throw new IllegalArgumentException("Path cannot be null");
         }
         for (Path part : path) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@booklore-api/src/main/java/org/booklore/util/FileUtils.java` around lines 34
- 44, containsParentTraversal currently returns false for a null Path but
normalizeAbsolutePath throws IllegalArgumentException for null; change
containsParentTraversal(Path path) to validate input the same way (throw
IllegalArgumentException when path is null) so callers get consistent null
handling. Update the method (containsParentTraversal) to check for null at the
start and throw IllegalArgumentException with a clear message, ensuring behavior
aligns with normalizeAbsolutePath.
booklore-api/src/main/java/org/booklore/controller/OpdsController.java (1)

140-146: Inconsistent response types across OPDS endpoints.

Only /catalog and /recent were updated to return byte[] with explicit UTF-8 encoding, while other endpoints (/, /libraries, /shelves, /magic-shelves, /authors, /series, /surprise) still return String. Since all endpoints declare charset=utf-8 in their content type, consider applying the same pattern consistently, or reverting to String if the explicit byte conversion isn't strictly necessary.

Also applies to: 151-157

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@booklore-api/src/main/java/org/booklore/controller/OpdsController.java`
around lines 140 - 146, The OPDS endpoints are inconsistent: getCatalog (and
recent) convert the feed String to byte[] with UTF-8 while other handlers (/,
/libraries, /shelves, /magic-shelves, /authors, /series, /surprise) still return
String; make them consistent by updating those handler methods to return
ResponseEntity<byte[]> and change their bodies to call
opdsFeedService.generateXFeed(...), convert the resulting String to byte[] using
StandardCharsets.UTF_8, and set the same
MediaType.parseMediaType(OPDS_ACQUISITION_MEDIA_TYPE) content type as in
getCatalog (or alternatively change getCatalog/recent back to returning
ResponseEntity<String> if you prefer Strings); pick one approach and apply it to
all methods (refer to getCatalog, opdsFeedService.generateCatalogFeed, and the
other endpoint handler method names) so all OPDS endpoints declare and deliver
UTF-8 consistently.
booklore-api/src/main/java/org/booklore/service/upload/FileUploadService.java (1)

352-354: Overly broad exception catch may mask bugs.

Catching RuntimeException could hide unexpected errors like NullPointerException. Consider catching only InvalidPathException which is what Path.of() throws for malformed paths.

♻️ Narrow exception type
+        } catch (java.nio.file.InvalidPathException e) {
-        } catch (RuntimeException e) {
             throw ApiError.GENERIC_BAD_REQUEST.createException("Invalid upload target path");
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@booklore-api/src/main/java/org/booklore/service/upload/FileUploadService.java`
around lines 352 - 354, The catch block in FileUploadService that currently
catches RuntimeException around the Path.of(...) call should be narrowed to
catch java.nio.file.InvalidPathException only; replace "catch (RuntimeException
e)" with "catch (InvalidPathException e)" (importing it) and pass the caught
exception as the cause when creating the ApiError (e.g.,
ApiError.GENERIC_BAD_REQUEST.createException("Invalid upload target path", e))
so only malformed-path errors are handled while other runtime errors still
surface.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@booklore-api/src/main/java/org/booklore/service/upload/FileUploadService.java`:
- Around line 341-355: The toSafeRelativePath method currently calls
Path.of(relativePath).normalize() before checking
FileUtils.containsParentTraversal, which defeats the traversal detection; update
to check for parent-traversal on the raw parsed Path elements before normalizing
(or inspect the original string for ".."), e.g., obtain Path parsed =
Path.of(relativePath) then verify parsed.isAbsolute() is false and iterate
parsed.getName(i) (or call FileUtils.containsParentTraversal(parsed) if it
inspects raw segments) to detect ".." segments, only then call
parsed.normalize() and return it; keep throwing
ApiError.GENERIC_BAD_REQUEST.createException("Invalid upload target path") on
any detection or exception.

In `@booklore-api/src/main/java/org/booklore/util/FileUtils.java`:
- Around line 70-73: The current check runs containsParentTraversal against
normalizedRelative which will hide parent-traversal like ".."; instead create a
raw Path from relativePath (e.g., rawRelative = Path.of(relativePath)), perform
the security checks against rawRelative (check rawRelative.isAbsolute() ||
containsParentTraversal(rawRelative)) and throw if invalid, then call
normalizedRelative = rawRelative.normalize() for further processing; update the
checks around containsParentTraversal, normalizedRelative, and relativePath in
FileUtils so traversal is detected before normalization.

In `@CHANGELOG.md`:
- Around line 1-11: The top-level release section "## [2.2.2] (2026-03-19)" is
placed above the document title; move that entire release block (the "##
[2.2.2]" heading and its following "### Bug Fixes" and "### Chores" entries) so
it appears below the main "# Changelog" heading instead, ensuring the file
starts with "# Changelog" and then lists releases like "## [2.2.2]" in
chronological order.

---

Outside diff comments:
In
`@booklore-api/src/main/java/org/booklore/service/book/BookDownloadService.java`:
- Line 107: The downloadBookFile method currently only normalizes the path
returned by bookFileEntity.getFullFilePath() without verifying it remains inside
the library root; update downloadBookFile to call
FileUtils.requirePathWithinBase (same pattern used in downloadBook and
downloadKoboBook) after normalization to enforce containment within the library
root and throw/handle the same error if the check fails so path validation is
consistent across methods.

---

Nitpick comments:
In `@booklore-api/src/main/java/org/booklore/controller/OpdsController.java`:
- Around line 140-146: The OPDS endpoints are inconsistent: getCatalog (and
recent) convert the feed String to byte[] with UTF-8 while other handlers (/,
/libraries, /shelves, /magic-shelves, /authors, /series, /surprise) still return
String; make them consistent by updating those handler methods to return
ResponseEntity<byte[]> and change their bodies to call
opdsFeedService.generateXFeed(...), convert the resulting String to byte[] using
StandardCharsets.UTF_8, and set the same
MediaType.parseMediaType(OPDS_ACQUISITION_MEDIA_TYPE) content type as in
getCatalog (or alternatively change getCatalog/recent back to returning
ResponseEntity<String> if you prefer Strings); pick one approach and apply it to
all methods (refer to getCatalog, opdsFeedService.generateCatalogFeed, and the
other endpoint handler method names) so all OPDS endpoints declare and deliver
UTF-8 consistently.

In
`@booklore-api/src/main/java/org/booklore/service/upload/FileUploadService.java`:
- Around line 352-354: The catch block in FileUploadService that currently
catches RuntimeException around the Path.of(...) call should be narrowed to
catch java.nio.file.InvalidPathException only; replace "catch (RuntimeException
e)" with "catch (InvalidPathException e)" (importing it) and pass the caught
exception as the cause when creating the ApiError (e.g.,
ApiError.GENERIC_BAD_REQUEST.createException("Invalid upload target path", e))
so only malformed-path errors are handled while other runtime errors still
surface.

In `@booklore-api/src/main/java/org/booklore/util/FileUtils.java`:
- Around line 34-44: containsParentTraversal currently returns false for a null
Path but normalizeAbsolutePath throws IllegalArgumentException for null; change
containsParentTraversal(Path path) to validate input the same way (throw
IllegalArgumentException when path is null) so callers get consistent null
handling. Update the method (containsParentTraversal) to check for null at the
start and throw IllegalArgumentException with a clear message, ensuring behavior
aligns with normalizeAbsolutePath.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6175d295-d43f-4e84-9675-66d0842549ab

📥 Commits

Reviewing files that changed from the base of the PR and between 6ef4448 and 79e0efd.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • booklore-api/src/main/java/org/booklore/controller/OpdsController.java
  • booklore-api/src/main/java/org/booklore/service/book/BookDownloadService.java
  • booklore-api/src/main/java/org/booklore/service/file/FileFingerprint.java
  • booklore-api/src/main/java/org/booklore/service/file/PathService.java
  • booklore-api/src/main/java/org/booklore/service/migration/migrations/MoveIconsToDataFolderMigration.java
  • booklore-api/src/main/java/org/booklore/service/opds/OpdsFeedService.java
  • booklore-api/src/main/java/org/booklore/service/upload/FileUploadService.java
  • booklore-api/src/main/java/org/booklore/util/FileUtils.java

@imajes imajes force-pushed the develop branch 2 times, most recently from 89113d4 to 37ca101 Compare March 20, 2026 22:20
# Conflicts:
#	booklore-api/src/main/java/org/booklore/service/file/FileFingerprint.java
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
booklore-api/src/main/java/org/booklore/service/file/FileFingerprint.java (1)

124-128: Consider reusing FileUtils.isAudioFile to reduce duplication.

This method duplicates FileUtils.isAudioFile (lines 235-239), and FileUtils is already imported.

♻️ Proposed refactor
-    private static boolean isAudioFile(String fileName) {
-        if (fileName == null) return false;
-        String lower = fileName.toLowerCase();
-        return lower.endsWith(".mp3") || lower.endsWith(".m4a") || lower.endsWith(".m4b") || lower.endsWith(".opus");
-    }

Then update line 63 to use FileUtils.isAudioFile(...) instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@booklore-api/src/main/java/org/booklore/service/file/FileFingerprint.java`
around lines 124 - 128, Replace the duplicated audio check in FileFingerprint by
delegating to the existing utility: remove or collapse the private static
boolean isAudioFile(String fileName) implementation and call
FileUtils.isAudioFile(fileName) wherever FileFingerprint.isAudioFile is
currently used; update callers (e.g., the place that currently invokes
FileFingerprint.isAudioFile) to use FileUtils.isAudioFile(...) directly so the
duplicated logic in FileFingerprint is eliminated and the shared FileUtils
implementation is reused.
booklore-api/src/main/java/org/booklore/util/FileUtils.java (1)

86-91: Consider walking up ancestors for real-path validation.

When the candidate path doesn't exist, only its immediate parent is checked. If the parent also doesn't exist, validation is skipped. For deeper defense-in-depth against symlink attacks on non-existent intermediate directories, consider walking up the tree to find the first existing ancestor.

♻️ Proposed enhancement
-            Path existingAnchor = Files.exists(normalizedCandidate)
-                    ? normalizedCandidate
-                    : normalizedCandidate.getParent();
-            if (existingAnchor == null || !Files.exists(existingAnchor)) {
-                return;
-            }
+            Path existingAnchor = normalizedCandidate;
+            while (existingAnchor != null && !Files.exists(existingAnchor)) {
+                existingAnchor = existingAnchor.getParent();
+            }
+            if (existingAnchor == null) {
+                return;
+            }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@booklore-api/src/main/java/org/booklore/util/FileUtils.java` around lines 86
- 91, The current validation in FileUtils.java only checks the candidate or its
immediate parent; update the logic in the method that computes existingAnchor so
it walks up the ancestor chain from normalizedCandidate until it finds the first
existing ancestor (loop using Path.getParent()), set existingAnchor to that
path, and if none found return; ensure you still preserve normalization and use
Files.exists(existingAnchor) for the check (adjust the existingAnchor variable
and its use accordingly).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@booklore-api/src/main/java/org/booklore/service/file/FileFingerprint.java`:
- Around line 55-57: Remove the redundant existence check that follows
validateReadableFolderPath: delete the if-block that tests
Files.exists(normalizedFolderPath) || Files.isDirectory(normalizedFolderPath)
and its RuntimeException throw, because validateReadableFolderPath(folderPath)
already validates existence and directory-ness; keep using normalizedFolderPath
afterwards and rely on validateReadableFolderPath and its RuntimeException for
error handling (references: validateReadableFolderPath and
normalizedFolderPath).

---

Nitpick comments:
In `@booklore-api/src/main/java/org/booklore/service/file/FileFingerprint.java`:
- Around line 124-128: Replace the duplicated audio check in FileFingerprint by
delegating to the existing utility: remove or collapse the private static
boolean isAudioFile(String fileName) implementation and call
FileUtils.isAudioFile(fileName) wherever FileFingerprint.isAudioFile is
currently used; update callers (e.g., the place that currently invokes
FileFingerprint.isAudioFile) to use FileUtils.isAudioFile(...) directly so the
duplicated logic in FileFingerprint is eliminated and the shared FileUtils
implementation is reused.

In `@booklore-api/src/main/java/org/booklore/util/FileUtils.java`:
- Around line 86-91: The current validation in FileUtils.java only checks the
candidate or its immediate parent; update the logic in the method that computes
existingAnchor so it walks up the ancestor chain from normalizedCandidate until
it finds the first existing ancestor (loop using Path.getParent()), set
existingAnchor to that path, and if none found return; ensure you still preserve
normalization and use Files.exists(existingAnchor) for the check (adjust the
existingAnchor variable and its use accordingly).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9f454891-4df9-4d64-b644-76ecf6464bd3

📥 Commits

Reviewing files that changed from the base of the PR and between 79e0efd and 5e4e98d.

📒 Files selected for processing (3)
  • booklore-api/src/main/java/org/booklore/service/book/BookDownloadService.java
  • booklore-api/src/main/java/org/booklore/service/file/FileFingerprint.java
  • booklore-api/src/main/java/org/booklore/util/FileUtils.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • booklore-api/src/main/java/org/booklore/service/book/BookDownloadService.java

Comment on lines +55 to 57
if (!Files.exists(normalizedFolderPath) || !Files.isDirectory(normalizedFolderPath)) {
throw new RuntimeException("Folder does not exist: " + normalizedFolderPath);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Redundant existence check — this code is unreachable.

validateReadableFolderPath(folderPath) at line 53 already verifies that the folder exists and is a directory (lines 117-118), throwing RuntimeException on failure. This check can never evaluate to true.

🧹 Proposed fix — remove dead code
     public static String generateFolderHash(Path folderPath) {
         Path normalizedFolderPath = validateReadableFolderPath(folderPath);
         try {
-            if (!Files.exists(normalizedFolderPath) || !Files.isDirectory(normalizedFolderPath)) {
-                throw new RuntimeException("Folder does not exist: " + normalizedFolderPath);
-            }
-
             List<Path> audioFiles;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@booklore-api/src/main/java/org/booklore/service/file/FileFingerprint.java`
around lines 55 - 57, Remove the redundant existence check that follows
validateReadableFolderPath: delete the if-block that tests
Files.exists(normalizedFolderPath) || Files.isDirectory(normalizedFolderPath)
and its RuntimeException throw, because validateReadableFolderPath(folderPath)
already validates existence and directory-ness; keep using normalizedFolderPath
afterwards and rely on validateReadableFolderPath and its RuntimeException for
error handling (references: validateReadableFolderPath and
normalizedFolderPath).

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.

1 participant