Skip to content

Conversation

@jaissica12
Copy link
Contributor

@jaissica12 jaissica12 commented Nov 17, 2025

Background

  • Added hashSha256 method and related helpers to provide SHA-256 hashing functionality for advertisers using the mParticle Web SDK without requiring the Rokt Kit dependency.
  • Code reference: attribute-service , hash-string, rokt-docs

What Has Changed

  • Added hashSha256 method
  • Added unit tests for hashing. Referred the same test cases from sdk-webto compare the hashed values

Screenshots/Video

  • {Include any screenshots or video demonstrating the new feature or fix, if applicable}

Checklist

  • I have performed a self-review of my own code.
  • I have made corresponding changes to the documentation.
  • I have added tests that prove my fix is effective or that my feature works.
  • I have tested this locally.

Additional Notes

  • {Any additional information or context relevant to this PR}

Reference Issue (For employees only. Ignore if you are an outside contributor)

@jaissica12 jaissica12 marked this pull request as ready for review November 17, 2025 16:28
@jaissica12 jaissica12 requested a review from rmi22186 November 17, 2025 16:30
*/
public async hashSha256(attribute: string | number | boolean | undefined | null): Promise<string> {
if (attribute === undefined || attribute === null) {
throw new Error('Value cannot be null or undefined');
Copy link
Contributor

Choose a reason for hiding this comment

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

  1. i worry this will cause more harm than good in crashing the current loop/thread. our typical pattern would be to log an error or warning.
  2. I would also make the argument that there's nothing wrong with passing a null or undefined into this particular method. The method is able to act on the value passed in - it should just respond with the attribute (which is either null or undefined).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the feedback about logging pattern. Updated the code:

  • For null/undefined inputs: Returns the value as-is with a logger.warning
  • For hash failures: Returns undefined with a logger.error

Copy link
Member

Choose a reason for hiding this comment

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

I know this was updated already, but as an fyi - returning the null or undefined is fine because the wSDK will automatically sanitize these out when you pass a false-y value to it. I just tested

@jaissica12 jaissica12 requested a review from samdozor November 20, 2025 17:08
@jaissica12 jaissica12 requested a review from rmi22186 November 24, 2025 20:35
Comment on lines 270 to 282
const hashedAttributes: IRoktPartnerAttributes = {};

for (const key in attributes) {
const attributeValue = attributes[key];

hashedAttributes[key] = attributeValue;

const hashedValue = await this.hashSha256(attributeValue);

if (hashedValue) {
hashedAttributes[`${key}sha256`] = hashedValue;
}
}
Copy link
Member

Choose a reason for hiding this comment

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

This currently hashes the attributes sequentially, but the Rokt wSDK hashes in parallel, which is faster.

Suggested change
const hashedAttributes: IRoktPartnerAttributes = {};
for (const key in attributes) {
const attributeValue = attributes[key];
hashedAttributes[key] = attributeValue;
const hashedValue = await this.hashSha256(attributeValue);
if (hashedValue) {
hashedAttributes[`${key}sha256`] = hashedValue;
}
}
// Get own property keys only
const keys = Object.keys(attributes);
if (keys.length === 0) {
return {};
}
// Hash all attributes in parallel
const hashPromises = keys.map(async (key) => {
const attributeValue = attributes[key];
const hashedValue = await this.hashSha256(attributeValue);
return { key, attributeValue, hashedValue };
});
const results = await Promise.all(hashPromises);
// Build the result object
const hashedAttributes: IRoktPartnerAttributes = {};
for (const { key, attributeValue, hashedValue } of results) {
hashedAttributes[key] = attributeValue;
if (hashedValue) {
hashedAttributes[`${key}sha256`] = hashedValue;
}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the feedback, updated!

@jaissica12 jaissica12 requested a review from rmi22186 December 2, 2025 20:35

} catch (error) {
return Promise.reject(error instanceof Error ? error : new Error('Unknown error occurred'));
return Promise.reject(error instanceof Error ? error : new Error('Failed to hash attributes'));
Copy link
Member

Choose a reason for hiding this comment

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

This will throw an error, similar to what you did with hashSha256 before. Can you instead log the error here?

} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.logger.error(`Failed to hash "${attribute}" and returning undefined, selectPlacements will continue: ${errorMessage}`);
this.logger.error(`Failed to hash "${attribute}" and returning undefined: ${errorMessage}`);
Copy link
Member

Choose a reason for hiding this comment

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

@mattbodle - What are your thoughts on removing ${attribute} here? One positive is that it would reduce the total distinct logs, but we wouldn't be able to target specific attributes that the partner is using which would help us pinpoint the exact problem. What do you think?

Copy link
Member

Choose a reason for hiding this comment

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

@gtamanaha also^^

Copy link
Member

Choose a reason for hiding this comment

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

actually since this would likely be email based on the test below, I say we remove it since it'd be pii

Copy link
Member

Choose a reason for hiding this comment

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

Should just be Failed to hashSha256, returning undefined: ${errorMessage}

Copy link
Contributor Author

@jaissica12 jaissica12 Dec 3, 2025

Choose a reason for hiding this comment

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

Updated error and warning message to exclude pii

Copy link
Contributor

@gtamanaha gtamanaha Dec 3, 2025

Choose a reason for hiding this comment

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

I think having the attribute will be helpful and you can use wildcard to filter DD queries. Example

Copy link
Member

Choose a reason for hiding this comment

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

@gtamanaha - The issue here is that there is potential for this value being email (PII).

} catch (error) {
return Promise.reject(error instanceof Error ? error : new Error('Unknown error occurred'));
const errorMessage = error instanceof Error ? error.message : String(error);
this.logger.error(`Failed to hash "${attributes}", selectPlacements will continue: ${errorMessage}`);
Copy link
Member

Choose a reason for hiding this comment

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

Should just be Failed to hashAttributes, returning undefined: ${errorMessage}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated!

@jaissica12 jaissica12 requested a review from rmi22186 December 3, 2025 17:22
@sonarqubecloud
Copy link

sonarqubecloud bot commented Dec 3, 2025

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.

5 participants