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 OTP type #1283

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open

Fix OTP type #1283

wants to merge 3 commits into from

Conversation

Sneezry
Copy link
Member

@Sneezry Sneezry commented Aug 26, 2024

Fix #1271

We have both OTPType and string definitions for OTP entry types. Previously, OTPType was a numeric enum. When importing OTP URLs, OTP entries were generated with a numeric type value, causing an issue where the period parameter was ignored. This happened because we were comparing OTPType.totp (which has a numeric value of 1) with the string "totp", leading to a false condition. As a result, the system incorrectly identified the entry as not time-based, causing the period parameter to be disregarded. This fix ensures all OTP types are now aligned with the new OTPType string enum, resolving the issue.

@Sneezry Sneezry requested a review from mymindstorm August 26, 2024 18:17
@mymindstorm
Copy link
Member

I feel like there are other places where this change needs to be made. E.g.

We really need to spend some time writing E2E tests. This change is scary to me because this enum is used in a lot of places where type checking is half broken due to vuex.

@Sneezry
Copy link
Member Author

Sneezry commented Sep 11, 2024

Also fixes: #1302 #1292 #1291

@mymindstorm we should have a hotfix ASAP.

@mymindstorm
Copy link
Member

found a bug:

  1. add a HOTP code
  2. set an encryption password

the hotp code becomes a totp code

@mymindstorm
Copy link
Member

this also occurs if you add a hotp when encryption is on and reload the extension

Copy link
Member

@mymindstorm mymindstorm left a comment

Choose a reason for hiding this comment

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

This has a bug in it.

this.type = OTPType.hex;
} else if ((decryptedData.type as unknown) === 6) {
this.type = OTPType.hhex;
} else {
Copy link
Member

@mymindstorm mymindstorm Sep 15, 2024

Choose a reason for hiding this comment

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

This line is causing the bug. This if statement is not considering if decryptedData.type is a valid OTPType already and will always return OTPType.totp when decryptedData.type is a string.

Recommend making a type guard for OTPType and using it whenever we need to check if a type is valid, such as in storage.ts and elsewhere. Plus another shared function to convert from legacy otptype to new version. That would help prevent subtle mistakes like this and help remove all this casting to unknown.

@@ -617,29 +614,53 @@ export class EntryStorage {
}

if (!entryData.type) {
entryData.type = OTPType[OTPType.totp];
entryData.type = OTPType.totp;
Copy link
Member

@mymindstorm mymindstorm Sep 15, 2024

Choose a reason for hiding this comment

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

move this into the case statement or otherwise refactor so we don't have to cast to unknown below. we can set type: unknown in otp.d.ts/RawOTPStorage

type = OTPType.hhex;
} else {
type = OTPType.totp;
}
Copy link

Choose a reason for hiding this comment

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

I recommend keeping numeric enums where you get the benefit of reverse mappings so you don't have to do this ugly thing.

this.type = OTPType.hhex;
} else {
this.type = OTPType.totp;
}

Copy link

Choose a reason for hiding this comment

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

(2) I recommend keeping numeric enums where you get the benefit of reverse mappings so you don't have to do this ugly thing.

// hex = 5
// hhex = 6

if ((entry.type as unknown) === 1) {

This comment was marked as outdated.

This comment was marked as outdated.

Copy link

Choose a reason for hiding this comment

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

Error OTP type entries would only ever be 1, there is no need to check the other numbers.

See my comment below

// hex = 5
// hhex = 6

if ((decryptedData.type as unknown) === 1) {

This comment was marked as outdated.

Copy link

Choose a reason for hiding this comment

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

(2) Error OTP type entries would only ever be 1, there is no need to check the other numbers.

See my comment below

@olfek
Copy link

olfek commented Sep 26, 2024

Flow in question:

  1. src\background.ts > getTotp()
  2. src\models\storage.ts > EntryStorage > import()
  3. src\models\otp.ts > OTPEntry
  4. src\models\storage.ts > EntryStorage > getOTPStorageFromEntry()
  5. src\models\storage.ts > EntryStorage > get()

So in the currently deployed extension we have this line:

type: (parseInt(data[hash].type) as OTPType) || OTPType[OTPType.totp],

(parseInt(data[hash].type) as OTPType) is always NaN because data[hash].type is a TEXT string.

OTPType[OTPType.totp] gives the string totp but the type of type is the enum OTPType.

From this point onwards, all equality checks on type fail and default type with relevant code is always in use. For example:

if (this.type === OTPType.totp && entry.period) {
this.period = entry.period;
} else {
this.period = 30;
}

Input period is lost and entry is saved with the default value of 30. Other properties may be affected too.

const storageItem: RawOTPStorage = {
encrypted,
hash: entry.hash,
index: entry.index,
type: OTPType[entry.type],
secret,
};

Type of type is string, entry.type is a string, OTPType[entry.type] gives a number, a number is stored.

switch (entryData.type) {
case "totp":
case "hotp":
case "battle":
case "steam":
case "hex":
case "hhex":
type = OTPType[entryData.type];
break;
default:
// we need correct the type here
// and save it
type = OTPType.totp;
entryData.type = OTPType[OTPType.totp];
}

Now when loading an entry back in, the number type doesn't match any string type so the switch goes to default.

Pretty significant bug I think, this PR is a high priority one for sure.

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.

Can't set period to 60
3 participants