From c4deead77c324119f0b1cc7c217edc9638d34575 Mon Sep 17 00:00:00 2001 From: ESS-ENN Date: Fri, 6 Dec 2024 18:37:43 +0530 Subject: [PATCH 1/3] swift-webview-config-allows-js-open-windows-swift --- ...ew-config-allows-js-open-windows-swift.yml | 141 ++++++++++++++++++ ...-allows-js-open-windows-swift-snapshot.yml | 58 +++++++ ...nfig-allows-js-open-windows-swift-test.yml | 10 ++ 3 files changed, 209 insertions(+) create mode 100644 rules/swift/security/swift-webview-config-allows-js-open-windows-swift.yml create mode 100644 tests/__snapshots__/swift-webview-config-allows-js-open-windows-swift-snapshot.yml create mode 100644 tests/swift/swift-webview-config-allows-js-open-windows-swift-test.yml diff --git a/rules/swift/security/swift-webview-config-allows-js-open-windows-swift.yml b/rules/swift/security/swift-webview-config-allows-js-open-windows-swift.yml new file mode 100644 index 00000000..73966da7 --- /dev/null +++ b/rules/swift/security/swift-webview-config-allows-js-open-windows-swift.yml @@ -0,0 +1,141 @@ +id: swift-webview-config-allows-js-open-windows-swift +language: swift +severity: warning +message: >- + Webviews were observed that explictly allow JavaScript in an WKWebview + to open windows automatically. Consider disabling this functionality if + not required, following the principle of least privelege. +note: >- + [CWE-272]: Least Privilege Violation + [REFERENCES] + https://mas.owasp.org/MASVS/controls/MASVS-PLATFORM-2/ + https://developer.apple.com/documentation/webkit/wkpreferences/1536573-javascriptcanopenwindowsautomati +utils: + match_JavaScriptCanOpenWindowsAutomatically: + kind: assignment + all: + - has: + stopBy: end + kind: navigation_expression + has: + stopBy: end + kind: simple_identifier + pattern: $R + - has: + stopBy: end + kind: navigation_suffix + has: + stopBy: end + kind: simple_identifier + regex: "^JavaScriptCanOpenWindowsAutomatically$" + - has: + kind: boolean_literal + regex: "^true$" + - follows: + stopBy: end + kind: property_declaration + all: + - has: + stopBy: end + kind: pattern + field: name + has: + kind: simple_identifier + pattern: $R + - has: + stopBy: end + kind: call_expression + field: value + all: + - has: + stopBy: end + kind: simple_identifier + regex: "^WKPreferences$" + - has: + stopBy: end + kind: call_suffix + has: + stopBy: end + kind: value_arguments + - not: + precedes: + stopBy: neighbor + kind: assignment + has: + stopBy: end + kind: boolean_literal + regex: "^true$|false" + - not: + follows: + stopBy: neighbor + kind: assignment + has: + stopBy: end + kind: boolean_literal + regex: "^true$" + match_non_boolean: + kind: assignment + all: + - has: + stopBy: end + kind: navigation_expression + has: + stopBy: end + kind: simple_identifier + pattern: $R + - has: + stopBy: end + kind: navigation_suffix + has: + stopBy: end + kind: simple_identifier + regex: "^JavaScriptCanOpenWindowsAutomatically$" + - has: + kind: simple_identifier + - follows: + stopBy: end + kind: property_declaration + all: + - has: + stopBy: end + kind: pattern + field: name + has: + kind: simple_identifier + pattern: $R + - has: + stopBy: end + kind: call_expression + field: value + all: + - has: + stopBy: end + kind: simple_identifier + regex: "^WKPreferences$" + - has: + stopBy: end + kind: call_suffix + has: + stopBy: end + kind: value_arguments + - not: + precedes: + stopBy: neighbor + kind: assignment + has: + stopBy: end + kind: boolean_literal + regex: "^true$|false" + - not: + follows: + stopBy: neighbor + kind: assignment + has: + stopBy: end + kind: boolean_literal + regex: "^true$" + +rule: + any: + - matches: match_JavaScriptCanOpenWindowsAutomatically + - matches: match_non_boolean diff --git a/tests/__snapshots__/swift-webview-config-allows-js-open-windows-swift-snapshot.yml b/tests/__snapshots__/swift-webview-config-allows-js-open-windows-swift-snapshot.yml new file mode 100644 index 00000000..bfe65f85 --- /dev/null +++ b/tests/__snapshots__/swift-webview-config-allows-js-open-windows-swift-snapshot.yml @@ -0,0 +1,58 @@ +id: swift-webview-config-allows-js-open-windows-swift +snapshots: + ? | + let prefs = WKPreferences() + prefs.JavaScriptCanOpenWindowsAutomatically = true + : labels: + - source: prefs.JavaScriptCanOpenWindowsAutomatically = true + style: primary + start: 28 + end: 79 + - source: prefs + style: secondary + start: 28 + end: 33 + - source: prefs.JavaScriptCanOpenWindowsAutomatically + style: secondary + start: 28 + end: 71 + - source: JavaScriptCanOpenWindowsAutomatically + style: secondary + start: 34 + end: 71 + - source: .JavaScriptCanOpenWindowsAutomatically + style: secondary + start: 33 + end: 71 + - source: 'true' + style: secondary + start: 75 + end: 79 + - source: prefs + style: secondary + start: 4 + end: 9 + - source: prefs + style: secondary + start: 4 + end: 9 + - source: WKPreferences + style: secondary + start: 12 + end: 25 + - source: () + style: secondary + start: 25 + end: 27 + - source: () + style: secondary + start: 25 + end: 27 + - source: WKPreferences() + style: secondary + start: 12 + end: 27 + - source: let prefs = WKPreferences() + style: secondary + start: 0 + end: 27 diff --git a/tests/swift/swift-webview-config-allows-js-open-windows-swift-test.yml b/tests/swift/swift-webview-config-allows-js-open-windows-swift-test.yml new file mode 100644 index 00000000..3772db95 --- /dev/null +++ b/tests/swift/swift-webview-config-allows-js-open-windows-swift-test.yml @@ -0,0 +1,10 @@ +id: swift-webview-config-allows-js-open-windows-swift +valid: + - | + let prefs2 = WKPreferences() + prefs2.JavaScriptCanOpenWindowsAutomatically = true + prefs2.JavaScriptCanOpenWindowsAutomatically = false +invalid: + - | + let prefs = WKPreferences() + prefs.JavaScriptCanOpenWindowsAutomatically = true From 8fe5055bc0bc944613105f19643a4b76c36c93b4 Mon Sep 17 00:00:00 2001 From: ESS-ENN Date: Fri, 6 Dec 2024 18:39:02 +0530 Subject: [PATCH 2/3] swift-webview-config-allows-universal-file-access-swift --- ...fig-allows-universal-file-access-swift.yml | 203 ++++++++++++++++++ ...s-universal-file-access-swift-snapshot.yml | 76 +++++++ ...llows-universal-file-access-swift-test.yml | 11 + 3 files changed, 290 insertions(+) create mode 100644 rules/swift/security/swift-webview-config-allows-universal-file-access-swift.yml create mode 100644 tests/__snapshots__/swift-webview-config-allows-universal-file-access-swift-snapshot.yml create mode 100644 tests/swift/swift-webview-config-allows-universal-file-access-swift-test.yml diff --git a/rules/swift/security/swift-webview-config-allows-universal-file-access-swift.yml b/rules/swift/security/swift-webview-config-allows-universal-file-access-swift.yml new file mode 100644 index 00000000..1c20b872 --- /dev/null +++ b/rules/swift/security/swift-webview-config-allows-universal-file-access-swift.yml @@ -0,0 +1,203 @@ +id: swift-webview-config-allows-universal-file-access-swift +severity: warning +language: swift +message: >- + Webviews were observed that do not disable access to application files. + If the WebView does not require loading content from the local filesystem + of the application, this setting should be disabled. +note: >- + [CWE-272] Least Privilege Violation. + [REFERENCES] + - https://mas.owasp.org/MASVS/controls/MASVS-PLATFORM-2/ +utils: + match_pattern_two: + kind: call_expression + all: + - has: + stopBy: end + kind: navigation_expression + all: + - has: + stopBy: neighbor + kind: simple_identifier + pattern: $W + - has: + stopBy: end + kind: navigation_suffix + has: + stopBy: end + kind: simple_identifier + regex: '^setValue$' + - has: + stopBy: neighbor + kind: call_suffix + all: + - has: + stopBy: end + kind: value_argument + has: + stopBy: neighbor + kind: boolean_literal + regex: '^true$' + - has: + stopBy: end + kind: value_argument + all: + - has: + stopBy: end + kind: simple_identifier + regex: '^forKey$' + - has: + stopBy: neighbor + kind: line_string_literal + has: + stopBy: neighbor + kind: line_str_text + regex: '^allowUniversalAccessFromFileURLs$' + - follows: + stopBy: end + kind: property_declaration + all: + - has: + stopBy: end + kind: pattern + has: + stopBy: neighbor + kind: simple_identifier + pattern: $W + - any: + - has: + stopBy: neighbor + kind: navigation_expression + - has: + stopBy: end + kind: call_expression + has: + stopBy: neighbor + kind: simple_identifier + regex: '^WKWebView$' + + - not: + precedes: + stopBy: neighbor + kind: call_expression + all: + - has: + stopBy: neighbor + kind: navigation_expression + all: + - has: + stopBy: neighbor + kind: simple_identifier + pattern: $W + - has: + stopBy: neighbor + kind: navigation_suffix + has: + stopBy: neighbor + kind: simple_identifier + regex: '^setValue$' + - has: + stopBy: neighbor + kind: call_suffix + all: + - has: + stopBy: end + kind: value_argument + has: + stopBy: neighbor + kind: boolean_literal + regex: '^false$' + - has: + stopBy: end + kind: value_argument + all: + - has: + stopBy: end + kind: simple_identifier + regex: '^forKey$' + - has: + stopBy: neighbor + kind: line_string_literal + has: + stopBy: neighbor + kind: line_str_text + regex: '^allowUniversalAccessFromFileURLs$' + + match_pattern_one: + kind: call_expression + all: + - has: + stopBy: neighbor + kind: navigation_expression + all: + - has: + stopBy: neighbor + kind: navigation_expression + all: + - has: + stopBy: end + kind: simple_identifier + pattern: $L + - has: + stopBy: neighbor + kind: navigation_suffix + has: + stopBy: neighbor + kind: simple_identifier + regex: '^configuration$' + - has: + stopBy: neighbor + kind: navigation_suffix + has: + stopBy: neighbor + kind: simple_identifier + regex: '^setValue$' + - has: + stopBy: neighbor + kind: call_suffix + has: + stopBy: neighbor + kind: value_arguments + all: + - has: + stopBy: neighbor + kind: value_argument + has: + stopBy: neighbor + kind: boolean_literal + regex: '^true$' + - has: + stopBy: neighbor + kind: value_argument + all: + - has: + stopBy: neighbor + kind: simple_identifier + regex: '^forKey$' + - has: + stopBy: neighbor + kind: line_string_literal + has: + stopBy: neighbor + kind: line_str_text + regex: '^allowUniversalAccessFromFileURLs$' + - follows: + stopBy: neighbor + kind: property_declaration + all: + - has: + stopBy: end + kind: pattern + has: + stopBy: neighbor + kind: simple_identifier + pattern: $L + - has: + stopBy: neighbor + kind: call_expression +rule: + kind: call_expression + any: + - matches: match_pattern_two + - matches: match_pattern_one diff --git a/tests/__snapshots__/swift-webview-config-allows-universal-file-access-swift-snapshot.yml b/tests/__snapshots__/swift-webview-config-allows-universal-file-access-swift-snapshot.yml new file mode 100644 index 00000000..597532f6 --- /dev/null +++ b/tests/__snapshots__/swift-webview-config-allows-universal-file-access-swift-snapshot.yml @@ -0,0 +1,76 @@ +id: swift-webview-config-allows-universal-file-access-swift +snapshots: + ? |- + let w = WKWebView(frame: .zero, configuration: config) + w.setValue(true, forKey: "allowUniversalAccessFromFileURLs") + let config = w.configuration + config.setValue(true, forKey: "allowUniversalAccessFromFileURLs") + : labels: + - source: 'w.setValue(true, forKey: "allowUniversalAccessFromFileURLs")' + style: primary + start: 55 + end: 115 + - source: w + style: secondary + start: 55 + end: 56 + - source: w.setValue + style: secondary + start: 55 + end: 65 + - source: setValue + style: secondary + start: 57 + end: 65 + - source: .setValue + style: secondary + start: 56 + end: 65 + - source: 'true' + style: secondary + start: 66 + end: 70 + - source: 'true' + style: secondary + start: 66 + end: 70 + - source: forKey + style: secondary + start: 72 + end: 78 + - source: allowUniversalAccessFromFileURLs + style: secondary + start: 81 + end: 113 + - source: '"allowUniversalAccessFromFileURLs"' + style: secondary + start: 80 + end: 114 + - source: 'forKey: "allowUniversalAccessFromFileURLs"' + style: secondary + start: 72 + end: 114 + - source: '(true, forKey: "allowUniversalAccessFromFileURLs")' + style: secondary + start: 65 + end: 115 + - source: w + style: secondary + start: 4 + end: 5 + - source: w + style: secondary + start: 4 + end: 5 + - source: WKWebView + style: secondary + start: 8 + end: 17 + - source: 'WKWebView(frame: .zero, configuration: config)' + style: secondary + start: 8 + end: 54 + - source: 'let w = WKWebView(frame: .zero, configuration: config)' + style: secondary + start: 0 + end: 54 diff --git a/tests/swift/swift-webview-config-allows-universal-file-access-swift-test.yml b/tests/swift/swift-webview-config-allows-universal-file-access-swift-test.yml new file mode 100644 index 00000000..d4b63a22 --- /dev/null +++ b/tests/swift/swift-webview-config-allows-universal-file-access-swift-test.yml @@ -0,0 +1,11 @@ +id: swift-webview-config-allows-universal-file-access-swift +valid: + - | + let w2 = WKWebView(frame: .zero, configuration: config) + w2.configuration.setValue(false, forKey: "allowUniversalAccessFromFileURLs") +invalid: + - | + let w = WKWebView(frame: .zero, configuration: config) + w.setValue(true, forKey: "allowUniversalAccessFromFileURLs") + let config = w.configuration + config.setValue(true, forKey: "allowUniversalAccessFromFileURLs") \ No newline at end of file From 4d68bb3122a59787e894f45693c1bb7dd9bb5349 Mon Sep 17 00:00:00 2001 From: ESS-ENN Date: Fri, 6 Dec 2024 18:40:00 +0530 Subject: [PATCH 3/3] insecure-biometrics-swift --- .../security/insecure-biometrics-swift.yml | 54 +++++++++++++++ .../insecure-biometrics-swift-snapshot.yml | 68 +++++++++++++++++++ .../swift/insecure-biometrics-swift-test.yml | 32 +++++++++ 3 files changed, 154 insertions(+) create mode 100644 rules/swift/security/insecure-biometrics-swift.yml create mode 100644 tests/__snapshots__/insecure-biometrics-swift-snapshot.yml create mode 100644 tests/swift/insecure-biometrics-swift-test.yml diff --git a/rules/swift/security/insecure-biometrics-swift.yml b/rules/swift/security/insecure-biometrics-swift.yml new file mode 100644 index 00000000..31dce566 --- /dev/null +++ b/rules/swift/security/insecure-biometrics-swift.yml @@ -0,0 +1,54 @@ +id: insecure-biometrics-swift +language: swift +severity: info +message: >- + The application was observed to leverage biometrics via Local + Authentication, which returns a simple boolean result for authentication. + This design is subject to bypass with runtime tampering tools such as + Frida, Substrate, and others. Although this is limited to rooted + (jailbroken) devices, consider implementing biometric authentication the + reliable way - via Keychain Services. +note: >- + [CWE-305] Authentication Bypass by Primary Weakness + [REFERENCES] + - https://mobile-security.gitbook.io/mobile-security-testing-guide/ios-testing-guide/0x06f-testing-local-authentication + - https://shirazkhan030.medium.com/biometric-authentication-in-ios-6c53c54f17df + +rule: + kind: navigation_expression + pattern: $X.evaluatePolicy + +constraints: + X: + any: + - pattern: LAContext() + - kind: simple_identifier + inside: + stopBy: end + follows: + stopBy: end + kind: property_declaration + all: + - has: + stopBy: end + kind: simple_identifier + pattern: $X + - has: + stopBy: end + kind: call_expression + field: value + pattern: LAContext() + - kind: simple_identifier + follows: + stopBy: end + kind: property_declaration + all: + - has: + stopBy: end + kind: simple_identifier + pattern: $X + - has: + stopBy: end + kind: call_expression + field: value + pattern: LAContext() diff --git a/tests/__snapshots__/insecure-biometrics-swift-snapshot.yml b/tests/__snapshots__/insecure-biometrics-swift-snapshot.yml new file mode 100644 index 00000000..3d1dd7f9 --- /dev/null +++ b/tests/__snapshots__/insecure-biometrics-swift-snapshot.yml @@ -0,0 +1,68 @@ +id: insecure-biometrics-swift +snapshots: + ? | + let context = LAContext() + class Testing { + var name: LAContext + + init(name: LAContext) { + self.name = name + } + + func speak() { + // ruleid: insecure-biometrics + context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: "Authenticate to the application") { success, evaluationError in + guard success else { + // do... + } + } + } + : labels: + - source: context.evaluatePolicy + style: primary + start: 179 + end: 201 + - source: context + style: secondary + start: 4 + end: 11 + - source: LAContext() + style: secondary + start: 14 + end: 25 + - source: let context = LAContext() + style: secondary + start: 0 + end: 25 + - source: let context = LAContext() + style: secondary + start: 0 + end: 25 + ? | + let context = LAContext() + context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: "Authenticate to the application") { success, evaluationError in + guard success else { + // do... + } + } + : labels: + - source: context.evaluatePolicy + style: primary + start: 26 + end: 48 + - source: context + style: secondary + start: 4 + end: 11 + - source: LAContext() + style: secondary + start: 14 + end: 25 + - source: let context = LAContext() + style: secondary + start: 0 + end: 25 + - source: let context = LAContext() + style: secondary + start: 0 + end: 25 diff --git a/tests/swift/insecure-biometrics-swift-test.yml b/tests/swift/insecure-biometrics-swift-test.yml new file mode 100644 index 00000000..03f1be55 --- /dev/null +++ b/tests/swift/insecure-biometrics-swift-test.yml @@ -0,0 +1,32 @@ +id: insecure-biometrics-swift +valid: + - | + let context = LAContext() + guard context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) else { + // do... + } +invalid: + - | + let context = LAContext() + context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: "Authenticate to the application") { success, evaluationError in + guard success else { + // do... + } + } + - | + let context = LAContext() + class Testing { + var name: LAContext + + init(name: LAContext) { + self.name = name + } + + func speak() { + // ruleid: insecure-biometrics + context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: "Authenticate to the application") { success, evaluationError in + guard success else { + // do... + } + } + }