Skip to content

Fix trash restore regressions from path hardening#232

Merged
runkids merged 1 commit into
runkids:mainfrom
jnhu76:fix/trash-restore-followup
Jun 17, 2026
Merged

Fix trash restore regressions from path hardening#232
runkids merged 1 commit into
runkids:mainfrom
jnhu76:fix/trash-restore-followup

Conversation

@jnhu76

@jnhu76 jnhu76 commented Jun 16, 2026

Copy link
Copy Markdown

Summary

This follow-up fixes two compatibility regressions introduced by the trash path hardening change:

  1. Nested entries returned by List() could use OS-native separators in TrashEntry.Name. On Windows this produced names like org\demo, which were then rejected by Restore() because validateTrashName() correctly rejects backslashes.
  2. Restoring into the current directory, such as source: . / agents_source: ., was incorrectly rejected by the previous string-prefix containment check.

Changes

  • Added trashLogicalName(parentRel, name) to build slash-separated logical trash names.
  • Updated List() to use slash-separated logical names like org/demo for TrashEntry.Name.
  • Kept TrashEntry.Path as an OS-native filesystem path.
  • Reworked ensureUnderBase / ensureStrictlyUnderBase to use filepath.Abs + filepath.Rel containment checks instead of string-prefix checks.
  • Kept validateTrashName() backslash rejection unchanged.

Tests

Added regression and guard coverage:

  • TestRestoreAllowsCurrentDirectoryDestination
  • TestEnsureUnderBaseAllowsChildOfDotButRejectsEscapes
  • TestRestoreNestedEntryReturnedByList
  • TestTrashLogicalNameNormalizesOSNativeSeparators
  • TestTrashLogicalNameDoesNotTranslateLiteralUnixBackslashes
  • TestEnsureUnderBaseRejectsSiblingPrefix
  • TestValidateTrashNameRejectsBackslash

Security boundary

This patch preserves the original hardening semantics:

  • Backslash remains invalid trash-name input.
  • Escaping paths are still rejected.
  • Shared-prefix sibling paths such as skills-evil are rejected.
  • Cleanup() still refuses to operate on the trash base itself.
  • Logical trash names and filesystem paths remain separate.
  • Unix literal backslashes are not translated into logical path separators.

Validation

gofmt -l internal/trash/trash.go internal/trash/trash_security_test.go
go test ./internal/trash/... -count=1
go test ./internal/trash/... -race
go vet ./internal/trash/...
grep -R "filepath.Join(parentRel, name)" -n internal/trash
git diff --check

go test ./... reaches pre-existing integration failures that require a built CLI binary in PATH. The trash package tests pass, and the observed full-suite failure is unrelated to this patch.

@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: 5b07ed4c6d

ℹ️ 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 internal/trash/trash.go Outdated
// filepath.ToSlash handles OS-native separators. ReplaceAll also handles
// literal backslashes when Windows-style parent paths are simulated on Unix.
parentRel = filepath.ToSlash(parentRel)
parentRel = strings.ReplaceAll(parentRel, `\`, "/")

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 Leave Unix backslashes invalid instead of separators

On Unix, backslash is a legal filename character, not an OS separator. Replacing it here means a malformed or legacy trash parent such as ..\outside is emitted by List() as ../outside/<name>, violating the existing invariant that listed names can be joined under a destination without escaping; filepath.ToSlash already handles real Windows separators when running on Windows, so literal Unix backslashes should remain invalid rather than being translated into logical path separators.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good catch — fixed. I removed the literal backslash replacement and now only rely on filepath.ToSlash() for OS-native separators. Added a Unix guard test to ensure literal backslashes are not translated into /, while validateTrashName() still rejects backslash input.

- List() now uses trashLogicalName() to produce slash-separated logical
  names, preventing Windows backslash paths that validateTrashName rejects.
- ensureUnderBase/ensureStrictlyUnderBase now use filepath.Abs+filepath.Rel
  instead of string prefix, fixing false escapes when base is '.'.
- Add regression tests for Restore(entry, '.'), nested List->Restore roundtrip,
  sibling prefix rejection, and validateTrashName backslash enforcement.
@jnhu76 jnhu76 force-pushed the fix/trash-restore-followup branch from 5b07ed4 to aa3151e Compare June 16, 2026 23:31
@runkids

runkids commented Jun 17, 2026

Copy link
Copy Markdown
Owner

Thanks! LGTM.

@runkids runkids merged commit 6bddfbb into runkids:main Jun 17, 2026
7 checks passed
@jnhu76 jnhu76 deleted the fix/trash-restore-followup branch June 17, 2026 08:12
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.

2 participants