@reliabilityworks/ruleset-expo 1.0.0
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.
- package/package.json +4 -0
- package/rules.json +774 -0
package/package.json
ADDED
package/rules.json
ADDED
|
@@ -0,0 +1,774 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "expo/android-cleartext-traffic",
|
|
4
|
+
"severity": "high",
|
|
5
|
+
"title": "Cleartext traffic allowed on Android",
|
|
6
|
+
"description": "Allowing cleartext traffic can expose sensitive data over unencrypted connections.",
|
|
7
|
+
"matcher": {
|
|
8
|
+
"type": "regex",
|
|
9
|
+
"fileGlobs": ["**/app.json"],
|
|
10
|
+
"pattern": "usesCleartextTraffic\\\"?\\s*:\\s*true",
|
|
11
|
+
"message": "android.usesCleartextTraffic is enabled"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"id": "expo/android-allow-backup",
|
|
16
|
+
"severity": "medium",
|
|
17
|
+
"title": "Android backups allowed",
|
|
18
|
+
"description": "android.allowBackup can allow app data to be included in backups and restored to other devices.",
|
|
19
|
+
"matcher": {
|
|
20
|
+
"type": "regex",
|
|
21
|
+
"fileGlobs": ["**/app.json"],
|
|
22
|
+
"pattern": "allowBackup\\\"?\\s*:\\s*true",
|
|
23
|
+
"message": "android.allowBackup is enabled"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"id": "expo/android-request-legacy-external-storage",
|
|
28
|
+
"severity": "medium",
|
|
29
|
+
"title": "Legacy external storage requested",
|
|
30
|
+
"description": "requestLegacyExternalStorage can broaden file access on Android 10 and may increase data exposure.",
|
|
31
|
+
"matcher": {
|
|
32
|
+
"type": "regex",
|
|
33
|
+
"fileGlobs": ["**/app.json"],
|
|
34
|
+
"pattern": "requestLegacyExternalStorage\\\"?\\s*:\\s*true",
|
|
35
|
+
"message": "android.requestLegacyExternalStorage is enabled"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "expo/android-network-security-config",
|
|
40
|
+
"severity": "low",
|
|
41
|
+
"title": "Android network security config referenced",
|
|
42
|
+
"description": "Review network security config for cleartext exceptions and trust anchors.",
|
|
43
|
+
"matcher": {
|
|
44
|
+
"type": "regex",
|
|
45
|
+
"fileGlobs": ["**/app.json"],
|
|
46
|
+
"pattern": "networkSecurityConfig\\\"?\\s*:\\s*[^\\n]*@xml/",
|
|
47
|
+
"message": "android.networkSecurityConfig referenced"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"id": "expo/android-permission-camera",
|
|
52
|
+
"severity": "low",
|
|
53
|
+
"title": "Android CAMERA permission requested",
|
|
54
|
+
"description": "Requesting camera access increases privacy risk; ensure it is necessary and well scoped.",
|
|
55
|
+
"matcher": {
|
|
56
|
+
"type": "regex",
|
|
57
|
+
"fileGlobs": ["**/app.json"],
|
|
58
|
+
"pattern": "android\\.permission\\.CAMERA",
|
|
59
|
+
"message": "CAMERA permission requested"
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"id": "expo/android-permission-record-audio",
|
|
64
|
+
"severity": "low",
|
|
65
|
+
"title": "Android RECORD_AUDIO permission requested",
|
|
66
|
+
"description": "Requesting microphone access increases privacy risk; ensure it is necessary and well scoped.",
|
|
67
|
+
"matcher": {
|
|
68
|
+
"type": "regex",
|
|
69
|
+
"fileGlobs": ["**/app.json"],
|
|
70
|
+
"pattern": "android\\.permission\\.RECORD_AUDIO",
|
|
71
|
+
"message": "RECORD_AUDIO permission requested"
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"id": "expo/android-permission-read-sms",
|
|
76
|
+
"severity": "high",
|
|
77
|
+
"title": "Android READ_SMS permission requested",
|
|
78
|
+
"description": "SMS access is highly sensitive; avoid requesting READ_SMS unless strictly required.",
|
|
79
|
+
"matcher": {
|
|
80
|
+
"type": "regex",
|
|
81
|
+
"fileGlobs": ["**/app.json"],
|
|
82
|
+
"pattern": "android\\.permission\\.READ_SMS",
|
|
83
|
+
"message": "READ_SMS permission requested"
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"id": "expo/android-permission-send-sms",
|
|
88
|
+
"severity": "high",
|
|
89
|
+
"title": "Android SEND_SMS permission requested",
|
|
90
|
+
"description": "SMS sending can be abused for fraud; avoid requesting SEND_SMS unless strictly required.",
|
|
91
|
+
"matcher": {
|
|
92
|
+
"type": "regex",
|
|
93
|
+
"fileGlobs": ["**/app.json"],
|
|
94
|
+
"pattern": "android\\.permission\\.SEND_SMS",
|
|
95
|
+
"message": "SEND_SMS permission requested"
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"id": "expo/android-permission-read-contacts",
|
|
100
|
+
"severity": "medium",
|
|
101
|
+
"title": "Android READ_CONTACTS permission requested",
|
|
102
|
+
"description": "Contacts access is sensitive; ensure collection is necessary and minimized.",
|
|
103
|
+
"matcher": {
|
|
104
|
+
"type": "regex",
|
|
105
|
+
"fileGlobs": ["**/app.json"],
|
|
106
|
+
"pattern": "android\\.permission\\.READ_CONTACTS",
|
|
107
|
+
"message": "READ_CONTACTS permission requested"
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"id": "expo/android-permission-write-contacts",
|
|
112
|
+
"severity": "medium",
|
|
113
|
+
"title": "Android WRITE_CONTACTS permission requested",
|
|
114
|
+
"description": "Writing contacts is sensitive; ensure it is necessary and properly disclosed.",
|
|
115
|
+
"matcher": {
|
|
116
|
+
"type": "regex",
|
|
117
|
+
"fileGlobs": ["**/app.json"],
|
|
118
|
+
"pattern": "android\\.permission\\.WRITE_CONTACTS",
|
|
119
|
+
"message": "WRITE_CONTACTS permission requested"
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"id": "expo/android-permission-access-fine-location",
|
|
124
|
+
"severity": "medium",
|
|
125
|
+
"title": "Android ACCESS_FINE_LOCATION permission requested",
|
|
126
|
+
"description": "Fine location is sensitive. Ensure collection is necessary and minimized.",
|
|
127
|
+
"matcher": {
|
|
128
|
+
"type": "regex",
|
|
129
|
+
"fileGlobs": ["**/app.json"],
|
|
130
|
+
"pattern": "android\\.permission\\.ACCESS_FINE_LOCATION",
|
|
131
|
+
"message": "ACCESS_FINE_LOCATION permission requested"
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"id": "expo/android-permission-access-background-location",
|
|
136
|
+
"severity": "high",
|
|
137
|
+
"title": "Android ACCESS_BACKGROUND_LOCATION permission requested",
|
|
138
|
+
"description": "Background location is highly sensitive; avoid requesting unless strictly required.",
|
|
139
|
+
"matcher": {
|
|
140
|
+
"type": "regex",
|
|
141
|
+
"fileGlobs": ["**/app.json"],
|
|
142
|
+
"pattern": "android\\.permission\\.ACCESS_BACKGROUND_LOCATION",
|
|
143
|
+
"message": "ACCESS_BACKGROUND_LOCATION permission requested"
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"id": "expo/android-permission-read-phone-state",
|
|
148
|
+
"severity": "medium",
|
|
149
|
+
"title": "Android READ_PHONE_STATE permission requested",
|
|
150
|
+
"description": "Phone state access can be sensitive; ensure it is necessary and minimized.",
|
|
151
|
+
"matcher": {
|
|
152
|
+
"type": "regex",
|
|
153
|
+
"fileGlobs": ["**/app.json"],
|
|
154
|
+
"pattern": "android\\.permission\\.READ_PHONE_STATE",
|
|
155
|
+
"message": "READ_PHONE_STATE permission requested"
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"id": "expo/android-permission-read-call-log",
|
|
160
|
+
"severity": "high",
|
|
161
|
+
"title": "Android READ_CALL_LOG permission requested",
|
|
162
|
+
"description": "Call logs are highly sensitive; avoid requesting READ_CALL_LOG unless strictly required.",
|
|
163
|
+
"matcher": {
|
|
164
|
+
"type": "regex",
|
|
165
|
+
"fileGlobs": ["**/app.json"],
|
|
166
|
+
"pattern": "android\\.permission\\.READ_CALL_LOG",
|
|
167
|
+
"message": "READ_CALL_LOG permission requested"
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"id": "expo/android-permission-write-call-log",
|
|
172
|
+
"severity": "high",
|
|
173
|
+
"title": "Android WRITE_CALL_LOG permission requested",
|
|
174
|
+
"description": "Writing call logs is highly sensitive; avoid requesting WRITE_CALL_LOG unless strictly required.",
|
|
175
|
+
"matcher": {
|
|
176
|
+
"type": "regex",
|
|
177
|
+
"fileGlobs": ["**/app.json"],
|
|
178
|
+
"pattern": "android\\.permission\\.WRITE_CALL_LOG",
|
|
179
|
+
"message": "WRITE_CALL_LOG permission requested"
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"id": "expo/android-permission-manage-external-storage",
|
|
184
|
+
"severity": "high",
|
|
185
|
+
"title": "Android MANAGE_EXTERNAL_STORAGE permission requested",
|
|
186
|
+
"description": "All files access is highly sensitive and broad; avoid requesting unless strictly required.",
|
|
187
|
+
"matcher": {
|
|
188
|
+
"type": "regex",
|
|
189
|
+
"fileGlobs": ["**/app.json"],
|
|
190
|
+
"pattern": "android\\.permission\\.MANAGE_EXTERNAL_STORAGE",
|
|
191
|
+
"message": "MANAGE_EXTERNAL_STORAGE permission requested"
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"id": "expo/android-permission-system-alert-window",
|
|
196
|
+
"severity": "high",
|
|
197
|
+
"title": "Android SYSTEM_ALERT_WINDOW permission requested",
|
|
198
|
+
"description": "Overlay permission can enable phishing and UI redress attacks; avoid requesting unless strictly required.",
|
|
199
|
+
"matcher": {
|
|
200
|
+
"type": "regex",
|
|
201
|
+
"fileGlobs": ["**/app.json"],
|
|
202
|
+
"pattern": "android\\.permission\\.SYSTEM_ALERT_WINDOW",
|
|
203
|
+
"message": "SYSTEM_ALERT_WINDOW permission requested"
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"id": "expo/android-permission-request-install-packages",
|
|
208
|
+
"severity": "high",
|
|
209
|
+
"title": "Android REQUEST_INSTALL_PACKAGES permission requested",
|
|
210
|
+
"description": "Allowing package installs can enable sideloading and abuse; avoid requesting unless strictly required.",
|
|
211
|
+
"matcher": {
|
|
212
|
+
"type": "regex",
|
|
213
|
+
"fileGlobs": ["**/app.json"],
|
|
214
|
+
"pattern": "android\\.permission\\.REQUEST_INSTALL_PACKAGES",
|
|
215
|
+
"message": "REQUEST_INSTALL_PACKAGES permission requested"
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"id": "expo/android-permission-package-usage-stats",
|
|
220
|
+
"severity": "high",
|
|
221
|
+
"title": "Android PACKAGE_USAGE_STATS permission requested",
|
|
222
|
+
"description": "Usage stats can reveal sensitive user behavior; avoid requesting unless strictly required.",
|
|
223
|
+
"matcher": {
|
|
224
|
+
"type": "regex",
|
|
225
|
+
"fileGlobs": ["**/app.json"],
|
|
226
|
+
"pattern": "android\\.permission\\.PACKAGE_USAGE_STATS",
|
|
227
|
+
"message": "PACKAGE_USAGE_STATS permission requested"
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
"id": "expo/android-permission-bind-accessibility-service",
|
|
232
|
+
"severity": "high",
|
|
233
|
+
"title": "Android BIND_ACCESSIBILITY_SERVICE permission referenced",
|
|
234
|
+
"description": "Accessibility services can capture sensitive data; ensure appropriate justification and protections.",
|
|
235
|
+
"matcher": {
|
|
236
|
+
"type": "regex",
|
|
237
|
+
"fileGlobs": ["**/app.json"],
|
|
238
|
+
"pattern": "android\\.permission\\.BIND_ACCESSIBILITY_SERVICE",
|
|
239
|
+
"message": "BIND_ACCESSIBILITY_SERVICE permission referenced"
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
"id": "expo/ios-ats-arbitrary-loads",
|
|
244
|
+
"severity": "high",
|
|
245
|
+
"title": "ATS allows arbitrary loads",
|
|
246
|
+
"description": "NSAllowsArbitraryLoads disables ATS protections and can permit insecure HTTP connections.",
|
|
247
|
+
"matcher": {
|
|
248
|
+
"type": "regex",
|
|
249
|
+
"fileGlobs": ["**/app.json"],
|
|
250
|
+
"pattern": "NSAllowsArbitraryLoads\\\"?\\s*:\\s*true",
|
|
251
|
+
"message": "NSAllowsArbitraryLoads enabled"
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
"id": "expo/ios-ats-arbitrary-loads-in-webcontent",
|
|
256
|
+
"severity": "high",
|
|
257
|
+
"title": "ATS allows arbitrary loads in web content",
|
|
258
|
+
"description": "NSAllowsArbitraryLoadsInWebContent permits insecure loads inside WKWebView / embedded web content.",
|
|
259
|
+
"matcher": {
|
|
260
|
+
"type": "regex",
|
|
261
|
+
"fileGlobs": ["**/app.json"],
|
|
262
|
+
"pattern": "NSAllowsArbitraryLoadsInWebContent\\\"?\\s*:\\s*true",
|
|
263
|
+
"message": "NSAllowsArbitraryLoadsInWebContent enabled"
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
"id": "expo/ios-ats-arbitrary-loads-for-media",
|
|
268
|
+
"severity": "medium",
|
|
269
|
+
"title": "ATS allows arbitrary loads for media",
|
|
270
|
+
"description": "NSAllowsArbitraryLoadsForMedia allows insecure media loads; review necessity.",
|
|
271
|
+
"matcher": {
|
|
272
|
+
"type": "regex",
|
|
273
|
+
"fileGlobs": ["**/app.json"],
|
|
274
|
+
"pattern": "NSAllowsArbitraryLoadsForMedia\\\"?\\s*:\\s*true",
|
|
275
|
+
"message": "NSAllowsArbitraryLoadsForMedia enabled"
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
"id": "expo/ios-ats-arbitrary-loads-for-webcontent",
|
|
280
|
+
"severity": "medium",
|
|
281
|
+
"title": "ATS allows arbitrary loads for web content",
|
|
282
|
+
"description": "NSAllowsArbitraryLoadsForWebContent allows insecure web loads; review necessity.",
|
|
283
|
+
"matcher": {
|
|
284
|
+
"type": "regex",
|
|
285
|
+
"fileGlobs": ["**/app.json"],
|
|
286
|
+
"pattern": "NSAllowsArbitraryLoadsForWebContent\\\"?\\s*:\\s*true",
|
|
287
|
+
"message": "NSAllowsArbitraryLoadsForWebContent enabled"
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
"id": "expo/ios-ats-exception-insecure-http-loads",
|
|
292
|
+
"severity": "high",
|
|
293
|
+
"title": "ATS exception allows insecure HTTP loads",
|
|
294
|
+
"description": "NSExceptionAllowsInsecureHTTPLoads allows non-TLS traffic for exception domains.",
|
|
295
|
+
"matcher": {
|
|
296
|
+
"type": "regex",
|
|
297
|
+
"fileGlobs": ["**/app.json"],
|
|
298
|
+
"pattern": "NSExceptionAllowsInsecureHTTPLoads\\\"?\\s*:\\s*true",
|
|
299
|
+
"message": "NSExceptionAllowsInsecureHTTPLoads enabled"
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
"id": "expo/ios-ats-exception-minimum-tls-1-0",
|
|
304
|
+
"severity": "high",
|
|
305
|
+
"title": "ATS exception allows TLSv1.0",
|
|
306
|
+
"description": "Allowing TLSv1.0 weakens transport security; prefer TLS 1.2+.",
|
|
307
|
+
"matcher": {
|
|
308
|
+
"type": "regex",
|
|
309
|
+
"fileGlobs": ["**/app.json"],
|
|
310
|
+
"pattern": "NSExceptionMinimumTLSVersion\\\"?\\s*:\\s*[^\\n]*TLSv1\\.0",
|
|
311
|
+
"message": "NSExceptionMinimumTLSVersion set to TLSv1.0"
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
"id": "expo/ios-allows-local-networking",
|
|
316
|
+
"severity": "low",
|
|
317
|
+
"title": "ATS allows local networking",
|
|
318
|
+
"description": "Allowing local networking may be intended, but review for exposure to untrusted networks.",
|
|
319
|
+
"matcher": {
|
|
320
|
+
"type": "regex",
|
|
321
|
+
"fileGlobs": ["**/app.json"],
|
|
322
|
+
"pattern": "NSAllowsLocalNetworking\\\"?\\s*:\\s*true",
|
|
323
|
+
"message": "NSAllowsLocalNetworking enabled"
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
"id": "expo/ios-ui-filesharing-enabled",
|
|
328
|
+
"severity": "medium",
|
|
329
|
+
"title": "iOS UIFileSharingEnabled enabled",
|
|
330
|
+
"description": "Enabling iTunes File Sharing can expose app documents to users and desktop access.",
|
|
331
|
+
"matcher": {
|
|
332
|
+
"type": "regex",
|
|
333
|
+
"fileGlobs": ["**/app.json"],
|
|
334
|
+
"pattern": "UIFileSharingEnabled\\\"?\\s*:\\s*true",
|
|
335
|
+
"message": "UIFileSharingEnabled enabled"
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
"id": "expo/ios-documents-in-place",
|
|
340
|
+
"severity": "low",
|
|
341
|
+
"title": "iOS supports opening documents in place",
|
|
342
|
+
"description": "Opening documents in place can interact with external providers; review implications for sensitive files.",
|
|
343
|
+
"matcher": {
|
|
344
|
+
"type": "regex",
|
|
345
|
+
"fileGlobs": ["**/app.json"],
|
|
346
|
+
"pattern": "LSSupportsOpeningDocumentsInPlace\\\"?\\s*:\\s*true",
|
|
347
|
+
"message": "LSSupportsOpeningDocumentsInPlace enabled"
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
"id": "expo/ios-get-task-allow-enabled",
|
|
352
|
+
"severity": "high",
|
|
353
|
+
"title": "iOS get-task-allow enabled",
|
|
354
|
+
"description": "get-task-allow enables debugging/attaching; ensure it is disabled for production builds.",
|
|
355
|
+
"matcher": {
|
|
356
|
+
"type": "regex",
|
|
357
|
+
"fileGlobs": ["**/app.json"],
|
|
358
|
+
"pattern": "get-task-allow\\\"?\\s*:\\s*true",
|
|
359
|
+
"message": "get-task-allow is enabled"
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
"id": "expo/ios-associated-domains",
|
|
364
|
+
"severity": "low",
|
|
365
|
+
"title": "Associated domains configured",
|
|
366
|
+
"description": "Ensure associated domains are correct and validated to prevent deep link hijacking.",
|
|
367
|
+
"matcher": {
|
|
368
|
+
"type": "regex",
|
|
369
|
+
"fileGlobs": ["**/app.json"],
|
|
370
|
+
"pattern": "com\\.apple\\.developer\\.associated-domains",
|
|
371
|
+
"message": "Associated domains entitlement referenced"
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
"id": "expo/ios-keychain-access-groups",
|
|
376
|
+
"severity": "medium",
|
|
377
|
+
"title": "iOS keychain access groups configured",
|
|
378
|
+
"description": "Review keychain access groups for unnecessary sharing between apps/extensions.",
|
|
379
|
+
"matcher": {
|
|
380
|
+
"type": "regex",
|
|
381
|
+
"fileGlobs": ["**/app.json"],
|
|
382
|
+
"pattern": "keychain-access-groups",
|
|
383
|
+
"message": "keychain-access-groups referenced"
|
|
384
|
+
}
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
"id": "expo/ios-user-tracking-usage-description",
|
|
388
|
+
"severity": "medium",
|
|
389
|
+
"title": "NSUserTrackingUsageDescription present",
|
|
390
|
+
"description": "Tracking permission prompts indicate access to identifiers; ensure tracking is necessary and minimized.",
|
|
391
|
+
"matcher": {
|
|
392
|
+
"type": "regex",
|
|
393
|
+
"fileGlobs": ["**/app.json"],
|
|
394
|
+
"pattern": "NSUserTrackingUsageDescription",
|
|
395
|
+
"message": "NSUserTrackingUsageDescription present"
|
|
396
|
+
}
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
"id": "expo/updates-url-http",
|
|
400
|
+
"severity": "high",
|
|
401
|
+
"title": "Expo Updates URL uses http://",
|
|
402
|
+
"description": "Using http:// for update URLs can allow man-in-the-middle attacks. Prefer https://.",
|
|
403
|
+
"matcher": {
|
|
404
|
+
"type": "regex",
|
|
405
|
+
"fileGlobs": ["**/app.json"],
|
|
406
|
+
"pattern": "updates[\\s\\S]*url[\\s\\S]*http://",
|
|
407
|
+
"flags": "s",
|
|
408
|
+
"message": "updates.url appears to use http://"
|
|
409
|
+
}
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
"id": "expo/updates-fallback-cache-timeout-zero",
|
|
413
|
+
"severity": "low",
|
|
414
|
+
"title": "Expo Updates fallbackToCacheTimeout is 0",
|
|
415
|
+
"description": "A zero cache timeout can increase reliance on network fetches for updates; review for availability and integrity needs.",
|
|
416
|
+
"matcher": {
|
|
417
|
+
"type": "regex",
|
|
418
|
+
"fileGlobs": ["**/app.json"],
|
|
419
|
+
"pattern": "fallbackToCacheTimeout\\\"?\\s*:\\s*0",
|
|
420
|
+
"message": "fallbackToCacheTimeout is set to 0"
|
|
421
|
+
}
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
"id": "expo/extra-contains-secret",
|
|
425
|
+
"severity": "high",
|
|
426
|
+
"title": "Potential secret stored in app config extra",
|
|
427
|
+
"description": "Values under expo.extra can be bundled into the app. Avoid embedding secrets in app config.",
|
|
428
|
+
"matcher": {
|
|
429
|
+
"type": "regex",
|
|
430
|
+
"fileGlobs": ["**/app.json"],
|
|
431
|
+
"pattern": "\\b(extra)[\\s\\S]*\\b(secret|token|password)\\b",
|
|
432
|
+
"flags": "is",
|
|
433
|
+
"message": "expo.extra appears to contain secret-like keys"
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
"id": "expo/scheme-http",
|
|
438
|
+
"severity": "medium",
|
|
439
|
+
"title": "App scheme set to http",
|
|
440
|
+
"description": "Using http as an app scheme can collide with normal web URLs and may enable unexpected deep link handling.",
|
|
441
|
+
"matcher": {
|
|
442
|
+
"type": "regex",
|
|
443
|
+
"fileGlobs": ["**/app.json"],
|
|
444
|
+
"pattern": "scheme\\\"?\\s*:\\s*\"http\"",
|
|
445
|
+
"message": "App scheme is set to http"
|
|
446
|
+
}
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
"id": "expo/android-intentfilter-host-wildcard",
|
|
450
|
+
"severity": "high",
|
|
451
|
+
"title": "Android intent filter host wildcard",
|
|
452
|
+
"description": "Using a wildcard host in intent filters can route unexpected URLs into the app. Restrict to known hosts.",
|
|
453
|
+
"matcher": {
|
|
454
|
+
"type": "regex",
|
|
455
|
+
"fileGlobs": ["**/app.json"],
|
|
456
|
+
"pattern": "host\\\"?\\s*:\\s*\"\\*\"",
|
|
457
|
+
"message": "Android intent filter host appears to be a wildcard"
|
|
458
|
+
}
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
"id": "expo/android-intentfilter-autoverify",
|
|
462
|
+
"severity": "low",
|
|
463
|
+
"title": "Android intent filter autoVerify enabled",
|
|
464
|
+
"description": "autoVerify is normally used for verified links. Ensure the associated domains are correct and controlled.",
|
|
465
|
+
"matcher": {
|
|
466
|
+
"type": "regex",
|
|
467
|
+
"fileGlobs": ["**/app.json"],
|
|
468
|
+
"pattern": "autoVerify\\\"?\\s*:\\s*true",
|
|
469
|
+
"message": "Android intent filter autoVerify is enabled"
|
|
470
|
+
}
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
"id": "expo/ios-associated-domains-wildcard",
|
|
474
|
+
"severity": "high",
|
|
475
|
+
"title": "iOS associated domains wildcard",
|
|
476
|
+
"description": "Wildcard associated domains can enable deep link hijacking. Prefer explicit domains.",
|
|
477
|
+
"matcher": {
|
|
478
|
+
"type": "regex",
|
|
479
|
+
"fileGlobs": ["**/app.json"],
|
|
480
|
+
"pattern": "applinks:\\*",
|
|
481
|
+
"message": "Associated domains includes applinks:*"
|
|
482
|
+
}
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
"id": "expo/eas-production-development-client",
|
|
486
|
+
"severity": "high",
|
|
487
|
+
"title": "EAS production build enables developmentClient",
|
|
488
|
+
"description": "Shipping a development client can expose debugging and introspection capabilities.",
|
|
489
|
+
"matcher": {
|
|
490
|
+
"type": "regex",
|
|
491
|
+
"fileGlobs": ["**/eas.json"],
|
|
492
|
+
"pattern": "\"production\"[\\s\\S]*developmentClient\\\"?\\s*:\\s*true",
|
|
493
|
+
"flags": "s",
|
|
494
|
+
"message": "developmentClient: true under production build"
|
|
495
|
+
}
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
"id": "expo/eas-production-distribution-internal",
|
|
499
|
+
"severity": "medium",
|
|
500
|
+
"title": "EAS production build distribution is internal",
|
|
501
|
+
"description": "Internal distributions can bypass store review and may be used for broad internal release; ensure it is intended.",
|
|
502
|
+
"matcher": {
|
|
503
|
+
"type": "regex",
|
|
504
|
+
"fileGlobs": ["**/eas.json"],
|
|
505
|
+
"pattern": "\"production\"[\\s\\S]*distribution\\\"?\\s*:\\s*\"internal\"",
|
|
506
|
+
"flags": "s",
|
|
507
|
+
"message": "distribution: internal under production build"
|
|
508
|
+
}
|
|
509
|
+
},
|
|
510
|
+
{
|
|
511
|
+
"id": "expo/eas-credentials-source-local",
|
|
512
|
+
"severity": "medium",
|
|
513
|
+
"title": "EAS credentialsSource is local",
|
|
514
|
+
"description": "Local credentials can increase risk of secret leakage through repositories and developer machines.",
|
|
515
|
+
"matcher": {
|
|
516
|
+
"type": "regex",
|
|
517
|
+
"fileGlobs": ["**/eas.json"],
|
|
518
|
+
"pattern": "credentialsSource\\\"?\\s*:\\s*\"local\"",
|
|
519
|
+
"message": "credentialsSource is set to local"
|
|
520
|
+
}
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
"id": "expo/eas-android-buildtype-apk",
|
|
524
|
+
"severity": "low",
|
|
525
|
+
"title": "EAS Android buildType is apk",
|
|
526
|
+
"description": "APK distributions are easier to sideload than AAB; ensure this is intended for your release model.",
|
|
527
|
+
"matcher": {
|
|
528
|
+
"type": "regex",
|
|
529
|
+
"fileGlobs": ["**/eas.json"],
|
|
530
|
+
"pattern": "buildType\\\"?\\s*:\\s*\"apk\"",
|
|
531
|
+
"message": "Android buildType is set to apk"
|
|
532
|
+
}
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
"id": "expo/eas-ios-simulator-build",
|
|
536
|
+
"severity": "low",
|
|
537
|
+
"title": "EAS iOS simulator build enabled",
|
|
538
|
+
"description": "Simulator builds can be appropriate for testing; ensure simulator settings are not used for production distribution.",
|
|
539
|
+
"matcher": {
|
|
540
|
+
"type": "regex",
|
|
541
|
+
"fileGlobs": ["**/eas.json"],
|
|
542
|
+
"pattern": "simulator\\\"?\\s*:\\s*true",
|
|
543
|
+
"message": "iOS simulator build enabled"
|
|
544
|
+
}
|
|
545
|
+
},
|
|
546
|
+
{
|
|
547
|
+
"id": "expo/eas-env-openai-api-key",
|
|
548
|
+
"severity": "high",
|
|
549
|
+
"title": "OPENAI_API_KEY set in EAS env",
|
|
550
|
+
"description": "API keys should be handled carefully; ensure they are not leaked through logs or client bundles.",
|
|
551
|
+
"matcher": {
|
|
552
|
+
"type": "regex",
|
|
553
|
+
"fileGlobs": ["**/eas.json"],
|
|
554
|
+
"pattern": "OPENAI_API_KEY",
|
|
555
|
+
"message": "OPENAI_API_KEY appears in eas.json env"
|
|
556
|
+
}
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
"id": "expo/eas-env-sentry-auth-token",
|
|
560
|
+
"severity": "medium",
|
|
561
|
+
"title": "SENTRY_AUTH_TOKEN set in EAS env",
|
|
562
|
+
"description": "Sentry auth tokens are sensitive and should be protected.",
|
|
563
|
+
"matcher": {
|
|
564
|
+
"type": "regex",
|
|
565
|
+
"fileGlobs": ["**/eas.json"],
|
|
566
|
+
"pattern": "SENTRY_AUTH_TOKEN",
|
|
567
|
+
"message": "SENTRY_AUTH_TOKEN appears in eas.json env"
|
|
568
|
+
}
|
|
569
|
+
},
|
|
570
|
+
{
|
|
571
|
+
"id": "expo/eas-env-google-services-json",
|
|
572
|
+
"severity": "medium",
|
|
573
|
+
"title": "GOOGLE_SERVICES_JSON set in EAS env",
|
|
574
|
+
"description": "Google services configuration can include identifiers and should be handled carefully.",
|
|
575
|
+
"matcher": {
|
|
576
|
+
"type": "regex",
|
|
577
|
+
"fileGlobs": ["**/eas.json"],
|
|
578
|
+
"pattern": "GOOGLE_SERVICES_JSON",
|
|
579
|
+
"message": "GOOGLE_SERVICES_JSON appears in eas.json env"
|
|
580
|
+
}
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
"id": "expo/eas-env-stripe-secret-key",
|
|
584
|
+
"severity": "high",
|
|
585
|
+
"title": "STRIPE_SECRET_KEY set in EAS env",
|
|
586
|
+
"description": "Stripe secret keys must never be shipped to clients; ensure server-side usage only.",
|
|
587
|
+
"matcher": {
|
|
588
|
+
"type": "regex",
|
|
589
|
+
"fileGlobs": ["**/eas.json"],
|
|
590
|
+
"pattern": "STRIPE_SECRET_KEY",
|
|
591
|
+
"message": "STRIPE_SECRET_KEY appears in eas.json env"
|
|
592
|
+
}
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
"id": "expo/eas-env-aws-access-key-id",
|
|
596
|
+
"severity": "high",
|
|
597
|
+
"title": "AWS_ACCESS_KEY_ID set in EAS env",
|
|
598
|
+
"description": "AWS credentials should be protected and rotated if exposed.",
|
|
599
|
+
"matcher": {
|
|
600
|
+
"type": "regex",
|
|
601
|
+
"fileGlobs": ["**/eas.json"],
|
|
602
|
+
"pattern": "AWS_ACCESS_KEY_ID",
|
|
603
|
+
"message": "AWS_ACCESS_KEY_ID appears in eas.json env"
|
|
604
|
+
}
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
"id": "expo/eas-env-aws-secret-access-key",
|
|
608
|
+
"severity": "high",
|
|
609
|
+
"title": "AWS_SECRET_ACCESS_KEY set in EAS env",
|
|
610
|
+
"description": "AWS credentials should be protected and rotated if exposed.",
|
|
611
|
+
"matcher": {
|
|
612
|
+
"type": "regex",
|
|
613
|
+
"fileGlobs": ["**/eas.json"],
|
|
614
|
+
"pattern": "AWS_SECRET_ACCESS_KEY",
|
|
615
|
+
"message": "AWS_SECRET_ACCESS_KEY appears in eas.json env"
|
|
616
|
+
}
|
|
617
|
+
},
|
|
618
|
+
{
|
|
619
|
+
"id": "expo/eas-env-database-url",
|
|
620
|
+
"severity": "high",
|
|
621
|
+
"title": "DATABASE_URL set in EAS env",
|
|
622
|
+
"description": "Database URLs can include credentials; ensure they are not exposed in client bundles.",
|
|
623
|
+
"matcher": {
|
|
624
|
+
"type": "regex",
|
|
625
|
+
"fileGlobs": ["**/eas.json"],
|
|
626
|
+
"pattern": "DATABASE_URL",
|
|
627
|
+
"message": "DATABASE_URL appears in eas.json env"
|
|
628
|
+
}
|
|
629
|
+
},
|
|
630
|
+
{
|
|
631
|
+
"id": "expo/eas-env-jwt-secret",
|
|
632
|
+
"severity": "high",
|
|
633
|
+
"title": "JWT_SECRET set in EAS env",
|
|
634
|
+
"description": "JWT signing secrets must remain server-side and protected.",
|
|
635
|
+
"matcher": {
|
|
636
|
+
"type": "regex",
|
|
637
|
+
"fileGlobs": ["**/eas.json"],
|
|
638
|
+
"pattern": "JWT_SECRET",
|
|
639
|
+
"message": "JWT_SECRET appears in eas.json env"
|
|
640
|
+
}
|
|
641
|
+
},
|
|
642
|
+
{
|
|
643
|
+
"id": "expo/eas-env-private-key",
|
|
644
|
+
"severity": "high",
|
|
645
|
+
"title": "PRIVATE_KEY set in EAS env",
|
|
646
|
+
"description": "Private key material must be protected; ensure it is not committed and is stored securely.",
|
|
647
|
+
"matcher": {
|
|
648
|
+
"type": "regex",
|
|
649
|
+
"fileGlobs": ["**/eas.json"],
|
|
650
|
+
"pattern": "\\bPRIVATE_KEY\\b",
|
|
651
|
+
"message": "PRIVATE_KEY appears in eas.json env"
|
|
652
|
+
}
|
|
653
|
+
},
|
|
654
|
+
{
|
|
655
|
+
"id": "expo/eas-env-firebase-private-key",
|
|
656
|
+
"severity": "high",
|
|
657
|
+
"title": "FIREBASE_PRIVATE_KEY set in EAS env",
|
|
658
|
+
"description": "Firebase service account keys should be protected and rotated if exposed.",
|
|
659
|
+
"matcher": {
|
|
660
|
+
"type": "regex",
|
|
661
|
+
"fileGlobs": ["**/eas.json"],
|
|
662
|
+
"pattern": "FIREBASE_PRIVATE_KEY",
|
|
663
|
+
"message": "FIREBASE_PRIVATE_KEY appears in eas.json env"
|
|
664
|
+
}
|
|
665
|
+
},
|
|
666
|
+
{
|
|
667
|
+
"id": "expo/asyncstorage-token-key",
|
|
668
|
+
"severity": "high",
|
|
669
|
+
"title": "AsyncStorage stores auth token",
|
|
670
|
+
"description": "Storing authentication tokens in AsyncStorage is not encrypted by default.",
|
|
671
|
+
"matcher": {
|
|
672
|
+
"type": "regex",
|
|
673
|
+
"fileGlobs": ["**/*.{js,jsx,ts,tsx}"],
|
|
674
|
+
"pattern": "AsyncStorage\\.setItem\\s*\\(\\s*['\\\"](auth_)?token['\\\"]",
|
|
675
|
+
"message": "AsyncStorage.setItem() called with token key"
|
|
676
|
+
}
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
"id": "expo/insecure-storage-asyncstorage-setitem",
|
|
680
|
+
"severity": "medium",
|
|
681
|
+
"title": "AsyncStorage used to store data",
|
|
682
|
+
"description": "AsyncStorage is not encrypted by default; avoid storing secrets or tokens in plaintext.",
|
|
683
|
+
"matcher": {
|
|
684
|
+
"type": "regex",
|
|
685
|
+
"fileGlobs": ["**/*.{js,jsx,ts,tsx}"],
|
|
686
|
+
"pattern": "AsyncStorage\\.setItem\\s*\\(",
|
|
687
|
+
"message": "AsyncStorage.setItem() detected"
|
|
688
|
+
}
|
|
689
|
+
},
|
|
690
|
+
{
|
|
691
|
+
"id": "expo/insecure-deeplink-getinitialurl",
|
|
692
|
+
"severity": "medium",
|
|
693
|
+
"title": "Deep link URL retrieved",
|
|
694
|
+
"description": "Deep link URLs should be validated before use to prevent navigation to untrusted destinations.",
|
|
695
|
+
"matcher": {
|
|
696
|
+
"type": "regex",
|
|
697
|
+
"fileGlobs": ["**/*.{js,jsx,ts,tsx}"],
|
|
698
|
+
"pattern": "Linking\\.getInitialURL\\s*\\(",
|
|
699
|
+
"message": "Linking.getInitialURL() detected"
|
|
700
|
+
}
|
|
701
|
+
},
|
|
702
|
+
{
|
|
703
|
+
"id": "expo/insecure-deeplink-openurl",
|
|
704
|
+
"severity": "medium",
|
|
705
|
+
"title": "Deep link URL opened",
|
|
706
|
+
"description": "Opening URLs should be restricted/validated to avoid navigation to untrusted destinations.",
|
|
707
|
+
"matcher": {
|
|
708
|
+
"type": "regex",
|
|
709
|
+
"fileGlobs": ["**/*.{js,jsx,ts,tsx}"],
|
|
710
|
+
"pattern": "Linking\\.openURL\\s*\\(",
|
|
711
|
+
"message": "Linking.openURL() detected"
|
|
712
|
+
}
|
|
713
|
+
},
|
|
714
|
+
{
|
|
715
|
+
"id": "expo/webbrowser-openbrowserasync",
|
|
716
|
+
"severity": "low",
|
|
717
|
+
"title": "External browser opened",
|
|
718
|
+
"description": "Opening external URLs can be abused for phishing if destinations are not validated.",
|
|
719
|
+
"matcher": {
|
|
720
|
+
"type": "regex",
|
|
721
|
+
"fileGlobs": ["**/*.{js,jsx,ts,tsx}"],
|
|
722
|
+
"pattern": "WebBrowser\\.openBrowserAsync\\s*\\(",
|
|
723
|
+
"message": "WebBrowser.openBrowserAsync() detected"
|
|
724
|
+
}
|
|
725
|
+
},
|
|
726
|
+
{
|
|
727
|
+
"id": "expo/insecure-http-fetch",
|
|
728
|
+
"severity": "high",
|
|
729
|
+
"title": "HTTP request via fetch",
|
|
730
|
+
"description": "Using http:// in network requests can expose traffic to interception.",
|
|
731
|
+
"matcher": {
|
|
732
|
+
"type": "regex",
|
|
733
|
+
"fileGlobs": ["**/*.{js,jsx,ts,tsx}"],
|
|
734
|
+
"pattern": "fetch\\s*\\(\\s*['\\\"]http://",
|
|
735
|
+
"message": "fetch() called with http:// URL"
|
|
736
|
+
}
|
|
737
|
+
},
|
|
738
|
+
{
|
|
739
|
+
"id": "expo/insecure-logging-secrets",
|
|
740
|
+
"severity": "medium",
|
|
741
|
+
"title": "Potential secret logged",
|
|
742
|
+
"description": "Logging secrets (tokens/passwords) can leak sensitive data to logs and third-party crash reporters.",
|
|
743
|
+
"matcher": {
|
|
744
|
+
"type": "regex",
|
|
745
|
+
"fileGlobs": ["**/*.{js,jsx,ts,tsx}"],
|
|
746
|
+
"pattern": "console\\.log\\([^\\n]*(token|password|secret)",
|
|
747
|
+
"message": "console.log with potential secret keyword"
|
|
748
|
+
}
|
|
749
|
+
},
|
|
750
|
+
{
|
|
751
|
+
"id": "expo/insecure-crypto-random-by-math",
|
|
752
|
+
"severity": "low",
|
|
753
|
+
"title": "Non-cryptographic random referenced",
|
|
754
|
+
"description": "Avoid using Math.random for secrets, tokens, or identifiers.",
|
|
755
|
+
"matcher": {
|
|
756
|
+
"type": "regex",
|
|
757
|
+
"fileGlobs": ["**/*.{js,jsx,ts,tsx}"],
|
|
758
|
+
"pattern": "Math\\.random\\s*\\(",
|
|
759
|
+
"message": "Math.random() detected"
|
|
760
|
+
}
|
|
761
|
+
},
|
|
762
|
+
{
|
|
763
|
+
"id": "expo/expo-push-token",
|
|
764
|
+
"severity": "medium",
|
|
765
|
+
"title": "Expo push token retrieved",
|
|
766
|
+
"description": "Push tokens can be used to target devices; treat them as sensitive identifiers.",
|
|
767
|
+
"matcher": {
|
|
768
|
+
"type": "regex",
|
|
769
|
+
"fileGlobs": ["**/*.{js,jsx,ts,tsx}"],
|
|
770
|
+
"pattern": "getExpoPushTokenAsync",
|
|
771
|
+
"message": "getExpoPushTokenAsync usage detected"
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
]
|