mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-25 02:26:12 +02:00
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - A qualified ACPX profile binds exact provider and runtime package versions. > - Package metadata alone does not prove which executable will run. > - A pathname, directory entry, or open inode can still change after verification. > - A verified launch therefore needs a one-use lease over entry bytes and retained directories, with ambient loader and package authority removed. > - Provider compatibility is admitted later, per provider; this primitive must fail closed when a provider needs an unqualified path or dependency. ## Linked Issues or Issue Description **Agent or provider** Internal qualified ACPX profiles. The first production consumer remains Codex-only in the follow-on dependency and adapter changes. **Why this adapter is useful** The runner needs to prove that installed package metadata and executable bytes match a reviewed profile, then prevent pathname replacement, symlink escape, host-package lookup, or loader injection from changing what executes. **How the agent is invoked** A later provider-specific adapter requests an opaque one-use command lease. On Linux, the lease starts Node with a synchronous hook, supplies the verified entry snapshot over a private pipe, and retains only the verified package directories needed by that profile. This pull request does not register an adapter, change runtime selection, or make any provider user-visible. **Compatibility boundary** The launched entry deliberately has a Linux descriptor-backed identity such as `/proc/self/fd/4/...`; preserving a mutable canonical pathname would reopen the replacement vulnerability. A provider that requires canonical `import.meta.url`, `__filename`, or `process.argv[1]` is not admitted by this primitive until its provider-specific adapter proves compatibility. Likewise, arbitrary package-manager ancestors are not retained. Hoisted dependencies must be explicitly qualified by a later provider-specific layer; otherwise startup fails closed. The initial production slice is Codex only, not Pi or Claude. ## What Changed - Verify exact server and optional runtime package versions from bounded metadata. - Require one supported relative Node executable and reject ambiguous or package-escaping paths. - Canonicalize the command directory and open final components without following symbolic links. - Bound and hash the open entry file while checking device, inode, size, and timestamps around the read. - Return an opaque installation and one-use launch lease instead of an executable pathname. - Reopen and revalidate retained directory and executable identities when acquiring a lease. - Load the admitted entry bytes and package-contained modules through retained Linux descriptors. - Resolve CommonJS and ESM bare imports only from retained roots, reject host ancestors, and make symlink escape terminal. - Open module bytes with `O_NOFOLLOW`, bound each read, recheck identity, and reject native or unsupported module formats. - Strip Node, native dynamic-loader, glibc, and OpenSSL injection variables before spawn. - Add regressions for replacement races, direct resources, sibling and ancestor imports, explicit runtime roots, host-ancestor denial, descendant and final symlinks, bounded reads, and environment injection. ## Verification - The authoritative GitHub Actions and exact-head Greptile review are the merge gates for the final head. - Prettier and `git diff --check` pass for the final two-file diff. - The diff does not change `pnpm-lock.yaml`, workflows, dependencies, public exports, server selection, or UI behavior. ## Risks The security/compatibility tradeoff is explicit: descriptor identity prevents a replaced lexical path from supplying resources, but pathname-sensitive providers require a later adapter-specific qualification. Unqualified or unsupported dependencies, native addons, non-Linux launch, and unsupported module formats fail closed. No production call path reaches this primitive in this pull request. ## Model Used OpenAI Codex with GPT-5 and repository tool use. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have described the issue and provider boundary above - [x] I have not referenced internal or instance-local Paperclip issues or links - [x] My branch name describes the change and contains no internal task identifier - [x] I have added or updated affected tests - [x] I have documented the trust, compatibility, dependency, and rollout boundaries - [ ] All applicable GitHub Actions are green on the final head - [ ] Greptile is 5/5 on the final head with every actionable comment resolved