Skip to content

fix(security): prevent XSS, reverse tabnabbing, and zip bomb attacks - #52

Merged
alexanderop merged 9 commits into
mainfrom
fix/javascript-injection-security
Nov 22, 2025
Merged

alexanderop merged 9 commits into
mainfrom
fix/javascript-injection-security

Conversation

@alexanderop

@alexanderop alexanderop commented Nov 22, 2025 •

Copy link
Copy Markdown
Owner

Summary

This PR implements comprehensive security hardening for MarkVim, addressing three critical attack vectors: XSS injection in markdown rendering, reverse tabnabbing via external links, and zip bomb attacks in document sharing.

Changes

  • XSS Prevention: Add DOMPurify sanitization to markdown preview and error fallback paths

    • Strips dangerous HTML tags and JavaScript event handlers
    • Prevents script tag injection and JavaScript protocol links
    • Maintains markdown-it's built-in security for markdown links
  • Reverse Tabnabbing Protection: Add rel="noopener noreferrer" to external links

    • Prevents malicious pages from accessing parent window reference
    • Applied via DOMPurify hook for all sanitized external links
  • Zip Bomb Protection: Implement streaming decompression with size limits

    • Replace gunzipSync with streaming Gunzip for memory-safe decompression
    • Enforce 2MB maximum decompressed size limit
    • Prevent zip bomb attacks in document sharing functionality
  • Testing: Add comprehensive security tests

    • XSS attack vectors (script tags, event handlers, javascript: links)
    • Link rel attribute validation
    • Zip bomb prevention and streaming decompression

Test Plan

  • XSS security tests for markdown rendering (script tags, event handlers, javascript: URLs)
  • Reverse tabnabbing tests for external link sanitization
  • Zip bomb protection tests with streaming decompression
  • All existing tests pass
  • Manual verification of shared documents with size limits

Summary by CodeRabbit

  • Security & Stability

    • Enhanced Markdown sanitization and automatic safety attributes on external links to prevent XSS and tabnabbing.
    • Enforced decompression size limits for shared documents to guard against resource-exhaustion attacks.
  • Tests

    • Added unit tests covering Markdown sanitization/edge cases and secure share URL parsing with size limits.
  • Documentation

    • Added guides for branch creation, PR workflows, command scaffolding, and testing Vue composables (helpers and examples).
  • Chores

    • Added DOMPurify dependency for sanitization.

✏️ Tip: You can customize this high-level summary in your review settings.

Add DOMPurify sanitization to strip dangerous HTML/JavaScript from rendered
markdown. This prevents multiple XSS attack vectors:
- Script tag injection: <script>alert()</script>
- Event handler injection: onerror, onclick, onload, etc.
- JavaScript protocol links: javascript: URLs in raw HTML
- Markdown links refuse javascript: by default (markdown-it security)

Add comprehensive security tests covering all attack vectors.
Both main render path and error fallback path are now sanitized.

Fixes: XSS vulnerability in HTML rendering (Severity: High)
- Add rel="noopener noreferrer" to external links via DOMPurify hook to prevent reverse tabnabbing attacks
- Implement streaming decompression with size limits (2MB max) to prevent zip bomb attacks in document sharing
- Add comprehensive security tests for Mermaid XSS vectors and link rel attributes
- Replace gunzipSync with streaming Gunzip for memory-safe decompression
@vercel

vercel Bot commented Nov 22, 2025 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
mark-vim Ready Ready Preview Comment Nov 22, 2025 11:29am

@coderabbitai

coderabbitai Bot commented Nov 22, 2025 •

Copy link
Copy Markdown

Warning

Rate limit exceeded

@alexanderop has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 9 minutes and 54 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between de52b7d and e56a3fe.

📒 Files selected for processing (4)
  • .claude/commands/pr.md (1 hunks)
  • .claude/skills/vue-test-composable/references/examples.md (1 hunks)
  • .claude/skills/vue-test-composable/references/helpers.md (1 hunks)
  • memory/testingStrategy.md (1 hunks)

Walkthrough

Adds documentation for Claude commands and Vue composable testing; integrates DOMPurify sanitization into markdown rendering (including noopener handling); implements streaming gzip decompression with a 2MB decompressed-size limit for shared URLs; and adds tests validating sanitization and size-limited import behavior.

Changes

Cohort / File(s) Summary
Claude command & scaffolding docs
.claude/commands/branch.md, .claude/commands/create-command.md, .claude/commands/pr.md
New command templates and guides for creating feature branches, scaffolding slash commands, and creating PRs; include frontmatter templates, contextual blocks, and step-by-step workflows.
Vue composable testing skill & refs
.claude/skills/vue-test-composable/skill.md, .claude/skills/vue-test-composable/references/helpers.md, .claude/skills/vue-test-composable/references/examples.md
New skill guide and reference docs describing testing strategies for independent, lifecycle-dependent, and injection-dependent composables, plus helper patterns (withSetup, useInjectedSetup) and example test snippets.
Markdown preview sanitization
src/modules/markdown-preview/composables/useMarkdown.ts, src/modules/markdown-preview/composables/useMarkdown.test.ts
Integrates DOMPurify sanitization into primary and fallback render paths, adds after-sanitize hook to append rel="noopener noreferrer" to external links, and adds tests for XSS, unsafe attributes, javascript: links, and mermaid safety.
Document share decompression safety
src/modules/share/composables/useDocumentShare.ts, src/modules/share/composables/useDocumentShare.test.ts
Adds streaming gzip decompression (safeGunzip) with MAX_DECOMPRESSED_SIZE (2MB) and guarded parse flow to reject oversized payloads; tests verify rejection of large shares and acceptance of small ones.
Dependency update
package.json
Adds dependency isomorphic-dompurify ^2.32.0 for cross-platform HTML sanitization.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Renderer as Markdown Renderer
    participant DOMPurify as DOMPurify
    participant Output as App Output

    User->>Renderer: Submit raw Markdown
    Renderer->>Renderer: Convert Markdown -> HTML
    Renderer->>DOMPurify: Sanitize HTML (after-sanitize hook)
    Note over DOMPurify: Strip scripts/unsafe attrs<br/>Add rel="noopener noreferrer" for external links
    DOMPurify->>Renderer: Return cleaned HTML
    Renderer->>Output: Render sanitized HTML
Loading
sequenceDiagram
    participant Browser
    participant Parser as Share URL Parser
    participant Gunzip as safeGunzip
    participant Validator as Size Validator
    participant App as Application

    Browser->>Parser: `#share`=<base64+gzip>
    Parser->>Gunzip: Stream and decompress
    Gunzip->>Validator: Emit chunks / track size
    alt Decompressed size > 2MB
        Validator->>App: Error — import rejected (too large)
    else Size within limit
        Validator->>App: Return decompressed JSON -> parse & import
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Review focus:
    • src/modules/share/composables/useDocumentShare.ts — verify streaming decompression, size-tracking, error paths, and resource cleanup.
    • src/modules/markdown-preview/composables/useMarkdown.ts — confirm DOMPurify hook logic, correct detection of external links, and consistent sanitization in fallback path.
    • Tests — ensure test fixtures, mocking of window/location, and size-encoding/decoding logic are correct and deterministic.
    • package.json — verify dependency addition and any build/test impacts.

Poem

🐰 In burrows of code I nibbled the grime,
Purified links, kept the scripts out of time.
I unwrapped each gzip with cautious delight,
Two megabytes guard the tunnel at night.
Hooray for safe tests and branches—hop bright!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the three main security improvements introduced in the changeset: XSS prevention, reverse tabnabbing protection, and zip bomb attack mitigation.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
src/modules/markdown-preview/composables/useMarkdown.ts (2)

2-27: DOMPurify hook for reverse tabnabbing is correct but could be slightly more robust

The afterSanitizeAttributes hook that adds rel="noopener noreferrer" to external http/https links is sound and idempotent (it merges with existing rel values and avoids duplicates). Two small robustness nits you might consider:

  • Normalize tagName when checking anchors, e.g. node.tagName.toUpperCase() === 'A', to be resilient across environments.
  • Optionally normalize href when checking protocol (href.toLowerCase().startsWith('http://') / https://) in case user-supplied HTML uses uppercased schemes.

Not required for correctness in typical browser/jsdom cases, but would harden the hook against odd markup.

If you’d like, please double-check the afterSanitizeAttributes hook semantics in the current isomorphic-dompurify/DOMPurify docs to confirm this usage matches recommended patterns.


40-58: Sanitization is correctly applied on both primary and fallback render paths

The main path now sanitizes rendered HTML before adding test IDs, and the fallback path sanitizes basic markdown-it output as well. This closes the XSS gap even when the primary renderer fails.

One minor consistency idea: if you ever rely on GitHub alert data-testid attributes in fallback scenarios, you might also run addDataTestIdToAlerts on the fallback HTML; currently only the primary path does so.

If you update fallback behavior, please ensure it still aligns with how createMarkdownRenderer formats GitHub alerts so tests remain consistent.

src/modules/share/composables/useDocumentShare.ts (1)

11-79: safeGunzip correctly enforces a decompressed size cap, with minor internal cleanups possible

The new safeGunzip helper cleanly enforces a ~2 MiB decompressed size limit and avoids accumulating unbounded data in memory, which is a solid mitigation for zip‑bomb style inputs. A couple of small points you might consider:

  1. Constructor callback redundancy

    You both pass a callback to new Gunzip((chunk) => { ... }) and then immediately assign gunzip.ondata = (chunk) => { ... }. Per fflate’s typings, the constructor callback is equivalent to setting ondata, so the initial callback becomes unused once you overwrite it. You can simplify to:

    const gunzip = new Gunzip()
    gunzip.ondata = (chunk) => {
      if (aborted) return
      totalSize += chunk.length
      if (totalSize > MAX_DECOMPRESSED_SIZE) {
        aborted = true
        return
      }
      chunks.push(chunk)
    }
  2. Stronger early‑abort semantics (optional)

    Currently, when totalSize exceeds the limit, you set aborted = true and stop accumulating chunks, but gunzip.push will still continue processing the remaining compressed data. If you want to truly stop decompression work once the limit is exceeded, you could instead throw from inside ondata when the limit is hit and rely on the surrounding tryCatch to capture that as an error. That would cap both memory use and decompression CPU for extreme zip‑bomb inputs.

The existing implementation is functionally correct and already prevents unbounded decompressed storage; these changes would just tighten and simplify the implementation.

Please double-check against the current fflate streaming docs that throwing inside Gunzip.ondata is the recommended way to abort decompression early before adopting that pattern.

src/modules/share/composables/useDocumentShare.test.ts (1)

20-57: Test logic is correct and properly validates zip bomb protection.

The test correctly creates an oversized document (2.1MB > 2MB limit), compresses it, and verifies rejection with an appropriate error message. The use of dynamic imports ensures proper test isolation.

Consider adding boundary tests for the exact 2MB limit.

To more thoroughly validate the size enforcement, consider adding tests for:

  • Exactly 2,097,152 bytes (2MB)
  • 2,097,151 bytes (1 byte under)
  • 2,097,153 bytes (1 byte over)

This would help catch off-by-one errors in the size limit check.

Example:

it('should reject documents at exactly 2MB', async () => {
  const EXACTLY_TWO_MB = 2 * 1024 * 1024 // 2,097,152 bytes
  const doc = {
    id: 'test',
    title: 'Test',
    content: 'a'.repeat(EXACTLY_TWO_MB - 100), // Account for JSON overhead
    createdAt: Date.now(),
    sharedAt: Date.now(),
  }
  // ... rest of test
})
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 83092b3 and cf8703e.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (9)
  • .claude/commands/branch.md (1 hunks)
  • .claude/skills/vue-test-composable/references/examples.md (1 hunks)
  • .claude/skills/vue-test-composable/references/helpers.md (1 hunks)
  • .claude/skills/vue-test-composable/skill.md (1 hunks)
  • package.json (1 hunks)
  • src/modules/markdown-preview/composables/useMarkdown.test.ts (1 hunks)
  • src/modules/markdown-preview/composables/useMarkdown.ts (3 hunks)
  • src/modules/share/composables/useDocumentShare.test.ts (1 hunks)
  • src/modules/share/composables/useDocumentShare.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
src/modules/share/composables/useDocumentShare.ts (1)
src/shared/utils/result.ts (4)
  • Result (6-8)
  • tryCatch (66-75)
  • Err (20-22)
  • Ok (13-15)
src/modules/markdown-preview/composables/useMarkdown.test.ts (1)
src/modules/markdown-preview/composables/useMarkdown.ts (1)
  • useMarkdown (29-70)
src/modules/markdown-preview/composables/useMarkdown.ts (1)
src/modules/markdown-preview/utils/markdown.ts (1)
  • addDataTestIdToAlerts (50-56)
src/modules/share/composables/useDocumentShare.test.ts (1)
src/modules/share/composables/useDocumentShare.ts (1)
  • useDocumentShare (97-356)
🪛 LanguageTool
.claude/commands/branch.md

[style] ~20-~20: Using many exclamation marks might seem excessive (in this case: 3 exclamation marks for a text that’s 812 characters long)
Context: ...t </current_branch> <recent_branches> !git branch --sort=-committerdate | head...

(EN_EXCESSIVE_EXCLAMATION)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: E2E Tests (sharing)
  • GitHub Check: E2E Tests (color)
  • GitHub Check: E2E Tests (editing)
  • GitHub Check: E2E Tests (accessibility)
  • GitHub Check: E2E Tests (theme)
  • GitHub Check: E2E Tests (document-persistence)
  • GitHub Check: E2E Tests (documents)
  • GitHub Check: E2E Tests (smoke.test)
🔇 Additional comments (7)
.claude/commands/branch.md (1)

1-57: Branch command doc looks clear and consistent

Flow, git commands, and branch naming rules are coherent; no issues from a tooling or usability perspective.

.claude/skills/vue-test-composable/skill.md (1)

1-38: Vue composable testing guide reads solid

The three-path breakdown (independent / lifecycle / injection) and references to helpers/examples are clear and actionable; no issues spotted.

src/modules/markdown-preview/composables/useMarkdown.test.ts (1)

1-142: Security test coverage for markdown rendering looks strong

The suite exercises reverse tabnabbing, script/event-handler stripping, javascript: URLs, and Mermaid-specific XSS vectors, while still validating safe HTML passthrough. This provides good regression coverage for the new sanitization behavior.

src/modules/share/composables/useDocumentShare.ts (1)

207-263: parseShareUrl integration with safeGunzip and error messaging looks correct

The share URL parsing flow now:

  • Validates #share= prefix and presence of encoded data.
  • Base64‑decodes into a Uint8Array.
  • Uses safeGunzip and surfaces either its error message (including “Document is too large to process safely.”) or proceeds to JSON decode + schema validation.

This keeps the existing user‑facing error semantics while adding the decompressed‑size guard, and cleanly reuses the common Result/tryCatch pattern already used elsewhere in the module.

If you later tweak MAX_DECOMPRESSED_SIZE, it’s worth re‑running the new zip‑bomb tests to ensure the thresholds and messages still line up with expectations.

package.json (1)

50-79: DOM sanitization dependency safe; verify transitive DOMPurify version

isomorphic-dompurify@^2.32.0 itself has no known security advisories. However, multiple security advisories exist for the upstream DOMPurify project, including nesting-based mXSS (CVE-2024-47875), prototype-pollution tampering (CVE-2024-48910), and depth-check bypass (CVE-2024-45801).

Check which DOMPurify version is actually installed when you depend on isomorphic-dompurify@^2.32.0 (inspect node_modules or your lockfile) and ensure it resolves to patched releases (DOMPurify 2.5.4 / 3.1.3 for the mXSS and depth issues, and 2.4.2 for the prototype-pollution issue).

src/modules/share/composables/useDocumentShare.test.ts (2)

1-18: LGTM! Excellent test isolation setup.

The test suite properly isolates state with vi.resetModules() and restores window.location in cleanup. This ensures each test gets a fresh module instance with its own mocked environment.


59-93: LGTM! Test correctly validates the success path.

The test properly verifies that small, valid documents are accepted and parsed correctly. The assertions check all critical aspects: non-null result, no error, and correct content extraction.

Together with the first test, these provide good coverage of the security requirement to reject oversized documents while accepting valid ones.

Comment thread .claude/skills/vue-test-composable/references/examples.md Outdated
Comment thread .claude/skills/vue-test-composable/references/helpers.md Outdated
The constructor callback was immediately overwritten by the ondata
assignment, making it dead code. Simplified to only use ondata.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
.claude/commands/pr.md (1)

84-90: Add language specifier to the YAML frontmatter code block.

Line 86 lacks a language identifier on the opening fence. Since this block documents YAML frontmatter, specify yaml:

-```
+```yaml
 ---
 description: <imperative verb phrase, e.g., "Run tests and fix failures">
 argument-hint: <show expected args, use [] for optional, <> for required>
 allowed-tools: <see category defaults below>
 model: <haiku for routine operations, omit for complex reasoning>
 ---
-```
+```yaml
.claude/commands/create-command.md (3)

188-192: Add language specifier to the markdown example code block.

Line 191 lacks a language identifier. Since this block contains markdown template content, specify markdown:

 **Linear (sequential steps):**
-```markdown
+```markdown
 # <Title>
 
 ## Context

285-289: Add language specifier to the markdown example code block.

Line 288 lacks a language identifier. Since this block contains markdown template content, specify markdown:

 **Complex (full protocol):**
-```markdown
+```markdown
 # <Title>
 
 ## Context

383-387: Add language specifier to the markdown example code block.

Line 386 lacks a language identifier for the example output block. Specify markdown:

 For `/create-command deploy "Deploy to staging or production environment"`:
 
-```markdown
+```markdown
 ---
 description: Deploy to staging or production environment
 argument-hint: <environment> [version]
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cf8703e and de52b7d.

📒 Files selected for processing (3)
  • .claude/commands/create-command.md (1 hunks)
  • .claude/commands/pr.md (1 hunks)
  • src/modules/share/composables/useDocumentShare.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-22T10:05:40.365Z
Learnt from: alexanderop
Repo: alexanderop/MarkVim PR: 51
File: .claude/commands/lint.md:12-12
Timestamp: 2025-11-22T10:05:40.365Z
Learning: In `.claude/commands/` files (Claude Code slash command templates), the syntax `!\`command\`` is intentional and valid. The `!` prefix executes the shell command and interpolates its output into the template. This is documented Claude Code behavior and should not be flagged as malformed syntax.

Applied to files:

  • .claude/commands/create-command.md
🧬 Code graph analysis (1)
src/modules/share/composables/useDocumentShare.ts (1)
src/shared/utils/result.ts (4)
  • Result (6-8)
  • tryCatch (66-75)
  • Err (20-22)
  • Ok (13-15)
🪛 LanguageTool
.claude/commands/pr.md

[style] ~35-~35: Using many exclamation marks might seem excessive (in this case: 7 exclamation marks for a text that’s 1807 characters long)
Context: ...e-only </files_changed> <existing_pr> !gh pr view --json number,title,state,ur...

(EN_EXCESSIVE_EXCLAMATION)

.claude/commands/create-command.md

[uncategorized] ~25-~25: The official name of this software platform is spelled with a capital “H”.
Context: ... merge) | model: haiku, git tools | | github | GitHub operations (PR, issues, CI) |...

(GITHUB)


[uncategorized] ~79-~79: The official name of this software platform is spelled with a capital “H”.
Context: ...sh(git branch), Bash(git checkout)| |github|Bash(gh *), Bash(git *)| |quali...

(GITHUB)


[uncategorized] ~86-~86: The official name of this software platform is spelled with a capital “H”.
Context: ...- Use model: haiku for: git-workflow, github, quality (simple), utility (simple) - O...

(GITHUB)

🪛 markdownlint-cli2 (0.18.1)
.claude/commands/pr.md

86-86: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

.claude/commands/create-command.md

191-191: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


288-288: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


386-386: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: E2E Tests (color)
  • GitHub Check: E2E Tests (sharing)
  • GitHub Check: E2E Tests (document-persistence)
  • GitHub Check: E2E Tests (theme)
  • GitHub Check: E2E Tests (documents)
  • GitHub Check: E2E Tests (accessibility)
  • GitHub Check: E2E Tests (smoke.test)
  • GitHub Check: E2E Tests (editing)
🔇 Additional comments (6)
.claude/commands/pr.md (1)

1-121: Documentation file is scaffolding infrastructure, not security-related code.

This file documents PR creation workflows and is part of the project's tooling documentation. It doesn't implement or validate the security hardening changes (XSS prevention, reverse tabnabbing, zip bomb protection) mentioned in the PR objectives. The documentation itself is well-structured and clear.

.claude/commands/create-command.md (1)

1-386: Documentation file is scaffolding infrastructure, not security-related code.

This file provides templates and patterns for creating new Claude slash commands and is part of the project's tooling infrastructure. It does not implement or validate the security hardening changes (XSS prevention via DOMPurify, reverse tabnabbing protection, or zip bomb defense) mentioned in the PR objectives.

src/modules/share/composables/useDocumentShare.ts (4)

3-3: LGTM! Type and module imports are correct.

The Result type import and Gunzip addition to fflate imports properly support the new streaming decompression implementation.

Also applies to: 6-6


11-13: LGTM! Security constants are well-defined.

The 2MB decompression limit provides effective zip bomb protection, and using 1024 bytes per KB for binary calculations is correct. The distinction from BYTES_TO_KB_DIVISOR (1000, line 105) for user-facing display is intentional and appropriate.


217-224: LGTM! Clean integration of safe decompression.

The Result-based error handling is properly integrated, with appropriate error messaging and early return on failure. The replacement of direct gunzipSync with streaming safeGunzip successfully implements zip bomb protection at the entry point.


20-67: The code is correctly implemented for the synchronous Gunzip API.

fflate's synchronous Gunzip throws errors directly from push(), so the existing tryCatch wrapper around gunzip.push() is the correct approach for error handling. The suggested onerror handler is unnecessary for this synchronous API variant.

The streaming decompression with incremental size checking and Result-based error handling is well-structured and effectively prevents zip bomb attacks.

- Close code block properly before Lifecycle Dependent Example section
- Add proper ## headings for Lifecycle and Injection Dependent examples
- Add ## heading for useInjectedSetup section in helpers.md
- Fix inline code formatting for inject() reference
@alexanderop
alexanderop merged commit 1e71ba3 into main Nov 22, 2025
15 checks passed
@alexanderop
alexanderop deleted the fix/javascript-injection-security branch November 22, 2025 11:29

This branch was successfully deployed

1 active deployment
Preview — e56a3fe3 Deployed Nov 22, 2025 by vercel[bot]
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.

1 participant