Skip to content

fix(api): confine notification, log, and API-key file operations - #2076

Merged
Eli Bosley (elibosley) merged 3 commits into
mainfrom
fix/notification-path-validation
Sep 8, 2026
Merged

fix(api): confine notification, log, and API-key file operations#2076
Eli Bosley (elibosley) merged 3 commits into
mainfrom
fix/notification-path-validation

Conversation

@elibosley

@elibosley Eli Bosley (elibosley) commented Sep 8, 2026

Copy link
Copy Markdown
Member

Summary

Confine notification, log, and API-key file operations to their storage directories. Notification IDs previously allowed outside-file deletion; the broader audit also found invalid log watcher paths and missing validation of stored API-key IDs.

Why This Exists

Work intent: FeatureOS security report 482091, expanded to audit other API filesystem boundaries.

The notification regression reproduced deletion of an outside temporary file. Log subscriptions accepted parent-directory paths. API-key saves and deletes trusted IDs supplied internally or loaded from disk. The public API generates new key IDs and looks up existing IDs, so the API-key change is additional protection against unsafe stored/internal data, not a demonstrated remote key-creation exploit.

Resolution

Extract the notification filename check into resolveFileInDirectory. Reject empty filenames, dot directory entries, separators, colons, and null bytes; require the resolved parent to equal the storage directory. Apply it before notification operations, log reads/subscriptions, and API-key reads/writes/deletes. Validate all deletion paths before deleting any key.

Reviewer Considerations

  • Preserve filename extensions, punctuation, Unicode, and malformed notification cleanup.
  • Preserve full log paths returned by log listing through the existing basename conversion, followed by validation.
  • Keep PrefixedID general: valid resource IDs such as network metric IDs contain slashes. Enforce filename rules at filesystem consumers.
  • Skip unsafe API-key records during disk loading rather than admitting them into the memory store.

Audit Scope

Inspected filesystem calls and path construction in api/src and shipped workspace package sources, then traced candidate values to their callers.

Surface Finding and disposition
Notifications Caller-controlled traversal; fixed across create/write, delete, archive, and restore.
Logs Basename normalization left . and .. usable as watcher targets; reads and subscriptions now validate.
API keys New IDs are generated; update/delete use existing keys. Stored/internal IDs lacked path validation; load/save/delete now validate.
Cookies Existing alphanumeric session-ID sanitization prevents traversal; unchanged.
REST customization Selects fixed banner/case paths; unchanged.
Network/CPU metrics and Docker template scanning Names come from OS discovery or files enumerated in configured directories; unchanged.
Activation images and configuration Local configuration intentionally supports filesystem paths; not redefined as filename-only input.
writeToBoot Removed the unrestricted helper after confirming that it has no callers.

Verification

Node 22, pnpm 10.15.0, from the repository root:

  • pnpm --filter ./api test src/core/utils/files/resolve-file-in-directory.spec.ts src/unraid-api/auth/api-key src/unraid-api/auth/cookie.service.spec.ts src/unraid-api/graph/resolvers/api-key src/unraid-api/graph/resolvers/logs src/unraid-api/graph/resolvers/notifications/notifications.service.spec.ts src/unraid-api/rest/rest.service.spec.ts src/unraid-api/graph/resolvers/metrics/network/network.service.spec.ts — 177 tests passed across 12 files.
  • pnpm --filter ./api type-check — passed.
  • Targeted ESLint for all changed TypeScript files — passed.
  • git diff --check — passed.

The original notification tests passed before the fix, while the outside-file regression failed. Twelve additional log/API-key regression cases failed before their protections were added.

Risk and Limits

Main compatibility concern: locally supplied API-key IDs containing filename separators or colons now fail. Valid IDs and full log paths remain supported by tests. This audit addresses application path traversal, not malicious replacement of trusted directories or symlinks by a local writer. It is not a guarantee that every filesystem-related security issue is absent. No live NAS was modified.

Summary by CodeRabbit

  • Bug Fixes
    • Improved file path validation for notifications, API keys, and logs.
    • Blocked path traversal, invalid filenames, absolute paths, and unsafe identifiers.
    • Preserved support for valid filenames, including those with special characters.
    • Prevented unsafe API key files from being loaded, created, or deleted.
  • Tests
    • Added coverage for invalid paths, safe filenames, and normalized log paths.

Validate notification filenames before filesystem access for delete, archive, restore, and creation. Preserve supported filenames and malformed notification cleanup.

Work intent: FeatureOS security report 482091. Regression coverage reproduces deletion outside the notification directory on the original implementation.
@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: a7ffad24-790c-4a72-a208-1a888d349cca

📥 Commits

Reviewing files that changed from the base of the PR and between 9a0cc88 and be84f44.

📒 Files selected for processing (7)
  • api/src/core/utils/files/resolve-file-in-directory.spec.ts
  • api/src/core/utils/files/resolve-file-in-directory.ts
  • api/src/unraid-api/auth/api-key.service.spec.ts
  • api/src/unraid-api/auth/api-key.service.ts
  • api/src/unraid-api/graph/resolvers/logs/logs.service.spec.ts
  • api/src/unraid-api/graph/resolvers/logs/logs.service.ts
  • api/src/unraid-api/graph/resolvers/notifications/notifications.service.ts

Included review availability: Your plan provides up to 2 included reviews per hour; 0 remain after this review.


Walkthrough

The API adds a shared file path validation utility. Notification, API-key, and log services use it to reject unsafe paths and prevent directory traversal. Tests cover invalid inputs, safe filenames, and filesystem operations.

Changes

File path validation

Layer / File(s) Summary
Shared path validation utility
api/src/core/utils/files/resolve-file-in-directory.ts, api/src/core/utils/files/resolve-file-in-directory.spec.ts
The new utility rejects unsafe filenames and paths outside the target directory. Tests cover traversal, absolute paths, platform-specific separators, null bytes, safe filenames, and relative directories.
Service path integration
api/src/unraid-api/graph/resolvers/notifications/notifications.service.ts, api/src/unraid-api/auth/api-key.service.ts, api/src/unraid-api/graph/resolvers/logs/logs.service.ts
Notification, API-key, and log services resolve file paths through the shared utility for reads, writes, deletion, archiving, restoration, and subscriptions.
Service regression coverage
api/src/unraid-api/graph/resolvers/notifications/notifications.service.spec.ts, api/src/unraid-api/auth/api-key.service.spec.ts, api/src/unraid-api/graph/resolvers/logs/logs.service.spec.ts
Tests cover traversal, invalid identifiers, GraphQL prefixed IDs, malformed notification files, safe filenames, rejected API-key IDs, and prevented log filesystem actions.

Priority: ⬇️ Low — Defer this API path-validation change because its supplied scope is limited to confining notification, log, and API-key file operations within storage directories.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to be84f

File-backed notification, API-key, and log operations now reject unsafe paths while preserving supported filename and log-path behavior. No concrete merge-blocking risk remains.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 8 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: confining notification, log, and API-key file operations to their storage directories.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/notification-path-validation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

A rabbit reads each line,
The patch grows clear beneath the moon,
Small changes hop in place,
Tests guard the garden path,
Reviews bloom before the dawn.

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

@linear-code
linear-code Bot marked this pull request as ready for review September 8, 2026 13:50
@codecov

codecov Bot commented Sep 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.10526% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 53.36%. Comparing base (3ec4764) to head (ba02e3e).

Files with missing lines Patch % Lines
api/src/unraid-api/auth/api-key.service.ts 83.33% 2 Missing ⚠️
.../src/core/utils/files/resolve-file-in-directory.ts 92.30% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2076      +/-   ##
==========================================
+ Coverage   53.33%   53.36%   +0.03%     
==========================================
  Files        1043     1043              
  Lines       72664    72674      +10     
  Branches     8387     8395       +8     
==========================================
+ Hits        38758    38786      +28     
+ Misses      33779    33761      -18     
  Partials      127      127              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

This plugin has been deployed to Cloudflare R2 and is available for testing.
Download it at this URL:

https://preview.dl.unraid.net/unraid-api/tag/PR2076/dynamix.unraid.net.plg

Reuse notification filename validation at filesystem consumers. Reject directory-only log targets and unsafe stored key IDs, and validate deletion batches before unlinking.

Work intent: extend FeatureOS report 482091 remediation with a filesystem-boundary audit. Verified 177 focused tests and API type checks.
@elibosley Eli Bosley (elibosley) changed the title fix(notifications): reject unsafe notification paths fix(api): confine notification, log, and API-key file operations Sep 8, 2026
@elibosley
Eli Bosley (elibosley) merged commit 421b53e into main Sep 8, 2026
13 of 14 checks passed
@elibosley
Eli Bosley (elibosley) deleted the fix/notification-path-validation branch September 8, 2026 16:23
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

🔄 PR Merged - Plugin Redirected to Staging

This PR has been merged and the preview plugin has been updated to redirect to the staging version.

For users testing this PR:

  • Your plugin will automatically update to the staging version on the next update check
  • The staging version includes all merged changes from this PR
  • No manual intervention required

Staging URL:

https://preview.dl.unraid.net/unraid-api/dynamix.unraid.net.plg

Thank you for testing! 🚀

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