narvy-cli 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- narvy/__init__.py +3 -0
- narvy/android/__init__.py +0 -0
- narvy/android/source_analyzer.py +268 -0
- narvy/android/split_bundle.py +475 -0
- narvy/android_rule_context.py +196 -0
- narvy/apk_memory_preflight.py +248 -0
- narvy/auth.py +40 -0
- narvy/ci_templates/bitbucket.yml +25 -0
- narvy/ci_templates/github.yml +60 -0
- narvy/ci_templates/gitlab.yml +32 -0
- narvy/cloud/__init__.py +0 -0
- narvy/cloud/aws_scan.py +1188 -0
- narvy/comment_filter.py +134 -0
- narvy/crypto_taint_lite.py +82 -0
- narvy/decompiler.py +337 -0
- narvy/doctor.py +244 -0
- narvy/host/__init__.py +0 -0
- narvy/host/audit.py +157 -0
- narvy/host/host_knowledge.py +982 -0
- narvy/host/lynis_bootstrap.py +400 -0
- narvy/host/lynis_parser.py +358 -0
- narvy/host/narvy_checks.py +789 -0
- narvy/host/report.py +69 -0
- narvy/host/ssh_exec.py +219 -0
- narvy/ios/__init__.py +0 -0
- narvy/ios/binary_analyzer.py +1201 -0
- narvy/ios/plist_checks.py +249 -0
- narvy/ios/source_analyzer.py +301 -0
- narvy/ios/third_party_filter.py +242 -0
- narvy/ios/trust_all_context.py +66 -0
- narvy/main.py +1983 -0
- narvy/native_hardening.py +503 -0
- narvy/reporter.py +151 -0
- narvy/rule_engine.py +57 -0
- narvy/rules/android/config.yml +77 -0
- narvy/rules/android/crypto.yml +34 -0
- narvy/rules/android/secrets.yml +161 -0
- narvy/rules/android/storage.yml +24 -0
- narvy/rules/android/webview.yml +23 -0
- narvy/rules/android.yml +219 -0
- narvy/rules/ios/objc/crypto.yml +56 -0
- narvy/rules/ios/objc/network.yml +67 -0
- narvy/rules/ios/objc/secrets.yml +79 -0
- narvy/rules/ios/objc/storage.yml +45 -0
- narvy/rules/ios/objc/webview.yml +45 -0
- narvy/rules/ios_swift.yml +327 -0
- narvy/rules/web/go.yml +2837 -0
- narvy/rules/web/java.yml +1576 -0
- narvy/rules/web/javascript.yml +3683 -0
- narvy/rules/web/kotlin.yml +413 -0
- narvy/rules/web/local/csharp_narvy/config.yml +61 -0
- narvy/rules/web/local/csharp_narvy/crypto.yml +64 -0
- narvy/rules/web/local/csharp_narvy/deserialization.yml +59 -0
- narvy/rules/web/local/csharp_narvy/injection.yml +122 -0
- narvy/rules/web/local/csharp_narvy/xxe.yml +48 -0
- narvy/rules/web/local/java_narvy/auth_jwt.yml +134 -0
- narvy/rules/web/local/java_narvy/deserialization.yml +108 -0
- narvy/rules/web/local/java_narvy/mybatis.yml +39 -0
- narvy/rules/web/local/java_narvy/snakeyaml.yml +34 -0
- narvy/rules/web/local/java_narvy/spring_authz.yml +32 -0
- narvy/rules/web/local/java_narvy/spring_config.yml +67 -0
- narvy/rules/web/local/java_narvy/spring_hardening.yml +291 -0
- narvy/rules/web/local/java_narvy/sqli.yml +235 -0
- narvy/rules/web/local/java_narvy/xxe.yml +212 -0
- narvy/rules/web/php.yml +1644 -0
- narvy/rules/web/python.yml +3967 -0
- narvy/rules/web/ruby.yml +703 -0
- narvy/rules/web/rust.yml +258 -0
- narvy/rules/web/secrets.yml +1420 -0
- narvy/rules/web/secrets_supplement.yml +383 -0
- narvy/sca/__init__.py +1 -0
- narvy/sca/android_deps.py +349 -0
- narvy/sca/ios_deps.py +578 -0
- narvy/sca/osv_client.py +617 -0
- narvy/sca/web_deps.py +955 -0
- narvy/scope_config.py +195 -0
- narvy/semgrep_engine.py +219 -0
- narvy/stack_protector_evidence.py +96 -0
- narvy/third_party_filter.py +211 -0
- narvy/uploader.py +92 -0
- narvy/weak_prng_context.py +270 -0
- narvy/web/__init__.py +0 -0
- narvy/web/nuclei_binary.py +105 -0
- narvy/web/scan_blocklist.py +96 -0
- narvy/web/scanner.py +842 -0
- narvy/web/source_analyzer.py +714 -0
- narvy/web/ssrf_guard.py +374 -0
- narvy_cli-1.0.0.dist-info/METADATA +165 -0
- narvy_cli-1.0.0.dist-info/RECORD +93 -0
- narvy_cli-1.0.0.dist-info/WHEEL +5 -0
- narvy_cli-1.0.0.dist-info/entry_points.txt +2 -0
- narvy_cli-1.0.0.dist-info/licenses/LICENSE +202 -0
- narvy_cli-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Objective-C transport-security checks.
|
|
2
|
+
|
|
3
|
+
- id: IOS-OBJC-NET-001
|
|
4
|
+
name: "Undocumented API Disables All HTTPS Certificate Validation"
|
|
5
|
+
pattern: 'allowsAnyHTTPSCertificateForHost'
|
|
6
|
+
severity: "CRITICAL"
|
|
7
|
+
confidence: "HIGH"
|
|
8
|
+
details:
|
|
9
|
+
cwe: "CWE-295"
|
|
10
|
+
masvs: "MSTG-NETWORK-3"
|
|
11
|
+
description: "setAllowsAnyHTTPSCertificateForHost:forHost: is a private/undocumented NSURLRequest API that disables ALL certificate validation for the given host. Its mere presence in a shipped binary is itself an App Store review red flag, and it is a trivial MITM path."
|
|
12
|
+
recommendation: "Remove this call entirely. Use NSURLSession with a proper URLSessionDelegate that validates the server trust (SecTrustEvaluateWithError), or implement certificate pinning."
|
|
13
|
+
|
|
14
|
+
- id: IOS-OBJC-NET-002
|
|
15
|
+
name: "NSURLConnection Accepts Any Certificate/Protection Space"
|
|
16
|
+
pattern: '(?s)canAuthenticateAgainstProtectionSpace\s*:.*?return\s+YES\s*;'
|
|
17
|
+
severity: "CRITICAL"
|
|
18
|
+
confidence: "MEDIUM"
|
|
19
|
+
details:
|
|
20
|
+
cwe: "CWE-295"
|
|
21
|
+
masvs: "MSTG-NETWORK-3"
|
|
22
|
+
description: "The NSURLConnection delegate's canAuthenticateAgainstProtectionSpace: unconditionally returns YES, meaning the connection will authenticate against ANY protection space/authentication method presented - including self-signed or attacker-controlled certificates."
|
|
23
|
+
recommendation: "Migrate to NSURLSession. If NSURLConnection must be kept, restrict this to only the expected authentication methods and validate the server trust before returning YES."
|
|
24
|
+
|
|
25
|
+
- id: IOS-OBJC-NET-003
|
|
26
|
+
name: "Continues Without Credential on Auth Challenge"
|
|
27
|
+
pattern: 'continueWithoutCredentialForAuthenticationChallenge\s*:'
|
|
28
|
+
severity: "HIGH"
|
|
29
|
+
confidence: "LOW"
|
|
30
|
+
details:
|
|
31
|
+
cwe: "CWE-295"
|
|
32
|
+
masvs: "MSTG-NETWORK-3"
|
|
33
|
+
description: "continueWithoutCredentialForAuthenticationChallenge: proceeds with the connection despite an unresolved authentication challenge (e.g. an untrusted server certificate). This is a low-confidence heuristic - it flags every call site, not just certificate-bypass usage; verify the surrounding logic manually."
|
|
34
|
+
recommendation: "Verify this is not being used to silently accept an untrusted server certificate. If it is, validate the server trust explicitly before deciding whether to proceed."
|
|
35
|
+
|
|
36
|
+
- id: IOS-OBJC-NET-004
|
|
37
|
+
name: "AFNetworking Certificate Validation Disabled"
|
|
38
|
+
pattern: 'allowInvalidCertificates\s*=\s*YES'
|
|
39
|
+
severity: "CRITICAL"
|
|
40
|
+
confidence: "HIGH"
|
|
41
|
+
details:
|
|
42
|
+
cwe: "CWE-295"
|
|
43
|
+
masvs: "MSTG-NETWORK-3"
|
|
44
|
+
description: "AFSecurityPolicy.allowInvalidCertificates is set to YES, which disables TLS certificate validation for this AFNetworking session - the app will accept any certificate, including a self-signed one from an attacker performing a MITM."
|
|
45
|
+
recommendation: "Set allowInvalidCertificates to NO (the default) in release builds. If pinning is required, use AFSecurityPolicy's pinning modes (AFSSLPinningModeCertificate/PublicKey) instead of disabling validation."
|
|
46
|
+
|
|
47
|
+
- id: IOS-OBJC-NET-005
|
|
48
|
+
name: "AFNetworking Domain Name Validation Disabled"
|
|
49
|
+
pattern: 'validatesDomainName\s*=\s*NO'
|
|
50
|
+
severity: "HIGH"
|
|
51
|
+
confidence: "HIGH"
|
|
52
|
+
details:
|
|
53
|
+
cwe: "CWE-297"
|
|
54
|
+
masvs: "MSTG-NETWORK-3"
|
|
55
|
+
description: "AFSecurityPolicy.validatesDomainName is set to NO, which disables hostname validation - a certificate for any domain will be accepted for this connection, enabling MITM even with a validly-signed certificate."
|
|
56
|
+
recommendation: "Leave validatesDomainName at its default (YES). If a specific case genuinely requires disabling it, pin the exact expected certificate/public key instead."
|
|
57
|
+
|
|
58
|
+
- id: IOS-OBJC-NET-006
|
|
59
|
+
name: "Hardcoded Cleartext HTTP URL"
|
|
60
|
+
pattern: '@"http://(?!localhost|127\.0\.0\.1)[^"]+"'
|
|
61
|
+
severity: "MEDIUM"
|
|
62
|
+
confidence: "LOW"
|
|
63
|
+
details:
|
|
64
|
+
cwe: "CWE-319"
|
|
65
|
+
masvs: "MSTG-NETWORK-1"
|
|
66
|
+
description: "A hardcoded http:// (non-TLS) URL literal is used. Traffic to this endpoint is sent in cleartext unless an ATS exception domain forces an upgrade. Low-confidence heuristic - some of these may be intentional (e.g. local dev servers, non-sensitive third-party feeds); verify manually."
|
|
67
|
+
recommendation: "Use https:// for any endpoint that carries sensitive data or app logic. Remove NSAllowsArbitraryLoads / per-domain HTTP exceptions from Info.plist for production builds."
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Objective-C hardcoded-credential checks.
|
|
2
|
+
|
|
3
|
+
- id: IOS-OBJC-S-001
|
|
4
|
+
name: "Hardcoded API Key"
|
|
5
|
+
pattern: '(?i)(api[_-]?key|api[_-]?secret)\s*=\s*@"[A-Za-z0-9+/=]{16,}"'
|
|
6
|
+
severity: "CRITICAL"
|
|
7
|
+
confidence: "HIGH"
|
|
8
|
+
details:
|
|
9
|
+
cwe: "CWE-798"
|
|
10
|
+
masvs: "MSTG-STORAGE-1"
|
|
11
|
+
description: "A hardcoded API key or secret token was found. Anyone who reads the source (or a source-available/leaked build) gains unauthorized access to whatever service this key authorizes."
|
|
12
|
+
recommendation: "Do not hardcode credentials. Retrieve them from a secure backend at runtime, or store them in the Keychain if they must persist on-device."
|
|
13
|
+
|
|
14
|
+
- id: IOS-OBJC-S-002
|
|
15
|
+
name: "Hardcoded Password"
|
|
16
|
+
pattern: '(?i)(password|passwd|pwd)\s*=\s*@"[^"]{6,}"\s*;'
|
|
17
|
+
severity: "CRITICAL"
|
|
18
|
+
confidence: "MEDIUM"
|
|
19
|
+
details:
|
|
20
|
+
cwe: "CWE-259"
|
|
21
|
+
masvs: "MSTG-STORAGE-1"
|
|
22
|
+
description: "A hardcoded password literal may have been found in the source code. Storing credentials directly in source is highly insecure."
|
|
23
|
+
recommendation: "Remove credentials from the code. Prompt the user or retrieve credentials from a secure backend/Keychain instead."
|
|
24
|
+
|
|
25
|
+
- id: IOS-OBJC-S-003
|
|
26
|
+
name: "Hardcoded Private Key"
|
|
27
|
+
# Require base64 key material after the header, so a bare "-----BEGIN PRIVATE KEY-----" marker constant is not flagged.
|
|
28
|
+
pattern: '-----BEGIN( RSA| EC| OPENSSH| DSA)? PRIVATE KEY-----[\r\n\s]+[A-Za-z0-9+/]{40,}'
|
|
29
|
+
severity: "CRITICAL"
|
|
30
|
+
confidence: "HIGH"
|
|
31
|
+
details:
|
|
32
|
+
cwe: "CWE-321"
|
|
33
|
+
masvs: "MSTG-CRYPTO-1"
|
|
34
|
+
description: "A private key block (e.g. RSA) was found embedded in the code. This completely compromises the security provided by the key."
|
|
35
|
+
recommendation: "Private keys should never ship inside the app bundle or source tree. Use the Keychain/Secure Enclave for on-device key material, or retrieve keys from a secure server at runtime."
|
|
36
|
+
|
|
37
|
+
- id: IOS-OBJC-S-004
|
|
38
|
+
name: "Hardcoded AWS Access Key ID"
|
|
39
|
+
pattern: 'AKIA[0-9A-Z]{16}'
|
|
40
|
+
severity: "CRITICAL"
|
|
41
|
+
confidence: "HIGH"
|
|
42
|
+
details:
|
|
43
|
+
cwe: "CWE-798"
|
|
44
|
+
masvs: "MSTG-STORAGE-1"
|
|
45
|
+
description: "An AWS Access Key ID was found hardcoded in the app. If paired with its secret key (also often nearby), this grants direct access to whatever AWS resources the key's IAM policy allows."
|
|
46
|
+
recommendation: "Revoke this key in the AWS IAM console immediately, then remove it from the code. Mobile apps should never hold long-lived AWS credentials - use temporary credentials via Cognito Identity Pools or a backend-issued token instead."
|
|
47
|
+
|
|
48
|
+
- id: IOS-OBJC-S-005
|
|
49
|
+
name: "Hardcoded Stripe Live Secret Key"
|
|
50
|
+
pattern: 'sk_live_[0-9a-zA-Z]{24,}'
|
|
51
|
+
severity: "CRITICAL"
|
|
52
|
+
confidence: "HIGH"
|
|
53
|
+
details:
|
|
54
|
+
cwe: "CWE-798"
|
|
55
|
+
masvs: "MSTG-STORAGE-1"
|
|
56
|
+
description: "A Stripe LIVE secret key was found hardcoded in the app. This key can create charges, issue refunds, and read all payment data on the connected Stripe account - a direct financial exposure, not just a data leak."
|
|
57
|
+
recommendation: "Roll this key immediately in the Stripe dashboard. Secret keys must never be shipped client-side; only publishable keys (pk_live_/pk_test_) belong in a mobile app, and all charge/refund logic must run server-side."
|
|
58
|
+
|
|
59
|
+
- id: IOS-OBJC-S-006
|
|
60
|
+
name: "Hardcoded JWT"
|
|
61
|
+
pattern: 'eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}'
|
|
62
|
+
severity: "MEDIUM"
|
|
63
|
+
confidence: "MEDIUM"
|
|
64
|
+
details:
|
|
65
|
+
cwe: "CWE-798"
|
|
66
|
+
masvs: "MSTG-STORAGE-1"
|
|
67
|
+
description: "A hardcoded JSON Web Token was found. If this is a real, live, long-lived token (as opposed to a test fixture), it grants whatever access its claims allow to anyone who reads the source."
|
|
68
|
+
recommendation: "Confirm whether this is test/fixture data. If it is a real token, revoke it and issue tokens dynamically per-session from the backend instead of embedding one statically."
|
|
69
|
+
|
|
70
|
+
- id: IOS-OBJC-S-007
|
|
71
|
+
name: "Credentials Embedded in URL"
|
|
72
|
+
pattern: 'https?://[^/\s:]+:[^/\s@"]+@'
|
|
73
|
+
severity: "HIGH"
|
|
74
|
+
confidence: "MEDIUM"
|
|
75
|
+
details:
|
|
76
|
+
cwe: "CWE-798"
|
|
77
|
+
masvs: "MSTG-STORAGE-1"
|
|
78
|
+
description: "A URL containing embedded username:password credentials (Basic Auth in the URL itself) was found. These credentials are visible in source, logs, and often browser/proxy history."
|
|
79
|
+
recommendation: "Remove credentials from the URL. Use an Authorization header set at request time from a securely stored credential, or better, an OAuth token flow."
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Objective-C local-storage checks.
|
|
2
|
+
|
|
3
|
+
- id: IOS-OBJC-STORAGE-001
|
|
4
|
+
name: "Sensitive Data Stored in NSUserDefaults"
|
|
5
|
+
pattern: '(?i)\[[\w\s.\[\]]*userdefaults[\w\s.\]]*\s+setObject\s*:[^;]*forKey\s*:\s*@"[^"]*(password|token|secret|api[_-]?key)[^"]*"'
|
|
6
|
+
severity: "HIGH"
|
|
7
|
+
confidence: "MEDIUM"
|
|
8
|
+
details:
|
|
9
|
+
cwe: "CWE-922"
|
|
10
|
+
masvs: "MSTG-STORAGE-1"
|
|
11
|
+
description: "A value is written to NSUserDefaults under a key that looks like a credential/token/secret. NSUserDefaults is a plaintext plist file, readable by anyone with filesystem access (jailbreak, backup extraction, or another process on a compromised device)."
|
|
12
|
+
recommendation: "Store credentials, tokens, and secrets in the Keychain (SecItemAdd with an appropriate kSecAttrAccessible level), never in NSUserDefaults."
|
|
13
|
+
|
|
14
|
+
- id: IOS-OBJC-STORAGE-002
|
|
15
|
+
name: "Insecure Keychain Accessibility (Always)"
|
|
16
|
+
pattern: 'kSecAttrAccessibleAlways(ThisDeviceOnly)?\b'
|
|
17
|
+
severity: "HIGH"
|
|
18
|
+
confidence: "HIGH"
|
|
19
|
+
details:
|
|
20
|
+
cwe: "CWE-922"
|
|
21
|
+
masvs: "MSTG-STORAGE-1"
|
|
22
|
+
description: "kSecAttrAccessibleAlways lets the Keychain item be read even while the device is locked. The non-'ThisDeviceOnly' variant additionally allows the item to be restored onto a different device from an iCloud/iTunes backup."
|
|
23
|
+
recommendation: "Use kSecAttrAccessibleWhenUnlockedThisDeviceOnly (or WhenPasscodeSetThisDeviceOnly for the strongest guarantee) unless the item genuinely must be available before first unlock."
|
|
24
|
+
|
|
25
|
+
- id: IOS-OBJC-STORAGE-003
|
|
26
|
+
name: "Sensitive Data Logged via NSLog"
|
|
27
|
+
pattern: '(?i)NSLog\s*\(\s*@"[^"]*(password|token|secret|api[_-]?key)[^"]*"'
|
|
28
|
+
severity: "MEDIUM"
|
|
29
|
+
confidence: "MEDIUM"
|
|
30
|
+
details:
|
|
31
|
+
cwe: "CWE-532"
|
|
32
|
+
masvs: "MSTG-STORAGE-3"
|
|
33
|
+
description: "NSLog output is readable via the device console, Console.app, and (on many crash reporters) uploaded with crash logs. Logging a value whose format string references a credential/token/secret risks leaking it."
|
|
34
|
+
recommendation: "Remove sensitive values from log statements entirely, or redact them (e.g. log only the last 4 characters) before logging."
|
|
35
|
+
|
|
36
|
+
- id: IOS-OBJC-STORAGE-004
|
|
37
|
+
name: "File Protection Explicitly Disabled"
|
|
38
|
+
pattern: 'NSFileProtectionNone\b'
|
|
39
|
+
severity: "MEDIUM"
|
|
40
|
+
confidence: "MEDIUM"
|
|
41
|
+
details:
|
|
42
|
+
cwe: "CWE-922"
|
|
43
|
+
masvs: "MSTG-STORAGE-1"
|
|
44
|
+
description: "NSFileProtectionNone explicitly disables Data Protection encryption for a file, making it readable even while the device is locked (e.g. after a jailbreak that bypasses the lock screen)."
|
|
45
|
+
recommendation: "Use NSFileProtectionCompleteUntilFirstUserAuthentication (the iOS default) or NSFileProtectionComplete for files containing sensitive data. Only use NSFileProtectionNone for genuinely non-sensitive files that must be accessible during background execution while locked."
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Objective-C WebView configuration checks.
|
|
2
|
+
|
|
3
|
+
- id: IOS-OBJC-WEBVIEW-001
|
|
4
|
+
name: "Deprecated UIWebView Usage"
|
|
5
|
+
pattern: '\bUIWebView\s*\*'
|
|
6
|
+
severity: "MEDIUM"
|
|
7
|
+
confidence: "HIGH"
|
|
8
|
+
details:
|
|
9
|
+
cwe: "CWE-1104"
|
|
10
|
+
masvs: "MSTG-PLATFORM-5"
|
|
11
|
+
description: "UIWebView is deprecated - Apple has rejected new App Store submissions using it since April 2020. It also lacks WKWebView's out-of-process JS execution and per-origin sandboxing, making JS-injection issues more impactful."
|
|
12
|
+
recommendation: "Migrate to WKWebView, which runs web content in a separate process and offers WKUserContentController for tighter control over injected scripts and message handlers."
|
|
13
|
+
|
|
14
|
+
- id: IOS-OBJC-WEBVIEW-002
|
|
15
|
+
name: "JavaScript Evaluated via String Concatenation"
|
|
16
|
+
pattern: 'stringByEvaluatingJavaScriptFromString\s*:\s*\[NSString stringWithFormat'
|
|
17
|
+
severity: "HIGH"
|
|
18
|
+
confidence: "MEDIUM"
|
|
19
|
+
details:
|
|
20
|
+
cwe: "CWE-79"
|
|
21
|
+
masvs: "MSTG-PLATFORM-5"
|
|
22
|
+
description: "JavaScript is built via stringWithFormat and evaluated in the WebView. If any interpolated value can be influenced by untrusted input (deep link, server response, user input), this is JavaScript injection into the WebView's context."
|
|
23
|
+
recommendation: "Avoid building JS strings via string formatting. If dynamic values must be passed into JS, JSON-encode them and use a fixed template, or use WKScriptMessageHandler for structured native<->JS communication instead."
|
|
24
|
+
|
|
25
|
+
- id: IOS-OBJC-WEBVIEW-003
|
|
26
|
+
name: "WebView Authentication Challenge Accepts Any Credential"
|
|
27
|
+
pattern: '(?s)webView\s*:\s*didReceiveAuthenticationChallenge\s*:.*?completionHandler\s*\(\s*NSURLSessionAuthChallengeUseCredential'
|
|
28
|
+
severity: "CRITICAL"
|
|
29
|
+
confidence: "MEDIUM"
|
|
30
|
+
details:
|
|
31
|
+
cwe: "CWE-295"
|
|
32
|
+
masvs: "MSTG-NETWORK-3"
|
|
33
|
+
description: "The WebView's authentication-challenge delegate method calls completionHandler with NSURLSessionAuthChallengeUseCredential, which accepts the credential/certificate offered without validating it - a trivial MITM path for content loaded in the WebView."
|
|
34
|
+
recommendation: "Validate the server trust (SecTrustEvaluateWithError against the expected certificate/public key) before calling completionHandler with UseCredential. Prefer NSURLSessionAuthChallengePerformDefaultHandling for the common case."
|
|
35
|
+
|
|
36
|
+
- id: IOS-OBJC-WEBVIEW-004
|
|
37
|
+
name: "Universal File Access From File URLs Enabled"
|
|
38
|
+
pattern: 'setAllowUniversalAccessFromFileURLs\s*:\s*YES'
|
|
39
|
+
severity: "HIGH"
|
|
40
|
+
confidence: "HIGH"
|
|
41
|
+
details:
|
|
42
|
+
cwe: "CWE-940"
|
|
43
|
+
masvs: "MSTG-PLATFORM-5"
|
|
44
|
+
description: "allowUniversalAccessFromFileURLs lets JavaScript running in a file:// context reach any origin, including making cross-origin requests and reading local files - a well-known path to universal XSS / local file exfiltration."
|
|
45
|
+
recommendation: "Leave allowUniversalAccessFromFileURLs at its default (NO/false) unless there is a specific, reviewed need. If local HTML must load remote resources, load it via a custom URL scheme handler instead."
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
# Swift source rules, authored by Narvy.
|
|
2
|
+
# Keychain, TLS, crypto, WebView, pasteboard, secrets.
|
|
3
|
+
|
|
4
|
+
rules:
|
|
5
|
+
- id: ios-swift-ecb-mode
|
|
6
|
+
languages: [swift]
|
|
7
|
+
severity: ERROR
|
|
8
|
+
message: >-
|
|
9
|
+
ECB mode is used for encryption (kCCOptionECBMode). ECB is insecure
|
|
10
|
+
because identical plaintext blocks encrypt to identical ciphertext
|
|
11
|
+
blocks, revealing patterns. Use GCM or CBC with a random IV instead.
|
|
12
|
+
metadata:
|
|
13
|
+
cwe: CWE-327
|
|
14
|
+
masvs: MSTG-CRYPTO-4
|
|
15
|
+
category: crypto
|
|
16
|
+
pattern-either:
|
|
17
|
+
- pattern: CCCrypt(..., kCCOptionECBMode, ...)
|
|
18
|
+
- pattern-regex: 'kCCOptionECBMode'
|
|
19
|
+
|
|
20
|
+
- id: ios-swift-weak-cipher
|
|
21
|
+
languages: [swift]
|
|
22
|
+
severity: ERROR
|
|
23
|
+
message: "DES/3DES/RC4 are broken ciphers (kCCAlgorithmDES/3DES/RC4). Use AES-256-GCM instead."
|
|
24
|
+
metadata:
|
|
25
|
+
cwe: CWE-327
|
|
26
|
+
masvs: MSTG-CRYPTO-4
|
|
27
|
+
category: crypto
|
|
28
|
+
pattern-regex: 'kCCAlgorithm(DES|3DES|RC4)\b'
|
|
29
|
+
|
|
30
|
+
- id: ios-swift-weak-hash
|
|
31
|
+
languages: [swift]
|
|
32
|
+
severity: WARNING
|
|
33
|
+
message: "MD5/SHA-1 are broken for security-sensitive hashing (CommonCrypto CC_MD5/CC_SHA1 or CryptoKit Insecure.MD5/Insecure.SHA1). Use SHA256+."
|
|
34
|
+
metadata:
|
|
35
|
+
cwe: CWE-328
|
|
36
|
+
masvs: MSTG-CRYPTO-4
|
|
37
|
+
category: crypto
|
|
38
|
+
pattern-either:
|
|
39
|
+
- pattern: CC_MD5(...)
|
|
40
|
+
- pattern: CC_SHA1(...)
|
|
41
|
+
- pattern: Insecure.MD5.hash(...)
|
|
42
|
+
- pattern: Insecure.SHA1.hash(...)
|
|
43
|
+
- pattern: Insecure.MD5()
|
|
44
|
+
- pattern: Insecure.SHA1()
|
|
45
|
+
|
|
46
|
+
- id: ios-swift-weak-prng
|
|
47
|
+
languages: [swift]
|
|
48
|
+
severity: ERROR
|
|
49
|
+
message: "rand()/random()/drand48() are not cryptographically secure. Use SecRandomCopyBytes or CryptoKit for security-sensitive values."
|
|
50
|
+
metadata:
|
|
51
|
+
cwe: CWE-338
|
|
52
|
+
masvs: MSTG-CRYPTO-6
|
|
53
|
+
category: crypto
|
|
54
|
+
pattern-either:
|
|
55
|
+
- pattern: rand()
|
|
56
|
+
- pattern: random()
|
|
57
|
+
- pattern: drand48()
|
|
58
|
+
- pattern: arc4random() % $N
|
|
59
|
+
|
|
60
|
+
- id: ios-swift-hardcoded-crypto-key
|
|
61
|
+
languages: [swift]
|
|
62
|
+
severity: ERROR
|
|
63
|
+
message: >-
|
|
64
|
+
A symmetric encryption key/passphrase is built directly from a string
|
|
65
|
+
literal (SymmetricKey/CCCrypt/CCCryptorCreate). Anyone who reads the
|
|
66
|
+
source (or decompiles a source-available build) recovers the exact
|
|
67
|
+
key used to encrypt/decrypt all protected data.
|
|
68
|
+
metadata:
|
|
69
|
+
cwe: CWE-321
|
|
70
|
+
masvs: MSTG-CRYPTO-1
|
|
71
|
+
category: crypto
|
|
72
|
+
pattern-either:
|
|
73
|
+
- pattern: 'SymmetricKey(data: Data("...".utf8))'
|
|
74
|
+
- pattern: 'SymmetricKey(data: "...".data(using: $ENC)!)'
|
|
75
|
+
- pattern: CCCrypt(..., "...", ...)
|
|
76
|
+
- pattern: CCCryptorCreate(..., "...", ...)
|
|
77
|
+
|
|
78
|
+
- id: ios-swift-trust-all-certs
|
|
79
|
+
languages: [swift]
|
|
80
|
+
severity: ERROR
|
|
81
|
+
message: >-
|
|
82
|
+
URLSession authentication challenge handler calls completionHandler
|
|
83
|
+
with .useCredential unconditionally - this accepts ANY server
|
|
84
|
+
certificate, disabling TLS validation entirely (trivial MITM).
|
|
85
|
+
metadata:
|
|
86
|
+
cwe: CWE-295
|
|
87
|
+
masvs: MSTG-NETWORK-3
|
|
88
|
+
category: network
|
|
89
|
+
pattern-either:
|
|
90
|
+
- pattern: 'completionHandler(.useCredential, URLCredential(trust: $T))'
|
|
91
|
+
- pattern: 'completionHandler(URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust: $T))'
|
|
92
|
+
|
|
93
|
+
- id: ios-swift-webview-js-untrusted-concat
|
|
94
|
+
languages: [swift]
|
|
95
|
+
severity: ERROR
|
|
96
|
+
message: >-
|
|
97
|
+
evaluateJavaScript() is called with a string built via concatenation
|
|
98
|
+
or interpolation instead of a fixed literal. If any part of the
|
|
99
|
+
string can be influenced by untrusted input (deep link, server
|
|
100
|
+
response, user input), this is JavaScript injection into the WebView.
|
|
101
|
+
metadata:
|
|
102
|
+
cwe: CWE-79
|
|
103
|
+
masvs: MSTG-PLATFORM-5
|
|
104
|
+
category: webview
|
|
105
|
+
patterns:
|
|
106
|
+
- pattern-either:
|
|
107
|
+
- pattern: $WV.evaluateJavaScript($A + $B, ...)
|
|
108
|
+
- pattern: $WV.evaluateJavaScript("...\($X)...", ...)
|
|
109
|
+
|
|
110
|
+
- id: ios-swift-uiwebview-deprecated
|
|
111
|
+
languages: [swift]
|
|
112
|
+
severity: WARNING
|
|
113
|
+
message: >-
|
|
114
|
+
UIWebView is deprecated (Apple rejects new App Store submissions using
|
|
115
|
+
it) and lacks WKWebView's process-isolation / modern security
|
|
116
|
+
protections (no per-origin sandboxing of JS, weaker WKUserContentController
|
|
117
|
+
controls). Migrate to WKWebView.
|
|
118
|
+
metadata:
|
|
119
|
+
cwe: CWE-1104
|
|
120
|
+
masvs: MSTG-PLATFORM-5
|
|
121
|
+
category: webview
|
|
122
|
+
pattern: UIWebView(...)
|
|
123
|
+
|
|
124
|
+
- id: ios-swift-log-sensitive-data
|
|
125
|
+
languages: [swift]
|
|
126
|
+
severity: WARNING
|
|
127
|
+
message: >-
|
|
128
|
+
A log statement (print/NSLog/os_log) includes a value whose name
|
|
129
|
+
looks like a credential/token/secret - readable via device console
|
|
130
|
+
logs, crash reporters, or (for print) even shipped in release builds
|
|
131
|
+
if not stripped.
|
|
132
|
+
metadata:
|
|
133
|
+
cwe: CWE-532
|
|
134
|
+
masvs: MSTG-STORAGE-3
|
|
135
|
+
category: logging
|
|
136
|
+
patterns:
|
|
137
|
+
- pattern-either:
|
|
138
|
+
- pattern: print($MSG)
|
|
139
|
+
- pattern: NSLog($MSG, ...)
|
|
140
|
+
- pattern: os_log($MSG, ...)
|
|
141
|
+
- metavariable-pattern:
|
|
142
|
+
metavariable: $MSG
|
|
143
|
+
patterns:
|
|
144
|
+
- pattern-regex: '(?i)(password|token|secret|api[_-]?key)'
|
|
145
|
+
|
|
146
|
+
- id: ios-swift-keychain-insecure-accessibility
|
|
147
|
+
languages: [swift]
|
|
148
|
+
severity: WARNING
|
|
149
|
+
message: >-
|
|
150
|
+
kSecAttrAccessibleAlways(ThisDeviceOnly) lets the Keychain item be
|
|
151
|
+
read even while the device is locked, and the non-"ThisDeviceOnly"
|
|
152
|
+
variants let it be restored onto a different device from an iCloud/
|
|
153
|
+
iTunes backup. Prefer kSecAttrAccessibleWhenUnlockedThisDeviceOnly.
|
|
154
|
+
metadata:
|
|
155
|
+
cwe: CWE-922
|
|
156
|
+
masvs: MSTG-STORAGE-1
|
|
157
|
+
category: storage
|
|
158
|
+
pattern-regex: 'kSecAttrAccessibleAlways\b'
|
|
159
|
+
|
|
160
|
+
- id: ios-swift-sql-injection
|
|
161
|
+
languages: [swift]
|
|
162
|
+
severity: ERROR
|
|
163
|
+
message: >-
|
|
164
|
+
SQL built via string concatenation/interpolation is passed to a raw
|
|
165
|
+
exec/query call (sqlite3_exec or an ORM's rawQuery-style API). Use
|
|
166
|
+
parameterized queries (bound placeholders) instead.
|
|
167
|
+
metadata:
|
|
168
|
+
cwe: CWE-89
|
|
169
|
+
masvs: MSTG-PLATFORM-2
|
|
170
|
+
category: injection
|
|
171
|
+
pattern-either:
|
|
172
|
+
- pattern: sqlite3_exec($DB, $A + $B, ...)
|
|
173
|
+
- pattern: sqlite3_exec($DB, "...\($X)...", ...)
|
|
174
|
+
- pattern: sqlite3_prepare_v2($DB, $A + $B, ...)
|
|
175
|
+
- pattern: sqlite3_prepare_v2($DB, "...\($X)...", ...)
|
|
176
|
+
- pattern: sqlite3_prepare($DB, "...\($X)...", ...)
|
|
177
|
+
- pattern: $DB.executeQuery("...\($X)...", ...)
|
|
178
|
+
- pattern: $DB.executeQuery($A + $B, ...)
|
|
179
|
+
- pattern: $DB.executeUpdate("...\($X)...", ...)
|
|
180
|
+
- pattern: $DB.executeUpdate($A + $B, ...)
|
|
181
|
+
|
|
182
|
+
- id: ios-swift-sql-injection-string-build
|
|
183
|
+
languages: [swift]
|
|
184
|
+
severity: ERROR
|
|
185
|
+
message: >-
|
|
186
|
+
A SQL statement (SELECT/INSERT/UPDATE/DELETE/...) injects a value via
|
|
187
|
+
Swift string interpolation (\(...)) or concatenation (+). If the
|
|
188
|
+
interpolated/concatenated value derives from user input this is SQL
|
|
189
|
+
injection. Use parameterized queries with bound placeholders instead.
|
|
190
|
+
metadata:
|
|
191
|
+
cwe: CWE-89
|
|
192
|
+
masvs: MSTG-PLATFORM-2
|
|
193
|
+
category: injection
|
|
194
|
+
pattern-regex: |-
|
|
195
|
+
(?i)"\s*(?:SELECT\b[^"\n]*?\bFROM\b|INSERT\s+INTO\b|UPDATE\b[^"\n]*?\bSET\b|DELETE\s+FROM\b|REPLACE\s+INTO\b|(?:DROP|ALTER|CREATE)\s+TABLE\b)(?:[^"\n]*?'[^'\n]*\\\(|[^"\n]*?(?:=|==|<>|!=|<=|>=|<|>|\bLIKE\b|\bGLOB\b)\s*\\\(|[^"\n]*?(?:=|==|<>|!=|<=|>=|<|>|\bLIKE\b|\bGLOB\b)\s*"\s*\+)
|
|
196
|
+
|
|
197
|
+
- id: ios-swift-cleartext-http-url
|
|
198
|
+
languages: [swift]
|
|
199
|
+
severity: WARNING
|
|
200
|
+
message: >-
|
|
201
|
+
A hardcoded http:// (non-TLS) URL literal is used. Traffic to this
|
|
202
|
+
endpoint is sent in cleartext unless ATS exception domains force it
|
|
203
|
+
to upgrade - verify this isn't a real network destination.
|
|
204
|
+
metadata:
|
|
205
|
+
cwe: CWE-319
|
|
206
|
+
masvs: MSTG-NETWORK-1
|
|
207
|
+
category: network
|
|
208
|
+
pattern-regex: '"http://(?!localhost|127\.0\.0\.1)[^"]+"'
|
|
209
|
+
|
|
210
|
+
# Hardcoded cloud / third-party credentials: fixed-prefix high-entropy tokens.
|
|
211
|
+
- id: ios-swift-hardcoded-aws-access-key
|
|
212
|
+
languages: [swift]
|
|
213
|
+
severity: ERROR
|
|
214
|
+
message: >-
|
|
215
|
+
An AWS Access Key ID (AKIA...) is hardcoded in Swift source. Paired
|
|
216
|
+
with its secret (often nearby), it grants direct access to whatever the
|
|
217
|
+
key's IAM policy allows. Revoke it in IAM now and remove it; mobile apps
|
|
218
|
+
should use temporary credentials (Cognito Identity Pools or a
|
|
219
|
+
backend-issued token), never a long-lived AWS key.
|
|
220
|
+
metadata:
|
|
221
|
+
cwe: CWE-798
|
|
222
|
+
masvs: MSTG-STORAGE-1
|
|
223
|
+
category: secrets
|
|
224
|
+
pattern-regex: '\bAKIA[0-9A-Z]{16}\b'
|
|
225
|
+
|
|
226
|
+
- id: ios-swift-hardcoded-google-api-key
|
|
227
|
+
languages: [swift]
|
|
228
|
+
severity: ERROR
|
|
229
|
+
message: >-
|
|
230
|
+
A Google API key (AIza...) is hardcoded in Swift source. Depending on
|
|
231
|
+
the key's API restrictions this can be abused for billed API calls or
|
|
232
|
+
data access. Restrict/rotate the key in the Google Cloud console and
|
|
233
|
+
load it from a secure backend instead of embedding it.
|
|
234
|
+
metadata:
|
|
235
|
+
cwe: CWE-798
|
|
236
|
+
masvs: MSTG-STORAGE-1
|
|
237
|
+
category: secrets
|
|
238
|
+
pattern-regex: '\bAIza[0-9A-Za-z\-_]{35}\b'
|
|
239
|
+
|
|
240
|
+
- id: ios-swift-hardcoded-stripe-secret-key
|
|
241
|
+
languages: [swift]
|
|
242
|
+
severity: ERROR
|
|
243
|
+
message: >-
|
|
244
|
+
A Stripe LIVE secret key (sk_live_...) is hardcoded in Swift source.
|
|
245
|
+
It can create charges, issue refunds and read all payment data - a
|
|
246
|
+
direct financial exposure. Roll it in the Stripe dashboard immediately;
|
|
247
|
+
only publishable keys (pk_live_/pk_test_) belong in a mobile app, and
|
|
248
|
+
charge/refund logic must run server-side.
|
|
249
|
+
metadata:
|
|
250
|
+
cwe: CWE-798
|
|
251
|
+
masvs: MSTG-STORAGE-1
|
|
252
|
+
category: secrets
|
|
253
|
+
pattern-regex: '\bsk_live_[0-9a-zA-Z]{24,}\b'
|
|
254
|
+
|
|
255
|
+
- id: ios-swift-hardcoded-github-token
|
|
256
|
+
languages: [swift]
|
|
257
|
+
severity: ERROR
|
|
258
|
+
message: >-
|
|
259
|
+
A GitHub personal-access token (ghp_...) is hardcoded in Swift source.
|
|
260
|
+
Anyone who reads it gains whatever repo/org access the token carries.
|
|
261
|
+
Revoke it in GitHub settings and remove it from the source tree.
|
|
262
|
+
metadata:
|
|
263
|
+
cwe: CWE-798
|
|
264
|
+
masvs: MSTG-STORAGE-1
|
|
265
|
+
category: secrets
|
|
266
|
+
pattern-regex: '\bghp_[A-Za-z0-9]{36}\b'
|
|
267
|
+
|
|
268
|
+
- id: ios-swift-hardcoded-slack-token
|
|
269
|
+
languages: [swift]
|
|
270
|
+
severity: ERROR
|
|
271
|
+
message: >-
|
|
272
|
+
A Slack token (xox[baprs]-...) is hardcoded in Swift source. It grants
|
|
273
|
+
whatever workspace access the token type allows. Revoke it in Slack and
|
|
274
|
+
remove it; load it from a secure backend if the app truly needs it.
|
|
275
|
+
metadata:
|
|
276
|
+
cwe: CWE-798
|
|
277
|
+
masvs: MSTG-STORAGE-1
|
|
278
|
+
category: secrets
|
|
279
|
+
pattern-regex: '\bxox[baprs]-[0-9A-Za-z-]{10,48}\b'
|
|
280
|
+
|
|
281
|
+
- id: ios-swift-hardcoded-private-key
|
|
282
|
+
languages: [swift]
|
|
283
|
+
severity: ERROR
|
|
284
|
+
message: >-
|
|
285
|
+
A PEM private-key block is embedded in Swift source. Shipping a private
|
|
286
|
+
key inside the app bundle fully compromises whatever it protects. Use
|
|
287
|
+
the Keychain / Secure Enclave for on-device key material, or fetch keys
|
|
288
|
+
from a secure server at runtime.
|
|
289
|
+
metadata:
|
|
290
|
+
cwe: CWE-321
|
|
291
|
+
masvs: MSTG-CRYPTO-1
|
|
292
|
+
category: secrets
|
|
293
|
+
# Require base64 key material after the header, so a bare "-----BEGIN PRIVATE KEY-----" marker constant is not flagged.
|
|
294
|
+
pattern-regex: '-----BEGIN( RSA| EC| OPENSSH| DSA)? PRIVATE KEY-----[\r\n\s]+[A-Za-z0-9+/]{40,}'
|
|
295
|
+
|
|
296
|
+
- id: ios-swift-credentials-in-url
|
|
297
|
+
languages: [swift]
|
|
298
|
+
severity: WARNING
|
|
299
|
+
message: >-
|
|
300
|
+
A URL with embedded username:password credentials (Basic Auth in the
|
|
301
|
+
URL) is hardcoded in Swift source. These credentials leak into logs,
|
|
302
|
+
proxies and crash reports. Use an Authorization header set at request
|
|
303
|
+
time from a securely stored credential, or an OAuth token flow.
|
|
304
|
+
metadata:
|
|
305
|
+
cwe: CWE-798
|
|
306
|
+
masvs: MSTG-STORAGE-1
|
|
307
|
+
category: secrets
|
|
308
|
+
pattern-regex: '"https?://[^/\s:"]+:[^/\s@"]+@'
|
|
309
|
+
|
|
310
|
+
- id: ios-swift-userdefaults-sensitive-data
|
|
311
|
+
languages: [swift]
|
|
312
|
+
severity: WARNING
|
|
313
|
+
message: >-
|
|
314
|
+
A value is written to UserDefaults under a key that looks like a
|
|
315
|
+
credential/token/secret. UserDefaults (plist-backed) is unencrypted
|
|
316
|
+
and readable by anyone with filesystem access (jailbreak, backup
|
|
317
|
+
extraction) - use the Keychain for secrets instead.
|
|
318
|
+
metadata:
|
|
319
|
+
cwe: CWE-922
|
|
320
|
+
masvs: MSTG-STORAGE-1
|
|
321
|
+
category: storage
|
|
322
|
+
patterns:
|
|
323
|
+
- pattern: '$UD.set($VALUE, forKey: $KEY)'
|
|
324
|
+
- metavariable-pattern:
|
|
325
|
+
metavariable: $KEY
|
|
326
|
+
patterns:
|
|
327
|
+
- pattern-regex: '(?i)"[^"]*(password|token|secret|api[_-]?key)[^"]*"'
|