runcloud 0.2.151 → 0.2.153

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.
@@ -151,7 +151,9 @@ function evaluateLocalItem(root, appJson, item) {
151
151
  return evaluateConfigItem(appJson, item, rule);
152
152
  switch (item.id) {
153
153
  case 'hardcoded_secrets':
154
- return patternItem(root, item, /sk-[A-Za-z0-9]{20,}|AIza[0-9A-Za-z_-]{30,}|-----BEGIN.*PRIVATE KEY|api[_-]?key\s*[:=]\s*['"][A-Za-z0-9]{16,}/i, 'fail', 'Hardcoded secret-like value found.', 'No secret-like values found.');
154
+ return patternItem(root, item, /sk-[A-Za-z0-9]{20,}|AIza[0-9A-Za-z_-]{30,}|-----BEGIN.*PRIVATE KEY|api[_-]?key\s*[:=]\s*['"][A-Za-z0-9]{16,}/i, 'fail', 'Hardcoded secret-like value found.', 'No secret-like values found.', {
155
+ filter: (match) => !isFirebaseClientKey(root, match),
156
+ });
155
157
  case 'http_endpoints':
156
158
  return patternItem(root, item, /http:\/\/(?!localhost|127\.0\.0\.1)/i, 'warning', 'Non-local HTTP endpoint found.', 'No non-local HTTP endpoints found.');
157
159
  case 'placeholder_content':
@@ -287,6 +289,16 @@ function patternItem(root, item, regex, statusOnHit, messageOnHit, messageOnMiss
287
289
  return makeItem(item, statusOnHit, messageOnHit, evidence(matches));
288
290
  return makeItem(item, 'pass', messageOnMiss, `scan: no matches for ${regex}`);
289
291
  }
292
+ const MICROPHONE_USE = /expo-av|AudioRecorder|requestRecordingPermissionsAsync|requestMicrophonePermissions/i;
293
+ function isFirebaseClientKey(root, match) {
294
+ if (!/AIza[0-9A-Za-z_-]{30,}/.test(match.text))
295
+ return false;
296
+ if (/(^|\/)(GoogleService-Info\.plist|google-services\.json)$/.test(match.file))
297
+ return true;
298
+ if (!/\bapiKey['"]?\s*:/.test(match.text))
299
+ return false;
300
+ return /initializeApp\s*\(|authDomain/.test(readText(path.join(root, match.file)) ?? '');
301
+ }
290
302
  function missingPermissionRequirements(root, appJson) {
291
303
  const missing = [...missingIosUsageDescriptions(root, appJson)];
292
304
  const permissions = arrayOfStrings(getPath(appJson, ['expo', 'android', 'permissions']));
@@ -295,7 +307,7 @@ function missingPermissionRequirements(root, appJson) {
295
307
  { name: 'camera', used: /expo-camera|CameraView|requestCameraPermissions/i, permissions: ['CAMERA'] },
296
308
  { name: 'location', used: /expo-location|getCurrentPositionAsync|watchPositionAsync/i, permissions: ['ACCESS_FINE_LOCATION', 'ACCESS_COARSE_LOCATION'] },
297
309
  { name: 'photos', used: /expo-image-picker|expo-media-library|MediaLibrary/i, permissions: ['READ_MEDIA_IMAGES', 'READ_EXTERNAL_STORAGE'] },
298
- { name: 'microphone', used: /expo-av|expo-audio|Audio\.|requestMicrophonePermissions/i, permissions: ['RECORD_AUDIO'] },
310
+ { name: 'microphone', used: MICROPHONE_USE, permissions: ['RECORD_AUDIO'] },
299
311
  { name: 'notifications', used: /expo-notifications/i, permissions: ['POST_NOTIFICATIONS'] },
300
312
  ];
301
313
  for (const check of androidChecks) {
@@ -314,13 +326,15 @@ function missingIosUsageDescriptions(root, appJson) {
314
326
  { name: 'camera', used: /expo-camera|CameraView|requestCameraPermissions/i, keys: ['NSCameraUsageDescription'] },
315
327
  { name: 'location', used: /expo-location|getCurrentPositionAsync|watchPositionAsync/i, keys: ['NSLocationWhenInUseUsageDescription', 'NSLocationAlwaysAndWhenInUseUsageDescription'] },
316
328
  { name: 'photos', used: /expo-image-picker|expo-media-library|MediaLibrary/i, keys: ['NSPhotoLibraryUsageDescription'] },
317
- { name: 'microphone', used: /expo-av|expo-audio|Audio\.|requestMicrophonePermissions/i, keys: ['NSMicrophoneUsageDescription'] },
329
+ { name: 'microphone', used: MICROPHONE_USE, keys: ['NSMicrophoneUsageDescription'] },
318
330
  ];
331
+ const pkg = readJson(path.join(root, 'package.json'));
332
+ const installed = new Set(['dependencies', 'devDependencies'].flatMap((field) => Object.keys(objectValue(getPath(pkg, [field])))));
319
333
  const missing = [];
320
334
  for (const check of checks) {
321
335
  if (!check.used.test(source))
322
336
  continue;
323
- if (!check.keys.some((key) => hasValue(infoPlist[key]))) {
337
+ if (!check.keys.some((key) => builtWithPurposeString(key, infoPlist, appJson, installed))) {
324
338
  missing.push(`ios ${check.name} usage description (${check.keys.join(' or ')})`);
325
339
  }
326
340
  }
@@ -333,6 +347,29 @@ function missingIosUsageDescriptions(root, appJson) {
333
347
  }
334
348
  return missing;
335
349
  }
350
+ const PLUGIN_PURPOSE_STRINGS = {
351
+ 'expo-camera': { auto: true, keys: { NSCameraUsageDescription: 'cameraPermission', NSMicrophoneUsageDescription: 'microphonePermission' } },
352
+ 'expo-image-picker': { auto: true, keys: { NSPhotoLibraryUsageDescription: 'photosPermission', NSCameraUsageDescription: 'cameraPermission', NSMicrophoneUsageDescription: 'microphonePermission' } },
353
+ 'expo-media-library': { auto: true, keys: { NSPhotoLibraryUsageDescription: 'photosPermission' } },
354
+ 'expo-av': { auto: true, keys: { NSMicrophoneUsageDescription: 'microphonePermission' } },
355
+ 'expo-location': { auto: true, keys: { NSLocationWhenInUseUsageDescription: 'locationWhenInUsePermission', NSLocationAlwaysAndWhenInUseUsageDescription: 'locationAlwaysAndWhenInUsePermission' } },
356
+ 'expo-audio': { auto: false, keys: { NSMicrophoneUsageDescription: 'microphonePermission' } },
357
+ };
358
+ function builtWithPurposeString(key, infoPlist, appJson, installed) {
359
+ let written = hasValue(infoPlist[key]);
360
+ for (const [name, plugin] of Object.entries(PLUGIN_PURPOSE_STRINGS)) {
361
+ const option = plugin.keys[key];
362
+ if (!option)
363
+ continue;
364
+ const listed = configPluginOptions(appJson, name);
365
+ if (!listed && !(plugin.auto && installed.has(name)))
366
+ continue;
367
+ if (listed?.[option] === false)
368
+ return false;
369
+ written = true;
370
+ }
371
+ return written;
372
+ }
336
373
  function configPluginOptions(appJson, name) {
337
374
  const plugins = getPath(appJson, ['expo', 'plugins']);
338
375
  if (!Array.isArray(plugins))
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const CLI_VERSION = '0.2.151';
1
+ export const CLI_VERSION = '0.2.153';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runcloud",
3
- "version": "0.2.151",
3
+ "version": "0.2.153",
4
4
  "description": "Create and control run.cloud remote mobile simulators and cloud sandboxes",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
Binary file
Binary file