Skip to content

feat: add key:rotate command#10174

Open
paulbalandan wants to merge 3 commits intocodeigniter4:4.8from
paulbalandan:key-rotate
Open

feat: add key:rotate command#10174
paulbalandan wants to merge 3 commits intocodeigniter4:4.8from
paulbalandan:key-rotate

Conversation

@paulbalandan
Copy link
Copy Markdown
Member

@paulbalandan paulbalandan commented May 7, 2026

Description
Adds a key:rotate spark command that demotes the current encryption.key to encryption.previousKeys in .env and generates a fresh key, so existing ciphertext stays decryptable via the KeyRotationDecorator fallback.

The actual key write is delegated to key:generate to avoid duplicating that logic. All inputs (--prefix, --length, --keep) are validated up-front, before any .env mutation, so an invalid value cannot leave the file half-rotated. Once validation passes, previousKeys is written first; if key:generate were ever to fail afterwards, the worst case is a stale-but-decryptable .env rather than a key-loss event. The replace/insert path uses anchored regexes that recognize DotEnv's optional export prefix and ignore comments mentioning the setting name.

Options: --force / -f, --prefix, --length, --keep=N.

The IO-swap pattern used to silence the key:generate sub-command was extracted to a reusable AbstractCommand::callSilently() helper in #10177 (per @michalsn's review); this branch will be rebased onto that once it lands.

Checklist:

  • Securely signed commits
  • Component(s) with PHPDoc blocks, only if necessary or adds value (without duplication)
  • Unit testing, with >80% coverage
  • User guide updated
  • Conforms to style guide

@github-actions github-actions Bot added the 4.8 PRs that target the `4.8` branch. label May 7, 2026
Copy link
Copy Markdown
Contributor

@memleakd memleakd left a comment

Choose a reason for hiding this comment

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

Thanks for working on this. I left a few inline questions about .env rewrite edge cases.

Comment thread system/Commands/Encryption/RotateKey.php Outdated
Comment thread system/Commands/Encryption/RotateKey.php Outdated
Comment thread system/Commands/Encryption/RotateKey.php Outdated
Comment thread system/Commands/Encryption/RotateKey.php Outdated
@paulbalandan paulbalandan force-pushed the key-rotate branch 2 times, most recently from db8a0eb to 64e98b0 Compare May 9, 2026 20:06

$keep = $options['keep'];

if (! is_numeric($keep) || (int) $keep < 0) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Does it make sense to use ctype_digit() instead of is_numeric() here? This will pass if someone writes '3.5'.


$length = $options['length'];

if (! is_numeric($length) || (int) $length < 1) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same here: ctype_digit()?

Comment on lines +262 to +266
if ($injected === $contents) {
// Fallback: append to the end. Shouldn't trigger because `key:generate`
// writes the `encryption.key` line just before this method runs.
$injected = $contents . "\nencryption.previousKeys = {$value}"; // @codeCoverageIgnore
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The code is okay (from what I understand), but this method runs before key:generate is called. So we need to update the comment.

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

Labels

4.8 PRs that target the `4.8` branch.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants