feat: error decrypting preferences section (#990)

* feat: error decrypting preferences section

* chore: upgrade snjs
This commit is contained in:
Mo
2022-04-21 10:33:59 -05:00
committed by GitHub
parent 1391f88263
commit fdf290ea1a
10 changed files with 265 additions and 90 deletions

View File

@@ -125,8 +125,9 @@ export const ChallengeModal: FunctionComponent<Props> = ({
[values],
)
const closeModal = () => {
const cancelChallenge = () => {
if (challenge.cancelable) {
application.cancelChallenge(challenge)
onDismiss(challenge).catch(console.error)
}
}
@@ -187,7 +188,7 @@ export const ChallengeModal: FunctionComponent<Props> = ({
className={`sn-component ${
challenge.reason === ChallengeReason.ApplicationUnlock ? 'challenge-modal-overlay' : ''
}`}
onDismiss={closeModal}
onDismiss={cancelChallenge}
dangerouslyBypassFocusLock={bypassModalFocusLock}
>
<DialogContent
@@ -199,7 +200,7 @@ export const ChallengeModal: FunctionComponent<Props> = ({
>
{challenge.cancelable && (
<button
onClick={closeModal}
onClick={cancelChallenge}
aria-label="Close modal"
className="flex p-1 bg-transparent border-0 cursor-pointer absolute top-4 right-4"
>

View File

@@ -0,0 +1,130 @@
import { AppState } from '@/UIModels/AppState'
import { observer } from 'mobx-react-lite'
import { FunctionComponent } from 'preact'
import {
PreferencesGroup,
PreferencesSegment,
Text,
Title,
Subtitle,
} from '@/Components/Preferences/PreferencesComponents'
import {
ButtonType,
DisplayStringForContentType,
EncryptedItemInterface,
} from '@standardnotes/snjs'
import { Button } from '@/Components/Button/Button'
import { HorizontalSeparator } from '@/Components/Shared/HorizontalSeparator'
import { useState } from 'preact/hooks'
export const ErroredItems: FunctionComponent<{ appState: AppState }> = observer(({ appState }) => {
const app = appState.application
const [erroredItems, setErroredItems] = useState(app.items.invalidItems)
const getContentTypeDisplay = (item: EncryptedItemInterface): string => {
const display = DisplayStringForContentType(item.content_type)
if (display) {
return `${display[0].toUpperCase()}${display.slice(1)}`
} else {
return `Item of type ${item.content_type}`
}
}
const deleteItem = async (item: EncryptedItemInterface): Promise<void> => {
return deleteItems([item])
}
const deleteItems = async (items: EncryptedItemInterface[]): Promise<void> => {
const confirmed = await app.alertService.confirm(
`Are you sure you want to permanently delete ${items.length} item(s)?`,
undefined,
'Delete',
ButtonType.Danger,
)
if (!confirmed) {
return
}
void app.mutator.deleteItems(items)
setErroredItems(app.items.invalidItems)
}
return (
<PreferencesGroup>
<PreferencesSegment>
<Title>
Error Decrypting Items <span className="ml-1 color-warning"></span>
</Title>
<Text>{`${erroredItems.length} items are errored and could not be decrypted.`}</Text>
<div className="flex">
<Button
className="min-w-20 mt-3 mr-2"
variant="normal"
label="Export all"
onClick={() => {
void app.getArchiveService().downloadEncryptedItems(erroredItems)
}}
/>
<Button
className="min-w-20 mt-3 mr-2"
variant="normal"
dangerStyle={true}
label="Delete all"
onClick={() => {
void deleteItems(erroredItems)
}}
/>
</div>
<HorizontalSeparator classes="mt-5 mb-3" />
{erroredItems.map((item, index) => {
return (
<>
<div className="flex items-center justify-between">
<div className="flex flex-col">
<Subtitle>{`${getContentTypeDisplay(item)} created on ${
item.createdAtString
}`}</Subtitle>
<Text>
<div>Item ID: {item.uuid}</div>
<div>Last Modified: {item.updatedAtString}</div>
</Text>
<div className="flex">
<Button
className="min-w-20 mt-3 mr-2"
variant="normal"
label="Attempt decryption"
onClick={() => {
void app.presentKeyRecoveryWizard()
}}
/>
<Button
className="min-w-20 mt-3 mr-2"
variant="normal"
label="Export"
onClick={() => {
void app.getArchiveService().downloadEncryptedItem(item)
}}
/>
<Button
className="min-w-20 mt-3 mr-2"
variant="normal"
dangerStyle={true}
label="Delete"
onClick={() => {
void deleteItem(item)
}}
/>
</div>
</div>
</div>
{index < erroredItems.length - 1 && <HorizontalSeparator classes="mt-5 mb-3" />}
</>
)
})}
</PreferencesSegment>
</PreferencesGroup>
)
})

View File

@@ -8,15 +8,21 @@ import { Encryption } from './Encryption'
import { PasscodeLock } from './PasscodeLock'
import { Privacy } from './Privacy'
import { Protections } from './Protections'
import { ErroredItems } from './ErroredItems'
interface SecurityProps extends MfaProps {
appState: AppState
application: WebApplication
}
export const securityPrefsHasBubble = (application: WebApplication): boolean => {
return application.items.invalidItems.length > 0
}
export const Security: FunctionComponent<SecurityProps> = (props) => (
<PreferencesPane>
<Encryption appState={props.appState} />
{props.application.items.invalidItems.length > 0 && <ErroredItems appState={props.appState} />}
<Protections application={props.application} />
<TwoFactorAuthWrapper mfaProvider={props.mfaProvider} userProvider={props.userProvider} />
<PasscodeLock appState={props.appState} application={props.application} />

View File

@@ -6,10 +6,17 @@ interface Props {
iconType: IconType
label: string
selected: boolean
hasBubble?: boolean
onClick: () => void
}
export const MenuItem: FunctionComponent<Props> = ({ iconType, label, selected, onClick }) => (
export const MenuItem: FunctionComponent<Props> = ({
iconType,
label,
selected,
onClick,
hasBubble,
}) => (
<div
className={`preferences-menu-item select-none ${selected ? 'selected' : ''}`}
onClick={(e) => {
@@ -20,5 +27,6 @@ export const MenuItem: FunctionComponent<Props> = ({ iconType, label, selected,
<Icon className="icon" type={iconType} />
<div className="min-w-1" />
{label}
{hasBubble && <span className="ml-1 color-warning"></span>}
</div>
)

View File

@@ -1,7 +1,8 @@
import { action, makeAutoObservable, observable } from 'mobx'
import { FeatureIdentifier, IconType } from '@standardnotes/snjs'
import { IconType } from '@standardnotes/snjs'
import { WebApplication } from '@/UIModels/Application'
import { ExtensionsLatestVersions } from './Panes/Extensions/ExtensionsLatestVersions'
import { securityPrefsHasBubble } from './Panes'
const PREFERENCE_IDS = [
'general',
@@ -17,10 +18,12 @@ const PREFERENCE_IDS = [
] as const
export type PreferenceId = typeof PREFERENCE_IDS[number]
interface PreferencesMenuItem {
readonly id: PreferenceId | FeatureIdentifier
readonly id: PreferenceId
readonly icon: IconType
readonly label: string
readonly hasBubble?: boolean
}
interface SelectableMenuItem extends PreferencesMenuItem {
@@ -34,8 +37,8 @@ const PREFERENCES_MENU_ITEMS: PreferencesMenuItem[] = [
{ id: 'account', label: 'Account', icon: 'user' },
{ id: 'general', label: 'General', icon: 'settings' },
{ id: 'security', label: 'Security', icon: 'security' },
{ id: 'appearance', label: 'Appearance', icon: 'themes' },
{ id: 'backups', label: 'Backups', icon: 'restore' },
{ id: 'appearance', label: 'Appearance', icon: 'themes' },
{ id: 'listed', label: 'Listed', icon: 'listed' },
{ id: 'shortcuts', label: 'Shortcuts', icon: 'keyboard' },
{ id: 'accessibility', label: 'Accessibility', icon: 'accessibility' },
@@ -47,14 +50,14 @@ const READY_PREFERENCES_MENU_ITEMS: PreferencesMenuItem[] = [
{ id: 'account', label: 'Account', icon: 'user' },
{ id: 'general', label: 'General', icon: 'settings' },
{ id: 'security', label: 'Security', icon: 'security' },
{ id: 'appearance', label: 'Appearance', icon: 'themes' },
{ id: 'backups', label: 'Backups', icon: 'restore' },
{ id: 'appearance', label: 'Appearance', icon: 'themes' },
{ id: 'listed', label: 'Listed', icon: 'listed' },
{ id: 'help-feedback', label: 'Help & feedback', icon: 'help' },
]
export class PreferencesMenu {
private _selectedPane: PreferenceId | FeatureIdentifier = 'account'
private _selectedPane: PreferenceId = 'account'
private _menu: PreferencesMenuItem[]
private _extensionLatestVersions: ExtensionsLatestVersions = new ExtensionsLatestVersions(
new Map(),
@@ -101,10 +104,14 @@ export class PreferencesMenu {
}
get menuItems(): SelectableMenuItem[] {
const menuItems = this._menu.map((preference) => ({
...preference,
selected: preference.id === this._selectedPane,
}))
const menuItems = this._menu.map((preference) => {
const item: SelectableMenuItem = {
...preference,
selected: preference.id === this._selectedPane,
hasBubble: this.sectionHasBubble(preference.id),
}
return item
})
return menuItems
}
@@ -113,7 +120,7 @@ export class PreferencesMenu {
return this._menu.find((item) => item.id === this._selectedPane)
}
get selectedPaneId(): PreferenceId | FeatureIdentifier {
get selectedPaneId(): PreferenceId {
if (this.selectedMenuItem != undefined) {
return this.selectedMenuItem.id
}
@@ -121,7 +128,15 @@ export class PreferencesMenu {
return 'account'
}
selectPane(key: PreferenceId | FeatureIdentifier): void {
selectPane(key: PreferenceId): void {
this._selectedPane = key
}
sectionHasBubble(id: PreferenceId): boolean {
if (id === 'security') {
return securityPrefsHasBubble(this.application)
}
return false
}
}

View File

@@ -13,6 +13,7 @@ export const PreferencesMenuView: FunctionComponent<{
iconType={pref.icon}
label={pref.label}
selected={pref.selected}
hasBubble={pref.hasBubble}
onClick={() => menu.selectPane(pref.id)}
/>
))}

View File

@@ -5,6 +5,7 @@ import {
BackupFile,
BackupFileDecryptedContextualPayload,
NoteContent,
EncryptedItemInterface,
} from '@standardnotes/snjs'
function sanitizeFileName(name: string): string {
@@ -148,7 +149,7 @@ export class ArchiveManager {
return this.textFile
}
downloadData(data: Blob | ObjectURL, fileName: string) {
downloadData(data: Blob | ObjectURL, fileName: string): void {
const link = document.createElement('a')
link.setAttribute('download', fileName)
link.href = typeof data === 'string' ? data : this.hrefForData(data)
@@ -156,4 +157,13 @@ export class ArchiveManager {
link.click()
link.remove()
}
downloadEncryptedItem(item: EncryptedItemInterface) {
this.downloadData(new Blob([JSON.stringify(item.payload.ejected())]), `${item.uuid}.txt`)
}
downloadEncryptedItems(items: EncryptedItemInterface[]) {
const data = JSON.stringify(items.map((i) => i.payload.ejected()))
this.downloadData(new Blob([data]), 'errored-items.txt')
}
}

View File

@@ -1051,6 +1051,10 @@
color: var(--sn-stylekit-neutral-contrast-color);
}
.color-warning {
color: var(--sn-stylekit-warning-color);
}
.active\:bg-info:active {
background-color: var(--sn-stylekit-info-color);
}

View File

@@ -70,9 +70,9 @@
"@reach/tooltip": "^0.16.2",
"@reach/visually-hidden": "^0.16.0",
"@standardnotes/components": "1.7.15",
"@standardnotes/filepicker": "1.11.0",
"@standardnotes/filepicker": "1.12.0",
"@standardnotes/sncrypto-web": "1.8.3",
"@standardnotes/snjs": "2.97.4",
"@standardnotes/snjs": "2.97.7",
"@standardnotes/stylekit": "5.23.0",
"@zip.js/zip.js": "^2.4.7",
"mobx": "^6.5.0",

144
yarn.lock
View File

@@ -2388,18 +2388,18 @@
dependencies:
"@sinonjs/commons" "^1.7.0"
"@standardnotes/auth@^3.18.7":
version "3.18.7"
resolved "https://registry.yarnpkg.com/@standardnotes/auth/-/auth-3.18.7.tgz#54544e102ff734a38f8e7425dcfbf8f833679d86"
integrity sha512-43t462vS2qMnypqcVjwLOkiWc9eb2dxDWy6663UqPbaQ1fmwK868WTNd+yHdeNKPUGAOD1uETMma+CFF8FqKBQ==
"@standardnotes/auth@^3.18.10":
version "3.18.10"
resolved "https://registry.yarnpkg.com/@standardnotes/auth/-/auth-3.18.10.tgz#6be69cb1d887464daec271aeda11af4784ecb9f5"
integrity sha512-ibJvW7T/xHQooF3XVN9aGqn8DEqZxXXI0V49jquWRAztvyZ4GTwcL2RmhS2vhxvKbD6g+pBC5IOYqDn6vlUkOg==
dependencies:
"@standardnotes/common" "^1.19.4"
"@standardnotes/common" "^1.19.5"
jsonwebtoken "^8.5.1"
"@standardnotes/common@^1.19.4":
version "1.19.4"
resolved "https://registry.yarnpkg.com/@standardnotes/common/-/common-1.19.4.tgz#6ee634dd7a4c21f63d79c8ec86d483f27f11d65b"
integrity sha512-nLy7sdewf9wzHuHWBfPBGMi90Lrmy3eMnIUNzyS3DbsuoJVb6VQ3Hss5f5EG7Du59keDtmfgvx+b0wAs9EoMqA==
"@standardnotes/common@^1.19.5":
version "1.19.5"
resolved "https://registry.yarnpkg.com/@standardnotes/common/-/common-1.19.5.tgz#2ebed30ae9a1136efbbc1d86cfe43be54dd817fc"
integrity sha512-rdKOdseVAXT/WJtjAQp7fp2FmXXjoMowSu0bCvE+ggjMigbiC1z8CbFYMa/Gav2Lp+nJkBUpIMOBIwFuvPJXXA==
"@standardnotes/components@1.7.15":
version "1.7.15"
@@ -2414,63 +2414,63 @@
"@typescript-eslint/eslint-plugin" "^5.12.1"
"@typescript-eslint/parser" "^5.12.1"
"@standardnotes/domain-events@^2.27.6":
version "2.27.6"
resolved "https://registry.yarnpkg.com/@standardnotes/domain-events/-/domain-events-2.27.6.tgz#663353fdbcd4d6c5e9af35dced549196a792d370"
integrity sha512-4UnUDmDI5z/RRFUpd0owvDFCOZDVVNFhELkCJYn6HFpHmBKlvbIgARQoH1cXqUt0mi9SzvoSFFoOLCjvCFkUog==
"@standardnotes/domain-events@^2.27.9":
version "2.27.9"
resolved "https://registry.yarnpkg.com/@standardnotes/domain-events/-/domain-events-2.27.9.tgz#dbbf2bc0449e5555a6cc07945f04e86eb624e5b4"
integrity sha512-UGNlUplpp4N7FL03TuGj8xVUM2Yl/pjkVUTGijHFQMu6O2k/ugu7Bqv9cGPZS99Ix3gZUA/JUdHf9+5yRKKxCw==
dependencies:
"@standardnotes/auth" "^3.18.7"
"@standardnotes/features" "^1.37.5"
"@standardnotes/auth" "^3.18.10"
"@standardnotes/features" "^1.37.8"
"@standardnotes/encryption@^1.4.4":
version "1.4.4"
resolved "https://registry.yarnpkg.com/@standardnotes/encryption/-/encryption-1.4.4.tgz#9fb0eede1df3f434c698aff8600208996893e232"
integrity sha512-KhKVm1rlFLQLjOj4cOlwNY7uYApszwiUc4kURWYEN4uL8vbtoMD4MQMxATVzOV6G0JFSabYHVnP+/esT35eEag==
"@standardnotes/encryption@^1.4.7":
version "1.4.7"
resolved "https://registry.yarnpkg.com/@standardnotes/encryption/-/encryption-1.4.7.tgz#11340aa5f90a01d20a778c63a83f9949fef0e7d1"
integrity sha512-ot/8YsKSe/Kk8wMbmvH4/Ks/vSr2cUsg+oZVKZVKjgmGnxG2J57vkWOKIKx4GtIUIoq4HYK8JEvhY8Rx83WulA==
dependencies:
"@standardnotes/models" "^1.4.4"
"@standardnotes/responses" "^1.6.6"
"@standardnotes/services" "^1.9.5"
"@standardnotes/models" "^1.4.7"
"@standardnotes/responses" "^1.6.9"
"@standardnotes/services" "^1.9.8"
"@standardnotes/features@^1.37.5":
version "1.37.5"
resolved "https://registry.yarnpkg.com/@standardnotes/features/-/features-1.37.5.tgz#f2c90baf2fb53177f25fccc840982dc1761833c0"
integrity sha512-mBj4Cdi2cZvQ4z8jNubWTzO310qT1wbubcubsewfim9PUUd5pRq4psJKOBVjlbV8y87LiA3im5lL88lluXSv3g==
"@standardnotes/features@^1.37.8":
version "1.37.8"
resolved "https://registry.yarnpkg.com/@standardnotes/features/-/features-1.37.8.tgz#9214d9909da721e5db4774bc8ea7b5964b2dc322"
integrity sha512-A5ZSnxTO5fKpsEnlFhD3eIfcphn1d2V+6ATDcaJJIVr0tMPL5Km4BRUvu8VieosHn0obojfFCJpXKSRkxuqtlA==
dependencies:
"@standardnotes/auth" "^3.18.7"
"@standardnotes/common" "^1.19.4"
"@standardnotes/auth" "^3.18.10"
"@standardnotes/common" "^1.19.5"
"@standardnotes/filepicker@1.11.0":
version "1.11.0"
resolved "https://registry.yarnpkg.com/@standardnotes/filepicker/-/filepicker-1.11.0.tgz#2a49d78e409529ccb96f17f0575851952e7247d1"
integrity sha512-aa9g9k/BxjWuQSf+Tj+l4nKJUniagzz/YRHiVngwWIKM7wnRrvbg4z5yVT71rBb259MMU1OAaWx5EsSDSEsVVg==
"@standardnotes/filepicker@1.12.0":
version "1.12.0"
resolved "https://registry.yarnpkg.com/@standardnotes/filepicker/-/filepicker-1.12.0.tgz#6ee1224c8a7d039565a27950aa4e879a18fecebf"
integrity sha512-67hsz3/xnOmvhxFVUurjuLU1jaMKEYXCwwGSls/NJxkXEX1WwWA9kvAwAnrJTRoFJAKVDyBglcLpRrFkOvEc9w==
"@standardnotes/models@^1.4.4":
version "1.4.4"
resolved "https://registry.yarnpkg.com/@standardnotes/models/-/models-1.4.4.tgz#dfe3f3b90d8aa1cc351d80f796cd2faacbcf2f92"
integrity sha512-KYbcuy/QZG4TOfTnZKevUgAljV/t70OLaxO3DxY7TD0bYt+ie+HjXCTn4Mvmy1mnX42EuR+gVkduPwf2eiX+3w==
"@standardnotes/models@^1.4.7":
version "1.4.7"
resolved "https://registry.yarnpkg.com/@standardnotes/models/-/models-1.4.7.tgz#e0e5c12f40b477db4e0dc1e20a7251461f064524"
integrity sha512-jD5EI9g3boNEJSPnZ4eeNOYjwLQEeQU839lFqe3RrDq6M7ukUgniHBxHxgEPzgPELyIbPFN+NOoLCZdqW9h2Kg==
dependencies:
"@standardnotes/features" "^1.37.5"
"@standardnotes/responses" "^1.6.6"
"@standardnotes/utils" "^1.6.0"
"@standardnotes/features" "^1.37.8"
"@standardnotes/responses" "^1.6.9"
"@standardnotes/utils" "^1.6.1"
"@standardnotes/responses@^1.6.6":
version "1.6.6"
resolved "https://registry.yarnpkg.com/@standardnotes/responses/-/responses-1.6.6.tgz#fe909bf99f9d443290ecf1af4ad6ffc983dd7e76"
integrity sha512-nSV/nbIrlcM5Flo6OpDBkAvtz6ApagH3n6wYLA26Q7vzWLgjPsTQxocC8WWN7/FnWIgRmotCzCCMULHR16E/UA==
"@standardnotes/responses@^1.6.9":
version "1.6.9"
resolved "https://registry.yarnpkg.com/@standardnotes/responses/-/responses-1.6.9.tgz#a69a8698ddada21f0edb6bbbf50f8a68a4614bfd"
integrity sha512-HUKOrqZ5WGnxMoDGh6aEnQY48Eo3W3rNpesm91vRuKBVTxbwb876RXlElEtEAz5MYepatIg5mcX2dPxIZIHSvg==
dependencies:
"@standardnotes/auth" "^3.18.7"
"@standardnotes/common" "^1.19.4"
"@standardnotes/features" "^1.37.5"
"@standardnotes/auth" "^3.18.10"
"@standardnotes/common" "^1.19.5"
"@standardnotes/features" "^1.37.8"
"@standardnotes/services@^1.9.5":
version "1.9.5"
resolved "https://registry.yarnpkg.com/@standardnotes/services/-/services-1.9.5.tgz#4d8a0ee3efd3eaf7b3da728e30be41b7b2195de7"
integrity sha512-jye2tPjprHe0Xri+QEUZFgxret8WXVm5H/LMBuZr+XKVUE+cojQRfkr0AkEumKeQ6VY1DGzonhrmhgCVTDBt5g==
"@standardnotes/services@^1.9.8":
version "1.9.8"
resolved "https://registry.yarnpkg.com/@standardnotes/services/-/services-1.9.8.tgz#3942dfc58584388e4ec886d11f039c3f2119b7a9"
integrity sha512-uWuLNsgStNcy1dIvAsPt3VO7M01nNna0PxGs1PZupG22vm7WSbZz7bXkHmIz5ToJmsFi69M9Bt8akuzgwi3eRw==
dependencies:
"@standardnotes/common" "^1.19.4"
"@standardnotes/models" "^1.4.4"
"@standardnotes/responses" "^1.6.6"
"@standardnotes/utils" "^1.6.0"
"@standardnotes/common" "^1.19.5"
"@standardnotes/models" "^1.4.7"
"@standardnotes/responses" "^1.6.9"
"@standardnotes/utils" "^1.6.1"
"@standardnotes/settings@^1.14.0":
version "1.14.0"
@@ -2491,22 +2491,22 @@
buffer "^6.0.3"
libsodium-wrappers "^0.7.9"
"@standardnotes/snjs@2.97.4":
version "2.97.4"
resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.97.4.tgz#e972b26709c30627c30050a2b7c8d065c5207689"
integrity sha512-f5QPd63zzvnZj0ncV30vsdrySffK6cUT3x0C2Pc0zoqJV4YsozJB8TzitNlatjYZn7GiwXrqYpXP5aL6Dc6Idw==
"@standardnotes/snjs@2.97.7":
version "2.97.7"
resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.97.7.tgz#1354d234aa93f5c7c86f4f56c0631394e3e5a155"
integrity sha512-2H9hggd/yE78f0/KFGP914efO2iq9hfAczI6eQzjZaXZD8LAXzBqko01ScVlUNoIisYlJvrbWp368bnR+erwqQ==
dependencies:
"@standardnotes/auth" "^3.18.7"
"@standardnotes/common" "^1.19.4"
"@standardnotes/domain-events" "^2.27.6"
"@standardnotes/encryption" "^1.4.4"
"@standardnotes/features" "^1.37.5"
"@standardnotes/models" "^1.4.4"
"@standardnotes/responses" "^1.6.6"
"@standardnotes/services" "^1.9.5"
"@standardnotes/auth" "^3.18.10"
"@standardnotes/common" "^1.19.5"
"@standardnotes/domain-events" "^2.27.9"
"@standardnotes/encryption" "^1.4.7"
"@standardnotes/features" "^1.37.8"
"@standardnotes/models" "^1.4.7"
"@standardnotes/responses" "^1.6.9"
"@standardnotes/services" "^1.9.8"
"@standardnotes/settings" "^1.14.0"
"@standardnotes/sncrypto-common" "^1.7.6"
"@standardnotes/utils" "^1.6.0"
"@standardnotes/utils" "^1.6.1"
"@standardnotes/stylekit@5.23.0":
version "5.23.0"
@@ -2521,12 +2521,12 @@
nanostores "^0.5.10"
prop-types "^15.8.1"
"@standardnotes/utils@^1.6.0":
version "1.6.0"
resolved "https://registry.yarnpkg.com/@standardnotes/utils/-/utils-1.6.0.tgz#b511040d9abfc3b9efb209f0f197002f56409624"
integrity sha512-HtD7IxLEq0QnBb0dddTTXDvaO91KDQghnhR+qSdJtUN0IpP9Ilk035ZfOhjKPyTdJzUFgaA8t2XOlsl/7dFn8A==
"@standardnotes/utils@^1.6.1":
version "1.6.1"
resolved "https://registry.yarnpkg.com/@standardnotes/utils/-/utils-1.6.1.tgz#db26a7fb8890c9e187054bc8951cac18e48feca0"
integrity sha512-WhcyoWGKHbrcCVTR5MJNSgbS/T3cAZPw1IhRCE+0t07ChBow8shOr3/8BN+J/lzPcRMcTfVh3+MQLxLKIHEA5A==
dependencies:
"@standardnotes/common" "^1.19.4"
"@standardnotes/common" "^1.19.5"
dompurify "^2.3.6"
lodash "^4.17.21"