fix(core): reject unsafe mount paths in RemoteFile.save() #3401
Workflow file for this run
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
| name: E2E Test Typescript SDK | |
| on: | |
| push: | |
| branches: [master, next] | |
| paths: | |
| - 'ts/**' | |
| # A catalog pin or lockfile change swaps what every ts/** package | |
| # resolves without touching one of their files -- @composio/client | |
| # 0.1.0-alpha.76 -> 2.0.0-rc.5 skipped this suite entirely that way. | |
| - 'pnpm-workspace.yaml' | |
| - 'pnpm-lock.yaml' | |
| - '.github/actions/setup-node-pnpm-bun/action.yml' | |
| - '.github/workflows/ts.test-e2e.yml' | |
| - 'mise.toml' | |
| - 'mise.lock' | |
| - 'toolchain-versions.json' | |
| pull_request: | |
| paths: | |
| - 'ts/**' | |
| # A catalog pin or lockfile change swaps what every ts/** package | |
| # resolves without touching one of their files -- @composio/client | |
| # 0.1.0-alpha.76 -> 2.0.0-rc.5 skipped this suite entirely that way. | |
| - 'pnpm-workspace.yaml' | |
| - 'pnpm-lock.yaml' | |
| - '.github/actions/setup-node-pnpm-bun/action.yml' | |
| - '.github/workflows/ts.test-e2e.yml' | |
| - 'mise.toml' | |
| - 'mise.lock' | |
| - 'toolchain-versions.json' | |
| workflow_dispatch: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| permissions: | |
| contents: read | |
| # Print Turborepo telemetry events to the logs instead of sending them. | |
| env: | |
| TURBO_TELEMETRY_DEBUG: 1 | |
| jobs: | |
| toolchain-versions: | |
| name: Read toolchain test matrix | |
| runs-on: ubuntu-24.04 | |
| permissions: read-all | |
| outputs: | |
| node: ${{ steps.versions.outputs.node }} | |
| deno: ${{ steps.versions.outputs.deno }} | |
| steps: | |
| - name: Checkout Code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Read versions | |
| id: versions | |
| run: | | |
| echo "node=$(jq -c '.node' toolchain-versions.json)" >> "$GITHUB_OUTPUT" | |
| echo "deno=$(jq -c '.deno' toolchain-versions.json)" >> "$GITHUB_OUTPUT" | |
| # Node.js E2E tests, running in Docker to isolate environment | |
| # Each test suite defines its own versions.node in e2e.test.ts | |
| # Tests that don't support the matrix version are automatically skipped | |
| test-e2e-node-docker: | |
| name: E2E Test Node.js ${{ matrix.node-version }} | |
| needs: toolchain-versions | |
| runs-on: ubuntu-24.04 | |
| permissions: read-all | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| node-version: ${{ fromJSON(needs.toolchain-versions.outputs.node) }} | |
| env: | |
| CI: 1 | |
| COMPOSIO_API_KEY: ${{ secrets.COMPOSIO_API_KEY }} | |
| COMPOSIO_BASE_URL: ${{ secrets.COMPOSIO_BASE_URL_STAGING }} | |
| COMPOSIO_USER_API_KEY: ${{ secrets.COMPOSIO_USER_API_KEY }} | |
| COMPOSIO_E2E_NODE_VERSION: ${{ matrix.node-version }} | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| steps: | |
| - name: Checkout Code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Normalize Composio API keys | |
| run: | | |
| { | |
| echo "COMPOSIO_USER_API_KEY=${COMPOSIO_USER_API_KEY}" | |
| echo "COMPOSIO_API_KEY=${COMPOSIO_API_KEY}" | |
| echo "COMPOSIO_BASE_URL=${COMPOSIO_BASE_URL}" | |
| } >> "$GITHUB_ENV" | |
| - name: Setup Node.js, pnpm, Bun | |
| id: setup-toolchain | |
| uses: ./.github/actions/setup-node-pnpm-bun | |
| with: | |
| enable-turbo-cache: 'true' | |
| - name: Install Dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build | |
| run: pnpm run build:packages | |
| - name: Check API keys | |
| id: api-keys | |
| env: | |
| IS_FORK_PR: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} | |
| run: | | |
| missing=() | |
| for name in COMPOSIO_API_KEY OPENAI_API_KEY ANTHROPIC_API_KEY; do | |
| if [ -z "${!name}" ]; then | |
| missing+=("$name") | |
| fi | |
| done | |
| if [ "${#missing[@]}" -gt 0 ]; then | |
| echo "available=false" >> "$GITHUB_OUTPUT" | |
| if [ "$GITHUB_ACTOR" = "dependabot[bot]" ] || [ "$IS_FORK_PR" = "true" ]; then | |
| echo "::notice::Skipping secret-backed E2E tests; missing ${missing[*]}." | |
| exit 0 | |
| fi | |
| printf '::error::%s is not set\n' "${missing[@]}" | |
| exit 1 | |
| fi | |
| echo "available=true" >> "$GITHUB_OUTPUT" | |
| echo "✅ API keys are configured" | |
| - name: Pre-build Docker image for e2e tests | |
| if: steps.api-keys.outputs.available == 'true' | |
| run: | | |
| docker build -f ts/e2e-tests/_utils/Dockerfile.node \ | |
| --build-arg NODE_VERSION=${{ matrix.node-version }} \ | |
| --label composio.e2e=true \ | |
| --label composio.runtime=node \ | |
| --label "composio.node_version=${{ matrix.node-version }}" \ | |
| -t "composio-e2e-node:${{ matrix.node-version }}" \ | |
| . | |
| - name: Run Node.js E2E Tests | |
| if: steps.api-keys.outputs.available == 'true' | |
| run: pnpm run test:e2e:node | |
| # Deno E2E tests, running in Docker to isolate environment | |
| # Each test suite defines its own versions.deno in e2e.test.ts | |
| # Tests that don't support the matrix version are automatically skipped | |
| test-e2e-deno-docker: | |
| name: E2E Test Deno ${{ matrix.deno-version }} | |
| needs: toolchain-versions | |
| runs-on: ubuntu-24.04 | |
| permissions: read-all | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| deno-version: ${{ fromJSON(needs.toolchain-versions.outputs.deno) }} | |
| env: | |
| CI: 1 | |
| COMPOSIO_API_KEY: ${{ secrets.COMPOSIO_API_KEY }} | |
| COMPOSIO_BASE_URL: ${{ secrets.COMPOSIO_BASE_URL_STAGING }} | |
| COMPOSIO_USER_API_KEY: ${{ secrets.COMPOSIO_USER_API_KEY }} | |
| COMPOSIO_E2E_DENO_VERSION: ${{ matrix.deno-version }} | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| steps: | |
| - name: Checkout Code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Normalize Composio API keys | |
| run: | | |
| { | |
| echo "COMPOSIO_USER_API_KEY=${COMPOSIO_USER_API_KEY}" | |
| echo "COMPOSIO_API_KEY=${COMPOSIO_API_KEY}" | |
| echo "COMPOSIO_BASE_URL=${COMPOSIO_BASE_URL}" | |
| } >> "$GITHUB_ENV" | |
| - name: Setup Deno | |
| uses: denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed # v2.0.5 | |
| with: | |
| deno-version: ${{ matrix.deno-version }} | |
| - name: Setup Node.js, pnpm, Bun | |
| id: setup-toolchain | |
| uses: ./.github/actions/setup-node-pnpm-bun | |
| with: | |
| enable-turbo-cache: 'true' | |
| - name: Install Dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build | |
| run: pnpm run build:packages | |
| - name: Check API keys | |
| id: api-keys | |
| env: | |
| IS_FORK_PR: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} | |
| run: | | |
| missing=() | |
| for name in COMPOSIO_API_KEY OPENAI_API_KEY; do | |
| if [ -z "${!name}" ]; then | |
| missing+=("$name") | |
| fi | |
| done | |
| if [ "${#missing[@]}" -gt 0 ]; then | |
| echo "available=false" >> "$GITHUB_OUTPUT" | |
| if [ "$GITHUB_ACTOR" = "dependabot[bot]" ] || [ "$IS_FORK_PR" = "true" ]; then | |
| echo "::notice::Skipping secret-backed E2E tests; missing ${missing[*]}." | |
| exit 0 | |
| fi | |
| printf '::error::%s is not set\n' "${missing[@]}" | |
| exit 1 | |
| fi | |
| echo "available=true" >> "$GITHUB_OUTPUT" | |
| echo "✅ API keys are configured" | |
| # Pre-build the Deno image like the Node and CLI jobs do. Without this, | |
| # ensureDenoImage() builds the image inside a bun:test beforeAll hook, | |
| # where the bun shell's quiet mode swallows all build output and a slow | |
| # registry or apt moment turns into an opaque hook timeout instead of a | |
| # visible, retryable step failure. | |
| - name: Pre-build Docker image for e2e tests | |
| if: steps.api-keys.outputs.available == 'true' | |
| run: | | |
| NODE_MAJOR="${{ steps.setup-toolchain.outputs.node-version }}" | |
| docker build -f ts/e2e-tests/_utils/Dockerfile.deno \ | |
| --build-arg DENO_VERSION=${{ matrix.deno-version }} \ | |
| --build-arg NODE_MAJOR=${NODE_MAJOR%%.*} \ | |
| --label composio.e2e=true \ | |
| --label composio.runtime=deno \ | |
| --label "composio.deno_version=${{ matrix.deno-version }}" \ | |
| -t "composio-e2e-deno:${{ matrix.deno-version }}" \ | |
| . | |
| - name: Run Deno E2E Tests | |
| if: steps.api-keys.outputs.available == 'true' | |
| run: pnpm run test:e2e:deno | |
| # CLI E2E tests, running in scratch Docker to isolate environment | |
| test-e2e-cli-docker: | |
| name: E2E Test CLI (scratch) | |
| runs-on: ubuntu-24.04 | |
| permissions: read-all | |
| env: | |
| CI: 1 | |
| COMPOSIO_USER_API_KEY: ${{ secrets.COMPOSIO_USER_API_KEY }} | |
| COMPOSIO_API_KEY: ${{ secrets.COMPOSIO_API_KEY }} | |
| COMPOSIO_BASE_URL: ${{ secrets.COMPOSIO_BASE_URL_STAGING }} | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| steps: | |
| - name: Checkout Code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Normalize Composio API keys | |
| run: | | |
| { | |
| echo "COMPOSIO_USER_API_KEY=${COMPOSIO_USER_API_KEY}" | |
| echo "COMPOSIO_API_KEY=${COMPOSIO_API_KEY}" | |
| echo "COMPOSIO_BASE_URL=${COMPOSIO_BASE_URL}" | |
| } >> "$GITHUB_ENV" | |
| - name: Setup Node.js, pnpm, Bun | |
| id: setup-toolchain | |
| uses: ./.github/actions/setup-node-pnpm-bun | |
| with: | |
| enable-turbo-cache: 'true' | |
| - name: Install Dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build | |
| run: pnpm run build:packages | |
| - name: Check API keys | |
| id: api-keys | |
| env: | |
| IS_FORK_PR: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} | |
| run: | | |
| missing=() | |
| for name in COMPOSIO_API_KEY OPENAI_API_KEY COMPOSIO_USER_API_KEY; do | |
| if [ -z "${!name}" ]; then | |
| missing+=("$name") | |
| fi | |
| done | |
| if [ "${#missing[@]}" -gt 0 ]; then | |
| echo "available=false" >> "$GITHUB_OUTPUT" | |
| if [ "$GITHUB_ACTOR" = "dependabot[bot]" ] || [ "$IS_FORK_PR" = "true" ]; then | |
| echo "::notice::Skipping secret-backed E2E tests; missing ${missing[*]}." | |
| exit 0 | |
| fi | |
| printf '::error::%s is not set\n' "${missing[@]}" | |
| exit 1 | |
| fi | |
| echo "available=true" >> "$GITHUB_OUTPUT" | |
| echo "✅ API keys are configured" | |
| - name: Pre-build Docker image for CLI e2e tests | |
| run: | | |
| CLI_VERSION=$(node -p "require('./ts/packages/cli/package.json').version") | |
| echo "COMPOSIO_E2E_CLI_VERSION=${CLI_VERSION}" >> "$GITHUB_ENV" | |
| docker build -f ts/e2e-tests/_utils/Dockerfile.cli \ | |
| --build-arg CLI_VERSION="${CLI_VERSION}" \ | |
| --build-arg NODE_VERSION="${{ steps.setup-toolchain.outputs.node-version }}" \ | |
| --label composio.e2e=true \ | |
| --label composio.runtime=cli \ | |
| --label "composio.cli_version=${CLI_VERSION}" \ | |
| -t "composio-e2e-cli:${CLI_VERSION}" \ | |
| . | |
| - name: Run CLI E2E Tests | |
| if: steps.api-keys.outputs.available == 'true' | |
| run: pnpm run test:e2e:cli | |
| # Cloudflare Workerd E2E tests | |
| test-e2e-cloudflare: | |
| name: E2E Test Cloudflare Workerd | |
| runs-on: ubuntu-24.04 | |
| permissions: read-all | |
| env: | |
| COMPOSIO_API_KEY: ${{ secrets.COMPOSIO_API_KEY }} | |
| COMPOSIO_BASE_URL: ${{ secrets.COMPOSIO_BASE_URL_STAGING }} | |
| COMPOSIO_USER_API_KEY: ${{ secrets.COMPOSIO_USER_API_KEY }} | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| steps: | |
| - name: Checkout Code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Normalize Composio API keys | |
| run: | | |
| { | |
| echo "COMPOSIO_USER_API_KEY=${COMPOSIO_USER_API_KEY}" | |
| echo "COMPOSIO_API_KEY=${COMPOSIO_API_KEY}" | |
| echo "COMPOSIO_BASE_URL=${COMPOSIO_BASE_URL}" | |
| } >> "$GITHUB_ENV" | |
| - name: Setup Node.js, pnpm, Bun | |
| uses: ./.github/actions/setup-node-pnpm-bun | |
| with: | |
| enable-turbo-cache: 'true' | |
| - name: Install Dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build | |
| run: pnpm run build:packages | |
| - name: Check API keys | |
| id: api-keys | |
| env: | |
| IS_FORK_PR: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} | |
| run: | | |
| missing=() | |
| for name in COMPOSIO_API_KEY OPENAI_API_KEY; do | |
| if [ -z "${!name}" ]; then | |
| missing+=("$name") | |
| fi | |
| done | |
| if [ "${#missing[@]}" -gt 0 ]; then | |
| echo "available=false" >> "$GITHUB_OUTPUT" | |
| if [ "$GITHUB_ACTOR" = "dependabot[bot]" ] || [ "$IS_FORK_PR" = "true" ]; then | |
| echo "::notice::Skipping secret-backed E2E tests; missing ${missing[*]}." | |
| exit 0 | |
| fi | |
| printf '::error::%s is not set\n' "${missing[@]}" | |
| exit 1 | |
| fi | |
| echo "available=true" >> "$GITHUB_OUTPUT" | |
| echo "✅ API keys are configured" | |
| - name: Run Cloudflare Workers E2E Tests | |
| if: steps.api-keys.outputs.available == 'true' | |
| run: pnpm run test:e2e:cloudflare | |
| env: | |
| COMPOSIO_USER_API_KEY: ${{ secrets.COMPOSIO_USER_API_KEY }} | |
| COMPOSIO_API_KEY: ${{ secrets.COMPOSIO_API_KEY }} | |
| COMPOSIO_BASE_URL: ${{ secrets.COMPOSIO_BASE_URL_STAGING }} | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} |