This template builds on the fundamentals of threat modeling, tailoring them for the unique context of an open-source library. Libraries are building blocks; their security impacts every application that uses them. Thinking like an attacker about your library is a critical step in providing a robust, trusted component to the ecosystem.
- Consumers are Developers: Your users are other developers integrating your code. They rely on your library's integrity and correct behavior.
- Integration Points are APIs: The primary interface for attack and misuse is often your public API.
- Supply Chain Risk: Your dependencies are your dependencies' dependencies, and so on.
- Side Effects & Resource Usage: Unintended consequences or excessive resource consumption can impact host applications.
We'll use STRIDE, a widely adopted framework, to help identify common types of threats:
- Spoofing: Impersonating someone or something else (e.g., faking identity).
- Tampering: Modifying data or code (e.g., altering a file, changing network packets).
- Repudiation: Denying an action that occurred (e.g., a user denies making a transaction).
- Information Disclosure: Revealing sensitive data to unauthorized individuals (e.g., leaking PII, credentials).
- Denial of Service: Preventing legitimate users from accessing a service or resource (e.g., crashing a server, exhausting resources).
- Elevation of Privilege: Gaining unauthorized access to higher-level permissions (e.g., a regular user becoming an administrator).
- Library Name: Two.js
- Brief Description: Two.js is a lightweight, renderer-agnostic 2D drawing library that provides a scene graph abstraction over multiple rendering backends (SVG, Canvas2D, WebGL). It solves the problem of writing portable vector / shape animation code once while letting developers switch or optimize the underlying renderer with minimal changes. It emphasizes an intuitive API for creating, grouping, transforming, styling, and animating vector primitives.
- Core Paradigm: Retained-mode scene graph (groups and shapes persist between frames) with an imperative mutation API and an explicit or automatic update loop.
- Key Public APIs / Constructors / Namespaces:
new Two(options)– Create a Two.js instance (specifyingtype,width,height,domElement,autostart, etc.).- Factory / shape creation helpers:
two.makeRectangle,two.makeCircle,two.makeEllipse,two.makePolygon,two.makePath,two.makeLine,two.makeCurve,two.makeGroup,two.makeText,two.makeSprite,two.makeImageSequence,two.load(for SVG / image imports where available). - Scene mutation:
group.add(child),group.remove(child),two.add(shape), property assignments (shape.fill,shape.stroke,shape.opacity,shape.linewidth,shape.visible,shape.translation,shape.rotation,shape.scale). - Animation / loop control:
two.play(),two.pause(),two.update(), event listeners (two.bind('update', fn),two.bind('resize', fn)). - Utility / export:
two.renderer,two.scene,shape.clone(),shape.noFill(),shape.noStroke(). - Collections:
two.scene.children(Collection wrapper),group.children. - Vector / math primitives:
Two.Vector,Two.Anchor, transformation matrix application via properties.
- Typical Usage Flow: Instantiate -> Create shapes -> Group / style -> Start animation loop (autostart or manual
update) -> Mutate properties per frame -> Renderer updates output surface. - Out of Scope (Current Model): Advanced plugin ecosystem, experimental renderers not in core distribution, server-side rendering scenarios (Node + headless GL), non-standard forks.
- Maintainer Context: Sole maintainer; release / build / dependency decisions centralized—heightens importance of supply chain, CI integrity, and key management.
Scope Focus (Current Session):
- Core Scene Graph & Mutation Layer (Groups, Shapes, Anchors, property setters, cloning).
- Public Factory Methods (
make*functions) and their parameter handling / validation. - Rendering Abstraction Dispatch (selection & invocation of SVG / Canvas2D / WebGL backends) – not deep-diving into internal renderer implementation specifics, but reviewing trust interfaces.
- Update / Animation Loop Execution Path (
two.update(), boundupdateevents) including traversal, matrix computations, and draw command emission. - External Resource Ingestion: Basic SVG path / image / font references where passed through existing helper methods (no extended parsing plugin deep dive).
- Build & Distribution Pipeline: Generation of
two.js,two.min.js,two.module.jsfromsrc/and publishing steps (risk of supply chain tampering, artifact integrity).
Explicitly Out of Scope (Deferred):
- Experimental / legacy examples under
junk/orextras/beyond noting they may expand attack surface if shipped or copied by consumers. - Non-browser environments (Node.js + headless GL) – different threat profile (file system, process-level concerns).
- Third-party downstream integrations (framework wrappers) not maintained in this repository.
Assumptions for This Model:
- Consumers integrate via standard bundlers or direct
<script>tags; no privileged browser extensions. - Browsers enforce origin policy and standard DOM / WebGL security constraints.
- No secrets handled by the library; sensitive data risk centers on inadvertent propagation of caller-provided data.
Visualize your library's interaction points (use Mermaid or a sketch on paper). This might include:
- Caller Application: The code integrating your library.
- Your Library's Core Logic: The main processing unit.
- External Dependencies: Other libraries your library uses.
- Data Sources/Sinks: Where your library gets data from, or sends data to (even if indirectly, through the caller).
- Trust Boundaries: Where does your library implicitly trust its caller? Where does it trust its dependencies? (e.g., does it trust all input from the caller? Does it assume its dependencies are always safe?).
Self-reflection: How does your library receive input? What does it output? What assumptions does it make about its environment?
Two.js is a 2D scene graph + rendering abstraction that lets a caller (developer code) create and manipulate vector shapes and text, then render them through one of several renderer backends (SVG / Canvas2D / WebGL). The core security- and integrity-relevant elements are:
- Public API Surface (
new Two(options),two.add(shape),two.makeXXX(),two.update(), shape mutation properties) – primary untrusted input channel. - Scene Graph (Root Group -> Groups -> Shapes -> Anchors / Vertices) – internal state derived from caller input; integrity critical.
- Render Loop (
requestAnimationFramecallback or manualtwo.update()) – transforms scene state into drawing commands. - Renderer Abstractions: SVG DOM Manipulation, Canvas2D Context Commands, WebGL Program / Buffer Operations – interface with browser subsystems.
- External Resources: Images (HTMLImageElement), Fonts (via CSS / browser), JSON / SVG import data – indirect data sources potentially attacker-controlled if caller passes them.
- Environment APIs: Browser DOM, WebGL context, Timing (
performance,requestAnimationFrame), possibly OffscreenCanvas – assumed trustworthy but may be leveraged for DoS (performance abuse) or info disclosure (timing side channels) if misused. - Output Surfaces: DOM Nodes (SVG),
<canvas>pixel buffer, WebGL framebuffer – observed by end user; side effects limited to rendering but may leak internal structure via generated SVG markup or error messages. - Optional Extensions / Plugins (
extras/arc.js,extras/zui.js) – broaden attack surface if they parse additional input or attach event listeners.
Trust Boundary Highlights:
- Caller Code -> Two.js Public API (All arguments are untrusted; must validate types / ranges to prevent resource exhaustion).
- Two.js Internal Scene State -> Renderer Backend (Assumes sanitized numeric geometry; malformed values could cause performance degradation or exceptions).
- External Resource Loading (Images / Imported SVG / JSON) -> Parsing / Texture Upload (Potential for large memory usage or maliciously crafted SVG path strings if importer exists).
- Build / Distribution Pipeline -> Published Artifacts (Integrity of distributed
two.js/two.min.js).
Primary Data Flows (Simplified): Caller Input -> API Methods -> Scene Graph Mutation -> Frame Update Traversal -> Backend Draw Calls -> Output Surface.
flowchart LR
subgraph A[Caller Application Code]
UI[User Events / App Logic]
API_Calls[Two.js API Calls: makeShape, add, set properties]
end
subgraph B[Two.js Core]
SG[Scene Graph: Groups, Shapes, Elements, etc.]
Loop[Update / Animation Loop]
Utils[Math / Utils]
end
subgraph C[Renderer Backends]
SVG[SVG Renderer]
CAN[Canvas2D Renderer]
GL[WebGL Renderer]
end
subgraph D[Browser / External]
DOM[DOM APIs]
GPU[WebGL / GPU]
IMG[Images / Fonts / Imported Data]
Time[requestAnimationFrame]
end
UI --> API_Calls
API_Calls --> SG
IMG -->|resource refs| API_Calls
SG --> Loop
Time --> Loop
Loop -->|traverse| SG
Loop -->|draw commands| SVG
Loop -->|draw commands| CAN
Loop -->|buffers & shaders| GL
SVG --> DOM
CAN --> DOM
GL --> GPU
GPU --> CAN
classDef boundary stroke:#d33,stroke-width:2,stroke-dasharray:4 4;
class A,B,C,D boundary;
Notes:
- Dashed red boxes represent high-level trust zones; most validation must occur at the A->B interface.
- The
IMG/ external resource node is untrusted even though it sits within Browser; treat resource-derived geometry / pixel data as hostile until validated (dimensions, type, size caps).
sequenceDiagram
participant Caller
participant Two as Two Instance
participant SG as Scene Graph
participant R as Renderer
participant Browser as Browser APIs
Caller->>Two: mutate shapes / set properties
Two->>SG: apply changes
Caller->>Two: two.update()
Two->>SG: traverse & compute matrices
Two->>R: issue draw instructions
R->>Browser: DOM / Canvas / WebGL ops
Browser-->>R: success / errors
R-->>Two: completion
Two-->>Caller: frame rendered
Security / Integrity Considerations (Anchored in Diagram):
- Input Validation: Constrain numeric values (e.g., stroke widths, vertex counts) to avoid pathological loops or memory spikes.
- Resource Limits: Cap imported image / SVG size & complexity (vertex count) before insertion into SG.
- Error Hygiene: Avoid leaking internal stack traces through thrown errors during traversal / rendering.
- Renderer Choice: Selecting WebGL exposes GPU memory & shader compilation errors—wrap and sanitize messages.
- Mutation Control: Provide immutable export serialization paths to prevent caller tampering with internal references after freeze (if implementing hardening).
- Timing: Avoid using high-resolution timing differences to conditionally branch in ways that could leak geometry structure (low risk but noted).
Assumptions:
- Browser environment is not intentionally malicious but can be a vector for performance constraints.
- Caller code may pass arbitrary data; no implicit trust in provided geometry, strings, or resource handles.
- Distribution artifacts served over a trusted channel (e.g., integrity attributes or package registry checks) to prevent supply chain tampering.
Asset Inventory (Two.js Specific):
- API Integrity & Semantics: Deterministic, documented behavior of shape construction, mutation, and rendering order.
- Scene Graph Consistency: Internal data structures (hierarchies, vertex arrays, transformation matrices) free from corruption or unintended shared references.
- Rendering Stability & Performance: Predictable frame times; avoidance of unbounded loops, runaway allocations, GPU state thrash, or layout trashing (SVG DOM bloat / Canvas overdraw).
- Distribution Artifacts: Authentic
two.js,two.min.js,two.module.jsfiles (no injected malicious code) – includes minified integrity. - Build / Release Credentials: Access tokens / signing keys (if introduced) and CI configuration controlling publication.
- Dependency Baseline: Integrity and security posture of direct dependencies (if any) and build tooling (bundler, minifier, transpiler) to prevent supply chain compromise.
- Error Channel Hygiene: Console / thrown error content not leaking internal structure or repository paths enabling targeted exploitation.
- Resource Handling Boundaries: Limits around imported SVG path complexity, image dimensions, or vertex counts to prevent memory / CPU exhaustion.
- Maintainer Reputation: Trust relationship with downstream ecosystem—core to adoption and risk management.
- Downstream Consumer Safety: Ensuring Two.js is not a vector enabling XSS (via unsafe SVG injection) or WebGL-based fingerprinting escalation beyond normal browser constraints.
Non-Assets / Low Sensitivity (Still Monitored):
- Transient numerical calculations (intermediate matrices) – important for correctness, low direct attacker value.
- Non-secret configuration values (renderer type, width/height) – can be tampered with for DoS but not confidentiality.
Approach: Enumerate critical components & flows. For each STRIDE category, phrase a concrete potential issue. (Blank cells = not materially applicable in this context.)
| Component / Interaction | Spoofing (S) | Tampering (T) | Repudiation (R) | Info Disclosure (I) | DoS (D) | Elev. Priv. (E) |
|---|---|---|---|---|---|---|
Instance Initialization (new Two(options)) |
Faked domElement reference causing unexpected attachment |
Malformed options override internal defaults (e.g., negative dimensions) | Caller denies altering options later | Logging of internal fallback decisions | Huge width/height triggers large buffer allocations | (N/A) |
Shape Factories (makePolygon, makePath, etc.) |
(N/A) | Excessive / malformed vertices mutate global arrays or cause numerical instability | Caller denies adding shape causing downstream state | Error text reveals internal vertex processing | Extremely large vertex counts or recursion in curves consume CPU | (N/A) |
Scene Graph Mutation (add/remove, property sets) |
(N/A) | Reassigning references causing unintended shared state; injection of rogue objects posing as shapes | Lack of audit trail for who mutated state in multi-plugin context | Exposed internal structure via serialization / debug output | Tight mutation loop each frame causing GC churn | (N/A) |
Update Loop (two.update()) |
(N/A) | Insertion of user-supplied callback altering traversal order | Caller denies having hooked an update callback that caused issue | Stack traces leak path to internal modules | Infinite loop / heavy per-frame computation blocks UI | (N/A) |
| Renderer Selection (SVG/Canvas/WebGL) | Spoofed type constant triggers unexpected code path | Overwriting renderer instance state pre-draw | (N/A) | WebGL error logs reveal driver / extension info | Forcing WebGL fallback thrash leads to performance collapse | Potential abuse of WebGL context to access extended capabilities (low) |
| SVG Output (DOM nodes) | (N/A) | Mutation of generated SVG DOM externally breaks invariants | (N/A) | Reading raw SVG reveals scene graph structure | Very deep/grouped SVG leads to layout thrash | (N/A) |
| WebGL Path (buffers, shaders) | (N/A) | Crafted data feeds large buffer uploads repeatedly | (N/A) | Timing of shader compile errors leaks complexity | Large textures / buffers exhaust GPU memory | (Browser sandbox restricts elevated privileges) |
| External Resource Loading (images/SVG import) | Spoofed resource URL masquerading as trusted path (caller-supplied) | Malicious SVG path data alters internal anchor arrays | Caller denies having loaded large asset | Metadata (dimensions) exposes assumptions; error reveals loader internals | Oversized images / recursive SVG cause memory/CPU spike | (N/A) |
| Dependency / Build Tooling | Typosquatting package name introduced | Modified build script injects code into bundle | Maintainer denies publishing compromised version | Build logs expose environment variables | Build system loop blocked by malicious test script | Compromise of CI runner leads to full pipeline control |
| Distribution (npm / CDN) | Spoofed package using similar name | Repacked two.min.js with hidden payload |
(N/A) | Minified source map leaks path structure if published inadvertently | Flood of fake versions confuses consumers | (If signing introduced, stolen key misuse) |
| Error Handling / Logging | (N/A) | User overrides console to alter perceived errors |
Caller denies seeing a thrown error | Detailed error surfaces internal code lines | Excess logging volume degrades performance | (N/A) |
Extensions / Plugins (extras/*) |
Malicious plugin claims to be official | Plugin mutates core prototypes | (N/A) | Plugin leaks scene data externally | Heavy plugin loop stalls frame | Plugin executes privileged APIs if environment misconfigured |
Observations:
- Repudiation mostly low concern in single-tenant, in-process library usage but noted where debugging disputes may arise.
- Elevation of Privilege is constrained by browser sandbox; primary risk is expanded surface via WebGL extensions or plugin misuse rather than privilege escalation in classic sense.
- DoS vectors dominate (unbounded geometry, large resources, per-frame heavy callbacks).
| Threat (Summary) | Proposed Mitigation / Control |
|---|---|
| Oversized canvas / dimensions (Init) -> Memory strain | Enforce sane max width/height defaults; clamp dimensions; document limits. |
| Malformed options overriding internal defaults | Validate option types; fallback silently with minimal info leakage; unit tests for boundary values. |
| Excessive vertices in shape factories | Add soft cap (warn) and hard cap (truncate or reject) on vertex count; iterative subdivision control. |
Rogue object passed to add() (shape impersonation) |
Duck-type minimal shape interface & freeze trusted prototypes; optionally symbol-tag internal classes. |
| Shared mutable references (clone pitfalls) | Deep clone vertices/anchors; document immutability expectations in cloning API; add tests. |
| User callback tampering traversal order | Isolate internal traversal from external callbacks; run user callbacks after internal state commit. |
Infinite loop in user update handler |
Provide watchdog (frame time threshold warning); recommend dev tooling snippet; doc guidance. |
| Renderer type spoof -> unexpected code path | Enum-check renderer types; fallback to safe default; log single concise warning. |
| SVG DOM mutated externally causing corruption | On each update, recompute critical attributes rather than trusting existing DOM; avoid relying on outer structure. |
| Large buffer / texture uploads (WebGL) | Enforce maximum texture size (query context caps); early reject oversize images; reuse buffers. |
| Malicious / huge SVG import (path explosion) | Pre-parse to count segments; reject if above threshold; limit recursion depth; timebox parsing. |
| Image resource exhaustion | Inspect naturalWidth/Height; reject > configured maximum; allow user override via option. |
| Typosquatted dependency injected during build | Pin exact versions; use lockfile; periodically audit with npm audit & manual diff review. |
| Build script tampering | Require code review / signed commit for release branch; keep build script minimal & monitored (checksum). |
| Distribution artifact alteration pre-publish | Generate SHA256 checksums; optionally provide Subresource Integrity (SRI) hashes in README. |
| Stolen publish token | Rotate tokens regularly; move to scoped automation token with least privilege; enable 2FA. |
| Error messages leak internals | Centralize error formatting; strip stack traces in production build; map internal names -> generic labels. |
| WebGL driver / extension info leakage | Avoid logging full extension lists; only expose minimal capability flags. |
Plugin prototype pollution (extras/*) |
Avoid modifying native / core prototypes; isolate plugin namespaces; add lint rule forbidding prototype writes. |
| High CPU from deep scene graph | Offer maxDepth traversal guard (warn); encourage flattening via docs; provide profiling helper. |
| Large SVG group causing layout thrash | Batch DOM writes (fragment or requestAnimationFrame batching); minimize attribute churn. |
| Unbounded logging DoS | Throttle repetitive warnings; single aggregated warning for repeated invalid inputs. |
| CI compromise leads to malicious release | Principle of least privilege CI; artifact build in ephemeral environment; verify checksum post-build before publish. |
| Source map leaking internal structure | Exclude source maps from production CDN by default; offer opt-in debug build. |
| GPU memory exhaustion via repeated shape->buffer churn | Implement geometry caching & eviction policy; reuse buffers where possible. |
| Timing side channel (low risk) | Avoid conditional branching on secret (none present); keep operations uniform per vertex batch. |
| Lack of audit for published bundle | Automated diff (size & AST-level) from previous release; fail build on unexpected large diffs. |
| Maintainer key single point of failure | Store recovery codes offline; document emergency revocation workflow; consider secondary maintainer onboarding plan. |
Future / Optional Hardening (Not Yet Implemented):
- WASM Offloading Guard: If adding WASM modules, sandbox inputs & impose execution time budget.
- Freeze Core Prototypes in Production:
Object.freeze(Two.Shape.prototype)etc., after initialization for tamper resistance. - Integrity Metadata: Publish a signed manifest listing artifact hashes.
Risk Levels (Qualitative Matrix):
- High: Readily triggerable by typical consumer misuse or modest attacker effort AND materially degrades consumer app (crash, unresponsive UI) or compromises supply chain trust.
- Medium: Requires atypical usage pattern OR yields moderate performance / reliability impact.
- Low: Improbable, low impact, or already substantially mitigated by platform sandbox.
| Threat (Condensed) | Likelihood | Impact | Risk | Rationale |
|---|---|---|---|---|
| Unbounded vertex count | High | High | High | Easy to supply huge arrays, leads to CPU & memory pressure. |
| Oversized images / SVG import | High | High | High | Common asset pipeline; large dimensions exhaust memory / layout. |
| Infinite loop in user update callback | Medium | High | High | Not default path but frequent custom animation code pattern. |
| Supply chain: compromised release token | Medium | High | High | Single maintainer; credential theft yields malicious distribution. |
| Build script tampering | Medium | High | High | Script executes during release; moderate barrier. |
| Rogue object injection into scene | Medium | Medium | Medium | Needs shape interface mimic; can corrupt traversal. |
| Renderer type spoof / fallback churn | Medium | Medium | Medium | Causes performance degradation; moderate ease. |
| SVG DOM external mutation | Medium | Medium | Medium | Downstream code often manipulates DOM; may desync state. |
| Large buffer / texture churn (WebGL) | Medium | Medium | Medium | Requires WebGL mode; performance/resource impact. |
| Excessive logging DoS | Medium | Low | Medium | Flooded console harms performance; trivial to trigger. |
| Typosquatted dependency | Low | High | Medium | Impact severe if occurs; likelihood reduced with pinning. |
| Error leaks internal structure | Medium | Low | Medium | Minor info disclosure; debugging paths. |
| GPU memory exhaustion | Low | High | Medium | Needs intentional heavy data; some users could inadvertently hit. |
| Timing side channel | Low | Low | Low | No secrets processed; minimal leverage. |
| Prototype pollution via plugin | Low | Medium | Low | Extras not always loaded; requires malicious plugin adoption. |
| Source map unintended exposure | Low | Low | Low | Sensitive only for structural hints; low exploit value. |
| WebGL driver info leak | Low | Low | Low | Browser already restricts; adds little attacker advantage. |
| Maintainer key single point of failure | Low | High | Medium | Catastrophic if lost; less likely with good hygiene. |
Priorities (Action Order): 1) Geometry / resource bounding, 2) Release credential hardening, 3) Build script minimalism & auditing, 4) Scene graph integrity guards, 5) Logging throttling & error hygiene, 6) Optional hardening (prototype freeze, signed manifest).
Immediate Action Plan (0–2 Weeks):
- Implement geometry / vertex / image bounding (hard + soft caps) with warnings & docs.
- Add centralized input validation utilities for factory methods; integrate in all
make*APIs. - Harden release pipeline: rotate publish token, enable 2FA enforcement, document checksum generation + publish hashes.
- Add logging throttle & single-warning aggregator for repetitive invalid inputs.
- Create automated size & diff check in build (fail on anomalous delta > threshold %).
Short Term (1–2 Months):
- Prototype scene graph traversal guard (max depth detection + warning).
- Introduce optional production hardening flag to freeze core prototypes post-init.
- Add fuzz tests (lightweight) for path / polygon creation with random vertex sets.
- Provide resource limit configuration surface (
two.config.limits).
Medium Term (Quarterly):
- Evaluate adding signed manifest + SRI hash publishing script.
- Assess feasibility of adding secondary maintainer or emergency recovery process.
- Benchmark performance baseline & document guidance for large scenes.
Ongoing Maintenance Cadence:
- Threat Model Review: Every major / minor release or quarterly (whichever sooner).
- Dependency & Tooling Audit: Monthly (
npm audit, manual diff of lockfile, check typosquats). - Release Checklist: Validate checksums, run diff size gate, ensure validation tests pass, rotate token annually.
- Incident Response Drill: Simulate compromised artifact scenario annually—validate revocation & notification workflow.
Monitoring / Telemetry (Non-invasive):
- Provide optional hook to collect (opt-in) anonymized stats: scene object counts, average vertex counts—inform future caps (ensure no PII / URLs).
Documentation Enhancements:
- Add SECURITY.md guidance on reporting issues & current hardening status.
- Update README with performance & limit recommendations once caps implemented.
Exit Criteria for This Iteration:
- All High-risk items have at least one implemented mitigation or a clearly documented deferral rationale.
- Build pipeline emits artifact hashes reproducibly.
Review Trigger Conditions:
- Introduction of new renderer backend.
- Addition of parsing-heavy feature (e.g., full SVG importer expansion, WASM module).
- Significant architectural refactor (scene graph storage, batching system).
Deferral Rationale Log (Track in Repo Issue):
- Signed release manifest (cost: setup complexity) – deferred until stable hash automation baseline exists.
- Prototype freeze default (developer ergonomics concern) – gated behind prod flag initially.
Tips for Your First Threat Model:
- Start Small: Don't try to model your entire project at once. Pick a critical feature or a new component.
- Keep it Simple: A simple sketch is better than no diagram. A few identified threats are better than none.
- Collaborate: Talk to your co-maintainers or a trusted peer. Different perspectives reveal different threats.
- Don't Aim for Perfection: You won't catch everything the first time. The process itself builds your security mindset.
Remember: Your library is a trust anchor for many projects. By dedicating time to threat modeling, you enhance not just your own project's security, but the security of the entire open-source ecosystem.