Skip to content

Conversation

@AugustinMauroy
Copy link
Member

Description

  • It's the only EOL between v20 to 22 It's mean after this pr we will hable to create @nodejs/v20-to-v22
  • the name of the codemod doesn't look good to me need to found something bettert

Related issue

close #264

@AugustinMauroy AugustinMauroy added awaiting reviewer Author has responded and needs action from the reviewer dep:v22 Migration handles deprecation introduced in node v22 labels Nov 4, 2025
@brunocroh brunocroh self-requested a review December 13, 2025 14:04
@JakobJingleheimer JakobJingleheimer added this to the 20.x → 22.x milestone Jan 25, 2026
@JakobJingleheimer JakobJingleheimer changed the title feat(crypto-createcipheriv-migration): introduce feat(DEP0106): migrate from node:crypto.createCipher & node:crypto.createDecipher Jan 25, 2026
@JakobJingleheimer JakobJingleheimer requested review from tniessen and removed request for tniessen January 25, 2026 17:41
@JakobJingleheimer
Copy link
Member

@nodejs/crypto I'm not familiar with this part of node. could you check the input and expected test fixtures that they're accurate?

@JakobJingleheimer JakobJingleheimer changed the title feat(DEP0106): migrate from node:crypto.createCipher & node:crypto.createDecipher feat(DEP0106): migrate from crypto.createCipher & crypto.createDecipher Jan 25, 2026
Copy link
Member

@JakobJingleheimer JakobJingleheimer left a comment

Choose a reason for hiding this comment

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

Looks fine to me! I'd like someone from the crypto team to take a look at the before & after test fixtures though before landing this.

Comment on lines +2 to +6
import {
getNodeImportCalls,
getNodeImportStatements,
} from '@nodejs/codemod-utils/ast-grep/import-statement';
import { getNodeRequireCalls } from '@nodejs/codemod-utils/ast-grep/require-call';
Copy link
Member

Choose a reason for hiding this comment

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

nit: there's a consolidated util for this now

Comment on lines +94 to +110
if (kind === 'cipher') {
const replacement = buildCipherReplacement({
binding,
algorithm,
password,
options: optionsText,
});
edits.push(call.replace(replacement));
} else {
const replacement = buildDecipherReplacement({
binding,
algorithm,
password,
options: optionsText,
});
edits.push(call.replace(replacement));
}
Copy link
Member

Choose a reason for hiding this comment

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

This can be DRYed up :)

Suggested change
if (kind === 'cipher') {
const replacement = buildCipherReplacement({
binding,
algorithm,
password,
options: optionsText,
});
edits.push(call.replace(replacement));
} else {
const replacement = buildDecipherReplacement({
binding,
algorithm,
password,
options: optionsText,
});
edits.push(call.replace(replacement));
}
const builder = kind === 'cipher' ? buildCipherReplacement : buildDecipherReplacement;
const replacement = builder({
binding,
algorithm,
password,
options: optionsText,
});
edits.push(call.replace(replacement));

If buildCipherReplacement & buildDecipherReplacement can be deduplicated:

Suggested change
if (kind === 'cipher') {
const replacement = buildCipherReplacement({
binding,
algorithm,
password,
options: optionsText,
});
edits.push(call.replace(replacement));
} else {
const replacement = buildDecipherReplacement({
binding,
algorithm,
password,
options: optionsText,
});
edits.push(call.replace(replacement));
}
const replacement = buildDeCipherReplacement({
binding,
algorithm,
password,
options: optionsText,
}, kind);
edits.push(call.replace(replacement));

Comment on lines +136 to +179
function buildCipherReplacement(params: {
binding: string;
algorithm: string;
password: string;
options?: string;
}): string {
const { binding, algorithm, password, options } = params;
const randomBytesCall = getMemberAccess(binding, 'randomBytes');
const scryptCall = getMemberAccess(binding, 'scryptSync');
const cipherCall = getCallableBinding(binding, 'createCipheriv');

return dedent(`
(() => {
const __dep0106Salt = ${randomBytesCall}(16);
const __dep0106Key = ${scryptCall}(${password}, __dep0106Salt, 32);
const __dep0106Iv = ${randomBytesCall}(16);
// DEP0106: Persist __dep0106Salt and __dep0106Iv with the ciphertext so it can be decrypted later.
// DEP0106: Adjust the derived key length (32 bytes) and IV length to match the chosen algorithm.
return ${cipherCall}(${algorithm}, __dep0106Key, __dep0106Iv${options ? `, ${options}` : ''});
})()
`);
}

function buildDecipherReplacement(params: {
binding: string;
algorithm: string;
password: string;
options?: string;
}): string {
const { binding, algorithm, password, options } = params;
const scryptCall = getMemberAccess(binding, 'scryptSync');
const decipherCall = getCallableBinding(binding, 'createDecipheriv');

return dedent(`
(() => {
// DEP0106: Replace the placeholders below with the salt and IV that were stored with the ciphertext.
const __dep0106Salt = /* TODO: stored salt Buffer */ Buffer.alloc(16);
const __dep0106Iv = /* TODO: stored IV Buffer */ Buffer.alloc(16);
const __dep0106Key = ${scryptCall}(${password}, __dep0106Salt, 32);
// DEP0106: Ensure __dep0106Salt and __dep0106Iv match the values used during encryption.
return ${decipherCall}(${algorithm}, __dep0106Key, __dep0106Iv${options ? `, ${options}` : ''});
})()
`);
}
Copy link
Member

Choose a reason for hiding this comment

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

These are nearly identical. I think they can be DRYed up:

Suggested change
function buildCipherReplacement(params: {
binding: string;
algorithm: string;
password: string;
options?: string;
}): string {
const { binding, algorithm, password, options } = params;
const randomBytesCall = getMemberAccess(binding, 'randomBytes');
const scryptCall = getMemberAccess(binding, 'scryptSync');
const cipherCall = getCallableBinding(binding, 'createCipheriv');
return dedent(`
(() => {
const __dep0106Salt = ${randomBytesCall}(16);
const __dep0106Key = ${scryptCall}(${password}, __dep0106Salt, 32);
const __dep0106Iv = ${randomBytesCall}(16);
// DEP0106: Persist __dep0106Salt and __dep0106Iv with the ciphertext so it can be decrypted later.
// DEP0106: Adjust the derived key length (32 bytes) and IV length to match the chosen algorithm.
return ${cipherCall}(${algorithm}, __dep0106Key, __dep0106Iv${options ? `, ${options}` : ''});
})()
`);
}
function buildDecipherReplacement(params: {
binding: string;
algorithm: string;
password: string;
options?: string;
}): string {
const { binding, algorithm, password, options } = params;
const scryptCall = getMemberAccess(binding, 'scryptSync');
const decipherCall = getCallableBinding(binding, 'createDecipheriv');
return dedent(`
(() => {
// DEP0106: Replace the placeholders below with the salt and IV that were stored with the ciphertext.
const __dep0106Salt = /* TODO: stored salt Buffer */ Buffer.alloc(16);
const __dep0106Iv = /* TODO: stored IV Buffer */ Buffer.alloc(16);
const __dep0106Key = ${scryptCall}(${password}, __dep0106Salt, 32);
// DEP0106: Ensure __dep0106Salt and __dep0106Iv match the values used during encryption.
return ${decipherCall}(${algorithm}, __dep0106Key, __dep0106Iv${options ? `, ${options}` : ''});
})()
`);
}
function buildDeCipherReplacement(
{
binding,
algorithm,
password,
options,
}: {
binding: string;
algorithm: string;
password: string;
options?: string;
},
kind: 'decipher' | 'cipher'
): string {
const scryptCall = getMemberAccess(binding, 'scryptSync');
const method = getCallableBinding(binding, kind === 'cipher' ? 'createCipheriv' : 'createDecipheriv');
return dedent(`
(() => {
const __dep0106Salt = ${randomBytesCall}(16);
const __dep0106Key = ${scryptCall}(${password}, __dep0106Salt, 32);
const __dep0106Iv = ${randomBytesCall}(16);
// DEP0106: Persist __dep0106Salt and __dep0106Iv with the ciphertext so it can be decrypted later.
// DEP0106: Adjust the derived key length (32 bytes) and IV length to match the chosen algorithm.
return ${method}(${algorithm}, __dep0106Key, __dep0106Iv${options ? `, ${options}` : ''});
})()
`);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting reviewer Author has responded and needs action from the reviewer dep:v22 Migration handles deprecation introduced in node v22

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sepc: createCipher-to-createCipheriv

4 participants