Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨[REPLAY-336] Privacy by Default: MVP scaffold #914

Closed
wants to merge 5 commits into from

Conversation

jagracey
Copy link
Contributor

Motivation

Part one of a series of PRs aimed at implementing privacy by default (REPLAY-336).

  • This PR adds censorshipLevel config property to rum-core for use with rum-recorder
  • Creates an importable reference to the global config object
  • Exposes new methods isFlagEnabled and getCensorshipLevel
  • Adds new helper method censorText for HTML text censorship that preserves whitespace
  • Defines basic constants for all further PRs to build upon: PRIVACY_INPUT_MASK and FORM_PRIVATE_TAG_NAMES.

Changes

Testing


I have gone over the contributing documentation.

@jagracey jagracey requested a review from a team as a code owner June 29, 2021 14:17
@jagracey jagracey requested a review from BenoitZugmeyer June 30, 2021 08:33
@codecov-commenter
Copy link

codecov-commenter commented Jun 30, 2021

Codecov Report

Merging #914 (3ad2e85) into main (12176b8) will decrease coverage by 0.16%.
The diff coverage is 60.86%.

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #914      +/-   ##
==========================================
- Coverage   89.10%   88.93%   -0.17%     
==========================================
  Files          81       81              
  Lines        3836     3859      +23     
  Branches      855      860       +5     
==========================================
+ Hits         3418     3432      +14     
- Misses        418      427       +9     
Impacted Files Coverage Δ
packages/rum-core/src/boot/rumPublicApi.ts 95.00% <ø> (ø)
.../src/domain/segmentCollection/segmentCollection.ts 98.43% <ø> (ø)
...m-recorder/src/domain/record/serializationUtils.ts 85.52% <38.46%> (-9.72%) ⬇️
packages/core/src/domain/configuration.ts 93.33% <50.00%> (-3.10%) ⬇️
packages/rum-recorder/src/boot/startRecording.ts 100.00% <100.00%> (ø)
packages/rum-recorder/src/constants.ts 100.00% <100.00%> (ø)
packages/rum-recorder/src/domain/record/privacy.ts 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 12176b8...3ad2e85. Read the comment docs.

@jagracey jagracey force-pushed the john.gracey/privacy-by-default-mvp branch from a452f7d to 3ad2e85 Compare June 30, 2021 11:52
@@ -13,6 +13,7 @@ export const DEFAULT_CONFIGURATION = {
silentMultipleInit: false,
trackInteractions: false,
trackViewsManually: false,
censorshipLevel: 'PUBLIC',
Copy link
Member

Choose a reason for hiding this comment

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

  • It could be interesting to pass recording options to the startSessionReplayRecording function instead of init, so they are scoped and can be changed every time the recording is restarted
  • I'm not found of censorship: I think it is a bit negatively connoted. What about something like privacyLevel or maskMode?
  • We should have an object constants (ex: similar to StatusType) to avoid hardcoding values

Copy link
Contributor Author

Choose a reason for hiding this comment

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

startSessionReplayRecording is interesting. 🤔

@@ -139,6 +142,10 @@ export function buildConfiguration(userConfiguration: UserConfiguration, buildEn
configuration.trackViewsManually = !!userConfiguration.trackViewsManually
}

if ('censorshipLevel' in userConfiguration) {
configuration.censorshipLevel = userConfiguration.censorshipLevel ?? configuration.censorshipLevel
Copy link
Member

Choose a reason for hiding this comment

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

I don't think we should diverge from the current configuration handling here. Just keep whatever value the user provides.

Suggested change
configuration.censorshipLevel = userConfiguration.censorshipLevel ?? configuration.censorshipLevel
configuration.censorshipLevel = userConfiguration.censorshipLevel

@@ -89,3 +91,5 @@ function isElement(node: Node): node is Element {
function isInputElement(elem: Element): elem is HTMLInputElement {
return elem.tagName === 'INPUT'
}

export const censorText = (text: string) => text.replace(/[^\s]/g, MASKING_CHAR)
Copy link
Member

Choose a reason for hiding this comment

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

This is equivalent:

Suggested change
export const censorText = (text: string) => text.replace(/[^\s]/g, MASKING_CHAR)
export const censorText = (text: string) => text.replace(/\S/g, MASKING_CHAR)

@@ -125,5 +126,27 @@ export function getElementInputValue(element: Element, ancestorInputPrivacyMode?
}

export function maskValue(value: string) {
if (isFlagEnabled('privacy-by-default-poc')) {
return value.replace(/.+/, PRIVACY_INPUT_MASK)
Copy link
Member

Choose a reason for hiding this comment

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

FMU, you are trying to replace the value with PRIVACY_INPUT_MASK, right? So the value length doesn't leak.

First, what about:

Suggested change
return value.replace(/.+/, PRIVACY_INPUT_MASK)
return PRIVACY_INPUT_MASK

This is not enough to avoid leaking input length, because we are capturing every keystroke, so we will generate one IncrementalSnapshot.Input record for each character entered by the user. We might want to adjust things around here.

But, I think we should not try to implement this in the same PR.

Copy link
Contributor Author

@jagracey jagracey Jun 30, 2021

Choose a reason for hiding this comment

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

Agreed. More to discuss, probably making a distinction between block and mask (or other synonyms) whereby mask censors (or at least throttles) JS events. I imagine block makes more sense for simple stuff like text nodes (textContent).

return PRIVACY_INPUT_MASK yeah in hindsight....😳

@@ -47,3 +51,7 @@ export function trackViewEndRecord(lifeCycle: LifeCycle, addRawRecord: (record:
})
})
}

export function getRumRecorderConfig(): Configuration | undefined {
Copy link
Member

Choose a reason for hiding this comment

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

We will speak about this a bit further. I don't think it will be that useful to have a singleton for the configuration, because the "censorship level" depends on the DOM tree, not only the user configuration.

Copy link
Contributor Author

@jagracey jagracey Jun 30, 2021

Choose a reason for hiding this comment

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

Naming is going to need to change as we progress through the POC.
In this case, this variable represents the default censorship mode.
The name should be changed reflect that (along with a few other vars + consts)

@@ -153,7 +153,7 @@ export function doStartSegmentCollection(
state = {
status: SegmentCollectionStatus.SegmentPending,
segment,
expirationTimeoutId: setTimeout(
expirationTimeoutId: window.setTimeout(
Copy link
Member

Choose a reason for hiding this comment

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

This shouldn't be needed. We'll have to audit which files are included during e2e tests and avoid including too many files in nodejs.

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.

3 participants