Skip to content

Commit

Permalink
Merge pull request #413 from statsig-io/sdk-key-verification
Browse files Browse the repository at this point in the history
SDK Key Verification
  • Loading branch information
sroyal-statsig authored Dec 12, 2023
2 parents 0a7f5b0 + c8e4d9f commit 3fdfdee
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 0 deletions.
8 changes: 8 additions & 0 deletions src/Errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,11 @@ export class StatsigInvalidArgumentError extends Error {
Object.setPrototypeOf(this, StatsigInvalidArgumentError.prototype);
}
}

export class StatsigSDKKeyMismatchError extends Error {
constructor(message?: string) {
super(message);

Object.setPrototypeOf(this, StatsigSDKKeyMismatchError.prototype);
}
}
8 changes: 8 additions & 0 deletions src/StatsigClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import StatsigLocalStorage from './utils/StatsigLocalStorage';
import Diagnostics from './utils/Diagnostics';
import ConsoleLogger from './utils/ConsoleLogger';
import { now } from './utils/Timing';
import { verifySDKKeyUsed } from './utils/ResponseVerification';

const MAX_VALUE_SIZE = 64;
const MAX_OBJ_SIZE = 2048;
Expand Down Expand Up @@ -1259,6 +1260,9 @@ export default class StatsigClient implements IHasStatsigInternal, IStatsig {
previousDerivedFields,
})
.eventually((json) => {
if (!verifySDKKeyUsed(json, this.sdkKey ?? '', this.errorBoundary)) {
return;
}
if (json?.has_updates) {
this.store
.saveWithoutUpdatingClientState(
Expand All @@ -1277,6 +1281,10 @@ export default class StatsigClient implements IHasStatsigInternal, IStatsig {
.then(async (json: Record<string, unknown>) => {
return this.errorBoundary.swallow('fetchAndSaveValues', async () => {
Diagnostics.mark.intialize.process.start({});
if (!verifySDKKeyUsed(json, this.sdkKey ?? '', this.errorBoundary)) {
Diagnostics.mark.intialize.process.end({ success: false });
return;
}
if (json?.has_updates) {
await this.store.save(
user,
Expand Down
10 changes: 10 additions & 0 deletions src/StatsigStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import StatsigAsyncStorage from './utils/StatsigAsyncStorage';
import StatsigLocalStorage from './utils/StatsigLocalStorage';
import { UserPersistentStorageInterface } from './StatsigSDKOptions';
import { EvaluationReason } from './utils/EvaluationReason';
import { verifySDKKeyUsed } from './utils/ResponseVerification';

export type EvaluationDetails = {
time: number;
Expand Down Expand Up @@ -579,6 +580,15 @@ export default class StatsigStore {
badFullResponse,
})
.then((json) => {
if (
!verifySDKKeyUsed(
json,
this.sdkInternal.getSDKKey(),
this.sdkInternal.getErrorBoundary(),
)
) {
return;
}
if (json?.has_updates) {
this.saveWithoutUpdatingClientState(user, json, prefetchUsers).catch(
(reason) =>
Expand Down
21 changes: 21 additions & 0 deletions src/utils/ResponseVerification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import ErrorBoundary from '../ErrorBoundary';
import { StatsigSDKKeyMismatchError } from '../Errors';
import { djb2Hash } from './Hashing';

export function verifySDKKeyUsed(
json: Record<string, unknown>,
sdkKey: string,
errorBoundary: ErrorBoundary,
): boolean {
const hashedSDKKeyUsed = json?.hashed_sdk_key_used;
if (hashedSDKKeyUsed != null && hashedSDKKeyUsed !== djb2Hash(sdkKey ?? '')) {
errorBoundary.logError(
'fetchAndSaveValues:eventually',
new StatsigSDKKeyMismatchError(
'The SDK key provided does not match the one used to generate values.',
),
);
return false;
}
return true;
}

0 comments on commit 3fdfdee

Please sign in to comment.