shiplint 0.1.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.
Files changed (130) hide show
  1. package/README.md +107 -0
  2. package/dist/cli/index.d.ts +3 -0
  3. package/dist/cli/index.d.ts.map +1 -0
  4. package/dist/cli/index.js +101 -0
  5. package/dist/cli/index.js.map +1 -0
  6. package/dist/core/index.d.ts +5 -0
  7. package/dist/core/index.d.ts.map +1 -0
  8. package/dist/core/index.js +21 -0
  9. package/dist/core/index.js.map +1 -0
  10. package/dist/core/scanner.d.ts +27 -0
  11. package/dist/core/scanner.d.ts.map +1 -0
  12. package/dist/core/scanner.js +104 -0
  13. package/dist/core/scanner.js.map +1 -0
  14. package/dist/formatters/index.d.ts +13 -0
  15. package/dist/formatters/index.d.ts.map +1 -0
  16. package/dist/formatters/index.js +29 -0
  17. package/dist/formatters/index.js.map +1 -0
  18. package/dist/formatters/json.d.ts +13 -0
  19. package/dist/formatters/json.d.ts.map +1 -0
  20. package/dist/formatters/json.js +17 -0
  21. package/dist/formatters/json.js.map +1 -0
  22. package/dist/formatters/sarif.d.ts +14 -0
  23. package/dist/formatters/sarif.d.ts.map +1 -0
  24. package/dist/formatters/sarif.js +108 -0
  25. package/dist/formatters/sarif.js.map +1 -0
  26. package/dist/formatters/text.d.ts +9 -0
  27. package/dist/formatters/text.d.ts.map +1 -0
  28. package/dist/formatters/text.js +128 -0
  29. package/dist/formatters/text.js.map +1 -0
  30. package/dist/index.d.ts +11 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +49 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/parsers/entitlements-parser.d.ts +26 -0
  35. package/dist/parsers/entitlements-parser.d.ts.map +1 -0
  36. package/dist/parsers/entitlements-parser.js +105 -0
  37. package/dist/parsers/entitlements-parser.js.map +1 -0
  38. package/dist/parsers/framework-detector.d.ts +76 -0
  39. package/dist/parsers/framework-detector.d.ts.map +1 -0
  40. package/dist/parsers/framework-detector.js +501 -0
  41. package/dist/parsers/framework-detector.js.map +1 -0
  42. package/dist/parsers/index.d.ts +10 -0
  43. package/dist/parsers/index.d.ts.map +1 -0
  44. package/dist/parsers/index.js +26 -0
  45. package/dist/parsers/index.js.map +1 -0
  46. package/dist/parsers/pbxproj-parser.d.ts +166 -0
  47. package/dist/parsers/pbxproj-parser.d.ts.map +1 -0
  48. package/dist/parsers/pbxproj-parser.js +423 -0
  49. package/dist/parsers/pbxproj-parser.js.map +1 -0
  50. package/dist/parsers/plist-parser.d.ts +26 -0
  51. package/dist/parsers/plist-parser.d.ts.map +1 -0
  52. package/dist/parsers/plist-parser.js +166 -0
  53. package/dist/parsers/plist-parser.js.map +1 -0
  54. package/dist/parsers/project-parser.d.ts +57 -0
  55. package/dist/parsers/project-parser.d.ts.map +1 -0
  56. package/dist/parsers/project-parser.js +618 -0
  57. package/dist/parsers/project-parser.js.map +1 -0
  58. package/dist/parsers/workspace-parser.d.ts +82 -0
  59. package/dist/parsers/workspace-parser.d.ts.map +1 -0
  60. package/dist/parsers/workspace-parser.js +287 -0
  61. package/dist/parsers/workspace-parser.js.map +1 -0
  62. package/dist/rules/auth/index.d.ts +5 -0
  63. package/dist/rules/auth/index.d.ts.map +1 -0
  64. package/dist/rules/auth/index.js +9 -0
  65. package/dist/rules/auth/index.js.map +1 -0
  66. package/dist/rules/auth/third-party-login-no-siwa.d.ts +11 -0
  67. package/dist/rules/auth/third-party-login-no-siwa.d.ts.map +1 -0
  68. package/dist/rules/auth/third-party-login-no-siwa.js +119 -0
  69. package/dist/rules/auth/third-party-login-no-siwa.js.map +1 -0
  70. package/dist/rules/base.d.ts +25 -0
  71. package/dist/rules/base.d.ts.map +1 -0
  72. package/dist/rules/base.js +37 -0
  73. package/dist/rules/base.js.map +1 -0
  74. package/dist/rules/config/ats-exception-without-justification.d.ts +12 -0
  75. package/dist/rules/config/ats-exception-without-justification.d.ts.map +1 -0
  76. package/dist/rules/config/ats-exception-without-justification.js +152 -0
  77. package/dist/rules/config/ats-exception-without-justification.js.map +1 -0
  78. package/dist/rules/config/index.d.ts +5 -0
  79. package/dist/rules/config/index.d.ts.map +1 -0
  80. package/dist/rules/config/index.js +9 -0
  81. package/dist/rules/config/index.js.map +1 -0
  82. package/dist/rules/index.d.ts +43 -0
  83. package/dist/rules/index.d.ts.map +1 -0
  84. package/dist/rules/index.js +103 -0
  85. package/dist/rules/index.js.map +1 -0
  86. package/dist/rules/metadata/index.d.ts +5 -0
  87. package/dist/rules/metadata/index.d.ts.map +1 -0
  88. package/dist/rules/metadata/index.js +9 -0
  89. package/dist/rules/metadata/index.js.map +1 -0
  90. package/dist/rules/metadata/missing-privacy-manifest.d.ts +12 -0
  91. package/dist/rules/metadata/missing-privacy-manifest.d.ts.map +1 -0
  92. package/dist/rules/metadata/missing-privacy-manifest.js +186 -0
  93. package/dist/rules/metadata/missing-privacy-manifest.js.map +1 -0
  94. package/dist/rules/privacy/att-tracking-mismatch.d.ts +12 -0
  95. package/dist/rules/privacy/att-tracking-mismatch.d.ts.map +1 -0
  96. package/dist/rules/privacy/att-tracking-mismatch.js +113 -0
  97. package/dist/rules/privacy/att-tracking-mismatch.js.map +1 -0
  98. package/dist/rules/privacy/index.d.ts +11 -0
  99. package/dist/rules/privacy/index.d.ts.map +1 -0
  100. package/dist/rules/privacy/index.js +21 -0
  101. package/dist/rules/privacy/index.js.map +1 -0
  102. package/dist/rules/privacy/location-always-unjustified.d.ts +11 -0
  103. package/dist/rules/privacy/location-always-unjustified.d.ts.map +1 -0
  104. package/dist/rules/privacy/location-always-unjustified.js +102 -0
  105. package/dist/rules/privacy/location-always-unjustified.js.map +1 -0
  106. package/dist/rules/privacy/missing-camera-purpose.d.ts +11 -0
  107. package/dist/rules/privacy/missing-camera-purpose.d.ts.map +1 -0
  108. package/dist/rules/privacy/missing-camera-purpose.js +83 -0
  109. package/dist/rules/privacy/missing-camera-purpose.js.map +1 -0
  110. package/dist/rules/privacy/missing-contacts-purpose.d.ts +11 -0
  111. package/dist/rules/privacy/missing-contacts-purpose.d.ts.map +1 -0
  112. package/dist/rules/privacy/missing-contacts-purpose.js +85 -0
  113. package/dist/rules/privacy/missing-contacts-purpose.js.map +1 -0
  114. package/dist/rules/privacy/missing-location-purpose.d.ts +12 -0
  115. package/dist/rules/privacy/missing-location-purpose.d.ts.map +1 -0
  116. package/dist/rules/privacy/missing-location-purpose.js +137 -0
  117. package/dist/rules/privacy/missing-location-purpose.js.map +1 -0
  118. package/dist/rules/privacy/missing-microphone-purpose.d.ts +11 -0
  119. package/dist/rules/privacy/missing-microphone-purpose.d.ts.map +1 -0
  120. package/dist/rules/privacy/missing-microphone-purpose.js +132 -0
  121. package/dist/rules/privacy/missing-microphone-purpose.js.map +1 -0
  122. package/dist/rules/privacy/missing-photo-library-purpose.d.ts +11 -0
  123. package/dist/rules/privacy/missing-photo-library-purpose.d.ts.map +1 -0
  124. package/dist/rules/privacy/missing-photo-library-purpose.js +102 -0
  125. package/dist/rules/privacy/missing-photo-library-purpose.js.map +1 -0
  126. package/dist/types/index.d.ts +140 -0
  127. package/dist/types/index.d.ts.map +1 -0
  128. package/dist/types/index.js +59 -0
  129. package/dist/types/index.js.map +1 -0
  130. package/package.json +68 -0
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MissingLocationPurposeRule = void 0;
4
+ const index_js_1 = require("../../types/index.js");
5
+ const plist_parser_js_1 = require("../../parsers/plist-parser.js");
6
+ const base_js_1 = require("../base.js");
7
+ const LOCATION_FRAMEWORKS = ['CoreLocation', 'MapKit'];
8
+ const WHEN_IN_USE_KEY = 'NSLocationWhenInUseUsageDescription';
9
+ const ALWAYS_AND_WHEN_IN_USE_KEY = 'NSLocationAlwaysAndWhenInUseUsageDescription';
10
+ const ALWAYS_KEY = 'NSLocationAlwaysUsageDescription';
11
+ exports.MissingLocationPurposeRule = {
12
+ id: 'privacy-002-missing-location-purpose',
13
+ name: 'Missing Location Usage Description',
14
+ description: 'Checks for location framework usage without required usage descriptions',
15
+ category: index_js_1.RuleCategory.Privacy,
16
+ severity: index_js_1.Severity.Critical,
17
+ confidence: index_js_1.Confidence.High,
18
+ guidelineReference: '5.1.1',
19
+ async evaluate(context) {
20
+ // Check if any location-related framework is linked
21
+ const detectedFrameworks = LOCATION_FRAMEWORKS.filter(f => context.hasFramework(f));
22
+ if (detectedFrameworks.length === 0) {
23
+ return [];
24
+ }
25
+ const findings = [];
26
+ const whenInUseDescription = context.plistString(WHEN_IN_USE_KEY);
27
+ const hasAlwaysDescription = context.hasPlistKey(ALWAYS_KEY) ||
28
+ context.hasPlistKey(ALWAYS_AND_WHEN_IN_USE_KEY);
29
+ // Case 1: Completely missing WhenInUse description
30
+ if (whenInUseDescription === undefined) {
31
+ findings.push((0, base_js_1.makeFinding)(this, {
32
+ description: `Your app links against location-related frameworks (${detectedFrameworks.join(', ')}) ` +
33
+ `but Info.plist is missing NSLocationWhenInUseUsageDescription. Apps that access location services ` +
34
+ `must provide a purpose string explaining why access is needed.`,
35
+ location: 'Info.plist',
36
+ fixGuidance: `Add NSLocationWhenInUseUsageDescription to your Info.plist with a clear, user-facing explanation ` +
37
+ `of why your app needs location access. For example:
38
+
39
+ <key>NSLocationWhenInUseUsageDescription</key>
40
+ <string>We use your location to show nearby restaurants and provide directions.</string>
41
+
42
+ This key is required for any location access. The description should explain the specific feature ` +
43
+ `that uses location and be written from the user's perspective.`,
44
+ documentationURL: 'https://developer.apple.com/documentation/corelocation/requesting_authorization_to_use_location_services',
45
+ }));
46
+ }
47
+ // Case 2: WhenInUse description is empty
48
+ else if (whenInUseDescription.trim() === '') {
49
+ findings.push((0, base_js_1.makeFinding)(this, {
50
+ title: 'Empty Location Usage Description',
51
+ description: `NSLocationWhenInUseUsageDescription exists in Info.plist but is empty. ` +
52
+ `Apple requires a meaningful description explaining why your app needs location access.`,
53
+ location: 'Info.plist',
54
+ fixGuidance: `Update NSLocationWhenInUseUsageDescription with a clear, specific explanation of why your app ` +
55
+ `needs location access. Generic or empty descriptions will be rejected.
56
+
57
+ Good example: "Find coffee shops near your current location."
58
+ Bad example: "Location access required" or ""`,
59
+ documentationURL: 'https://developer.apple.com/documentation/corelocation/requesting_authorization_to_use_location_services',
60
+ }));
61
+ }
62
+ // Case 3: WhenInUse description is a placeholder
63
+ else if ((0, plist_parser_js_1.isPlaceholder)(whenInUseDescription)) {
64
+ findings.push((0, base_js_1.makeFinding)(this, {
65
+ title: 'Placeholder Location Usage Description',
66
+ description: `NSLocationWhenInUseUsageDescription appears to contain placeholder text: "${whenInUseDescription}". ` +
67
+ `Apple requires meaningful, user-facing descriptions.`,
68
+ location: 'Info.plist',
69
+ fixGuidance: `Replace the placeholder text with a clear explanation of why your app needs location access. ` +
70
+ `The description should be specific to your app's features.
71
+
72
+ Current value: "${whenInUseDescription}"
73
+
74
+ Write a description that helps users understand what feature uses location and why.`,
75
+ documentationURL: 'https://developer.apple.com/documentation/corelocation/requesting_authorization_to_use_location_services',
76
+ }));
77
+ }
78
+ // Check Always permission configuration
79
+ if (hasAlwaysDescription) {
80
+ const alwaysAndWhenInUseDescription = context.plistString(ALWAYS_AND_WHEN_IN_USE_KEY);
81
+ // iOS 11+ requires NSLocationAlwaysAndWhenInUseUsageDescription for Always permission
82
+ if (alwaysAndWhenInUseDescription === undefined) {
83
+ // Only flag if they have the legacy key but not the new one
84
+ if (context.hasPlistKey(ALWAYS_KEY)) {
85
+ findings.push((0, base_js_1.makeFinding)(this, {
86
+ title: 'Missing Always And When In Use Description',
87
+ description: `Your app has NSLocationAlwaysUsageDescription but is missing ` +
88
+ `NSLocationAlwaysAndWhenInUseUsageDescription. Since iOS 11, both keys are required ` +
89
+ `when requesting Always location permission.`,
90
+ location: 'Info.plist',
91
+ fixGuidance: `Add NSLocationAlwaysAndWhenInUseUsageDescription to your Info.plist. This key is required ` +
92
+ `for iOS 11+ when requesting Always permission.
93
+
94
+ <key>NSLocationWhenInUseUsageDescription</key>
95
+ <string>See nearby places while you're using the app.</string>
96
+
97
+ <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
98
+ <string>Send you alerts when you're near saved places, even when the app is closed.</string>
99
+
100
+ Note: Always permission is heavily scrutinized. Only request it if you have a visible, ` +
101
+ `continuous location feature like navigation or fitness tracking.`,
102
+ documentationURL: 'https://developer.apple.com/documentation/corelocation/choosing_the_location_services_authorization_to_request',
103
+ }));
104
+ }
105
+ }
106
+ // Case: Always description exists but is empty
107
+ else if (alwaysAndWhenInUseDescription.trim() === '') {
108
+ findings.push((0, base_js_1.makeFinding)(this, {
109
+ title: 'Empty Always Location Description',
110
+ description: `NSLocationAlwaysAndWhenInUseUsageDescription exists but is empty. ` +
111
+ `Apple requires a meaningful description for Always location access.`,
112
+ location: 'Info.plist',
113
+ fixGuidance: `Provide a clear explanation of why your app needs Always location access. ` +
114
+ `This should describe a user-facing feature that requires continuous location.
115
+
116
+ Example: "Track your runs in the background so you can see your route even ` +
117
+ `when the screen is off."`,
118
+ documentationURL: 'https://developer.apple.com/documentation/corelocation/choosing_the_location_services_authorization_to_request',
119
+ }));
120
+ }
121
+ // Case: Always description is a placeholder
122
+ else if ((0, plist_parser_js_1.isPlaceholder)(alwaysAndWhenInUseDescription)) {
123
+ findings.push((0, base_js_1.makeFinding)(this, {
124
+ title: 'Placeholder Always Location Description',
125
+ description: `NSLocationAlwaysAndWhenInUseUsageDescription contains placeholder text: ` +
126
+ `"${alwaysAndWhenInUseDescription}".`,
127
+ location: 'Info.plist',
128
+ fixGuidance: `Replace the placeholder with a real description of your continuous location feature. ` +
129
+ `Always permission requires a clear, user-visible justification.`,
130
+ documentationURL: 'https://developer.apple.com/documentation/corelocation/choosing_the_location_services_authorization_to_request',
131
+ }));
132
+ }
133
+ }
134
+ return findings;
135
+ },
136
+ };
137
+ //# sourceMappingURL=missing-location-purpose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing-location-purpose.js","sourceRoot":"","sources":["../../../src/rules/privacy/missing-location-purpose.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Rule: Missing Microphone Usage Description
3
+ *
4
+ * Detects when an app uses audio recording frameworks without the required
5
+ * NSMicrophoneUsageDescription in Info.plist.
6
+ *
7
+ * App Store Review Guideline: 5.1.1
8
+ */
9
+ import type { Rule } from '../../types/index.js';
10
+ export declare const MissingMicrophonePurposeRule: Rule;
11
+ //# sourceMappingURL=missing-microphone-purpose.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing-microphone-purpose.d.ts","sourceRoot":"","sources":["../../../src/rules/privacy/missing-microphone-purpose.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,IAAI,EAAwB,MAAM,sBAAsB,CAAC;AAUvE,eAAO,MAAM,4BAA4B,EAAE,IA+H1C,CAAC"}
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MissingMicrophonePurposeRule = void 0;
4
+ const index_js_1 = require("../../types/index.js");
5
+ const plist_parser_js_1 = require("../../parsers/plist-parser.js");
6
+ const base_js_1 = require("../base.js");
7
+ // Frameworks that definitely use microphone for recording
8
+ const MICROPHONE_FRAMEWORKS = ['AVFAudio', 'Speech'];
9
+ const MICROPHONE_KEY = 'NSMicrophoneUsageDescription';
10
+ const SPEECH_KEY = 'NSSpeechRecognitionUsageDescription';
11
+ exports.MissingMicrophonePurposeRule = {
12
+ id: 'privacy-005-missing-microphone-purpose',
13
+ name: 'Missing Microphone Usage Description',
14
+ description: 'Checks for audio recording framework usage without NSMicrophoneUsageDescription',
15
+ category: index_js_1.RuleCategory.Privacy,
16
+ severity: index_js_1.Severity.Critical,
17
+ confidence: index_js_1.Confidence.High,
18
+ guidelineReference: '5.1.1',
19
+ async evaluate(context) {
20
+ // Check if any audio recording framework is linked
21
+ const detectedFrameworks = MICROPHONE_FRAMEWORKS.filter(f => context.hasFramework(f));
22
+ // AVFoundation CAN record audio but is also used by virtually every app that plays video/audio.
23
+ // Only flag it with lower confidence when no other microphone frameworks are present.
24
+ const hasAVFoundation = context.hasFramework('AVFoundation');
25
+ const hasOnlyAVFoundation = hasAVFoundation && detectedFrameworks.length === 0;
26
+ if (detectedFrameworks.length === 0 && !hasAVFoundation) {
27
+ return [];
28
+ }
29
+ const findings = [];
30
+ const microphoneDescription = context.plistString(MICROPHONE_KEY);
31
+ const speechDescription = context.plistString(SPEECH_KEY);
32
+ // Determine which frameworks to report
33
+ const frameworksToReport = hasAVFoundation
34
+ ? [...detectedFrameworks, 'AVFoundation']
35
+ : detectedFrameworks;
36
+ // If Speech framework is linked, check for speech recognition description
37
+ if (context.hasFramework('Speech')) {
38
+ if (speechDescription === undefined) {
39
+ findings.push((0, base_js_1.makeFinding)(this, {
40
+ title: 'Missing Speech Recognition Usage Description',
41
+ description: `Your app links against the Speech framework but Info.plist is missing ` +
42
+ `NSSpeechRecognitionUsageDescription. Apps using speech recognition must provide a ` +
43
+ `purpose string explaining why access is needed.`,
44
+ location: 'Info.plist',
45
+ fixGuidance: `Add NSSpeechRecognitionUsageDescription to your Info.plist:
46
+
47
+ <key>NSSpeechRecognitionUsageDescription</key>
48
+ <string>We use speech recognition to transcribe your voice notes.</string>
49
+
50
+ Note: You'll also need NSMicrophoneUsageDescription since speech recognition requires microphone access.`,
51
+ documentationURL: 'https://developer.apple.com/documentation/bundleresources/information_property_list/nsspeechrecognitionusagedescription',
52
+ }));
53
+ }
54
+ else if (speechDescription.trim() === '') {
55
+ findings.push((0, base_js_1.makeFinding)(this, {
56
+ title: 'Empty Speech Recognition Usage Description',
57
+ description: `NSSpeechRecognitionUsageDescription exists but is empty.`,
58
+ location: 'Info.plist',
59
+ fixGuidance: `Provide a meaningful description for speech recognition usage.`,
60
+ documentationURL: 'https://developer.apple.com/documentation/bundleresources/information_property_list/nsspeechrecognitionusagedescription',
61
+ }));
62
+ }
63
+ else if ((0, plist_parser_js_1.isPlaceholder)(speechDescription)) {
64
+ findings.push((0, base_js_1.makeFinding)(this, {
65
+ title: 'Placeholder Speech Recognition Usage Description',
66
+ description: `NSSpeechRecognitionUsageDescription contains placeholder text: "${speechDescription}".`,
67
+ location: 'Info.plist',
68
+ fixGuidance: `Replace the placeholder with a real description of your speech recognition feature.`,
69
+ documentationURL: 'https://developer.apple.com/documentation/bundleresources/information_property_list/nsspeechrecognitionusagedescription',
70
+ }));
71
+ }
72
+ }
73
+ // When only AVFoundation is detected (no AVFAudio/Speech), use Medium confidence
74
+ // since AVFoundation is commonly used for audio/video playback, not just recording
75
+ const confidenceLevel = hasOnlyAVFoundation ? index_js_1.Confidence.Medium : index_js_1.Confidence.High;
76
+ const avFoundationCaveat = hasOnlyAVFoundation
77
+ ? `\n\nNote: AVFoundation is commonly used for audio/video playback. If your app only plays ` +
78
+ `media and doesn't record audio, you may not need this permission.`
79
+ : '';
80
+ // Case 1: Completely missing microphone description
81
+ if (microphoneDescription === undefined) {
82
+ findings.push((0, base_js_1.makeCustomFinding)(this, this.severity, confidenceLevel, {
83
+ title: 'Missing Microphone Usage Description',
84
+ description: `Your app links against audio frameworks (${frameworksToReport.join(', ')}) ` +
85
+ `but Info.plist is missing NSMicrophoneUsageDescription. Apps that access the microphone ` +
86
+ `must provide a purpose string explaining why access is needed.${avFoundationCaveat}`,
87
+ location: 'Info.plist',
88
+ fixGuidance: `Add NSMicrophoneUsageDescription to your Info.plist with a clear, user-facing explanation ` +
89
+ `of why your app needs microphone access. For example:
90
+
91
+ <key>NSMicrophoneUsageDescription</key>
92
+ <string>We need microphone access to record voice messages and make calls.</string>
93
+
94
+ The description should explain the specific feature that uses the microphone.`,
95
+ documentationURL: 'https://developer.apple.com/documentation/bundleresources/information_property_list/nsmicrophoneusagedescription',
96
+ }));
97
+ }
98
+ // Case 2: Empty description
99
+ else if (microphoneDescription.trim() === '') {
100
+ findings.push((0, base_js_1.makeCustomFinding)(this, this.severity, confidenceLevel, {
101
+ title: 'Empty Microphone Usage Description',
102
+ description: `NSMicrophoneUsageDescription exists in Info.plist but is empty. ` +
103
+ `Apple requires a meaningful description explaining why your app needs microphone access.${avFoundationCaveat}`,
104
+ location: 'Info.plist',
105
+ fixGuidance: `Update NSMicrophoneUsageDescription with a clear, specific explanation of why your app ` +
106
+ `needs microphone access. Generic or empty descriptions will be rejected.
107
+
108
+ Good example: "Record audio for your video messages."
109
+ Bad example: "Microphone access required" or ""`,
110
+ documentationURL: 'https://developer.apple.com/documentation/bundleresources/information_property_list/nsmicrophoneusagedescription',
111
+ }));
112
+ }
113
+ // Case 3: Placeholder text detected
114
+ else if ((0, plist_parser_js_1.isPlaceholder)(microphoneDescription)) {
115
+ findings.push((0, base_js_1.makeCustomFinding)(this, this.severity, confidenceLevel, {
116
+ title: 'Placeholder Microphone Usage Description',
117
+ description: `NSMicrophoneUsageDescription appears to contain placeholder text: "${microphoneDescription}". ` +
118
+ `Apple requires meaningful, user-facing descriptions.${avFoundationCaveat}`,
119
+ location: 'Info.plist',
120
+ fixGuidance: `Replace the placeholder text with a clear explanation of why your app needs microphone access. ` +
121
+ `The description should be specific to your app's features.
122
+
123
+ Current value: "${microphoneDescription}"
124
+
125
+ Write a description that helps users understand what feature uses the microphone and why.`,
126
+ documentationURL: 'https://developer.apple.com/documentation/bundleresources/information_property_list/nsmicrophoneusagedescription',
127
+ }));
128
+ }
129
+ return findings;
130
+ },
131
+ };
132
+ //# sourceMappingURL=missing-microphone-purpose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing-microphone-purpose.js","sourceRoot":"","sources":["../../../src/rules/privacy/missing-microphone-purpose.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Rule: Missing Photo Library Usage Description
3
+ *
4
+ * Detects when an app uses Photos framework without the required
5
+ * NSPhotoLibraryUsageDescription in Info.plist.
6
+ *
7
+ * App Store Review Guideline: 5.1.1
8
+ */
9
+ import type { Rule } from '../../types/index.js';
10
+ export declare const MissingPhotoLibraryPurposeRule: Rule;
11
+ //# sourceMappingURL=missing-photo-library-purpose.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing-photo-library-purpose.d.ts","sourceRoot":"","sources":["../../../src/rules/privacy/missing-photo-library-purpose.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,IAAI,EAAwB,MAAM,sBAAsB,CAAC;AASvE,eAAO,MAAM,8BAA8B,EAAE,IAgG5C,CAAC"}
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MissingPhotoLibraryPurposeRule = void 0;
4
+ const index_js_1 = require("../../types/index.js");
5
+ const plist_parser_js_1 = require("../../parsers/plist-parser.js");
6
+ const base_js_1 = require("../base.js");
7
+ const PHOTO_LIBRARY_FRAMEWORKS = ['Photos', 'PhotosUI'];
8
+ const PHOTO_LIBRARY_KEY = 'NSPhotoLibraryUsageDescription';
9
+ const PHOTO_LIBRARY_ADD_KEY = 'NSPhotoLibraryAddUsageDescription';
10
+ exports.MissingPhotoLibraryPurposeRule = {
11
+ id: 'privacy-004-missing-photo-library-purpose',
12
+ name: 'Missing Photo Library Usage Description',
13
+ description: 'Checks for Photos framework usage without NSPhotoLibraryUsageDescription',
14
+ category: index_js_1.RuleCategory.Privacy,
15
+ severity: index_js_1.Severity.Critical,
16
+ confidence: index_js_1.Confidence.High,
17
+ guidelineReference: '5.1.1',
18
+ async evaluate(context) {
19
+ // Check if any Photos-related framework is linked
20
+ const detectedFrameworks = PHOTO_LIBRARY_FRAMEWORKS.filter(f => context.hasFramework(f));
21
+ if (detectedFrameworks.length === 0) {
22
+ return [];
23
+ }
24
+ const findings = [];
25
+ const photoLibraryDescription = context.plistString(PHOTO_LIBRARY_KEY);
26
+ const photoLibraryAddDescription = context.plistString(PHOTO_LIBRARY_ADD_KEY);
27
+ // Case 1: Completely missing read access description
28
+ if (photoLibraryDescription === undefined) {
29
+ findings.push((0, base_js_1.makeFinding)(this, {
30
+ description: `Your app links against photo library frameworks (${detectedFrameworks.join(', ')}) ` +
31
+ `but Info.plist is missing NSPhotoLibraryUsageDescription. Apps that access the photo library ` +
32
+ `must provide a purpose string explaining why access is needed.`,
33
+ location: 'Info.plist',
34
+ fixGuidance: `Add NSPhotoLibraryUsageDescription to your Info.plist with a clear, user-facing explanation ` +
35
+ `of why your app needs photo library access. For example:
36
+
37
+ <key>NSPhotoLibraryUsageDescription</key>
38
+ <string>We need access to your photos to let you select images for your posts.</string>
39
+
40
+ If your app only needs to save photos (not read), you can use NSPhotoLibraryAddUsageDescription instead.`,
41
+ documentationURL: 'https://developer.apple.com/documentation/bundleresources/information_property_list/nsphotolibraryusagedescription',
42
+ }));
43
+ }
44
+ // Case 2: Empty description
45
+ else if (photoLibraryDescription.trim() === '') {
46
+ findings.push((0, base_js_1.makeFinding)(this, {
47
+ title: 'Empty Photo Library Usage Description',
48
+ description: `NSPhotoLibraryUsageDescription exists in Info.plist but is empty. ` +
49
+ `Apple requires a meaningful description explaining why your app needs photo library access.`,
50
+ location: 'Info.plist',
51
+ fixGuidance: `Update NSPhotoLibraryUsageDescription with a clear, specific explanation of why your app ` +
52
+ `needs photo library access. Generic or empty descriptions will be rejected.
53
+
54
+ Good example: "Choose photos to share with your friends."
55
+ Bad example: "Photo access required" or ""`,
56
+ documentationURL: 'https://developer.apple.com/documentation/bundleresources/information_property_list/nsphotolibraryusagedescription',
57
+ }));
58
+ }
59
+ // Case 3: Placeholder text detected
60
+ else if ((0, plist_parser_js_1.isPlaceholder)(photoLibraryDescription)) {
61
+ findings.push((0, base_js_1.makeFinding)(this, {
62
+ title: 'Placeholder Photo Library Usage Description',
63
+ description: `NSPhotoLibraryUsageDescription appears to contain placeholder text: "${photoLibraryDescription}". ` +
64
+ `Apple requires meaningful, user-facing descriptions.`,
65
+ location: 'Info.plist',
66
+ fixGuidance: `Replace the placeholder text with a clear explanation of why your app needs photo library access. ` +
67
+ `The description should be specific to your app's features.
68
+
69
+ Current value: "${photoLibraryDescription}"
70
+
71
+ Write a description that helps users understand what feature uses the photo library and why.`,
72
+ documentationURL: 'https://developer.apple.com/documentation/bundleresources/information_property_list/nsphotolibraryusagedescription',
73
+ }));
74
+ }
75
+ // Also check for add-only permission if it exists but is empty/placeholder
76
+ if (photoLibraryAddDescription !== undefined) {
77
+ if (photoLibraryAddDescription.trim() === '') {
78
+ findings.push((0, base_js_1.makeFinding)(this, {
79
+ title: 'Empty Photo Library Add Usage Description',
80
+ description: `NSPhotoLibraryAddUsageDescription exists in Info.plist but is empty. ` +
81
+ `Apple requires a meaningful description explaining why your app needs to save photos.`,
82
+ location: 'Info.plist',
83
+ fixGuidance: `Update NSPhotoLibraryAddUsageDescription with a clear explanation of why your app needs to save photos.
84
+
85
+ Good example: "Save edited photos to your library."`,
86
+ documentationURL: 'https://developer.apple.com/documentation/bundleresources/information_property_list/nsphotolibraryaddusagedescription',
87
+ }));
88
+ }
89
+ else if ((0, plist_parser_js_1.isPlaceholder)(photoLibraryAddDescription)) {
90
+ findings.push((0, base_js_1.makeFinding)(this, {
91
+ title: 'Placeholder Photo Library Add Usage Description',
92
+ description: `NSPhotoLibraryAddUsageDescription contains placeholder text: "${photoLibraryAddDescription}".`,
93
+ location: 'Info.plist',
94
+ fixGuidance: `Replace the placeholder with a real description of why your app needs to save photos.`,
95
+ documentationURL: 'https://developer.apple.com/documentation/bundleresources/information_property_list/nsphotolibraryaddusagedescription',
96
+ }));
97
+ }
98
+ }
99
+ return findings;
100
+ },
101
+ };
102
+ //# sourceMappingURL=missing-photo-library-purpose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing-photo-library-purpose.js","sourceRoot":"","sources":["../../../src/rules/privacy/missing-photo-library-purpose.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * TypeScript interfaces for ReviewShield
3
+ */
4
+ /**
5
+ * Severity level of a finding
6
+ */
7
+ export declare enum Severity {
8
+ Critical = "critical",
9
+ High = "high",
10
+ Medium = "medium",
11
+ Low = "low",
12
+ Info = "info"
13
+ }
14
+ /**
15
+ * Confidence level of a finding
16
+ */
17
+ export declare enum Confidence {
18
+ High = "high",
19
+ Medium = "medium",
20
+ Low = "low"
21
+ }
22
+ /**
23
+ * Category of a rule
24
+ */
25
+ export declare enum RuleCategory {
26
+ Privacy = "privacy",
27
+ Auth = "auth",
28
+ Entitlements = "entitlements",
29
+ Performance = "performance",
30
+ Content = "content",
31
+ Metadata = "metadata",
32
+ Config = "config"
33
+ }
34
+ /**
35
+ * Source of a dependency
36
+ */
37
+ export declare enum DependencySource {
38
+ CocoaPods = "cocoapods",
39
+ SPM = "spm",
40
+ Carthage = "carthage",
41
+ Manual = "manual"
42
+ }
43
+ /**
44
+ * A third-party dependency
45
+ */
46
+ export interface Dependency {
47
+ name: string;
48
+ version?: string;
49
+ source: DependencySource;
50
+ }
51
+ /**
52
+ * A single finding from a rule evaluation
53
+ */
54
+ export interface Finding {
55
+ ruleId: string;
56
+ severity: Severity;
57
+ confidence: Confidence;
58
+ title: string;
59
+ description: string;
60
+ location?: string;
61
+ guideline: string;
62
+ fixGuidance: string;
63
+ documentationURL?: string;
64
+ }
65
+ /**
66
+ * Result of a scan
67
+ */
68
+ export interface ScanResult {
69
+ projectPath: string;
70
+ timestamp: Date;
71
+ findings: Finding[];
72
+ rulesRun: string[];
73
+ duration: number;
74
+ }
75
+ /**
76
+ * Parsed Info.plist data
77
+ */
78
+ export interface ParsedInfoPlist {
79
+ bundleIdentifier?: string;
80
+ displayName?: string;
81
+ bundleName?: string;
82
+ minimumOSVersion?: string;
83
+ requiredDeviceCapabilities: string[];
84
+ backgroundModes: string[];
85
+ usageDescriptions: Record<string, string>;
86
+ raw: Record<string, unknown>;
87
+ }
88
+ /**
89
+ * Scan context containing all parsed project data
90
+ */
91
+ export interface ScanContext {
92
+ projectPath: string;
93
+ infoPlist: Record<string, unknown>;
94
+ entitlements: Record<string, unknown>;
95
+ linkedFrameworks: Set<string>;
96
+ dependencies: Dependency[];
97
+ plistString(key: string): string | undefined;
98
+ plistArray(key: string): unknown[] | undefined;
99
+ plistBool(key: string): boolean | undefined;
100
+ hasPlistKey(key: string): boolean;
101
+ hasFramework(name: string): boolean;
102
+ hasEntitlement(key: string): boolean;
103
+ entitlementString(key: string): string | undefined;
104
+ entitlementArray(key: string): unknown[] | undefined;
105
+ }
106
+ /**
107
+ * Rule interface - each rule must implement this
108
+ */
109
+ export interface Rule {
110
+ id: string;
111
+ name: string;
112
+ description: string;
113
+ category: RuleCategory;
114
+ severity: Severity;
115
+ confidence: Confidence;
116
+ guidelineReference: string;
117
+ /**
118
+ * Evaluate the rule against the scan context
119
+ */
120
+ evaluate(context: ScanContext): Promise<Finding[]>;
121
+ }
122
+ /**
123
+ * Output format options
124
+ */
125
+ export declare enum OutputFormat {
126
+ Text = "text",
127
+ JSON = "json",
128
+ SARIF = "sarif"
129
+ }
130
+ /**
131
+ * CLI options
132
+ */
133
+ export interface ScanOptions {
134
+ path: string;
135
+ format?: OutputFormat;
136
+ verbose?: boolean;
137
+ rules?: string[];
138
+ exclude?: string[];
139
+ }
140
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ /**
3
+ * TypeScript interfaces for ReviewShield
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.OutputFormat = exports.DependencySource = exports.RuleCategory = exports.Confidence = exports.Severity = void 0;
7
+ /**
8
+ * Severity level of a finding
9
+ */
10
+ var Severity;
11
+ (function (Severity) {
12
+ Severity["Critical"] = "critical";
13
+ Severity["High"] = "high";
14
+ Severity["Medium"] = "medium";
15
+ Severity["Low"] = "low";
16
+ Severity["Info"] = "info";
17
+ })(Severity || (exports.Severity = Severity = {}));
18
+ /**
19
+ * Confidence level of a finding
20
+ */
21
+ var Confidence;
22
+ (function (Confidence) {
23
+ Confidence["High"] = "high";
24
+ Confidence["Medium"] = "medium";
25
+ Confidence["Low"] = "low";
26
+ })(Confidence || (exports.Confidence = Confidence = {}));
27
+ /**
28
+ * Category of a rule
29
+ */
30
+ var RuleCategory;
31
+ (function (RuleCategory) {
32
+ RuleCategory["Privacy"] = "privacy";
33
+ RuleCategory["Auth"] = "auth";
34
+ RuleCategory["Entitlements"] = "entitlements";
35
+ RuleCategory["Performance"] = "performance";
36
+ RuleCategory["Content"] = "content";
37
+ RuleCategory["Metadata"] = "metadata";
38
+ RuleCategory["Config"] = "config";
39
+ })(RuleCategory || (exports.RuleCategory = RuleCategory = {}));
40
+ /**
41
+ * Source of a dependency
42
+ */
43
+ var DependencySource;
44
+ (function (DependencySource) {
45
+ DependencySource["CocoaPods"] = "cocoapods";
46
+ DependencySource["SPM"] = "spm";
47
+ DependencySource["Carthage"] = "carthage";
48
+ DependencySource["Manual"] = "manual";
49
+ })(DependencySource || (exports.DependencySource = DependencySource = {}));
50
+ /**
51
+ * Output format options
52
+ */
53
+ var OutputFormat;
54
+ (function (OutputFormat) {
55
+ OutputFormat["Text"] = "text";
56
+ OutputFormat["JSON"] = "json";
57
+ OutputFormat["SARIF"] = "sarif";
58
+ })(OutputFormat || (exports.OutputFormat = OutputFormat = {}));
59
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH;;GAEG;AACH,IAAY,QAMX;AAND,WAAY,QAAQ;IAClB,iCAAqB,CAAA;IACrB,yBAAa,CAAA;IACb,6BAAiB,CAAA;IACjB,uBAAW,CAAA;IACX,yBAAa,CAAA;AACf,CAAC,EANW,QAAQ,wBAAR,QAAQ,QAMnB;AAED;;GAEG;AACH,IAAY,UAIX;AAJD,WAAY,UAAU;IACpB,2BAAa,CAAA;IACb,+BAAiB,CAAA;IACjB,yBAAW,CAAA;AACb,CAAC,EAJW,UAAU,0BAAV,UAAU,QAIrB;AAED;;GAEG;AACH,IAAY,YAQX;AARD,WAAY,YAAY;IACtB,mCAAmB,CAAA;IACnB,6BAAa,CAAA;IACb,6CAA6B,CAAA;IAC7B,2CAA2B,CAAA;IAC3B,mCAAmB,CAAA;IACnB,qCAAqB,CAAA;IACrB,iCAAiB,CAAA;AACnB,CAAC,EARW,YAAY,4BAAZ,YAAY,QAQvB;AAED;;GAEG;AACH,IAAY,gBAKX;AALD,WAAY,gBAAgB;IAC1B,2CAAuB,CAAA;IACvB,+BAAW,CAAA;IACX,yCAAqB,CAAA;IACrB,qCAAiB,CAAA;AACnB,CAAC,EALW,gBAAgB,gCAAhB,gBAAgB,QAK3B;AA0FD;;GAEG;AACH,IAAY,YAIX;AAJD,WAAY,YAAY;IACtB,6BAAa,CAAA;IACb,6BAAa,CAAA;IACb,+BAAe,CAAA;AACjB,CAAC,EAJW,YAAY,4BAAZ,YAAY,QAIvB"}