# Description Found by the scheduled security audit while reviewing the CLI release pipeline. `ts/packages/cli/scripts/_acp-adapters.ts` downloads four `@zed-industries/codex-acp-<platform>` tarballs directly from the registry at release-build time — it has to, because `pnpm` only installs the optional dependency matching the runner's own platform while the archive must name all four. Those bytes were then extracted, `chmod 0755`'d, and packaged inside the published `composio` CLI archive **without being checked against any known hash**. That made the codex-acp binaries the one dependency in the release that received none of the integrity verification `pnpm install --frozen-lockfile` applies to everything else, despite `pnpm-lock.yaml` already pinning a `sha512` for each of them: ``` '@zed-industries/codex-acp-linux-x64@0.16.0': resolution: {integrity: sha512-xs5zZBLpJuciEbZNx6ZSNL0qCa9h3i/zWpj40sp6QtF+L4Ow/7qzHdBzboGhHdcz1jrLedfZeRFDA2Elj8TLMA==} ``` Distribution is `curl -fsSL https://composio.dev/install | sh`, so anything that reached those bytes between the registry and the archive would ship executable to every CLI user inside an otherwise-signed release. ## What changed New `scripts/_tarball-integrity.ts`: - `parseLockfileIntegrity` — maps every `name@version` in `pnpm-lock.yaml` to its recorded hash. Line-based rather than a YAML parse: the shape being read is two adjacent lines, and this keeps a build script free of a YAML dependency. - `expectedIntegrityFor` — returns the pinned hash, and **throws** for a package the lockfile never resolved. - `assertBytesMatchIntegrity` — Web Crypto digest compared against the pinned value; the algorithm is read from the expected string so a lockfile that moves off sha512 keeps working. - `findLockfilePath` / `loadLockfileIntegrity` — walk up from the script rather than resolving a fixed `../../..`. `_acp-adapters.ts` now looks the hash up *before* the download and verifies the archive bytes before extracting them. Two deliberate choices: - **Checked against the lockfile, not the registry's `dist.integrity`** — whoever can serve the tarball can serve the metadata that vouches for it. - **Fail-closed throughout** — a missing lockfile, or a package absent from it, aborts the build rather than packaging unverified bytes. Scope is limited to the integrity gap. The `@zed-industries/codex-acp*` family is deprecated upstream ("replaced by `@agentclientprotocol/codex-acp`") and pinned at `0.16.0`; migrating it is a separate call for whoever owns the ACP integration. # How did I test this PR **Unit — 16 new tests, all passing:** ``` $ vitest run test/src/scripts/tarball-integrity.test.ts Test Files 1 passed (1) Tests 16 passed (16) ``` Covers quoted scoped keys and unquoted bare keys, resolutions carrying no integrity, `snapshots:` repeats, a package absent from the lockfile, a version pinned under a different number, tampered bytes, a single flipped byte, an uncomputable algorithm, sha256 as well as sha512, and the upward lockfile walk (including the no-lockfile-anywhere case). One test asserts this repository actually pins a hash for all four packaged codex-acp binaries. **Live end-to-end against the real registry** — the real 74,914,872-byte `@zed-industries/codex-acp-linux-x64@0.16.0` tarball, downloaded and run through the same code path the build uses: ``` downloaded 74914872 bytes from https://registry.npmjs.org/@zed-industries/codex-acp-linux-x64/-/codex-acp-linux-x64-0.16.0.tgz PASS: real tarball matches the lockfile pin PASS: tampered rejected — TarballIntegrityError: Integrity mismatch for @zed-industries/codex-acp-linux-x64@0.16.0. PASS: unpinned version refused — TarballIntegrityError: @zed-industries/codex-acp-linux-x64@0.99.0 has no integrity hash in pnpm-lock.yaml; refusing to package unverified bytes. ``` So the check passes on genuine bytes, rejects a single flipped byte in a 75 MB archive, and refuses a version the lockfile does not pin. **Lint / types / boundaries:** - `oxlint` on all three files — clean. (First draft used `node:crypto`; `no-restricted-imports` bans it, so the digest went to Web Crypto.) - `prettier --check` — clean. - `tsc --noEmit` — no errors in any of the three changed files. This sandbox has 820 pre-existing errors from an unbuilt workspace (only 7 of 70 projects installed); `test/src/scripts/release-artifacts.test.ts` fails to import here for the same reason, on `next` as well as on this branch. - `pnpm run validate:boundaries` — `lint boundaries OK: 4 registered disables across 4 files` (unchanged; `scripts/` and `test/` sit outside the `src/` Effect boundary policy). # Security - **Grype** — `grype dir:ts/packages/cli/scripts --only-fixed --fail-on medium` → `No vulnerabilities found`. - **Socket** — could not run. `doppler secrets get SOCKET_API_TOKEN --plain --project hermes --config dev_zen` returns empty in this cron sandbox, so `socket ci` exits with `Auth Error`. Reporting rather than skipping silently; this change adds no dependency, so the dependency-alert surface is unchanged. - No new dependencies, no new network destinations. The one behavioural change is a fail-closed integrity check on bytes that were previously trusted unverified. Origin: cron-48e51eab745f / [zen-cron-44e260352d1a](https://zen.corp.composio.io/dashboard/#/chat/zen-cron-44e260352d1a) Triggered by: saransh@composio.dev | Source: unknown Session: https://zen.corp.composio.io/dashboard/#/chat/zen-cron-44e260352d1a
Composio TypeScript workspace
This directory contains the TypeScript half of the Composio SDK monorepo: the core SDK, provider adapters, the CLI, examples, and end-to-end tests. For an overview of Composio itself, start at the root README and docs.composio.dev.
If you just want to use the SDK:
npm install @composio/core
import { Composio } from '@composio/core';
const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY });
const session = await composio.create('user_123');
const tools = await session.tools();
See the @composio/core README and the quickstart for the full flow, including provider setup for your agent framework.
Packages
Published packages:
| Package | Description |
|---|---|
@composio/core |
The Composio SDK. Ships its TypeScript source and SDK docs so installed copies are inspectable by coding agents. |
@composio/slim |
Same API as @composio/core without the packaged source and docs; smaller install. |
composio CLI |
Standalone CLI binary: search, execute, and script tools from your shell. |
@composio/* providers |
Adapters that format Composio tools for agent frameworks (OpenAI, Anthropic, Vercel AI SDK, LangChain, and more). See the provider table. |
@composio/experimental |
Experimental integrations, currently the Pi provider. |
@composio/json-schema-to-zod |
JSON Schema to Zod conversion. |
Internal (unpublished) packages: cli-keyring and cli-local-tools support the CLI; ts-builders generates TypeScript source.
Layout
ts/
packages/ Published and internal packages (see above)
examples/ Runnable examples per feature and framework
e2e-tests/ Runtime E2E tests (Node, Deno, Cloudflare Workers, CLI)
docs/ Workspace SDK docs: API notes and internal guides
scripts/ Build, validation, and scaffolding scripts
vendor/ Read-only reference submodules; do not edit
Development
Commands run from the repository root. Install the pinned toolchain first:
mise install
pnpm install
Build and verify:
pnpm build:packages # build all TS packages
pnpm typecheck # typecheck all TS packages
pnpm lint:packages # oxlint over ts/packages
pnpm test # package unit tests plus example validation
Runtime E2E suites (require credentials):
pnpm test:e2e:node
pnpm test:e2e:deno
pnpm test:e2e:cloudflare
pnpm test:e2e:cli
Scaffolding:
pnpm create:provider <name> [--agentic] # new provider package
pnpm create:example <name> # new example under ts/examples
Changesets are required for changes to published packages; see the contribution guidelines.