Skip to content

fix(sftp): reject archive-window folder drags with a clear extract-first error - #3687

Draft
netcatty-bot wants to merge 2 commits into
mainfrom
ai/issue-3057-38709428820
Draft

netcatty-bot wants to merge 2 commits into
mainfrom
ai/issue-3057-38709428820

Conversation

@netcatty-bot

@netcatty-bot netcatty-bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Draft proposal to reject standalone, pathless, zero-byte, extensionless upload entries and show an extract-first message. Not ready to merge: this condition also matches legitimate empty files, and the actual WinRAR 5.40 drop metadata in #3057 is still unconfirmed.

Type of Change

  • Bug fix (proposed; currently blocked)

Related Issue (optional)

Related to #3057. This proposal does not establish that the reported folder and TXT failures are fixed; the issue must remain open.

Changes Made

  • Adds a heuristic filter before upload conflict handling, plus a localized refusal message and mappings in drag/drop and paste result reporting.
  • Applies the filter in the common upload service, including local and remote destinations.
  • Adds tests for the assumed pseudo-folder shape and mixed successful uploads.

Known blockers / evidence (2026-10-16)

At head 94fa2fe3a6c4e618e1bf65b357788340344e9bb7, a comparison through the unmodified production capture/upload functions confirms:

  1. A readable empty README with no disk path uploads at the merge base, but this PR rejects it as a folder. The same happens when the drag item explicitly supplies isFile: true. A path-backed empty file and a readable pathless file with content still upload. File size and name do not establish directory identity.
  2. A file with content and nonempty relative path web/same.txt bypasses the filter, reaches the local filesystem read, and still fails with ENOENT. This is a controlled comparison matching the earlier relative-path failure, not proof of the native WinRAR payload.
  3. A rejected item plus a normal same-name file whose conflict is skipped returns a failure and a cancellation together. The shared reporter shows only cancellation and hides the extract-first failure.

The reporter already supplied Netcatty 1.1.83, WinRAR 5.40 and a video covering archive folders and TXT files. No repeat request for those materials is needed. The remaining discriminating fact is the actual Windows drag metadata, as described in the existing issue evidence comment.

The former assertion that WinRAR/7-Zip archive drags always expose only FILEDESCRIPTOR/FILECONTENTS as a pathless, empty, extensionless File was unsupported. Electron documents empty paths for JavaScript-created files, and its implementation returns the underlying blob path. Windows supports several shell transfer formats, and explicitly permits zero-length files in FILEDESCRIPTOR. These sources do not identify what WinRAR 5.40 supplied in this report. Chromium's current Windows implementation has separate virtual-file and temporary-file handling; its current source is not version-specific acceptance for Netcatty 1.1.83.

Screenshots / Demo

No native Windows WinRAR acceptance completed. Prior full-app controls validated ordinary extracted-folder Merge and readable pathless-file upload; those controls do not prove this proposed filter fixes archive drag/drop.

Testing

  • 49 focused existing tests pass: domain filter, upload service, shared reporter, drop capture/materialization.
  • Head-versus-base production-entry comparison with real disk staging/copy verifies the regressions and positive controls above.
  • Two independent full-diff reviews completed; neither is clean.
  • Native Windows WinRAR folder and TXT drag acceptance.
  • All active review findings resolved with discriminating evidence.

Current remote CI passes, but it does not cover the reproduced regressions. The current-head remote review has active findings; its earlier “no major issues” summary does not override them. No source changes or new review requests were made during the disposition audit.

Checklist

  • Proposed behavior safely distinguishes an unreadable folder from a supported empty file.
  • The original report is covered by a demonstrated fix.
  • No breaking changes.

Keep this draft pending evidence. Do not merge on the present diagnosis. Issue #3057 must remain open.

@netcatty-bot netcatty-bot added automation:bot-pr PR created or owned by Cursor automation automation:codex-loop Own/bot PR waiting on Codex review↔fix loop triage Touched by Cursor automation labels Oct 15, 2026
@binaricat

Copy link
Copy Markdown
Owner

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-15T10:02:54.398512Z 94fa2fe Draft marked ready
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

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

Copy link
Copy Markdown

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: f6afecbc0a

ℹ️ 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 thread domain/sftpVirtualDrop.ts
Comment on lines +47 to +48
const size = entry.file?.size ?? entry.size;
return size === undefined || size === 0;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve legitimate empty extensionless files

A legitimate zero-byte, extensionless virtual file (for example an empty README dragged from an archive, email client, or another shell data source without a disk path) has exactly the same shape as this heuristic and is therefore rejected as a folder. Since file size and the absence of a dot do not identify a directory, this prevents supported empty files from being uploaded; the rejection needs an actual folder/source signal or must otherwise avoid classifying ambiguous zero-byte files as directories.

Useful? React with 👍 / 👎.

Comment on lines +44 to +46
const rawError = isSftpVirtualFolderUploadError(item.error)
? t("sftp.upload.virtualFolderError")
: item.error;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Localize the sentinel in every upload-result consumer

This translation only covers callers of reportSftpUploadResults, but the native SFTP paste flow calls uploadExternalEntries and reports failures through showUploadResults in useSftpKeyboardShortcuts.ts, where failed.error is concatenated directly. If a path-less archive item arrives through that supported clipboard path, users see the raw netcatty:virtual-folder-upload marker rather than the extract-first message; either all consumers need the mapping or the upload layer should return a display-safe error.

Useful? React with 👍 / 👎.

@binaricat

Copy link
Copy Markdown
Owner

@codex review

Repository owner deleted a comment from binaricat Oct 15, 2026
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

Reviewed commit: 94fa2fe3a6

ℹ️ 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".

@netcatty-bot
netcatty-bot marked this pull request as ready for review October 15, 2026 10:00
@netcatty-bot netcatty-bot added automation:codex-clean Last Codex review reported clean and removed automation:codex-loop Own/bot PR waiting on Codex review↔fix loop labels Oct 15, 2026
@netcatty-bot

Copy link
Copy Markdown
Collaborator Author

Codex reported no major issues. This PR is marked ready for human review/merge.

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

Copy link
Copy Markdown

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: 94fa2fe3a6

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread lib/uploadService.ts
Comment on lines +288 to +291
const withVirtualFolderResults = (results: UploadResult[]): UploadResult[] => [
...virtualFolderResults,
...results,
];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Report virtual-folder failures alongside skipped items

When a mixed drop contains a rejected archive folder and another entry whose conflict is skipped or stopped, this helper returns both the sentinel failure and a cancelled result. Both reportSftpUploadResults and showUploadResults immediately return when any result is cancelled, so the new extract-first error is never shown in that scenario; process the non-cancelled failures before the cancellation shortcut, or avoid combining these result types.

Useful? React with 👍 / 👎.

@binaricat
binaricat marked this pull request as draft October 16, 2026 02:16

@binaricat binaricat left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Audit of 94fa2fe3a6c4e618e1bf65b357788340344e9bb7: changes requested. Kept this PR as a draft and replaced the automatic issue-closing statement with a related-issue reference.

P2 — preserve readable empty files (domain/sftpVirtualDrop.ts:43-48, lib/uploadService.ts:272-278). I compared the unmodified production uploadFromDataTransfer path against its merge base using real filesystem staging and destination writes. new File([], 'README') uploads a zero-byte file at the base; this PR returns netcatty:virtual-folder-upload before any staging. An explicit file entry (isFile: true, isDirectory: false) is also rejected. A path-backed empty file and a pathless file with content both still upload. The same filter covers local destinations too. Neither zero length nor absence of an extension is directory evidence. Please remove the ambiguous rejection or use a demonstrated source/directory signal, and preserve supported empty-file upload.

P2 — mixed skipped items hide the failure (lib/uploadService.ts:288-290). A rejected root plus notes.txt whose conflict is skipped returns both the sentinel failure and {cancelled:true}. Passing that actual result to reportSftpUploadResults yields only sftp.upload.cancelled; the promised actionable error is hidden. The paste reporter has the same early return. This confirms the current-head remote finding; it is not resolved merely because the CI is green.

Original-report coverage remains unproven. A controlled file with content and path: 'web/same.txt' bypasses localPath at domain/sftpVirtualDrop.ts:38, reaches the filesystem read and still returns ENOENT at both base and PR head. Yesterday's actual Electron/UI controls exhibited this relative-path failure; they were not native WinRAR metadata. The reporter's video covers both archive folder and TXT failures, so a guessed empty-folder rejection cannot establish full coverage.

The PR's former statement that WinRAR/7-Zip always supply only FILEDESCRIPTOR/FILECONTENTS, rendering a pathless zero-size extensionless pseudo-file, is not established by the sources. Electron's API also returns an empty path for JavaScript-created Files; Windows documents multiple shell formats and valid zero-length FILEDESCRIPTOR files. Chromium current Windows source separately handles virtual names and materialized temporary files. None determines WinRAR 5.40's actual metadata in this report.

49 focused existing tests pass; two independent full-diff reviews confirm the empty-file regression. No further guessed classifier or drag/drop framework was added. Native WinRAR metadata is still the least missing fact; the existing diagnostic/evidence comment already requests precisely that, and the reporter need not resend versions, ZIP or video. Please leave #3057 open and this proposal draft until the native data distinguishes the actual failing path and a bounded fix can be verified.

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

Labels

automation:bot-pr PR created or owned by Cursor automation automation:codex-clean Last Codex review reported clean triage Touched by Cursor automation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants