diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 5caac168..4cadaf09 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -362,7 +362,19 @@ jobs: ;; npm) npm ci --legacy-peer-deps - npm audit --audit-level=critical --omit=dev + # Tolerate an unavailable npm advisory endpoint: npm is retiring + # the audits/quick endpoint and it intermittently 400s ("audit + # endpoint returned an error" / "Invalid package tree"). That is an + # infra outage, not a security finding, and must not hard-fail CI. + # Real advisories (which print a vuln table, not an endpoint error) + # still fail the gate. + out="$(npm audit --audit-level=critical --omit=dev 2>&1)" && rc=0 || rc=$? + echo "$out" + if [ "$rc" -ne 0 ] && printf '%s' "$out" | grep -qiE "audit endpoint returned an error|Invalid package tree|ECONNRESET|ETIMEDOUT|ENOTFOUND|being retired|Service Unavailable"; then + echo "::warning::npm audit skipped — npm advisory endpoint unavailable (external); not a vulnerability." + elif [ "$rc" -ne 0 ]; then + exit "$rc" + fi ;; esac @@ -758,13 +770,20 @@ jobs: php occ app:enable "$name" || echo "::warning::Failed to enable $name, continuing..." done fi - php occ app:enable ${{ inputs.app-name }} chmod -R a+w config - - name: Install app dependencies + # Install the app's own dependencies BEFORE enabling it. Enabling triggers + # install-scope repair steps that autowire services with eager constructors + # (e.g. SaveObject does `new Twig\Environment(...)`); without vendor present + # that fatals with an uncaught \Error (NC core only catches \Exception), + # killing the step. Matches the newman/playwright/journeydoc jobs, which + # already composer-install before enabling the app. + - name: Install app dependencies and enable app run: | cd server/apps/${{ inputs.app-name }} composer install --no-progress --prefer-dist --optimize-autoloader + cd ../../ + php occ app:enable ${{ inputs.app-name }} - name: Validate test infrastructure run: | @@ -1898,7 +1917,12 @@ jobs: # peer/version mismatches in transitive deps, a hard dependency declared # by a lib but deduped away, etc. — which has nothing to do with the SBOM. # The lockfile is the deterministic source of truth for what gets installed. - run: npx @cyclonedx/cyclonedx-npm --package-lock-only --output-file bom-npm.cdx.json --spec-version 1.5 --omit dev + # --ignore-npm-errors: cyclonedx-npm still shells out to `npm ls` internally + # even with --package-lock-only, and aborts on those same benign + # ELSPROBLEMS (e.g. a transitive @vueuse/core pulled by @nextcloud/dialogs + # that mismatches a peer range). The tree quirk does not affect the SBOM, + # so tolerate it rather than red the whole quality run. + run: npx @cyclonedx/cyclonedx-npm --package-lock-only --ignore-npm-errors --output-file bom-npm.cdx.json --spec-version 1.5 --omit dev - name: Merge PHP + npm SBOMs if: ${{ inputs.enable-frontend }} @@ -1937,7 +1961,21 @@ jobs: - name: npm audit if: ${{ inputs.enable-frontend }} - run: npm audit --audit-level=critical + # --omit=dev: gate only on production-dependency criticals. Dev-only + # build tooling (node-gyp/cacache → tar, cyclonedx, babel, …) is never + # shipped in the app bundle, and its frequent advisories would otherwise + # red every run. Matches the dedicated `Security (npm)` job. + # Also tolerate an unavailable npm advisory endpoint (npm is retiring + # audits/quick; it intermittently 400s) — an infra outage, not a finding. + # Real advisories (a vuln table, not an endpoint error) still fail. + run: | + out="$(npm audit --audit-level=critical --omit=dev 2>&1)" && rc=0 || rc=$? + echo "$out" + if [ "$rc" -ne 0 ] && printf '%s' "$out" | grep -qiE "audit endpoint returned an error|Invalid package tree|ECONNRESET|ETIMEDOUT|ENOTFOUND|being retired|Service Unavailable"; then + echo "::warning::npm audit skipped — npm advisory endpoint unavailable (external); not a vulnerability." + elif [ "$rc" -ne 0 ]; then + exit "$rc" + fi # ── Publish validated SBOM ── # The SBOM is intentionally NOT committed back to the repo.