@@ -192,6 +192,78 @@ public async Task ItDeniesUncPathsAsync()
192192 await Assert . ThrowsAnyAsync < Exception > ( async ( ) => await target . AppendTextAsync ( "text" , "//UNC/server/folder/file.docx" ) ) ;
193193 }
194194
195+ [ Theory ]
196+ [ InlineData ( "\\ \\ UNC\\ server\\ folder\\ file.docx" ) ]
197+ [ InlineData ( "//UNC/server/folder/file.docx" ) ]
198+ [ InlineData ( "/\\ UNC\\ server\\ folder\\ file.docx" ) ]
199+ [ InlineData ( "\\ /UNC/server/folder/file.docx" ) ]
200+ [ InlineData ( "\\ \\ ?\\ UNC\\ server\\ folder\\ file.docx" ) ]
201+ [ InlineData ( "\\ \\ .\\ UNC\\ server\\ folder\\ file.docx" ) ]
202+ public async Task ItRejectsUncOrExtendedPathsOnReadAsync ( string path )
203+ {
204+ // Arrange
205+ var fileSystemConnectorMock = new Mock < IFileSystemConnector > ( MockBehavior . Strict ) ;
206+ var documentConnectorMock = new Mock < IDocumentConnector > ( MockBehavior . Strict ) ;
207+ var target = new DocumentPlugin ( documentConnectorMock . Object , fileSystemConnectorMock . Object )
208+ {
209+ AllowedDirectories = [ Path . GetTempPath ( ) ]
210+ } ;
211+
212+ // Act & Assert — rejected before any filesystem access
213+ await Assert . ThrowsAsync < ArgumentException > ( async ( ) => await target . ReadTextAsync ( path ) ) ;
214+ }
215+
216+ [ Theory ]
217+ [ InlineData ( "\\ \\ UNC\\ server\\ folder\\ file.docx" ) ]
218+ [ InlineData ( "//UNC/server/folder/file.docx" ) ]
219+ [ InlineData ( "/\\ UNC\\ server\\ folder\\ file.docx" ) ]
220+ [ InlineData ( "\\ /UNC/server/folder/file.docx" ) ]
221+ [ InlineData ( "\\ \\ ?\\ UNC\\ server\\ folder\\ file.docx" ) ]
222+ [ InlineData ( "\\ \\ .\\ UNC\\ server\\ folder\\ file.docx" ) ]
223+ public async Task ItRejectsUncOrExtendedPathsOnAppendAsync ( string path )
224+ {
225+ // Arrange
226+ var fileSystemConnectorMock = new Mock < IFileSystemConnector > ( MockBehavior . Strict ) ;
227+ var documentConnectorMock = new Mock < IDocumentConnector > ( MockBehavior . Strict ) ;
228+ var target = new DocumentPlugin ( documentConnectorMock . Object , fileSystemConnectorMock . Object )
229+ {
230+ AllowedDirectories = [ Path . GetTempPath ( ) ]
231+ } ;
232+
233+ // Act & Assert — rejected before any filesystem access
234+ await Assert . ThrowsAsync < ArgumentException > ( async ( ) => await target . AppendTextAsync ( "text" , path ) ) ;
235+ }
236+
237+ [ Theory ]
238+ [ InlineData ( "/\\ evil-server\\ share" ) ]
239+ [ InlineData ( "\\ /evil-server/share" ) ]
240+ public async Task ItDeniesMixedSeparatorUncPathsIntroducedViaEnvVarExpansionAsync ( string envVarValue )
241+ {
242+ // Arrange — env var that expands to a mixed-separator UNC path
243+ var envVarName = "SK_TEST_MIXUNC_" + Guid . NewGuid ( ) . ToString ( "N" ) [ .. 8 ] ;
244+
245+ try
246+ {
247+ Environment . SetEnvironmentVariable ( envVarName , envVarValue ) ;
248+ var maliciousPath = $ "%{ envVarName } %{ Path . DirectorySeparatorChar } secret.docx";
249+
250+ var fileSystemConnectorMock = new Mock < IFileSystemConnector > ( MockBehavior . Strict ) ;
251+ var documentConnectorMock = new Mock < IDocumentConnector > ( MockBehavior . Strict ) ;
252+ var target = new DocumentPlugin ( documentConnectorMock . Object , fileSystemConnectorMock . Object )
253+ {
254+ AllowedDirectories = [ Path . GetTempPath ( ) ]
255+ } ;
256+
257+ // Act & Assert
258+ await Assert . ThrowsAsync < ArgumentException > ( async ( ) => await target . ReadTextAsync ( maliciousPath ) ) ;
259+ await Assert . ThrowsAsync < ArgumentException > ( async ( ) => await target . AppendTextAsync ( "text" , maliciousPath ) ) ;
260+ }
261+ finally
262+ {
263+ Environment . SetEnvironmentVariable ( envVarName , null ) ;
264+ }
265+ }
266+
195267 [ Fact ]
196268 public async Task ItDeniesDisallowedFoldersAsync ( )
197269 {
0 commit comments