Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(BitField): throw an error if bit to resolve is undefined #5565

Merged
merged 9 commits into from
Jun 9, 2021
Merged

fix(BitField): throw an error if bit to resolve is undefined #5565

merged 9 commits into from
Jun 9, 2021

Conversation

kevinbioj
Copy link
Contributor

@kevinbioj kevinbioj commented Apr 28, 2021

Please describe the changes this PR makes and why it should be merged:

Currently, BitField.has(undefined) or BitField.has(<BitField>.FLAGS.ANY_NON_SPECIFIED_FLAG) returns true.

Status and versioning classification:

Please move lines that apply to you out of the comment:

  • Code changes have been tested against the Discord API, or there are no code changes
  • I know how to update typings and have done so, or typings don't need updating
  • This PR changes the library's interface (methods or parameters added)
  • This PR includes breaking changes (methods removed or renamed, parameters moved or removed)
  • This PR only includes non-code changes, like changes to documentation, README, etc.

Copy link
Contributor

@jzeuzs jzeuzs left a comment

Choose a reason for hiding this comment

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

lgtm

@kevinbioj kevinbioj changed the title fix(BitField): has must return false it bit is undefined fix(BitField): has must return false if bit is undefined Apr 28, 2021
src/util/BitField.js Outdated Show resolved Hide resolved
Co-authored-by: Vlad Frangu <[email protected]>
@kevinbioj kevinbioj changed the title fix(BitField): has must return false if bit is undefined fix(BitField): has must return false if bit equals defaultBit Apr 30, 2021
@kyranet kyranet requested a review from iCrawl April 30, 2021 12:27
@SpaceEEC
Copy link
Member

I don't really think this is what we want.

Taking an example where this would yield an unexpected result: A command handler.

In our example no permissions would be required, maybe it's a ping command.
The handler wants to catch invalid permissions early, so it's resolving the required permissions (defaulting to an empty array) to 0n.
Because it's clever it's saving the number instead of the array. (Saving a few cpu cycles, performance and stuff)
Then when a member wants to run said command the handler checks for them using effectively message.member.permissions.has(0n) and gets false, thus denying the user to execute said command.
(Except when they are admin, but that's unrelated to this issue)

As result you'd now need to change your command handler to use something like requiredPermissions === Permissions.defaultBit || message.member.permissions.has(requiredPermissions).

bitField.has(0) should return true, since there are no bits not set in the bit field.


Another inconsistency here is that apparently identical values would yield different results
Here: bitField.has(0) with false and bitField.has([]) with true (Both would return 0 if passed to BitField.resolve)

i.e. bitField.has(BitField.resolve(n)) === bitField.has(n) should always return true (or both throw an error)


Maybe this issue here be approached by editing BitField.resolve, although I am pretty sure we internally rely on undefined resolving to BitField.defaultBit everywhere.

@kyranet kyranet self-requested a review April 30, 2021 20:20
@kevinbioj
Copy link
Contributor Author

kevinbioj commented May 1, 2021

I don't really think this is what we want.

Taking an example where this would yield an unexpected result: A command handler.

In our example no permissions would be required, maybe it's a ping command.
The handler wants to catch invalid permissions early, so it's resolving the required permissions (defaulting to an empty array) to 0n.
Because it's clever it's saving the number instead of the array. (Saving a few cpu cycles, performance and stuff)
Then when a member wants to run said command the handler checks for them using effectively message.member.permissions.has(0n) and gets false, thus denying the user to execute said command.
(Except when they are admin, but that's unrelated to this issue)

As result you'd now need to change your command handler to use something like requiredPermissions === Permissions.defaultBit || message.member.permissions.has(requiredPermissions).

bitField.has(0) should return true, since there are no bits not set in the bit field.

Another inconsistency here is that apparently identical values would yield different results
Here: bitField.has(0) with false and bitField.has([]) with true (Both would return 0 if passed to BitField.resolve)

i.e. bitField.has(BitField.resolve(n)) === bitField.has(n) should always return true (or both throw an error)

Maybe this issue here be approached by editing BitField.resolve, although I am pretty sure we internally rely on undefined resolving to BitField.defaultBit everywhere.

I'm not sure what to think anymore... Is this a "non-issue"? Or should we go back to the basic change where we check if bit is undefined (and incidentally if the array is empty, if one is provided).

Going back to the command handler example, I, in my opinion, think we should not call Permissions#has if we have no permissions to check against (at least that's my vision).

kyranet
kyranet previously requested changes May 7, 2021
Copy link
Member

@kyranet kyranet left a comment

Choose a reason for hiding this comment

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

Please address the issue explained by @SpaceEEC, as this current code contains a critical bug that will make it behave in unexpected ways.

@kevinbioj
Copy link
Contributor Author

Please address the issue explained by @SpaceEEC, as this current code contains a critical bug that will make it behave in unexpected ways.

My main proposal was to return false whenever an unknown/undefined bit was given to the method (even tho I wasn't checking for possible undefined bits in an array). Vlad then requested to check, instead, if bit was equal to defaultBit after calling resolve since when undefined, the defaultBit is returned, leading to the noticed issue.
Now I see the problem in that case, but that does not tell me what would an acceptable solution be.

  • Should <BitField>.constructor.resolve throw an error when typeof bit === undefined ?
  • Should we silently ignore undefined bit(s) and do not count them ? (making <BitField>.has(undefined) === false)
  • Should we keep the current behavior and therefore, close this PR.

@kevinbioj kevinbioj changed the title fix(BitField): has must return false if bit equals defaultBit fix(BitField): throw an error if bit to resolve is undefined May 7, 2021
@kevinbioj
Copy link
Contributor Author

The first solution is probably the best one, I mean we should not give any answer if we are not able to give one.

  • Reverted changes to BitField#has
  • Updated static resolve method so if typeof bit === undefined it passes through all if and throws an error

@kevinbioj
Copy link
Contributor Author

Need advice for the three last commits.
Needed so <Permissions>.has([..., undefined, ...]) throws an error.

Copy link
Member

@SpaceEEC SpaceEEC left a comment

Choose a reason for hiding this comment

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

Should be good to go otherwise.

@@ -38,10 +38,11 @@ class BitField {
/**
* Checks whether the bitfield has a bit, or multiple bits.
* @param {BitFieldResolvable} bit Bit(s) to check for
* @param {...*} hasParams Additional parameters for the has method, if any
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure what exactly this solves, but I'd rather drop it and the recursion that requires it.
BitField.resolve handles arrays just fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Without this, it used to return true whenever they had admin no matter if you actually passed true to checkAdmin.
I still do not really understand the issue here, anyway as you said <BitField>.constructor.resolve handles arrays well already.

@iCrawl iCrawl merged commit 0156f69 into discordjs:master Jun 9, 2021
@iCrawl iCrawl added this to the Version 13 milestone Jun 27, 2021
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.

6 participants