From 84983931c9f26e75d9312ba8aab1443bb638111d Mon Sep 17 00:00:00 2001 From: robgruen Date: Wed, 21 Oct 2026 14:51:12 -0700 Subject: [PATCH 1/2] PHP vulnterability mitigation, added acr buildkit tasks --- pipelines/azure-build-docker-container.yml | 21 +++--- ts/.dockerignore | 13 +++- ts/Dockerfile | 74 ++++++++++++++-------- ts/README.md | 41 ++++++++++++ ts/acr-build.yaml | 14 ++++ 5 files changed, 125 insertions(+), 38 deletions(-) create mode 100644 ts/acr-build.yaml diff --git a/pipelines/azure-build-docker-container.yml b/pipelines/azure-build-docker-container.yml index f9c0c62dcb..11d6bb905d 100644 --- a/pipelines/azure-build-docker-container.yml +++ b/pipelines/azure-build-docker-container.yml @@ -7,9 +7,9 @@ # is the "Build and deploy Node.js app to Azure Web App" step. # # Azure DevOps port of .github/workflows/build-docker-container.yml. The image -# build runs entirely inside ACR via `az acr build`: the agent only uploads the -# ts/ build context (Dockerfile + sources) and ACR's build agents run the -# multi-stage build (pnpm install + build + pnpm deploy) — see ts/Dockerfile. +# build runs entirely inside ACR via `az acr run`: the agent only uploads the +# ts/ build context and ACR's build agents run the BuildKit-enabled task in +# ts/acr-build.yaml (pnpm install + build + pnpm deploy, then push). # # Differences from the GitHub Actions version, and why: # * Credentials moved out of GitHub. The GitHub job authenticated with @@ -19,7 +19,7 @@ # Identity Federation) referenced by $(azureSubscription). The AzureCLI@2 # task signs in through it with no stored secret to rotate. # * Node / pnpm setup and docker/setup-buildx-action are dropped. `az acr -# build` performs the whole image build inside ACR from ts/Dockerfile, so +# run` performs the whole image build inside ACR from ts/Dockerfile, so # the agent never runs pnpm, Node, or a local Docker build — those GitHub # steps contributed nothing to the actual build. # * dorny/paths-filter is replaced by the trigger `paths` filter below (build @@ -77,11 +77,10 @@ jobs: displayName: Checkout TypeAgent repository # Federated (secret-free) login via the ARM service connection, then hand - # the build off to ACR. `az acr build` uploads $(buildDirectory) as the - # build context, runs ts/Dockerfile on ACR's build agents, and pushes the - # result as $(containerRegistry).azurecr.io/$(imageName):$(imageTag). + # the build off to ACR. The task enables BuildKit for secret mounts and + # pushes the result as $(containerRegistry).azurecr.io/$(imageName):$(imageTag). - task: AzureCLI@2 - displayName: Build image in ACR (az acr build) + displayName: Build and push image in ACR (BuildKit) inputs: azureSubscription: $(azureSubscription) scriptType: bash @@ -89,8 +88,8 @@ jobs: inlineScript: | set -euo pipefail az account show --query "{subscription:name, id:id}" -o table - az acr build \ + az acr run \ --registry "$(containerRegistry)" \ - --image "$(imageName):$(imageTag)" \ - --file "$(buildDirectory)/Dockerfile" \ + --file "$(buildDirectory)/acr-build.yaml" \ + --set "imageName=$(imageName)" "imageTag=$(imageTag)" \ "$(buildDirectory)" diff --git a/ts/.dockerignore b/ts/.dockerignore index 958b26c9d6..4ba32a18d2 100644 --- a/ts/.dockerignore +++ b/ts/.dockerignore @@ -1,5 +1,14 @@ -node_modules +**/node_modules .git .gitignore *.md -dist \ No newline at end of file +**/dist +**/out +**/*.tsbuildinfo +**/*.done.build.log +**/.npmrc +**/.npmrc.* +**/.env +**/.env.* +**/config.local.yaml +**/config.local.yml \ No newline at end of file diff --git a/ts/Dockerfile b/ts/Dockerfile index c6f8dd1079..d629db51fe 100644 --- a/ts/Dockerfile +++ b/ts/Dockerfile @@ -1,26 +1,31 @@ -FROM node:22 AS base +# syntax=docker/dockerfile:1 +FROM node:24.22.1-bookworm AS base ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" -RUN npm install -g corepack@latest -RUN corepack enable pnpm -RUN corepack install -g pnpm@latest -# dependencies -RUN apt-get update -RUN apt-get install -y libsecret-1-0 -RUN apt-get install -y libsecret-1-dev -RUN apt install -y gnome-keyring -RUN curl -sL https://aka.ms/InstallAzureCLIDeb | bash +WORKDIR /usr/src/app +COPY package.json ./package.json +# npm honors private-registry credentials; Corepack does not read .npmrc. +RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \ + npm install --global corepack@0.36.0 \ + && corepack disable pnpm \ + && npm install --global "$(node -p "require('./package.json').packageManager.split('+')[0]")" \ + && npm cache clean --force -# Install dependencies required for Chrome/Puppeteer -RUN apt-get update && apt-get install -y \ +# Refresh security updates as well as installing the app's system dependencies. +RUN apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get upgrade -y \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + libsecret-1-0 \ + libsecret-1-dev \ + gnome-keyring \ + curl \ wget \ gnupg \ ca-certificates \ procps \ libxss1 \ - libgconf-2-4 \ libatk1.0-0 \ libatk-bridge2.0-0 \ libgdk-pixbuf2.0-0 \ @@ -29,19 +34,34 @@ RUN apt-get update && apt-get install -y \ libnss3-dev \ libxss-dev \ fonts-liberation \ - libappindicator3-1 \ libasound2 \ libdbus-1-3 \ libnspr4 \ libxcomposite1 \ libxdamage1 \ libxrandr2 \ - xdg-utils + xdg-utils \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists + +RUN mkdir -p /etc/apt/keyrings \ + && curl -fsSL https://packages.microsoft.com/keys/microsoft.asc -o /tmp/microsoft.asc \ + && gpg --dearmor -o /etc/apt/keyrings/microsoft.gpg /tmp/microsoft.asc \ + && chmod 644 /etc/apt/keyrings/microsoft.gpg \ + && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/microsoft.gpg] https://packages.microsoft.com/repos/azure-cli/ bookworm main" > /etc/apt/sources.list.d/azure-cli.list \ + && apt-get update \ + && apt-get install -y --no-install-recommends azure-cli=2.91.0-1~bookworm \ + && rm /tmp/microsoft.asc \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists # Install Chrome for Puppeteer -RUN wget -q https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb \ - && apt-get install -y ./google-chrome-stable_current_amd64.deb \ - && rm google-chrome-stable_current_amd64.deb +RUN curl -fsSL https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb -o /tmp/google-chrome.deb \ + && apt-get update \ + && apt-get install -y --no-install-recommends /tmp/google-chrome.deb \ + && rm /tmp/google-chrome.deb \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists # base only image FROM base AS build @@ -52,10 +72,14 @@ WORKDIR /usr/src/app # runtime) and that download is flaky in CI (fails with ECONNRESET), so skip # it — the bundled CPU execution provider is all this container uses. ENV ONNXRUNTIME_NODE_INSTALL_CUDA=skip -RUN pnpm install --frozen-lockfile -RUN pnpm run -r build -RUN pnpm deploy --legacy --filter=agent-api --prod /prod/api -RUN pnpm deploy --legacy --filter=tools-scripts --prod /prod/tools +RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \ + pnpm install --frozen-lockfile +RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \ + pnpm run -r build +RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \ + pnpm deploy --legacy --filter=agent-api --prod /prod/api +RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \ + pnpm deploy --legacy --filter=tools-scripts --prod /prod/tools # image with api and tool bits only FROM base AS api @@ -82,7 +106,7 @@ WORKDIR /prod/api #RUN mkdir /blob-cache ###################################################################################################### -EXPOSE 80:3000 +EXPOSE 3000 # The API server binds loopback by default so a local run isn't reachable from # the network. In the container the published port is the whole point and the @@ -102,8 +126,8 @@ RUN echo az login --identity >> start.sh #RUN echo blobfuse2 mount /mnt/blob --config-file=mount-blobfuse.yaml >> start.sh # run getKeys RUN echo node ../tools/scripts/getKeys.mjs >> start.sh -# start API server -RUN echo pnpm start >> start.sh +# Start Node directly so startup does not need npm registry credentials. +RUN echo node ./dist/index.js >> start.sh # run start script CMD ["bash", "start.sh"] diff --git a/ts/README.md b/ts/README.md index b6ea3806a8..25922294bf 100644 --- a/ts/README.md +++ b/ts/README.md @@ -26,6 +26,47 @@ In this directory: - Run `pnpm i` - Run `pnpm run build` +### Docker image + +The production [Dockerfile](./Dockerfile) uses Node 24 on Debian Bookworm, +Corepack 0.36.0, and Azure CLI 2.91.0. The pnpm version comes from +`packageManager` in [package.json](./package.json). Chrome is installed from +Google's stable channel. The image currently targets `linux/amd64`. +The API listens on container port 3000; publish a host port with Docker's +`--publish` option, for example `--publish 80:3000`. + +Build from this directory with Docker BuildKit: + +```console +docker build --pull --no-cache --platform linux/amd64 --tag typeagent:local . +``` + +`--pull` refreshes the base image, and `--no-cache` reruns the OS security +updates instead of reusing an older package-install layer. Updating this image +does not update the host Docker Engine; update Docker Desktop or Docker Engine +separately. + +For a private npm registry, supply a Linux-compatible npm configuration +containing the registry URL and its authentication as a BuildKit secret: + +```console +docker build --pull --no-cache --platform linux/amd64 --secret id=npmrc,src= --tag typeagent:local . +``` + +Alternatively, use `--secret id=npmrc,env=TYPEAGENT_BUILD_NPMRC` when the +configuration is held in an environment variable. Host-specific `tokenHelper` +commands must be resolved before passing the configuration to the Linux build. +The secret is mounted only for package installation and build commands; npm +credentials, local API-key configuration, and host build outputs are excluded +from the build context. pnpm is installed through npm so the same registry +authentication is used for its installation. Application startup uses Node +directly and does not need npm registry access. + +The [ACR task](./acr-build.yaml) enables BuildKit for the +[container pipeline](../pipelines/azure-build-docker-container.yml) and retains +the existing build-and-push behavior. The image's normal startup still uses +Azure managed identity. + ### Agent Specific Steps (Optional) #### VSCode Agent diff --git a/ts/acr-build.yaml b/ts/acr-build.yaml new file mode 100644 index 0000000000..a4ed581d50 --- /dev/null +++ b/ts/acr-build.yaml @@ -0,0 +1,14 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +version: v1.1.0 +stepTimeout: 3600 +steps: + - build: >- + --pull --no-cache --platform linux/amd64 + -t {{.Run.Registry}}/{{.Values.imageName}}:{{.Values.imageTag}} + -f Dockerfile . + env: + - DOCKER_BUILDKIT=1 + - push: + - "{{.Run.Registry}}/{{.Values.imageName}}:{{.Values.imageTag}}" From 689197bacb821108c55cf037623b2755a1b6169f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 21 Oct 2026 23:09:28 +0000 Subject: [PATCH 2/2] Fix ACR run task arguments Co-authored-by: robgruen <25374553+robgruen@users.noreply.github.com> --- pipelines/azure-build-docker-container.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pipelines/azure-build-docker-container.yml b/pipelines/azure-build-docker-container.yml index 11d6bb905d..cdda2b0cda 100644 --- a/pipelines/azure-build-docker-container.yml +++ b/pipelines/azure-build-docker-container.yml @@ -90,6 +90,7 @@ jobs: az account show --query "{subscription:name, id:id}" -o table az acr run \ --registry "$(containerRegistry)" \ - --file "$(buildDirectory)/acr-build.yaml" \ - --set "imageName=$(imageName)" "imageTag=$(imageTag)" \ + --file acr-build.yaml \ + --set "imageName=$(imageName)" \ + --set "imageTag=$(imageTag)" \ "$(buildDirectory)"