From c5e225d335537257f44168b8ad05222afccb508a Mon Sep 17 00:00:00 2001 From: Mo Date: Tue, 20 Sep 2022 12:18:00 -0500 Subject: [PATCH] fix: move wrapped storage to unwrapped if not encrypted (#1603) --- .eslintignore | 19 ++++++++++- .eslintrc.json | 2 +- packages/api/.eslintignore | 1 + packages/desktop/.eslintignore | 3 ++ packages/mobile/.eslintignore | 3 +- packages/mobile/src/Lib/ApplicationState.ts | 2 +- packages/mobile/src/Lib/Interface.ts | 7 +++- .../Settings/Sections/SecuritySection.tsx | 4 +-- packages/responses/.eslintignore | 1 + .../Domain/Device/MobileDeviceInterface.ts | 3 +- packages/snjs/.eslintignore | 6 +++- packages/snjs/lib/Application/Application.ts | 31 ++++++++--------- .../Services/Protection/ProtectionService.ts | 22 ++++++------- .../Storage/DiskStorageService.spec.ts | 33 +++++++++++++++++++ .../Services/Storage/DiskStorageService.ts | 17 ++++++---- packages/snjs/mocha/.eslintrc.js | 2 +- packages/web/.eslintignore | 4 +++ packages/web/.eslintrc | 4 ++- .../javascripts/Application/Application.ts | 9 +++-- .../Panes/Security/BiometricsLock.tsx | 4 +-- .../Panes/Security/MultitaskingPrivacy.tsx | 13 +++----- 21 files changed, 134 insertions(+), 56 deletions(-) create mode 100644 packages/desktop/.eslintignore create mode 100644 packages/snjs/lib/Services/Storage/DiskStorageService.spec.ts create mode 100644 packages/web/.eslintignore diff --git a/.eslintignore b/.eslintignore index 0539464e3..65b475733 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,19 @@ node_modules -scripts \ No newline at end of file +scripts +actions +.yarn +.vscode +.sass-cache +.husky +.github +.git +**/dist +**/coverage +**/node_modules +packages/components +packages/docs +packages/mobile/android +packages/mobile/ios +packages/mobile/html +packages/snjs/mocha +*.d.ts \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json index e79a129eb..9a0c1e6f3 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,4 +1,4 @@ { "extends": ["./node_modules/@standardnotes/config/src/.eslintrc"], - "ignorePatterns": [".eslintrc.js", "*.webpack.*.js", "webpack-defaults.js", "jest.config.js", "__mocks__"] + "ignorePatterns": [".eslintrc.js", "*.webpack.*.js", "webpack-defaults.js", "jest.config.js", "__mocks__", "**/**/coverage"] } diff --git a/packages/api/.eslintignore b/packages/api/.eslintignore index f06235c46..5a19e8ace 100644 --- a/packages/api/.eslintignore +++ b/packages/api/.eslintignore @@ -1,2 +1,3 @@ node_modules dist +coverage \ No newline at end of file diff --git a/packages/desktop/.eslintignore b/packages/desktop/.eslintignore new file mode 100644 index 000000000..50a1ac7f7 --- /dev/null +++ b/packages/desktop/.eslintignore @@ -0,0 +1,3 @@ +node_modules +dist +jsign \ No newline at end of file diff --git a/packages/mobile/.eslintignore b/packages/mobile/.eslintignore index aee931263..e46c0592a 100644 --- a/packages/mobile/.eslintignore +++ b/packages/mobile/.eslintignore @@ -5,4 +5,5 @@ ios e2e android fastlane -WebFrame \ No newline at end of file +WebFrame +__tests__ \ No newline at end of file diff --git a/packages/mobile/src/Lib/ApplicationState.ts b/packages/mobile/src/Lib/ApplicationState.ts index 2102d5a9a..ca25eb6fe 100644 --- a/packages/mobile/src/Lib/ApplicationState.ts +++ b/packages/mobile/src/Lib/ApplicationState.ts @@ -570,7 +570,7 @@ export class ApplicationState extends ApplicationService { ).includes(state) } - private async getScreenshotPrivacyEnabled(): Promise { + private async getScreenshotPrivacyEnabled(): Promise { return this.application.getMobileScreenshotPrivacyEnabled() } diff --git a/packages/mobile/src/Lib/Interface.ts b/packages/mobile/src/Lib/Interface.ts index 3f9d36e3e..403eaffd7 100644 --- a/packages/mobile/src/Lib/Interface.ts +++ b/packages/mobile/src/Lib/Interface.ts @@ -73,6 +73,11 @@ export class MobileDevice implements MobileDeviceInterface { ;(this.stateObserverService as unknown) = undefined } + consoleLog(...args: any[]): void { + // eslint-disable-next-line no-console + console.log(args) + } + async setLegacyRawKeychainValue(value: LegacyRawKeychainValue): Promise { await Keychain.setKeys(value) } @@ -378,7 +383,7 @@ export class MobileDevice implements MobileDeviceInterface { await Keychain.clearKeys() } - async setAndroidScreenshotPrivacy(enable: boolean): Promise { + setAndroidScreenshotPrivacy(enable: boolean): void { if (Platform.OS === 'android') { enable ? FlagSecure.activate() : FlagSecure.deactivate() } diff --git a/packages/mobile/src/Screens/Settings/Sections/SecuritySection.tsx b/packages/mobile/src/Screens/Settings/Sections/SecuritySection.tsx index ce8f54607..914c04ca9 100644 --- a/packages/mobile/src/Screens/Settings/Sections/SecuritySection.tsx +++ b/packages/mobile/src/Screens/Settings/Sections/SecuritySection.tsx @@ -38,14 +38,14 @@ export const SecuritySection = (props: Props) => { useEffect(() => { let mounted = true const getHasScreenshotPrivacy = async () => { - const hasScreenshotPrivacyEnabled = (await application?.getMobileScreenshotPrivacyEnabled()) ?? true + const hasScreenshotPrivacyEnabled = application?.getMobileScreenshotPrivacyEnabled() if (mounted) { setHasScreenshotPrivacy(hasScreenshotPrivacyEnabled) } } void getHasScreenshotPrivacy() const getHasBiometrics = async () => { - const appHasBiometrics = await application!.hasBiometrics() + const appHasBiometrics = application!.hasBiometrics() if (mounted) { setHasBiometrics(appHasBiometrics) } diff --git a/packages/responses/.eslintignore b/packages/responses/.eslintignore index f06235c46..5a19e8ace 100644 --- a/packages/responses/.eslintignore +++ b/packages/responses/.eslintignore @@ -1,2 +1,3 @@ node_modules dist +coverage \ No newline at end of file diff --git a/packages/services/src/Domain/Device/MobileDeviceInterface.ts b/packages/services/src/Domain/Device/MobileDeviceInterface.ts index 2ca8c5992..65bfa7279 100644 --- a/packages/services/src/Domain/Device/MobileDeviceInterface.ts +++ b/packages/services/src/Domain/Device/MobileDeviceInterface.ts @@ -6,8 +6,9 @@ export interface MobileDeviceInterface extends DeviceInterface { getRawKeychainValue(): Promise getDeviceBiometricsAvailability(): Promise - setAndroidScreenshotPrivacy(enable: boolean): Promise + setAndroidScreenshotPrivacy(enable: boolean): void authenticateWithBiometrics(): Promise hideMobileInterfaceFromScreenshots(): void stopHidingMobileInterfaceFromScreenshots(): void + consoleLog(...args: any[]): void } diff --git a/packages/snjs/.eslintignore b/packages/snjs/.eslintignore index 5d09b15e4..1a615092b 100644 --- a/packages/snjs/.eslintignore +++ b/packages/snjs/.eslintignore @@ -2,4 +2,8 @@ node_modules dist test *.config.js -mocha/**/* \ No newline at end of file +mocha/**/* +coverage +e2e-server.js +jest-global.ts +webpack.*.js \ No newline at end of file diff --git a/packages/snjs/lib/Application/Application.ts b/packages/snjs/lib/Application/Application.ts index 9f7cd573a..526d22e78 100644 --- a/packages/snjs/lib/Application/Application.ts +++ b/packages/snjs/lib/Application/Application.ts @@ -324,6 +324,7 @@ export class SNApplication ) } } + await this.handleStage(ExternalServices.ApplicationStage.StorageDecrypted_09) this.apiService.loadHost() @@ -929,30 +930,30 @@ export class SNApplication return this.deinit(this.getDeinitMode(), DeinitSource.Lock) } - async setBiometricsTiming(timing: MobileUnlockTiming) { + setBiometricsTiming(timing: MobileUnlockTiming) { return this.protectionService.setBiometricsTiming(timing) } - async getMobileScreenshotPrivacyEnabled(): Promise { + getMobileScreenshotPrivacyEnabled(): boolean { return this.protectionService.getMobileScreenshotPrivacyEnabled() } - async getMobilePasscodeTiming(): Promise { - return this.getValue(StorageKey.MobilePasscodeTiming, StorageValueModes.Nonwrapped) as Promise< - MobileUnlockTiming | undefined - > - } - - async getMobileBiometricsTiming(): Promise { - return this.getValue(StorageKey.MobileBiometricsTiming, StorageValueModes.Nonwrapped) as Promise< - MobileUnlockTiming | undefined - > - } - - async setMobileScreenshotPrivacyEnabled(isEnabled: boolean) { + setMobileScreenshotPrivacyEnabled(isEnabled: boolean) { return this.protectionService.setMobileScreenshotPrivacyEnabled(isEnabled) } + getMobilePasscodeTiming(): MobileUnlockTiming | undefined { + return this.getValue(StorageKey.MobilePasscodeTiming, StorageValueModes.Nonwrapped) as + | MobileUnlockTiming + | undefined + } + + getMobileBiometricsTiming(): MobileUnlockTiming | undefined { + return this.getValue(StorageKey.MobileBiometricsTiming, StorageValueModes.Nonwrapped) as + | MobileUnlockTiming + | undefined + } + async loadMobileUnlockTiming() { return this.protectionService.loadMobileUnlockTiming() } diff --git a/packages/snjs/lib/Services/Protection/ProtectionService.ts b/packages/snjs/lib/Services/Protection/ProtectionService.ts index bc9ea1865..3c7c345d5 100644 --- a/packages/snjs/lib/Services/Protection/ProtectionService.ts +++ b/packages/snjs/lib/Services/Protection/ProtectionService.ts @@ -257,36 +257,36 @@ export class SNProtectionService extends AbstractService implem ] } - private async getBiometricsTiming(): Promise { - return this.storageService.getValue>( + private getBiometricsTiming(): MobileUnlockTiming | undefined { + return this.storageService.getValue( StorageKey.MobileBiometricsTiming, StorageValueModes.Nonwrapped, ) } - private async getPasscodeTiming(): Promise { - return this.storageService.getValue>( + private getPasscodeTiming(): MobileUnlockTiming | undefined { + return this.storageService.getValue( StorageKey.MobilePasscodeTiming, StorageValueModes.Nonwrapped, ) } async setBiometricsTiming(timing: MobileUnlockTiming) { - await this.storageService.setValue(StorageKey.MobileBiometricsTiming, timing, StorageValueModes.Nonwrapped) + this.storageService.setValue(StorageKey.MobileBiometricsTiming, timing, StorageValueModes.Nonwrapped) this.mobileBiometricsTiming = timing } - async setMobileScreenshotPrivacyEnabled(isEnabled: boolean) { + setMobileScreenshotPrivacyEnabled(isEnabled: boolean) { return this.storageService.setValue(StorageKey.MobileScreenshotPrivacyEnabled, isEnabled, StorageValueModes.Default) } - async getMobileScreenshotPrivacyEnabled(): Promise { - return this.storageService.getValue(StorageKey.MobileScreenshotPrivacyEnabled, StorageValueModes.Default) + getMobileScreenshotPrivacyEnabled(): boolean { + return this.storageService.getValue(StorageKey.MobileScreenshotPrivacyEnabled, StorageValueModes.Default, false) } - async loadMobileUnlockTiming() { - this.mobilePasscodeTiming = await this.getPasscodeTiming() - this.mobileBiometricsTiming = await this.getBiometricsTiming() + loadMobileUnlockTiming(): void { + this.mobilePasscodeTiming = this.getPasscodeTiming() + this.mobileBiometricsTiming = this.getBiometricsTiming() } private async validateOrRenewSession( diff --git a/packages/snjs/lib/Services/Storage/DiskStorageService.spec.ts b/packages/snjs/lib/Services/Storage/DiskStorageService.spec.ts new file mode 100644 index 000000000..8ed72ac2c --- /dev/null +++ b/packages/snjs/lib/Services/Storage/DiskStorageService.spec.ts @@ -0,0 +1,33 @@ +import { DiskStorageService } from './DiskStorageService' + +import { InternalEventBus, DeviceInterface, InternalEventBusInterface } from '@standardnotes/services' +import { Environment } from '@standardnotes/models' + +describe('diskStorageService', () => { + let storageService: DiskStorageService + let internalEventBus: InternalEventBusInterface + let device: DeviceInterface + + beforeEach(() => { + internalEventBus = {} as jest.Mocked + device = {} as jest.Mocked + + storageService = new DiskStorageService(device, 'test', Environment.Desktop, internalEventBus) + }) + + it('setInitialValues should set unwrapped values as wrapped value if wrapped value is not encrypted', async () => { + storageService.isStorageWrapped = jest.fn().mockReturnValue(false) + + await storageService['setInitialValues']({ + wrapped: { content: { foo: 'bar' } } as never, + nonwrapped: {}, + unwrapped: { bar: 'zoo' }, + }) + + expect(storageService['values']).toEqual({ + wrapped: { content: { foo: 'bar' } } as never, + nonwrapped: {}, + unwrapped: { bar: 'zoo', foo: 'bar' }, + }) + }) +}) diff --git a/packages/snjs/lib/Services/Storage/DiskStorageService.ts b/packages/snjs/lib/Services/Storage/DiskStorageService.ts index 828680bd7..b7f6f6bd1 100644 --- a/packages/snjs/lib/Services/Storage/DiskStorageService.ts +++ b/packages/snjs/lib/Services/Storage/DiskStorageService.ts @@ -112,14 +112,10 @@ export class DiskStorageService extends Services.AbstractService implements Serv const value = await this.deviceInterface.getRawStorageValue(this.getPersistenceKey()) const values = value ? JSON.parse(value as string) : undefined - this.setInitialValues(values) + await this.setInitialValues(values) } - /** - * Called by platforms with the value they load from disk, - * after they handle initializeFromDisk - */ - private setInitialValues(values?: Services.StorageValuesObject) { + private async setInitialValues(values?: Services.StorageValuesObject) { const sureValues = values || this.defaultValuesObject() if (!sureValues[Services.ValueModesKeys.Unwrapped]) { @@ -127,6 +123,13 @@ export class DiskStorageService extends Services.AbstractService implements Serv } this.values = sureValues + + if (!this.isStorageWrapped()) { + this.values[Services.ValueModesKeys.Unwrapped] = { + ...(this.values[Services.ValueModesKeys.Wrapped].content as object), + ...this.values[Services.ValueModesKeys.Unwrapped], + } + } } public isStorageWrapped(): boolean { @@ -370,7 +373,7 @@ export class DiskStorageService extends Services.AbstractService implements Serv * Clears simple values from storage only. Does not affect payloads. */ async clearValues() { - this.setInitialValues() + await this.setInitialValues() await this.immediatelyPersistValuesToDisk() } diff --git a/packages/snjs/mocha/.eslintrc.js b/packages/snjs/mocha/.eslintrc.js index 00f68f3b4..3cfc2c7d3 100644 --- a/packages/snjs/mocha/.eslintrc.js +++ b/packages/snjs/mocha/.eslintrc.js @@ -1,5 +1,5 @@ module.exports = { - extends: ['../.eslintrc.js'], + extends: ['../.eslintrc'], globals: { chai: true, chaiAsPromised: true, diff --git a/packages/web/.eslintignore b/packages/web/.eslintignore new file mode 100644 index 000000000..5adbe0594 --- /dev/null +++ b/packages/web/.eslintignore @@ -0,0 +1,4 @@ +dist +node_modules +web.webpack-defaults.js +coverage \ No newline at end of file diff --git a/packages/web/.eslintrc b/packages/web/.eslintrc index 17b9ea48a..d533e8431 100644 --- a/packages/web/.eslintrc +++ b/packages/web/.eslintrc @@ -19,7 +19,9 @@ "__mocks__", "src/components", "src/favicon", - "src/vendor" + "src/vendor", + "coverage", + "*.config.js" ], "rules": { "standard/no-callback-literal": 0, // Disable this as we have too many callbacks relying on literals diff --git a/packages/web/src/javascripts/Application/Application.ts b/packages/web/src/javascripts/Application/Application.ts index f80b10309..0f2583368 100644 --- a/packages/web/src/javascripts/Application/Application.ts +++ b/packages/web/src/javascripts/Application/Application.ts @@ -76,6 +76,11 @@ export class WebApplication extends SNApplication implements WebApplicationInter if (this.isNativeMobileWeb()) { this.mobileWebReceiver = new MobileWebReceiver(this) + + // eslint-disable-next-line no-console + console.log = (...args) => { + this.mobileDevice.consoleLog(...args) + } } this.onVisibilityChange = () => { @@ -228,7 +233,7 @@ export class WebApplication extends SNApplication implements WebApplicationInter async handleMobileGainingFocusEvent(): Promise {} async handleMobileLosingFocusEvent(): Promise { - if (await this.getMobileScreenshotPrivacyEnabled()) { + if (this.getMobileScreenshotPrivacyEnabled()) { this.mobileDevice.stopHidingMobileInterfaceFromScreenshots() } @@ -236,7 +241,7 @@ export class WebApplication extends SNApplication implements WebApplicationInter } async handleMobileResumingFromBackgroundEvent(): Promise { - if (await this.getMobileScreenshotPrivacyEnabled()) { + if (this.getMobileScreenshotPrivacyEnabled()) { this.mobileDevice.hideMobileInterfaceFromScreenshots() } } diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/Security/BiometricsLock.tsx b/packages/web/src/javascripts/Components/Preferences/Panes/Security/BiometricsLock.tsx index c9814b227..f53ce8ae4 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/Security/BiometricsLock.tsx +++ b/packages/web/src/javascripts/Components/Preferences/Panes/Security/BiometricsLock.tsx @@ -20,7 +20,7 @@ const BiometricsLock = ({ application }: Props) => { useEffect(() => { const getHasBiometrics = async () => { - const appHasBiometrics = await application.hasBiometrics() + const appHasBiometrics = application.hasBiometrics() setHasBiometrics(appHasBiometrics) } @@ -50,7 +50,7 @@ const BiometricsLock = ({ application }: Props) => { await disableBiometrics() } else { setHasBiometrics(true) - await application.enableBiometrics() + application.enableBiometrics() await setBiometricsTimingValue(MobileUnlockTiming.OnQuit) } } diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/Security/MultitaskingPrivacy.tsx b/packages/web/src/javascripts/Components/Preferences/Panes/Security/MultitaskingPrivacy.tsx index d5244d635..4d4fe87cc 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/Security/MultitaskingPrivacy.tsx +++ b/packages/web/src/javascripts/Components/Preferences/Panes/Security/MultitaskingPrivacy.tsx @@ -13,22 +13,19 @@ type Props = { } const MultitaskingPrivacy = ({ application }: Props) => { - const [hasScreenshotPrivacy, setHasScreenshotPrivacy] = useState(false) + const [hasScreenshotPrivacy, setHasScreenshotPrivacy] = useState(false) useEffect(() => { - const getHasScreenshotPrivacy = async () => { - const hasScreenshotPrivacyEnabled = (await application.getMobileScreenshotPrivacyEnabled()) ?? true - setHasScreenshotPrivacy(hasScreenshotPrivacyEnabled) - } - void getHasScreenshotPrivacy() + const hasScreenshotPrivacyEnabled = application.getMobileScreenshotPrivacyEnabled() + setHasScreenshotPrivacy(hasScreenshotPrivacyEnabled) }, [application]) const onScreenshotPrivacyPress = async () => { const enable = !hasScreenshotPrivacy setHasScreenshotPrivacy(enable) - await application.setMobileScreenshotPrivacyEnabled(enable) - await (application.deviceInterface as MobileDeviceInterface).setAndroidScreenshotPrivacy(enable) + application.setMobileScreenshotPrivacyEnabled(enable) + ;(application.deviceInterface as MobileDeviceInterface).setAndroidScreenshotPrivacy(enable) } const screenshotPrivacyFeatureText = isIOS() ? 'Multitasking Privacy' : 'Multitasking/Screenshot Privacy'