Fix "File traversal detected" crash processing plugin commands on Windows (#7422)
What changed, and why it matters
This commit fixes a bug in BTCPay Server's plugin system where a safety check that prevents directory traversal attacks was accidentally rejecting valid plugin names on Windows. The original code always appended a '/' slash, but Windows uses '\' as its path separator, so the safety check compared paths with mismatched slashes and falsely flagged them as attacks, causing a crash. The fix makes the trailing slash match the operating system's separator. There is no direct evidence in the commit that an actual security vulnerability was exploited; it appears to be a bug fix for a false-positive crash.
Treat as a reliability/correctness fix rather than a security patch. Verify the fix on Windows by running plugin commands and confirming the 'File traversal detected' crash no longer occurs. Review whether any other path-prefix checks in the codebase use hard-coded '/' separators and could have similar false positives on Windows.
Security signals we found
Path traversal guard present and being repaired
Use of Path.GetFullPath and directory-prefix check for sandboxing
Fix for cross-platform path separator mismatch
No new input validation or sanitization added
Crash/DoS symptom ('InvalidOperationException: File traversal detected') on Windows
Evidence from the diff
In PluginManager.cs, AssertSafeName() validates that a resolved plugin path stays within the plugin directory by comparing Path.GetFullPath(pluginDir) plus a trailing slash against Path.GetFullPath(Path.Combine(fullPluginDir, plugin)). The original code appended a hard-coded ‘/’ via WithTrailingSlash(), which on Windows does not match the ‘' returned by GetFullPath, so the StartsWith guard fails for legitimate paths and throws ‘File traversal detected’. The patch replaces WithTrailingSlash() with an OS-aware trailing Path.DirectorySeparatorChar. This restores the intended path traversal defense but does not change the security model or add new validation.
Changed components
BTCPayServer/Plugins/PluginManager.csAssertSafeName methodPlugin command processing on WindowsInspect captured patch +4 / −1
diff --git a/BTCPayServer/Plugins/PluginManager.cs b/BTCPayServer/Plugins/PluginManager.cs
index 8e18c25..efa314f 100644
--- a/BTCPayServer/Plugins/PluginManager.cs
+++ b/BTCPayServer/Plugins/PluginManager.cs
@@ -557,7 +557,10 @@ namespace BTCPayServer.Plugins
private static void AssertSafeName(string pluginDir, string plugin)
{
- var fullPluginDir = Path.GetFullPath(pluginDir).WithTrailingSlash();
+ // platform separator: WithTrailingSlash's '/' mismatches GetFullPath on Windows
+ var fullPluginDir = Path.GetFullPath(pluginDir);
+ if (!fullPluginDir.EndsWith(Path.DirectorySeparatorChar))
+ fullPluginDir += Path.DirectorySeparatorChar;
var fullPath = Path.GetFullPath(Path.Combine(fullPluginDir, plugin));
if (!fullPath.StartsWith(fullPluginDir, StringComparison.Ordinal))
throw new InvalidOperationException("File traversal detected");
Why this scored 33/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.