Skip to content

fix(deps): replace node-rsa to node crypto#1380

Merged
nameless-mc merged 2 commits intomainfrom
fix/replace-node-rsa
Apr 20, 2026
Merged

fix(deps): replace node-rsa to node crypto#1380
nameless-mc merged 2 commits intomainfrom
fix/replace-node-rsa

Conversation

@nameless-mc
Copy link
Copy Markdown
Contributor

Why

node-rsa is no longer maintained and could be a blocker for ESM migration, so replace it with the Node.js built-in crypto module.

What

Replace the node-rsa package with Node.js built-in crypto module.

Removed dependencies

  • node-rsa (dependencies)
  • @types/node-rsa (devDependencies)
  • asn1 (transitive dependency of node-rsa)

Changed files

File Description
src/plugin/core/crypto/private-key.ts Use crypto.KeyObject instead of RSA class. Key generation via crypto.generateKeyPairSync(), import via crypto.createPrivateKey(), export via KeyObject.export()
src/plugin/core/crypto/public-key.ts Public key import via crypto.createPublicKey(). Signature verification via crypto.verify()
src/plugin/core/crypto/sign.ts Signing via crypto.sign()
package.json Remove node-rsa and @types/node-rsa
pnpm-lock.yaml Lockfile update

API mapping

Operation Before (node-rsa) After (crypto)
Key generation new RSA({ b: 1024 }) crypto.generateKeyPairSync("rsa", { modulusLength: 1024 })
Private key import new RSA(key) crypto.createPrivateKey({ key, format: "pem" })
Public key import new RSA(key, "pkcs8-public-der") crypto.createPublicKey({ key, format: "der", type: "spki" })
Private key export key.exportKey("pkcs1-private") key.export({ type: "pkcs1", format: "pem" })
Public key export key.exportKey("pkcs8-public-der") publicKey.export({ type: "spki", format: "der" })
Sign key.sign(contents) crypto.sign("sha1", contents, { key, padding: RSA_PKCS1_PADDING })
Verify crypto.createVerify("RSA-SHA1") crypto.verify("sha1", data, { key, padding: RSA_PKCS1_PADDING }, signature)

How to test

Affected commands

  • plugin keygen — Private key generation
  • plugin pack — Plugin packaging
  • plugin info — Plugin info display
  • plugin upload — Plugin upload (Plugin ID verification)

Checklist

  • Read CONTRIBUTING.md
  • Updated documentation if it is required.
  • Added/updated tests if it is required. (or tested manually)
  • Passed pnpm lint and pnpm test on the root directory.

@nameless-mc nameless-mc self-assigned this Mar 4, 2026
@nameless-mc nameless-mc requested a review from a team as a code owner March 4, 2026 03:14
@nameless-mc nameless-mc requested review from chihiro-adachi, Copilot and shabaraba and removed request for a team March 4, 2026 03:14
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR replaces the unmaintained node-rsa package with Node.js's built-in crypto module for all RSA key operations (generation, import/export, signing, and verification), reducing external dependencies and unblocking a future ESM migration.

Changes:

  • Crypto operations in private-key.ts, public-key.ts, and sign.ts migrated from node-rsa RSA class to crypto.KeyObject / crypto.sign / crypto.verify
  • node-rsa and @types/node-rsa removed from package.json
  • pnpm-lock.yaml updated to remove node-rsa, @types/node-rsa, and the transitive asn1 dependency

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/plugin/core/crypto/sign.ts Replaced node-rsa signing with crypto.sign() using sha1 and RSA_PKCS1_PADDING
src/plugin/core/crypto/public-key.ts Replaced node-rsa RSA class with crypto.KeyObject; import via createPublicKey, verify via crypto.verify()
src/plugin/core/crypto/private-key.ts Replaced node-rsa RSA class with crypto.KeyObject; key gen via generateKeyPairSync, import via createPrivateKey, export via KeyObject.export()
package.json Removed node-rsa and @types/node-rsa dependencies
pnpm-lock.yaml Lockfile updated to reflect removed packages including transitive asn1

Two issues were found:

1. Dead guard in PublicKey.importKey() (moderate/bug)

crypto.createPublicKey() always returns a KeyObject with type === "public" — it never returns a key with any other type when given a valid DER/SPKI input (it throws on malformed or private-only input instead). The check if (publicKey.type !== "public") on line 44 of public-key.ts is dead code that can never be true, making it a misleading no-op rather than a real guard.

2. Unnecessary key serialization round-trip in PrivateKey.sign() (moderate/performance)

PrivateKey.sign() calls this.exportPrivateKey(), which serializes the internally stored crypto.KeyObject to a PEM string, and then passes that string to sign(), which passes it back to crypto.sign() where it is reparsed. Since crypto.sign() accepts a crypto.KeyObject directly, the sign() helper function's second parameter should be updated to accept crypto.KeyObject (or the broader crypto.KeyLike) and PrivateKey.sign() should pass this.key directly to avoid the unnecessary serialization overhead.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/plugin/core/crypto/sign.ts Outdated
import RSA from "node-rsa";
import crypto from "crypto";

export const sign = (contents: Buffer, privateKey: string): Buffer => {
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sign function in sign.ts accepts a string (PEM) for the private key, and PrivateKey.sign() calls this.exportPrivateKey() to convert the stored KeyObject to a PEM string before passing it to sign(). This introduces an unnecessary round-trip: the KeyObject is serialized to PEM only to be re-parsed internally by crypto.sign().

Since both PrivateKey and sign.ts now use the crypto module, the sign function's second parameter should be updated to accept crypto.KeyObject (or the broader crypto.KeyLike) directly, and PrivateKey.sign() should pass this.key instead of this.exportPrivateKey().

Suggested change
export const sign = (contents: Buffer, privateKey: string): Buffer => {
export const sign = (contents: Buffer, privateKey: crypto.KeyLike): Buffer => {

Copilot uses AI. Check for mistakes.
Comment thread src/plugin/core/crypto/public-key.ts Outdated
Comment on lines 44 to 46
if (publicKey.type !== "public") {
throw new Error("key must contain public key");
}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

crypto.createPublicKey() always returns a KeyObject with type === "public" — it will never return a key of any other type (it throws if the input is a private key instead of being silently allowed). Therefore, the check if (publicKey.type !== "public") on line 44 is dead code that can never be true, making it misleading rather than protective.

The old code used rsa.isPrivate() to detect if a private key was accidentally passed. With the crypto module, crypto.createPublicKey() will throw a TypeError if a private-key-only PEM is passed where a public key is expected, so no explicit check is needed. The dead check should be removed to avoid confusion.

Suggested change
if (publicKey.type !== "public") {
throw new Error("key must contain public key");
}

Copilot uses AI. Check for mistakes.
@nameless-mc nameless-mc force-pushed the fix/replace-node-rsa branch from de4f35d to 22b324f Compare April 9, 2026 09:07
@nameless-mc nameless-mc force-pushed the fix/replace-node-rsa branch from 22b324f to ddd6874 Compare April 14, 2026 05:22
@nameless-mc nameless-mc merged commit 1952af7 into main Apr 20, 2026
42 checks passed
@nameless-mc nameless-mc deleted the fix/replace-node-rsa branch April 20, 2026 04:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants