Repository navigation
Conversation
Add SECURITY.md with the support policy decided in openabdev#1567: only the latest minor line gets fixes, with no backports. It covers private vulnerability reporting and how upstream CVEs in the Node.js base image, agent CLIs, and Debian packages ship as new releases. Add a "Base Image Security Release" runbook to RELEASING.md with a release-note template that has separate sections for breaking-change migration and the deprecated per-agent repositories. Fix outdated references in RELEASING.md. The release build is build-operator.yml, not build.yml. Images use the unified openab:<version>-<agent> tags. Cargo.toml carries only the stable version. Manual re-runs need --ref <tag>; without it the workflow builds main HEAD and tags the result as the requested version. Refs openabdev#1567 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
- Remove the no_cache rebuild step. It re-pushed an existing version tag, which contradicts SECURITY.md. A failed verification now leads to the next beta. - The bump step says each Dockerfile declares only its own Node major ARG and that the smoke tests read the expected value from the Dockerfiles, so a bump does not edit any workflow. - Make the release-note step idempotent with a <!-- security-note --> marker, and use VERSION=0.10.0 as the example. - Reduce the reusable template to the Security section. Breaking-change migration and the per-agent repository notice belong in the specific release note. - Derive the Node agent list from Dockerfile.unified instead of keeping two hand-written lists. Refs openabdev#1567 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks for the review. 5742a5d addresses the text findings.
|
|
Note LGTM ✅ - All ten round-1 findings are resolved. Private vulnerability reporting is now enabled, the runbook no longer overwrites tags, and every release-flow statement I re-checked matches the workflows. One cross-PR dependency is noted below. It is tracked as F1 on #1570, not as a blocker here. What This PR DoesThe repo had no SECURITY.md, and RELEASING.md described a release flow that no longer exists: How It Works
Findings
Finding Details🟢 F1-F2: Round-1 resolution
🟢 F3: Claims re-verified
🟢 F4: Verify loopThe awk in Step 3.2, run with mawk 1.3.4 on the Non-blocking Notes
Baseline Check
What's Good (🟢)
5. Three Reasons We Might Not Need This PR
|
The derived loop in validate-packaged-pins already enforces that every
Dockerfile built FROM node:${NODE<n>_VERSION} carries the same pin as
Dockerfile.unified. The two literal assert_line lines added only the
version itself, which forced a workflow edit on every Node bump. That
contradicts the Dockerfile-only bump described in RELEASING.md (openabdev#1572)
and in the node-security-check issue text (openabdev#1571).
Verified with the extracted step script: the tree as committed passes,
bumping all nine NODE22 Dockerfiles to 22.23.5 passes, and bumping only
Dockerfile.pi fails.
Refs openabdev#1567
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ON (#1570) * chore(docker): pin Node.js base image via NODE22_VERSION/NODE24_VERSION Replace the floating node:22-trixie-slim and node:24-trixie-slim bases with global build ARGs that hold the full Node.js version (22.23.4 and 24.22.1, the October 2026 security releases). The pin makes the Node.js version shipped in each image visible in the repo and changes it only through a reviewed bump, as decided in #1567. The smoke tests now check that node --version in each Node-based image matches the ARG default. A new assert_line keeps the ARG value identical across every Dockerfile that declares it. Nightly keeps floating as the Node.js canary. It passes only the major version (NODE22_VERSION=22, NODE24_VERSION=24), so its base resolves to the floating tags, and it does not run the version assertion. Also fix docs/opencode.md, which still named node:22-bookworm-slim. Refs #1567 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(review): derive Node.js pin checks from Dockerfiles, record nightly Node version Address round-1 review findings on #1570: - F1: Replace the hardcoded 8-agent allowlist and kimi case mapping in both smoke-test workflows with .github/scripts/node_base_arg.sh, which resolves the NODE<n>_VERSION ARG from the (target) stage's base image, following stage inheritance. Non-Node images skip; an unpinned FROM node: fails. validate-packaged-pins now also requires every Dockerfile built FROM node:${NODE<n>_VERSION} to carry the same pin as Dockerfile.unified, so a new Node-based Dockerfile cannot fail open or ship a different version. - F2: Nightly records the Node.js version that actually shipped for each Node-based variant/arch in the job summary (warn-only; the float is intentional). - F3: Generalize the Dockerfile.opencode base-image comment instead of listing a partial set of sibling Dockerfiles. * chore(docker): drop hard-coded Node pin assertions The derived loop in validate-packaged-pins already enforces that every Dockerfile built FROM node:${NODE<n>_VERSION} carries the same pin as Dockerfile.unified. The two literal assert_line lines added only the version itself, which forced a workflow edit on every Node bump. That contradicts the Dockerfile-only bump described in RELEASING.md (#1572) and in the node-security-check issue text (#1571). Verified with the extracted step script: the tree as committed passes, bumping all nine NODE22 Dockerfiles to 22.23.5 passes, and bumping only Dockerfile.pi fails. Refs #1567 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(docker): make node_base_arg.sh the only Node FROM parser validate-packaged-pins found Node-based Dockerfiles and unpinned bases with its own ^FROM node: regex. node_base_arg.sh already skips --platform and matches FROM in any case, and the parity gate's regex did neither. An unpinned FROM --platform=... node:<tag> or from node:<tag> in Dockerfile.package, which has no PR-time smoke test, therefore passed silently. node_base_arg.sh --all now checks every FROM line in a file. It prints each NODE<n>_VERSION in use and fails on any unpinned node: base. The parity gate runs it on every tracked Dockerfile, including those in subdirectories, so the gate and the runtime check share one parser. The file list is assigned before the loop, so a git failure stops the step instead of skipping every check. Inline fixtures pin the two reported forms and a pinned --platform base as regression cases. Verified with the extracted step in ubuntu:24.04: - passes as committed and after a full NODE22 bump - fails on a partial bump, a missing Dockerfile.unified pin, an unpinned --platform or lowercase from base in Dockerfile.package, an unpinned base in openshell/Dockerfile, and outside a git repo Refs #1567 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(review): make node_base_arg.sh the single fail-closed Node base parser Address round-3 F1 on #1570: the parity gate kept its own `^FROM node:` regex, so `FROM --platform=... node:<tag>`, lowercase `from node:<tag>`, digest-only, registry-qualified, or build-arg Node bases passed silently, and the resolver itself skipped several of those forms. - node_base_arg.sh classifies every FROM in the file. Any reference to a Node.js image (node:, node@, library/node, docker.io/library/node) that is not node:${NODE<n>_VERSION}-<suffix> fails, as does FROM ${VAR} unless VAR is allowlisted (BUILDER_IMAGE). A missing tag suffix gets its own message. Stage names are matched case-insensitively, as Docker does. - New --check mode prints "<file> NODE<n>_VERSION" per pin used. validate-packaged-pins now uses it over every tracked Dockerfile (including nested ones) instead of a second grep parser, fails if no Node-based Dockerfile is found, requires a full x.y.z pin in Dockerfile.unified, and names the file and expected pin on mismatch. - Add test_node_base_arg.sh (21 fixtures plus the current repository targets) and run it in validate-packaged-pins. * fix(review): recognize any registry Node base and continued FROM lines Address round-4 findings on #1570: node_base_arg.sh --check still passed FROM index.docker.io/library/node:22, FROM mirror.gcr.io/library/node:22, and a FROM split across lines with "\" (parsed as an empty image). - Treat any image whose repository name is "node", under any registry or namespace, as a Node.js base that must be node:${NODE<n>_VERSION}-<suffix>. - Join "\" continuation lines (skipping comment lines) before parsing, as Docker does; reject a FROM with no image and the "# escape=" parser directive, which would change the continuation character. - Add 9 fixtures (30 total) covering these forms. * fix(review): reject unresolvable build args in base images; trigger on nested Dockerfiles Address round-4 findings on #1570: - node_base_arg.sh: any build arg other than ${NODE<n>_VERSION} inside a base image reference (node${TAG}-slim, ${IMG}:22, ${REG}/node:...) now fails, since it cannot be resolved statically and could hide an unpinned Node.js base. Document the RUN-heredoc limitation (false error only). - docker-smoke-test.yml: also trigger on '**/Dockerfile*', so a PR that only changes a nested Dockerfile still runs the validate-packaged-pins check that scans every tracked Dockerfile. - Add 4 fixtures (34 total). * fix(review): fail repository-target tests when node_base_arg.sh exits non-zero The repository-target checks compared command-substitution output only, so a non-Node target whose lookup crashed (empty output, non-zero exit) passed as "not Node-based". Check the exit status explicitly. * fix(review): check the declared default of allowlisted base-image build args FROM ${BUILDER_IMAGE} was trusted by name, so ARG BUILDER_IMAGE=node:22 would pass. Classify the declared ARG default as well (CI overrides are not statically known), and guard against a self-referencing default. Add fixtures for the default, parameter-expansion and bare-composite forms (39 total). --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: chaodu-agent <chaodu-agent@users.noreply.github.com>
What problem does this solve?
The repo has no SECURITY.md, so there is no stated support policy and no private reporting path. RELEASING.md has no steps for a base-image security release. It also no longer matches the release workflows. It names
build.yml, which does not exist, lists the deprecated per-agent image repositories, and shows a manual re-run command that buildsmainHEAD and tags the result as the requested version. #1567 asked for SECURITY.md and the runbook in one PR, together with the fixes for these outdated references.Part of #1567 (step 3, third PR).
Merge gates.
node-security-check.yml.Discord Discussion URL: https://discord.com/channels/1491295327620169908/1491365158868619404/1562955032012398712
Review Contract
Goal
A maintainer handling a Node.js (or other upstream) security release can follow RELEASING.md from detection to release note without guessing. A user or reporter can read SECURITY.md to learn which versions get fixes and how to report a vulnerability privately.
Non-goals
Accepted Residual Risks
gh api repos/openabdev/openab/private-vulnerability-reportingreturns{"enabled":false}, so that button does not exist until a maintainer enables it under Settings > Code security. If you prefer another channel, tell me in review and I will change the text.node-security-check.ymland theNODE22_VERSION/NODE24_VERSIONARGs from the companion PRs. Until those merge, the references point at files that do not exist yet.##### Step N — ...style.Acceptance Criteria
.github/workflows/, exceptnode-security-check.ymluntil its PR merges.SECURITY.md,docs/image-tags.md,#base-image-security-release,#release-flow-tag-driven).build-operator.yml,release-pr.yml,build-binaries.yml, orbuild-oabctl.yml. The Validation section lists the evidence for each one.Follow-ups
node-security-check.ymlrow to the workflow table after its PR merges.docs/image-tags.mdomitsdevinfrom its agent list. This PR leaves that file unchanged.At a Glance
N/A. Documentation only.
Prior Art & Industry Research
Not applicable. This is a documentation and policy change.
Proposed Solution
SECURITY.md (new). It covers four topics.
node-security-check.ymland the runbook.RELEASING.md, new "Base Image Security Release" runbook. It has five steps.
node --versionfor every Node agent. The list comes fromDockerfile.unified. If verification fails, cut the next beta. Never re-push an existing tag.stable-*.openab-<version>GitHub Release. A<!-- security-note -->marker keeps a re-run from adding the note twice, and the install instructions thatrelease.ymlappends are kept.An English release-note template follows. It holds only the Security section. A release that also carries breaking changes adds its own migration section.
RELEASING.md, outdated references.
build.ymlbecomesbuild-operator.ymleverywhere.--ref <tag>.openab:<version>-<agent>scheme, has nolatest, and linksdocs/image-tags.md.build-binaries.ymlandbuild-oabctl.yml.Cargo.tomlcarries only the stable version.Why this approach?
The runbook sits next to the release flow it extends, and its prose follows the file's existing 繁中 + English style. The release-note template is in English because GitHub release notes are. SECURITY.md states the policy as a rule with one example, not as a version table that would go stale with every release.
Alternatives Considered
docs/security-releases.md. Rejected because the steps depend on the release flow, so they belong in RELEASING.md where maintainers already look.Validation
--reffix rests on two facts.gh workflow run --helpdocuments--refas the branch or tag that contains the workflow version to run.build-operator.ymlchecks out with norefand tags${GITHUB_SHA:0:7}. So-f tag=v0.7.0-beta.1alone buildsmainHEAD and labels it0.7.0-beta.1.imagetools createcalls inbuild-operator.yml.merge-manifestscreates${VERSION}-${AGENT},beta-${AGENT}, and${SHORT_SHA}-${AGENT}.promote-stablecreates${CHART_VERSION}-${AGENT},${MAJOR_MINOR}-${AGENT}, andstable-${AGENT}.build-binaries.ymlruns onpush: tags: v*.build-oabctl.ymlruns on Chart.yaml pushes to main. Both upload toopenab-<version>(see theopenab-0.10.0-beta.5assets).release-pr.yml, which writesversion = "${STABLE}".*.ymlname exists. Everything passed exceptnode-security-check.yml, which comes from the companion PR.git diff --checkis clean.🤖 Generated with Claude Code