Skip to content

fix: close case-insensitive blocklist bypass and dotfile filter gap - #115

Open
sadegh wants to merge 198 commits into
bitbonsai:mainfrom
sadegh:fix/security-pathfilter
Open

fix: close case-insensitive blocklist bypass and dotfile filter gap#115
sadegh wants to merge 198 commits into
bitbonsai:mainfrom
sadegh:fix/security-pathfilter

Conversation

@sadegh

@sadegh sadegh commented Apr 28, 2026

Copy link
Copy Markdown

Summary

Two security fixes to PathFilter, both in src/pathfilter.ts:

1. Case-insensitive blocklist bypass (HIGH)

The blocklist regex was case-sensitive, so .Obsidian/app.json and .GIT/hooks/post-merge bypassed the .obsidian/** and .git/** patterns on macOS/Windows (case-insensitive filesystems). Fixed by adding the i flag to the compiled regex in simpleGlobMatch.

2. Dotfile filter gap (HIGH)

isFile() returns false for dotfiles (last component starts with .) because they have a dot at position 0. This caused dotfiles like .bashrc and notes/.env to skip the extension allowlist entirely. Fixed by explicitly denying dotfiles that don't end with an allowed extension in the else branch of isAllowed.

Extensionless non-dotfile paths (notes, 1. Project, attachments/folder) are unaffected — they're treated as directory names and pass through as before.

Chained impact: Combining both gaps enabled writing to .Git/hooks/post-merge on macOS (capital G bypasses .git/**, no extension bypasses the allowlist), which executes on the next git pull in a vault that is also a git repo.

Changes

  • src/pathfilter.ts — two targeted changes, no new methods or helpers
  • src/pathfilter.test.ts — 6 new regression tests across two describe blocks
  • src/filesystem.ts — comment added to moveFile documenting why isAllowedForListing is intentional for binary file moves (no logic change)
  • src/filesystem.test.ts — one test documenting the moveFile dotfile-destination behavior

All 187 tests pass (180 existing + 7 new).

Test coverage

New tests cover:

  • Mixed-case .obsidian, .OBSIDIAN, .ObSiDiAn paths
  • Mixed-case .Git, .GIT paths
  • Node_Modules bypass
  • .bashrc, .zshrc, notes/.env dotfile writes
  • .hidden.md (hidden file with allowed extension — should pass)
  • Extensionless non-dotfile paths still allowed (notes, Makefile, attachments/folder)
  • Documented moveFile dotfile-destination behavior

mirowolff and others added 30 commits September 23, 2025 16:49
- Replace Bun scripts with npm equivalents using tsx and vitest
- Convert Bun.file(), Bun.write(), Bun.Glob(), and Bun.YAML APIs to Node.js equivalents
- Update dependencies: remove @types/bun, add Node.js tooling (tsx, typescript, vitest, js-yaml)
- Add TypeScript configuration and Vitest test config
- Update shebang and usage messages from bun to node
- Maintain backward compatibility for all MCP server functionality
- All tests passing after conversion

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Update README.md with npm/npx commands replacing all Bun references
- Bump version to 0.5.0 in package.json and server.ts
- Update installation instructions for Node.js runtime
- Replace all bunx commands with npx equivalents
- Update developer documentation and troubleshooting guides

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add build script using tsc with separate build configuration
- Update bin configuration to point to compiled JS files in dist/
- Include compiled JavaScript files in npm package
- Fix TypeScript strict mode errors for compilation
- Update .gitignore to include dist/ in version control for publishing
- Resolves Claude Desktop execution issues with direct TypeScript execution

This ensures the package works without requiring users to have tsx installed.
Update error message to show correct npx command instead of referencing server.ts file directly.
Prepare for npm publish with corrected Node.js compatibility.
- Change main field from server.ts to dist/server.js
- Add executable permission to compiled JavaScript file
- This resolves the 'env: bun: No such file or directory' error
- Version bump to 0.5.2
- Normalize '.' path to empty string for root directory listing
- Ensure consistent behavior between '.', '/', and '' paths
- All now correctly return both files and directories from vault root
- Version bump to 0.5.3

Resolves issue where LLMs using '.' path only received files without directories.
- Remove js-yaml dependency and use gray-matter for all YAML operations
- Fix write_note, update_frontmatter, and manage_tags YAML serialization errors
- Add comprehensive integration tests for all frontmatter operations
- Tests cover write_note with frontmatter, append/prepend modes, update operations
- Tests cover tag management (add, remove, list) and edge cases
- All 22 tests now passing including frontmatter validation
- Version bump to 0.5.4

Resolves: 'yaml.dump is not a function' error in all frontmatter tools
- Add detailed changelog documenting all versions from 0.1.0 to 0.5.4
- Document major migration from Bun to Node.js in v0.5.0
- Document YAML frontmatter fixes and comprehensive test suite in v0.5.4
- Include migration notes and breaking changes
- Update README configuration examples to use @latest tag to prevent caching
- Follows Keep a Changelog format with semantic versioning
Prevents Claude Code local settings from being tracked in git
File is now properly ignored and won't appear as modified
- Add centered MCP-Obsidian logo at top of README
- Include embedded demo video with autoplay and loop features
- Improve visual presentation and user engagement

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add website link (mcp-obsidian.org) to header section
- Include Windsurf IDE in supported platforms
- Add performance and dependency highlights to features
- Enhance developer experience with MCP Inspector tips
- Add advanced use case examples for better user guidance
- Complete architecture section with missing src/search.ts

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Introduces a new patch_note tool that allows replacing specific strings in notes without rewriting the entire file, reducing token usage and improving performance for small edits.

Features:
- String-based replacement with oldString/newString parameters
- Safety: fails if multiple matches found (unless replaceAll=true)
- Preserves frontmatter and file structure
- Returns match count and detailed success/error messages

Changes:
- Added PatchNoteParams and PatchNoteResult types
- Implemented patchNote() method in FileSystemService
- Added patch_note tool to MCP server
- Comprehensive test coverage (8 new tests)
- Bumped version to 0.6.0
## Changes

- Add comprehensive tests for patch_note feature (16 tests)
  - Single/multiple occurrence handling
  - Empty string validation
  - Special characters and whitespace preservation
  - Case sensitivity
  - Performance testing

- Add validation for empty oldString/newString in patchNote

- Reorganize test structure
  - Move tests from tests/ to src/ (co-location)
  - Merge filesystem, patch, and integration tests into src/filesystem.test.ts
  - Move frontmatter tests to src/frontmatter.test.ts

All 38 tests passing
- Remove .npmrc from git tracking to prevent auth token exposure
- Add .npmrc to .gitignore for security
- Update package-lock.json with peer dependency changes
- Remove duplicate GitHub Sponsors badge from README
- Add npm version badge as second badge in README
- Badges now: GitHub Stars, npm version, Sponsors, Ko-Fi, Liberapay
### Added
- prettyPrint parameter (default: false) for all JSON responses
- Token-optimized responses with 40-60% reduction

### Changed
- Minified search result field names (path→p, title→t, excerpt→ex, matchCount→mc, lineNumber→ln)
- Reduced search excerpt context from 50 to 21 characters
- Removed redundant fields from all responses
- Compact JSON format by default for minimal token usage

### Performance
- 40-60% token reduction in typical API responses
- More efficient for high-volume operations
- Better cost optimization for AI interactions

All tests passing (76/76).
mirowolff and others added 26 commits March 20, 2026 08:31
…n/minor-and-patch-e2f43b3205

deps: bump the minor-and-patch group with 2 updates
fix: block symlinks that resolve outside vault boundary
…tory

feat: extract createServer factory for library consumers
feat: add list_all_tags tool and Obsidian CLI routing in skill
send-broadcast.ts uses Node builtins that aren't available
in the Astro/Cloudflare type context. It's a CLI-only script
and doesn't need to be checked during the website build.
Replace the single hardcoded binary check with a cascading priority
table and stale PATH detection. Obsidian 1.12.7+ bundles a dedicated
obsidian-cli binary (~10x faster) and changes the PATH registration
mechanism from a ~/.zprofile entry to a /usr/local/bin symlink.

Closes bitbonsai#92
Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.9.3 to 6.0.2.
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Commits](microsoft/TypeScript@v5.9.3...v6.0.2)

---
updated-dependencies:
- dependency-name: typescript
  dependency-version: 6.0.2
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
- Add preserveStringify() using yaml.parseDocument for AST-aware updates
- Only modified keys are rewritten; unmodified fields keep raw formatting
- Fixes YYYY-MM-DD dates becoming ISO timestamps (bitbonsai#77)
- Fixes HH:MM values parsed as sexagesimal integers (bitbonsai#75)
- Fixes quoted strings losing their quote style (bitbonsai#76)
- Applies to update_frontmatter, manage_tags, and write_note append/prepend
- Update CHANGELOG, README, and package version to 0.11.1
- Refresh website copy: FeatureGrid, ComparisonTable, UpdateCallout
- Sync markdown mirrors: features, demo, how-it-works, index
Bumps the minor-and-patch group with 3 updates in the / directory: [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk), [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) and [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest).


Updates `@modelcontextprotocol/sdk` from 1.27.1 to 1.29.0
- [Release notes](https://github.com/modelcontextprotocol/typescript-sdk/releases)
- [Commits](modelcontextprotocol/typescript-sdk@v1.27.1...v1.29.0)

Updates `@types/node` from 25.5.0 to 25.5.2
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `vitest` from 4.1.0 to 4.1.2
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.2/packages/vitest)

---
updated-dependencies:
- dependency-name: "@modelcontextprotocol/sdk"
  dependency-version: 1.29.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
- dependency-name: "@types/node"
  dependency-version: 25.5.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor-and-patch
- dependency-name: vitest
  dependency-version: 4.1.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
…rn/minor-and-patch-b219d17f8d

deps: bump the minor-and-patch group across 1 directory with 3 updates
- Add ignoreDeprecations for node10 moduleResolution
- Add explicit types: ["node"] for TS 6 resolution
…n/typescript-6.0.2

deps: bump typescript from 5.9.3 to 6.0.2
- Change validation from falsy check to explicit undefined/null check
- Empty string is now a valid way to delete matched text
- Update test to verify deletion behavior
- none (default): permanent delete (previous behavior)
- local: move to .trash/ inside vault, preserving folder structure
- system: move to OS trash via the trash package
- Add trashMode parameter to tool schema, types, and handler
- Add tests for local and system trash modes
- Update docs: CHANGELOG, README, website
…n PathFilter

On macOS/Windows (case-insensitive filesystems), the blocklist regex was
case-sensitive, allowing paths like .Obsidian/app.json or .GIT/hooks/post-merge
to bypass protection against .obsidian and .git. Combined with the fact that
dotfiles and extensionless paths (where isFile() returns false) skipped the
extension allowlist entirely, this enabled an RCE chain: write a git hook via
.Git/hooks/post-merge and execute arbitrary code on the next git pull.

Fixes:
- simpleGlobMatch: add `i` flag to regex so blocklist matches case-insensitively
- isAllowed: deny dotfiles (.bashrc, .zshrc, notes/.env) that don't end with
  an allowed extension; extensionless non-dotfile paths (directories, Makefile)
  are still allowed since they can't escape the vault boundary
- moveFile: restore isAllowedForListing (intentional for binary file moves) with
  an explanatory comment; previously changed to isAllowed which broke .png moves

Adds regression tests covering all three fixes.
- Reduce pathfilter.ts block comment to one line (why, not how)
- Change .md test to .hidden.md — more realistic hidden markdown file
- Add comment in moveFile noting that newPath uses isAllowedForListing
  so dotfile destinations are not blocked (by design for binary moves)
- Add test documenting current move_file dotfile-destination behavior
ErycM added a commit to ErycM/mcpvault that referenced this pull request May 15, 2026
Anyone cloning this fork (including future-self) should immediately
understand (a) why it exists, (b) what's different from upstream, (c)
how the cherry-picks were security-audited before pulling them in, (d)
how to keep the fork in sync as upstream evolves, and (e) the plan to
eventually retire the fork once PRs bitbonsai#113/bitbonsai#115 + our own --allowed-extensions
all land upstream.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bitbonsai

Copy link
Copy Markdown
Owner

Hey @sadegh, good eye, you spotted the case-insensitive deny-list bypass. Heads up: 0.11.4 just shipped a fix for exactly that (case-insensitive matching + trailing dot/space canonicalization, GHSA-j99q-93c9-h869), so this PR is largely superseded. If anything is still uncovered after 0.11.4, rebase onto current main and I'll take a look. Thanks for caring about this.

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.