Decision: Violation Modes

Decision

Four violation modes configurable via onViolation in WebShield.init():

Mode What happens Config value
degrade Features silently break — app partially works but core functionality fails 'degrade'
shutdown document.body.innerHTML = '', all timers and intervals killed 'shutdown'
redirect location.href = config.redirectUrl 'redirect'
honeypot App appears to work normally, all actions logged to reportUrl 'honeypot'
Custom Callback function called with violation details fn(detail)

Default: 'degrade'

Threshold: config.violationThreshold (default: 2) — number of module reports before the mode triggers. Prevents false positives from a single noisy detector.

Why degrade is the default and the recommendation

shutdown is the instinct — nuke the page, done. But it immediately signals to the attacker that protection exists and triggered. They know exactly what happened and can focus their bypass attempts.

degrade is psychologically stronger. The attacker copies the app, it appears to load, but core features silently fail. No error message. No indication something is wrong. They spend hours debugging their copy, assuming they broke something in the extraction process. The confusion is the protection.

Real-world outcome: a determined attacker eventually figures it out regardless. But degrade wastes their time and creates doubt about whether their copy will ever work. Most walk away.

Why honeypot mode exists

For cases where you want intelligence rather than deterrence. The app “works” on the wrong domain, but every action — lesson completed, answer submitted, content viewed — is logged to your endpoint. You learn who copied you, how they’re using it, and whether it’s worth a DMCA notice.

Only useful if you have a reportUrl endpoint to receive the logs and someone to review them.

Violation threshold rationale

Setting violationThreshold: 1 means a single false positive triggers the mode. DevTools detectors in particular have false positive rates worth accounting for — a user with a slow machine might trip the timing-based anti-debug check legitimately.

violationThreshold: 2 (default) requires two independent module reports before action. Reduces false positives significantly without meaningfully weakening protection — a real attacker will trigger multiple modules.

For maximum aggression: set to 1 and use shutdown. For minimum friction to legitimate users: set to 3 or 4 and use degrade.

Implementation contract

The violation handler in core/violation.js:

// Pseudocode
let count = 0
function report(moduleName, severity, detail) {
  count++
  logToReporter({ moduleName, severity, detail, count })
  if (count >= config.violationThreshold) {
    trigger(config.onViolation)
  }
}

function trigger(mode) {
  if (mode === 'shutdown')  { document.body.innerHTML = ''; killTimers() }
  if (mode === 'degrade')   { enableDegradeMode() }  // flags read by app
  if (mode === 'redirect')  { location.href = config.redirectUrl }
  if (mode === 'honeypot')  { enableHoneypotMode() }
  if (typeof mode === 'function') { mode({ count, reports }) }
}

degrade mode sets a global flag that app-level checks can read. The library does not know which features to break — the app integrates by checking WebShield.isDegraded() at feature boundaries.