Skip to content

Latest commit

 

History

History
249 lines (180 loc) · 23.6 KB

File metadata and controls

249 lines (180 loc) · 23.6 KB

Shield - Roadmap

Current Status

Shield covers the full environment and tamper-detection surface: DevTools detection, WebDriver/Playwright/CDP automation flags, headless browser heuristics, patched-API detection, canvas entropy spoofing, extension detection, and active content protection (selection blocking, print prevention, screenshot prevention, watermarking). A cross-suite gap analysis found no new detection categories needed at this time — the pending items below are the right next work.


Planned Work

1. Type Safety Improvements

Priority: High | Effort: 1-2 hours | Status: Done ✅

Event handlers use unsafe as type assertions instead of leveraging the existing EventDataMap:

// Current (unsafe):
const data = event.data as { isOpen: boolean };

// Recommended (type-safe):
type DevToolsData = EventDataMap[ProtectionEventType.DEVTOOLS_STATE_CHANGE];

Implemented via the isEventType() type guard in src/core/mediator/eventDataTypes.ts, used across all event handlers.

Tasks

  • Create type guard helpers for event data validation
  • Update devToolsEventHandler.ts to use typed event data
  • Update extensionEventHandlers.ts to use typed event data
  • Update protectedContentManager.ts to use typed event data
  • Update securityOverlayManager.ts to use typed event data

2. ScreenshotDetector Consistency

Priority: Medium | Effort: 30 mins | Status: Done ✅ (resolved by deletion)

screenshotDetector.ts added event listeners directly to window instead of using eventManager. Investigation found the file was dead code: zero references anywhere, not exported (the utils barrel only exports environment and dom), and its heuristics were already duplicated by ScreenshotStrategy, which correctly uses eventManager via registerEvent. The leak concern was therefore moot (the class was never instantiated), so the orphan was deleted rather than refactored.

If screenshot detection later grows more independent heuristics, adopt the DevTools Strategy → DetectorManager → [detectors] pattern starting from ScreenshotStrategy's real logic.

Tasks

  • Refactor screenshotDetector.ts to use eventManager → deleted dead duplicate; ScreenshotStrategy already uses eventManager

3. ClipboardStrategy Integration

Priority: High | Effort: 1-2 hours | Status: Done ✅

ClipboardStrategy is now integrated into ContentProtector (see src/core/ContentProtector.ts, gated on options.preventClipboard).

Current Status: The strategy is mature with:

  • ✅ Copy/cut/paste event handling
  • ✅ Clipboard API interception
  • ✅ document.execCommand interception
  • ✅ Proper cleanup/restore methods

Tasks

  • Add preventClipboard option to ContentProtectionOptions
  • Add clipboardOptions to ContentProtectionOptions
  • Integrate ClipboardStrategy initialization in ContentProtector.initializeStrategies()
  • Add unit tests for clipboard protection (src/tests/strategies/ClipboardStrategy.test.ts)
  • Update documentation (README ContentProtector example + new complete "ContentProtector Options" table in REFERENCE.md covering clipboardOptions and all other strategy options)

4. Architecture - Shared LoggableComponent Base Class

Priority: Low | Effort: 2-3 hours | Status: Done ✅

Three abstract classes duplicate logging/error-handling functionality:

Class Duplicated Features
AbstractStrategy safeExecute, handleError, log/warn/error
AbstractDevToolsDetector safeExecute, handleError
AbstractEventHandler log/warn/error

LoggableComponent now owns COMPONENT_NAME + debugMode + logger + setDebugMode/isDebugEnabled/log/warn/error. The three abstracts extend it and keep only their own concerns. safeExecute/handleError stayed on the strategy/detector bases because their error-type enums diverge (StrategyErrorType vs DetectorErrorType) — sharing them would require generics that obscure the call sites. AbstractEventHandler's prior log/warn overrides were redundant (they re-implemented gates that SimpleLoggingService already does) and were removed.

Tasks

  • Create src/utils/base/LoggableComponent.ts
  • Refactor AbstractStrategy to extend it
  • Refactor AbstractDevToolsDetector to extend it
  • Refactor AbstractEventHandler to extend it
  • Update tests, verify build passes (tsc clean, build green, 46/46 tests pass)

5. Risk-Gated Adaptive Protection (assessAndProtect)

Priority: High | Effort: 3-5 hours | Status: Done ✅ (implemented in src/policy.ts, exported from src/index.ts, tested in src/tests/policy.test.ts)

assess() and ContentProtector are currently independent APIs — users must manually read the assessment result and activate strategies. A declarative policy bridge closes this gap: run assess() once, then activate only the strategies that are warranted by the detected signals. Legitimate users see no protection overhead; automation and scrapers trigger it automatically.

Proposed API:

import { assessAndProtect } from '@tindalabs/shield';

const protector = await assessAndProtect(element, {
  policies: [
    // Watermark all medium-risk sessions (headless, automation)
    {
      when: { riskScore: { gte: 0.3 } },
      enable: ['enableWatermark'],
      watermarkOptions: { text: (assessment) => `PROTECTED-${assessment.sessionId}` },
    },
    // Add clipboard + selection protection for high-risk sessions
    {
      when: { riskScore: { gte: 0.6 } },
      enable: ['preventSelection', 'preventClipboard'],
    },
    // Screenshot prevention specifically for headless automation
    {
      when: { signals: { 'shield.automation.headless': true } },
      enable: ['preventScreenshots'],
    },
  ],
  // OTel span emitter — policy triggers emit span events
  spanEmitter: (name, attrs) => span.addEvent(name, attrs),
});

Why this matters:

  • No legitimate user friction — protection is proportional to detected risk, not always-on
  • Traceable watermarks — watermark text can embed the assess() session token, so scraped content carries a forensic trace back to the session that extracted it
  • OTel-native policy observability — every policy trigger emits a span event (shield.policy.triggered, shield.strategy.activated) visible in Grafana; operators can see in real time how often and by what signal their content is being targeted
  • Composable with Scent — pair with scent.observe({ extraSignals: assessment.signals }) for identity-aware policies ("this device has triggered protection 12 times this week")

Use cases / marketing angles:

  • Adaptive content protection — the primary positioning; broader than any single use case
  • Anti-AI scraping — headless + automation signals map directly to scraper profiles; watermark embeds a forensic trace into any scraped content
  • Risk-proportional DRM — financial, legal, and media documents get protection only when the session warrants it

Tasks

  • Design PolicyEngine type: PolicyRule[] with when (signal conditions + risk threshold) and enable (strategy keys)
  • Implement assessAndProtect(element, options) — runs assess(), evaluates policies in order, initialises ContentProtector with the union of matched strategies
  • Support dynamic watermark text via watermarkOptions.text: string | ((assessment: ShieldAssessment) => string)
  • Emit OTel span events for each triggered policy rule (name: shield.policy.triggered, attrs: matched signals + enabled strategies)
  • Export assessAndProtect and PolicyRule from src/index.ts
  • Add unit tests: no-match policy (no protector created), single-match, multi-match, watermark text factory, OTel emit
  • Add example to README and REFERENCE.md under "Adaptive Protection"
  • Add use-case section to README: "Anti-AI scraping / adaptive content protection" — new "Use cases — adaptive content protection" section covering anti-AI scraping (with forensic-trace watermark factory example), risk-proportional DRM, and a "when not to reach for it" note pointing back to plain ContentProtector

Advisory Backlog — 2026-05-19

Findings from a full C-level assessment (CTO / CPO / COO / CMO / CFO + competitive research). Overall score: 6.3/10 — technically superior to the incumbent; held back entirely by distribution. Full report: c-level/reports/shield_2026-05-19.md

Immediate (this week)

  • Fix BrowserExtensionOptions.showOverlay: trueshowOverlay?: boolean in src/types/index.ts:203 — compile-breaking for any consumer of extension detection
  • Add coverage/ to .gitignore — generated output should not be in version control
  • Add SECURITY.md with maintainer contact and responsible disclosure path (90-day timeline)
  • Tag v0.1.0 and push ✅ — tag exists locally; publish.yml is wired to trigger on tag push. Verify the npm publish actually resolved (npm view @tindalabs/shield) — see follow-up in the 2026-05-29 backlog.

Next Sprint (1–4 weeks)

  • Move attachShieldToSpan() example to main README ✅ — new top-level section "OTel-instrumented protection" right after ContentProtector, with quick-start example, Blindspot integration variant, and the full table of emitted span events + attributes. (The "Zero runtime dependencies" badge already shipped in the badges commit above.)

  • Add README badges (npm version, CI status, MIT license, zero-runtime-deps) ✅ — coverage badge skipped: no coverage service is wired into CI yet (Codecov/Coveralls would need configuring). Worth adding once that's set up.

  • Add CONTRIBUTING.md (done ✅) + PR template ✅ at .github/PULL_REQUEST_TEMPLATE.md

  • Add .github/dependabot.yml for monthly dev-dep updates ✅ — npm (grouped minor/patch into one PR per cycle) + github-actions, both monthly.

  • Increase test coverage to 60%+ on ContentProtector, ClipboardStrategy, and all AbstractStrategy.remove() / cleanup paths — verified met. Baseline as of last audit: ContentProtector.ts 88%, ClipboardStrategy.ts 61%, AbstractStrategy.ts 71%. Remaining gaps in those files (ContentProtector lines 74-78/98/107/229/326-329; ClipboardStrategy lines 125-143/180-220; AbstractStrategy remove() paths at 89-90/205-210) are minor and can be picked up opportunistically.

  • Broader coverage uplift — in progress. Overall coverage now at 76% stmts / 61% branch / 83% funcs / 78% lines (372 tests, up from 40%/28%/37%/42% with 58 tests). Highest-leverage gaps to attack next, ordered by impact-per-test:

    1. assess.ts — 2.43%92.68% ✅ (16 tests; SSR baseline, signal flags, extension detection, risk clamp/rounding, lean spanAttributes)
    2. otel.ts — 0%100% ✅ (13 tests; every attachShieldToSpan handler + emitter-throw isolation)
    3. eventManager.ts 46%91.62% lines ✅ (31 tests; document/window/element targets, owner/selector/type queries, conflicts, isolation). intervalManager.ts 37%100% lines ✅, timeoutManager.ts 59%100% lines ✅ (28 tests across both).
    4. ContentProtectionMediator.ts — 19%92.04% lines ✅ and the 5 event handlers: devToolsEventHandler 11%100%, extensionEventHandlers 7%86.2%, iFrameEventHandlers 17%94.4%, screenShotEventHandlers 21%100%, abstractEventHandler 63%87.5%. Registry: 92.85%. (40 tests across two new suites.)
    5. protectedContentManager.ts — 11%90.47% lines ✅, keyboardShortcutManager.ts — 11%90.56% lines ✅ (38 tests across two suites; supersession bug surfaced — see follow-up above).
    6. Individual DevTools detectors — 0% each → all 7 detectors + manager covered ✅: sizeDetector 81%, timingDetector 90%, dateToStringDetector 95%, funcToStringDetector 97%, regToStringDetector 55% (Firefox/QQ-specific branches not reachable in jsdom), defineGetterDetector 81%, debugLibDetector 89%, debuggerDetector 23% (Worker not supported in jsdom — limited to constructor/dispose/isSupported paths), devToolsDetectorManager 91%. 41 tests across two new suites.
    7. LoggableComponent.ts — 40%100% ✅ (9 tests; log gating, warn brief/verbose, debug-mode toggle)
    8. dom.ts, orientation.ts, LoggingDelegate.ts — 0%dom.ts 88%, orientation.ts 100%, LoggingDelegate.ts 100% ✅ (18 tests in one combined suite).
    9. Bonus consolidation while testing: the 7 utility classes that still construct their own SimpleLoggingService ✅ Done across 3 commits (54306c8, 67a3e7c, 584840a). All 7 (TimeoutManager, IntervalManager, EventManager, SecurityOverlayManager, ProtectedContentManager, DevToolsDetectorManager, ContentProtector) now extend LoggableComponent. Net ~80 lines removed of boilerplate; SimpleLoggingService is now imported only by the base + the legacy LoggingDelegate companion. All 372 tests still green; behavioral parity preserved (same log prefixes, same "Debug mode enabled" line).

    Recent slice ✅: heavy strategies — DevToolsStrategy 25% → 80.95%, ScreenshotStrategy 35% → 83.33% (31 net new tests; both existing single-test stubs replaced with comprehensive suites covering lifecycle, event handlers, mediator publish paths, updateOptions branches).

    Recent slice ✅: long-tail strategy polish — ContextMenuStrategy 44% → 59.8%, ExtensionStrategy 48% → 65.93%, IFrameStrategy 58% → 82.52% (27 net new tests across the three suites). Lower per-file leverage than past slices but solid polish — overall now at 77.86% lines.

    All numbered coverage items are now ✅. Remaining gaps are long-tail branches (DOM-observer paths in ContextMenuStrategy, configPath fetch + fallback in ExtensionStrategy, fallback setInterval paths). Marginal cost-per-pp is high; ~78% is a reasonable "done" point.

    Distribution polish ✅ done. Only remaining gating work for v0.1.0: tag and push v0.1.0 (line 164) — that triggers publish.yml and makes npm install @tindalabs/shield work.

  • Write "migrating from disable-devtool" guide — captures incumbent's user base via search; name the gap: structured output, OTel, no boolean-only API

  • ProtectedContentManager priority-supersession orphan bug. ✅ Fixed: the displaced active state is now re-queued before the higher-priority replacement is applied, mirroring the SecurityOverlayManager re-queue fix in 4d14467. Pinned test inverted to assert the correct fallback behaviour, plus a new test covering the dismiss-and-resurface flow.

  • Prune the aspirational ProtectionEventType enum ✅ Done. Dropped 19 unused event types from the enum and their matching EventDataMap entries: STRATEGY_APPLIED/UPDATED, SELECTION_ATTEMPT, CONTEXT_MENU_ATTEMPT, KEYBOARD_SHORTCUT_BLOCKED, PRINT_ATTEMPT, DRAG_ATTEMPT, WATERMARK_TAMPERED/CREATED/REMOVED, FULLSCREEN_CHANGE, MEDIATOR_INITIALIZED/DISPOSED, ERROR_OCCURRED, DEBUG_MESSAGE, CONFIG_UPDATED, KEYBOARD_SHORTCUTS_REQUESTED/PROVIDED/UPDATED. Also removed the unused WatermarkEvent interface. The enum is now 10 entries — exactly what's wired through the mediator today — and the file carries a header explaining the detect-and-react fan-out vs direct-blocking asymmetry. All 372 tests still green.

  • Decide if blocking strategies should adopt the mediator (deferred — defensible no). Pros: single subscribe point for OTel telemetry (today wired via attachShieldToSpan callbacks — a parallel path); pluggability for consumers who want to react to blocked events without subclassing; future cross-component reactions (e.g. throttled toasts on repeated clipboard attempts). Cons: ceremony with no current decoupling benefit. Don't refactor until a concrete need surfaces; revisit if/when item above gets a real second consumer.

Strategic (1–3 months)

  • Coordinate simultaneous Show HN with scent + blindspot-ux launches as "The Tindalabs browser stack" — three composable libraries are a stronger story than three separate posts
  • Feature attachShieldToSpan() in OTel community channels (CNCF Slack, OTel SIG discussions) — OTel community is the highest-conversion distribution channel for this unique capability
  • Build community extension signature database (PR-contributed, similar to uBlock filter lists) — each new signature increases Shield's value and creates a contribution flywheel
  • Fix event-handler type assertions (ROADMAP item #1) using EventDataMap — improves type safety and reduces contributor friction

Watch List

  • Chrome/Safari DevTools heuristics — major browser updates break timing-based detection; monitor disable-devtool GitHub issues for breakage reports as a leading indicator (same heuristics apply to Shield)
  • Clipboard API permission changes — Chrome has been restricting programmatic clipboard access; monitor Web Capabilities Working Group for changes affecting ClipboardStrategy
  • @fingerprintjs/botd feature expansion — if Fingerprint adds active content protection features, Shield's positioning narrows; monitor their changelog

Advisory Backlog — 2026-05-29

Findings from the second full C-level assessment (CTO / CPO / COO / CMO / CFO / CSO + competitive research). Overall score: 7.7/10 (up from 6.3 — every internal gap from the May 19 round is closed; remaining work is non-technical). Full report: c-level/reports/shield_2026-05-29.md

The cross-cutting finding: the product is built, distribution is the bottleneck, and detection claims need real-browser verification before being marketed aggressively.

Immediate (this week)

  • Verify v0.1.0 is live on npm. ✅ Confirmed: npm view @tindalabs/shield version returns 0.1.0; tarball resolves at registry.npmjs.org/@tindalabs/shield/-/shield-0.1.0.tgz.
  • Enable npm publish provenance + 2FA on the publishing account. ✅ Three layers shipped: (1) auth-and-writes 2FA enabled on the isonimus npm account (manual). (2) --provenance flag + id-token: write in publish.yml → signed attestation linking tarball to workflow run + commit SHA. (3) Trusted Publishing (OIDC) wired in publish.yml — no NPM_TOKEN secret needed; GitHub's OIDC token is exchanged at publish time for a short-lived npm token. Manual one-time setup: add GitHub Actions as a trusted publisher in https://www.npmjs.com/package/@tindalabs/shield/access (org=tindalabs, repo=shield, workflow=publish.yml).
  • Cut a real [0.1.0] CHANGELOG section. ✅ Done in commit d447a04[Unreleased] rotated into [0.1.0] - 2026-05-29 capturing assess(), attachShieldToSpan(), assessAndProtect(), ContentProtector, ClipboardStrategy integration, CI/publish workflows, and the jest-environment-jsdom dependency relocation.
  • Add CodeQL workflow ✅ Added at .github/workflows/codeql.yml — runs javascript-typescript analysis with the security-and-quality query pack on every push to main, every PR, and weekly on Monday 06:00 UTC. Findings surface in the repo's Security tab.
  • Add a 30-second "which API do I want?" decision tree ✅ Added as a new ## Which API do I want? section between Install and Quick Start. Three-row table maps user goal (observe / block / both) to the right API (assess() / ContentProtector / assessAndProtect()) with one-line "what it does" descriptions, plus a pointer to attachShieldToSpan() for OTel-instrumented wrapping.

Next Sprint (1–4 weeks)

  • Cross-browser Playwright smoke job in CI. ✅ Added the e2e/ package: a Vite-built fixture loads Shield's source and exposes assess() on window; the spec drives it via page.evaluate and asserts result shape, the navigator.webdriver signal mirrors the live engine, a forced __REACT_DEVTOOLS_GLOBAL_HOOK__ signature composes risk → flags → spanAttributes end-to-end, and a clean session stays lean. Crucially this exercises the Worker-based DevTools debugger detector that jsdom cannot run (the path pinned at 23% unit coverage). CI runs Chromium + Firefox on ubuntu-latest and WebKit on macos-latest (Playwright's WebKit throws an "internal error" on Linux regardless of the static server — confirmed locally — so it is gated off Linux and verified natively on macOS, the engine where timing heuristics diverge most). 8/8 green on Chromium + Firefox locally.
  • Write "Migrating from disable-devtool" guide (carried over from the May 19 backlog; still the highest-leverage CMO action). Title for the search query: "Migrating from disable-devtool to a structured, OTel-native tamper detection library." Cross-post to Dev.to and r/javascript.
  • Host the demo/ SPA publicly (Vercel / GitHub Pages / shield.tindalabs.dev) with a "copy as JSON" button on the output. Currently localhost-only — the most shareable asset Shield has is unreachable to prospects.
  • Add bundle-size badge to README + CI (size-limit or bundlejs). "Zero deps" sells; "X kB gzipped" closes.
  • Add issue templates (.github/ISSUE_TEMPLATE/bug-report.md, false-positive.md, feature-request.md) and good-first-issue label on signature-registry work — opens the contribution flywheel before community PRs land blindly.
  • Add "Privacy & Data Handling" + explicit "What's NOT detected" README sections. Shield runs entirely client-side and transmits nothing unless the consumer wires spanEmitter — saying so explicitly closes the GDPR-conscious user's first question.
  • Enforce coverage threshold in CI (coverageThreshold: { global: { lines: 70 } } in jest.config.js). Backstops the 78% baseline against regression without forcing it higher.
  • Pin GitHub Actions versions to commit SHAs. Dependabot already keeps these fresh; SHA-pinning (actions/checkout@<sha> instead of @v6) closes the supply-chain window between major-version float releases.

Strategic (1–3 months)

  • Coordinated Tindalabs stack launch — Show HN + Product Hunt with blindspot + scent + shield as one narrative, not three. (Carried over from May 19; still the single highest-leverage strategic action.) Every other distribution recommendation amplifies after the first 100 strangers install Shield.
  • OTel SIG demo + CNCF Slack thread + PR to awesome-opentelemetry. This is the highest-conversion community for Shield's unique OTel-native wedge; no incumbent in the category is here yet.
  • Community extension signature registry (carried over from May 19; the contribution flywheel). Each PR-contributed signature increases library value uBlock-style.
  • Resolve Tindalabs commercial intent. Is the stack a portfolio play (CV / authority) or a future commercial entity? Either is defensible; ambiguity costs strategic clarity. If commercial, scope the SaaS layer (hosted policy editor / managed signature registry) — Shield itself stays MIT.
  • Address bus factor. Recruit one co-maintainer or publish a succession plan in SECURITY.md — materially de-risks the project for any downstream commercial customer.

Watch List additions — 2026-05-29

  • Anti-detect arms race (Camoufox / Nodriver / Patchright). Detection-side libraries co-evolve continuously with these; falling behind = silent false negatives. Monitor the 2026 anti-detect benchmarks as a leading indicator of which heuristics are still discriminating.
  • disable-devtool activity. If the incumbent resumes active development or adds structured output, Shield's wedge over it narrows. Last release as of this assessment: 10 months stale.