Skip to content

Block file:// navigation to executables in preview pane#356

Merged
schuyler merged 2 commits into
mainfrom
claude/fix-cve-webview-navigation-351
Apr 8, 2026
Merged

Block file:// navigation to executables in preview pane#356
schuyler merged 2 commits into
mainfrom
claude/fix-cve-webview-navigation-351

Conversation

@schuyler

@schuyler schuyler commented Apr 8, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds MPURLSecurityPolicy utility class with UTI-based detection of executables/bundles and symlink-aware scope checking
  • Guards decidePolicyForNavigationAction: to block JS-triggered file:// navigations outside document scope or to executables
  • Guards openOrCreateFileForUrl: to block user-clicked links to executables with a warning alert
  • 16 unit tests covering app bundles, plugin bundles, executable scripts, symlink escape, path traversal, nil/edge cases

Related Issue

Related to #351

Manual Testing Plan

  1. Open a Markdown file containing <a href="file:///Applications/Calculator.app">click</a> — clicking should show a "Blocked: Link target is an executable" alert
  2. Open a Markdown file with <script>window.location='file:///Applications/Calculator.app'</script> — no app should launch, blocked silently with NSLog
  3. Normal file:// links to .md files in the same directory should continue to work
  4. External https:// links should continue to open in the default browser

Design Notes

  • Executable detection uses NSWorkspace typeOfFile: + UTTypeConformsTo() against six dangerous UTI types, with POSIX executable bit fallback for scripts without registered UTIs
  • Scope check resolves symlinks on the parent directory separately from the target, because realpath() (used by URLByResolvingSymlinksInPath) fails when the final component doesn't exist
  • User-clicked file:// links intentionally skip the scope check — only executables are blocked. Opening local documents from Markdown links is a legitimate use case.

schuyler added 2 commits April 7, 2026 20:16
Addresses CVE-2019-12138 (directory traversal to arbitrary program execution)
and CVE-2019-12173 (RCE via file:// URI to .app bundles, auto-clicked via
JavaScript).

Adds MPURLSecurityPolicy utility class with two guards:
- isExecutableOrAppBundleAtURL: detects executables/bundles via UTI
  conformance (CoreServices) with POSIX executable bit fallback
- url:isWithinScopeOfBaseURL: checks if a target URL is within the
  document's directory tree after resolving symlinks and path traversal

Guards applied at two call sites in MPDocument.m:
- decidePolicyForNavigationAction: blocks JS-triggered file:// navigations
  outside document scope or to executables
- openOrCreateFileForUrl: blocks user-clicked links to executables with
  a warning alert

Related to #351
URLByResolvingSymlinksInPath uses realpath() internally, which fails when
the final path component doesn't exist — leaving intermediate symlinks
unresolved. Resolve the parent directory separately so that symlink escapes
like docs/evil-link/payload (where evil-link points outside scope) are
caught even when payload doesn't exist on disk.

Related to #351
@github-actions

github-actions Bot commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Report

Current Coverage: 57.52%

Coverage Details (Summary)
Name                                                                                                                                   Coverage            
-------------------------------------------------------------------------------------------------------------------------------------- ------------------- 
MASPreferences.bundle                                                                                                                  0.00% (0/0)         
MacDown 3000.app                                                                                                                       59.91% (8285/13828) 
    /Users/runner/work/macdown3000/macdown3000/MacDown/Code/Utility/MPMathJaxListener.m                                                61.54% (16/26)      
        -[MPMathJaxListener callbacks]                                                                                                 100.00% (5/5)       
        -[MPMathJaxListener addCallback:forKey:]                                                                                       100.00% (3/3)       
        -[MPMathJaxListener invokeCallbackForKey:]                                                                                     100.00% (8/8)       
        +[MPMathJaxListener isSelectorExcludedFromWebScript:]                                                                          0.00% (0/5)         
        +[MPMathJaxListener isKeyExcludedFromWebScript:]                                                                               0.00% (0/5)         
    /Users/runner/work/macdown3000/macdown3000/Dependency/peg-markdown-highlight/HGMarkdownHighlighter.m                               85.51% (478/559)    
        styleparsing_error_callback                                                                                                    90.00% (9/10)       
        -[HGMarkdownHighlighter init]                                                                                                  92.86% (13/14)      
        -[HGMarkdownHighlighter initWithTextView:]                                                                                     83.33% (5/6)        
        -[HGMarkdownHighlighter initWithTextView:waitInterval:]                                                                        83.33% (5/6)        
        -[HGMarkdownHighlighter initWithTextView:waitInterval:styles:]                                                                 83.33% (5/6)        
        -[HGMarkdownHighlighter parseText:]                                                                                            100.00% (9/9)       
        -[HGMarkdownHighlighter convertOffsets:text:]                                                                                  26.19% (11/42)      
        -[HGMarkdownHighlighter requestParsing]                                                                                        100.00% (15/15)     
        __39-[HGMarkdownHighlighter requestParsing]_block_invoke                                                                       100.00% (12/12)     
        __39-[HGMarkdownHighlighter requestParsing]_block_invoke.55                                                                    100.00% (3/3)       
        __39-[HGMarkdownHighlighter requestParsing]_block_invoke.67                                                                    100.00% (4/4)       
        -[HGMarkdownHighlighter getClearFontTraitMask:]                                                                                88.89% (16/18)      
        -[HGMarkdownHighlighter clearHighlightingForRange:]                                                                            100.00% (31/31)     
        __51-[HGMarkdownHighlighter clearHighlightingForRange:]_block_invoke                                                           100.00% (8/8)       
        -[HGMarkdownHighlighter readClearTextStylesFromTextView]                                                                       100.00% (17/17)     
        -[HGMarkdownHighlighter applyHighlighting:withRange:]                                                                          80.00% (60/75)      
        __53-[HGMarkdownHighlighter applyHighlighting:withRange:]_block_invoke                                                         92.31% (12/13)      
        -[HGMarkdownHighlighter applyVisibleRangeHighlighting]                                                                         100.00% (16/16)     
        -[HGMarkdownHighlighter clearHighlighting]                                                                                     100.00% (3/3)       
        -[HGMarkdownHighlighter cacheElementList:]                                                                                     100.00% (6/6)       
        -[HGMarkdownHighlighter clearElementsCache]                                                                                    100.00% (2/2)       
        -[HGMarkdownHighlighter textViewTextDidChange:]                                                                                0.00% (0/11)        
        __47-[HGMarkdownHighlighter textViewTextDidChange:]_block_invoke                                                               0.00% (0/3)         
        -[HGMarkdownHighlighter textViewDidScroll:]                                                                                    100.00% (11/11)     
        __43-[HGMarkdownHighlighter textViewDidScroll:]_block_invoke                                                                   100.00% (6/6)       
        __43-[HGMarkdownHighlighter textViewDidScroll:]_block_invoke_2                                                                 100.00% (3/3)       
        -[HGMarkdownHighlighter getDefaultStyles]                                                                                      100.00% (27/27)     
        -[HGMarkdownHighlighter applyStyleDependenciesToTargetTextView]                                                                92.86% (13/14)      
        -[HGMarkdownHighlighter setStyles:]                                                                                            100.00% (8/8)       
        -[HGMarkdownHighlighter getDefaultSelectedTextAttributes]                                                                      100.00% (7/7)       
        -[HGMarkdownHighlighter handleStyleParsingError:]                                                                              100.00% (12/12)     
        -[HGMarkdownHighlighter applyStylesFromStylesheet:withErrorHandler:]                                                           86.05% (74/86)      
        -[HGMarkdownHighlighter setTargetTextView:]                                                                                    100.00% (7/7)       
        -[HGMarkdownHighlighter parseAndHighlightNow]                                                                                  100.00% (3/3)       
        -[HGMarkdownHighlighter highlightNow]                                                                                          100.00% (3/3)       
        -[HGMarkdownHighlighter activate]                                                                                              100.00% (24/24)     
        -[HGMarkdownHighlighter deactivate]                                                                                            100.00% (18/18)     
    /Users/runner/work/macdown3000/macdown3000/Dependency/peg-markdown-highlight/HGMarkdownHighlightingStyle.m                         87.69% (57/65)      
        +[HGMarkdownHighlightingStyle colorFromARGBColor:]                                                                             100.00% (6/6)       

... (2882 more lines truncated)

📊 **Full coverage report available in workflow artifacts**

@schuyler schuyler merged commit 7f21b54 into main Apr 8, 2026
10 checks passed
@schuyler schuyler deleted the claude/fix-cve-webview-navigation-351 branch April 8, 2026 03:30
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