Skip to content

fix(core): only restore config files to places Repot scans - #108

Merged
junjuny0227 merged 2 commits into
mainfrom
fix/snapshot-target-allowlist
Nov 5, 2026
Merged

junjuny0227 merged 2 commits into
mainfrom
fix/snapshot-target-allowlist

Conversation

@junjuny0227

Copy link
Copy Markdown
Owner

Summary

An opened repot.json only checked that a config-file target was relative and had no ... A hostile file from someone else could therefore carry an item (with its contents) that the restore script writes to ~/.ssh/authorized_keys, ~/Library/LaunchAgents/x.plist or ~/.config/autostart/x.desktop. These are places Repot itself never writes. Now a config-file or shell-plugin target is accepted only if the item's provider produces it on a scan. Any other item is skipped and counted, the same way other unsafe items already are (the app and CLI already report skipped_items). Spec §10 (and §15 for the one limit), plan 11 Global Constraints. The same hole is described in #107 (plan 16, SSH). Its ssh::is_config_target fits into the same dispatcher.

Type

  • feat
  • fix
  • refactor / chore
  • docs
  • ci / build

Changes

  • Each provider exposes pub(crate) fn is_config_target(rel), built from the same constants its scan uses (now named constants instead of inline literals):
    • git: CONFIG_FILES (.gitconfig, .config/git/config), DEFAULT_IGNORE (.config/git/ignore), plus a custom core.excludesFile only if it sits directly in home or .config/git/ and its name contains ignore.
    • shell: RC_FILES (the 7 startup files it scans), PowerShell profile via TargetPath::PowerShellProfile. Plugins use is_plugin_target: one folder directly under .oh-my-zsh/custom/{plugins,themes}/ (OMZ_KINDS, plugin_target).
    • vscode (VS Code and Cursor): USER_FILES (settings.json, keybindings.json) and one file in SNIPPETS_DIR.
    • jetbrains: one file with the right extension in a SETTINGS_DIRS folder, or options/ + OPTION_FILES.
  • snapshot::is_scanned_config_target(provider, target) pairs each provider with its target kind (git/shell → home, shell → PowerShell profile, vscode → editor user folder, jetbrains → IDE folder). Every other provider/kind pair is rejected. Shell plugins must also come from the shell provider.
  • Tests (snapshot_test.rs):
    • skips_config_files_aimed_where_repot_never_writes: 28 hostile items are skipped (.ssh/authorized_keys from git and shell, Library/LaunchAgents/x.plist, .config/autostart/{x,ignore}.desktop, a git item targeting .zshrc, cross-provider and cross-kind targets, editor/IDE files Repot doesn't read, nested or out-of-place plugin folders, an unknown provider). 8 legitimate edge targets still load.
    • every_place_a_real_scan_writes_loads_from_the_file: on macOS and Windows FakeEnvs, the real Git, ShellProvider, VsCode and JetBrains providers scan every file they know (27 config files + 2 plugins each, including a custom excludesFile). The export → repot.json → open path skips nothing, and re-exporting gives the same bundle.
    • The existing JetBrains round-trip test now builds its item with provider: "jetbrains". It used to reuse the .zshrc item's provider. The committed fixture v1-basic.json is untouched and still loads with 0 skipped.
  • Mutation check: I broke each of 13 rules once (dispatcher, plugin provider check, plugin target check, provider/kind pairing, git ignore folder rule, git ignore name rule, git custom ignore dropped, shell rc list, plugin nesting, snippet nesting, snippets dropped, JetBrains extension, JetBrains option list). Every mutation made a test fail, and each file was restored from a backup copy.

Checks

  • cargo fmt --check
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo test --workspace
  • pnpm --filter @repot/desktop typecheck / test / build
  • Ran the real flow if behavior changed (--example scan / --example export, or tauri dev)
  • Not checked locally, CI only (say which, e.g. Windows/Linux providers, shellcheck):

Also ran: pnpm --filter @repot/desktop test and build (no frontend change; typecheck not run), pnpm check, and the CI example tests cargo test -p repot-core --example e2e_export --example smoke_export --example cross_os_e2e --example render_samples. Not run: the real app or CLI on this Mac. Windows is covered only by the FakeEnv test.

Spec and generated files

  • Spec updated in this PR if a product decision changed, or no decision changed
  • Generated files regenerated from their source, not edited by hand (scan.json, snapshots/, icons/) (none changed)
  • Snapshot diffs read and intended

Boundaries

  • repot-core does not depend on Tauri and does no network calls
  • Commands and file reads go through Env; tests use FakeEnv and fixtures, not real tools
  • No new crate or npm dependency, or the plan/spec was updated first
  • The app still only reads the device; it writes only to the export folder

Known limit (spec §15): a core.excludesFile elsewhere (e.g. ~/dotfiles/gitignore) is still exported from a scan, but that item is skipped when the file is opened again.

@github-actions github-actions Bot added the bug Something isn't working label Nov 5, 2026
@junjuny0227
junjuny0227 merged commit f16cf71 into main Nov 5, 2026
16 checks passed
@junjuny0227
junjuny0227 deleted the fix/snapshot-target-allowlist branch November 5, 2026 03:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant