From 3f16d49fe43485d75b3a1cbeaf6b7fcc7a044ee7 Mon Sep 17 00:00:00 2001 From: mikield Date: Sat, 22 Aug 2026 13:28:39 +0200 Subject: [PATCH] Send permission bit fields as decimal, not binary Bitwise::getBitSet() returns decbin(), and three payloads were sending its result to Discord: a command's default_member_permissions, and the allow and deny of a channel permission overwrite. Discord reads all three as decimal. Asking for ADMINISTRATOR, which is 1 << 3, therefore sent "1000". Discord read that as one thousand, which is ADMINISTRATOR together with MANAGE_GUILD, ADD_REACTIONS, VIEW_AUDIT_LOG, PRIORITY_SPEAKER and STREAM. Every command registered with default permissions has been granting five permissions nobody asked for, and channel overwrites have been allowing and denying the wrong things. The three now send the decimal value, and the matching getters read it back the same way rather than through fromBitSet. getBitSet itself is untouched: a binary representation is a reasonable thing to expose, and it has its own test. It just is not what goes on the wire. The reason this survived is that the test asserted the payload equalled getBitSet(), so it described the behaviour rather than the requirement and passed either way. Both tests now assert the literal value Discord expects. While there, EditPermissionsBuilderTest built its Bitwise with new Bitwise(1 << 1, 1 << 2, 1 << 3). The constructor takes a single int, so the second and third arguments were dropped and the test only ever exercised one flag; it now uses Bitwise::from. --- src/Rest/Helpers/Channel/EditPermissionsBuilder.php | 8 ++++---- src/Rest/Helpers/Command/CommandBuilder.php | 4 ++-- .../Helpers/Channel/EditPermissionsBuilderTest.php | 11 +++++++++-- tests/Rest/Helpers/Command/CommandBuilderTest.php | 8 +++++++- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/Rest/Helpers/Channel/EditPermissionsBuilder.php b/src/Rest/Helpers/Channel/EditPermissionsBuilder.php index cd8ddf45..d49f9087 100644 --- a/src/Rest/Helpers/Channel/EditPermissionsBuilder.php +++ b/src/Rest/Helpers/Channel/EditPermissionsBuilder.php @@ -37,7 +37,7 @@ public function getOverwriteId(): ?string public function setAllow(Bitwise $allow): self { - $this->data['allow'] = $allow->getBitSet(); + $this->data['allow'] = (string) $allow->get(); return $this; } @@ -45,13 +45,13 @@ public function setAllow(Bitwise $allow): self public function getAllow(): ?Bitwise { return isset($this->data['allow']) - ? Bitwise::fromBitSet($this->data['allow']) + ? new Bitwise((int) $this->data['allow']) : null; } public function setDeny(Bitwise $deny): self { - $this->data['deny'] = $deny->getBitSet(); + $this->data['deny'] = (string) $deny->get(); return $this; } @@ -59,7 +59,7 @@ public function setDeny(Bitwise $deny): self public function getDeny(): ?Bitwise { return isset($this->data['deny']) - ? Bitwise::fromBitSet($this->data['deny']) + ? new Bitwise((int) $this->data['deny']) : null; } diff --git a/src/Rest/Helpers/Command/CommandBuilder.php b/src/Rest/Helpers/Command/CommandBuilder.php index 31f9665f..72915f91 100644 --- a/src/Rest/Helpers/Command/CommandBuilder.php +++ b/src/Rest/Helpers/Command/CommandBuilder.php @@ -134,7 +134,7 @@ public function getOptions(): ?array */ public function setDefaultMemberPermissions(Bitwise $permissions): self { - $this->data['default_member_permissions'] = $permissions->getBitSet(); + $this->data['default_member_permissions'] = (string) $permissions->get(); return $this; } @@ -145,7 +145,7 @@ public function setDefaultMemberPermissions(Bitwise $permissions): self public function getDefaultMemberPermissions(): ?Bitwise { return isset($this->data['default_member_permissions']) - ? Bitwise::fromBitSet($this->data['default_member_permissions']) + ? new Bitwise((int) $this->data['default_member_permissions']) : null; } diff --git a/tests/Rest/Helpers/Channel/EditPermissionsBuilderTest.php b/tests/Rest/Helpers/Channel/EditPermissionsBuilderTest.php index 62feaf44..752d8470 100644 --- a/tests/Rest/Helpers/Channel/EditPermissionsBuilderTest.php +++ b/tests/Rest/Helpers/Channel/EditPermissionsBuilderTest.php @@ -39,7 +39,7 @@ public function testSetAllow(): void $this->assertNull($builder->getAllow()); - $bitwise = new Bitwise( + $bitwise = Bitwise::from( 1 << 1, 1 << 2, 1 << 3 @@ -48,6 +48,7 @@ public function testSetAllow(): void $builder->setAllow($bitwise); $this->assertEquals($bitwise->get(), $builder->getAllow()->get()); + $this->assertSame('14', $builder->get()['allow']); } public function testSetDeny(): void @@ -56,7 +57,7 @@ public function testSetDeny(): void $this->assertNull($builder->getDeny()); - $bitwise = new Bitwise( + $bitwise = Bitwise::from( 1 << 1, 1 << 2, 1 << 3 @@ -65,5 +66,11 @@ public function testSetDeny(): void $builder->setDeny($bitwise); $this->assertEquals($bitwise->get(), $builder->getDeny()->get()); + + /* + * A decimal bit field, as Discord reads it; the binary representation + * would be read back as a different set of permissions. + */ + $this->assertSame('14', $builder->get()['deny']); } } diff --git a/tests/Rest/Helpers/Command/CommandBuilderTest.php b/tests/Rest/Helpers/Command/CommandBuilderTest.php index 9554d9d5..bd1a07d3 100644 --- a/tests/Rest/Helpers/Command/CommandBuilderTest.php +++ b/tests/Rest/Helpers/Command/CommandBuilderTest.php @@ -92,7 +92,13 @@ public function testSetDefaultMemberPermissions(): void $commandBuilder->setDefaultMemberPermissions($permissions); $this->assertEquals($permissions->get(), $commandBuilder->getDefaultMemberPermissions()->get()); - $this->assertEquals($permissions->getBitSet(), $commandBuilder->get()['default_member_permissions']); + + /* + * Discord reads this as a decimal bit field. Sending the binary + * representation would be read back as an entirely different, and + * much larger, set of permissions. + */ + $this->assertSame('6', $commandBuilder->get()['default_member_permissions']); } public function testSetDmPermission(): void