Files
composiohq__composio/ts
Alberto Schiabel 18944df9eb fix(cli): verify codex-acp tarballs against the lockfile before packaging them (#4217)
# 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
2026-08-25 23:57:49 +02:00
..

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.

Support