From 233b7b80602ea05dd9af3da0d4b57cb54ff901cf Mon Sep 17 00:00:00 2001 From: codeGlaze Date: Sun, 4 Oct 2026 21:02:17 -0400 Subject: [PATCH 1/4] ci: report the Clojure dependencies to GitHub's dependency graph Dependabot cannot read project.clj, so it watched only the workflows and the npm test tools. lein pom writes the dependencies as a Maven pom, which the submission action resolves and uploads; Dependabot alerts then cover the server's libraries. Weekly, on pushes and on pull requests. --- .github/workflows/dependency-graph.yml | 60 ++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 .github/workflows/dependency-graph.yml diff --git a/.github/workflows/dependency-graph.yml b/.github/workflows/dependency-graph.yml new file mode 100644 index 000000000..bee93aa11 --- /dev/null +++ b/.github/workflows/dependency-graph.yml @@ -0,0 +1,60 @@ +# Reports the app's Clojure and Java libraries to GitHub's dependency graph, so Dependabot alerts +# cover them. +# +# Dependabot cannot read project.clj, so without this it watches only the workflows and the npm +# test tools, never the libraries the server runs on. `lein pom` writes the same dependencies as a +# Maven pom.xml, and the submission action resolves it (transitive dependencies included) and +# uploads the result. Alerts then appear under Security > Dependabot like any other ecosystem. +# No API key: this uses GitHub's own advisory database, not NVD. +# +# On pull requests it submits the PR's graph too, which is what a dependency-review check would +# compare against. + +name: Dependency graph + +on: + push: + branches: [integration, develop, main] + pull_request: + branches: [integration, develop, main] + schedule: + # Mondays: new advisories arrive without any commit here. + - cron: '17 6 * * 1' + workflow_dispatch: + +permissions: + contents: write + +jobs: + submit: + name: Submit Clojure dependencies + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '21' + + # Pinned to the version continuous-integration.yml uses. + - name: Install Leiningen + run: | + curl -fsSL -o "$HOME/lein" \ + https://raw.githubusercontent.com/technomancy/leiningen/2.11.2/bin/lein + chmod +x "$HOME/lein" + echo "$HOME" >> "$GITHUB_PATH" + "$HOME/lein" version + + # project.clj declares the Apache snapshots repository over http and mirrors it to https; + # lein pom writes the declared http URL, which Maven 3.8+ refuses outright. + - name: Write pom.xml from project.clj + run: | + lein pom + sed -i 's#http://repository.apache.org#https://repository.apache.org#' pom.xml + + - name: Submit the dependency snapshot + uses: advanced-security/maven-dependency-submission-action@v4 + with: + directory: . From d4ce7a94abf3a3d97f3a48bad30c3076772815e2 Mon Sep 17 00:00:00 2001 From: codeGlaze Date: Sun, 4 Oct 2026 21:02:17 -0400 Subject: [PATCH 2/4] Record ci/clojure-cve-scan in CHANGELOG.md under Summer Patch --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bc404516..c7f72b966 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1183,6 +1183,12 @@ one 10px gutter, and the header's tab menus open on screen again. - **Lint is clean** — the 50 warnings and 3 infos on `integration` are fixed, not suppressed: shadowing locals renamed for what they hold, unused requires removed, nested lets merged; no behaviour change (`ad3c612b`). - **The pick walker moved into shared code** — `walk-entries`, `walk-picks`, `picks-of` and `relink-picks` now live in `orcpub.dnd.e5.picks` (as `walk`, `walk-typed`, `keys-of`, `relink`), unchanged, so the server can use them and their tests run in CI (`439593dd`). +### ci/clojure-cve-scan + +**Added** + +- **Security alerts cover the server's Clojure libraries** — a CI job reports them to GitHub's dependency graph each week and on every push and pull request, so Dependabot warns about a vulnerable one; it could not read `project.clj` before (`233b7b80`). + ## [breaking/2026-stack-modernization] ### Infrastructure From c0a5acda27ccd101925644285a8e7a4e2e8451ff Mon Sep 17 00:00:00 2001 From: codeGlaze Date: Sun, 4 Oct 2026 21:04:09 -0400 Subject: [PATCH 3/4] ci: submit the dependency graph from the main CI job instead of a second workflow The standalone workflow failed at Leiningen setup: the project's plugins and Datomic Pro need the setup the main job already does (local libs into ~/.m2, Datomic Pro installed). Submitting after its lein deps avoids duplicating forty lines. Push and manual runs only; a weekly schedule is not needed, because GitHub re-checks a submitted graph against new advisories by itself. --- .github/workflows/continuous-integration.yml | 22 +++++++ .github/workflows/dependency-graph.yml | 60 -------------------- 2 files changed, 22 insertions(+), 60 deletions(-) delete mode 100644 .github/workflows/dependency-graph.yml diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 45352ad36..cf6b555ab 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -57,6 +57,10 @@ jobs: name: Test & Lint (${{ needs.detect-stack.outputs.stack-label }}) runs-on: ubuntu-latest needs: detect-stack + # contents: write is for the dependency-graph submission below. + permissions: + contents: write + checks: write steps: - name: Checkout @@ -116,6 +120,24 @@ jobs: - name: Install dependencies run: lein deps + # Dependabot cannot read project.clj, so it never saw the server's libraries. lein pom writes + # them as a Maven pom (resolved from the ~/.m2 filled above) and the action uploads the full + # graph; GitHub then re-checks it against every new advisory without another run. Not on + # pull requests: alerts read the default branch, and a fork's PR gets a read-only token. + # The pom keeps project.clj's declared http Apache URL, which Maven 3.8+ refuses; lein + # itself uses the https mirror. + - name: Write pom.xml for the dependency graph + if: github.event_name != 'pull_request' + run: | + lein pom + sed -i 's#http://repository.apache.org#https://repository.apache.org#' pom.xml + + - name: Submit dependencies to the dependency graph + if: github.event_name != 'pull_request' + uses: advanced-security/maven-dependency-submission-action@v4 + with: + directory: . + - name: Run linter id: lint run: | diff --git a/.github/workflows/dependency-graph.yml b/.github/workflows/dependency-graph.yml deleted file mode 100644 index bee93aa11..000000000 --- a/.github/workflows/dependency-graph.yml +++ /dev/null @@ -1,60 +0,0 @@ -# Reports the app's Clojure and Java libraries to GitHub's dependency graph, so Dependabot alerts -# cover them. -# -# Dependabot cannot read project.clj, so without this it watches only the workflows and the npm -# test tools, never the libraries the server runs on. `lein pom` writes the same dependencies as a -# Maven pom.xml, and the submission action resolves it (transitive dependencies included) and -# uploads the result. Alerts then appear under Security > Dependabot like any other ecosystem. -# No API key: this uses GitHub's own advisory database, not NVD. -# -# On pull requests it submits the PR's graph too, which is what a dependency-review check would -# compare against. - -name: Dependency graph - -on: - push: - branches: [integration, develop, main] - pull_request: - branches: [integration, develop, main] - schedule: - # Mondays: new advisories arrive without any commit here. - - cron: '17 6 * * 1' - workflow_dispatch: - -permissions: - contents: write - -jobs: - submit: - name: Submit Clojure dependencies - runs-on: ubuntu-latest - timeout-minutes: 15 - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: '21' - - # Pinned to the version continuous-integration.yml uses. - - name: Install Leiningen - run: | - curl -fsSL -o "$HOME/lein" \ - https://raw.githubusercontent.com/technomancy/leiningen/2.11.2/bin/lein - chmod +x "$HOME/lein" - echo "$HOME" >> "$GITHUB_PATH" - "$HOME/lein" version - - # project.clj declares the Apache snapshots repository over http and mirrors it to https; - # lein pom writes the declared http URL, which Maven 3.8+ refuses outright. - - name: Write pom.xml from project.clj - run: | - lein pom - sed -i 's#http://repository.apache.org#https://repository.apache.org#' pom.xml - - - name: Submit the dependency snapshot - uses: advanced-security/maven-dependency-submission-action@v4 - with: - directory: . From 050e7d4f490b3851bcd1e9ca9371f75fd6289033 Mon Sep 17 00:00:00 2001 From: codeGlaze Date: Sun, 4 Oct 2026 21:09:42 -0400 Subject: [PATCH 4/4] ci: the dependency graph gets its own job; the shared setup moves to a local action Greptile on #49: the test job runs pull-request code, so it must not hold contents: write. The submission is now a separate push-only job, the only one with write access, and the setup both jobs need (JDK, Leiningen, Maven cache, local libs, Datomic Pro, lein deps) lives once in .github/actions/setup-lein. The third-party submission action is pinned to a commit. --- .github/actions/setup-lein/action.yml | 68 +++++++++++ .github/workflows/continuous-integration.yml | 114 +++++++------------ 2 files changed, 109 insertions(+), 73 deletions(-) create mode 100644 .github/actions/setup-lein/action.yml diff --git a/.github/actions/setup-lein/action.yml b/.github/actions/setup-lein/action.yml new file mode 100644 index 000000000..30f83c886 --- /dev/null +++ b/.github/actions/setup-lein/action.yml @@ -0,0 +1,68 @@ +name: Set up Leiningen and the project dependencies +description: > + JDK, Leiningen, the Maven cache, the committed local libs and Datomic Pro, then lein deps. + Shared by the test and dependency-graph jobs so their setup cannot drift. +inputs: + java-version: {required: true} + java-distribution: {required: true} + lein-version: {required: true} + needs-datomic-pro: {required: true} +runs: + using: composite + steps: + - name: Set up JDK ${{ inputs.java-version }} + uses: actions/setup-java@v4 + with: + distribution: ${{ inputs.java-distribution }} + java-version: ${{ inputs.java-version }} + + - name: Set up Leiningen + uses: DeLaGuardo/setup-clojure@12.5 + with: + lein: ${{ inputs.lein-version }} + + - name: Cache Maven dependencies + uses: actions/cache@v4 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-j${{ inputs.java-version }}-${{ hashFiles('project.clj') }} + restore-keys: | + ${{ runner.os }}-maven-j${{ inputs.java-version }}- + + - name: Load local libs (pdfbox) + shell: bash + run: | + mkdir -p ~/.m2/repository/org/ + cp -rv ./lib/org/* ~/.m2/repository/org/ + + # Datomic Pro jars are not committed to git. Download and maven-install + # them the same way .devcontainer/post-create.sh does. + - name: Install Datomic Pro + if: inputs.needs-datomic-pro == 'true' + shell: bash + run: | + DATOMIC_VERSION=1.0.7482 + TARGET_DIR="lib/com/datomic/datomic-pro/${DATOMIC_VERSION}" + ZIP_PATH="/tmp/datomic-pro-${DATOMIC_VERSION}.zip" + DOWNLOAD_URL="https://datomic-pro-downloads.s3.amazonaws.com/${DATOMIC_VERSION}/datomic-pro-${DATOMIC_VERSION}.zip" + + echo "Downloading Datomic Pro ${DATOMIC_VERSION}..." + curl --fail --location --silent --show-error -o "$ZIP_PATH" "$DOWNLOAD_URL" + + mkdir -p "${TARGET_DIR}" + unzip -q "$ZIP_PATH" -d "${TARGET_DIR}" + + # Flatten nested directory if present (zip contains datomic-pro-VERSION/ subdir) + TOP_SUBDIR=$(find "${TARGET_DIR}" -mindepth 1 -maxdepth 1 -type d -print -quit || true) + if [ -n "${TOP_SUBDIR}" ] && [ -z "$(find "${TARGET_DIR}" -maxdepth 1 -type f -print -quit)" ]; then + mv "${TOP_SUBDIR}"/* "${TARGET_DIR}/" + rmdir "${TOP_SUBDIR}" + fi + + # Populate ~/.m2 with Datomic Pro artifacts + (cd "${TARGET_DIR}" && bash bin/maven-install) + echo "Datomic Pro ${DATOMIC_VERSION} installed to local Maven repo" + + - name: Install dependencies + shell: bash + run: lein deps diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index cf6b555ab..83aa9268d 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -57,86 +57,18 @@ jobs: name: Test & Lint (${{ needs.detect-stack.outputs.stack-label }}) runs-on: ubuntu-latest needs: detect-stack - # contents: write is for the dependency-graph submission below. - permissions: - contents: write - checks: write steps: - name: Checkout uses: actions/checkout@v4 - - name: Set up JDK ${{ needs.detect-stack.outputs.java-version }} - uses: actions/setup-java@v4 + - name: Set up Leiningen and dependencies + uses: ./.github/actions/setup-lein with: - distribution: ${{ needs.detect-stack.outputs.java-distribution }} java-version: ${{ needs.detect-stack.outputs.java-version }} - - - name: Set up Leiningen - uses: DeLaGuardo/setup-clojure@12.5 - with: - lein: ${{ needs.detect-stack.outputs.lein-version }} - - - name: Cache Maven dependencies - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-j${{ needs.detect-stack.outputs.java-version }}-${{ hashFiles('project.clj') }} - restore-keys: | - ${{ runner.os }}-maven-j${{ needs.detect-stack.outputs.java-version }}- - - - name: Load local libs (pdfbox) - run: | - mkdir -p ~/.m2/repository/org/ - cp -rv ./lib/org/* ~/.m2/repository/org/ - - # Datomic Pro jars are not committed to git. Download and maven-install - # them the same way .devcontainer/post-create.sh does. - - name: Install Datomic Pro - if: needs.detect-stack.outputs.needs-datomic-pro == 'true' - run: | - DATOMIC_VERSION=1.0.7482 - TARGET_DIR="lib/com/datomic/datomic-pro/${DATOMIC_VERSION}" - ZIP_PATH="/tmp/datomic-pro-${DATOMIC_VERSION}.zip" - DOWNLOAD_URL="https://datomic-pro-downloads.s3.amazonaws.com/${DATOMIC_VERSION}/datomic-pro-${DATOMIC_VERSION}.zip" - - echo "Downloading Datomic Pro ${DATOMIC_VERSION}..." - curl --fail --location --silent --show-error -o "$ZIP_PATH" "$DOWNLOAD_URL" - - mkdir -p "${TARGET_DIR}" - unzip -q "$ZIP_PATH" -d "${TARGET_DIR}" - - # Flatten nested directory if present (zip contains datomic-pro-VERSION/ subdir) - TOP_SUBDIR=$(find "${TARGET_DIR}" -mindepth 1 -maxdepth 1 -type d -print -quit || true) - if [ -n "${TOP_SUBDIR}" ] && [ -z "$(find "${TARGET_DIR}" -maxdepth 1 -type f -print -quit)" ]; then - mv "${TOP_SUBDIR}"/* "${TARGET_DIR}/" - rmdir "${TOP_SUBDIR}" - fi - - # Populate ~/.m2 with Datomic Pro artifacts - (cd "${TARGET_DIR}" && bash bin/maven-install) - echo "Datomic Pro ${DATOMIC_VERSION} installed to local Maven repo" - - - name: Install dependencies - run: lein deps - - # Dependabot cannot read project.clj, so it never saw the server's libraries. lein pom writes - # them as a Maven pom (resolved from the ~/.m2 filled above) and the action uploads the full - # graph; GitHub then re-checks it against every new advisory without another run. Not on - # pull requests: alerts read the default branch, and a fork's PR gets a read-only token. - # The pom keeps project.clj's declared http Apache URL, which Maven 3.8+ refuses; lein - # itself uses the https mirror. - - name: Write pom.xml for the dependency graph - if: github.event_name != 'pull_request' - run: | - lein pom - sed -i 's#http://repository.apache.org#https://repository.apache.org#' pom.xml - - - name: Submit dependencies to the dependency graph - if: github.event_name != 'pull_request' - uses: advanced-security/maven-dependency-submission-action@v4 - with: - directory: . + java-distribution: ${{ needs.detect-stack.outputs.java-distribution }} + lein-version: ${{ needs.detect-stack.outputs.lein-version }} + needs-datomic-pro: ${{ needs.detect-stack.outputs.needs-datomic-pro }} - name: Run linter id: lint @@ -193,3 +125,39 @@ jobs: test-output.txt cljs-output.txt retention-days: 7 + + # Dependabot cannot read project.clj, so it never saw the server's libraries. lein pom writes them + # as a Maven pom and the action uploads the resolved graph; GitHub then re-checks it against new + # advisories by itself. Its own job, so contents: write never reaches a job that runs pull-request + # code; push and manual runs only, since alerts read the default branch. + dependency-graph: + name: Submit Clojure dependencies + runs-on: ubuntu-latest + needs: detect-stack + if: github.event_name != 'pull_request' + permissions: + contents: write + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Leiningen and dependencies + uses: ./.github/actions/setup-lein + with: + java-version: ${{ needs.detect-stack.outputs.java-version }} + java-distribution: ${{ needs.detect-stack.outputs.java-distribution }} + lein-version: ${{ needs.detect-stack.outputs.lein-version }} + needs-datomic-pro: ${{ needs.detect-stack.outputs.needs-datomic-pro }} + + # The pom keeps project.clj's declared http Apache URL, which Maven 3.8+ refuses; lein itself + # uses the https mirror. + - name: Write pom.xml + run: | + lein pom + sed -i 's#http://repository.apache.org#https://repository.apache.org#' pom.xml + + # Pinned to a commit: this third-party action holds write access. + - name: Submit the dependency graph + uses: advanced-security/maven-dependency-submission-action@fe8d4d650a4b66508612d0683ce4726d51dfe6ac # v4 + with: + directory: .