Skip to content

📦🔧:check the file the importer opened - #143

Merged
openinf-commit-queue[bot] merged 1 commit into
mainfrom
claude/project-thread-fqaph2-importer
Nov 2, 2026
Merged

openinf-commit-queue[bot] merged 1 commit into
mainfrom
claude/project-thread-fqaph2-importer

Conversation

@DerekNonGeneric

@DerekNonGeneric DerekNonGeneric commented Nov 2, 2026 •

Copy link
Copy Markdown
Member

Requested by DerekNonGeneric

Before: every destination check in writeWithinDestDir checked a path, not the file actually opened. Suppose a directory was swapped for a symbolic link after the last check. The open call would then follow the link, and O_TRUNC would empty whatever file it reached before anything was written (CWE-367). Separately, the download log line and the error for a failed download printed the URL exactly as given. A URL containing a line break could fake an extra log line, and one containing a terminal escape could change how the terminal displays output (CWE-117). CodeRabbit's security deepscan reported both.

After: on Linux, a swapped-in link is refused before any byte of the outside file changes. The worst a winning race can do is create an empty file outside destDir. Other platforms offer no way to ask where an open file descriptor points. For them, the README now asks callers to use a destDir that no less trusted user can change during an import. Control characters in a URL are percent-encoded in both the log line and the error message.

How: the destination file is now opened with O_CREAT | O_WRONLY | O_NOFOLLOW, without O_TRUNC. On Linux, the code reads the descriptor's real path from /proc/self/fd/<fd> and refuses the write if it is outside the physical destDir. Only after that check does it call handle.truncate(0) and write. printableUrl percent-encodes C0 and C1 control characters. I added three tests:

  • A race test mocks open so the parent directory is swapped for a link at the last moment. The file outside destDir keeps its contents. This test is skipped on platforms other than Linux.
  • A test checks that a longer existing file is still fully overwritten.
  • A test checks that a URL containing CR, LF, ESC and NEL appears percent-encoded in both the log line and the error.

The race test and the URL test fail against the old code. All 69 tests pass on Node 24.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • File imports now reject destinations that resolve outside the chosen directory, including certain symlink-based paths. On Linux, the opened destination is checked before it is changed.
    • Control characters in download URLs are percent-encoded in logs and error messages, making them safer to display.
  • Documentation
    • Clarified that on platforms without the Linux destination check, the import directory should not be changeable by less-trusted processes during an import.

@DerekNonGeneric DerekNonGeneric self-assigned this Nov 2, 2026
@coderabbitai

coderabbitai Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: 87ac4204-b8c9-47c1-96b8-d7d6dd0e8878
Commits

Reviewing files that changed from the base of the PR and between 992bb6e and 25b50b8.

Files selected for processing: 3
  • packages/gh-file-importer/README.md
  • packages/gh-file-importer/src/index.ts
  • packages/gh-file-importer/test/index.ts
Files skipped from review as they are similar to previous changes: 1
  • packages/gh-file-importer/README.md

Included review availability: This review used your included allowance. 0 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 5 reviews per hour.

Walkthrough

The importer checks opened destination files on Linux before truncating or writing them. It also percent-encodes control characters in URLs displayed in download logs and HTTP failure messages.

Changes: Destination file safety

Layer / File(s) Summary
Validate opened destination before writing
packages/gh-file-importer/src/index.ts, packages/gh-file-importer/README.md, packages/gh-file-importer/test/index.ts, .changeset/importer-opened-file.md
The importer opens the destination without truncation. On Linux, it checks the opened descriptor’s physical path before truncating or writing. Documentation describes destination-tree requirements and the limits of the check. Tests cover shorter overwrites, a directory-swap race, and descriptor lookup errors.

Printable download URLs

Layer / File(s) Summary
Encode control characters in displayed URLs
packages/gh-file-importer/src/index.ts, packages/gh-file-importer/test/index.ts
Download-start logs and HTTP failure messages use URLs with C0 and C1 control characters percent-encoded. A test checks the encoded output.

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

Possibly related PRs

  • OpenINF/sdk#130: Changes the same importer destination-write path. This PR adds a check of the opened destination descriptor before truncation and writing.
Merge Risk: Moderate · up to 25b50

On restricted Linux systems, a directory swap can still overwrite a file outside the destination. Handle unexpected lookup failures before merging.

Pre-merge checks: Passed 5
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly identifies the main change: checking the file that the importer opened before writing. The emojis are unnecessary, but they do not make the title unrelated or misleading.
Docstring Coverage Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat…
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.
Full details: Docstring Coverage

Explanation

Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 2 functions across 2 files. (1 skipped: 1 unsupported.)

Finishing Touches
Generate docstrings
  • Commit to this branch
  • Create a new PR
Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.

@coderabbitai coderabbitai 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.

  • Fix comments: Fix CodeRabbit comments on this PR

Comment thread packages/gh-file-importer/README.md Resolved
Comment thread packages/gh-file-importer/src/index.ts Resolved

Copy link
Copy Markdown
Member Author

@coderabbitai review


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@DerekNonGeneric
DerekNonGeneric force-pushed the claude/project-thread-fqaph2-importer branch from 679fac5 to 1e5bded Compare November 2, 2026 23:27
Every check writeWithinDestDir made was of a path. A directory
swapped for a symbolic link after the last check still redirected
the open, and O_TRUNC emptied whatever file it reached before a byte
was written. The file is now opened without truncating it, and on
Linux /proc/self/fd names the file the descriptor actually reached;
one outside destDir is refused before any of it changes. Where /proc
is mounted but the lookup fails, the import fails rather than writing
unchecked. A swap that wins the race costs at most an empty file
created outside. No other platform, and no Linux system without
/proc, says where a descriptor leads, so the README now asks for a
destDir nobody less trusted than the caller can change mid-import.

The download log line and the failed-download error printed the URL
as given, so a line break in it could forge a log line and an escape
could restyle the terminal. Control characters in it are now
percent-encoded, the way a URL carries them.

Signed-off-by: Derek Lewis <DerekNonGeneric@inf.is>
Assisted-by: Claude-Code:claude-opus-5
@DerekNonGeneric
DerekNonGeneric force-pushed the claude/project-thread-fqaph2-importer branch from 1e5bded to 25b50b8 Compare November 2, 2026 23:33
@DerekNonGeneric DerekNonGeneric added the 🚀 Status: Commit Queue Land this pull request when its checks pass label Nov 2, 2026 — with Claude
@openinf-commit-queue
openinf-commit-queue Bot merged commit 4ef9d63 into main Nov 2, 2026
11 checks passed
@openinf-commit-queue openinf-commit-queue Bot removed the 🚀 Status: Commit Queue Land this pull request when its checks pass label Nov 2, 2026
@openinf-commit-queue
openinf-commit-queue Bot deleted the claude/project-thread-fqaph2-importer branch November 2, 2026 23:44
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