Add path injection query

This commit is contained in:
Tony Torralba
2022-11-28 11:11:39 +01:00
parent 0f87eb45db
commit 8cc66172c3
11 changed files with 453 additions and 0 deletions

View File

@@ -82,10 +82,12 @@ private module Frameworks {
private import codeql.swift.frameworks.StandardLibrary.Data
private import codeql.swift.frameworks.StandardLibrary.InputStream
private import codeql.swift.frameworks.StandardLibrary.NSData
private import codeql.swift.frameworks.StandardLibrary.NsUrl
private import codeql.swift.frameworks.StandardLibrary.String
private import codeql.swift.frameworks.StandardLibrary.Url
private import codeql.swift.frameworks.StandardLibrary.UrlSession
private import codeql.swift.frameworks.StandardLibrary.WebView
private import codeql.swift.security.PathInjection
}
/**

View File

@@ -0,0 +1,17 @@
import swift
private import codeql.swift.dataflow.DataFlow
private import codeql.swift.dataflow.ExternalFlow
private import codeql.swift.dataflow.FlowSteps
/**
* A model for `NSURL` members that permit taint flow.
*/
private class NsUrlSummaries extends SummaryModelCsv {
override predicate row(string row) {
row =
[
";NSURL;true;init(string:);(String);;Argument[0];ReturnValue;taint",
// TODO
]
}
}

View File

@@ -0,0 +1,99 @@
/** Provides classes and predicates to reason about path injection vulnerabilities. */
import swift
private import codeql.swift.dataflow.DataFlow
private import codeql.swift.dataflow.ExternalFlow
/** A data flow sink for path injection vulnerabilities. */
abstract class PathInjectionSink extends DataFlow::Node { }
/** A sanitizer for path injection vulnerabilities. */
abstract class PathInjectionSanitizer extends DataFlow::Node { }
/**
* A unit class for adding additional taint steps.
*
* Extend this class to add additional taint steps that should apply to paths related to
* path injection vulnerabilities.
*/
class PathInjectionAdditionalTaintStep extends Unit {
/**
* Holds if the step from `node1` to `node2` should be considered a taint
* step for paths related to path injection vulnerabilities.
*/
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
}
private class DefaultPathInjectionSink extends PathInjectionSink {
DefaultPathInjectionSink() { sinkNode(this, "path-injection") }
}
private class DefaultPathInjectionSanitizer extends PathInjectionSanitizer {
DefaultPathInjectionSanitizer() { none() } // TODO: Implement a proper path sanitizer
}
private class PathInjectionSinks extends SinkModelCsv {
override predicate row(string row) {
row =
[
";Data;true;write(to:options:);;;Argument[0];path-injection",
";NSData;true;write(to:atomically:);;;Argument[0];path-injection",
";NSData;true;write(to:options:);;;Argument[0];path-injection",
";NSData;true;write(toFile:atomically:);;;Argument[0];path-injection",
";NSData;true;write(toFile:options:);;;Argument[0];path-injection",
";FileManager;true;contentsOfDirectory(at:includingPropertiesForKeys:options:);;;Argument[0];path-injection",
";FileManager;true;contentsOfDirectory(atPath:);;;Argument[0];path-injection",
";FileManager;true;enumerator(at:includingPropertiesForKeys:options:errorHandler:);;;Argument[0];path-injection",
";FileManager;true;enumerator(atPath:);;;Argument[0];path-injection",
";FileManager;true;subpathsOfDirectory(atPath:);;;Argument[0];path-injection",
";FileManager;true;subpaths(atPath:);;;Argument[0];path-injection",
";FileManager;true;createDirectory(at:withIntermediateDirectories:attributes:);;;Argument[0];path-injection",
";FileManager;true;createDirectory(atPath:withIntermediateDirectories:attributes:);;;Argument[0];path-injection",
";FileManager;true;createFile(atPath:contents:attributes:);;;Argument[0];path-injection",
";FileManager;true;removeItem(at:);;;Argument[0];path-injection",
";FileManager;true;removeItem(atPath:);;;Argument[0];path-injection",
";FileManager;true;trashItem(at:resultingItemURL:);;;Argument[0];path-injection",
";FileManager;true;replaceItem(at:withItemAt:backupItemName:options:resultingItemURL:);;;Argument[0..1];path-injection",
";FileManager;true;replaceItemAt(_:withItemAt:backupItemName:options:);;;Argument[0..1];path-injection",
";FileManager;true;replaceItemAt(_:withItemAt:backupItemName:options:resultingItemURL:);;;Argument[0..1];path-injection",
";FileManager;true;copyItem(at:to:);;;Argument[0..1];path-injection",
";FileManager;true;copyItem(atPath:toPath:);;;Argument[0..1];path-injection",
";FileManager;true;moveItem(at:to:);;;Argument[0..1];path-injection",
";FileManager;true;moveItem(atPath:toPath:);;;Argument[0..1];path-injection",
";FileManager;true;createSymbolicLink(at:withDestinationURL:);;;Argument[0..1];path-injection",
";FileManager;true;createSymbolicLink(atPath:withDestinationPath:);;;Argument[0..1];path-injection",
";FileManager;true;linkItem(at:to:);;;Argument[0..1];path-injection",
";FileManager;true;linkItem(atPath:toPath:);;;Argument[0..1];path-injection",
";FileManager;true;destinationOfSymbolicLink(atPath:);;;Argument[0];path-injection",
";FileManager;true;fileExists(atPath:);;;Argument[0];path-injection",
";FileManager;true;fileExists(atPath:isDirectory:);;;Argument[0];path-injection",
";FileManager;true;isReadableFile(atPath:);;;Argument[0];path-injection",
";FileManager;true;isWritableFile(atPath:);;;Argument[0];path-injection",
";FileManager;true;isDeletableFile(atPath:);;;Argument[0];path-injection",
";FileManager;true;componentsToDisplay(forPath:);;;Argument[0];path-injection",
";FileManager;true;displayName(atPath:);;;Argument[0];path-injection",
";FileManager;true;attributesOfItem(atPath:);;;Argument[0];path-injection",
";FileManager;true;attributesOfFileSystem(forPath:);;;Argument[0];path-injection",
";FileManager;true;setAttributes(_:ofItemAtPath:);;;Argument[1];path-injection",
";FileManager;true;contents(atPath:);;;Argument[0];path-injection",
";FileManager;true;contentsEqual(atPath:andPath:);;;Argument[0..1];path-injection",
";FileManager;true;getRelationship(_:ofDirectoryAt:toItemAt:);;;Argument[1..2];path-injection",
";FileManager;true;getRelationship(_:of:in:toItemAt:);;;Argument[3];path-injection",
";FileManager;true;changeCurrentDirectoryPath(_:);;;Argument[0];path-injection",
";FileManager;true;unmountVolume(at:options:completionHandler:);;;Argument[0];path-injection",
";FileManager;true;NSHFSTypeOfFile(_:);;;Argument[0];path-injection",
// Deprecated FileManager methods:
";FileManager;true;changeFileAttributes(_:atPath:);;;Argument[1];path-injection",
";FileManager;true;fileAttributes(atPath:traverseLink:);;;Argument[0];path-injection",
";FileManager;true;fileSystemAttributes(atPath:);;;Argument[0];path-injection",
";FileManager;true;directoryContents(atPath:);;;Argument[0];path-injection",
";FileManager;true;createDirectory(atPath:attributes:);;;Argument[0];path-injection",
";FileManager;true;createSymbolicLink(atPath:pathContent:);;;Argument[0..1];path-injection",
";FileManager;true;pathContentOfSymbolicLink(atPath:);;;Argument[0];path-injection",
";FileManager;true;replaceItemAtURL(originalItemURL:withItemAtURL:backupItemName:options:);;;Argument[0..1];path-injection",
";NIOFileHandle;true;init(descriptor:);;;Argument[0];path-injection",
";NIOFileHandle;true;init(path:mode:flags:);;;Argument[0];path-injection",
";NIOFileHandle;true;init(path:);;;Argument[0];path-injection"
]
}
}

View File

@@ -0,0 +1,30 @@
/**
* Provides a taint-tracking configuration for reasoning about path injection
* vulnerabilities.
*/
import swift
private import codeql.swift.dataflow.DataFlow
private import codeql.swift.dataflow.ExternalFlow
private import codeql.swift.dataflow.FlowSources
private import codeql.swift.dataflow.TaintTracking
private import codeql.swift.security.PathInjection
/**
* A taint-tracking configuration for path injection vulnerabilities.
*/
class PathInjectionConfiguration extends TaintTracking::Configuration {
PathInjectionConfiguration() { this = "PathInjectionConfiguration" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) { sink instanceof PathInjectionSink }
override predicate isSanitizer(DataFlow::Node sanitizer) {
sanitizer instanceof PathInjectionSanitizer
}
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
any(PathInjectionAdditionalTaintStep s).step(node1, node2)
}
}

View File

@@ -0,0 +1,58 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Accessing paths controlled by users can allow an attacker to access unexpected resources. This
can result in sensitive information being revealed or deleted, or an attacker being able to influence
behavior by modifying unexpected files.</p>
<p>Paths that are naively constructed from data controlled by a user may contain unexpected special characters,
such as "..". Such a path may potentially point to any directory on the file system.</p>
</overview>
<recommendation>
<p>Validate user input before using it to construct a file path. Ideally, follow these rules:</p>
<ul>
<li>Do not allow more than a single "." character.</li>
<li>Do not allow directory separators such as "/" or "\" (depending on the file system).</li>
<li>Do not rely on simply replacing problematic sequences such as "../". For example, after applying this filter to
".../...//" the resulting string would still be "../".</li>
<li>Ideally use a whitelist of known good patterns.</li>
</ul>
</recommendation>
<example>
<p>
In the first example, a file name is read from an HTTP request and then used to access a file.
However, a malicious response could include a file name that is an absolute path, such as
<code>"/Applications/(current_application)/Documents/sensitive.data"</code>.
</p>
<p>
In the second example, it appears that the user is restricted to opening a file within the
<code>"/Library/Caches"</code> home directory. However, a malicious response could contain a file name containing
special characters. For example, the string <code>"../../Documents/sensitive.data"</code> will result in the code
reading the file located at <code>"/Applications/(current_application)/Library/Caches/../../Documents/sensitive.data"</code>,
which contains users' sensitive data. This file may then be made accesible to an attacker, giving them access to all this data.
</p>
<sample src="PathInjectionBad.swift" />
<p>
In the third example, the path used to access the file system is normalized <em>before</em> being checked against a
known prefix. This ensures that regardless of the user input, the resulting path is safe.
</p>
<sample src="PathInjectionGood.swift" />
</example>
<references>
<li>OWASP: <a href="https://owasp.org/www-community/attacks/Path_Traversal">Path Traversal</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,25 @@
/**
* @name Uncontrolled data used in path expression
* @description Accessing paths influenced by users can allow an attacker to access unexpected resources.
* @kind path-problem
* @problem.severity error
* @security-severity 7.5
* @precision high
* @id swift/path-injection
* @tags security
* external/cwe/cwe-022
* external/cwe/cwe-023
* external/cwe/cwe-036
* external/cwe/cwe-073
* external/cwe/cwe-099
*/
import swift
import codeql.swift.dataflow.DataFlow
import codeql.swift.security.PathInjectionQuery
import DataFlow::PathGraph
from DataFlow::PathNode source, DataFlow::PathNode sink
where any(PathInjectionConfiguration c).hasFlowPath(source, sink)
select sink.getNode(), source, sink, "This path depends on a $@.", source.getNode(),
"user-provided value"

View File

@@ -0,0 +1,10 @@
let fm = FileManager.default
let path = try String(contentsOf: URL(string: "http://example.com/")!)
// BAD
return fm.contents(atPath: path)
// BAD
if (path.hasPrefix(NSHomeDirectory() + "/Library/Caches")) {
return fm.contents(atPath: path)
}

View File

@@ -0,0 +1,8 @@
let fm = FileManager.default
let path = try String(contentsOf: URL(string: "http://example.com/")!)
// GOOD
let filePath = FilePath(stringLiteral: path)
if (filePath.lexicallyNormalized().starts(with: FilePath(stringLiteral: NSHomeDirectory() + "/Library/Caches"))) {
return fm.contents(atPath: path)
}

View File

@@ -0,0 +1,24 @@
import swift
import codeql.swift.dataflow.DataFlow
import codeql.swift.dataflow.FlowSources
import codeql.swift.security.PathInjectionQuery
import TestUtilities.InlineExpectationsTest
class PathInjectionTest extends InlineExpectationsTest {
PathInjectionTest() { this = "PathInjectionTest" }
override string getARelevantTag() { result = "hasPathInjection" }
override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(
PathInjectionConfiguration config, DataFlow::Node source, DataFlow::Node sink, Expr sinkExpr
|
config.hasFlow(source, sink) and
sinkExpr = sink.asExpr() and
location = sinkExpr.getLocation() and
element = sinkExpr.toString() and
tag = "hasPathInjection" and
value = source.asExpr().getLocation().getStartLine().toString()
)
}
}

View File

@@ -0,0 +1,180 @@
// --- stubs ---
struct URL {
init?(string: String) {}
}
class NSURL {
init?(string: String) {}
}
extension String {
init(contentsOf: URL) {
let data = ""
self.init(data)
}
}
class Data {
struct WritingOptions : OptionSet { let rawValue: Int }
init<S>(_ elements: S) {}
func write(to: URL, options: Data.WritingOptions = []) {}
}
class NSData {
struct WritingOptions : OptionSet { let rawValue: Int }
func write(to: URL, atomically: Bool) -> Bool { return false }
func write(to: URL, options: NSData.WritingOptions) {}
func write(toFile: String, atomically: Bool) -> Bool { return false }
func write(toFile: String, options: NSData.WritingOptions) {}
}
struct URLResourceKey {}
struct FileAttributeKey : Hashable {}
struct ObjCBool {}
class FileManager {
class DirectoryEnumerator {}
struct DirectoryEnumerationOptions : OptionSet { let rawValue: Int }
struct ItemReplacementOptions : OptionSet { let rawValue: Int }
struct UnmountOptions : OptionSet { let rawValue: Int }
struct SearchPathDomainMask {}
enum SearchPathDirectory : UInt { case none }
enum URLRelationship : Int { case none }
func contentsOfDirectory(at: URL, includingPropertiesForKeys: [URLResourceKey]?, options: FileManager.DirectoryEnumerationOptions) -> [URL] { return []}
func contentsOfDirectory(atPath: String) -> [String] { return [] }
func enumerator(at: URL, includingPropertiesForKeys: [URLResourceKey]?, options: FileManager.DirectoryEnumerationOptions, errorHandler: ((URL, Error) -> Bool)?) -> FileManager.DirectoryEnumerator? { return nil }
func enumerator(atPath: String) -> FileManager.DirectoryEnumerator? { return nil }
func subpathsOfDirectory(atPath: String) -> [String] { return [] }
func subpaths(atPath: String) -> [String]? { return nil }
func createDirectory(at: URL, withIntermediateDirectories: Bool, attributes: [FileAttributeKey : Any]?) {}
func createDirectory(atPath: String, withIntermediateDirectories: Bool, attributes: [FileAttributeKey : Any]?) {}
func createFile(atPath: String, contents: Data?, attributes: [FileAttributeKey : Any]?) -> Bool { return false }
func removeItem(at: URL) {}
func removeItem(atPath: String) {}
func trashItem(at: URL, resultingItemURL: AutoreleasingUnsafeMutablePointer<NSURL?>?) {}
func replaceItemAt(_: URL, withItemAt: URL, backupItemName: String?, options: FileManager.ItemReplacementOptions) -> URL? { return nil}
func replaceItem(at: URL, withItemAt: URL, backupItemName: String?, options: FileManager.ItemReplacementOptions, resultingItemURL: AutoreleasingUnsafeMutablePointer<NSURL?>?) {}
func copyItem(at: URL, to: URL) {}
func copyItem(atPath: String, toPath: String) {}
func moveItem(at: URL, to: URL) {}
func moveItem(atPath: String, toPath: String) {}
func createSymbolicLink(at: URL, withDestinationURL: URL) {}
func createSymbolicLink(atPath: String, withDestinationPath: String) {}
func linkItem(at: URL, to: URL) {}
func linkItem(atPath: String, toPath: String) {}
func destinationOfSymbolicLink(atPath: String) -> String { return "" }
func fileExists(atPath: String) -> Bool { return false }
func fileExists(atPath: String, isDirectory: UnsafeMutablePointer<ObjCBool>?) -> Bool { return false }
func isReadableFile(atPath: String) -> Bool { return false }
func isWritableFile(atPath: String) -> Bool { return false }
func isExecutableFile(atPath: String) -> Bool { return false }
func isDeletableFile(atPath: String) -> Bool { return false }
func componentsToDisplay(forPath: String) -> [String]? { return nil }
func displayName(atPath: String) -> String { return "" }
func attributesOfItem(atPath: String) -> [FileAttributeKey : Any] { return [:] }
func attributesOfFileSystem(forPath: String) -> [FileAttributeKey : Any] { return [:] }
func setAttributes(_: [FileAttributeKey : Any], ofItemAtPath: String) {}
func contents(atPath: String) -> Data? { return nil }
func contentsEqual(atPath: String, andPath: String) -> Bool { return false }
func getRelationship(_: UnsafeMutablePointer<FileManager.URLRelationship>, ofDirectoryAt: URL, toItemAt: URL) {}
func getRelationship(_: UnsafeMutablePointer<FileManager.URLRelationship>, of: FileManager.SearchPathDirectory, in: FileManager.SearchPathDomainMask, toItemAt: URL) {}
func changeCurrentDirectoryPath(_: String) -> Bool { return false }
func unmountVolume(at: URL, options: FileManager.UnmountOptions, completionHandler: (Error?) -> Void) {}
func NSHFSTypeOfFile(_: String!) -> String! { return "" }
// Deprecated methods
func changeFileAttributes(_: [AnyHashable : Any], atPath: String) -> Bool { return false }
func fileAttributes(atPath: String, traverseLink: Bool) -> [AnyHashable : Any]? { return nil }
func fileSystemAttributes(atPath: String) -> [AnyHashable : Any]? { return nil }
func directoryContents(atPath: String) -> [Any]? { return nil }
func createDirectory(atPath: String, attributes: [AnyHashable : Any]) -> Bool { return false }
func createSymbolicLink(atPath: String, pathContent: String) -> Bool { return false }
func pathContentOfSymbolicLink(atPath: String) -> String? { return nil }
func replaceItemAtURL(originalItemURL: NSURL, withItemAtURL: NSURL, backupItemName: String?, options: FileManager.ItemReplacementOptions) -> NSURL? { return nil }
}
// --- tests ---
func test() {
let remoteString = String(contentsOf: URL(string: "http://example.com/")!)
let remoteUrl = URL(string: remoteString)!
let remoteNsUrl = NSURL(string: remoteString)!
let safeUrl = URL(string: "")!
let safeNsUrl = NSURL(string: "")!
Data("").write(to: remoteUrl, options: []) // $ hasPathInjection=102
let nsData = NSData()
let _ = nsData.write(to: remoteUrl, atomically: false) // $ hasPathInjection=102
nsData.write(to: remoteUrl, options: []) // $ hasPathInjection=102
let _ = nsData.write(toFile: remoteString, atomically: false) // $ hasPathInjection=102
nsData.write(toFile: remoteString, options: []) // $ hasPathInjection=102
let fm = FileManager()
let _ = fm.contentsOfDirectory(at: remoteUrl, includingPropertiesForKeys: [], options: []) // $ hasPathInjection=102
let _ = fm.contentsOfDirectory(atPath: remoteString) // $ hasPathInjection=102
let _ = fm.enumerator(at: remoteUrl, includingPropertiesForKeys: [], options: [], errorHandler: nil) // $ hasPathInjection=102
let _ = fm.enumerator(atPath: remoteString) // $ hasPathInjection=102
let _ = fm.subpathsOfDirectory(atPath: remoteString) // $ hasPathInjection=102
let _ = fm.subpaths(atPath: remoteString) // $ hasPathInjection=102
fm.createDirectory(at: remoteUrl, withIntermediateDirectories: false, attributes: [:]) // $ hasPathInjection=102
let _ = fm.createDirectory(atPath: remoteString, attributes: [:]) // $ hasPathInjection=102
let _ = fm.createFile(atPath: remoteString, contents: nil, attributes: [:]) // $ hasPathInjection=102
fm.removeItem(at: remoteUrl) // $ hasPathInjection=102
fm.removeItem(atPath: remoteString) // $ hasPathInjection=102
fm.trashItem(at: remoteUrl, resultingItemURL: AutoreleasingUnsafeMutablePointer<NSURL?>.init(bitPattern: 0)) // $ hasPathInjection=102
let _ = fm.replaceItemAt(remoteUrl, withItemAt: safeUrl, backupItemName: nil, options: []) // $ hasPathInjection=102
let _ = fm.replaceItemAt(safeUrl, withItemAt: remoteUrl, backupItemName: nil, options: []) // $ hasPathInjection=102
fm.replaceItem(at: remoteUrl, withItemAt: safeUrl, backupItemName: nil, options: [], resultingItemURL: AutoreleasingUnsafeMutablePointer<NSURL?>.init(bitPattern: 0)) // $ hasPathInjection=102
fm.replaceItem(at: safeUrl, withItemAt: remoteUrl, backupItemName: nil, options: [], resultingItemURL: AutoreleasingUnsafeMutablePointer<NSURL?>.init(bitPattern: 0)) // $ hasPathInjection=102
fm.copyItem(at: remoteUrl, to: safeUrl) // $ hasPathInjection=102
fm.copyItem(at: safeUrl, to: remoteUrl) // $ hasPathInjection=102
fm.copyItem(atPath: remoteString, toPath: "") // $ hasPathInjection=102
fm.copyItem(atPath: "", toPath: remoteString) // $ hasPathInjection=102
fm.moveItem(at: remoteUrl, to: safeUrl) // $ hasPathInjection=102
fm.moveItem(at: safeUrl, to: remoteUrl) // $ hasPathInjection=102
fm.moveItem(atPath: remoteString, toPath: "") // $ hasPathInjection=102
fm.moveItem(atPath: "", toPath: remoteString) // $ hasPathInjection=102
fm.createSymbolicLink(at: remoteUrl, withDestinationURL: safeUrl) // $ hasPathInjection=102
fm.createSymbolicLink(at: safeUrl, withDestinationURL: remoteUrl) // $ hasPathInjection=102
fm.createSymbolicLink(atPath: remoteString, withDestinationPath: "") // $ hasPathInjection=102
fm.createSymbolicLink(atPath: "", withDestinationPath: remoteString) // $ hasPathInjection=102
fm.linkItem(at: remoteUrl, to: safeUrl) // $ hasPathInjection=102
fm.linkItem(at: safeUrl, to: remoteUrl) // $ hasPathInjection=102
fm.linkItem(atPath: remoteString, toPath: "") // $ hasPathInjection=102
fm.linkItem(atPath: "", toPath: remoteString) // $ hasPathInjection=102
let _ = fm.destinationOfSymbolicLink(atPath: remoteString) // $ hasPathInjection=102
let _ = fm.fileExists(atPath: remoteString) // $ hasPathInjection=102
let _ = fm.fileExists(atPath: remoteString, isDirectory: UnsafeMutablePointer<ObjCBool>.init(bitPattern: 0)) // $ hasPathInjection=102
let _ = fm.isReadableFile(atPath: remoteString) // $ hasPathInjection=102
let _ = fm.isWritableFile(atPath: remoteString) // $ hasPathInjection=102
let _ = fm.isDeletableFile(atPath: remoteString) // $ hasPathInjection=102
let _ = fm.componentsToDisplay(forPath: remoteString) // $ hasPathInjection=102
let _ = fm.displayName(atPath: remoteString) // $ hasPathInjection=102
let _ = fm.attributesOfItem(atPath: remoteString) // $ hasPathInjection=102
let _ = fm.attributesOfFileSystem(forPath: remoteString) // $ hasPathInjection=102
fm.setAttributes([:], ofItemAtPath: remoteString) // $ hasPathInjection=102
let _ = fm.contents(atPath: remoteString) // $ hasPathInjection=102
let _ = fm.contentsEqual(atPath: remoteString, andPath: "") // $ hasPathInjection=102
let _ = fm.contentsEqual(atPath: "", andPath: remoteString) // $ hasPathInjection=102
fm.getRelationship(UnsafeMutablePointer<FileManager.URLRelationship>.allocate(capacity: 0), ofDirectoryAt: remoteUrl, toItemAt: safeUrl) // $ hasPathInjection=102
fm.getRelationship(UnsafeMutablePointer<FileManager.URLRelationship>.allocate(capacity: 0), ofDirectoryAt: safeUrl, toItemAt: remoteUrl) // $ hasPathInjection=102
fm.getRelationship(UnsafeMutablePointer<FileManager.URLRelationship>.allocate(capacity: 0), of: FileManager.SearchPathDirectory.none, in: FileManager.SearchPathDomainMask(), toItemAt: remoteUrl) // $ hasPathInjection=102
let _ = fm.changeCurrentDirectoryPath(remoteString) // $ hasPathInjection=102
let _ = fm.unmountVolume(at: remoteUrl, options: [], completionHandler: { _ in }) // $ hasPathInjection=102
let _ = fm.NSHFSTypeOfFile(remoteString) // $ hasPathInjection=102
// Deprecated methods
let _ = fm.changeFileAttributes([:], atPath: remoteString) // $ hasPathInjection=102
let _ = fm.fileAttributes(atPath: remoteString, traverseLink: false) // $ hasPathInjection=102
let _ = fm.fileSystemAttributes(atPath: remoteString) // $ hasPathInjection=102
let _ = fm.directoryContents(atPath: remoteString) // $ hasPathInjection=102
let _ = fm.createDirectory(atPath: remoteString, attributes: [:]) // $ hasPathInjection=102
let _ = fm.createSymbolicLink(atPath: remoteString, pathContent: "") // $ hasPathInjection=102
let _ = fm.createSymbolicLink(atPath: "", pathContent: remoteString) // $ hasPathInjection=102
let _ = fm.pathContentOfSymbolicLink(atPath: remoteString) // $ hasPathInjection=102
let _ = fm.replaceItemAtURL(originalItemURL: remoteNsUrl, withItemAtURL: safeNsUrl, backupItemName: nil, options: []) // $ hasPathInjection=102
let _ = fm.replaceItemAtURL(originalItemURL: safeNsUrl, withItemAtURL: remoteNsUrl, backupItemName: nil, options: []) // $ hasPathInjection=102
}