Skip to content

assessAndProtect activates default-on strategies no matched policy requested #17

Description

@Isonimus

Summary

assessAndProtect() activates protection strategies (and their overlays) that no matched policy requested, because it only flips matched strategies on and leaves the rest at ContentProtector's defaults — several of which default to true.

This contradicts the documented contract ("Legitimate users see no protection overhead; automation and scrapers trigger it automatically" — i.e. activate only what the policies warrant).

Root cause

ContentProtector's constructor defaults several strategies ON (src/core/ContentProtector.ts):

preventSelection: true,
preventScreenshots: true,
// ...
preventDevTools: false,

assessAndProtect() builds protectionOptions by setting only the matched strategy keys to true (src/policy.ts), then new ContentProtector(protectionOptions). Unset keys fall through to the defaults above — so e.g. preventScreenshots activates even when every matched policy only asked for enableWatermark.

Reproduction

await assessAndProtect(el, {
  policies: [{ when: { riskScore: { gte: 0 } }, enable: ['enableWatermark'] }],
});
// Expected: only the watermark is active.
// Actual:   screenshot protection (and its red overlay) is also active,
//           plus any other default-on strategy.

Proposed fix

In assessAndProtect(), initialize all StrategyKey booleans to false in protectionOptions before enabling the matched ones, so the protector reflects exactly the matched set:

const ALL_STRATEGY_KEYS: StrategyKey[] = [
  'preventSelection', 'preventContextMenu', 'preventKeyboardShortcuts',
  'preventPrinting', 'preventScreenshots', 'preventClipboard',
  'enableWatermark', 'preventDevTools', 'preventExtensions',
];
const protectionOptions: ContentProtectionOptions = { targetElement };
for (const k of ALL_STRATEGY_KEYS) protectionOptions[k] = enabledStrategies.has(k);

A test asserting that a single-strategy policy leaves all other strategies inactive would lock this in.

Impact / workaround

Consumers get protections (and overlays) they didn't opt into. Found while wiring Shield into the tindalabs.dev demo; worked around there by calling protector.updateOptions({...}) post-creation to pin the matched set — but the fix belongs in assessAndProtect.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions