Skip to content

feat(disk-tools): add standalone File Shredder tool#53

Merged
dbfx merged 3 commits intomainfrom
feat/file-shredder
Mar 23, 2026
Merged

feat(disk-tools): add standalone File Shredder tool#53
dbfx merged 3 commits intomainfrom
feat/file-shredder

Conversation

@dbfx
Copy link
Contributor

@dbfx dbfx commented Mar 23, 2026

Summary

  • Adds a new File Shredder tool under the Disk Tools sidebar menu (/file-shredder)
  • Users can pick arbitrary files and folders via native dialogs, review the list, then securely shred them
  • 2-pass overwrite (random bytes + zeros with datasync) before rm — same algorithm as the cleaner's secure delete, but as a standalone on-demand tool
  • Full safety layer: protected-path blocklist (OS system dirs, user profile roots, root-level paths), symlink skipping via lstat, recursion depth limit (50), confirmation dialog, and cancellation support

Files changed

  • IPC handler: src/main/ipc/file-shredder.ipc.ts — file/folder pickers, shred logic, progress, cancel
  • Types: ShredderEntry, ShredderProgress, ShredderResult in src/shared/types.ts
  • Channels: 6 new shredder:* IPC channels
  • Preload: 6 new window.kudu API methods
  • Store: Zustand store in src/renderer/src/stores/file-shredder-store.ts
  • Page: FileShredderPage.tsx — full UI with add/remove items, progress bar, results summary
  • Navigation: Added to Disk Tools flyout in sidebar + route in App.tsx
  • i18n: English translations in fileShredder.json

Test plan

  • Add files via "Add Files" button, verify they appear in the list with correct sizes
  • Add folders via "Add Folders" button, verify recursive size calculation
  • Remove individual items from the list via X button
  • Clear all items via "Clear All"
  • Shred files and verify the confirmation dialog appears first
  • Verify progress bar updates during shredding
  • Cancel mid-shred and verify it stops
  • Verify protected paths (e.g. system directories) are rejected with an error
  • Verify symlinks in a selected folder are skipped, not followed
  • Verify the tool appears in the Disk Tools flyout menu in the sidebar

🤖 Generated with Claude Code

Add a new File Shredder under the Disk Tools menu that lets users
securely destroy arbitrary files and folders on demand. Data is
overwritten with random bytes then zeroed (2-pass) before deletion,
making recovery virtually impossible.

Safety measures:
- Protected-path blocklist (system dirs, user profile roots)
- Root-level directory rejection
- Symlink skipping throughout (lstat, isSymbolicLink checks)
- Recursion depth limit (50) to prevent stack overflow
- Confirmation dialog before shredding
- Cancellation support mid-operation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3b187da1c5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Address three review findings:

- P1: Check isProtectedPath() on every child directory during recursion,
  not just on the top-level user-selected paths. Prevents shredding
  .ssh, Desktop, Documents etc. inside a selected home folder.

- P1: Replace rm(dir, { recursive: true }) with bottom-up rmdir() that
  only removes empty directories. If the depth limit (50) truncated
  collection, files beyond that depth were never shredded — recursive rm
  would have silently deleted them without the overwrite pass.

- P2: Add `cancelled` boolean to ShredderResult. Final progress event
  now reports actual progress instead of blanket 100%. UI returns to
  idle on cancel so remaining entries stay visible for retry.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@dbfx
Copy link
Contributor Author

dbfx commented Mar 23, 2026

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e445f6bf09

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

… cleanup

- P1: Check `cancelled` between each 1 MB chunk inside shredFile() so
  a multi-GB overwrite can be interrupted within ~1 MB of the cancel
  request, instead of running both passes to completion.

- P2: Deduplicate collected file list before processing. Overlapping
  selections (parent folder + child, or folder + explicit file inside
  it) no longer shred the same file twice / report a bogus ENOENT
  failure on the second attempt.

- P2: removeEmptyDirs() now checks isProtectedPath() on each child
  directory before recursing, matching the guard in collectFiles().
  Prevents rmdir() from removing an empty protected directory (e.g.
  .ssh, Desktop) that was correctly skipped during shredding.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@dbfx dbfx merged commit 9cfdce8 into main Mar 23, 2026
8 checks passed
@dbfx dbfx deleted the feat/file-shredder branch March 23, 2026 12:45
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 982d2a5732

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +327 to +331
await shredFile(filePath)
await rm(filePath, { force: true })
const fileSize = fileSizes.get(filePath) || 0
bytesShredded += fileSize
shredded++

Choose a reason for hiding this comment

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

P1 Badge Do not delete the current file after a cancel

shredFile() now returns early when cancelled flips, but this caller treats that as success and immediately runs rm(filePath, { force: true }) and increments the shredded counters. If the user cancels while a large file is mid-overwrite, the current file is still deleted and reported as fully shredded even though only part of the overwrite pass actually ran.

Useful? React with 👍 / 👎.


function isProtectedPath(targetPath: string): boolean {
const normalized = normalize(resolve(targetPath)).replace(/\\/g, '/')
const name = basename(normalized).toLowerCase()

Choose a reason for hiding this comment

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

P2 Badge Reject paths nested under protected directories

isProtectedPath() only checks the final path segment against the blocklist, so nested selections like ~/.ssh/id_rsa or %USERPROFILE%\\AppData\\Roaming\\Vendor pass validation because their basename is not .ssh/appdata. That bypasses the safety guard for the exact directory trees this feature is trying to protect, as soon as the user selects a child path directly instead of its parent.

Useful? React with 👍 / 👎.

Comment on lines +130 to +134
for (const entry of entries) {
if (entry.isSymbolicLink()) continue
const fullPath = join(dirPath, entry.name)
if (entry.isDirectory()) {
if (isProtectedPath(fullPath)) continue

Choose a reason for hiding this comment

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

P2 Badge Honor cancellation while enumerating selected folders

The cancel IPC only flips a module flag, but collectFiles() never observes it while walking directories. On a large folder selection, clicking Cancel does nothing until the entire tree has been enumerated and the handler finally reaches the later if (cancelled) break in the shred loop, so the longest phase of the operation remains uninterruptible.

Useful? React with 👍 / 👎.

@dbfx dbfx restored the feat/file-shredder branch March 23, 2026 12:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant