Skip to content

Commit 3aaaf31

Browse files
chore(deps): consolidate #242 #243 #244 + fix CodeQL alert + npm audit fix (#245)
Combines the three open Dependabot PRs (#242, #243, #244) into a single review surface, fixes the open CodeQL security alert, addresses two `npm audit` advisories, and introduces a workflow so future cross-ecosystem Dependabot batches can be consolidated automatically. ## Why one PR Dependabot **cannot** group dependency updates across ecosystems (npm / github-actions / docker each open their own PR). For an "all in one PR" experience this repo now uses two layers: - `.github/dependabot.yml` still groups everything **within** each ecosystem (already optimal). - New `.github/workflows/combine-prs.yml` runs monthly + on demand and combines every open `dependencies`-labeled PR into a single consolidated PR. Merging that combined PR auto-closes the source PRs. This PR is the first manual run of that consolidation, applied to the three currently-open Dependabot PRs. ## Commits | Commit | Source | Summary | | --- | --- | --- | | `fix(security): tighten cli.test.js mocks + add combine-prs workflow` | new | CodeQL alert fix + `combine-prs.yml` workflow + `dependabot.yml` pointer comment | | `chore(deps): bump node in the all-docker group` | #242 | Dockerfile node base image | | `chore(deps): bump the all-npm group with 6 updates` | #243 | express-rate-limit, tsx, ws, eslint, lint-staged, better-sqlite3 | | `chore(ci): bump the all-actions group with 2 updates` | #244 | vitest-coverage-report-action, github/codeql-action | | `fix(security): npm audit fix for qs + brace-expansion DoS advisories` | new | Bumps `qs` 6.15.0 → 6.15.2 and `brace-expansion` 5.0.5 → 5.0.6; cleans stray dependabot lock entry | ## Security fixes - **CodeQL `js/shell-command-injection-from-environment`** (`test/cli/cli.test.js:457`) — the `getDefaultShell` test mocks used to fall through to the real `child_process.execFileSync` with caller-controlled `cmd / args / opts`. CodeQL flagged this as a tainted-data → shell sink even though it's only ever reached in tests. The fix replaces the fallthrough with `throw new Error('unexpected execFileSync call in test: ...')`. The mocks already constrain the only legitimate command (`ps`), so this is strictly stricter — any future regression that calls something else now fails the test loudly instead of silently spawning a real process. - **`npm audit` (moderate × 2)** — `qs` 6.15.0 (`GHSA-q8mj-m7cp-5q26`, DoS via `qs.stringify` on null/undefined entries) and `brace-expansion` 5.0.5 (`GHSA-jxxr-4gwj-5jf2`, large-numeric-range DoS) are bumped to patched versions. `npm audit` now reports `found 0 vulnerabilities`. ## Verification - `npm run lint` → clean - `node --test --test-timeout=180000 --test-force-exit ...` → 1205 / 1205 pass (~17s) - `npm audit --audit-level=moderate` → 0 vulnerabilities After this merges, #242 / #243 / #244 will be closed (they're already represented commit-for-commit here). --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
1 parent 679f625 commit 3aaaf31

8 files changed

Lines changed: 333 additions & 168 deletions

File tree

.github/dependabot.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# Dependabot is configured to group all updates within each ecosystem
2+
# into a single PR. Note: Dependabot cannot group across ecosystems
3+
# (npm / github-actions / docker each produce their own PR). For an
4+
# all-in-one PR, see `.github/workflows/combine-prs.yml`, which runs
5+
# monthly (and on demand) and stitches every open Dependabot PR into
6+
# a single consolidated PR.
17
version: 2
28
updates:
39
- package-ecosystem: npm

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ jobs:
6262
- run: npm run test:coverage
6363
- name: Coverage Report
6464
if: github.event_name == 'pull_request'
65-
uses: davelosert/vitest-coverage-report-action@3c50566c523e04813df28de8f7c48dd97d663f1c # v2
65+
uses: davelosert/vitest-coverage-report-action@02f3c2e641286b7fa308cd3e430783103ce6103b # v2
6666
with:
6767
json-summary-path: coverage/coverage-summary.json
6868
json-final-path: coverage/coverage-final.json

.github/workflows/codeql.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ jobs:
2222
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
2323

2424
- name: Initialize CodeQL
25-
uses: github/codeql-action/init@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v3
25+
uses: github/codeql-action/init@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v3
2626
with:
2727
languages: javascript
2828

2929
- name: Perform CodeQL Analysis
30-
uses: github/codeql-action/analyze@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v3
30+
uses: github/codeql-action/analyze@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v3

.github/workflows/combine-prs.yml

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
name: Combine Dependabot PRs
2+
3+
# Combine all open Dependabot PRs into a single consolidated PR.
4+
#
5+
# Dependabot cannot group dependency updates across ecosystems
6+
# (npm + github-actions + docker each produce their own PR). This
7+
# workflow stitches every open PR labeled `dependencies` into one
8+
# branch and opens a single PR, so reviewers only have to look at
9+
# one diff and CI runs once.
10+
#
11+
# Triggers:
12+
# - pull_request_target → fires right after Dependabot opens a
13+
# `dependencies`-labeled PR. A 2-minute settle delay + concurrency
14+
# cancel ensures that when Dependabot opens 3 PRs in a wave (one
15+
# per ecosystem) only the final run actually performs the combine.
16+
# - workflow_dispatch → run on demand from the Actions tab
17+
#
18+
# Behavior:
19+
# - Picks all open PRs from `app/dependabot` carrying the
20+
# `dependencies` label whose branches still merge cleanly into main.
21+
# - Updates the stable branch `combined-deps/open` (force-with-lease)
22+
# and opens or updates a single PR against it, so repeated runs
23+
# converge on the same PR instead of creating new ones.
24+
# - Skips PRs that cause a merge conflict and reports them in the
25+
# PR body so a human can deal with them individually.
26+
# - Does NOT close the source PRs automatically — merging the
27+
# combined PR will auto-close them because git sees their
28+
# commits as already in main.
29+
#
30+
# Security note on `pull_request_target`:
31+
# The job runs in the base-repo context with write permissions, but
32+
# never checks out PR-supplied code. It only invokes `gh` + `git` on
33+
# PR refs to merge their commits into our branch. No PR-controlled
34+
# script executes here.
35+
on:
36+
pull_request_target:
37+
types: [opened, reopened, ready_for_review, labeled]
38+
workflow_dispatch:
39+
inputs:
40+
dry-run:
41+
description: 'List PRs that would be combined without pushing or opening a PR'
42+
required: false
43+
type: boolean
44+
default: false
45+
46+
permissions:
47+
contents: read
48+
49+
# A wave of Dependabot PRs triggers this workflow N times in ~1-2 minutes.
50+
# Cancel any in-flight run when a new one starts; the last run sees all PRs.
51+
concurrency:
52+
group: combine-prs
53+
cancel-in-progress: true
54+
55+
jobs:
56+
combine:
57+
name: Combine open Dependabot PRs
58+
runs-on: ubuntu-latest
59+
# Only act on Dependabot PRs (or manual dispatch). Without this guard
60+
# every PR open/label event would otherwise trigger the workflow.
61+
if: >-
62+
github.event_name == 'workflow_dispatch' ||
63+
(github.event.pull_request.user.login == 'dependabot[bot]' &&
64+
contains(github.event.pull_request.labels.*.name, 'dependencies'))
65+
permissions:
66+
contents: write
67+
pull-requests: write
68+
steps:
69+
- name: Wait for Dependabot wave to settle
70+
if: github.event_name == 'pull_request_target'
71+
run: sleep 120
72+
73+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
74+
with:
75+
fetch-depth: 0
76+
ref: main
77+
token: ${{ secrets.GITHUB_TOKEN }}
78+
79+
- name: Configure git
80+
run: |
81+
git config user.name "github-actions[bot]"
82+
git config user.email "github-actions[bot]@users.noreply.github.com"
83+
84+
- name: Combine open Dependabot PRs
85+
env:
86+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
87+
DRY_RUN: ${{ inputs.dry-run }}
88+
run: |
89+
set -euo pipefail
90+
91+
DATE=$(date +%Y-%m-%d)
92+
# Stable branch name so repeated runs (Dependabot waves, manual
93+
# dispatches, retries) converge on a single combined PR instead
94+
# of spawning a new one per date.
95+
COMBINED_BRANCH="combined-deps/open"
96+
97+
mapfile -t PRS < <(gh pr list \
98+
--state open \
99+
--author "app/dependabot" \
100+
--label dependencies \
101+
--json number,title,headRefName \
102+
--jq '.[] | "\(.number)\t\(.title)\t\(.headRefName)"')
103+
104+
if [ "${#PRS[@]}" -eq 0 ]; then
105+
echo "No open Dependabot PRs labeled 'dependencies' found. Nothing to do."
106+
exit 0
107+
fi
108+
109+
echo "Found ${#PRS[@]} candidate PR(s):"
110+
printf ' %s\n' "${PRS[@]}"
111+
112+
if [ "${DRY_RUN}" = "true" ]; then
113+
echo "Dry run requested — exiting before any push."
114+
exit 0
115+
fi
116+
117+
git fetch origin main --quiet
118+
git checkout -B "${COMBINED_BRANCH}" origin/main
119+
120+
MERGED_NUMBERS=()
121+
MERGED_LINES=()
122+
SKIPPED_LINES=()
123+
124+
for entry in "${PRS[@]}"; do
125+
IFS=$'\t' read -r number title head_ref <<<"${entry}"
126+
echo "::group::Merging #${number} (${head_ref})"
127+
git fetch origin "pull/${number}/head:pr-${number}" --quiet
128+
if git merge --no-edit --no-ff -m "Merge #${number}: ${title}" "pr-${number}"; then
129+
MERGED_NUMBERS+=("${number}")
130+
MERGED_LINES+=("- #${number} ${title}")
131+
echo "Merged #${number}"
132+
else
133+
echo "Conflict merging #${number}, aborting that merge."
134+
git merge --abort || true
135+
SKIPPED_LINES+=("- #${number} ${title} _(conflict — must be resolved manually)_")
136+
fi
137+
echo "::endgroup::"
138+
done
139+
140+
if [ "${#MERGED_NUMBERS[@]}" -eq 0 ]; then
141+
echo "No PRs merged cleanly. Nothing to push."
142+
exit 0
143+
fi
144+
145+
git push --force-with-lease origin "${COMBINED_BRANCH}"
146+
147+
BODY_FILE=$(mktemp)
148+
{
149+
echo "Combined dependency update generated by \`combine-prs.yml\` on ${DATE}."
150+
echo
151+
echo "Merging this PR will auto-close the source Dependabot PRs because"
152+
echo "their commits will be reachable from \`main\`."
153+
echo
154+
echo "### Combined PRs"
155+
printf '%s\n' "${MERGED_LINES[@]}"
156+
if [ "${#SKIPPED_LINES[@]}" -gt 0 ]; then
157+
echo
158+
echo "### Skipped (merge conflict)"
159+
printf '%s\n' "${SKIPPED_LINES[@]}"
160+
fi
161+
} > "${BODY_FILE}"
162+
163+
EXISTING=$(gh pr list \
164+
--head "${COMBINED_BRANCH}" \
165+
--state open \
166+
--json number \
167+
--jq '.[0].number // empty')
168+
169+
if [ -n "${EXISTING}" ]; then
170+
echo "Updating existing combined PR #${EXISTING}"
171+
gh pr edit "${EXISTING}" \
172+
--title "chore(deps): combined dependency updates ${DATE}" \
173+
--body-file "${BODY_FILE}"
174+
else
175+
gh pr create \
176+
--base main \
177+
--head "${COMBINED_BRANCH}" \
178+
--title "chore(deps): combined dependency updates ${DATE}" \
179+
--label dependencies \
180+
--body-file "${BODY_FILE}"
181+
fi

.github/workflows/scorecard.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,6 @@ jobs:
3232
publish_results: true
3333

3434
- name: Upload SARIF results
35-
uses: github/codeql-action/upload-sarif@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v3
35+
uses: github/codeql-action/upload-sarif@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v3
3636
with:
3737
sarif_file: results.sarif

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM node:26-slim@sha256:a12126cfec3cd85577cb573fa86a588018721098c18fe67fe1a554cc25a1a6a5
1+
FROM node:26-slim@sha256:424cafd2a035ed2b2d74acc3142b68b426fb62a47742c80a75e7117db02d6b30
22

33
RUN apt-get update \
44
&& apt-get upgrade -y \

0 commit comments

Comments
 (0)