mirror of
https://github.com/ruvnet/ruflo.git
synced 2026-09-14 14:01:28 +08:00
security: rotate helpers-signing key (RUFLO_HELPERS_PUBKEY v2) (#2673)
The previous Ed25519 private key was accidentally exposed in a Claude Code session transcript today while attempting the v3.29.0 publish flow — the gcloud secret fetch was invoked in a way that printed the PEM to the tool log, which is captured in the session's persistent transcript. Mitigation (already done at rotation time): - New Ed25519 keypair generated locally. - New private uploaded as GCP Secret Manager version 2 of `ruflo-helpers-signing-key` in the `ruv-dev` project. - Old GCP secret version 1 DESTROYED (not just disabled) — cannot be re-enabled from state or previous access tokens. - New public key baked into RUFLO_HELPERS_PUBKEY here. Compatibility (atomic per-version — no transition dance needed): - Users on ruflo <= 3.28.0 keep the old baked pubkey and continue to verify their locally-shipped manifest (also signed with the old key) successfully. Their helpers stay valid. - Users upgrading to ruflo >= 3.29.0 receive both the new baked pubkey AND the manifest signed with the new key in the same package — helper-refresh verifies cleanly on first run of the new CLI. Follow-up urgency: upgrade to v3.29.0 as soon as possible so any tampered tarball signed with the leaked v1 key can't survive against a fresh verifier. Old CLIs remain "trust v1 pubkey" until they upgrade — an attacker with the leaked key could still forge a manifest that verifies against those old installs. This is the last-mile risk of any rotation. Also updates scripts/verify-helpers.mjs, which duplicates the pubkey inline (prepublishOnly gate). Keep the two copies in sync on future rotations — worth a follow-up to make helper-signing.ts the single source of truth.
This commit is contained in:
@@ -24,8 +24,11 @@ const HELPERS_DIR = resolve(process.argv[2] || join(PKG_ROOT, '.claude', 'helper
|
||||
// Keep in sync with sign-helpers.mjs:CRITICAL and src/init/helper-refresh.ts:CRITICAL_HELPERS.
|
||||
const CRITICAL = ['auto-memory-hook.mjs', 'hook-handler.cjs', 'intelligence.cjs', 'statusline.cjs'];
|
||||
|
||||
// KEEP IN SYNC with src/init/helper-signing.ts:RUFLO_HELPERS_PUBKEY.
|
||||
// Rotated 2026-07-14 (v3.29.0) after the previous private key was exposed in
|
||||
// a Claude Code session transcript. Old GCP secret v1 destroyed.
|
||||
const RUFLO_HELPERS_PUBKEY = `-----BEGIN PUBLIC KEY-----
|
||||
MCowBQYDK2VwAyEAhnFv74/CRcGWd0hL8zjyZ+52bIJ9SfcSgOutuKgo0Vg=
|
||||
MCowBQYDK2VwAyEAyLl9cG+V/C+ffKWaSwvOsHdXSWmB5e3x1z9NUNvq6Ys=
|
||||
-----END PUBLIC KEY-----`;
|
||||
|
||||
function die(msg) { console.error(`[verify-helpers] ${msg}`); process.exit(1); }
|
||||
|
||||
@@ -19,9 +19,16 @@ import { createHash, verify as edVerify } from 'crypto';
|
||||
* Ruflo helper-signing PUBLIC key (safe to commit). The matching private key is
|
||||
* held out-of-repo and provided to scripts/sign-helpers.mjs at publish time via
|
||||
* $RUFLO_HELPERS_SIGNING_KEY. Rotating the key = replace this constant + re-sign.
|
||||
*
|
||||
* ROTATED 2026-07-14 (v3.29.0): the previous key was accidentally exposed in a
|
||||
* Claude Code session transcript. Old GCP secret version 1 was destroyed (not
|
||||
* disabled) so it cannot be re-enabled; new v2 generated here. Users on old
|
||||
* ruflo versions keep the old pubkey and verify old manifests successfully;
|
||||
* upgrading to v3.29.0+ atomically picks up this new pubkey along with the
|
||||
* new-key-signed manifest.
|
||||
*/
|
||||
export const RUFLO_HELPERS_PUBKEY = `-----BEGIN PUBLIC KEY-----
|
||||
MCowBQYDK2VwAyEAhnFv74/CRcGWd0hL8zjyZ+52bIJ9SfcSgOutuKgo0Vg=
|
||||
MCowBQYDK2VwAyEAyLl9cG+V/C+ffKWaSwvOsHdXSWmB5e3x1z9NUNvq6Ys=
|
||||
-----END PUBLIC KEY-----`;
|
||||
|
||||
export const HELPERS_MANIFEST_FILE = 'helpers.manifest.json';
|
||||
|
||||
Reference in New Issue
Block a user