sync: bring tinyland main current with origin#65
Merged
Jess Sullivan (Jesssullivan) merged 10 commits intoJun 8, 2026
Merged
Conversation
A hostile peer can publish a SymlinkManifest whose target points outside
the sync root or at a local secret store; every pulling host previously
materialized it verbatim (create_local_symlink ran std::os::unix::fs::symlink
on the attacker-supplied target with no validation). All three production
restore callers (auto-roam Pull, FileProvider, CLI pull) flow through
create_local_symlink, so the guard is centralized there.
Reject, before the link is created (skip + structured warn, not a hard error
so one hostile entry does not abort the pull):
- empty target
- absolute target (is_absolute, plus explicit leading-/ , leading-\\ and
Windows Prefix screening)
- `..` that escapes above the link's own directory (lexical resolve, no
filesystem canonicalize); the link is always created inside the sync root,
so this is at least as strict as "stays within the root"
- resolved target hitting the fail-closed security deny-set
(check_security_path_components)
Also self-defends upload_symlink_with_device (egress) by bailing on the same
conditions, since that public API can be called outside the collector which
already screens targets.
No behavior change when no symlink is restored/uploaded; default-off config
paths are untouched.
The FileProvider direct-read path built a master-only EncryptionContext (EncryptionContext::new) and so could never read a per-device (wrapped_file_keys-only) manifest, unlike tcfsd and the CLI which both attach this device's age unwrap identity. This is the hard-gate prerequisite to ever flipping crypto.per_device_wrapping; this PR does NOT flip it (default stays false). - New FP-local crate::device_ctx::build_encryption_context replicates tcfsd's build_encryption_context: loads the device registry + this device's age secret and attaches them via .with_device_wrapping. Gated on a per_device_wrapping config flag that defaults false, so the default config yields exactly EncryptionContext::new(mk) — byte identical for master-only manifests. - grpc_backend fetch_direct_to_file now builds the device-aware context. - direct.rs and uniffi_bridge.rs FAIL CLOSED on a per-device manifest (wrapped_file_keys present) instead of silently copying raw ciphertext; they only implement master-key unwrapping. - FP crate gains an optional tcfs-secrets dependency (grpc feature only). Dedupe follow-up: build_encryption_context now exists in tcfsd, the CLI, and here. Lifting one copy into a shared crate (e.g. tcfs-sync) was deferred because tcfs-sync does not currently depend on tcfs-secrets and adding that edge to a core crate is broader than this security fix.
…FileProvider config The FileProvider device-aware read path (PR Jesssullivan#492) reads `per_device_wrapping` and `device_registry_path` from its JSON config, but no in-repo producer emitted them. `tcfs config fileprovider` / `tcfs init --fileprovider-config-out` render the FileProvider bootstrap JSON from the active config via `build_fileprovider_init_config`; this adds the two keys so the read path actually receives them. - `FileProviderInitConfig` gains `per_device_wrapping` (mirrors `crypto.per_device_wrapping`) and `device_registry_path` (resolved from `sync.device_identity`, falling back to the shared default registry path). - Default-off is byte-identical: `per_device_wrapping` is omitted from the rendered JSON via `skip_serializing_if` when false, and `device_registry_path` is only populated when wrapping is on. So the legacy master-only bootstrap output is unchanged. - The extension derives the device secret (`device-<id>.age`) itself from the registry path + device id, matching PR Jesssullivan#492's `device_ctx`. - Tests assert the keys are omitted when off (byte-identical) and present (with the resolved/default registry path) when on. Does NOT flip `crypto.per_device_wrapping` (still default false). Out-of-repo producers (lab nix, Swift HostApp keychain enrichment, App-Group sandbox secret access) are specified in the PR body, not changed here.
…-restore-guard DRAFT: TIN-1737 fail-closed guard for symlink restore ingress (agent-drafted, needs review)
…provider-device-ctx DRAFT: TIN-1417 B1 FileProvider device-aware EncryptionContext (agent-drafted, needs review)
…provider-config DRAFT: TIN-1417 B1 propagate per_device_wrapping + device_registry_path to FileProvider config (agent-drafted, needs review)
…n-20260607-darwin-bazel
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
tinyland/mainwith the currentJesssullivan/tummycryptorigin/mainline.cargo-denyignore forRUSTSEC-2026-0173(proc-macro-error2unmaintained) after checkingage 0.11.3; the transitive path remainsage -> i18n-embed-fl -> proc-macro-error2, and the advisory reports no safe upgrade.Validation
git diff --check tinyland/main..HEADorigin/mainintocodex/sync-origin-main-20260607-darwin-bazel.cargo-deny checkcargo tree -i proc-macro-error2cargo info age@0.11.3Notes