Lock the indexer image and close the advisories in the build tooling

The indexer image installed @subql/node and the @polkadot -> @pezkuwi aliases
with an unlocked npm install, and the build cache kept the layer from the day
it was first built: the indexers ran @pezkuwi/wasm-crypto 7.5.17/7.5.18, the
build whose ed25519 and secp256k1 are stubs, plus vm2 3.9.19 (the mapping
sandbox, 54 advisories). No lockfile meant Dependabot never saw that tree.

- docker/indexer holds the image's package.json and package-lock.json:
  @subql/node 6.4.6 as today, @pezkuwi 16.5.39 / 14.0.26 / 7.5.21, overrides
  for vm2 3.12.2, uuid 11 and package-json 7 (got 11). The Dockerfile runs
  npm ci on Node 24 (Node 20 is out of support).
- The pruned-state patch stops the build when a pattern matches nothing,
  instead of reporting success with the fallback missing.
- CI builds the indexer image and runs the crypto smoke test inside it
  through the @polkadot aliases, the way subql-node loads them.
- The root yarn.lock, which the host installs as root on every deploy to run
  subql codegen/build, moves the advisory ranges forward with targeted
  resolutions and @pezkuwi/braces. Left: elliptic (low, via ethers 5 in
  @subql/cli) and sprintf-js (moderate, via jest's argparse 1); neither has
  a fixed release.
This commit is contained in:
Satoshi Qazi Muhammed committed 2026-10-12 17:14:07 +04:00
1 parent 5a92becba0
commit c4f2bd943b
8 files changed
+5557 -305

No files matched your search

+18 -7
View File
@@ -3,7 +3,10 @@
// verify with every key type. A stack that only loads is not enough: the broken
// @pezkuwi/wasm-crypto 7.5.17 loaded fine and failed at ed25519 and ecdsa.
//
// node bot-smoke.mjs <esm|cjs>
// node bot-smoke.mjs <esm|cjs|indexer>
//
// indexer: the subql-node image, which loads @pezkuwi through @polkadot aliases
// (CommonJS); the aliases are checked against the lock too.
import fs from 'node:fs';
import { createRequire } from 'node:module';
@@ -25,17 +28,25 @@ function check (name, ok, detail = '') {
const lock = JSON.parse(fs.readFileSync(path.join(dir, 'package-lock.json'), 'utf8'));
for (const name of ['@pezkuwi/wasm-crypto', '@pezkuwi/util-crypto', '@pezkuwi/api', '@pezkuwi/keyring']) {
const scope = mode === 'indexer' ? '@polkadot' : '@pezkuwi';
const locked = ['@pezkuwi/wasm-crypto', '@pezkuwi/util-crypto', '@pezkuwi/api', '@pezkuwi/keyring'];
if (mode === 'indexer') {
locked.push('@polkadot/wasm-crypto', '@polkadot/util-crypto', '@polkadot/api', '@polkadot/keyring');
}
for (const name of locked) {
const want = lock.packages[`node_modules/${name}`]?.version;
const have = JSON.parse(fs.readFileSync(path.join(dir, 'node_modules', name, 'package.json'), 'utf8')).version;
check(`${name} is the locked version`, !!want && want === have, `lock ${want}, installed ${have}`);
}
// Load the stack the way the bot does: the noter is an ES module, the payout
// bot is CommonJS.
const { Keyring } = mode === 'cjs' ? require('@pezkuwi/api') : await import('@pezkuwi/api');
const { cryptoWaitReady, mnemonicGenerate } = mode === 'cjs' ? require('@pezkuwi/util-crypto') : await import('@pezkuwi/util-crypto');
// Load the stack the way the image does: the noter is an ES module, the payout
// bot and subql-node are CommonJS.
const load = (name) => mode === 'esm' ? import(`${scope}/${name}`) : require(`${scope}/${name}`);
const { Keyring } = await load('api');
const { cryptoWaitReady, mnemonicGenerate } = await load('util-crypto');
check('crypto initialises', await cryptoWaitReady());
@@ -61,5 +72,5 @@ for (const type of ['sr25519', 'ed25519', 'ecdsa']) {
}
}
console.log(failed ? `FAILED: ${failed}` : 'all bot smoke checks passed');
console.log(failed ? `FAILED: ${failed}` : `all ${mode} smoke checks passed`);
process.exit(failed ? 1 : 0);