Skip to content

Commit

Permalink
Merge branch 'jul/filter-could-not-find-key' into 'master'
Browse files Browse the repository at this point in the history
fix(assertKey): add non-null falsy key detection

See merge request TankerHQ/sdk-js!973
  • Loading branch information
JMounier committed Apr 17, 2023
2 parents ccfe0f1 + de49360 commit ced2a62
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 13 deletions.
33 changes: 30 additions & 3 deletions packages/crypto/src/__tests__/resourceId.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { InvalidArgument } from '@tanker/errors';
import { InternalError, InvalidArgument } from '@tanker/errors';
import { assert, expect, sinon } from '@tanker/test-utils';

import { random } from '../random';
import { ready } from '../ready';
import { getKeyFromCompositeResourceId } from '../resourceId';
import { MAC_SIZE, SESSION_ID_SIZE } from '../tcrypto';
import { assertKey, getKeyFromCompositeResourceId } from '../resourceId';
import { MAC_SIZE, SESSION_ID_SIZE, SYMMETRIC_KEY_SIZE } from '../tcrypto';
import type { CompositeResourceId } from '../resourceId';

describe('getKeyFromCompositeResourceId', () => {
Expand Down Expand Up @@ -36,3 +36,30 @@ describe('getKeyFromCompositeResourceId', () => {
assert(keyMapper.calledTwice);
});
});

describe('assertKey', () => {
let resourceId: Uint8Array;

before(async () => {
await ready;
resourceId = random(MAC_SIZE);
});

it('succeeds when the key is a Uint8Array', () => {
expect(() => assertKey(resourceId, new Uint8Array())).to.not.throw();
expect(() => assertKey(resourceId, random(SYMMETRIC_KEY_SIZE))).to.not.throw();
});

it('throws InvalidArgument when the key is null', () => {
expect(() => assertKey(resourceId, null)).to.throw(InvalidArgument);
});

it('throws InternalError when the key is falsy but not null', () => {
//@ts-expect-error
expect(() => assertKey(resourceId, undefined)).to.throw(InternalError);
//@ts-expect-error
expect(() => assertKey(resourceId, '')).to.throw(InternalError);
//@ts-expect-error
expect(() => assertKey(resourceId, 0)).to.throw(InternalError);
});
});
11 changes: 8 additions & 3 deletions packages/crypto/src/resourceId.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { InvalidArgument } from '@tanker/errors';
import { InternalError, InvalidArgument } from '@tanker/errors';
import { assertString } from '@tanker/types';
import type { b64string, Key } from './aliases';
import type { KeyMapper } from './EncryptionFormats/KeyMapper';
Expand Down Expand Up @@ -63,12 +63,17 @@ export const deriveSessionKey = (sessionKey: Key, seed: Uint8Array): Key => gene

export function assertKey(resourceId: Uint8Array, key: Key | null): asserts key is Key {
if (!key) {
if (key !== null) {
// This is a safeguard for unexpected behavior between local-storage, network and the resource coalescer:
// We suspect that a falsy value different from null is mixed in keys somehow
throw new InternalError(`Unreachable code during resource key look-up: ${utils.toBase64(resourceId)}, key found: ${key}`);
}
throw new InvalidArgument(`could not find key for resource: ${utils.toBase64(resourceId)}`);
}
}

export const getKeyFromCompositeResourceId = async (resourceId: CompositeResourceId, keyMapper: KeyMapper) => {
let key: Key | null;
let key: Key | null = null;
const sessionKey = await keyMapper(resourceId.sessionId);
if (sessionKey) {
key = deriveSessionKey(sessionKey, resourceId.resourceId);
Expand All @@ -83,7 +88,7 @@ export const getKeyFromCompositeResourceId = async (resourceId: CompositeResourc
export const getKeyFromResourceId = async (b64resourceId: b64string, keyMapper: KeyMapper) => {
const resourceId = parseResourceId(b64resourceId);

let key: Key | null;
let key: Key | null = null;
if ('sessionId' in resourceId) {
key = await getKeyFromCompositeResourceId(resourceId, keyMapper);
} else {
Expand Down
14 changes: 7 additions & 7 deletions packages/datastore/dexie-base/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,9 +308,9 @@ export const dexieStoreBase = ((DexieClass: Class<IDexie>): DataStoreAdapter =>
// - either withLimit is a Dexie Collection or Table to convert to a Promise<Array<Object>>
// - or sortBy() has been called and withLimit is already a Promise<Array<Object>>,
if (this._isTable(withLimit) || this._isCollection(withLimit)) {
res = (withLimit as ITable | ICollection).toArray();
res = withLimit.toArray();
} else {
res = withLimit as typeof res;
res = withLimit;
}

return res.then(fromDB);
Expand All @@ -323,12 +323,12 @@ export const dexieStoreBase = ((DexieClass: Class<IDexie>): DataStoreAdapter =>

delete = this.withReopen((table: string, id: string) => this._db.table(table).delete(id));

_isTable(obj: any): boolean {
_isTable(obj: any): obj is ITable {
// @ts-expect-error this._db.Table is a Class (has a prototype)
return obj instanceof this._db.Table;
}

_isCollection(obj: any): boolean {
_isCollection(obj: any): obj is ICollection {
// @ts-expect-error this._db.Collection is a Class (has a prototype)
return obj instanceof this._db.Collection;
}
Expand Down Expand Up @@ -457,7 +457,7 @@ export const dexieStoreBase = ((DexieClass: Class<IDexie>): DataStoreAdapter =>
this._isTable(q)
&& (index === sortKey || !index && this.isIndexed(table, sortKey))
) {
res = (q as ITable).orderBy(sortKey); // ICollection (Dexie)
res = q.orderBy(sortKey); // ICollection (Dexie)
} else {
res = (q as ICollection).sortBy(sortKey); // Promise<Array<Object>>
}
Expand All @@ -469,9 +469,9 @@ export const dexieStoreBase = ((DexieClass: Class<IDexie>): DataStoreAdapter =>
let res: ICollection | Promise<Array<Record<string, any>>>;

if (this._isTable(query) || this._isCollection(query)) {
res = (query as ITable | ICollection).limit(limit);
res = query.limit(limit);
} else {
res = (query as Promise<Array<Record<string, any>>>).then((array) => array.slice(0, limit));
res = query.then((array) => array.slice(0, limit));
}

return res;
Expand Down

0 comments on commit ced2a62

Please sign in to comment.