Skip to content

fix: Avoid undefined array key for address list#12557

Open
kesselb wants to merge 1 commit intomainfrom
bug/noid/undefined-array-key-addr-list
Open

fix: Avoid undefined array key for address list#12557
kesselb wants to merge 1 commit intomainfrom
bug/noid/undefined-array-key-addr-list

Conversation

@kesselb
Copy link
Contributor

@kesselb kesselb commented Mar 12, 2026

No description provided.

@kesselb kesselb force-pushed the bug/noid/undefined-array-key-addr-list branch from 91edd9c to 84e3fcb Compare March 12, 2026 21:28
$fromHeader = $headers->getHeader('From');
if ($fromHeader instanceof Horde_Mime_Headers_Element_Address) {
$firstAddr = AddressList::fromHorde($fromHeader->getAddressList(true))?->first();
$firstAddr = AddressList::fromHorde($fromHeader->getAddressList(true))->first();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Declared self as return type for fromHorder caused a psalm warning about ? being unless here.

=> $obj instanceof Horde_Mail_Rfc822_Address));
public static function fromHorde(Horde_Mail_Rfc822_List $hordeList): self {
$hordeObjects = iterator_to_array($hordeList, false);
$hordeAddresses = array_values(array_filter($hordeObjects, static fn (mixed $obj) => $obj instanceof Horde_Mail_Rfc822_Address));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why changing Horde_Mail_Rfc822_Object to mixed:

Introducing intermediate variables did not only improve the readability, but also made it easier for psalm to unterstand the code. It's currently not documented, that Horde_Mail_Rfc822_List always resolved to Horde_Mail_Rfc822_Objects (it should be, but isn't) and therefore psalm will complain about the typehint.

The instanceof ensures, as before, that only objects of type Horde_Mail_Rfc822_Address are passed through.

if ($this->addresses === []) {
return null;
public function first(): ?Address {
foreach ($this->addresses as $address) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

While 33 has a polyfill for array_first, 32 has not.

Signed-off-by: Daniel Kesselberg <mail@danielkesselberg.de>
@kesselb kesselb force-pushed the bug/noid/undefined-array-key-addr-list branch from 1a1e1dc to f3497f8 Compare March 12, 2026 22:35
Comment on lines 183 to -201
@@ -198,7 +196,6 @@ public function testGeneratedMessage(): void {
$this->assertEquals($replies, []);
$imapMessage->method('isOneClickUnsubscribe')->willReturn(false);
$imapMessage->method('getUnsubscribeUrl')->willReturn(null);
$addessList->method('first')->willreturn('noreply@test.com');
Copy link
Contributor Author

Choose a reason for hiding this comment

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

1) OCA\Mail\Tests\Unit\Service\AiIntegrationsServiceTest::testGeneratedMessage
PHPUnit\Framework\MockObject\IncompatibleReturnValueException: Method first may not return value of type string, its declared return type is "?OCA\Mail\Address"

The mock is not used -> remove

@kesselb
Copy link
Contributor Author

kesselb commented Mar 12, 2026

/backport to stable5.7

@kesselb kesselb requested a review from Copilot March 12, 2026 22:37
@kesselb kesselb marked this pull request as ready for review March 12, 2026 22:37
Copy link

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 fixes an "undefined array key" bug in AddressList that occurred when the internal $addresses array had non-sequential keys (e.g., after array_filter removed elements), causing $this->addresses[0] in first() to fail.

Changes:

  • Rewrote AddressList::first() to use foreach instead of direct index access, and ensured fromHorde() produces sequential arrays via array_values()
  • Added array_values() in TransmissionService::getAddressList() to normalize keys after array_filter and group expansion
  • Added test coverage for AddressList::first() and fromHorde() with non-standard input (groups producing gaps)

Reviewed changes

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

Show a summary per file
File Description
lib/AddressList.php Fixed first() to be key-agnostic, normalized keys in fromHorde(), updated type annotations to list<Address>
lib/Service/TransmissionService.php Added array_values() after filtering/expanding recipients to ensure sequential array keys
lib/Service/PhishingDetection/PhishingDetectionService.php Removed unnecessary null-safe operator since fromHorde() always returns self
tests/AddressListTest.php Added tests for first() and fromHorde() with group-expanded (gapped) input
tests/Unit/Service/AiIntegrationsServiceTest.php Removed unused AddressList mock that was never wired into the test subject

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

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants